1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! Argument parsing and checking.
//!

use crate::robj::GetSexp;
use crate::robj::Robj;
use crate::single_threaded;
use libR_sys::*;

/// Convert a list of tokens to an array of tuples.
#[doc(hidden)]
#[macro_export]
macro_rules! push_args {
    ($args: expr, $name: ident = $val : expr) => {
        $args.push((stringify!($name), Robj::from($val)));
    };
    ($args: expr, $name: ident = $val : expr, $($rest: tt)*) => {
        $args.push((stringify!($name), Robj::from($val)));
        push_args!($args, $($rest)*);
    };
    ($args: expr, $val : expr) => {
        $args.push(("", Robj::from($val)));
    };
    ($args: expr, $val : expr, $($rest: tt)*) => {
        $args.push(("", Robj::from($val)));
        push_args!($args, $($rest)*);
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! args {
    () => {
        Vec::<(&str, Robj)>::new()
    };
    ($($rest: tt)*) => {
        {
            let mut args = Vec::<(&str, Robj)>::new();
            push_args!(args, $($rest)*);
            args
        }
    };
}

#[doc(hidden)]
pub unsafe fn append_with_name(tail: SEXP, obj: Robj, name: &str) -> SEXP {
    single_threaded(|| {
        let cons = Rf_cons(obj.get(), R_NilValue);
        SET_TAG(cons, crate::make_symbol(name));
        SETCDR(tail, cons);
        cons
    })
}

#[doc(hidden)]
pub unsafe fn append(tail: SEXP, obj: Robj) -> SEXP {
    single_threaded(|| {
        let cons = Rf_cons(obj.get(), R_NilValue);
        SETCDR(tail, cons);
        cons
    })
}

#[doc(hidden)]
pub unsafe fn make_lang(sym: &str) -> Robj {
    Robj::from_sexp(single_threaded(|| Rf_lang1(crate::make_symbol(sym))))
}

/// Convert a list of tokens to an array of tuples.
#[doc(hidden)]
#[macro_export]
macro_rules! append_lang {
    ($tail: ident, $name: ident = $val : expr) => {
        $tail = append_with_name($tail, Robj::from($val), stringify!($name));
    };
    ($tail: ident, $name: ident = $val : expr, $($rest: tt)*) => {
        $tail = append_with_name($tail, Robj::from($val), stringify!($name));
        append_lang!($tail, $($rest)*);
    };
    ($tail: ident, $val : expr) => {
        $tail = append($tail, Robj::from($val));
    };
    ($tail: ident, $val : expr, $($rest: tt)*) => {
        $tail = append($tail, Robj::from($val));
        append_lang!($tail, $($rest)*);
    };
}

/// A macro for constructing R langage objects.
///
/// Example:
/// ```
/// use extendr_api::prelude::*;
/// test! {
///     let call_to_c = lang!("c", 1., 2., 3.);
///     let vec = call_to_c.eval().unwrap();
///     assert_eq!(vec, r!([1., 2., 3.]));
///
///     let list = lang!("list", a=1, b=2).eval().unwrap();
///     assert_eq!(list.len(), 2);
/// }
/// ```
#[macro_export]
macro_rules! lang {
    ($sym : expr) => {
        unsafe {
            make_lang($sym)
        }
    };
    ($sym : expr, $($rest: tt)*) => {
        unsafe {
            use extendr_api::robj::GetSexp;
            let res = make_lang($sym);
            let mut tail = res.get();
            append_lang!(tail, $($rest)*);
            let _ = tail;
            res
        }
    };
}