git.delta.rocks / jrsonnet / refs/commits / abae2f2e28fb

difftreelog

refactor(evaluator) use closures where possible

Лач2020-05-30parent: #0787247.patch.diff
in: master

9 files changed

modifiedcrates/jsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- 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" }
deletedcrates/jsonnet-evaluator/src/binding.rsdiffbeforeafterboth
--- 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<ObjValue>, super_obj: Option<ObjValue>) -> Val;
-}
-pub type BoxedBinding = Rc<dyn Binding>;
-
-#[derive(Debug)]
-pub struct NoArgsBinding {
-	pub expr: Expr,
-	pub context_creator: BoxedContextCreator,
-}
-impl Binding for NoArgsBinding {
-	fn evaluate(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> 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<ObjValue>, super_obj: Option<ObjValue>) -> Val {
-		Val::Lazy(BoxedLazyVal(Rc::new(ArgsBindingLazyVal {
-			context_creator: self.context_creator.clone(),
-			expr: self.expr.clone(),
-			args: self.args.clone(),
-			this,
-			super_obj,
-		})))
-	}
-}
modifiedcrates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- 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<ObjValue>, super_obj: &Option<ObjValue>) -> Context;
-}
-pub type BoxedContextCreator = Rc<dyn ContextCreator>;
 
-#[derive(Debug)]
-pub struct ConstantContextCreator {
-	pub context: FutureContext,
-}
-impl ContextCreator for ConstantContextCreator {
-	fn create_context(&self, _this: &Option<ObjValue>, _super_obj: &Option<ObjValue>) -> Context {
-		self.context.clone().unwrap()
-	}
-}
+rc_fn_helper!(
+	ContextCreator,
+	context_creator,
+	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Context
+);
 
 future_wrapper!(Context, FutureContext);
 
@@ -23,10 +14,16 @@
 	dollar: Option<ObjValue>,
 	this: Option<ObjValue>,
 	super_obj: Option<ObjValue>,
-	bindings: Rc<RefCell<HashMap<String, BoxedBinding>>>,
+	bindings: Rc<RefCell<HashMap<String, Binding>>>,
 }
 pub struct Context(Rc<ContextInternals>);
-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<String, BoxedBinding>,
+		new_bindings: HashMap<String, Binding>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
 		new_super_obj: Option<ObjValue>,
 	) -> 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)
modifiedcrates/jsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
--- 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<dyn $orig>);
-		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))
+			};
+		}
 	};
 }
modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- 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<String, BoxedBinding>, FutureNewBindings);
-
-#[derive(Debug)]
-pub struct ObjectContextCreator {
-	original: Context,
-	future_bindings: FutureNewBindings,
-}
-
-impl ContextCreator for ObjectContextCreator {
-	fn create_context(&self, this: &Option<ObjValue>, super_obj: &Option<ObjValue>) -> 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<String, Binding>, 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<String, BoxedBinding> = HashMap::new();
+			let future_this = FutureObjValue::new();
+			let context_creator = context_creator!(
+				closure!(clone context, clone future_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
+					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<String, Binding> = 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<String, BoxedBinding> = HashMap::new();
+			let mut new_bindings: HashMap<String, Binding> = 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),
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
before · crates/jsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]45mod binding;6mod ctx;7mod dynamic;8mod evaluate;9mod obj;10mod val;1112pub use binding::*;13pub use ctx::*;14pub use dynamic::*;15pub use evaluate::*;16use jsonnet_parser::*;17pub use obj::*;18use std::fmt::Debug;19use std::rc::Rc;20pub use val::*;2122pub trait FunctionRhs: Debug {23	fn evaluate(&self, ctx: Context) -> Val;24}25dynamic_wrapper!(FunctionRhs, BoxedFunctionRhs);2627pub trait FunctionDefault: Debug {28	fn default(&self, ctx: Context, expr: Expr) -> Val;29}30dynamic_wrapper!(FunctionDefault, BoxedFunctionDefault);3132#[cfg(test)]33pub mod tests {34	use super::{evaluate, Context, Val};35	use jsonnet_parser::*;3637	// macro_rules! eval {38	// 	($str: expr) => {39	// 		evaluate(Context::new(), &parse($str).unwrap())40	// 	};41	// }42	macro_rules! assert_eval {43		($str: expr) => {44			assert_eq!(45				evaluate(Context::new(), &parse($str).unwrap()),46				Val::Literal(LiteralType::True)47				)48		};49	}50	macro_rules! assert_json {51		($str: expr, $out: expr) => {52			assert_eq!(53				format!("{}", evaluate(Context::new(), &parse($str).unwrap())),54				$out55				)56		};57	}58	macro_rules! assert_eval_neg {59		($str: expr) => {60			assert_eq!(61				evaluate(Context::new(), &parse($str).unwrap()),62				Val::Literal(LiteralType::False)63				)64		};65	}6667	/// Sanity checking, before trusting to another tests68	#[test]69	fn equality_operator() {70		assert_eval!("2 == 2");71		assert_eval_neg!("2 != 2");72		assert_eval!("2 != 3");73		assert_eval_neg!("2 == 3");74		assert_eval!("'Hello' == 'Hello'");75		assert_eval_neg!("'Hello' != 'Hello'");76		assert_eval!("'Hello' != 'World'");77		assert_eval_neg!("'Hello' == 'World'");78	}7980	#[test]81	fn math_evaluation() {82		assert_eval!("2 + 2 * 2 == 6");83		assert_eval!("3 + (2 + 2 * 2) == 9");84	}8586	#[test]87	fn string_concat() {88		assert_eval!("'Hello' + 'World' == 'HelloWorld'");89		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");90		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");91	}9293	#[test]94	fn local() {95		assert_eval!("local a = 2; local b = 3; a + b == 5");96		assert_eval!("local a = 1, b = a + 1; a + b == 3");97		assert_eval!("local a = 1; local a = 2; a == 2");98	}99100	#[test]101	fn object_lazyness() {102		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);103	}104105	#[test]106	fn object_inheritance() {107		assert_json!("{a:self.b} + {b:3}", r#"{"a":3,"b":3}"#);108	}109110	#[test]111	fn test_object() {112		assert_json!("{a:2}", r#"{"a":2}"#);113		assert_json!("{a:2+2}", r#"{"a":4}"#);114		assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);115		assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);116		assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);117		assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);118		assert_json!(119			r#"120				{121					name: "Alice",122					welcome: "Hello " + self.name + "!",123				}124			"#,125			r#"{"name":"Alice","welcome":"Hello Alice!"}"#126		);127		assert_json!(128			r#"129				{130					name: "Alice",131					welcome: "Hello " + self.name + "!",132				} + {133					name: "Bob"134				}135			"#,136			r#"{"name":"Bob","welcome":"Hello Bob!"}"#137		);138	}139140	#[test]141	fn functions() {142		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");143		assert_json!(144			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,145			r#""HelloDearWorld""#146		);147	}148149	#[test]150	fn local_methods() {151		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");152		assert_json!(153			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,154			r#""HelloDearWorld""#155		);156	}157158	#[test]159	fn object_locals() {160		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);161		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);162		assert_json!(163			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,164			r#"{"test":{"test":4}}"#165		);166	}167168	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly169	#[test]170	fn std_assert_ok() {171		let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";172		evaluate(173			Context::new(),174			&parse(&(std + "std.assertEqual(4.5 << 2, 16,)")).unwrap(),175		);176	}177178	#[test]179	#[should_panic]180	fn std_assert_failure() {181		let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";182		evaluate(183			Context::new(),184			&parse(&(std + "std.assertEqual(4.5 << 2, 15,)")).unwrap(),185		);186	}187}
after · crates/jsonnet-evaluator/src/lib.rs
1#![feature(box_syntax, box_patterns)]2#![feature(type_alias_impl_trait)]3#![feature(debug_non_exhaustive)]4#![allow(macro_expanded_macro_exports_accessed_by_absolute_paths)]5mod ctx;6mod dynamic;7mod evaluate;8mod obj;9mod val;1011pub use ctx::*;12pub use dynamic::*;13pub use evaluate::*;14use jsonnet_parser::*;15pub use obj::*;16pub use val::*;1718rc_fn_helper!(19	Binding,20	binding,21	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Val22);23rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);24rc_fn_helper!(25	FunctionDefault,26	function_default,27	dyn Fn(Context, Expr) -> Val28);2930#[cfg(test)]31pub mod tests {32	use super::{evaluate, Context, Val};33	use jsonnet_parser::*;3435	macro_rules! eval {36		($str: expr) => {37			evaluate(Context::new(), &parse($str).unwrap())38		};39	}4041	macro_rules! eval_stdlib {42		($str: expr) => {43			let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";44			evaluate(Context::new(), &parse(&(std + $str)).unwrap())45		};46	}4748	macro_rules! assert_eval {49		($str: expr) => {50			assert_eq!(51				evaluate(Context::new(), &parse($str).unwrap()),52				Val::Literal(LiteralType::True)53				)54		};55	}56	macro_rules! assert_json {57		($str: expr, $out: expr) => {58			assert_eq!(59				format!("{}", evaluate(Context::new(), &parse($str).unwrap())),60				$out61				)62		};63	}64	macro_rules! assert_eval_neg {65		($str: expr) => {66			assert_eq!(67				evaluate(Context::new(), &parse($str).unwrap()),68				Val::Literal(LiteralType::False)69				)70		};71	}7273	/// Sanity checking, before trusting to another tests74	#[test]75	fn equality_operator() {76		assert_eval!("2 == 2");77		assert_eval_neg!("2 != 2");78		assert_eval!("2 != 3");79		assert_eval_neg!("2 == 3");80		assert_eval!("'Hello' == 'Hello'");81		assert_eval_neg!("'Hello' != 'Hello'");82		assert_eval!("'Hello' != 'World'");83		assert_eval_neg!("'Hello' == 'World'");84	}8586	#[test]87	fn math_evaluation() {88		assert_eval!("2 + 2 * 2 == 6");89		assert_eval!("3 + (2 + 2 * 2) == 9");90	}9192	#[test]93	fn string_concat() {94		assert_eval!("'Hello' + 'World' == 'HelloWorld'");95		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");96		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");97	}9899	#[test]100	fn local() {101		assert_eval!("local a = 2; local b = 3; a + b == 5");102		assert_eval!("local a = 1, b = a + 1; a + b == 3");103		assert_eval!("local a = 1; local a = 2; a == 2");104	}105106	#[test]107	fn object_lazyness() {108		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);109	}110111	#[test]112	fn object_inheritance() {113		assert_json!("{a:self.b} + {b:3}", r#"{"a":3,"b":3}"#);114	}115116	#[test]117	fn test_object() {118		assert_json!("{a:2}", r#"{"a":2}"#);119		assert_json!("{a:2+2}", r#"{"a":4}"#);120		assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);121		assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);122		assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);123		assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);124		assert_json!(125			r#"126				{127					name: "Alice",128					welcome: "Hello " + self.name + "!",129				}130			"#,131			r#"{"name":"Alice","welcome":"Hello Alice!"}"#132		);133		assert_json!(134			r#"135				{136					name: "Alice",137					welcome: "Hello " + self.name + "!",138				} + {139					name: "Bob"140				}141			"#,142			r#"{"name":"Bob","welcome":"Hello Bob!"}"#143		);144	}145146	#[test]147	fn functions() {148		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");149		assert_json!(150			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,151			r#""HelloDearWorld""#152		);153	}154155	#[test]156	fn local_methods() {157		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");158		assert_json!(159			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,160			r#""HelloDearWorld""#161		);162	}163164	#[test]165	fn object_locals() {166		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);167		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);168		assert_json!(169			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,170			r#"{"test":{"test":4}}"#171		);172	}173174	#[test]175	fn direct_self() {176		println!(177			"{:#?}",178			eval!(179				r#"180					{181						local me = self,182						a: 3,183						b(): me.a,184					}185				"#186			)187		);188	}189190	#[test]191	fn indirect_self() {192		// `self` assigned to `me` was lost when being193		// referenced from field194		eval_stdlib!(195			r#"{196				local me = std.trace("test", self),197				b: me,198			}.b"#199		);200	}201202	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly203	#[test]204	fn std_assert_ok() {205		eval_stdlib!("std.assertEqual(4.5 << 2, 16)");206	}207208	#[test]209	#[should_panic]210	fn std_assert_failure() {211		eval_stdlib!("std.assertEqual(4.5 << 2, 15)");212	}213214	#[test]215	fn base64_works() {216		eval_stdlib!(r#"std.base64("test")"#);217	}218}
modifiedcrates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- 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<ObjValue>,
 	this_entries: Rc<BTreeMap<String, ObjMember>>,
 }
-pub struct ObjValue(Rc<ObjValueInternals>);
-dummy_debug!(ObjValue);
+pub struct ObjValue(pub(crate) Rc<ObjValueInternals>);
+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<ObjValue>,
@@ -47,26 +68,30 @@
 		}
 		fields
 	}
-	pub fn get_raw(&self, key: &str, real_this: Option<ObjValue>) -> Option<Val> {
+	pub fn get(&self, key: &str) -> Option<Val> {
+		// TODO: Cache get_raw result
+		self.get_raw(key, Some(self))
+	}
+	fn get_raw(&self, key: &str, real_this: Option<&ObjValue>) -> Option<Val> {
 		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,
 		}
 	}
modifiedcrates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- 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<ObjValue>,
-	pub super_obj: Option<ObjValue>,
-}
-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<ObjValue>,
-	pub super_obj: Option<ObjValue>,
-}
-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<ObjValue>, _super_obj: Option<ObjValue>) -> 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<ObjValue>, super_obj: Option<ObjValue>) -> 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<String>, Val)>) -> Val {
-		let mut new_bindings: HashMap<String, BoxedBinding> = HashMap::new();
+		let mut new_bindings: HashMap<String, Binding> = 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<Val>),
 	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, "<<FUNC>>")?;
modifiedcrates/jsonnet-parser/src/expr.rsdiffbeforeafterboth
--- 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,