1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
use r_derive::*;
use crate::callable::core::*;
use crate::context::Context;
use crate::error::Error;
use crate::formals;
use crate::lang::*;
use crate::object::*;
/// Get an Environment
///
/// Fetches an object's environment.
///
/// # In-Language
///
/// ## Usage
///
/// ```custom,{class=r}
/// environment(fun)
/// ```
///
/// ## Arguments
///
/// `fun`: An object for which to fetch a relevant environment. When missing,
/// return the current execution environment. Although `fun` may imply that
/// this only operates on `function`s, `environment`s can be fetched from
/// other objects with meaningful associated `environment`s such as
/// `environment`s (returning their parent), or `promise`s (returning their
/// expression's originating environment).
///
/// ## Examples
///
/// ```custom,{class=r-repl}
/// environment()
/// ```
///
/// ```custom,{class=r-repl}
/// environment(parent())
/// ```
///
#[doc(alias = "environment")]
#[builtin(sym = "environment")]
#[derive(Debug, Clone, PartialEq)]
pub struct PrimitiveEnvironment;
formals!(PrimitiveEnvironment, "(fun,)");
impl Callable for PrimitiveEnvironment {
fn call(&self, args: ExprList, stack: &mut CallStack) -> EvalResult {
let (vals, _) = self.match_arg_exprs(args, stack)?;
let mut vals = Obj::List(vals);
// default when `fun` is missing or not found
let fun = vals.try_get_named("fun");
if let Ok(Obj::Promise(_, Expr::Missing, _))
| Err(Signal::Error(Error::ArgumentMissing(_))) = fun
{
return Ok(Obj::Environment(stack.env().clone()));
};
// otherwise we can evaluate value and return result's environment
match fun?.force(stack)? {
Obj::Promise(.., e) => Ok(Obj::Environment(e.clone())),
Obj::Function(.., e) => Ok(Obj::Environment(e.clone())),
Obj::Environment(e) => Ok(Obj::Environment(e.clone())),
_ => Error::ArgumentInvalid(String::from("fun")).into(),
}
}
}
#[cfg(test)]
mod test {
use crate::{r, r_expect};
#[test]
fn no_args() {
(r! { environment() }).expect("environment() returns non-environment value");
}
#[test]
fn functions_return_env() {
r_expect! {{"
x <- function() { function() { } }
environment(x()) != environment(x)
"}}
}
#[test]
fn capture_local_env() {
r_expect! {{"
x <- function() { environment() }
x() != environment(x)
"}}
}
}