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