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 = Expressions::from_str("1 + 2").unwrap();
156///    assert!(expr.is_expressions());
157/// }
158/// ```
159#[deprecated(since = "0.10.0", note = "Use Expressions::from_str() instead")]
160pub fn parse(code: &str) -> Result<Expressions> {
161    Expressions::from_str(code)
162}
163
164/// Parse a string into an R executable object and run it.
165/// Used by the R! macro.
166/// ```
167/// use extendr_api::prelude::*;
168/// test! {
169///    let res = eval_string("1 + 2").unwrap();
170///    assert_eq!(res, r!(3.));
171/// }
172/// ```
173pub fn eval_string(code: &str) -> Result<Robj> {
174    single_threaded(|| {
175        let expr = Expressions::from_str(code)?;
176        let mut res = Robj::from(());
177        if let Some(expr) = expr.as_expressions() {
178            for lang in expr.values() {
179                res = lang.eval()?
180            }
181        }
182        Ok(res)
183    })
184}
185
186/// Parse a string into an R executable object and run it using
187///   parameters param.0, param.1, ...
188///
189/// Used by the R! macro.
190/// ```
191/// use extendr_api::prelude::*;
192/// test! {
193///    let res = eval_string_with_params("param.0", &[&r!(3.)]).unwrap();
194///    assert_eq!(res, r!(3.));
195/// }
196/// ```
197pub fn eval_string_with_params(code: &str, values: &[&Robj]) -> Result<Robj> {
198    single_threaded(|| {
199        let env = Environment::new_with_parent(Environment::global());
200        for (i, &v) in values.iter().enumerate() {
201            let key = Symbol::from_string(format!("param.{}", i));
202            env.set_local(key, v);
203        }
204
205        let expr = Expressions::from_str(code)?;
206        let mut res = Robj::from(());
207        if let Some(expr) = expr.as_expressions() {
208            for lang in expr.values() {
209                res = lang.eval_with_env(&env)?
210            }
211        }
212
213        Ok(res)
214    })
215}
216
217/// Find a function or primitive that may be in a namespace.
218/// ```
219/// use extendr_api::prelude::*;
220/// test! {
221///    assert!(find_namespaced_function("+").is_ok());
222///    assert!(find_namespaced_function("ls").is_ok());
223///    assert!(find_namespaced_function("base::ls").is_ok());
224///    assert!(find_namespaced_function("ls")?.is_language());
225///    assert!(!find_namespaced_function("basex::ls").is_ok());
226/// }
227/// ```
228pub fn find_namespaced_function(name: &str) -> Result<Language> {
229    let mut iter = name.split("::");
230    match (iter.next(), iter.next(), iter.next()) {
231        (Some(key), None, None) => {
232            let gf = global_function(Symbol::from_string(key))?;
233            Ok(Language::from_values(&[gf]))
234        }
235        (Some(ns), Some(key), None) => {
236            let namespace = find_namespace(ns)?;
237            Ok(Language::from_values(&[
238                namespace.local(Symbol::from_string(key))?
239            ]))
240        }
241        _ => Err(Error::NotFound(r!(name))),
242    }
243}