From abae2f2e28fb70605894d3befd6a98a9045d5a64 Mon Sep 17 00:00:00 2001 From: Лач Date: Sat, 30 May 2020 20:59:19 +0000 Subject: [PATCH] refactor(evaluator): use closures where possible --- --- a/crates/jsonnet-evaluator/Cargo.toml +++ b/crates/jsonnet-evaluator/Cargo.toml @@ -8,6 +8,7 @@ [dependencies] jsonnet-parser = { path = "../jsonnet-parser" } +closure = "0.3.0" [dev-dependencies] jsonnet-stdlib = { version = "0.1.0", path = "../jsonnet-stdlib" } --- a/crates/jsonnet-evaluator/src/binding.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::{ - ArgsBindingLazyVal, BoxedContextCreator, BoxedLazyVal, NoArgsBindingLazyVal, ObjValue, Val, -}; -use jsonnet_parser::{Expr, ParamsDesc}; -use std::{fmt::Debug, rc::Rc}; - -pub trait Binding: Debug { - fn evaluate(&self, this: Option, super_obj: Option) -> Val; -} -pub type BoxedBinding = Rc; - -#[derive(Debug)] -pub struct NoArgsBinding { - pub expr: Expr, - pub context_creator: BoxedContextCreator, -} -impl Binding for NoArgsBinding { - fn evaluate(&self, this: Option, super_obj: Option) -> Val { - Val::Lazy(BoxedLazyVal(Rc::new(NoArgsBindingLazyVal { - context_creator: self.context_creator.clone(), - expr: self.expr.clone(), - this, - super_obj, - }))) - } -} -#[derive(Debug)] -pub struct ArgsBinding { - pub expr: Expr, - pub args: ParamsDesc, - pub context_creator: BoxedContextCreator, -} -impl Binding for ArgsBinding { - fn evaluate(&self, this: Option, super_obj: Option) -> Val { - Val::Lazy(BoxedLazyVal(Rc::new(ArgsBindingLazyVal { - context_creator: self.context_creator.clone(), - expr: self.expr.clone(), - args: self.args.clone(), - this, - super_obj, - }))) - } -} --- a/crates/jsonnet-evaluator/src/ctx.rs +++ b/crates/jsonnet-evaluator/src/ctx.rs @@ -1,20 +1,11 @@ -use crate::{dummy_debug, future_wrapper, BoxedBinding, ObjValue}; +use crate::{future_wrapper, rc_fn_helper, Binding, ObjValue}; use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; - -pub trait ContextCreator: Debug { - fn create_context(&self, this: &Option, super_obj: &Option) -> Context; -} -pub type BoxedContextCreator = Rc; -#[derive(Debug)] -pub struct ConstantContextCreator { - pub context: FutureContext, -} -impl ContextCreator for ConstantContextCreator { - fn create_context(&self, _this: &Option, _super_obj: &Option) -> Context { - self.context.clone().unwrap() - } -} +rc_fn_helper!( + ContextCreator, + context_creator, + dyn Fn(Option, Option) -> Context +); future_wrapper!(Context, FutureContext); @@ -23,10 +14,16 @@ dollar: Option, this: Option, super_obj: Option, - bindings: Rc>>, + bindings: Rc>>, } pub struct Context(Rc); -dummy_debug!(Context); +impl Debug for Context { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Context") + .field("this", &self.0.this.clone().map(|e| Rc::as_ptr(&e.0))) + .finish() + } +} impl Context { pub fn new_future() -> FutureContext { FutureContext(Rc::new(RefCell::new(None))) @@ -53,12 +50,12 @@ })) } - pub fn binding(&self, name: &str) -> BoxedBinding { + pub fn binding(&self, name: &str) -> Binding { self.0 .bindings .borrow() .get(name) - .map(|e| e.clone()) + .cloned() .unwrap_or_else(|| { panic!("can't find {} in {:?}", name, self); }) @@ -72,14 +69,15 @@ pub fn extend( &self, - new_bindings: HashMap, + new_bindings: HashMap, new_dollar: Option, new_this: Option, new_super_obj: Option, ) -> Context { - let dollar = new_dollar.or(self.0.dollar.clone()); - let this = new_this.or(self.0.this.clone()); - let super_obj = new_super_obj.or(self.0.super_obj.clone()); + println!("Extend with {:?} {:?}", new_dollar, new_this); + let dollar = new_dollar.or_else(|| self.0.dollar.clone()); + let this = new_this.or_else(|| self.0.this.clone()); + let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone()); let bindings = if new_bindings.is_empty() { self.0.bindings.clone() } else { @@ -97,6 +95,13 @@ })) } } + +impl Default for Context { + fn default() -> Self { + Self::new() + } +} + impl PartialEq for Context { fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) --- a/crates/jsonnet-evaluator/src/dynamic.rs +++ b/crates/jsonnet-evaluator/src/dynamic.rs @@ -1,23 +1,4 @@ #[macro_export] -macro_rules! dynamic_wrapper { - ($orig: ident, $wrapper: ident) => { - #[derive(Debug, Clone)] - pub struct $wrapper(pub std::rc::Rc); - impl std::ops::Deref for $wrapper { - type Target = dyn $orig; - fn deref(&self) -> &Self::Target { - &*self.0 - } - } - impl std::cmp::PartialEq for $wrapper { - fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) - } - } - }; -} - -#[macro_export] macro_rules! future_wrapper { ($orig: ty, $wrapper: ident) => { #[derive(Debug, Clone)] @@ -39,17 +20,36 @@ self.unwrap() } } + impl Default for $wrapper { + fn default() -> Self { + Self::new() + } + } }; } #[macro_export] -macro_rules! dummy_debug { - ($struct: ident) => { - impl std::fmt::Debug for $struct { +macro_rules! rc_fn_helper { + ($name: ident, $macro_name: ident, $fn: ty) => { + #[derive(Clone)] + #[doc = "Function wrapper"] + pub struct $name(pub std::rc::Rc<$fn>); + impl std::fmt::Debug for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct(std::stringify!($struct)) - .finish_non_exhaustive() + f.debug_struct(std::stringify!($name)).finish() + } + } + impl std::cmp::PartialEq for $name { + fn eq(&self, other: &$name) -> bool { + std::ptr::eq(&self.0, &other.0) } } + #[doc = "Macro to ease wrapper creation"] + #[macro_export] + macro_rules! $macro_name { + ($val: expr) => { + $crate::$name(std::rc::Rc::new($val)) + }; + } }; } --- a/crates/jsonnet-evaluator/src/evaluate.rs +++ b/crates/jsonnet-evaluator/src/evaluate.rs @@ -1,70 +1,53 @@ -use crate::BoxedLazyVal; use crate::{ - bool_val, ArgsBinding, BoxedBinding, BoxedContextCreator, ConstantContextCreator, Context, - FuncDesc, FunctionDefault, FunctionRhs, NoArgsBinding, Val, -}; -use crate::{ - future_wrapper, BoxedFunctionDefault, BoxedFunctionRhs, ContextCreator, ObjMember, ObjValue, - PlainLazyVal, + binding, bool_val, context_creator, function_default, function_rhs, future_wrapper, lazy_val, + Binding, Context, ContextCreator, FuncDesc, ObjMember, ObjValue, Val, }; +use closure::closure; use jsonnet_parser::{ ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc, - Visibility, + UnaryOpType, Visibility, }; use std::{ - cell::RefCell, collections::{BTreeMap, HashMap}, rc::Rc, }; -pub fn evaluate_binding<'t>( - b: &BindSpec, - context_creator: BoxedContextCreator, -) -> (String, BoxedBinding) { +pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, Binding) { + let b = b.clone(); if let Some(args) = &b.params { + let args = args.clone(); ( b.name.clone(), - Rc::new(ArgsBinding { - expr: *b.value.clone(), - args: args.clone(), - context_creator: context_creator.clone(), - }), + binding!(move |this, super_obj| Val::Lazy(lazy_val!( + closure!(clone b, clone args, clone context_creator, || evaluate_method( + context_creator.0(this.clone(), super_obj.clone()), + &b.value, + args.clone() + )) + ))), ) } else { ( b.name.clone(), - Rc::new(NoArgsBinding { - expr: *b.value.clone(), - context_creator: context_creator.clone(), - }) as BoxedBinding, + binding!(move |this, super_obj| { + println!("Evaluating binding"); + Val::Lazy(lazy_val!( + closure!(clone context_creator, clone b, || evaluate( + context_creator.0(this.clone(), super_obj.clone()), + &b.value + )) + )) + }), ) } } -#[derive(Debug)] -struct MethodRhs { - rhs: Expr, -} -impl FunctionRhs for MethodRhs { - fn evaluate(&self, ctx: Context) -> Val { - evaluate(ctx, &self.rhs) - } -} - -#[derive(Debug)] -struct MethodDefault {} -impl FunctionDefault for MethodDefault { - fn default(&self, ctx: Context, expr: Expr) -> Val { - evaluate(ctx, &expr) - } -} - pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val { Val::Func(FuncDesc { ctx, params: arg_spec, - eval_rhs: BoxedFunctionRhs(Rc::new(MethodRhs { rhs: expr.clone() })), - eval_default: BoxedFunctionDefault(Rc::new(MethodDefault {})), + eval_rhs: function_rhs!(closure!(clone expr, |ctx| evaluate(ctx, &expr))), + eval_default: function_default!(|ctx, default| evaluate(ctx, &default)), }) } @@ -74,7 +57,7 @@ jsonnet_parser::FieldName::Dyn(expr) => { let name = evaluate(context, expr).unwrap_if_lazy(); match name { - Val::Str(n) => n.clone(), + Val::Str(n) => n, _ => panic!( "dynamic field name can be only evaluated to 'string', got: {:?}", name @@ -84,10 +67,19 @@ } } +pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Val { + match (op, b) { + (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.0()), + (UnaryOpType::Not, Val::Literal(LiteralType::True)) => Val::Literal(LiteralType::False), + (UnaryOpType::Not, Val::Literal(LiteralType::False)) => Val::Literal(LiteralType::True), + (op, o) => panic!("unary op not implemented: {:?} {:?}", op, o), + } +} + pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val { match (a, op, b) { - (Val::Lazy(l), o, r) => evaluate_binary_op(&l.evaluate(), o, r), - (l, o, Val::Lazy(r)) => evaluate_binary_op(l, o, &r.evaluate()), + (Val::Lazy(a), o, b) => evaluate_binary_op(&a.0(), o, b), + (a, o, Val::Lazy(b)) => evaluate_binary_op(a, o, &b.0()), (Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2), (Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2), @@ -119,8 +111,8 @@ (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2), (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2), - (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val(v1 == v2), - (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val(v1 != v2), + (Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val((v1 - v2).abs() < f64::EPSILON), + (Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val((v1 - v2).abs() > f64::EPSILON), (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => { Val::Num(((*v1 as i32) & (*v2 as i32)) as f64) @@ -135,48 +127,44 @@ } } -future_wrapper!(HashMap, FutureNewBindings); - -#[derive(Debug)] -pub struct ObjectContextCreator { - original: Context, - future_bindings: FutureNewBindings, -} - -impl ContextCreator for ObjectContextCreator { - fn create_context(&self, this: &Option, super_obj: &Option) -> Context { - self.original.extend( - self.future_bindings.clone().unwrap(), - self.original.dollar().clone().or_else(|| this.clone()), - this.clone(), - super_obj.clone(), - ) - } -} +future_wrapper!(HashMap, FutureNewBindings); +future_wrapper!(ObjValue, FutureObjValue); // TODO: Asserts pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue { match object { ObjBody::MemberList(members) => { let future_bindings = FutureNewBindings::new(); - let binding_context_creator = Rc::new(ObjectContextCreator { - future_bindings: future_bindings.clone(), - original: context.clone(), - }); - let mut bindings: HashMap = HashMap::new(); + let future_this = FutureObjValue::new(); + let context_creator = context_creator!( + closure!(clone context, clone future_bindings, |this: Option, super_obj: Option| { + println!("Context created"); + context.clone().extend( + future_bindings.clone().unwrap(), + context.clone().dollar().clone().or_else(||this.clone()), + this, + super_obj + ) + }) + ); + let mut bindings: HashMap = HashMap::new(); for (n, b) in members .iter() .filter_map(|m| match m { Member::BindStmt(b) => Some(b.clone()), _ => None, }) - .map(|b| evaluate_binding(&b, binding_context_creator.clone())) + .map(|b| { + evaluate_binding(&b, context_creator.clone()) + }) { bindings.insert(n, b); } - let bindings = future_bindings.fill(bindings); + future_bindings.fill(bindings); + + println!("Bindings filled"); let mut new_members = BTreeMap::new(); - for member in members.iter() { + for member in members.into_iter() { match member { Member::Field(FieldMember { name, @@ -185,16 +173,22 @@ visibility, value, }) => { - let name = evaluate_field_name(context.clone(), name); + let name = evaluate_field_name(context.clone(), &name); new_members.insert( name, ObjMember { - add: *plus, + add: plus, visibility: visibility.clone(), - invoke: Rc::new(NoArgsBinding { - context_creator: binding_context_creator.clone(), - expr: value.clone(), - }), + invoke: binding!( + closure!(clone value, clone context_creator, clone future_this, |this, super_obj| { + // FIXME: I should take "this" instead of "future_this" there? + // TODO: Assert + evaluate( + context_creator.0(this, super_obj), + &value, + ) + }) + ), }, ); } @@ -204,26 +198,31 @@ value, .. }) => { - let name = evaluate_field_name(context.clone(), name); + let name = evaluate_field_name(context.clone(), &name); new_members.insert( name, ObjMember { add: false, visibility: Visibility::Hidden, - invoke: Rc::new(ArgsBinding { - expr: value.clone(), - args: params.clone(), - context_creator: binding_context_creator.clone(), - }), + invoke: binding!( + closure!(clone value, clone context_creator, clone future_this, |this, super_obj| { + // FIXME: I should take "this" instead of "future_this" there? + // TODO: Assert + evaluate_method( + context_creator.0(this, super_obj), + &value.clone(), + params.clone(), + ) + }) + ), }, ); } Member::BindStmt(_) => {} Member::AssertStmt(_) => {} - _ => todo!(), } } - ObjValue::new(None, Rc::new(new_members)) + future_this.fill(ObjValue::new(None, Rc::new(new_members))) } _ => todo!(), } @@ -232,6 +231,21 @@ pub fn evaluate(context: Context, expr: &Expr) -> Val { use Expr::*; match &*expr { + Literal(LiteralType::This) => { + println!("{:?}", context.this()); + Val::Obj( + context + .this() + .clone() + .unwrap_or_else(|| panic!("this not found")), + ) + } + Literal(LiteralType::Super) => Val::Obj( + context + .super_obj() + .clone() + .unwrap_or_else(|| panic!("super not found")), + ), Literal(t) => Val::Literal(t.clone()), Parened(e) => evaluate(context, e), Str(v) => Val::Str(v.clone()), @@ -239,45 +253,34 @@ BinaryOp(v1, o, v2) => { evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2)) } + UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)), Var(name) => { let variable = context.binding(&name); - let val = variable.evaluate(None, None); - val + variable.0(None, None).unwrap_if_lazy() } Index(box value, box index) => { match ( evaluate(context.clone(), value).unwrap_if_lazy(), - evaluate(context.clone(), index), + evaluate(context, index), ) { - (Val::Literal(LiteralType::Super), _idx) => todo!(), - (Val::Literal(LiteralType::This), idx) => match &idx.unwrap_if_lazy() { - Val::Str(str) => context - .this() - .clone() - .unwrap_or_else(|| panic!("'this' is not defined in current context")) - .get_raw(str, None) - .unwrap_or_else(|| { - panic!( - "key {} not found in current context 'this' ({:?})", - str, - context.this() - ) - }), - _ => panic!("bad index"), - }, (Val::Obj(v), Val::Str(s)) => v - .get_raw(&s, None) + .get(&s) .unwrap_or_else(|| panic!("{} not found in {:?}", s, v)), + (Val::Arr(v), Val::Num(n)) => v + .get(n as usize) + .unwrap_or_else(|| panic!("out of bounds")) + .clone(), (v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()), } } LocalExpr(bindings, returned) => { - let mut new_bindings: HashMap = HashMap::new(); + let mut new_bindings: HashMap = HashMap::new(); let future_context = Context::new_future(); - let context_creator = Rc::new(ConstantContextCreator { - context: future_context.clone(), - }); + let context_creator = context_creator!( + closure!(clone future_context, |_, _| future_context.clone().unwrap()) + ); + for (k, v) in bindings .iter() .map(move |b| evaluate_binding(b, context_creator.clone())) @@ -299,11 +302,10 @@ .into_iter() .map(|a| { ( - a.0, - Val::Lazy(BoxedLazyVal(Rc::new(PlainLazyVal { - context: context.clone(), - expr: *a.1, - }))), + a.clone().0, + Val::Lazy(lazy_val!( + closure!(clone context, clone a, || evaluate(context.clone(), &a.clone().1)) + )), ) }) .collect(), @@ -318,9 +320,9 @@ cond_then, cond_else, } => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() { - Val::Literal(LiteralType::True) => evaluate(context.clone(), cond_then), + Val::Literal(LiteralType::True) => evaluate(context, cond_then), Val::Literal(LiteralType::False) => match cond_else { - Some(v) => evaluate(context.clone(), v), + Some(v) => evaluate(context, v), None => Val::Literal(LiteralType::False), }, v => panic!("if condition evaluated to {:?} (boolean needed instead)", v), --- a/crates/jsonnet-evaluator/src/lib.rs +++ b/crates/jsonnet-evaluator/src/lib.rs @@ -1,44 +1,50 @@ #![feature(box_syntax, box_patterns)] #![feature(type_alias_impl_trait)] #![feature(debug_non_exhaustive)] - -mod binding; +#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)] mod ctx; mod dynamic; mod evaluate; mod obj; mod val; -pub use binding::*; pub use ctx::*; pub use dynamic::*; pub use evaluate::*; use jsonnet_parser::*; pub use obj::*; -use std::fmt::Debug; -use std::rc::Rc; pub use val::*; -pub trait FunctionRhs: Debug { - fn evaluate(&self, ctx: Context) -> Val; -} -dynamic_wrapper!(FunctionRhs, BoxedFunctionRhs); - -pub trait FunctionDefault: Debug { - fn default(&self, ctx: Context, expr: Expr) -> Val; -} -dynamic_wrapper!(FunctionDefault, BoxedFunctionDefault); +rc_fn_helper!( + Binding, + binding, + dyn Fn(Option, Option) -> Val +); +rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val); +rc_fn_helper!( + FunctionDefault, + function_default, + dyn Fn(Context, Expr) -> Val +); #[cfg(test)] pub mod tests { use super::{evaluate, Context, Val}; use jsonnet_parser::*; - // macro_rules! eval { - // ($str: expr) => { - // evaluate(Context::new(), &parse($str).unwrap()) - // }; - // } + macro_rules! eval { + ($str: expr) => { + evaluate(Context::new(), &parse($str).unwrap()) + }; + } + + macro_rules! eval_stdlib { + ($str: expr) => { + let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";"; + evaluate(Context::new(), &parse(&(std + $str)).unwrap()) + }; + } + macro_rules! assert_eval { ($str: expr) => { assert_eq!( @@ -165,23 +171,48 @@ ); } + #[test] + fn direct_self() { + println!( + "{:#?}", + eval!( + r#" + { + local me = self, + a: 3, + b(): me.a, + } + "# + ) + ); + } + + #[test] + fn indirect_self() { + // `self` assigned to `me` was lost when being + // referenced from field + eval_stdlib!( + r#"{ + local me = std.trace("test", self), + b: me, + }.b"# + ); + } + // We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly #[test] fn std_assert_ok() { - let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";"; - evaluate( - Context::new(), - &parse(&(std + "std.assertEqual(4.5 << 2, 16,)")).unwrap(), - ); + eval_stdlib!("std.assertEqual(4.5 << 2, 16)"); } #[test] #[should_panic] fn std_assert_failure() { - let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";"; - evaluate( - Context::new(), - &parse(&(std + "std.assertEqual(4.5 << 2, 15,)")).unwrap(), - ); + eval_stdlib!("std.assertEqual(4.5 << 2, 15)"); + } + + #[test] + fn base64_works() { + eval_stdlib!(r#"std.base64("test")"#); } } --- a/crates/jsonnet-evaluator/src/obj.rs +++ b/crates/jsonnet-evaluator/src/obj.rs @@ -1,7 +1,8 @@ -use crate::{dummy_debug, evaluate_binary_op, BoxedBinding, Val}; +use crate::{evaluate_binary_op, Binding, Val}; use jsonnet_parser::{BinaryOpType, Visibility}; use std::{ collections::{BTreeMap, BTreeSet}, + fmt::Debug, rc::Rc, }; @@ -9,7 +10,7 @@ pub struct ObjMember { pub add: bool, pub visibility: Visibility, - pub invoke: BoxedBinding, + pub invoke: Binding, } #[derive(Debug)] @@ -17,8 +18,28 @@ super_obj: Option, this_entries: Rc>, } -pub struct ObjValue(Rc); -dummy_debug!(ObjValue); +pub struct ObjValue(pub(crate) Rc); +impl Debug for ObjValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(super_obj) = self.0.super_obj.as_ref() { + if f.alternate() { + write!(f, "{:#?}", super_obj)?; + } else { + write!(f, "{:?}", super_obj)?; + } + write!(f, " + ")?; + } + let mut debug = f.debug_struct("ObjValue"); + debug.field("$ptr", &Rc::as_ptr(&self.0)); + for (name, member) in self.0.this_entries.iter() { + debug.field(name, member); + } + debug.finish_non_exhaustive() + // .field("fields", &self.fields()) + // .finish_non_exhaustive() + } +} + impl ObjValue { pub fn new( super_obj: Option, @@ -47,26 +68,30 @@ } fields } - pub fn get_raw(&self, key: &str, real_this: Option) -> Option { + pub fn get(&self, key: &str) -> Option { + // TODO: Cache get_raw result + self.get_raw(key, Some(self)) + } + fn get_raw(&self, key: &str, real_this: Option<&ObjValue>) -> Option { match (self.0.this_entries.get(key), &self.0.super_obj) { - (Some(k), None) => Some(k.invoke.evaluate( - real_this.or_else(|| Some(self.clone())), - self.0.super_obj.clone().map(|e| e.clone()), + (Some(k), None) => Some(k.invoke.0( + real_this.as_ref().map(|e| (*e).clone()), + self.0.super_obj.clone(), )), (Some(k), Some(s)) => { - let our = k - .invoke - .evaluate(Some(self.clone()), self.0.super_obj.clone()); + let our = k.invoke.0( + real_this.as_ref().map(|e| (*e).clone()), + self.0.super_obj.clone(), + ); if k.add { - s.get_raw(key, Some(self.clone())) - .map_or(Some(our.clone()), |v| { - Some(evaluate_binary_op(&v, BinaryOpType::Add, &our)) - }) + s.get_raw(key, real_this).map_or(Some(our.clone()), |v| { + Some(evaluate_binary_op(&v, BinaryOpType::Add, &our)) + }) } else { Some(our) } } - (None, Some(s)) => s.get_raw(key, Some(self.clone())), + (None, Some(s)) => s.get_raw(key, real_this), (None, None) => None, } } --- a/crates/jsonnet-evaluator/src/val.rs +++ b/crates/jsonnet-evaluator/src/val.rs @@ -1,135 +1,62 @@ -use crate::{ - dynamic_wrapper, evaluate, evaluate_method, BoxedContextCreator, Context, FunctionDefault, - FunctionRhs, ObjValue, -}; -use crate::{Binding, BoxedBinding, BoxedFunctionDefault, BoxedFunctionRhs, FutureContext}; -use jsonnet_parser::{ArgsDesc, Expr, LiteralType, Param, ParamsDesc}; +use crate::{binding, rc_fn_helper, Binding, Context, FunctionDefault, FunctionRhs, ObjValue}; +use closure::closure; +use jsonnet_parser::{LiteralType, ParamsDesc}; use std::{ collections::HashMap, fmt::{Debug, Display}, - ops::Deref, - rc::Rc, }; -pub trait LazyVal: Debug { - fn evaluate(&self) -> Val; -} -dynamic_wrapper!(LazyVal, BoxedLazyVal); - -#[derive(Debug)] -pub struct PlainLazyVal { - pub expr: Expr, - pub context: Context, -} -impl LazyVal for PlainLazyVal { - fn evaluate(&self) -> Val { - evaluate(self.context.clone(), &self.expr) - } -} - -#[derive(Debug)] -pub struct NoArgsBindingLazyVal { - pub expr: Expr, - pub context_creator: BoxedContextCreator, - - pub this: Option, - pub super_obj: Option, -} -impl LazyVal for NoArgsBindingLazyVal { - fn evaluate(&self) -> Val { - evaluate( - self.context_creator - .create_context(&self.this, &self.super_obj), - &self.expr, - ) - } -} - -#[derive(Debug)] -pub struct ArgsBindingLazyVal { - pub expr: Expr, - pub args: ParamsDesc, - pub context_creator: BoxedContextCreator, - - pub this: Option, - pub super_obj: Option, -} -impl LazyVal for ArgsBindingLazyVal { - fn evaluate(&self) -> Val { - evaluate_method( - self.context_creator - .create_context(&self.this, &self.super_obj), - &self.expr, - self.args.clone(), - ) - } -} - -#[derive(Debug)] -pub struct FunctionDefaultBinding { - eval: BoxedFunctionDefault, - default: Expr, - ctx: FutureContext, -} -impl Binding for FunctionDefaultBinding { - fn evaluate(&self, _this: Option, _super_obj: Option) -> Val { - self.eval - .default(self.ctx.clone().unwrap(), self.default.clone()) - } -} +rc_fn_helper!(LazyVal, lazy_val, dyn Fn() -> Val); -#[derive(Debug)] -pub struct ValBinding { - val: Val, -} -impl Binding for ValBinding { - fn evaluate(&self, this: Option, super_obj: Option) -> Val { - self.val.clone() - } -} - #[derive(Debug, PartialEq, Clone)] pub struct FuncDesc { pub ctx: Context, pub params: ParamsDesc, - pub eval_rhs: BoxedFunctionRhs, - pub eval_default: BoxedFunctionDefault, + pub eval_rhs: FunctionRhs, + pub eval_default: FunctionDefault, } impl FuncDesc { // TODO: Check for unset variables pub fn evaluate(&self, args: Vec<(Option, Val)>) -> Val { - let mut new_bindings: HashMap = HashMap::new(); + let mut new_bindings: HashMap = HashMap::new(); let future_ctx = Context::new_future(); - self.params - .with_defaults() - .into_iter() - .for_each(|Param(name, default)| { - new_bindings.insert( - name, - Rc::new(FunctionDefaultBinding { - eval: self.eval_default.clone(), - default: *default.unwrap().clone(), - ctx: future_ctx.clone(), - }), - ); - }); - for (name, val) in args.iter().filter(|e| e.0.is_some()) { + // self.params + // .with_defaults() + // .into_iter() + // .for_each(|Param(name, default)| { + // let default = Rc::new(*default.unwrap()); + // new_bindings.insert( + // name, + // binding!(move |_, _| Val::Lazy(lazy_val!(|| self + // .eval_default + // .0 + // .default(future_ctx.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(), - Rc::new(ValBinding { val: val.clone() }), + binding!( + closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone 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(), Rc::new(ValBinding { val: val.clone() })); + new_bindings.insert( + param.0.clone(), + binding!( + closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone())))) + ), + ); } } let ctx = self .ctx .extend(new_bindings, None, None, None) .into_future(future_ctx); - self.eval_rhs.evaluate(ctx) + self.eval_rhs.0(ctx) } } @@ -138,15 +65,18 @@ Literal(LiteralType), Str(String), Num(f64), - Lazy(BoxedLazyVal), + Lazy(LazyVal), Arr(Vec), Obj(ObjValue), Func(FuncDesc), + + // Library functions implemented in native + Intristic(String, String), } impl Val { pub fn unwrap_if_lazy(self) -> Self { if let Val::Lazy(v) = self { - v.evaluate().unwrap_if_lazy() + v.0().unwrap_if_lazy() } else { self } @@ -191,12 +121,12 @@ write!(f, ",")?; } write!(f, "\"{}\":", field)?; - write!(f, "{}", value.get_raw(&field, None).unwrap())?; + write!(f, "{}", value.get(&field).unwrap())?; } write!(f, "}}")?; } Val::Lazy(lazy) => { - write!(f, "{}", lazy.evaluate())?; + write!(f, "{}", lazy.0())?; } Val::Func(_) => { write!(f, "<>")?; --- a/crates/jsonnet-parser/src/expr.rs +++ b/crates/jsonnet-parser/src/expr.rs @@ -37,7 +37,7 @@ AssertStmt(AssertStmt), } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum UnaryOpType { Plus, Minus, -- gitstuff