extendr_api/
functions.rs

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