git.delta.rocks / jrsonnet / refs/commits / 028057c85dc5

difftreelog

refactor! remove Lazy from Val

Yaroslav Bolyukin2020-12-01parent: #194642f.patch.diff
in: master
Now lazy is used only in objects/arrays
BREAKING CHANGE: value_type() is never fails

9 files changed

modifiedbindings/jsonnet/src/val_extract.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_extract.rs
+++ b/bindings/jsonnet/src/val_extract.rs
@@ -9,7 +9,7 @@
 
 #[no_mangle]
 pub extern "C" fn jsonnet_json_extract_string(_vm: &EvaluationState, v: &Val) -> *mut c_char {
-	match v.unwrap_if_lazy().unwrap() {
+	match v {
 		Val::Str(s) => CString::new(&*s as &str).unwrap().into_raw(),
 		_ => std::ptr::null_mut(),
 	}
@@ -20,9 +20,9 @@
 	v: &Val,
 	out: &mut c_double,
 ) -> c_int {
-	match v.unwrap_if_lazy().unwrap() {
+	match v {
 		Val::Num(n) => {
-			*out = n;
+			*out = *n;
 			1
 		}
 		_ => 0,
@@ -30,7 +30,7 @@
 }
 #[no_mangle]
 pub extern "C" fn jsonnet_json_extract_bool(_vm: &EvaluationState, v: &Val) -> c_int {
-	match v.unwrap_if_lazy().unwrap() {
+	match v {
 		Val::Bool(false) => 0,
 		Val::Bool(true) => 1,
 		_ => 2,
@@ -38,7 +38,7 @@
 }
 #[no_mangle]
 pub extern "C" fn jsonnet_json_extract_null(_vm: &EvaluationState, v: &Val) -> c_int {
-	match v.unwrap_if_lazy().unwrap() {
+	match v {
 		Val::Null => 1,
 		_ => 0,
 	}
modifiedcrates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/format.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/format.rs
@@ -1,7 +1,8 @@
 //! faster std.format impl
 #![allow(clippy::too_many_arguments)]
 
-use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val, ValType};
+use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val};
+use jrsonnet_types::ValType;
 use thiserror::Error;
 
 #[derive(Debug, Clone, Error)]
@@ -573,7 +574,7 @@
 				);
 			}
 		}
-		ConvTypeV::Char => match value.clone().unwrap_if_lazy()? {
+		ConvTypeV::Char => match value.clone() {
 			Val::Num(n) => tmp_out.push(
 				std::char::from_u32(n as u32)
 					.ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?,
@@ -590,7 +591,7 @@
 				throw!(TypeMismatch(
 					"%c requires number/string",
 					vec![ValType::Num, ValType::Str],
-					value.value_type()?,
+					value.value_type(),
 				));
 			}
 		},
modifiedcrates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -33,9 +33,9 @@
 ) -> Result<()> {
 	use std::fmt::Write;
 	let mtype = options.mtype;
-	match val.unwrap_if_lazy()? {
+	match val {
 		Val::Bool(v) => {
-			if v {
+			if *v {
 				buf.push_str("true");
 			} else {
 				buf.push_str("false");
@@ -63,7 +63,7 @@
 						}
 					}
 					buf.push_str(cur_padding);
-					manifest_json_ex_buf(item, buf, cur_padding, options)?;
+					manifest_json_ex_buf(&item?, buf, cur_padding, options)?;
 				}
 				cur_padding.truncate(old_len);
 
@@ -118,7 +118,6 @@
 			buf.push('}');
 		}
 		Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
-		Val::Lazy(_) => unreachable!(),
 	};
 	Ok(())
 }
modifiedcrates/jrsonnet-evaluator/src/builtin/sort.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/builtin/sort.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs
@@ -46,7 +46,6 @@
 	let mut sort_type = SortKeyType::Unknown;
 	for i in values.iter_mut() {
 		let i = key_getter(i);
-		i.inplace_unwrap()?;
 		match (i, sort_type) {
 			(Val::Str(_), SortKeyType::Unknown) => sort_type = SortKeyType::String,
 			(Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,
modifiedcrates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/evaluate.rs
1use crate::{2	context_creator, error::Error::*, future_wrapper, lazy_val, push, throw, with_state, Context,3	ContextCreator, FuncDesc, FuncVal, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4	ValType,5};6use closure::closure;7use jrsonnet_parser::{8	ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,9	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,10	Visibility,11};12use rustc_hash::FxHashMap;13use std::{collections::HashMap, rc::Rc};1415pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {16	let b = b.clone();17	if let Some(params) = &b.params {18		let params = params.clone();19		(20			b.name.clone(),21			LazyBinding::Bindable(Rc::new(move |this, super_obj| {22				Ok(lazy_val!(23					closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(24						context_creator.0(this.clone(), super_obj.clone())?,25						b.name.clone(),26						params.clone(),27						b.value.clone(),28					)))29				))30			})),31		)32	} else {33		(34			b.name.clone(),35			LazyBinding::Bindable(Rc::new(move |this, super_obj| {36				Ok(lazy_val!(closure!(clone context_creator, clone b, ||37						evaluate_named(38							context_creator.0(this.clone(), super_obj.clone())?,39							&b.value,40							b.name.clone()41						)42				)))43			})),44		)45	}46}4748pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {49	Val::Func(Rc::new(FuncVal::Normal(FuncDesc {50		name,51		ctx,52		params,53		body,54	})))55}5657pub fn evaluate_field_name(58	context: Context,59	field_name: &jrsonnet_parser::FieldName,60) -> Result<Option<Rc<str>>> {61	Ok(match field_name {62		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),63		jrsonnet_parser::FieldName::Dyn(expr) => {64			let lazy = evaluate(context, expr)?;65			let value = lazy.unwrap_if_lazy()?;66			if matches!(value, Val::Null) {67				None68			} else {69				Some(value.try_cast_str("dynamic field name")?)70			}71		}72	})73}7475pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {76	Ok(match (op, b) {77		(o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,78		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),79		(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),80		(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),81		(op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type()?)),82	})83}8485pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {86	Ok(match (a, b) {87		(Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),8889		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)90		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),91		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),9293		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),94		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),9596		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),97		(Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())),98		(Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,99		_ => throw!(BinaryOperatorDoesNotOperateOnValues(100			BinaryOpType::Add,101			a.value_type()?,102			b.value_type()?,103		)),104	})105}106107pub fn evaluate_binary_op_special(108	context: Context,109	a: &LocExpr,110	op: BinaryOpType,111	b: &LocExpr,112) -> Result<Val> {113	Ok(114		match (evaluate(context.clone(), a)?.unwrap_if_lazy()?, op, b) {115			(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),116			(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),117			(a, op, eb) => {118				evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?119			}120		},121	)122}123124pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {125	Ok(match (a, op, b) {126		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,127128		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),129130		// Bool X Bool131		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),132		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),133134		// Str X Str135		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),136		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),137		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),138		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),139140		// Num X Num141		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,142		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {143			if *v2 <= f64::EPSILON {144				throw!(DivisionByZero)145			}146			Val::new_checked_num(v1 / v2)?147		}148149		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,150151		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),152		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),153		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),154		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),155156		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {157			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)158		}159		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {160			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)161		}162		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {163			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)164		}165		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {166			if *v2 < 0.0 {167				throw!(RuntimeError("shift by negative exponent".into()))168			}169			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)170		}171		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {172			if *v2 < 0.0 {173				throw!(RuntimeError("shift by negative exponent".into()))174			}175			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)176		}177178		_ => throw!(BinaryOperatorDoesNotOperateOnValues(179			op,180			a.value_type()?,181			b.value_type()?,182		)),183	})184}185186future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);187future_wrapper!(ObjValue, FutureObjValue);188189pub fn evaluate_comp<T>(190	context: Context,191	value: &impl Fn(Context) -> Result<T>,192	specs: &[CompSpec],193) -> Result<Option<Vec<T>>> {194	Ok(match specs.get(0) {195		None => Some(vec![value(context)?]),196		Some(CompSpec::IfSpec(IfSpecData(cond))) => {197			if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {198				evaluate_comp(context, value, &specs[1..])?199			} else {200				None201			}202		}203		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {204			match evaluate(context.clone(), expr)?.unwrap_if_lazy()? {205				Val::Arr(list) => {206					let mut out = Vec::new();207					for item in list.iter() {208						let item = item.unwrap_if_lazy()?;209						out.push(evaluate_comp(210							context.clone().with_var(var.clone(), item.clone()),211							value,212							&specs[1..],213						)?);214					}215					Some(out.into_iter().flatten().flatten().collect())216				}217				_ => throw!(InComprehensionCanOnlyIterateOverArray),218			}219		}220	})221}222223pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {224	let new_bindings = FutureNewBindings::new();225	let future_this = FutureObjValue::new();226	let context_creator = context_creator!(227		closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {228			Ok(context.clone().extend_unbound(229				new_bindings.clone().unwrap(),230				context.dollar().clone().or_else(||this.clone()),231				Some(this.unwrap()),232				super_obj233			)?)234		})235	);236	{237		let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();238		for (n, b) in members239			.iter()240			.filter_map(|m| match m {241				Member::BindStmt(b) => Some(b.clone()),242				_ => None,243			})244			.map(|b| evaluate_binding(&b, context_creator.clone()))245		{246			bindings.insert(n, b);247		}248		new_bindings.fill(bindings);249	}250251	let mut new_members = HashMap::new();252	for member in members.iter() {253		match member {254			Member::Field(FieldMember {255				name,256				plus,257				params: None,258				visibility,259				value,260			}) => {261				let name = evaluate_field_name(context.clone(), name)?;262				if name.is_none() {263					continue;264				}265				let name = name.unwrap();266				new_members.insert(267					name.clone(),268					ObjMember {269						add: *plus,270						visibility: *visibility,271						invoke: LazyBinding::Bindable(Rc::new(272							closure!(clone name, clone value, clone context_creator, |this, super_obj| {273								Ok(LazyVal::new_resolved(evaluate(274									context_creator.0(this, super_obj)?,275									&value,276								)?))277							}),278						)),279						location: value.1.clone(),280					},281				);282			}283			Member::Field(FieldMember {284				name,285				params: Some(params),286				value,287				..288			}) => {289				let name = evaluate_field_name(context.clone(), name)?;290				if name.is_none() {291					continue;292				}293				let name = name.unwrap();294				new_members.insert(295					name.clone(),296					ObjMember {297						add: false,298						visibility: Visibility::Hidden,299						invoke: LazyBinding::Bindable(Rc::new(300							closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| {301								// TODO: Assert302								Ok(LazyVal::new_resolved(evaluate_method(303									context_creator.0(this, super_obj)?,304									name.clone(),305									params.clone(),306									value.clone(),307								)))308							}),309						)),310						location: value.1.clone(),311					},312				);313			}314			Member::BindStmt(_) => {}315			Member::AssertStmt(_) => {}316		}317	}318	Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))319}320321pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {322	Ok(match object {323		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,324		ObjBody::ObjComp(obj) => {325			let future_this = FutureObjValue::new();326			let mut new_members = HashMap::new();327			for (k, v) in evaluate_comp(328				context.clone(),329				&|ctx| {330					let new_bindings = FutureNewBindings::new();331					let context_creator = context_creator!(332						closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {333							Ok(context.clone().extend_unbound(334								new_bindings.clone().unwrap(),335								context.dollar().clone().or_else(||this.clone()),336								None,337								super_obj338							)?)339						})340					);341					let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();342					for (n, b) in obj343						.pre_locals344						.iter()345						.chain(obj.post_locals.iter())346						.map(|b| evaluate_binding(b, context_creator.clone()))347					{348						bindings.insert(n, b);349					}350					let bindings = new_bindings.fill(bindings);351					let ctx = ctx.extend_unbound(bindings, None, None, None)?;352					let key = evaluate(ctx.clone(), &obj.key)?;353					let value = LazyBinding::Bindable(Rc::new(354						closure!(clone ctx, clone obj.value, |this, _super_obj| {355							Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(FxHashMap::default(), None, this, None), &value)?))356						}),357					));358359					Ok((key, value))360				},361				&obj.compspecs,362			)?363			.unwrap()364			{365				match k {366					Val::Null => {}367					Val::Str(n) => {368						new_members.insert(369							n,370							ObjMember {371								add: false,372								visibility: Visibility::Normal,373								invoke: v,374								location: obj.value.1.clone(),375							},376						);377					}378					v => throw!(FieldMustBeStringGot(v.value_type()?)),379				}380			}381382			future_this.fill(ObjValue::new(None, Rc::new(new_members)))383		}384	})385}386387pub fn evaluate_apply(388	context: Context,389	value: &LocExpr,390	args: &ArgsDesc,391	loc: &Option<ExprLocation>,392	tailstrict: bool,393) -> Result<Val> {394	let lazy = evaluate(context.clone(), value)?;395	let value = lazy.unwrap_if_lazy()?;396	Ok(match value {397		Val::Func(f) => {398			let body = || f.evaluate(context, loc, args, tailstrict);399			if tailstrict {400				body()?401			} else {402				push(loc, || format!("function <{}> call", f.name()), body)?403			}404		}405		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),406	})407}408409pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: Rc<str>) -> Result<Val> {410	use Expr::*;411	let LocExpr(expr, _loc) = lexpr;412	Ok(match &**expr {413		Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),414		_ => evaluate(context, lexpr)?,415	})416}417418pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {419	use Expr::*;420	let LocExpr(expr, loc) = expr;421	Ok(match &**expr {422		Literal(LiteralType::This) => {423			Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)424		}425		Literal(LiteralType::Dollar) => {426			Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)427		}428		Literal(LiteralType::True) => Val::Bool(true),429		Literal(LiteralType::False) => Val::Bool(false),430		Literal(LiteralType::Null) => Val::Null,431		Parened(e) => evaluate(context, e)?,432		Str(v) => Val::Str(v.clone()),433		Num(v) => Val::new_checked_num(*v)?,434		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,435		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,436		Var(name) => push(437			loc,438			|| format!("variable <{}>", name),439			|| Ok(Val::Lazy(context.binding(name.clone())?).unwrap_if_lazy()?),440		)?,441		Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {442			let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;443			context444				.super_obj()445				.clone()446				.expect("no super found")447				.get_raw(name, &context.this().clone().expect("no this found"))?448				.expect("value not found")449		}450		Index(value, index) => {451			match (452				evaluate(context.clone(), value)?.unwrap_if_lazy()?,453				evaluate(context, index)?,454			) {455				(Val::Obj(v), Val::Str(s)) => {456					let sn = s.clone();457					push(458						loc,459						|| format!("field <{}> access", sn),460						|| {461							if let Some(v) = v.get(s.clone())? {462								Ok(v.unwrap_if_lazy()?)463							} else if v.get("__intrinsic_namespace__".into())?.is_some() {464								Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s))))465							} else {466								throw!(NoSuchField(s))467							}468						},469					)?470				}471				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(472					ValType::Obj,473					ValType::Str,474					n.value_type()?,475				)),476477				(Val::Arr(v), Val::Num(n)) => {478					if n.fract() > f64::EPSILON {479						throw!(FractionalIndex)480					}481					v.get(n as usize)482						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?483						.clone()484						.unwrap_if_lazy()?485				}486				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),487				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(488					ValType::Arr,489					ValType::Num,490					n.value_type()?,491				)),492493				(Val::Str(s), Val::Num(n)) => Val::Str(494					s.chars()495						.skip(n as usize)496						.take(1)497						.collect::<String>()498						.into(),499				),500				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(501					ValType::Str,502					ValType::Num,503					n.value_type()?,504				)),505506				(v, _) => throw!(CantIndexInto(v.value_type()?)),507			}508		}509		LocalExpr(bindings, returned) => {510			let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();511			let future_context = Context::new_future();512513			let context_creator = context_creator!(514				closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))515			);516517			for (k, v) in bindings518				.iter()519				.map(|b| evaluate_binding(b, context_creator.clone()))520			{521				new_bindings.insert(k, v);522			}523524			let context = context525				.extend_unbound(new_bindings, None, None, None)?526				.into_future(future_context);527			evaluate(context, &returned.clone())?528		}529		Arr(items) => {530			let mut out = Vec::with_capacity(items.len());531			for item in items {532				out.push(Val::Lazy(lazy_val!(533					closure!(clone context, clone item, || {534						evaluate(context.clone(), &item)535					})536				)));537			}538			Val::Arr(Rc::new(out))539		}540		ArrComp(expr, comp_specs) => Val::Arr(541			// First comp_spec should be for_spec, so no "None" possible here542			Rc::new(evaluate_comp(context, &|ctx| evaluate(ctx, expr), comp_specs)?.unwrap()),543		),544		Obj(body) => Val::Obj(evaluate_object(context, body)?),545		ObjExtend(s, t) => evaluate_add_op(546			&evaluate(context.clone(), s)?,547			&Val::Obj(evaluate_object(context, t)?),548		)?,549		Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,550		Function(params, body) => {551			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())552		}553		Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))),554		AssertExpr(AssertStmt(value, msg), returned) => {555			let assertion_result = push(556				&value.1,557				|| "assertion condition".to_owned(),558				|| {559					evaluate(context.clone(), value)?560						.try_cast_bool("assertion condition should be of type `boolean`")561				},562			)?;563			if assertion_result {564				evaluate(context, returned)?565			} else if let Some(msg) = msg {566				throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));567			} else {568				throw!(AssertionFailed(Val::Null.to_string()?));569			}570		}571		ErrorStmt(e) => push(572			loc,573			|| "error statement".to_owned(),574			|| {575				throw!(RuntimeError(576					evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,577				))578			},579		)?,580		IfElse {581			cond,582			cond_then,583			cond_else,584		} => {585			if push(586				loc,587				|| "if condition".to_owned(),588				|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),589			)? {590				evaluate(context, cond_then)?591			} else {592				match cond_else {593					Some(v) => evaluate(context, v)?,594					None => Val::Null,595				}596			}597		}598		Import(path) => {599			let mut tmp = loc600				.clone()601				.expect("imports cannot be used without loc_data")602				.0;603			let import_location = Rc::make_mut(&mut tmp);604			import_location.pop();605			push(606				loc,607				|| format!("import {:?}", path),608				|| with_state(|s| s.import_file(import_location, path)),609			)?610		}611		ImportStr(path) => {612			let mut tmp = loc613				.clone()614				.expect("imports cannot be used without loc_data")615				.0;616			let import_location = Rc::make_mut(&mut tmp);617			import_location.pop();618			Val::Str(with_state(|s| s.import_file_str(import_location, path))?)619		}620		Literal(LiteralType::Super) => throw!(StandaloneSuper),621	})622}
after · crates/jrsonnet-evaluator/src/evaluate.rs
1use crate::{2	context_creator, error::Error::*, future_wrapper, lazy_val, push, throw, with_state, Context,3	ContextCreator, FuncDesc, FuncVal, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,4};5use closure::closure;6use jrsonnet_parser::{7	ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,8	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,9	Visibility,10};11use jrsonnet_types::ValType;12use rustc_hash::FxHashMap;13use std::{collections::HashMap, rc::Rc};1415pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {16	let b = b.clone();17	if let Some(params) = &b.params {18		let params = params.clone();19		(20			b.name.clone(),21			LazyBinding::Bindable(Rc::new(move |this, super_obj| {22				Ok(lazy_val!(23					closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(24						context_creator.0(this.clone(), super_obj.clone())?,25						b.name.clone(),26						params.clone(),27						b.value.clone(),28					)))29				))30			})),31		)32	} else {33		(34			b.name.clone(),35			LazyBinding::Bindable(Rc::new(move |this, super_obj| {36				Ok(lazy_val!(closure!(clone context_creator, clone b, ||37						evaluate_named(38							context_creator.0(this.clone(), super_obj.clone())?,39							&b.value,40							b.name.clone()41						)42				)))43			})),44		)45	}46}4748pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {49	Val::Func(Rc::new(FuncVal::Normal(FuncDesc {50		name,51		ctx,52		params,53		body,54	})))55}5657pub fn evaluate_field_name(58	context: Context,59	field_name: &jrsonnet_parser::FieldName,60) -> Result<Option<Rc<str>>> {61	Ok(match field_name {62		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),63		jrsonnet_parser::FieldName::Dyn(expr) => {64			let value = evaluate(context, expr)?;65			if matches!(value, Val::Null) {66				None67			} else {68				Some(value.try_cast_str("dynamic field name")?)69			}70		}71	})72}7374pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {75	Ok(match (op, b) {76		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),77		(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),78		(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),79		(op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),80	})81}8283pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {84	Ok(match (a, b) {85		(Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),8687		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)88		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),89		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),9091		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),92		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),9394		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),95		(Val::Arr(a), Val::Arr(b)) => {96			let mut out = Vec::with_capacity(a.len() + b.len());97			out.extend(a.iter_lazy());98			out.extend(b.iter_lazy());99			Val::Arr(out.into())100		}101		(Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,102		_ => throw!(BinaryOperatorDoesNotOperateOnValues(103			BinaryOpType::Add,104			a.value_type(),105			b.value_type(),106		)),107	})108}109110pub fn evaluate_binary_op_special(111	context: Context,112	a: &LocExpr,113	op: BinaryOpType,114	b: &LocExpr,115) -> Result<Val> {116	Ok(match (evaluate(context.clone(), a)?, op, b) {117		(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),118		(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),119		(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,120	})121}122123pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {124	Ok(match (a, op, b) {125		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,126127		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),128129		// Bool X Bool130		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),131		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),132133		// Str X Str134		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),135		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),136		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),137		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),138139		// Num X Num140		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,141		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {142			if *v2 <= f64::EPSILON {143				throw!(DivisionByZero)144			}145			Val::new_checked_num(v1 / v2)?146		}147148		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,149150		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),151		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),152		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),153		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),154155		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {156			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)157		}158		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {159			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)160		}161		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {162			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)163		}164		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {165			if *v2 < 0.0 {166				throw!(RuntimeError("shift by negative exponent".into()))167			}168			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)169		}170		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {171			if *v2 < 0.0 {172				throw!(RuntimeError("shift by negative exponent".into()))173			}174			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)175		}176177		_ => throw!(BinaryOperatorDoesNotOperateOnValues(178			op,179			a.value_type(),180			b.value_type(),181		)),182	})183}184185future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);186future_wrapper!(ObjValue, FutureObjValue);187188pub fn evaluate_comp<T>(189	context: Context,190	value: &impl Fn(Context) -> Result<T>,191	specs: &[CompSpec],192) -> Result<Option<Vec<T>>> {193	Ok(match specs.get(0) {194		None => Some(vec![value(context)?]),195		Some(CompSpec::IfSpec(IfSpecData(cond))) => {196			if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {197				evaluate_comp(context, value, &specs[1..])?198			} else {199				None200			}201		}202		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {203			Val::Arr(list) => {204				let mut out = Vec::new();205				for item in list.iter() {206					out.push(evaluate_comp(207						context.clone().with_var(var.clone(), item?.clone()),208						value,209						&specs[1..],210					)?);211				}212				Some(out.into_iter().flatten().flatten().collect())213			}214			_ => throw!(InComprehensionCanOnlyIterateOverArray),215		},216	})217}218219pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {220	let new_bindings = FutureNewBindings::new();221	let future_this = FutureObjValue::new();222	let context_creator = context_creator!(223		closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {224			Ok(context.clone().extend_unbound(225				new_bindings.clone().unwrap(),226				context.dollar().clone().or_else(||this.clone()),227				Some(this.unwrap()),228				super_obj229			)?)230		})231	);232	{233		let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();234		for (n, b) in members235			.iter()236			.filter_map(|m| match m {237				Member::BindStmt(b) => Some(b.clone()),238				_ => None,239			})240			.map(|b| evaluate_binding(&b, context_creator.clone()))241		{242			bindings.insert(n, b);243		}244		new_bindings.fill(bindings);245	}246247	let mut new_members = HashMap::new();248	for member in members.iter() {249		match member {250			Member::Field(FieldMember {251				name,252				plus,253				params: None,254				visibility,255				value,256			}) => {257				let name = evaluate_field_name(context.clone(), name)?;258				if name.is_none() {259					continue;260				}261				let name = name.unwrap();262				new_members.insert(263					name.clone(),264					ObjMember {265						add: *plus,266						visibility: *visibility,267						invoke: LazyBinding::Bindable(Rc::new(268							closure!(clone name, clone value, clone context_creator, |this, super_obj| {269								Ok(LazyVal::new_resolved(evaluate(270									context_creator.0(this, super_obj)?,271									&value,272								)?))273							}),274						)),275						location: value.1.clone(),276					},277				);278			}279			Member::Field(FieldMember {280				name,281				params: Some(params),282				value,283				..284			}) => {285				let name = evaluate_field_name(context.clone(), name)?;286				if name.is_none() {287					continue;288				}289				let name = name.unwrap();290				new_members.insert(291					name.clone(),292					ObjMember {293						add: false,294						visibility: Visibility::Hidden,295						invoke: LazyBinding::Bindable(Rc::new(296							closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| {297								// TODO: Assert298								Ok(LazyVal::new_resolved(evaluate_method(299									context_creator.0(this, super_obj)?,300									name.clone(),301									params.clone(),302									value.clone(),303								)))304							}),305						)),306						location: value.1.clone(),307					},308				);309			}310			Member::BindStmt(_) => {}311			Member::AssertStmt(_) => {}312		}313	}314	Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))315}316317pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {318	Ok(match object {319		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,320		ObjBody::ObjComp(obj) => {321			let future_this = FutureObjValue::new();322			let mut new_members = HashMap::new();323			for (k, v) in evaluate_comp(324				context.clone(),325				&|ctx| {326					let new_bindings = FutureNewBindings::new();327					let context_creator = context_creator!(328						closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {329							Ok(context.clone().extend_unbound(330								new_bindings.clone().unwrap(),331								context.dollar().clone().or_else(||this.clone()),332								None,333								super_obj334							)?)335						})336					);337					let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();338					for (n, b) in obj339						.pre_locals340						.iter()341						.chain(obj.post_locals.iter())342						.map(|b| evaluate_binding(b, context_creator.clone()))343					{344						bindings.insert(n, b);345					}346					let bindings = new_bindings.fill(bindings);347					let ctx = ctx.extend_unbound(bindings, None, None, None)?;348					let key = evaluate(ctx.clone(), &obj.key)?;349					let value = LazyBinding::Bindable(Rc::new(350						closure!(clone ctx, clone obj.value, |this, _super_obj| {351							Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(FxHashMap::default(), None, this, None), &value)?))352						}),353					));354355					Ok((key, value))356				},357				&obj.compspecs,358			)?359			.unwrap()360			{361				match k {362					Val::Null => {}363					Val::Str(n) => {364						new_members.insert(365							n,366							ObjMember {367								add: false,368								visibility: Visibility::Normal,369								invoke: v,370								location: obj.value.1.clone(),371							},372						);373					}374					v => throw!(FieldMustBeStringGot(v.value_type())),375				}376			}377378			future_this.fill(ObjValue::new(None, Rc::new(new_members)))379		}380	})381}382383pub fn evaluate_apply(384	context: Context,385	value: &LocExpr,386	args: &ArgsDesc,387	loc: &Option<ExprLocation>,388	tailstrict: bool,389) -> Result<Val> {390	let value = evaluate(context.clone(), value)?;391	Ok(match value {392		Val::Func(f) => {393			let body = || f.evaluate(context, loc, args, tailstrict);394			if tailstrict {395				body()?396			} else {397				push(loc, || format!("function <{}> call", f.name()), body)?398			}399		}400		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),401	})402}403404pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: Rc<str>) -> Result<Val> {405	use Expr::*;406	let LocExpr(expr, _loc) = lexpr;407	Ok(match &**expr {408		Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),409		_ => evaluate(context, lexpr)?,410	})411}412413pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {414	use Expr::*;415	let LocExpr(expr, loc) = expr;416	Ok(match &**expr {417		Literal(LiteralType::This) => {418			Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)419		}420		Literal(LiteralType::Dollar) => {421			Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)422		}423		Literal(LiteralType::True) => Val::Bool(true),424		Literal(LiteralType::False) => Val::Bool(false),425		Literal(LiteralType::Null) => Val::Null,426		Parened(e) => evaluate(context, e)?,427		Str(v) => Val::Str(v.clone()),428		Num(v) => Val::new_checked_num(*v)?,429		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,430		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,431		Var(name) => push(432			loc,433			|| format!("variable <{}>", name),434			|| Ok(context.binding(name.clone())?.evaluate()?),435		)?,436		Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {437			let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;438			context439				.super_obj()440				.clone()441				.expect("no super found")442				.get_raw(name, Some(&context.this().clone().expect("no this found")))?443				.expect("value not found")444		}445		Index(value, index) => {446			match (evaluate(context.clone(), value)?, evaluate(context, index)?) {447				(Val::Obj(v), Val::Str(s)) => {448					let sn = s.clone();449					push(450						loc,451						|| format!("field <{}> access", sn),452						|| {453							if let Some(v) = v.get(s.clone())? {454								Ok(v)455							} else if v.get("__intrinsic_namespace__".into())?.is_some() {456								Ok(Val::Func(Rc::new(FuncVal::Intrinsic(s))))457							} else {458								throw!(NoSuchField(s))459							}460						},461					)?462				}463				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(464					ValType::Obj,465					ValType::Str,466					n.value_type(),467				)),468469				(Val::Arr(v), Val::Num(n)) => {470					if n.fract() > f64::EPSILON {471						throw!(FractionalIndex)472					}473					v.get(n as usize)?474						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?475						.clone()476				}477				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),478				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(479					ValType::Arr,480					ValType::Num,481					n.value_type(),482				)),483484				(Val::Str(s), Val::Num(n)) => Val::Str(485					s.chars()486						.skip(n as usize)487						.take(1)488						.collect::<String>()489						.into(),490				),491				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(492					ValType::Str,493					ValType::Num,494					n.value_type(),495				)),496497				(v, _) => throw!(CantIndexInto(v.value_type())),498			}499		}500		LocalExpr(bindings, returned) => {501			let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();502			let future_context = Context::new_future();503504			let context_creator = context_creator!(505				closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))506			);507508			for (k, v) in bindings509				.iter()510				.map(|b| evaluate_binding(b, context_creator.clone()))511			{512				new_bindings.insert(k, v);513			}514515			let context = context516				.extend_unbound(new_bindings, None, None, None)?517				.into_future(future_context);518			evaluate(context, &returned.clone())?519		}520		Arr(items) => {521			let mut out = Vec::with_capacity(items.len());522			for item in items {523				out.push(LazyVal::new(Box::new(524					closure!(clone context, clone item, || {525						evaluate(context.clone(), &item)526					}),527				)));528			}529			Val::Arr(out.into())530		}531		ArrComp(expr, comp_specs) => Val::Arr(532			// First comp_spec should be for_spec, so no "None" possible here533			evaluate_comp(context, &|ctx| evaluate(ctx, expr), comp_specs)?534				.unwrap()535				.into(),536		),537		Obj(body) => Val::Obj(evaluate_object(context, body)?),538		ObjExtend(s, t) => evaluate_add_op(539			&evaluate(context.clone(), s)?,540			&Val::Obj(evaluate_object(context, t)?),541		)?,542		Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,543		Function(params, body) => {544			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())545		}546		Intrinsic(name) => Val::Func(Rc::new(FuncVal::Intrinsic(name.clone()))),547		AssertExpr(AssertStmt(value, msg), returned) => {548			let assertion_result = push(549				&value.1,550				|| "assertion condition".to_owned(),551				|| {552					evaluate(context.clone(), value)?553						.try_cast_bool("assertion condition should be of type `boolean`")554				},555			)?;556			if assertion_result {557				evaluate(context, returned)?558			} else if let Some(msg) = msg {559				throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));560			} else {561				throw!(AssertionFailed(Val::Null.to_string()?));562			}563		}564		ErrorStmt(e) => push(565			loc,566			|| "error statement".to_owned(),567			|| {568				throw!(RuntimeError(569					evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,570				))571			},572		)?,573		IfElse {574			cond,575			cond_then,576			cond_else,577		} => {578			if push(579				loc,580				|| "if condition".to_owned(),581				|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),582			)? {583				evaluate(context, cond_then)?584			} else {585				match cond_else {586					Some(v) => evaluate(context, v)?,587					None => Val::Null,588				}589			}590		}591		Import(path) => {592			let mut tmp = loc593				.clone()594				.expect("imports cannot be used without loc_data")595				.0;596			let import_location = Rc::make_mut(&mut tmp);597			import_location.pop();598			push(599				loc,600				|| format!("import {:?}", path),601				|| with_state(|s| s.import_file(import_location, path)),602			)?603		}604		ImportStr(path) => {605			let mut tmp = loc606				.clone()607				.expect("imports cannot be used without loc_data")608				.0;609			let import_location = Rc::make_mut(&mut tmp);610			import_location.pop();611			Val::Str(with_state(|s| s.import_file_str(import_location, path))?)612		}613		Literal(LiteralType::Super) => throw!(StandaloneSuper),614	})615}
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -22,11 +22,10 @@
 			} else {
 				Number::from_f64(*n).expect("to json number")
 			}),
-			Val::Lazy(v) => (&v.evaluate()?).try_into()?,
 			Val::Arr(a) => {
 				let mut out = Vec::with_capacity(a.len());
 				for item in a.iter() {
-					out.push(item.try_into()?);
+					out.push((&item?).try_into()?);
 				}
 				Self::Array(out)
 			}
@@ -55,9 +54,9 @@
 			Value::Array(a) => {
 				let mut out = Vec::with_capacity(a.len());
 				for v in a {
-					out.push(v.into());
+					out.push(LazyVal::new_resolved(v.into()));
 				}
-				Self::Arr(Rc::new(out))
+				Self::Arr(out.into())
 			}
 			Value::Object(o) => {
 				let mut entries = HashMap::with_capacity(o.len());
modifiedcrates/jrsonnet-evaluator/src/obj.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj.rs
+++ b/crates/jrsonnet-evaluator/src/obj.rs
@@ -102,9 +102,10 @@
 		visible_fields
 	}
 	pub fn get(&self, key: Rc<str>) -> Result<Option<Val>> {
-		Ok(self.get_raw(key, self)?)
+		Ok(self.get_raw(key, None)?)
 	}
-	pub(crate) fn get_raw(&self, key: Rc<str>, real_this: &Self) -> Result<Option<Val>> {
+	pub(crate) fn get_raw(&self, key: Rc<str>, real_this: Option<&Self>) -> Result<Option<Val>> {
+		let real_this = real_this.unwrap_or(self);
 		let cache_key = (key.clone(), Rc::as_ptr(&real_this.0) as usize);
 
 		if let Some(v) = self.0.value_cache.borrow().get(&cache_key) {
@@ -115,7 +116,7 @@
 			(Some(k), Some(s)) => {
 				let our = self.evaluate_this(k, real_this)?;
 				if k.add {
-					s.get_raw(key, real_this)?
+					s.get_raw(key, Some(real_this))?
 						.map_or(Ok(Some(our.clone())), |v| {
 							Ok(Some(evaluate_add_op(&v, &our)?))
 						})
@@ -123,7 +124,7 @@
 					Ok(Some(our))
 				}
 			}
-			(None, Some(s)) => s.get_raw(key, real_this),
+			(None, Some(s)) => s.get_raw(key, Some(real_this)),
 			(None, None) => Ok(None),
 		}?;
 		self.0
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -10,12 +10,8 @@
 	throw, with_state, Context, ObjValue, Result,
 };
 use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LiteralType, LocExpr, ParamsDesc};
-use std::{
-	cell::RefCell,
-	collections::HashMap,
-	fmt::{Debug, Display},
-	rc::Rc,
-};
+use jrsonnet_types::ValType;
+use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};
 
 enum LazyValInternals {
 	Computed(Val),
@@ -166,43 +162,108 @@
 	}
 }
 
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum ValType {
-	Bool,
-	Null,
-	Str,
-	Num,
-	Arr,
-	Obj,
-	Func,
+#[derive(Clone)]
+pub enum ManifestFormat {
+	YamlStream(Box<ManifestFormat>),
+	Yaml(usize),
+	Json(usize),
+	ToString,
+	String,
+}
+
+#[derive(Debug, Clone)]
+pub enum ArrValue {
+	Lazy(Rc<Vec<LazyVal>>),
+	Eager(Rc<Vec<Val>>),
 }
-impl ValType {
-	pub const fn name(&self) -> &'static str {
-		use ValType::*;
+impl ArrValue {
+	pub fn len(&self) -> usize {
 		match self {
-			Bool => "boolean",
-			Null => "null",
-			Str => "string",
-			Num => "number",
-			Arr => "array",
-			Obj => "object",
-			Func => "function",
+			ArrValue::Lazy(l) => l.len(),
+			ArrValue::Eager(e) => e.len(),
 		}
 	}
+
+	pub fn is_empty(&self) -> bool {
+		self.len() == 0
+	}
+
+	pub fn get(&self, index: usize) -> Result<Option<Val>> {
+		match self {
+			ArrValue::Lazy(vec) => {
+				if let Some(v) = vec.get(index) {
+					Ok(Some(v.evaluate()?))
+				} else {
+					Ok(None)
+				}
+			}
+			ArrValue::Eager(vec) => Ok(vec.get(index).cloned()),
+		}
+	}
+
+	pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {
+		match self {
+			ArrValue::Lazy(vec) => vec.get(index).cloned(),
+			ArrValue::Eager(vec) => vec
+				.get(index)
+				.cloned()
+				.map(|val| LazyVal::new_resolved(val)),
+		}
+	}
+
+	pub fn evaluated(&self) -> Result<Rc<Vec<Val>>> {
+		Ok(match self {
+			ArrValue::Lazy(vec) => {
+				let mut out = Vec::with_capacity(vec.len());
+				for item in vec.iter() {
+					out.push(item.evaluate()?);
+				}
+				Rc::new(out)
+			}
+			ArrValue::Eager(vec) => vec.clone(),
+		})
+	}
+
+	pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {
+		(0..self.len()).map(move |idx| match self {
+			ArrValue::Lazy(l) => l[idx].evaluate(),
+			ArrValue::Eager(e) => Ok(e[idx].clone()),
+		})
+	}
+
+	pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {
+		(0..self.len()).map(move |idx| match self {
+			ArrValue::Lazy(l) => l[idx].clone(),
+			ArrValue::Eager(e) => LazyVal::new_resolved(e[idx].clone()),
+		})
+	}
+
+	pub fn reversed(self) -> Self {
+		match self {
+			ArrValue::Lazy(vec) => {
+				let mut out = (&vec as &Vec<_>).clone();
+				out.reverse();
+				Self::Lazy(Rc::new(out))
+			}
+			ArrValue::Eager(vec) => {
+				let mut out = (&vec as &Vec<_>).clone();
+				out.reverse();
+				Self::Eager(Rc::new(out))
+			}
+		}
+	}
 }
-impl Display for ValType {
-	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-		write!(f, "{}", self.name())
+
+impl From<Vec<LazyVal>> for ArrValue {
+	fn from(v: Vec<LazyVal>) -> Self {
+		Self::Lazy(Rc::new(v))
 	}
 }
 
-#[derive(Clone)]
-pub enum ManifestFormat {
-	YamlStream(Box<ManifestFormat>),
-	Yaml(usize),
-	Json(usize),
-	ToString,
-	String,
+impl From<Vec<Val>> for ArrValue {
+	fn from(v: Vec<Val>) -> Self {
+		Self::Eager(Rc::new(v))
+	}
 }
 
 #[derive(Debug, Clone)]
@@ -211,8 +272,7 @@
 	Null,
 	Str(Rc<str>),
 	Num(f64),
-	Lazy(LazyVal),
-	Arr(Rc<Vec<Val>>),
+	Arr(ArrValue),
 	Obj(ObjValue),
 	Func(Rc<FuncVal>),
 }
@@ -237,40 +297,33 @@
 	}
 
 	pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {
-		let this_type = self.value_type()?;
+		let this_type = self.value_type();
 		if this_type != val_type {
 			throw!(TypeMismatch(context, vec![val_type], this_type))
 		} else {
 			Ok(())
 		}
 	}
+	pub fn unwrap_num(self) -> Result<f64> {
+		Ok(matches_unwrap!(self, Self::Num(v), v))
+	}
+	pub fn unwrap_func(self) -> Result<Rc<FuncVal>> {
+		Ok(matches_unwrap!(self, Self::Func(v), v))
+	}
 	pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {
 		self.assert_type(context, ValType::Bool)?;
-		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Self::Bool(v), v))
+		Ok(matches_unwrap!(self, Self::Bool(v), v))
 	}
 	pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {
 		self.assert_type(context, ValType::Str)?;
-		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Self::Str(v), v))
+		Ok(matches_unwrap!(self, Self::Str(v), v))
 	}
 	pub fn try_cast_num(self, context: &'static str) -> Result<f64> {
 		self.assert_type(context, ValType::Num)?;
-		Ok(matches_unwrap!(self.unwrap_if_lazy()?, Self::Num(v), v))
-	}
-	pub fn inplace_unwrap(&mut self) -> Result<()> {
-		while let Self::Lazy(lazy) = self {
-			*self = lazy.evaluate()?;
-		}
-		Ok(())
+		self.unwrap_num()
 	}
-	pub fn unwrap_if_lazy(&self) -> Result<Self> {
-		Ok(if let Self::Lazy(v) = self {
-			v.evaluate()?.unwrap_if_lazy()?
-		} else {
-			self.clone()
-		})
-	}
-	pub fn value_type(&self) -> Result<ValType> {
-		Ok(match self {
+	pub fn value_type(&self) -> ValType {
+		match self {
 			Self::Str(..) => ValType::Str,
 			Self::Num(..) => ValType::Num,
 			Self::Arr(..) => ValType::Arr,
@@ -278,16 +331,15 @@
 			Self::Bool(_) => ValType::Bool,
 			Self::Null => ValType::Null,
 			Self::Func(..) => ValType::Func,
-			Self::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,
-		})
+		}
 	}
 
 	pub fn to_string(&self) -> Result<Rc<str>> {
-		Ok(match self.unwrap_if_lazy()? {
+		Ok(match self {
 			Self::Bool(true) => "true".into(),
 			Self::Bool(false) => "false".into(),
 			Self::Null => "null".into(),
-			Self::Str(s) => s,
+			Self::Str(s) => s.clone(),
 			v => manifest_json_ex(
 				&v,
 				&ManifestJsonOptions {
@@ -325,7 +377,7 @@
 		};
 		let mut out = Vec::with_capacity(arr.len());
 		for i in arr.iter() {
-			out.push(i.manifest(ty)?);
+			out.push(i?.manifest(ty)?);
 		}
 		Ok(out)
 	}
@@ -348,7 +400,7 @@
 				if !arr.is_empty() {
 					for v in arr.iter() {
 						out.push_str("---\n");
-						out.push_str(&v.manifest(format)?);
+						out.push_str(&v?.manifest(format)?);
 						out.push('\n');
 					}
 					out.push_str("...");
@@ -456,7 +508,7 @@
 
 /// Native implementation of `std.primitiveEquals`
 pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {
-	Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {
+	Ok(match (val_a, val_b) {
 		(Val::Bool(a), Val::Bool(b)) => a == b,
 		(Val::Null, Val::Null) => true,
 		(Val::Str(a), Val::Str(b)) => a == b,
@@ -476,10 +528,7 @@
 
 /// Native implementation of `std.equals`
 pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {
-	let val_a = val_a.unwrap_if_lazy()?;
-	let val_b = val_b.unwrap_if_lazy()?;
-
-	if val_a.value_type()? != val_b.value_type()? {
+	if val_a.value_type() != val_b.value_type() {
 		return Ok(false);
 	}
 	match (val_a, val_b) {
@@ -489,7 +538,7 @@
 				return Ok(false);
 			}
 			for (a, b) in a.iter().zip(b.iter()) {
-				if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {
+				if !equals(&a?, &b?)? {
 					return Ok(false);
 				}
 			}
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -97,6 +97,7 @@
 	Mul,
 	Div,
 
+	/// Implemented as intrinsic, put here for completeness
 	Mod,
 
 	Add,