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}