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/// 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)]
54#[allow(clippy::not_unsafe_ptr_arg_deref)]
55pub fn append(tail: SEXP, obj: Robj) -> SEXP {
56    single_threaded(|| unsafe {
57        let cons = Rf_cons(obj.get(), R_NilValue);
58        SETCDR(tail, cons);
59        cons
60    })
61}
62
63#[doc(hidden)]
64pub fn make_lang(sym: &str) -> Robj {
65    unsafe { 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        #[allow(unused_unsafe)]
106        unsafe {
107            make_lang($sym)
108        }
109    };
110    ($sym : expr, $($rest: tt)*) => {
111        unsafe {
112            use $crate::robj::GetSexp;
113            let res = make_lang($sym);
114            let mut tail = res.get();
115            append_lang!(tail, $($rest)*);
116            let _ = tail;
117            res
118        }
119    };
120}