extendr_api/
functions.rs

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