--- a/cmds/jrsonnet/src/main.rs +++ b/cmds/jrsonnet/src/main.rs @@ -90,7 +90,7 @@ let opts: Opts = Opts::parse(); let evaluator = jsonnet_evaluator::EvaluationState::default(); if !opts.no_stdlib { - evaluator.add_stdlib(); + evaluator.with_stdlib(); } let mut input = current_dir().unwrap(); input.push(opts.input.clone()); @@ -105,7 +105,7 @@ let v = match opts.format { Format::Json => { if opts.no_stdlib { - evaluator.add_stdlib(); + evaluator.with_stdlib(); } evaluator.add_global("__tmp__to_json__".to_owned(), v); let v = evaluator.parse_evaluate_raw(&format!( @@ -122,7 +122,7 @@ } Format::Yaml => { if opts.no_stdlib { - evaluator.add_stdlib(); + evaluator.with_stdlib(); } evaluator.add_global("__tmp__to_yaml__".to_owned(), v); let v = evaluator --- a/crates/jsonnet-evaluator/src/ctx.rs +++ b/crates/jsonnet-evaluator/src/ctx.rs @@ -1,5 +1,6 @@ use crate::{ - future_wrapper, rc_fn_helper, resolved_lazy_val, LazyBinding, LazyVal, ObjValue, Result, Val, + future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val, LazyBinding, LazyVal, + ObjValue, Result, Val, }; use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; @@ -11,12 +12,11 @@ future_wrapper!(Context, FutureContext); -#[derive(Debug)] struct ContextInternals { dollar: Option, this: Option, super_obj: Option, - bindings: Rc>, + bindings: LayeredHashMap, } pub struct Context(Rc); impl Debug for Context { @@ -48,7 +48,7 @@ dollar: None, this: None, super_obj: None, - bindings: Rc::new(HashMap::new()), + bindings: LayeredHashMap::default(), })) } @@ -65,14 +65,14 @@ } pub fn with_var(&self, name: String, value: Val) -> Result { - let mut new_bindings: HashMap<_, LazyBinding> = HashMap::new(); - new_bindings.insert(name, LazyBinding::Bound(resolved_lazy_val!(value.clone()))); + let mut new_bindings = HashMap::new(); + new_bindings.insert(name, resolved_lazy_val!(value)); self.extend(new_bindings, None, None, None) } pub fn extend( &self, - new_bindings: HashMap, + new_bindings: HashMap, new_dollar: Option, new_this: Option, new_super_obj: Option, @@ -83,14 +83,7 @@ let bindings = if new_bindings.is_empty() { self.0.bindings.clone() } else { - let mut new = HashMap::new(); // = self.0.bindings.clone(); - for (k, v) in self.0.bindings.iter() { - new.insert(k.clone(), v.clone()); - } - for (k, v) in new_bindings.into_iter() { - new.insert(k, v.evaluate(this.clone(), super_obj.clone())?); - } - Rc::new(new) + self.0.bindings.extend(new_bindings) }; Ok(Context(Rc::new(ContextInternals { dollar, @@ -99,6 +92,21 @@ bindings, }))) } + pub fn extend_unbound( + &self, + new_bindings: HashMap, + new_dollar: Option, + new_this: Option, + new_super_obj: Option, + ) -> Result { + let this = new_this.or_else(|| self.0.this.clone()); + let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone()); + let mut new = HashMap::new(); + for (k, v) in new_bindings.into_iter() { + new.insert(k, v.evaluate(this.clone(), super_obj.clone())?); + } + self.extend(new, new_dollar, this, super_obj) + } } impl Default for Context { --- a/crates/jsonnet-evaluator/src/error.rs +++ b/crates/jsonnet-evaluator/src/error.rs @@ -7,6 +7,11 @@ TypeMismatch(&'static str, Vec, ValType), NoSuchField(String), + UnknownFunctionParameter(String), + BindingParameterASecondTime(String), + TooManyArgsFunctionHas(usize), + FunctionParameterNotBoundInCall(String), + RuntimeError(String), StackOverflow, FractionalIndex, --- a/crates/jsonnet-evaluator/src/evaluate.rs +++ b/crates/jsonnet-evaluator/src/evaluate.rs @@ -1,12 +1,12 @@ use crate::{ - binding, context_creator, create_error, function_default, function_rhs, future_wrapper, - lazy_val, push, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, - Result, Val, + binding, context_creator, create_error, future_wrapper, lazy_val, push, Context, + ContextCreator, FuncDesc, LazyBinding, ObjMember, ObjValue, Result, Val, }; use closure::closure; use jsonnet_parser::{ - ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember, ForSpecData, - IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType, Visibility, + el, Arg, ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, FieldMember, + ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType, + Visibility, }; use std::{ collections::{BTreeMap, HashMap}, @@ -15,16 +15,16 @@ pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, LazyBinding) { let b = b.clone(); - if let Some(args) = &b.params { - let args = args.clone(); + if let Some(params) = &b.params { + let params = params.clone(); ( b.name.clone(), LazyBinding::Bindable(Rc::new(move |this, super_obj| { Ok(lazy_val!( - closure!(clone b, clone args, clone context_creator, || Ok(evaluate_method( + closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method( context_creator.0(this.clone(), super_obj.clone())?, - &b.value, - args.clone() + params.clone(), + b.value.clone(), ))) )) })), @@ -46,13 +46,8 @@ } } -pub fn evaluate_method(ctx: Context, expr: &LocExpr, arg_spec: ParamsDesc) -> Val { - Val::Func(FuncDesc { - ctx, - params: arg_spec, - eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))), - eval_default: function_default!(closure!(|ctx, default| evaluate(ctx, &default))), - }) +pub fn evaluate_method(ctx: Context, params: ParamsDesc, body: LocExpr) -> Val { + Val::Func(FuncDesc { ctx, params, body }) } pub fn evaluate_field_name( @@ -214,7 +209,7 @@ let future_this = FutureObjValue::new(); let context_creator = context_creator!( closure!(clone context, clone new_bindings, clone future_this, |this: Option, super_obj: Option| { - Ok(context.clone().extend( + Ok(context.clone().extend_unbound( new_bindings.clone().unwrap(), context.clone().dollar().clone().or_else(||this.clone()), Some(this.unwrap()), @@ -292,8 +287,8 @@ // TODO: Assert Ok(evaluate_method( context_creator.0(this, super_obj)?, - &value.clone(), params.clone(), + value.clone(), )) }) ), @@ -393,7 +388,7 @@ } let context = context - .extend(new_bindings, None, None, None)? + .extend_unbound(new_bindings, None, None, None)? .into_future(future_context); evaluate(context, &returned.clone())? } @@ -417,7 +412,7 @@ &evaluate(context.clone(), s)?, &Val::Obj(evaluate_object(context, t.clone())?), )?, - Apply(value, ArgsDesc(args), tailstrict) => { + Apply(value, args, tailstrict) => { let value = evaluate(context.clone(), value)?.unwrap_if_lazy()?; match value { Val::Intristic(ns, name) => match (&ns as &str, &name as &str) { @@ -448,7 +443,13 @@ assert!(v >= 0.0); let mut out = Vec::with_capacity(v as usize); for i in 0..v as usize { - out.push(d.evaluate(vec![(None, Val::Num(i as f64))])?) + let call_ctx = + Context::new().with_var("v".to_owned(), Val::Num(i as f64))?; + out.push(d.evaluate( + call_ctx, + &ArgsDesc(vec![Arg(None, el!(Expr::Var("v".to_owned())))]), + true, + )?) } Val::Arr(out) } else { @@ -536,31 +537,7 @@ }, Val::Func(f) => { let body = #[inline(always)] - || { - f.evaluate( - args.clone() - .into_iter() - .map( - #[inline(always)] - move |a| { - Ok(( - a.clone().0, - if *tailstrict { - Val::Lazy(LazyVal::new_resolved(evaluate( - context.clone(), - &a.1, - )?)) - } else { - Val::Lazy(lazy_val!( - closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1)) - )) - }, - )) - }, - ) - .collect::>>()?, - ) - }; + || f.evaluate(context, args, *tailstrict); if *tailstrict { body()? } else { @@ -570,7 +547,7 @@ _ => panic!("{:?} is not a function", value), } } - Function(params, body) => evaluate_method(context, body, params.clone()), + Function(params, body) => evaluate_method(context, params.clone(), body.clone()), AssertExpr(AssertStmt(value, msg), returned) => { let assertion_result = push(value.clone(), "assertion condition".to_owned(), || { evaluate(context.clone(), &value)? --- a/crates/jsonnet-evaluator/src/function.rs +++ b/crates/jsonnet-evaluator/src/function.rs @@ -0,0 +1,80 @@ +use crate::{create_error, evaluate, lazy_val, resolved_lazy_val, Context, Error, Result}; +use closure::closure; +use jsonnet_parser::{ArgsDesc, ParamsDesc}; +use std::collections::HashMap; + +/// Creates correct [context](Context) for function body evaluation, returning error on invalid call +/// +/// * `ctx` used for passed argument expressions execution, and for body execution (if `body_ctx` is not set) +/// * `body_ctx` used for default parameter values execution, and for body execution (if set) +/// * `params` function parameters definition +/// * `args` passed function arguments +/// * `tailstruct` if true - function arguments is eager executed, otherwise - lazy +pub fn parse_function_call( + ctx: Context, + body_ctx: Option, + params: &ParamsDesc, + args: &ArgsDesc, + tailstrict: bool, +) -> Result { + inline_parse_function_call(ctx, body_ctx, params, args, tailstrict) +} + +/// See [parse_function_call](parse_function_call) +/// +/// ## Notes +/// This function is always inlined for tailstrict +#[inline(always)] +pub(crate) fn inline_parse_function_call( + ctx: Context, + body_ctx: Option, + params: &ParamsDesc, + args: &ArgsDesc, + tailstrict: bool, +) -> Result { + let mut out = HashMap::new(); + let mut positioned_args = vec![None; params.0.len()]; + for (id, arg) in args.iter().enumerate() { + let idx = if let Some(name) = &arg.0 { + params.iter().position(|p| &p.0 == name).ok_or_else(|| { + create_error::<()>(Error::UnknownFunctionParameter(name.clone())) + .err() + .unwrap() + })? + } else { + id + }; + + if idx >= params.len() { + create_error(Error::TooManyArgsFunctionHas(params.len()))?; + } + if positioned_args[idx].is_some() { + create_error(Error::BindingParameterASecondTime(params[idx].0.clone()))?; + } + positioned_args[idx] = Some(arg.1.clone()); + } + // Fill defaults + for (id, p) in params.iter().enumerate() { + let (ctx, expr) = if let Some(arg) = &positioned_args[id] { + (ctx.clone(), arg) + } else if let Some(default) = &p.1 { + ( + body_ctx + .clone() + .expect("no default context set for call with defined default parameter value"), + default, + ) + } else { + create_error(Error::FunctionParameterNotBoundInCall(p.0.clone()))?; + unreachable!() + }; + let val = if tailstrict { + resolved_lazy_val!(evaluate(ctx.clone(), expr)?) + } else { + lazy_val!(closure!(clone ctx, clone expr, ||evaluate(ctx.clone(), &expr))) + }; + out.insert(p.0.clone(), val); + } + + Ok(body_ctx.unwrap_or(ctx).extend(out, None, None, None)?) +} --- a/crates/jsonnet-evaluator/src/lib.rs +++ b/crates/jsonnet-evaluator/src/lib.rs @@ -8,6 +8,7 @@ mod error; mod evaluate; mod function; +mod map; mod obj; mod val; @@ -15,6 +16,7 @@ pub use dynamic::*; pub use error::*; pub use evaluate::*; +pub use function::parse_function_call; use jsonnet_parser::*; pub use obj::*; use std::{cell::RefCell, collections::HashMap, fmt::Debug, path::PathBuf, rc::Rc}; @@ -24,17 +26,12 @@ Binding, binding, dyn Fn(Option, Option) -> Result -); -rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Result); -rc_fn_helper!( - FunctionDefault, - function_default, - dyn Fn(Context, LocExpr) -> Result ); +type BindableFn = dyn Fn(Option, Option) -> Result; #[derive(Clone)] pub enum LazyBinding { - Bindable(Rc, Option) -> Result>), + Bindable(Rc), Bound(LazyVal), } @@ -59,7 +56,7 @@ impl Default for EvaluationSettings { fn default() -> Self { EvaluationSettings { - max_stack_frames: 500, + max_stack_frames: 200, max_stack_trace_size: 20, } } @@ -181,7 +178,7 @@ self.0.globals.borrow_mut().insert(name, value); } - pub fn add_stdlib(&self) { + pub fn with_stdlib(&self) -> &Self { self.begin_state(); use jsonnet_stdlib::STDLIB_STR; if cfg!(feature = "serialized-stdlib") { @@ -199,6 +196,7 @@ let val = self.evaluate_file(&PathBuf::from("std.jsonnet")).unwrap(); self.add_global("std".to_owned(), val); self.end_state(); + self } pub fn create_default_context(&self) -> Result { @@ -210,7 +208,7 @@ LazyBinding::Bound(resolved_lazy_val!(value.clone())), ); } - Context::new().extend(new_bindings, None, None, None) + Context::new().extend_unbound(new_bindings, None, None, None) } #[inline(always)] @@ -297,7 +295,7 @@ #[test] fn eval_state_standard() { let state = EvaluationState::default(); - state.add_stdlib(); + state.with_stdlib(); assert_eq!( state .parse_evaluate_raw(r#"std.assertEqual(std.base64("test"), "dGVzdA==")"#) @@ -308,106 +306,47 @@ macro_rules! eval { ($str: expr) => { - evaluate( - Context::new(), - EvaluationState::default(), - &parse( - $str, - &ParserSettings { - loc_data: true, - file_name: "test.jsonnet".to_owned(), - }, - ) - .unwrap(), - ) + EvaluationState::default() + .with_stdlib() + .parse_evaluate_raw($str) + .unwrap() }; } - - macro_rules! eval_stdlib { + macro_rules! eval_json { ($str: expr) => {{ - let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";"; - evaluate( - Context::new(), - EvaluationState::default(), - &parse( - &(std + $str), - &ParserSettings { - loc_data: true, - file_name: "test.jsonnet".to_owned(), - }, - ) - .unwrap(), - ) + let evaluator = EvaluationState::default(); + evaluator.with_stdlib(); + let val = evaluator.parse_evaluate_raw($str).unwrap(); + evaluator.add_global("__tmp__to_yaml__".to_owned(), val); + evaluator + .parse_evaluate_raw("std.manifestJsonEx(__tmp__to_yaml__, \"\")") + .unwrap() + .try_cast_str("there should be json string") + .unwrap() + .clone() + .replace("\n", "") }}; } + /// Asserts given code returns `true` macro_rules! assert_eval { ($str: expr) => { - assert_eq!( - evaluate( - Context::new(), - EvaluationState::default(), - &parse( - $str, - &ParserSettings { - loc_data: true, - file_name: "test.jsonnet".to_owned(), - } - ) - .unwrap() - ), - Val::Bool(true) - ) + assert_eq!(eval!($str), Val::Bool(true)) }; } - macro_rules! assert_json { - ($str: expr, $out: expr) => { - assert_eq!( - format!( - "{}", - evaluate( - Context::new(), - EvaluationState::default(), - &parse( - $str, - &ParserSettings { - loc_data: true, - file_name: "test.jsonnet".to_owned(), - } - ) - .unwrap() - ) - ), - $out - ) + + /// Asserts given code returns `false` + macro_rules! assert_eval_neg { + ($str: expr) => { + assert_eq!(eval!($str), Val::Bool(false)) }; } - macro_rules! assert_json_stdlib { + macro_rules! assert_json { ($str: expr, $out: expr) => { - assert_eq!(format!("{}", eval_stdlib!($str)), $out) - }; - } - macro_rules! assert_eval_neg { - ($str: expr) => { - assert_eq!( - evaluate( - Context::new(), - EvaluationState::default(), - &parse( - $str, - &ParserSettings { - loc_data: true, - file_name: "test.jsonnet".to_owned(), - } - ) - .unwrap() - ), - Val::Bool(false) - ) + assert_eq!(eval_json!($str), $out.replace("\t", "")) }; } - /* /// Sanity checking, before trusting to another tests #[test] fn equality_operator() { @@ -435,6 +374,23 @@ } #[test] + fn function_contexts() { + assert_eval!( + r#" + local k = { + t(name = self.h): [self.h, name], + h: 3, + }; + local f = { + t: k.t(), + h: 4, + }; + f.t[0] == f.t[1] + "# + ); + } + + #[test] fn local() { assert_eval!("local a = 2; local b = 3; a + b == 5"); assert_eval!("local a = 1, b = a + 1; a + b == 3"); @@ -446,19 +402,20 @@ assert_json!("local a = {a:error 'test'}; {}", r#"{}"#); } + /// FIXME: This test gets stackoverflow in debug build #[test] fn object_inheritance() { - assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#); + assert_json!("{a: self.b} + {b:3}", r#"{"a": 3,"b": 3}"#); } #[test] fn test_object() { - assert_json!("{a:2}", r#"{"a":2}"#); - assert_json!("{a:2+2}", r#"{"a":4}"#); - assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#); - assert_json!("{b:3}+{b:2}", r#"{"b":2}"#); - assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#); - assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#); + assert_json!("{a:2}", r#"{"a": 2}"#); + assert_json!("{a:2+2}", r#"{"a": 4}"#); + assert_json!("{a:2}+{b:2}", r#"{"a": 2,"b": 2}"#); + assert_json!("{b:3}+{b:2}", r#"{"b": 2}"#); + assert_json!("{b:3}+{b+:2}", r#"{"b": 5}"#); + assert_json!("local test='a'; {[test]:2}", r#"{"a": 2}"#); assert_json!( r#" { @@ -466,7 +423,7 @@ welcome: "Hello " + self.name + "!", } "#, - r#"{"name":"Alice","welcome":"Hello Alice!"}"# + r#"{"name": "Alice","welcome": "Hello Alice!"}"# ); assert_json!( r#" @@ -477,7 +434,7 @@ name: "Bob" } "#, - r#"{"name":"Bob","welcome":"Hello Bob!"}"# + r#"{"name": "Bob","welcome": "Hello Bob!"}"# ); } @@ -501,11 +458,11 @@ #[test] fn object_locals() { - assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#); - assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#); + assert_json!(r#"{local a = 3, b: a}"#, r#"{"b": 3}"#); + assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b": 3}"#); assert_json!( r#"{local a = function (b) {[b]:4}, test: a("test")}"#, - r#"{"test":{"test":4}}"# + r#"{"test": {"test": 4}}"# ); } @@ -529,7 +486,7 @@ fn indirect_self() { // `self` assigned to `me` was lost when being // referenced from field - eval_stdlib!( + eval!( r#"{ local me = self, a: 3, @@ -541,47 +498,47 @@ // We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly #[test] fn std_assert_ok() { - eval_stdlib!("std.assertEqual(4.5 << 2, 16)"); + eval!("std.assertEqual(4.5 << 2, 16)"); } #[test] #[should_panic] fn std_assert_failure() { - eval_stdlib!("std.assertEqual(4.5 << 2, 15)"); + eval!("std.assertEqual(4.5 << 2, 15)"); } #[test] fn string_is_string() { assert_eq!( - eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"), + eval!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"), Val::Bool(false) ); } #[test] fn base64_works() { - assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#); + assert_json!(r#"std.base64("test")"#, r#""dGVzdA==""#); } #[test] fn utf8_chars() { - assert_json_stdlib!( + assert_json!( r#"local c="😎";{c:std.codepoint(c),l:std.length(c)}"#, - r#"{"c":128526,"l":1}"# + r#"{"c": 128526,"l": 1}"# ) } #[test] fn json() { - assert_json_stdlib!( + assert_json!( r#"std.manifestJsonEx({a:3, b:4, c:6},"")"#, - r#""{\n"a": 3,\n"b": 4,\n"c": 6\n}""# + r#""{\n\"a\": 3,\n\"b\": 4,\n\"c\": 6\n}""# ); } #[test] fn test() { - assert_json_stdlib!( + assert_json!( r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#, "[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]" ); @@ -617,5 +574,4 @@ "# ); } - */ } --- /dev/null +++ b/crates/jsonnet-evaluator/src/map.rs @@ -0,0 +1,46 @@ +use std::{borrow::Borrow, collections::HashMap, hash::Hash, rc::Rc}; + +#[derive(Default, Debug)] +struct LayeredHashMapInternals { + parent: Option>, + current: HashMap, +} + +#[derive(Debug)] +pub struct LayeredHashMap(Rc>); + +impl LayeredHashMap { + pub fn extend(&self, new_layer: HashMap) -> Self { + let super_map = self.clone(); + LayeredHashMap(Rc::new(LayeredHashMapInternals { + parent: Some(super_map), + current: new_layer, + })) + } + + pub fn get(&self, key: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq, + { + (self.0) + .current + .get(&key) + .or_else(|| self.0.parent.as_ref().and_then(|p| p.get(key))) + } +} + +impl Clone for LayeredHashMap { + fn clone(&self) -> Self { + LayeredHashMap(self.0.clone()) + } +} + +impl Default for LayeredHashMap { + fn default() -> Self { + LayeredHashMap(Rc::new(LayeredHashMapInternals { + parent: None, + current: HashMap::new(), + })) + } +} --- a/crates/jsonnet-evaluator/src/val.rs +++ b/crates/jsonnet-evaluator/src/val.rs @@ -1,11 +1,9 @@ use crate::{ - create_error, Context, Error, FunctionDefault, FunctionRhs, LazyBinding, ObjValue, Result, + create_error, evaluate, function::inline_parse_function_call, Context, Error, ObjValue, Result, }; -use closure::closure; -use jsonnet_parser::{Param, ParamsDesc}; +use jsonnet_parser::{ArgsDesc, LocExpr, ParamsDesc}; use std::{ cell::RefCell, - collections::HashMap, fmt::{Debug, Display}, rc::Rc, }; @@ -73,47 +71,21 @@ pub struct FuncDesc { pub ctx: Context, pub params: ParamsDesc, - pub eval_rhs: FunctionRhs, - pub eval_default: FunctionDefault, + pub body: LocExpr, } impl FuncDesc { // TODO: Check for unset variables /// This function is always inlined to make tailstrict work #[inline(always)] - pub fn evaluate(&self, args: Vec<(Option, Val)>) -> Result { - let mut new_bindings: HashMap = HashMap::new(); - let future_ctx = Context::new_future(); - - for Param(name, default) in self.params.with_defaults() { - let default = default.unwrap(); - let eval_default = self.eval_default.clone(); - new_bindings.insert( - name, - 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(), - 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(), - LazyBinding::Bound(resolved_lazy_val!(val.clone())), - ); - } - } - let ctx = self - .ctx - .extend(new_bindings, None, None, None)? - .into_future(future_ctx); - self.eval_rhs.0(ctx) + pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result { + let ctx = inline_parse_function_call( + call_ctx, + Some(self.ctx.clone()), + &self.params, + args, + tailstrict, + )?; + evaluate(ctx, &self.body) } }