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

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate.rs22.0 KiBsourcehistory
1use crate::{2	equals, error::Error::*, push, throw, with_state, ArrValue, Bindable, Context, ContextCreator,3	FuncDesc, FuncVal, FutureWrapper, LazyBinding, LazyVal, LazyValValue, ObjMember, ObjValue,4	ObjectAssertion, Result, Val,5};6use jrsonnet_gc::{Gc, Trace};7use jrsonnet_interner::IStr;8use jrsonnet_parser::{9	ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,10	ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,11	Visibility,12};13use jrsonnet_types::ValType;14use rustc_hash::{FxHashMap, FxHasher};15use std::{collections::HashMap, hash::BuildHasherDefault};1617pub fn evaluate_binding_in_future(18	b: &BindSpec,19	context_creator: FutureWrapper<Context>,20) -> LazyVal {21	let b = b.clone();22	if let Some(params) = &b.params {23		let params = params.clone();2425		#[derive(Trace)]26		#[trivially_drop]27		struct LazyMethodBinding {28			context_creator: FutureWrapper<Context>,29			name: IStr,30			params: ParamsDesc,31			value: LocExpr,32		}33		impl LazyValValue for LazyMethodBinding {34			fn get(self: Box<Self>) -> Result<Val> {35				Ok(evaluate_method(36					self.context_creator.unwrap(),37					self.name,38					self.params,39					self.value,40				))41			}42		}4344		LazyVal::new(Box::new(LazyMethodBinding {45			context_creator,46			name: b.name.clone(),47			params,48			value: b.value.clone(),49		}))50	} else {51		#[derive(Trace)]52		#[trivially_drop]53		struct LazyNamedBinding {54			context_creator: FutureWrapper<Context>,55			name: IStr,56			value: LocExpr,57		}58		impl LazyValValue for LazyNamedBinding {59			fn get(self: Box<Self>) -> Result<Val> {60				evaluate_named(self.context_creator.unwrap(), &self.value, self.name)61			}62		}63		LazyVal::new(Box::new(LazyNamedBinding {64			context_creator,65			name: b.name.clone(),66			value: b.value,67		}))68	}69}7071pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {72	let b = b.clone();73	if let Some(params) = &b.params {74		let params = params.clone();7576		#[derive(Trace)]77		#[trivially_drop]78		struct BindableMethodLazyVal {79			this: Option<ObjValue>,80			super_obj: Option<ObjValue>,8182			context_creator: ContextCreator,83			name: IStr,84			params: ParamsDesc,85			value: LocExpr,86		}87		impl LazyValValue for BindableMethodLazyVal {88			fn get(self: Box<Self>) -> Result<Val> {89				Ok(evaluate_method(90					self.context_creator.create(self.this, self.super_obj)?,91					self.name,92					self.params,93					self.value,94				))95			}96		}9798		#[derive(Trace)]99		#[trivially_drop]100		struct BindableMethod {101			context_creator: ContextCreator,102			name: IStr,103			params: ParamsDesc,104			value: LocExpr,105		}106		impl Bindable for BindableMethod {107			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {108				Ok(LazyVal::new(Box::new(BindableMethodLazyVal {109					this,110					super_obj,111112					context_creator: self.context_creator.clone(),113					name: self.name.clone(),114					params: self.params.clone(),115					value: self.value.clone(),116				})))117			}118		}119120		(121			b.name.clone(),122			LazyBinding::Bindable(Gc::new(Box::new(BindableMethod {123				context_creator,124				name: b.name.clone(),125				params,126				value: b.value.clone(),127			}))),128		)129	} else {130		#[derive(Trace)]131		#[trivially_drop]132		struct BindableNamedLazyVal {133			this: Option<ObjValue>,134			super_obj: Option<ObjValue>,135136			context_creator: ContextCreator,137			name: IStr,138			value: LocExpr,139		}140		impl LazyValValue for BindableNamedLazyVal {141			fn get(self: Box<Self>) -> Result<Val> {142				evaluate_named(143					self.context_creator.create(self.this, self.super_obj)?,144					&self.value,145					self.name,146				)147			}148		}149150		#[derive(Trace)]151		#[trivially_drop]152		struct BindableNamed {153			context_creator: ContextCreator,154			name: IStr,155			value: LocExpr,156		}157		impl Bindable for BindableNamed {158			fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {159				Ok(LazyVal::new(Box::new(BindableNamedLazyVal {160					this,161					super_obj,162163					context_creator: self.context_creator.clone(),164					name: self.name.clone(),165					value: self.value.clone(),166				})))167			}168		}169170		(171			b.name.clone(),172			LazyBinding::Bindable(Gc::new(Box::new(BindableNamed {173				context_creator,174				name: b.name.clone(),175				value: b.value.clone(),176			}))),177		)178	}179}180181pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {182	Val::Func(Gc::new(FuncVal::Normal(FuncDesc {183		name,184		ctx,185		params,186		body,187	})))188}189190pub fn evaluate_field_name(191	context: Context,192	field_name: &jrsonnet_parser::FieldName,193) -> Result<Option<IStr>> {194	Ok(match field_name {195		jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),196		jrsonnet_parser::FieldName::Dyn(expr) => {197			let value = evaluate(context, expr)?;198			if matches!(value, Val::Null) {199				None200			} else {201				Some(value.try_cast_str("dynamic field name")?)202			}203		}204	})205}206207pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {208	Ok(match (op, b) {209		(UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),210		(UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),211		(UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),212		(op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type())),213	})214}215216pub fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {217	Ok(match (a, b) {218		(Val::DebugGcTraceValue(v1), Val::DebugGcTraceValue(v2)) => {219			evaluate_add_op(&v1.value, &v2.value)?220		}221		(Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),222223		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)224		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),225		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),226227		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),228		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),229230		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.extend_from(v1.clone())),231		(Val::Arr(a), Val::Arr(b)) => {232			let mut out = Vec::with_capacity(a.len() + b.len());233			out.extend(a.iter_lazy());234			out.extend(b.iter_lazy());235			Val::Arr(out.into())236		}237		(Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,238		_ => throw!(BinaryOperatorDoesNotOperateOnValues(239			BinaryOpType::Add,240			a.value_type(),241			b.value_type(),242		)),243	})244}245246pub fn evaluate_binary_op_special(247	context: Context,248	a: &LocExpr,249	op: BinaryOpType,250	b: &LocExpr,251) -> Result<Val> {252	Ok(match (evaluate(context.clone(), a)?, op, b) {253		(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),254		(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),255		(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,256	})257}258259pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {260	Ok(match (a, op, b) {261		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,262263		(a, BinaryOpType::Eq, b) => Val::Bool(equals(a, b)?),264		(a, BinaryOpType::Neq, b) => Val::Bool(!equals(a, b)?),265266		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),267268		// Bool X Bool269		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),270		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),271272		// Str X Str273		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),274		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),275		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),276		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),277278		// Num X Num279		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,280		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {281			if *v2 <= f64::EPSILON {282				throw!(DivisionByZero)283			}284			Val::new_checked_num(v1 / v2)?285		}286287		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,288289		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),290		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),291		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),292		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),293294		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {295			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)296		}297		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {298			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)299		}300		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {301			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)302		}303		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {304			if *v2 < 0.0 {305				throw!(RuntimeError("shift by negative exponent".into()))306			}307			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)308		}309		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {310			if *v2 < 0.0 {311				throw!(RuntimeError("shift by negative exponent".into()))312			}313			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)314		}315316		_ => throw!(BinaryOperatorDoesNotOperateOnValues(317			op,318			a.value_type(),319			b.value_type(),320		)),321	})322}323324pub fn evaluate_comp(325	context: Context,326	specs: &[CompSpec],327	callback: &mut impl FnMut(Context) -> Result<()>,328) -> Result<()> {329	match specs.get(0) {330		None => callback(context)?,331		Some(CompSpec::IfSpec(IfSpecData(cond))) => {332			if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {333				evaluate_comp(context, &specs[1..], callback)?334			}335		}336		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {337			Val::Arr(list) => {338				for item in list.iter() {339					evaluate_comp(340						context.clone().with_var(var.clone(), item?.clone()),341						&specs[1..],342						callback,343					)?344				}345			}346			_ => throw!(InComprehensionCanOnlyIterateOverArray),347		},348	}349	Ok(())350}351352pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {353	let new_bindings = FutureWrapper::new();354	let future_this = FutureWrapper::new();355	let context_creator = ContextCreator(context.clone(), new_bindings.clone());356	{357		let mut bindings: FxHashMap<IStr, LazyBinding> =358			FxHashMap::with_capacity_and_hasher(members.len(), BuildHasherDefault::default());359		for (n, b) in members360			.iter()361			.filter_map(|m| match m {362				Member::BindStmt(b) => Some(b.clone()),363				_ => None,364			})365			.map(|b| evaluate_binding(&b, context_creator.clone()))366		{367			bindings.insert(n, b);368		}369		new_bindings.fill(bindings);370	}371372	let mut new_members = FxHashMap::default();373	let mut assertions: Vec<Box<dyn ObjectAssertion>> = Vec::new();374	for member in members.iter() {375		match member {376			Member::Field(FieldMember {377				name,378				plus,379				params: None,380				visibility,381				value,382			}) => {383				let name = evaluate_field_name(context.clone(), name)?;384				if name.is_none() {385					continue;386				}387				let name = name.unwrap();388389				#[derive(Trace)]390				#[trivially_drop]391				struct ObjMemberBinding {392					context_creator: ContextCreator,393					value: LocExpr,394					name: IStr,395				}396				impl Bindable for ObjMemberBinding {397					fn bind(398						&self,399						this: Option<ObjValue>,400						super_obj: Option<ObjValue>,401					) -> Result<LazyVal> {402						Ok(LazyVal::new_resolved(evaluate_named(403							self.context_creator.create(this, super_obj)?,404							&self.value,405							self.name.clone(),406						)?))407					}408				}409				new_members.insert(410					name.clone(),411					ObjMember {412						add: *plus,413						visibility: *visibility,414						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {415							context_creator: context_creator.clone(),416							value: value.clone(),417							name,418						}))),419						location: value.1.clone(),420					},421				);422			}423			Member::Field(FieldMember {424				name,425				params: Some(params),426				value,427				..428			}) => {429				let name = evaluate_field_name(context.clone(), name)?;430				if name.is_none() {431					continue;432				}433				let name = name.unwrap();434				#[derive(Trace)]435				#[trivially_drop]436				struct ObjMemberBinding {437					context_creator: ContextCreator,438					value: LocExpr,439					params: ParamsDesc,440					name: IStr,441				}442				impl Bindable for ObjMemberBinding {443					fn bind(444						&self,445						this: Option<ObjValue>,446						super_obj: Option<ObjValue>,447					) -> Result<LazyVal> {448						Ok(LazyVal::new_resolved(evaluate_method(449							self.context_creator.create(this, super_obj)?,450							self.name.clone(),451							self.params.clone(),452							self.value.clone(),453						)))454					}455				}456				new_members.insert(457					name.clone(),458					ObjMember {459						add: false,460						visibility: Visibility::Hidden,461						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {462							context_creator: context_creator.clone(),463							value: value.clone(),464							params: params.clone(),465							name,466						}))),467						location: value.1.clone(),468					},469				);470			}471			Member::BindStmt(_) => {}472			Member::AssertStmt(stmt) => {473				#[derive(Trace)]474				#[trivially_drop]475				struct ObjectAssert {476					context_creator: ContextCreator,477					assert: AssertStmt,478				}479				impl ObjectAssertion for ObjectAssert {480					fn run(481						&self,482						this: Option<ObjValue>,483						super_obj: Option<ObjValue>,484					) -> Result<()> {485						let ctx = self.context_creator.create(this, super_obj)?;486						evaluate_assert(ctx, &self.assert)487					}488				}489				assertions.push(Box::new(ObjectAssert {490					context_creator: context_creator.clone(),491					assert: stmt.clone(),492				}));493			}494		}495	}496	let this = ObjValue::new(None, Gc::new(new_members), Gc::new(assertions));497	future_this.fill(this.clone());498	Ok(this)499}500501pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {502	Ok(match object {503		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,504		ObjBody::ObjComp(obj) => {505			let future_this = FutureWrapper::new();506			let mut new_members = FxHashMap::default();507			evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {508				let new_bindings = FutureWrapper::new();509				let context_creator = ContextCreator(context.clone(), new_bindings.clone());510				let mut bindings: FxHashMap<IStr, LazyBinding> =511					FxHashMap::with_capacity_and_hasher(512						obj.pre_locals.len() + obj.post_locals.len(),513						BuildHasherDefault::default(),514					);515				for (n, b) in obj516					.pre_locals517					.iter()518					.chain(obj.post_locals.iter())519					.map(|b| evaluate_binding(b, context_creator.clone()))520				{521					bindings.insert(n, b);522				}523				new_bindings.fill(bindings.clone());524				let ctx = ctx.extend_unbound(bindings, None, None, None)?;525				let key = evaluate(ctx.clone(), &obj.key)?;526527				match key {528					Val::Null => {}529					Val::Str(n) => {530						#[derive(Trace)]531						#[trivially_drop]532						struct ObjCompBinding {533							context: Context,534							value: LocExpr,535						}536						impl Bindable for ObjCompBinding {537							fn bind(538								&self,539								this: Option<ObjValue>,540								_super_obj: Option<ObjValue>,541							) -> Result<LazyVal> {542								Ok(LazyVal::new_resolved(evaluate(543									self.context.clone().extend(544										FxHashMap::default(),545										None,546										this,547										None,548									),549									&self.value,550								)?))551							}552						}553						new_members.insert(554							n,555							ObjMember {556								add: false,557								visibility: Visibility::Normal,558								invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjCompBinding {559									context: ctx,560									value: obj.value.clone(),561								}))),562								location: obj.value.1.clone(),563							},564						);565					}566					v => throw!(FieldMustBeStringGot(v.value_type())),567				}568569				Ok(())570			})?;571572			let this = ObjValue::new(None, Gc::new(new_members), Gc::new(Vec::new()));573			future_this.fill(this.clone());574			this575		}576	})577}578579pub fn evaluate_apply(580	context: Context,581	value: &LocExpr,582	args: &ArgsDesc,583	loc: Option<&ExprLocation>,584	tailstrict: bool,585) -> Result<Val> {586	let value = evaluate(context.clone(), value)?;587	Ok(match value {588		Val::Func(f) => {589			let body = || f.evaluate(context, loc, args, tailstrict);590			if tailstrict {591				body()?592			} else {593				push(loc, || format!("function <{}> call", f.name()), body)?594			}595		}596		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),597	})598}599600pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {601	let value = &assertion.0;602	let msg = &assertion.1;603	let assertion_result = push(604		value.1.as_ref(),605		|| "assertion condition".to_owned(),606		|| {607			evaluate(context.clone(), value)?608				.try_cast_bool("assertion condition should be of type `boolean`")609		},610	)?;611	if !assertion_result {612		push(613			value.1.as_ref(),614			|| "assertion failure".to_owned(),615			|| {616				if let Some(msg) = msg {617					throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));618				} else {619					throw!(AssertionFailed(Val::Null.to_string()?));620				}621			},622		)?623	}624	Ok(())625}626627pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {628	use Expr::*;629	let LocExpr(expr, _loc) = lexpr;630	Ok(match &**expr {631		Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),632		_ => evaluate(context, lexpr)?,633	})634}635636pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {637	use Expr::*;638	let LocExpr(expr, loc) = expr;639	Ok(match &**expr {640		Literal(LiteralType::This) => {641			Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)642		}643		Literal(LiteralType::Super) => Val::Obj(644			context645				.super_obj()646				.clone()647				.ok_or(NoSuperFound)?648				.with_this(context.this().clone().unwrap()),649		),650		Literal(LiteralType::Dollar) => {651			Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)652		}653		Literal(LiteralType::True) => Val::Bool(true),654		Literal(LiteralType::False) => Val::Bool(false),655		Literal(LiteralType::Null) => Val::Null,656		Parened(e) => evaluate(context, e)?,657		Str(v) => Val::Str(v.clone()),658		Num(v) => Val::new_checked_num(*v)?,659		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,660		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,661		Var(name) => push(662			loc.as_ref(),663			|| format!("variable <{}>", name),664			|| context.binding(name.clone())?.evaluate(),665		)?,666		Index(value, index) => {667			match (evaluate(context.clone(), value)?, evaluate(context, index)?) {668				(Val::Obj(v), Val::Str(s)) => {669					let sn = s.clone();670					push(671						loc.as_ref(),672						|| format!("field <{}> access", sn),673						|| {674							if let Some(v) = v.get(s.clone())? {675								Ok(v)676							} else if v.get("__intrinsic_namespace__".into())?.is_some() {677								Ok(Val::Func(Gc::new(FuncVal::Intrinsic(s))))678							} else {679								throw!(NoSuchField(s))680							}681						},682					)?683				}684				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(685					ValType::Obj,686					ValType::Str,687					n.value_type(),688				)),689690				(Val::Arr(v), Val::Num(n)) => {691					if n.fract() > f64::EPSILON {692						throw!(FractionalIndex)693					}694					v.get(n as usize)?695						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?696				}697				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),698				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(699					ValType::Arr,700					ValType::Num,701					n.value_type(),702				)),703704				(Val::Str(s), Val::Num(n)) => Val::Str(705					s.chars()706						.skip(n as usize)707						.take(1)708						.collect::<String>()709						.into(),710				),711				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(712					ValType::Str,713					ValType::Num,714					n.value_type(),715				)),716717				(v, _) => throw!(CantIndexInto(v.value_type())),718			}719		}720		LocalExpr(bindings, returned) => {721			let mut new_bindings: FxHashMap<IStr, LazyVal> = HashMap::with_capacity_and_hasher(722				bindings.len(),723				BuildHasherDefault::<FxHasher>::default(),724			);725			let future_context = Context::new_future();726			for b in bindings {727				new_bindings.insert(728					b.name.clone(),729					evaluate_binding_in_future(b, future_context.clone()),730				);731			}732			let context = context733				.extend_bound(new_bindings)734				.into_future(future_context);735			evaluate(context, &returned.clone())?736		}737		Arr(items) => {738			let mut out = Vec::with_capacity(items.len());739			for item in items {740				// TODO: Implement ArrValue::Lazy with same context for every element?741				#[derive(Trace)]742				#[trivially_drop]743				struct ArrayElement {744					context: Context,745					item: LocExpr,746				}747				impl LazyValValue for ArrayElement {748					fn get(self: Box<Self>) -> Result<Val> {749						evaluate(self.context, &self.item)750					}751				}752				out.push(LazyVal::new(Box::new(ArrayElement {753					context: context.clone(),754					item: item.clone(),755				})));756			}757			Val::Arr(out.into())758		}759		ArrComp(expr, comp_specs) => {760			let mut out = Vec::new();761			evaluate_comp(context, comp_specs, &mut |ctx| {762				out.push(evaluate(ctx, expr)?);763				Ok(())764			})?;765			Val::Arr(ArrValue::Eager(Gc::new(out)))766		}767		Obj(body) => Val::Obj(evaluate_object(context, body)?),768		ObjExtend(s, t) => evaluate_add_op(769			&evaluate(context.clone(), s)?,770			&Val::Obj(evaluate_object(context, t)?),771		)?,772		Apply(value, args, tailstrict) => {773			evaluate_apply(context, value, args, loc.as_ref(), *tailstrict)?774		}775		Function(params, body) => {776			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())777		}778		Intrinsic(name) => Val::Func(Gc::new(FuncVal::Intrinsic(name.clone()))),779		AssertExpr(assert, returned) => {780			evaluate_assert(context.clone(), assert)?;781			evaluate(context, returned)?782		}783		ErrorStmt(e) => push(784			loc.as_ref(),785			|| "error statement".to_owned(),786			|| {787				throw!(RuntimeError(788					evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,789				))790			},791		)?,792		IfElse {793			cond,794			cond_then,795			cond_else,796		} => {797			if push(798				loc.as_ref(),799				|| "if condition".to_owned(),800				|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),801			)? {802				evaluate(context, cond_then)?803			} else {804				match cond_else {805					Some(v) => evaluate(context, v)?,806					None => Val::Null,807				}808			}809		}810		Import(path) => {811			let tmp = loc812				.clone()813				.expect("imports cannot be used without loc_data")814				.0;815			let mut import_location = tmp.to_path_buf();816			import_location.pop();817			push(818				loc.as_ref(),819				|| format!("import {:?}", path),820				|| with_state(|s| s.import_file(&import_location, path)),821			)?822		}823		ImportStr(path) => {824			let tmp = loc825				.clone()826				.expect("imports cannot be used without loc_data")827				.0;828			let mut import_location = tmp.to_path_buf();829			import_location.pop();830			Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)831		}832	})833}