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 libR_sys::*;
8
9/// Convert a list of tokens to an array of tuples.
10#[doc(hidden)]
11#[macro_export]
12macro_rules! push_args {
13    ($args: expr, $name: ident = $val : expr) => {
14        $args.push((stringify!($name), Robj::from($val)));
15    };
16    ($args: expr, $name: ident = $val : expr, $($rest: tt)*) => {
17        $args.push((stringify!($name), Robj::from($val)));
18        push_args!($args, $($rest)*);
19    };
20    ($args: expr, $val : expr) => {
21        $args.push(("", Robj::from($val)));
22    };
23    ($args: expr, $val : expr, $($rest: tt)*) => {
24        $args.push(("", Robj::from($val)));
25        push_args!($args, $($rest)*);
26    };
27}
28
29#[doc(hidden)]
30#[macro_export]
31macro_rules! args {
32    () => {
33        Vec::<(&str, Robj)>::new()
34    };
35    ($($rest: tt)*) => {
36        {
37            let mut args = Vec::<(&str, Robj)>::new();
38            push_args!(args, $($rest)*);
39            args
40        }
41    };
42}
43
44#[doc(hidden)]
45pub unsafe fn append_with_name(tail: SEXP, obj: Robj, name: &str) -> SEXP {
46    single_threaded(|| {
47        let cons = Rf_cons(obj.get(), R_NilValue);
48        SET_TAG(cons, crate::make_symbol(name));
49        SETCDR(tail, cons);
50        cons
51    })
52}
53
54#[doc(hidden)]
55pub unsafe fn append(tail: SEXP, obj: Robj) -> SEXP {
56    single_threaded(|| {
57        let cons = Rf_cons(obj.get(), R_NilValue);
58        SETCDR(tail, cons);
59        cons
60    })
61}
62
63#[doc(hidden)]
64pub unsafe fn make_lang(sym: &str) -> Robj {
65    Robj::from_sexp(single_threaded(|| Rf_lang1(crate::make_symbol(sym))))
66}
67
68/// Convert a list of tokens to an array of tuples.
69#[doc(hidden)]
70#[macro_export]
71macro_rules! append_lang {
72    ($tail: ident, $name: ident = $val : expr) => {
73        $tail = append_with_name($tail, Robj::from($val), stringify!($name));
74    };
75    ($tail: ident, $name: ident = $val : expr, $($rest: tt)*) => {
76        $tail = append_with_name($tail, Robj::from($val), stringify!($name));
77        append_lang!($tail, $($rest)*);
78    };
79    ($tail: ident, $val : expr) => {
80        $tail = append($tail, Robj::from($val));
81    };
82    ($tail: ident, $val : expr, $($rest: tt)*) => {
83        $tail = append($tail, Robj::from($val));
84        append_lang!($tail, $($rest)*);
85    };
86}
87
88/// A macro for constructing R language objects.
89///
90/// Example:
91/// ```
92/// use extendr_api::prelude::*;
93/// test! {
94///     let call_to_c = lang!("c", 1., 2., 3.);
95///     let vec = call_to_c.eval().unwrap();
96///     assert_eq!(vec, r!([1., 2., 3.]));
97///
98///     let list = lang!("list", a=1, b=2).eval().unwrap();
99///     assert_eq!(list.len(), 2);
100/// }
101/// ```
102#[macro_export]
103macro_rules! lang {
104    ($sym : expr) => {
105        unsafe {
106            make_lang($sym)
107        }
108    };
109    ($sym : expr, $($rest: tt)*) => {
110        unsafe {
111            use extendr_api::robj::GetSexp;
112            let res = make_lang($sym);
113            let mut tail = res.get();
114            append_lang!(tail, $($rest)*);
115            let _ = tail;
116            res
117        }
118    };
119}