--- a/crates/jsonnet-evaluator/src/ctx.rs +++ b/crates/jsonnet-evaluator/src/ctx.rs @@ -1,8 +1,6 @@ use crate::{ - future_wrapper, lazy_binding, lazy_val, rc_fn_helper, LazyBinding, LazyVal, ObjValue, Result, - Val, + future_wrapper, rc_fn_helper, resolved_lazy_val, LazyBinding, LazyVal, ObjValue, Result, Val, }; -use closure::closure; use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; rc_fn_helper!( @@ -68,12 +66,7 @@ pub fn with_var(&self, name: String, value: Val) -> Result { let mut new_bindings: HashMap<_, LazyBinding> = HashMap::new(); - new_bindings.insert( - name, - lazy_binding!( - closure!(clone value, |_t, _s|Ok(lazy_val!(closure!(clone value, ||Ok(value.clone()))))) - ), - ); + new_bindings.insert(name, LazyBinding::Bound(resolved_lazy_val!(value.clone()))); self.extend(new_bindings, None, None, None) } @@ -95,7 +88,7 @@ new.insert(k.clone(), v.clone()); } for (k, v) in new_bindings.into_iter() { - new.insert(k, v.0(this.clone(), super_obj.clone())?); + new.insert(k, v.evaluate(this.clone(), super_obj.clone())?); } Rc::new(new) }; --- a/crates/jsonnet-evaluator/src/evaluate.rs +++ b/crates/jsonnet-evaluator/src/evaluate.rs @@ -1,7 +1,7 @@ use crate::{ binding, context_creator, create_error, function_default, function_rhs, future_wrapper, - lazy_binding, lazy_val, push, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember, - ObjValue, Result, Val, + lazy_val, push, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, + Result, Val, }; use closure::closure; use jsonnet_parser::{ @@ -19,18 +19,20 @@ let args = args.clone(); ( b.name.clone(), - lazy_binding!(move |this, super_obj| Ok(lazy_val!( - closure!(clone b, clone args, clone context_creator, || Ok(evaluate_method( - context_creator.0(this.clone(), super_obj.clone())?, - &b.value, - args.clone() - ))) - ))), + LazyBinding::Bindable(Rc::new(move |this, super_obj| { + Ok(lazy_val!( + closure!(clone b, clone args, clone context_creator, || Ok(evaluate_method( + context_creator.0(this.clone(), super_obj.clone())?, + &b.value, + args.clone() + ))) + )) + })), ) } else { ( b.name.clone(), - lazy_binding!(move |this, super_obj| { + LazyBinding::Bindable(Rc::new(move |this, super_obj| { Ok(lazy_val!(closure!(clone context_creator, clone b, || push(b.value.clone(), "thunk".to_owned(), ||{ evaluate( @@ -39,7 +41,7 @@ ) }) ))) - }), + })), ) } } @@ -405,7 +407,7 @@ &evaluate(context.clone(), s)?, &Val::Obj(evaluate_object(context, t.clone())?), )?, - Apply(value, ArgsDesc(args)) => { + Apply(value, ArgsDesc(args), tailstrict) => { let value = evaluate(context.clone(), value)?.unwrap_if_lazy()?; match value { Val::Intristic(ns, name) => match (&ns as &str, &name as &str) { @@ -502,7 +504,8 @@ evaluate(context.clone(), &args[0].1)?, evaluate(context, &args[1].1)?, ) { - println!("{}", a); + // TODO: Line numbers as in original jsonnet + println!("TRACE: {}", a); b } else { panic!("bad trace call"); @@ -517,9 +520,15 @@ .map(move |a| { ( a.clone().0, - Val::Lazy(lazy_val!( - closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1)) - )), + if *tailstrict { + Val::Lazy(LazyVal::new_resolved( + evaluate(context.clone(), &a.1).unwrap(), + )) + } else { + Val::Lazy(lazy_val!( + closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1)) + )) + }, ) }) .collect(), --- a/crates/jsonnet-evaluator/src/lib.rs +++ b/crates/jsonnet-evaluator/src/lib.rs @@ -6,28 +6,23 @@ mod dynamic; mod error; mod evaluate; +mod function; mod obj; mod val; -use closure::closure; pub use ctx::*; pub use dynamic::*; pub use error::*; pub use evaluate::*; use jsonnet_parser::*; pub use obj::*; -use std::{cell::RefCell, collections::HashMap, path::PathBuf, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, fmt::Debug, path::PathBuf, rc::Rc}; pub use val::*; rc_fn_helper!( Binding, binding, dyn Fn(Option, Option) -> Result -); -rc_fn_helper!( - LazyBinding, - lazy_binding, - dyn Fn(Option, Option) -> Result ); rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Result); rc_fn_helper!( @@ -36,6 +31,26 @@ dyn Fn(Context, LocExpr) -> Result ); +#[derive(Clone)] +pub enum LazyBinding { + Bindable(Rc, Option) -> Result>), + Bound(LazyVal), +} + +impl Debug for LazyBinding { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "LazyBinding") + } +} +impl LazyBinding { + pub fn evaluate(&self, this: Option, super_obj: Option) -> Result { + match self { + LazyBinding::Bindable(v) => v(this, super_obj), + LazyBinding::Bound(v) => Ok(v.clone()), + } + } +} + pub struct FileData(String, LocExpr, Option); #[derive(Default)] pub struct EvaluationStateInternals { @@ -168,9 +183,7 @@ for (name, value) in globals.iter() { new_bindings.insert( name.clone(), - lazy_binding!( - closure!(clone value, |_self, _super_obj| Ok(lazy_val!(closure!(clone value, ||Ok(value.clone()))))) - ), + LazyBinding::Bound(resolved_lazy_val!(value.clone())), ); } Context::new().extend(new_bindings, None, None, None) @@ -179,7 +192,7 @@ pub fn push(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result) -> Result { { let mut stack = self.0.stack.borrow_mut(); - if stack.len() > 5000 { + if stack.len() > 500 { drop(stack); return self.error(Error::StackOverflow); } else { --- a/crates/jsonnet-evaluator/src/val.rs +++ b/crates/jsonnet-evaluator/src/val.rs @@ -1,6 +1,5 @@ use crate::{ - create_error, lazy_binding, Context, Error, FunctionDefault, FunctionRhs, LazyBinding, - ObjValue, Result, + create_error, Context, Error, FunctionDefault, FunctionRhs, LazyBinding, ObjValue, Result, }; use closure::closure; use jsonnet_parser::{Param, ParamsDesc}; @@ -12,7 +11,7 @@ }; struct LazyValInternals { - pub f: Box Result>, + pub f: Option Result>>, pub cached: RefCell>, } #[derive(Clone)] @@ -20,10 +19,16 @@ impl LazyVal { pub fn new(f: Box Result>) -> Self { LazyVal(Rc::new(LazyValInternals { - f, + f: Some(f), cached: RefCell::new(None), })) } + pub fn new_resolved(val: Val) -> Self { + LazyVal(Rc::new(LazyValInternals { + f: None, + cached: RefCell::new(Some(val)), + })) + } pub fn evaluate(&self) -> Result { { let cached = self.0.cached.borrow(); @@ -31,17 +36,24 @@ return Ok(cached.clone().unwrap()); } } - let result = (self.0.f)()?; + let result = (self.0.f.as_ref().unwrap())()?; self.0.cached.borrow_mut().replace(result.clone()); Ok(result) } } + #[macro_export] macro_rules! lazy_val { ($f: expr) => { $crate::LazyVal::new(Box::new($f)) }; } +#[macro_export] +macro_rules! resolved_lazy_val { + ($f: expr) => { + $crate::LazyVal::new_resolved($f) + }; +} impl Debug for LazyVal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if self.0.cached.borrow().is_some() { @@ -75,25 +87,23 @@ let eval_default = self.eval_default.clone(); new_bindings.insert( name, - lazy_binding!(closure!(clone future_ctx, clone default, clone eval_default, |_, _| Ok(lazy_val!(closure!(clone future_ctx, clone eval_default, clone default, || (eval_default.clone()).0 - (future_ctx.clone().unwrap(), default.clone())))))), + LazyBinding::Bound(lazy_val!( + closure!(clone future_ctx, clone eval_default, clone default, || (eval_default.clone()).0 + (future_ctx.clone().unwrap(), default.clone())) + )), ); } for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) { new_bindings.insert( name.as_ref().unwrap().clone(), - lazy_binding!( - closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone()))))) - ), + LazyBinding::Bound(resolved_lazy_val!(val.clone())), ); } for (i, param) in self.params.0.iter().enumerate() { if let Some((None, val)) = args.get(i) { new_bindings.insert( param.0.clone(), - lazy_binding!( - closure!(clone val, |_, _| Ok(lazy_val!(closure!(clone val, || Ok(val.clone()))))) - ), + LazyBinding::Bound(resolved_lazy_val!(val.clone())), ); } }