git.delta.rocks / jrsonnet / refs/commits / 26c294dc66fa

difftreelog

fix(evaluator) instristics

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

6 files changed

addedcrates/jsonnet-evaluator/README.mddiffbeforeafterboth
--- /dev/null
+++ b/crates/jsonnet-evaluator/README.md
@@ -0,0 +1,11 @@
+# jsonnet-evaluator
+
+Interpreter for parsed jsonnet tree
+
+## Intristics
+
+Some functions from stdlib are implemented as intristics
+
+### Intristic handling
+
+If indexed jsonnet object has field '__intristic_namespace__' of type 'string', then any not found field/method is resolved as `Val::Intristic(__intristic_namespace__, name)`
modifiedcrates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/ctx.rs
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -1,4 +1,4 @@
-use crate::{future_wrapper, rc_fn_helper, Binding, ObjValue};
+use crate::{future_wrapper, rc_fn_helper, LazyBinding, ObjValue, LazyVal, Val};
 use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
 rc_fn_helper!(
@@ -14,7 +14,7 @@
 	dollar: Option<ObjValue>,
 	this: Option<ObjValue>,
 	super_obj: Option<ObjValue>,
-	bindings: Rc<RefCell<HashMap<String, Binding>>>,
+	bindings: Rc<HashMap<String, LazyVal>>,
 }
 pub struct Context(Rc<ContextInternals>);
 impl Debug for Context {
@@ -46,14 +46,13 @@
 			dollar: None,
 			this: None,
 			super_obj: None,
-			bindings: Rc::new(RefCell::new(HashMap::new())),
+			bindings: Rc::new(HashMap::new()),
 		}))
 	}
 
-	pub fn binding(&self, name: &str) -> Binding {
+	pub fn binding(&self, name: &str) -> LazyVal {
 		self.0
 			.bindings
-			.borrow()
 			.get(name)
 			.cloned()
 			.unwrap_or_else(|| {
@@ -69,7 +68,7 @@
 
 	pub fn extend(
 		&self,
-		new_bindings: HashMap<String, Binding>,
+		new_bindings: HashMap<String, LazyBinding>,
 		new_dollar: Option<ObjValue>,
 		new_this: Option<ObjValue>,
 		new_super_obj: Option<ObjValue>,
@@ -80,11 +79,14 @@
 		let bindings = if new_bindings.is_empty() {
 			self.0.bindings.clone()
 		} else {
-			let new = self.0.bindings.clone();
+			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.borrow_mut().insert(k, v);
+				new.insert(k, v.0(this.clone(), super_obj.clone()));
 			}
-			new
+			Rc::new(new)
 		};
 		Context(Rc::new(ContextInternals {
 			dollar,
modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -1,6 +1,7 @@
 use crate::{
-	binding, bool_val, context_creator, function_default, function_rhs, future_wrapper, lazy_val,
-	Binding, Context, ContextCreator, FuncDesc, ObjMember, ObjValue, Val,
+	binding, bool_val, context_creator, function_default, function_rhs, future_wrapper,
+	lazy_binding, lazy_val, Binding, Context, ContextCreator, FuncDesc, LazyBinding, ObjMember,
+	ObjValue, Val,
 };
 use closure::closure;
 use jsonnet_parser::{
@@ -12,30 +13,28 @@
 	rc::Rc,
 };
 
-pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (String, Binding) {
+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();
 		(
 			b.name.clone(),
-			binding!(move |this, super_obj| Val::Lazy(lazy_val!(
+			lazy_binding!(move |this, super_obj| 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(),
-			binding!(move |this, super_obj| {
-				Val::Lazy(lazy_val!(
-					closure!(clone context_creator, clone b, || evaluate(
-						context_creator.0(this.clone(), super_obj.clone()),
-						&b.value
-					))
-				))
+			lazy_binding!(move |this, super_obj| {
+				lazy_val!(closure!(clone context_creator, clone b, || evaluate(
+					context_creator.0(this.clone(), super_obj.clone()),
+					&b.value
+				)))
 			}),
 		)
 	}
@@ -87,6 +86,19 @@
 		(Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),
 		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),
 
+		(Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
+			bool_val(false)
+		}
+		(Val::Literal(LiteralType::False), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
+			bool_val(false)
+		}
+		(Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::False)) => {
+			bool_val(false)
+		}
+		(Val::Literal(LiteralType::True), BinaryOpType::And, Val::Literal(LiteralType::True)) => {
+			bool_val(true)
+		}
+
 		(Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),
 
 		(Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),
@@ -126,37 +138,39 @@
 	}
 }
 
-future_wrapper!(HashMap<String, Binding>, FutureNewBindings);
+future_wrapper!(HashMap<String, LazyBinding>, 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 new_bindings = FutureNewBindings::new();
 			let future_this = FutureObjValue::new();
 			let context_creator = context_creator!(
-				closure!(clone context, clone future_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
+				closure!(clone context, clone new_bindings, clone future_this, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {
 					context.clone().extend(
-						future_bindings.clone().unwrap(),
+						new_bindings.clone().unwrap(),
 						context.clone().dollar().clone().or_else(||this.clone()),
-						Some(future_this.clone().unwrap()),
+						Some(this.clone().unwrap()),
 						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, context_creator.clone()))
 			{
-				bindings.insert(n, b);
+				let mut bindings: HashMap<String, LazyBinding> = 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, context_creator.clone()))
+				{
+					bindings.insert(n, b);
+				}
+				new_bindings.fill(bindings);
 			}
-			future_bindings.fill(bindings);
 
 			let mut new_members = BTreeMap::new();
 			for member in members.into_iter() {
@@ -176,11 +190,12 @@
 								visibility: visibility.clone(),
 								invoke: binding!(
 									closure!(clone value, clone context_creator, |this, super_obj| {
+										let context = context_creator.0(this, super_obj);
 										// TODO: Assert
 										evaluate(
-											context_creator.0(this, super_obj),
+											context,
 											&value,
-										)
+										).unwrap_if_lazy()
 									})
 								),
 							},
@@ -244,27 +259,39 @@
 			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);
-			variable.0(None, None).unwrap_if_lazy()
-		}
+		Var(name) => Val::Lazy(context.binding(&name)).unwrap_if_lazy(),
 		Index(box value, box index) => {
 			match (
 				evaluate(context.clone(), value).unwrap_if_lazy(),
-				evaluate(context, index),
+				evaluate(context.clone(), index),
 			) {
 				(Val::Obj(v), Val::Str(s)) => v
 					.get(&s)
-					.unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),
+					.unwrap_or_else(closure!(clone context, || {
+						if let Some(n) = v.get("__intristic_namespace__") {
+							if let Val::Str(n) = n.unwrap_if_lazy() {
+								Val::Intristic(n, s)
+							} else {
+								panic!("__intristic_namespace__ should be string");
+							}
+						} else {
+							panic!("{} not found in {:?}", s, v)
+						}
+					}))
+					.unwrap_if_lazy(),
 				(Val::Arr(v), Val::Num(n)) => v
 					.get(n as usize)
 					.unwrap_or_else(|| panic!("out of bounds"))
 					.clone(),
+				(Val::Str(s), Val::Num(n)) => {
+					// FIXME: Only works for ASCII
+					Val::Str(String::from_utf8(vec![s.as_bytes()[n as usize]]).unwrap())
+				}
 				(v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),
 			}
 		}
 		LocalExpr(bindings, returned) => {
-			let mut new_bindings: HashMap<String, Binding> = HashMap::new();
+			let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
 			let future_context = Context::new_future();
 
 			let context_creator = context_creator!(
@@ -287,6 +314,49 @@
 		Apply(box value, ArgsDesc(args)) => {
 			let value = evaluate(context.clone(), value).unwrap_if_lazy();
 			match value {
+				// TODO: Capture context of application
+				Val::Intristic(ns, name) => match (&ns as &str, &name as &str) {
+					("std", "length") => {
+						assert_eq!(args.len(), 1);
+						let expr = &args.get(0).unwrap().1;
+						match evaluate(context, expr) {
+							Val::Str(n) => Val::Num(n.len() as f64),
+							Val::Arr(i) => Val::Num(i.len() as f64),
+							v => panic!("can't get length of {:?}", v),
+						}
+					}
+					("std", "type") => {
+						assert_eq!(args.len(), 1);
+						let expr = &args.get(0).unwrap().1;
+						Val::Str(evaluate(context, expr).type_of().to_owned())
+					}
+					("std", "makeArray") => {
+						assert_eq!(args.len(), 2);
+						if let (Val::Num(v), Val::Func(d)) = (
+							evaluate(context.clone(), &args[0].1),
+							evaluate(context, &args[1].1),
+						) {
+							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))]))
+							}
+							Val::Arr(out)
+						} else {
+							panic!("bad makeArray call");
+						}
+					}
+					("std", "codepoint") => {
+						assert_eq!(args.len(), 1);
+						if let Val::Str(s) = evaluate(context.clone(), &args[0].1) {
+							// FIXME: this is not a codepoint
+							Val::Num(s.as_bytes()[0] as f64)
+						} else {
+							panic!("bad codepoint call");
+						}
+					}
+					(ns, name) => panic!("Intristic not found: {}.{}", ns, name),
+				},
 				Val::Func(f) => f.evaluate(
 					args.clone()
 						.into_iter()
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)]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 = 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}
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!(24	LazyBinding,25	lazy_binding,26	dyn Fn(Option<ObjValue>, Option<ObjValue>) -> LazyVal27);28rc_fn_helper!(FunctionRhs, function_rhs, dyn Fn(Context) -> Val);29rc_fn_helper!(30	FunctionDefault,31	function_default,32	dyn Fn(Context, Expr) -> Val33);3435#[cfg(test)]36pub mod tests {37	use super::{evaluate, Context, Val};38	use jsonnet_parser::*;3940	macro_rules! eval {41		($str: expr) => {42			evaluate(Context::new(), &parse($str).unwrap())43		};44	}4546	macro_rules! eval_stdlib {47		($str: expr) => {{48			let std = "local std = ".to_owned() + jsonnet_stdlib::STDLIB_STR + ";";49			evaluate(Context::new(), &parse(&(std + $str)).unwrap())50			}};51	}5253	macro_rules! assert_eval {54		($str: expr) => {55			assert_eq!(56				evaluate(Context::new(), &parse($str).unwrap()),57				Val::Literal(LiteralType::True)58				)59		};60	}61	macro_rules! assert_json {62		($str: expr, $out: expr) => {63			assert_eq!(64				format!("{}", evaluate(Context::new(), &parse($str).unwrap())),65				$out66				)67		};68	}69	macro_rules! assert_json_stdlib {70		($str: expr, $out: expr) => {71			assert_eq!(72				format!("{}", eval_stdlib!($str)),73				$out74				)75		};76	}77	macro_rules! assert_eval_neg {78		($str: expr) => {79			assert_eq!(80				evaluate(Context::new(), &parse($str).unwrap()),81				Val::Literal(LiteralType::False)82				)83		};84	}8586	/// Sanity checking, before trusting to another tests87	#[test]88	fn equality_operator() {89		assert_eval!("2 == 2");90		assert_eval_neg!("2 != 2");91		assert_eval!("2 != 3");92		assert_eval_neg!("2 == 3");93		assert_eval!("'Hello' == 'Hello'");94		assert_eval_neg!("'Hello' != 'Hello'");95		assert_eval!("'Hello' != 'World'");96		assert_eval_neg!("'Hello' == 'World'");97	}9899	#[test]100	fn math_evaluation() {101		assert_eval!("2 + 2 * 2 == 6");102		assert_eval!("3 + (2 + 2 * 2) == 9");103	}104105	#[test]106	fn string_concat() {107		assert_eval!("'Hello' + 'World' == 'HelloWorld'");108		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");109		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");110	}111112	#[test]113	fn local() {114		assert_eval!("local a = 2; local b = 3; a + b == 5");115		assert_eval!("local a = 1, b = a + 1; a + b == 3");116		assert_eval!("local a = 1; local a = 2; a == 2");117	}118119	#[test]120	fn object_lazyness() {121		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);122	}123124	#[test]125	fn object_inheritance() {126		assert_json!("{a: self.b} + {b:3}", r#"{"a":3,"b":3}"#);127	}128129	#[test]130	fn test_object() {131		assert_json!("{a:2}", r#"{"a":2}"#);132		assert_json!("{a:2+2}", r#"{"a":4}"#);133		assert_json!("{a:2}+{b:2}", r#"{"a":2,"b":2}"#);134		assert_json!("{b:3}+{b:2}", r#"{"b":2}"#);135		assert_json!("{b:3}+{b+:2}", r#"{"b":5}"#);136		assert_json!("local test='a'; {[test]:2}", r#"{"a":2}"#);137		assert_json!(138			r#"139				{140					name: "Alice",141					welcome: "Hello " + self.name + "!",142				}143			"#,144			r#"{"name":"Alice","welcome":"Hello Alice!"}"#145		);146		assert_json!(147			r#"148				{149					name: "Alice",150					welcome: "Hello " + self.name + "!",151				} + {152					name: "Bob"153				}154			"#,155			r#"{"name":"Bob","welcome":"Hello Bob!"}"#156		);157	}158159	#[test]160	fn functions() {161		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");162		assert_json!(163			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,164			r#""HelloDearWorld""#165		);166	}167168	#[test]169	fn local_methods() {170		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");171		assert_json!(172			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,173			r#""HelloDearWorld""#174		);175	}176177	#[test]178	fn object_locals() {179		assert_json!(r#"{local a = 3, b: a}"#, r#"{"b":3}"#);180		assert_json!(r#"{local a = 3, local c = a, b: c}"#, r#"{"b":3}"#);181		assert_json!(182			r#"{local a = function (b) {[b]:4}, test: a("test")}"#,183			r#"{"test":{"test":4}}"#184		);185	}186187	#[test]188	fn direct_self() {189		println!(190			"{:#?}",191			eval!(192				r#"193					{194						local me = self,195						a: 3,196						b(): me.a,197					}198				"#199			)200		);201	}202203	#[test]204	fn indirect_self() {205		// `self` assigned to `me` was lost when being206		// referenced from field207		eval_stdlib!(208			r#"{209				local me = self,210				a: 3,211				b: me.a,212			}.b"#213		);214	}215216	// We can't trust other tests (And official jsonnet testsuite), if assert is not working correctly217	#[test]218	fn std_assert_ok() {219		eval_stdlib!("std.assertEqual(4.5 << 2, 16)");220	}221222	#[test]223	#[should_panic]224	fn std_assert_failure() {225		eval_stdlib!("std.assertEqual(4.5 << 2, 15)");226	}227228	#[test]229	fn string_is_string() {230		assert_eq!(231			eval_stdlib!("local arr = 'hello'; (!std.isArray(arr)) && (!std.isString(arr))"),232			Val::Literal(LiteralType::False)233		);234	}235236	#[test]237	fn base64_works() {238		assert_json_stdlib!(r#"std.base64("test")"#, r#""dGVzdA==""#);239	}240}
modifiedcrates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/obj.rs
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -70,17 +70,17 @@
 	}
 	pub fn get(&self, key: &str) -> Option<Val> {
 		// TODO: Cache get_raw result
-		self.get_raw(key, Some(self))
+		self.get_raw(key, self)
 	}
-	fn get_raw(&self, key: &str, real_this: Option<&ObjValue>) -> Option<Val> {
+	fn get_raw(&self, key: &str, real_this: &ObjValue) -> Option<Val> {
 		match (self.0.this_entries.get(key), &self.0.super_obj) {
 			(Some(k), None) => Some(k.invoke.0(
-				real_this.as_ref().map(|e| (*e).clone()),
+				Some(real_this.clone()),
 				self.0.super_obj.clone(),
 			)),
 			(Some(k), Some(s)) => {
 				let our = k.invoke.0(
-					real_this.as_ref().map(|e| (*e).clone()),
+					Some(real_this.clone()),
 					self.0.super_obj.clone(),
 				);
 				if k.add {
modifiedcrates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/val.rs
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -1,4 +1,6 @@
-use crate::{binding, rc_fn_helper, Binding, Context, FunctionDefault, FunctionRhs, ObjValue};
+use crate::{
+	lazy_binding, rc_fn_helper, Context, FunctionDefault, FunctionRhs, LazyBinding, ObjValue,
+};
 use closure::closure;
 use jsonnet_parser::{LiteralType, ParamsDesc};
 use std::{
@@ -18,7 +20,7 @@
 impl FuncDesc {
 	// TODO: Check for unset variables
 	pub fn evaluate(&self, args: Vec<(Option<String>, Val)>) -> Val {
-		let mut new_bindings: HashMap<String, Binding> = HashMap::new();
+		let mut new_bindings: HashMap<String, LazyBinding> = HashMap::new();
 		let future_ctx = Context::new_future();
 
 		// self.params
@@ -37,8 +39,8 @@
 		for (name, val) in args.clone().into_iter().filter(|e| e.0.is_some()) {
 			new_bindings.insert(
 				name.as_ref().unwrap().clone(),
-				binding!(
-					closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))
+				lazy_binding!(
+					closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))
 				),
 			);
 		}
@@ -46,8 +48,8 @@
 			if let Some((None, val)) = args.get(i) {
 				new_bindings.insert(
 					param.0.clone(),
-					binding!(
-						closure!(clone val, |_, _| Val::Lazy(lazy_val!(closure!(clone val, || val.clone()))))
+					lazy_binding!(
+						closure!(clone val, |_, _| lazy_val!(closure!(clone val, || val.clone())))
 					),
 				);
 			}