git.delta.rocks / jrsonnet / refs/commits / 067b138a5c9b

difftreelog

chore(evaluator) partial interpreter implementation

Лач2020-05-29parent: #45767d3.patch.diff
in: master

8 files changed

modifiedcrates/jsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jsonnet-evaluator/Cargo.toml
+++ b/crates/jsonnet-evaluator/Cargo.toml
@@ -8,3 +8,6 @@
 
 [dependencies]
 jsonnet-parser = { path = "../jsonnet-parser" }
+
+[dev-dependencies]
+jsonnet-stdlib = { version = "0.1.0", path = "../jsonnet-stdlib" }
addedcrates/jsonnet-evaluator/src/binding.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jsonnet-evaluator/src/binding.rs
@@ -0,0 +1,43 @@
+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,
+		})))
+	}
+}
addedcrates/jsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jsonnet-evaluator/src/ctx.rs
@@ -0,0 +1,110 @@
+use crate::{dummy_debug, future_wrapper, BoxedBinding, 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()
+	}
+}
+
+future_wrapper!(Context, FutureContext);
+
+#[derive(Debug)]
+struct ContextInternals {
+	dollar: Option<ObjValue>,
+	this: Option<ObjValue>,
+	super_obj: Option<ObjValue>,
+	bindings: Rc<RefCell<HashMap<String, BoxedBinding>>>,
+}
+pub struct Context(Rc<ContextInternals>);
+dummy_debug!(Context);
+impl Context {
+	pub fn new_future() -> FutureContext {
+		FutureContext(Rc::new(RefCell::new(None)))
+	}
+
+	pub fn dollar(&self) -> &Option<ObjValue> {
+		&self.0.dollar
+	}
+
+	pub fn this(&self) -> &Option<ObjValue> {
+		&self.0.this
+	}
+
+	pub fn super_obj(&self) -> &Option<ObjValue> {
+		&self.0.super_obj
+	}
+
+	pub fn new() -> Context {
+		Context(Rc::new(ContextInternals {
+			dollar: None,
+			this: None,
+			super_obj: None,
+			bindings: Rc::new(RefCell::new(HashMap::new())),
+		}))
+	}
+
+	pub fn binding(&self, name: &str) -> BoxedBinding {
+		self.0
+			.bindings
+			.borrow()
+			.get(name)
+			.map(|e| e.clone())
+			.unwrap_or_else(|| {
+				panic!("can't find {} in {:?}", name, self);
+			})
+	}
+	pub fn into_future(self, ctx: FutureContext) -> Context {
+		{
+			ctx.0.borrow_mut().replace(self);
+		}
+		ctx.unwrap()
+	}
+
+	pub fn extend(
+		&self,
+		new_bindings: HashMap<String, BoxedBinding>,
+		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());
+		let bindings = if new_bindings.is_empty() {
+			self.0.bindings.clone()
+		} else {
+			let new = self.0.bindings.clone();
+			for (k, v) in new_bindings.into_iter() {
+				new.borrow_mut().insert(k, v);
+			}
+			new
+		};
+		Context(Rc::new(ContextInternals {
+			dollar,
+			this,
+			super_obj,
+			bindings,
+		}))
+	}
+}
+impl PartialEq for Context {
+	fn eq(&self, other: &Self) -> bool {
+		Rc::ptr_eq(&self.0, &other.0)
+	}
+}
+
+impl Clone for Context {
+	fn clone(&self) -> Self {
+		Context(self.0.clone())
+	}
+}
addedcrates/jsonnet-evaluator/src/dynamic.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jsonnet-evaluator/src/dynamic.rs
@@ -0,0 +1,55 @@
+#[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)]
+		pub struct $wrapper(pub std::rc::Rc<std::cell::RefCell<Option<$orig>>>);
+		impl $wrapper {
+			pub fn unwrap(self) -> $orig {
+				self.0.borrow().as_ref().map(|e| e.clone()).unwrap()
+			}
+			pub fn new() -> Self {
+				$wrapper(std::rc::Rc::new(std::cell::RefCell::new(None)))
+			}
+			pub fn fill(self, val: $orig) -> $orig {
+				if self.0.borrow().is_some() {
+					panic!("wrapper is filled already");
+				}
+				{
+					self.0.borrow_mut().replace(val);
+				}
+				self.unwrap()
+			}
+		}
+	};
+}
+
+#[macro_export]
+macro_rules! dummy_debug {
+	($struct: ident) => {
+		impl std::fmt::Debug for $struct {
+			fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+				f.debug_struct(std::stringify!($struct))
+					.finish_non_exhaustive()
+			}
+		}
+	};
+}
addedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
after · crates/jsonnet-evaluator/src/evaluate.rs
1use crate::BoxedLazyVal;2use crate::{3	bool_val, ArgsBinding, BoxedBinding, BoxedContextCreator, ConstantContextCreator, Context,4	FuncDesc, FunctionDefault, FunctionRhs, NoArgsBinding, Val,5};6use crate::{7	future_wrapper, BoxedFunctionDefault, BoxedFunctionRhs, ContextCreator, ObjMember, ObjValue,8	PlainLazyVal,9};10use jsonnet_parser::{11	ArgsDesc, BinaryOpType, BindSpec, Expr, FieldMember, LiteralType, Member, ObjBody, ParamsDesc,12	Visibility,13};14use std::{15	cell::RefCell,16	collections::{BTreeMap, HashMap},17	rc::Rc,18};1920pub fn evaluate_binding<'t>(21	b: &BindSpec,22	context_creator: BoxedContextCreator,23) -> (String, BoxedBinding) {24	if let Some(args) = &b.params {25		(26			b.name.clone(),27			Rc::new(ArgsBinding {28				expr: *b.value.clone(),29				args: args.clone(),30				context_creator: context_creator.clone(),31			}),32		)33	} else {34		(35			b.name.clone(),36			Rc::new(NoArgsBinding {37				expr: *b.value.clone(),38				context_creator: context_creator.clone(),39			}) as BoxedBinding,40		)41	}42}4344#[derive(Debug)]45struct MethodRhs {46	rhs: Expr,47}48impl FunctionRhs for MethodRhs {49	fn evaluate(&self, ctx: Context) -> Val {50		evaluate(ctx, &self.rhs)51	}52}5354#[derive(Debug)]55struct MethodDefault {}56impl FunctionDefault for MethodDefault {57	fn default(&self, ctx: Context, expr: Expr) -> Val {58		evaluate(ctx, &expr)59	}60}6162pub fn evaluate_method(ctx: Context, expr: &Expr, arg_spec: ParamsDesc) -> Val {63	Val::Func(FuncDesc {64		ctx,65		params: arg_spec,66		eval_rhs: BoxedFunctionRhs(Rc::new(MethodRhs { rhs: expr.clone() })),67		eval_default: BoxedFunctionDefault(Rc::new(MethodDefault {})),68	})69}7071pub fn evaluate_field_name(context: Context, field_name: &jsonnet_parser::FieldName) -> String {72	match field_name {73		jsonnet_parser::FieldName::Fixed(n) => n.clone(),74		jsonnet_parser::FieldName::Dyn(expr) => {75			let name = evaluate(context, expr).unwrap_if_lazy();76			match name {77				Val::Str(n) => n.clone(),78				_ => panic!(79					"dynamic field name can be only evaluated to 'string', got: {:?}",80					name81				),82			}83		}84	}85}8687pub fn evaluate_binary_op(a: &Val, op: BinaryOpType, b: &Val) -> Val {88	match (a, op, b) {89		(Val::Lazy(l), o, r) => evaluate_binary_op(&l.evaluate(), o, r),90		(l, o, Val::Lazy(r)) => evaluate_binary_op(l, o, &r.evaluate()),9192		(Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1.to_owned() + &v2),93		(Val::Str(v1), BinaryOpType::Eq, Val::Str(v2)) => bool_val(v1 == v2),94		(Val::Str(v1), BinaryOpType::Ne, Val::Str(v2)) => bool_val(v1 != v2),9596		(Val::Str(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Str(format!("{}{}", v1, v2)),97		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize)),9899		(Val::Obj(v1), BinaryOpType::Add, Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),100101		(Val::Arr(a), BinaryOpType::Add, Val::Arr(b)) => Val::Arr([&a[..], &b[..]].concat()),102103		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),104		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => Val::Num(v1 / v2),105		(Val::Num(v1), BinaryOpType::Mod, Val::Num(v2)) => Val::Num(v1 % v2),106107		(Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),108		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::Num(v1 - v2),109110		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {111			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)112		}113		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {114			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)115		}116117		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => bool_val(v1 < v2),118		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => bool_val(v1 > v2),119		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => bool_val(v1 <= v2),120		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => bool_val(v1 >= v2),121122		(Val::Num(v1), BinaryOpType::Eq, Val::Num(v2)) => bool_val(v1 == v2),123		(Val::Num(v1), BinaryOpType::Ne, Val::Num(v2)) => bool_val(v1 != v2),124125		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {126			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)127		}128		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {129			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)130		}131		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {132			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)133		}134		_ => panic!("no rules for binary operation: {:?} {:?} {:?}", a, op, b),135	}136}137138future_wrapper!(HashMap<String, BoxedBinding>, FutureNewBindings);139140#[derive(Debug)]141pub struct ObjectContextCreator {142	original: Context,143	future_bindings: FutureNewBindings,144}145146impl ContextCreator for ObjectContextCreator {147	fn create_context(&self, this: &Option<ObjValue>, super_obj: &Option<ObjValue>) -> Context {148		self.original.extend(149			self.future_bindings.clone().unwrap(),150			self.original.dollar().clone().or_else(|| this.clone()),151			this.clone(),152			super_obj.clone(),153		)154	}155}156157// TODO: Asserts158pub fn evaluate_object(context: Context, object: ObjBody) -> ObjValue {159	match object {160		ObjBody::MemberList(members) => {161			let future_bindings = FutureNewBindings::new();162			let binding_context_creator = Rc::new(ObjectContextCreator {163				future_bindings: future_bindings.clone(),164				original: context.clone(),165			});166			let mut bindings: HashMap<String, BoxedBinding> = HashMap::new();167			for (n, b) in members168				.iter()169				.filter_map(|m| match m {170					Member::BindStmt(b) => Some(b.clone()),171					_ => None,172				})173				.map(|b| evaluate_binding(&b, binding_context_creator.clone()))174			{175				bindings.insert(n, b);176			}177			let bindings = future_bindings.fill(bindings);178			let mut new_members = BTreeMap::new();179			for member in members.iter() {180				match member {181					Member::Field(FieldMember {182						name,183						plus,184						params: None,185						visibility,186						value,187					}) => {188						let name = evaluate_field_name(context.clone(), name);189						new_members.insert(190							name,191							ObjMember {192								add: *plus,193								visibility: visibility.clone(),194								invoke: Rc::new(NoArgsBinding {195									context_creator: binding_context_creator.clone(),196									expr: value.clone(),197								}),198							},199						);200					}201					Member::Field(FieldMember {202						name,203						params: Some(params),204						value,205						..206					}) => {207						let name = evaluate_field_name(context.clone(), name);208						new_members.insert(209							name,210							ObjMember {211								add: false,212								visibility: Visibility::Hidden,213								invoke: Rc::new(ArgsBinding {214									expr: value.clone(),215									args: params.clone(),216									context_creator: binding_context_creator.clone(),217								}),218							},219						);220					}221					Member::BindStmt(_) => {}222					Member::AssertStmt(_) => {}223					_ => todo!(),224				}225			}226			ObjValue::new(None, Rc::new(new_members))227		}228		_ => todo!(),229	}230}231232pub fn evaluate(context: Context, expr: &Expr) -> Val {233	use Expr::*;234	match &*expr {235		Literal(t) => Val::Literal(t.clone()),236		Parened(e) => evaluate(context, e),237		Str(v) => Val::Str(v.clone()),238		Num(v) => Val::Num(*v),239		BinaryOp(v1, o, v2) => {240			evaluate_binary_op(&evaluate(context.clone(), v1), *o, &evaluate(context, v2))241		}242		Var(name) => {243			let variable = context.binding(&name);244			let val = variable.evaluate(None, None);245			val246		}247		Index(box value, box index) => {248			match (249				evaluate(context.clone(), value).unwrap_if_lazy(),250				evaluate(context.clone(), index),251			) {252				(Val::Literal(LiteralType::Super), _idx) => todo!(),253				(Val::Literal(LiteralType::This), idx) => match &idx.unwrap_if_lazy() {254					Val::Str(str) => context255						.this()256						.clone()257						.unwrap_or_else(|| panic!("'this' is not defined in current context"))258						.get_raw(str, None)259						.unwrap_or_else(|| {260							panic!(261								"key {} not found in current context 'this' ({:?})",262								str,263								context.this()264							)265						}),266					_ => panic!("bad index"),267				},268				(Val::Obj(v), Val::Str(s)) => v269					.get_raw(&s, None)270					.unwrap_or_else(|| panic!("{} not found in {:?}", s, v)),271				(v, i) => todo!("not implemented: {:?}[{:?}]", v, i.unwrap_if_lazy()),272			}273		}274		LocalExpr(bindings, returned) => {275			let mut new_bindings: HashMap<String, BoxedBinding> = HashMap::new();276			let future_context = Context::new_future();277278			let context_creator = Rc::new(ConstantContextCreator {279				context: future_context.clone(),280			});281			for (k, v) in bindings282				.iter()283				.map(move |b| evaluate_binding(b, context_creator.clone()))284			{285				new_bindings.insert(k, v);286			}287288			let context = context289				.extend(new_bindings, None, None, None)290				.into_future(future_context);291			evaluate(context, &*returned.clone())292		}293		Obj(body) => Val::Obj(evaluate_object(context, body.clone())),294		Apply(box value, ArgsDesc(args)) => {295			let value = evaluate(context.clone(), value).unwrap_if_lazy();296			match value {297				Val::Func(f) => f.evaluate(298					args.clone()299						.into_iter()300						.map(|a| {301							(302								a.0,303								Val::Lazy(BoxedLazyVal(Rc::new(PlainLazyVal {304									context: context.clone(),305									expr: *a.1,306								}))),307							)308						})309						.collect(),310				),311				_ => panic!("{:?} is not a function", value),312			}313		}314		Function(params, body) => evaluate_method(context, body, params.clone()),315		Error(e) => panic!("error: {}", evaluate(context, e)),316		IfElse {317			cond,318			cond_then,319			cond_else,320		} => match evaluate(context.clone(), &cond.0).unwrap_if_lazy() {321			Val::Literal(LiteralType::True) => evaluate(context.clone(), cond_then),322			Val::Literal(LiteralType::False) => match cond_else {323				Some(v) => evaluate(context.clone(), v),324				None => Val::Literal(LiteralType::False),325			},326			v => panic!("if condition evaluated to {:?} (boolean needed instead)", v),327		},328		_ => panic!("evaluation not implemented: {:?}", expr),329	}330}
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -1,59 +1,187 @@
 #![feature(box_syntax, box_patterns)]
+#![feature(type_alias_impl_trait)]
+#![feature(debug_non_exhaustive)]
 
+mod binding;
+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::*;
 
-#[derive(Debug, Clone, PartialEq)]
-pub enum Val {
-	Str(String),
-	Num(f64),
+pub trait FunctionRhs: Debug {
+	fn evaluate(&self, ctx: Context) -> Val;
 }
+dynamic_wrapper!(FunctionRhs, BoxedFunctionRhs);
 
-pub fn evaluate(expr: &Expr) -> Val {
-	use Expr::*;
-	match expr {
-		Parened(e) => evaluate(e),
-		Str(v) => Val::Str(v.clone()),
-		Num(v) => Val::Num(*v),
-		BinaryOp(v1, o, v2) => match (evaluate(v1), o, evaluate(v2)) {
-			(Val::Str(v1), BinaryOpType::Add, Val::Str(v2)) => Val::Str(v1 + &v2),
-			(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => {
-				Val::Str(v1.repeat(v2 as usize))
-			},
-			(Val::Num(v1), BinaryOpType::Add, Val::Num(v2)) => Val::Num(v1 + v2),
-			(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Num(v1 * v2),
-			_ => panic!("Can't evaluate binary op: {:?} {:?} {:?}", v1, o, v2),
-		},
-		_ => panic!("Can't evaluate: {:?}", expr),
-	}
+pub trait FunctionDefault: Debug {
+	fn default(&self, ctx: Context, expr: Expr) -> Val;
 }
+dynamic_wrapper!(FunctionDefault, BoxedFunctionDefault);
 
 #[cfg(test)]
 pub mod tests {
-	use super::{evaluate, Val};
-	use jsonnet_parser::parse;
+	use super::{evaluate, Context, Val};
+	use jsonnet_parser::*;
+
+	// macro_rules! eval {
+	// 	($str: expr) => {
+	// 		evaluate(Context::new(), &parse($str).unwrap())
+	// 	};
+	// }
+	macro_rules! assert_eval {
+		($str: expr) => {
+			assert_eq!(
+				evaluate(Context::new(), &parse($str).unwrap()),
+				Val::Literal(LiteralType::True)
+				)
+		};
+	}
+	macro_rules! assert_json {
+		($str: expr, $out: expr) => {
+			assert_eq!(
+				format!("{}", evaluate(Context::new(), &parse($str).unwrap())),
+				$out
+				)
+		};
+	}
+	macro_rules! assert_eval_neg {
+		($str: expr) => {
+			assert_eq!(
+				evaluate(Context::new(), &parse($str).unwrap()),
+				Val::Literal(LiteralType::False)
+				)
+		};
+	}
+
+	/// Sanity checking, before trusting to another tests
+	#[test]
+	fn equality_operator() {
+		assert_eval!("2 == 2");
+		assert_eval_neg!("2 != 2");
+		assert_eval!("2 != 3");
+		assert_eval_neg!("2 == 3");
+		assert_eval!("'Hello' == 'Hello'");
+		assert_eval_neg!("'Hello' != 'Hello'");
+		assert_eval!("'Hello' != 'World'");
+		assert_eval_neg!("'Hello' == 'World'");
+	}
+
 	#[test]
 	fn math_evaluation() {
-		assert_eq!(evaluate(&parse("2+2*2").unwrap()), Val::Num(6.0));
+		assert_eval!("2 + 2 * 2 == 6");
+		assert_eval!("3 + (2 + 2 * 2) == 9");
+	}
+
+	#[test]
+	fn string_concat() {
+		assert_eval!("'Hello' + 'World' == 'HelloWorld'");
+		assert_eval!("'Hello' * 3 == 'HelloHelloHello'");
+		assert_eval!("'Hello' + 'World' * 3 == 'HelloWorldWorldWorld'");
+	}
+
+	#[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");
+		assert_eval!("local a = 1; local a = 2; a == 2");
+	}
+
+	#[test]
+	fn object_lazyness() {
+		assert_json!("local a = {a:error 'test'}; {}", r#"{}"#);
+	}
+
+	#[test]
+	fn object_inheritance() {
+		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!(
+			r#"
+				{
+					name: "Alice",
+					welcome: "Hello " + self.name + "!",
+				}
+			"#,
+			r#"{"name":"Alice","welcome":"Hello Alice!"}"#
+		);
+		assert_json!(
+			r#"
+				{
+					name: "Alice",
+					welcome: "Hello " + self.name + "!",
+				} + {
+					name: "Bob"
+				}
+			"#,
+			r#"{"name":"Bob","welcome":"Hello Bob!"}"#
+		);
 	}
 
 	#[test]
-	fn math_evaluation_with_parened() {
-		assert_eq!(evaluate(&parse("3+(2+2*2)").unwrap()), Val::Num(9.0));
+	fn functions() {
+		assert_json!(r#"local a = function(b, c = 2) b + c; a(2)"#, "4");
+		assert_json!(
+			r#"local a = function(b, c = "Dear") b + c + d, d = "World"; a("Hello")"#,
+			r#""HelloDearWorld""#
+		);
 	}
 
 	#[test]
-	fn string_concat() {
-		assert_eq!(
-			evaluate(&parse("\"Hello\"+\"World\"").unwrap()),
-			Val::Str("HelloWorld".to_owned()),
+	fn local_methods() {
+		assert_json!(r#"local a(b, c = 2) = b + c; a(2)"#, "4");
+		assert_json!(
+			r#"local a(b, c = "Dear") = b + c + d, d = "World"; a("Hello")"#,
+			r#""HelloDearWorld""#
 		);
 	}
 
 	#[test]
-	fn string_repeat() {
-		assert_eq!(
-			evaluate(&parse("\"Hello\"*3").unwrap()),
-			Val::Str("HelloHelloHello".to_owned()),
+	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 = function (b) {[b]:4}, test: a("test")}"#,
+			r#"{"test":{"test":4}}"#
+		);
+	}
+
+	// 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(),
+		);
+	}
+
+	#[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(),
 		);
 	}
 }
addedcrates/jsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jsonnet-evaluator/src/obj.rs
@@ -0,0 +1,83 @@
+use crate::{dummy_debug, evaluate_binary_op, BoxedBinding, Val};
+use jsonnet_parser::{BinaryOpType, Visibility};
+use std::{
+	collections::{BTreeMap, BTreeSet},
+	rc::Rc,
+};
+
+#[derive(Debug)]
+pub struct ObjMember {
+	pub add: bool,
+	pub visibility: Visibility,
+	pub invoke: BoxedBinding,
+}
+
+#[derive(Debug)]
+pub struct ObjValueInternals {
+	super_obj: Option<ObjValue>,
+	this_entries: Rc<BTreeMap<String, ObjMember>>,
+}
+pub struct ObjValue(Rc<ObjValueInternals>);
+dummy_debug!(ObjValue);
+impl ObjValue {
+	pub fn new(
+		super_obj: Option<ObjValue>,
+		this_entries: Rc<BTreeMap<String, ObjMember>>,
+	) -> ObjValue {
+		ObjValue(Rc::new(ObjValueInternals {
+			super_obj,
+			this_entries,
+		}))
+	}
+	pub fn with_super(&self, super_obj: ObjValue) -> ObjValue {
+		match &self.0.super_obj {
+			None => ObjValue::new(Some(super_obj), self.0.this_entries.clone()),
+			Some(v) => ObjValue::new(Some(v.with_super(super_obj)), self.0.this_entries.clone()),
+		}
+	}
+	pub fn fields(&self) -> BTreeSet<String> {
+		let mut fields = BTreeSet::new();
+		self.0.this_entries.keys().for_each(|k| {
+			fields.insert(k.clone());
+		});
+		if self.0.super_obj.is_some() {
+			for field in self.0.super_obj.clone().unwrap().fields() {
+				fields.insert(field);
+			}
+		}
+		fields
+	}
+	pub 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), Some(s)) => {
+				let our = k
+					.invoke
+					.evaluate(Some(self.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))
+						})
+				} else {
+					Some(our)
+				}
+			}
+			(None, Some(s)) => s.get_raw(key, Some(self.clone())),
+			(None, None) => None,
+		}
+	}
+}
+impl Clone for ObjValue {
+	fn clone(&self) -> Self {
+		ObjValue(self.0.clone())
+	}
+}
+impl PartialEq for ObjValue {
+	fn eq(&self, other: &Self) -> bool {
+		Rc::ptr_eq(&self.0, &other.0)
+	}
+}
addedcrates/jsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/jsonnet-evaluator/src/val.rs
@@ -0,0 +1,216 @@
+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 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())
+	}
+}
+
+#[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,
+}
+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 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()) {
+			new_bindings.insert(
+				name.as_ref().unwrap().clone(),
+				Rc::new(ValBinding { 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() }));
+			}
+		}
+		let ctx = self
+			.ctx
+			.extend(new_bindings, None, None, None)
+			.into_future(future_ctx);
+		self.eval_rhs.evaluate(ctx)
+	}
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum Val {
+	Literal(LiteralType),
+	Str(String),
+	Num(f64),
+	Lazy(BoxedLazyVal),
+	Arr(Vec<Val>),
+	Obj(ObjValue),
+	Func(FuncDesc),
+}
+impl Val {
+	pub fn unwrap_if_lazy(self) -> Self {
+		if let Val::Lazy(v) = self {
+			v.evaluate().unwrap_if_lazy()
+		} else {
+			self
+		}
+	}
+	pub fn type_of(&self) -> &'static str {
+		match self {
+			Val::Str(..) => "string",
+			Val::Num(..) => "number",
+			Val::Arr(..) => "array",
+			Val::Obj(..) => "object",
+			Val::Func(..) => "function",
+			_ => panic!("no native type found"),
+		}
+	}
+}
+impl Display for Val {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		match self {
+			Val::Literal(v) => write!(f, "{}", v)?,
+			Val::Str(str) => write!(f, "\"{}\"", str)?,
+			Val::Num(n) => write!(f, "{}", n)?,
+			Val::Arr(values) => {
+				write!(f, "[")?;
+				let mut first = true;
+				for value in values {
+					if first {
+						first = false;
+					} else {
+						write!(f, ",")?;
+					}
+					write!(f, "{}", value)?;
+				}
+				write!(f, "]")?;
+			}
+			Val::Obj(value) => {
+				write!(f, "{{")?;
+				let mut first = true;
+				for field in value.fields() {
+					if first {
+						first = false;
+					} else {
+						write!(f, ",")?;
+					}
+					write!(f, "\"{}\":", field)?;
+					write!(f, "{}", value.get_raw(&field, None).unwrap())?;
+				}
+				write!(f, "}}")?;
+			}
+			Val::Lazy(lazy) => {
+				write!(f, "{}", lazy.evaluate())?;
+			}
+			Val::Func(_) => {
+				write!(f, "<<FUNC>>")?;
+			}
+			v => panic!("no json equivalent for {:?}", v),
+		};
+		Ok(())
+	}
+}
+
+pub fn bool_val(v: bool) -> Val {
+	if v {
+		Val::Literal(LiteralType::True)
+	} else {
+		Val::Literal(LiteralType::False)
+	}
+}