Skip to main content

extendr_api/wrapper/
function.rs

1use super::*;
2use extendr_ffi::{get_closure_body, get_closure_env, get_closure_formals, Rf_lcons};
3/// Wrapper for creating functions (CLOSSXP).
4/// ```
5/// use extendr_api::prelude::*;
6/// use extendr_api::wrapper::symbol::missing_arg;
7/// test! {
8///     // Closures are functions.
9///     let expr = R!("function(a = 1, b) {c <- a + b}")?;
10///     let func = expr.as_function().unwrap();
11///
12///     let expected_formals = Pairlist::from_pairs(vec![("a", r!(1.0)), ("b", missing_arg().into())]);
13///     let expected_body = lang!(
14///         "{", lang!("<-", sym!(c), lang!("+", sym!(a), sym!(b))));
15///     assert_eq!(func.formals().unwrap(), expected_formals);
16///     assert_eq!(func.body().unwrap(), expected_body);
17///     assert_eq!(func.environment().unwrap(), Environment::global());
18///
19///     // Primitives can also be functions.
20///     let expr = R!("`~`")?;
21///     let func = expr.as_function().unwrap();
22///     assert_eq!(func.formals(), None);
23///     assert_eq!(func.body(), None);
24///     assert_eq!(func.environment(), None);
25/// }
26/// ```
27#[derive(PartialEq, Clone)]
28pub struct Function {
29    pub(crate) robj: Robj,
30}
31
32impl Function {
33    #[cfg(feature = "non-api")]
34    /// Make a function from parts.
35    /// ```
36    /// use extendr_api::prelude::*;
37    /// test! {
38    ///     let formals = pairlist!(a=NULL);
39    ///     let body = lang!("+", sym!(a), r!(1)).try_into()?;
40    ///     let env = Environment::global();
41    ///     let f = r!(Function::from_parts(formals, body, env )?);
42    ///     assert_eq!(f.call(pairlist!(a=1))?, r!(2));
43    /// }
44    /// ```
45    pub fn from_parts(formals: Pairlist, body: Language, env: Environment) -> Result<Self> {
46        single_threaded(|| unsafe {
47            let sexp = extendr_ffi::Rf_allocSExp(SEXPTYPE::CLOSXP);
48            let robj = Robj::from_sexp(sexp);
49            extendr_ffi::SET_FORMALS(sexp, formals.get());
50            extendr_ffi::SET_BODY(sexp, body.get());
51            extendr_ffi::SET_CLOENV(sexp, env.get());
52            Ok(Function { robj })
53        })
54    }
55
56    /// Do the equivalent of x(a, b, c)
57    /// ```
58    /// use extendr_api::prelude::*;
59    /// test! {
60    ///     let function = R!("function(a, b) a + b").unwrap().as_function().unwrap();
61    ///     assert_eq!(function.call(pairlist!(a=1, b=2)).unwrap(), r!(3));
62    /// }
63    /// ```
64    pub fn call(&self, args: Pairlist) -> Result<Robj> {
65        single_threaded(|| unsafe {
66            let call = Robj::from_sexp(Rf_lcons(self.get(), args.get()));
67            call.eval()
68        })
69    }
70
71    /// Get the formal arguments of the function or None if it is a primitive.
72    pub fn formals(&self) -> Option<Pairlist> {
73        unsafe {
74            if self.rtype() == Rtype::Function {
75                let sexp = self.robj.get();
76                Some(
77                    Robj::from_sexp(get_closure_formals(sexp))
78                        .try_into()
79                        .unwrap(),
80                )
81            } else {
82                None
83            }
84        }
85    }
86
87    /// Get the body of the function or None if it is a primitive.
88    pub fn body(&self) -> Option<Robj> {
89        unsafe {
90            if self.rtype() == Rtype::Function {
91                let sexp = self.robj.get();
92                Some(Robj::from_sexp(get_closure_body(sexp)))
93            } else {
94                None
95            }
96        }
97    }
98
99    /// Get the environment of the function or None if it is a primitive.
100    pub fn environment(&self) -> Option<Environment> {
101        unsafe {
102            if self.rtype() == Rtype::Function {
103                let sexp = self.robj.get();
104                Some(
105                    Robj::from_sexp(get_closure_env(sexp))
106                        .try_into()
107                        .expect("Should be an environment"),
108                )
109            } else {
110                None
111            }
112        }
113    }
114}
115
116impl std::fmt::Debug for Function {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        write!(f, "{}", self.deparse().unwrap())
119    }
120}