Skip to main content

extendr_api/
lang_macros.rs

1//! Argument parsing and checking.
2//!
3
4use crate::robj::GetSexp;
5use crate::robj::Robj;
6use crate::single_threaded;
7use extendr_ffi::{R_NilValue, Rf_cons, Rf_lang1, SETCDR, SET_TAG, SEXP};
8
9#[doc(hidden)]
10pub unsafe fn append_with_name(tail: SEXP, obj: Robj, name: &str) -> SEXP {
11    single_threaded(|| {
12        let cons = Rf_cons(obj.get(), R_NilValue);
13        SET_TAG(cons, crate::make_symbol(name));
14        SETCDR(tail, cons);
15        cons
16    })
17}
18
19#[doc(hidden)]
20#[allow(clippy::not_unsafe_ptr_arg_deref)]
21pub fn append(tail: SEXP, obj: Robj) -> SEXP {
22    single_threaded(|| unsafe {
23        let cons = Rf_cons(obj.get(), R_NilValue);
24        SETCDR(tail, cons);
25        cons
26    })
27}
28
29#[doc(hidden)]
30pub fn make_lang(sym: &str) -> Robj {
31    unsafe { Robj::from_sexp(single_threaded(|| Rf_lang1(crate::make_symbol(sym)))) }
32}
33
34/// Convert a list of tokens to an array of tuples.
35#[doc(hidden)]
36#[macro_export]
37macro_rules! append_lang {
38    ($tail: ident, $name: ident = $val : expr) => {
39        $tail = $crate::append_with_name($tail, $crate::Robj::from($val), stringify!($name));
40    };
41    ($tail: ident, $name: ident = $val : expr, $($rest: tt)*) => {
42        $tail = $crate::append_with_name($tail, $crate::Robj::from($val), stringify!($name));
43        $crate::append_lang!($tail, $($rest)*);
44    };
45    ($tail: ident, $val : expr) => {
46        $tail = $crate::append($tail, $crate::Robj::from($val));
47    };
48    ($tail: ident, $val : expr, $($rest: tt)*) => {
49        $tail = $crate::append($tail, $crate::Robj::from($val));
50        $crate::append_lang!($tail, $($rest)*);
51    };
52}
53
54/// A macro for constructing R language objects.
55///
56/// Example:
57/// ```
58/// use extendr_api::prelude::*;
59/// test! {
60///     let call_to_c = lang!("c", 1., 2., 3.);
61///     let vec = call_to_c.eval().unwrap();
62///     assert_eq!(vec, r!([1., 2., 3.]));
63///
64///     let list = lang!("list", a=1, b=2).eval().unwrap();
65///     assert_eq!(list.len(), 2);
66/// }
67/// ```
68#[macro_export]
69macro_rules! lang {
70    ($sym : expr) => {
71        $crate::make_lang($sym)
72    };
73    ($sym : expr, $($rest: tt)*) => {
74        unsafe {
75            use $crate::robj::GetSexp;
76            let res = $crate::make_lang($sym);
77            let mut tail = res.get();
78            $crate::append_lang!(tail, $($rest)*);
79            let _ = tail;
80            res
81        }
82    };
83}