Skip to main content

extendr_api/
functions.rs

1use crate as extendr_api;
2use crate::*;
3use extendr_ffi::{
4    R_BaseNamespace, R_BlankScalarString, R_BlankString, R_NaString, R_NilValue, R_Srcref,
5    R_dot_Generic,
6};
7
8/// Get a global function from global_env() and ancestors.
9/// ```
10/// use extendr_api::prelude::*;
11/// test! {
12///     let ls = global_function(sym!(ls))?;
13///     assert_eq!(ls.is_function(), true);
14/// }
15/// ```
16pub fn global_function<K: Into<Robj>>(key: K) -> Result<Robj> {
17    let key = key.into();
18    Environment::global().find_function(key)
19}
20
21/// Find a namespace by name.
22///
23/// See also [`Robj::double_colon`].
24/// ```
25/// use extendr_api::prelude::*;
26/// test! {
27///    assert_eq!(find_namespace("base").is_ok(), true);
28///    assert_eq!(find_namespace("stats").is_ok(), true);
29/// }
30/// ```
31/// [`Robj::double_colon`]: Operators::double_colon
32pub fn find_namespace<K: Into<Robj>>(key: K) -> Result<Environment> {
33    let key = key.into();
34    let res = single_threaded(|| call!(".getNamespace", key.clone()));
35    if let Ok(res) = res {
36        Ok(res.try_into()?)
37    } else {
38        Err(Error::NamespaceNotFound(key))
39    }
40}
41
42/// The current interpreter environment.
43///
44/// ```
45/// use extendr_api::prelude::*;
46/// test! {
47///    assert!(Environment::current().is_environment());
48/// }
49/// ```
50#[deprecated(since = "0.10.0", note = "Use `Environment::current()` instead")]
51pub fn current_env() -> Environment {
52    Environment::current()
53}
54
55/// The "global" environment
56///
57/// ```
58/// use extendr_api::prelude::*;
59/// test! {
60///     Environment::global().set_local(sym!(x), "hello");
61///     assert_eq!(Environment::global().local(sym!(x)), Ok(r!("hello")));
62/// }
63/// ```
64#[deprecated(since = "0.10.0", note = "Use `Environment::global()` instead")]
65pub fn global_env() -> Environment {
66    Environment::global()
67}
68
69/// An empty environment at the root of the environment tree
70#[deprecated(since = "0.10.0", note = "Use `Environment::empty()` instead")]
71pub fn empty_env() -> Environment {
72    Environment::empty()
73}
74
75/// Create a new environment
76///
77/// ```
78/// use extendr_api::prelude::*;
79/// test! {
80///     let env: Environment = new_env(Environment::global(), true, 10).try_into().unwrap();
81///     env.set_local(sym!(x), "hello");
82///     assert_eq!(env.local(sym!(x)), Ok(r!("hello")));
83/// }
84/// ```
85#[cfg(use_r_newenv)]
86pub fn new_env(parent: Environment, hash: bool, capacity: i32) -> Environment {
87    use extendr_ffi::R_NewEnv;
88    single_threaded(|| unsafe {
89        let env = R_NewEnv(parent.robj.get(), hash as i32, capacity);
90        Robj::from_sexp(env).try_into().unwrap()
91    })
92}
93
94// R_NewEnv is available as of R 4.1.0. For the older version, we call an R function `new.env()`.
95#[cfg(not(use_r_newenv))]
96pub fn new_env(parent: Environment, hash: bool, capacity: i32) -> Environment {
97    call!("new.env", hash, parent, capacity)
98        .unwrap()
99        .try_into()
100        .unwrap()
101}
102
103/// The base environment; formerly `R_NilValue`
104#[deprecated(since = "0.10.0", note = "Use `Environment::base()` instead")]
105pub fn base_env() -> Environment {
106    Environment::base()
107}
108
109/// The namespace for base.
110///
111/// ```
112/// use extendr_api::prelude::*;
113/// test! {
114///    assert_eq!(base_namespace().parent().ok_or("no parent")?, Environment::global());
115/// }
116/// ```
117pub fn base_namespace() -> Environment {
118    unsafe { Robj::from_sexp(R_BaseNamespace).try_into().unwrap() }
119}
120
121/// Current srcref, for debuggers
122pub fn srcref() -> Robj {
123    unsafe { Robj::from_sexp(R_Srcref) }
124}
125
126/// The nil object
127pub fn nil_value() -> Robj {
128    unsafe { Robj::from_sexp(R_NilValue) }
129}
130
131/// ".Generic"
132pub fn dot_generic() -> Robj {
133    unsafe { Robj::from_sexp(R_dot_Generic) }
134}
135
136/// NA_STRING as a CHARSXP
137pub fn na_string() -> Robj {
138    unsafe { Robj::from_sexp(R_NaString) }
139}
140
141/// "" as a CHARSXP
142pub fn blank_string() -> Robj {
143    unsafe { Robj::from_sexp(R_BlankString) }
144}
145
146/// "" as a STRSXP
147pub fn blank_scalar_string() -> Robj {
148    unsafe { Robj::from_sexp(R_BlankScalarString) }
149}
150
151/// Parse a string into an R executable object
152/// ```
153/// use extendr_api::prelude::*;
154/// test! {
155///    let expr = parse("1 + 2").unwrap();
156///    assert!(expr.is_expressions());
157/// }
158/// ```
159pub fn parse(code: &str) -> Result<Expressions> {
160    single_threaded(|| unsafe {
161        use extendr_ffi::{ParseStatus, R_NilValue, R_ParseVector};
162        let mut status = ParseStatus::PARSE_NULL;
163        let status_ptr = (&mut status) as *mut _;
164        let codeobj: Robj = code.into();
165        let parsed = Robj::from_sexp(R_ParseVector(codeobj.get(), -1, status_ptr, R_NilValue));
166        match status {
167            ParseStatus::PARSE_OK => parsed.try_into(),
168            _ => Err(Error::ParseError {
169                status,
170                code: code.into(),
171            }),
172        }
173    })
174}
175
176/// Parse a string into an R executable object and run it.
177/// Used by the R! macro.
178/// ```
179/// use extendr_api::prelude::*;
180/// test! {
181///    let res = eval_string("1 + 2").unwrap();
182///    assert_eq!(res, r!(3.));
183/// }
184/// ```
185pub fn eval_string(code: &str) -> Result<Robj> {
186    single_threaded(|| {
187        let expr = parse(code)?;
188        let mut res = Robj::from(());
189        if let Some(expr) = expr.as_expressions() {
190            for lang in expr.values() {
191                res = lang.eval()?
192            }
193        }
194        Ok(res)
195    })
196}
197
198/// Parse a string into an R executable object and run it using
199///   parameters param.0, param.1, ...
200///
201/// Used by the R! macro.
202/// ```
203/// use extendr_api::prelude::*;
204/// test! {
205///    let res = eval_string_with_params("param.0", &[&r!(3.)]).unwrap();
206///    assert_eq!(res, r!(3.));
207/// }
208/// ```
209pub fn eval_string_with_params(code: &str, values: &[&Robj]) -> Result<Robj> {
210    single_threaded(|| {
211        let env = Environment::new_with_parent(Environment::global());
212        for (i, &v) in values.iter().enumerate() {
213            let key = Symbol::from_string(format!("param.{}", i));
214            env.set_local(key, v);
215        }
216
217        let expr = parse(code)?;
218        let mut res = Robj::from(());
219        if let Some(expr) = expr.as_expressions() {
220            for lang in expr.values() {
221                res = lang.eval_with_env(&env)?
222            }
223        }
224
225        Ok(res)
226    })
227}
228
229/// Find a function or primitive that may be in a namespace.
230/// ```
231/// use extendr_api::prelude::*;
232/// test! {
233///    assert!(find_namespaced_function("+").is_ok());
234///    assert!(find_namespaced_function("ls").is_ok());
235///    assert!(find_namespaced_function("base::ls").is_ok());
236///    assert!(find_namespaced_function("ls")?.is_language());
237///    assert!(!find_namespaced_function("basex::ls").is_ok());
238/// }
239/// ```
240pub fn find_namespaced_function(name: &str) -> Result<Language> {
241    let mut iter = name.split("::");
242    match (iter.next(), iter.next(), iter.next()) {
243        (Some(key), None, None) => {
244            let gf = global_function(Symbol::from_string(key))?;
245            Ok(Language::from_values(&[gf]))
246        }
247        (Some(ns), Some(key), None) => {
248            let namespace = find_namespace(ns)?;
249            Ok(Language::from_values(&[
250                namespace.local(Symbol::from_string(key))?
251            ]))
252        }
253        _ => Err(Error::NotFound(r!(name))),
254    }
255}