diff --git a/src/callable/core.rs b/src/callable/core.rs index 52b3a448..759e2d6a 100644 --- a/src/callable/core.rs +++ b/src/callable/core.rs @@ -261,14 +261,20 @@ impl TryFrom<&str> for Box { } } -pub fn force_closures(vals: List, stack: &mut CallStack) -> Vec<(Option, Obj)> { +pub fn force_closures( + vals: List, + stack: &mut CallStack, +) -> Result, Obj)>, Signal> { // Force any closures that were created during call. This helps with using // variables as argument for sep and collapse parameters. vals.values .borrow_mut() .clone() .into_iter() - .map(|(k, v)| (k, v.clone().force(stack).unwrap_or(Obj::Null))) // TODO: raise this error + .map(|(k, v)| match (k, v.force(stack)) { + (k, Ok(v)) => Ok((k, v)), + (_, Err(e)) => Err(e), + }) .collect() } diff --git a/src/callable/primitive/paste.rs b/src/callable/primitive/paste.rs index b8a6e3b1..5b9705a3 100644 --- a/src/callable/primitive/paste.rs +++ b/src/callable/primitive/paste.rs @@ -1,7 +1,6 @@ use r_derive::*; use crate::callable::core::*; -use crate::context::Context; use crate::error::*; use crate::lang::*; use crate::object::*; @@ -10,35 +9,31 @@ use crate::object::*; #[builtin(sym = "paste")] pub struct PrimitivePaste; impl Callable for PrimitivePaste { + fn formals(&self) -> ExprList { + ExprList::from(vec![ + (None, Expr::Ellipsis(None)), + (Some(String::from("sep")), Expr::String(" ".to_string())), + (Some(String::from("collapse")), Expr::Null), + ]) + } + fn call(&self, args: ExprList, stack: &mut CallStack) -> EvalResult { - let Obj::List(vals) = stack.parent_frame().eval_list_lazy(args)? else { - unreachable!() - }; + let (args, ellipsis) = self.match_arg_exprs(args, stack)?; - let mut vals = force_closures(vals, stack); + let ellipsis = force_closures(ellipsis, stack)?; + let args = force_closures(args, stack)?; let mut sep = String::from(" "); let mut should_collapse = false; let mut collapse = String::new(); - // find non-ellipsis arg indices - let named_indices: Vec = vals - .iter() - .enumerate() - .filter(|(_, (k, _))| { - *k == Some("collapse".to_string()) || *k == Some("sep".to_string()) - }) - .map(|(i, _)| i) - .collect(); - // remove named sep and collapse args from our arguments and populate values - for i in named_indices.iter().rev() { - let (Some(k), v) = vals.remove(*i) else { - continue; - }; + for (k, v) in args.iter().rev() { + let Some(k) = k else { continue }; + match (k.as_str(), v) { ("sep", Obj::Vector(v)) => { - sep = v.into(); + sep = (*v).clone().into(); } ("sep", _) => { return Err(Signal::Error(Error::Other( @@ -48,7 +43,7 @@ impl Callable for PrimitivePaste { ("collapse", Obj::Null) => continue, ("collapse", Obj::Vector(v)) => { should_collapse = true; - collapse = v.into(); + collapse = (*v).clone().into(); } ("collapse", _) => { return Err(Signal::Error(Error::WithCallStack( @@ -63,7 +58,7 @@ impl Callable for PrimitivePaste { } // coerce all of our remaining arguments into vectors of strings - let vec_s_vec: Vec> = vals + let vec_s_vec: Vec> = ellipsis .into_iter() .map(|(_, v)| -> Result, Signal> { match v.as_character()? { diff --git a/src/callable/primitive/rnorm.rs b/src/callable/primitive/rnorm.rs index f18de2f8..792f0d10 100644 --- a/src/callable/primitive/rnorm.rs +++ b/src/callable/primitive/rnorm.rs @@ -21,7 +21,7 @@ impl Callable for PrimitiveRnorm { fn call(&self, args: ExprList, stack: &mut CallStack) -> EvalResult { use Error::ArgumentInvalid; let (vals, _) = self.match_arg_exprs(args, stack)?; - let vals = force_closures(vals, stack); + let vals = force_closures(vals, stack)?; let mut vals = Obj::List(List::from(vals)); let n: i32 = vals.try_get_named("n")?.try_into()?; diff --git a/src/callable/primitive/runif.rs b/src/callable/primitive/runif.rs index a71819b3..379b3a08 100644 --- a/src/callable/primitive/runif.rs +++ b/src/callable/primitive/runif.rs @@ -23,7 +23,7 @@ impl Callable for PrimitiveRunif { use Error::ArgumentInvalid; let (vals, _) = self.match_arg_exprs(args, stack)?; - let vals = force_closures(vals, stack); + let vals = force_closures(vals, stack)?; let mut vals = Obj::List(List::from(vals)); let n: i32 = vals.try_get_named("n")?.try_into()?; diff --git a/src/context/core.rs b/src/context/core.rs index f39b59a3..36cef332 100644 --- a/src/context/core.rs +++ b/src/context/core.rs @@ -24,8 +24,12 @@ pub trait Context: std::fmt::Debug + std::fmt::Display { fn env(&self) -> Rc; - fn eval_call(&mut self, expr: Expr) -> EvalResult { - self.eval(expr) + fn eval_call_in(&mut self, expr: Expr, env: Rc) -> EvalResult { + self.eval_in(expr, env) + } + + fn eval_in(&mut self, expr: Expr, env: Rc) -> EvalResult { + env.clone().eval(expr) } fn eval(&mut self, expr: Expr) -> EvalResult { diff --git a/src/lang.rs b/src/lang.rs index 36f98970..31620eff 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -693,7 +693,7 @@ impl Context for CallStack { self.last_frame().env().clone() } - fn eval_call(&mut self, expr: Expr) -> EvalResult { + fn eval_call_in(&mut self, expr: Expr, _env: Rc) -> EvalResult { let Expr::Call(what, args) = expr.clone() else { return internal_err!(); }; @@ -768,12 +768,22 @@ impl Context for CallStack { } } + fn eval_in(&mut self, expr: Expr, env: Rc) -> EvalResult { + use Expr::*; + match expr { + List(x) => self.eval_list_lazy(x), + Symbol(s) => env.clone().get(s), + Call(..) => self.eval_call_in(expr, env), + _ => self.last_frame().eval(expr), + } + } + fn eval(&mut self, expr: Expr) -> EvalResult { use Expr::*; match expr { List(x) => self.eval_list_lazy(x), Symbol(s) => self.get(s), - Call(..) => self.eval_call(expr), + Call(..) => self.eval_call_in(expr, self.env()), _ => self.last_frame().eval(expr), } }