git.delta.rocks / jrsonnet / refs/commits / 8d08861d28f9

difftreelog

source

crates/jrsonnet-evaluator/src/evaluate.rs21.9 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::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + v2).into()),219220		// Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)221		(Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),222		(Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),223224		(Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().to_string()?).into()),225		(o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().to_string()?, s).into()),226227		(Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.extend_from(v1.clone())),228		(Val::Arr(a), Val::Arr(b)) => {229			let mut out = Vec::with_capacity(a.len() + b.len());230			out.extend(a.iter_lazy());231			out.extend(b.iter_lazy());232			Val::Arr(out.into())233		}234		(Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,235		_ => throw!(BinaryOperatorDoesNotOperateOnValues(236			BinaryOpType::Add,237			a.value_type(),238			b.value_type(),239		)),240	})241}242243pub fn evaluate_binary_op_special(244	context: Context,245	a: &LocExpr,246	op: BinaryOpType,247	b: &LocExpr,248) -> Result<Val> {249	Ok(match (evaluate(context.clone(), a)?, op, b) {250		(Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),251		(Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),252		(a, op, eb) => evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?)?,253	})254}255256pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {257	Ok(match (a, op, b) {258		(a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,259260		(a, BinaryOpType::Eq, b) => Val::Bool(equals(a, b)?),261		(a, BinaryOpType::Neq, b) => Val::Bool(!equals(a, b)?),262263		(Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),264265		// Bool X Bool266		(Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),267		(Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),268269		// Str X Str270		(Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),271		(Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),272		(Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),273		(Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),274275		// Num X Num276		(Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,277		(Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {278			if *v2 <= f64::EPSILON {279				throw!(DivisionByZero)280			}281			Val::new_checked_num(v1 / v2)?282		}283284		(Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,285286		(Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),287		(Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),288		(Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),289		(Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),290291		(Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {292			Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)293		}294		(Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {295			Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)296		}297		(Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {298			Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)299		}300		(Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {301			if *v2 < 0.0 {302				throw!(RuntimeError("shift by negative exponent".into()))303			}304			Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)305		}306		(Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {307			if *v2 < 0.0 {308				throw!(RuntimeError("shift by negative exponent".into()))309			}310			Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)311		}312313		_ => throw!(BinaryOperatorDoesNotOperateOnValues(314			op,315			a.value_type(),316			b.value_type(),317		)),318	})319}320321pub fn evaluate_comp(322	context: Context,323	specs: &[CompSpec],324	callback: &mut impl FnMut(Context) -> Result<()>,325) -> Result<()> {326	match specs.get(0) {327		None => callback(context)?,328		Some(CompSpec::IfSpec(IfSpecData(cond))) => {329			if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {330				evaluate_comp(context, &specs[1..], callback)?331			}332		}333		Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {334			Val::Arr(list) => {335				for item in list.iter() {336					evaluate_comp(337						context.clone().with_var(var.clone(), item?.clone()),338						&specs[1..],339						callback,340					)?341				}342			}343			_ => throw!(InComprehensionCanOnlyIterateOverArray),344		},345	}346	Ok(())347}348349pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {350	let new_bindings = FutureWrapper::new();351	let future_this = FutureWrapper::new();352	let context_creator = ContextCreator(context.clone(), new_bindings.clone());353	{354		let mut bindings: FxHashMap<IStr, LazyBinding> =355			FxHashMap::with_capacity_and_hasher(members.len(), BuildHasherDefault::default());356		for (n, b) in members357			.iter()358			.filter_map(|m| match m {359				Member::BindStmt(b) => Some(b.clone()),360				_ => None,361			})362			.map(|b| evaluate_binding(&b, context_creator.clone()))363		{364			bindings.insert(n, b);365		}366		new_bindings.fill(bindings);367	}368369	let mut new_members = FxHashMap::default();370	let mut assertions: Vec<Box<dyn ObjectAssertion>> = Vec::new();371	for member in members.iter() {372		match member {373			Member::Field(FieldMember {374				name,375				plus,376				params: None,377				visibility,378				value,379			}) => {380				let name = evaluate_field_name(context.clone(), name)?;381				if name.is_none() {382					continue;383				}384				let name = name.unwrap();385386				#[derive(Trace)]387				#[trivially_drop]388				struct ObjMemberBinding {389					context_creator: ContextCreator,390					value: LocExpr,391					name: IStr,392				}393				impl Bindable for ObjMemberBinding {394					fn bind(395						&self,396						this: Option<ObjValue>,397						super_obj: Option<ObjValue>,398					) -> Result<LazyVal> {399						Ok(LazyVal::new_resolved(evaluate_named(400							self.context_creator.create(this, super_obj)?,401							&self.value,402							self.name.clone(),403						)?))404					}405				}406				new_members.insert(407					name.clone(),408					ObjMember {409						add: *plus,410						visibility: *visibility,411						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {412							context_creator: context_creator.clone(),413							value: value.clone(),414							name,415						}))),416						location: value.1.clone(),417					},418				);419			}420			Member::Field(FieldMember {421				name,422				params: Some(params),423				value,424				..425			}) => {426				let name = evaluate_field_name(context.clone(), name)?;427				if name.is_none() {428					continue;429				}430				let name = name.unwrap();431				#[derive(Trace)]432				#[trivially_drop]433				struct ObjMemberBinding {434					context_creator: ContextCreator,435					value: LocExpr,436					params: ParamsDesc,437					name: IStr,438				}439				impl Bindable for ObjMemberBinding {440					fn bind(441						&self,442						this: Option<ObjValue>,443						super_obj: Option<ObjValue>,444					) -> Result<LazyVal> {445						Ok(LazyVal::new_resolved(evaluate_method(446							self.context_creator.create(this, super_obj)?,447							self.name.clone(),448							self.params.clone(),449							self.value.clone(),450						)))451					}452				}453				new_members.insert(454					name.clone(),455					ObjMember {456						add: false,457						visibility: Visibility::Hidden,458						invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjMemberBinding {459							context_creator: context_creator.clone(),460							value: value.clone(),461							params: params.clone(),462							name,463						}))),464						location: value.1.clone(),465					},466				);467			}468			Member::BindStmt(_) => {}469			Member::AssertStmt(stmt) => {470				#[derive(Trace)]471				#[trivially_drop]472				struct ObjectAssert {473					context_creator: ContextCreator,474					assert: AssertStmt,475				}476				impl ObjectAssertion for ObjectAssert {477					fn run(478						&self,479						this: Option<ObjValue>,480						super_obj: Option<ObjValue>,481					) -> Result<()> {482						let ctx = self.context_creator.create(this, super_obj)?;483						evaluate_assert(ctx, &self.assert)484					}485				}486				assertions.push(Box::new(ObjectAssert {487					context_creator: context_creator.clone(),488					assert: stmt.clone(),489				}));490			}491		}492	}493	let this = ObjValue::new(None, Gc::new(new_members), Gc::new(assertions));494	future_this.fill(this.clone());495	Ok(this)496}497498pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {499	Ok(match object {500		ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,501		ObjBody::ObjComp(obj) => {502			let future_this = FutureWrapper::new();503			let mut new_members = FxHashMap::default();504			evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {505				let new_bindings = FutureWrapper::new();506				let context_creator = ContextCreator(context.clone(), new_bindings.clone());507				let mut bindings: FxHashMap<IStr, LazyBinding> =508					FxHashMap::with_capacity_and_hasher(509						obj.pre_locals.len() + obj.post_locals.len(),510						BuildHasherDefault::default(),511					);512				for (n, b) in obj513					.pre_locals514					.iter()515					.chain(obj.post_locals.iter())516					.map(|b| evaluate_binding(b, context_creator.clone()))517				{518					bindings.insert(n, b);519				}520				new_bindings.fill(bindings.clone());521				let ctx = ctx.extend_unbound(bindings, None, None, None)?;522				let key = evaluate(ctx.clone(), &obj.key)?;523524				match key {525					Val::Null => {}526					Val::Str(n) => {527						#[derive(Trace)]528						#[trivially_drop]529						struct ObjCompBinding {530							context: Context,531							value: LocExpr,532						}533						impl Bindable for ObjCompBinding {534							fn bind(535								&self,536								this: Option<ObjValue>,537								_super_obj: Option<ObjValue>,538							) -> Result<LazyVal> {539								Ok(LazyVal::new_resolved(evaluate(540									self.context.clone().extend(541										FxHashMap::default(),542										None,543										this,544										None,545									),546									&self.value,547								)?))548							}549						}550						new_members.insert(551							n,552							ObjMember {553								add: false,554								visibility: Visibility::Normal,555								invoke: LazyBinding::Bindable(Gc::new(Box::new(ObjCompBinding {556									context: ctx,557									value: obj.value.clone(),558								}))),559								location: obj.value.1.clone(),560							},561						);562					}563					v => throw!(FieldMustBeStringGot(v.value_type())),564				}565566				Ok(())567			})?;568569			let this = ObjValue::new(None, Gc::new(new_members), Gc::new(Vec::new()));570			future_this.fill(this.clone());571			this572		}573	})574}575576pub fn evaluate_apply(577	context: Context,578	value: &LocExpr,579	args: &ArgsDesc,580	loc: Option<&ExprLocation>,581	tailstrict: bool,582) -> Result<Val> {583	let value = evaluate(context.clone(), value)?;584	Ok(match value {585		Val::Func(f) => {586			let body = || f.evaluate(context, loc, args, tailstrict);587			if tailstrict {588				body()?589			} else {590				push(loc, || format!("function <{}> call", f.name()), body)?591			}592		}593		v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),594	})595}596597pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {598	let value = &assertion.0;599	let msg = &assertion.1;600	let assertion_result = push(601		value.1.as_ref(),602		|| "assertion condition".to_owned(),603		|| {604			evaluate(context.clone(), value)?605				.try_cast_bool("assertion condition should be of type `boolean`")606		},607	)?;608	if !assertion_result {609		push(610			value.1.as_ref(),611			|| "assertion failure".to_owned(),612			|| {613				if let Some(msg) = msg {614					throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));615				} else {616					throw!(AssertionFailed(Val::Null.to_string()?));617				}618			},619		)?620	}621	Ok(())622}623624pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {625	use Expr::*;626	let LocExpr(expr, _loc) = lexpr;627	Ok(match &**expr {628		Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),629		_ => evaluate(context, lexpr)?,630	})631}632633pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {634	use Expr::*;635	let LocExpr(expr, loc) = expr;636	Ok(match &**expr {637		Literal(LiteralType::This) => {638			Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)639		}640		Literal(LiteralType::Super) => Val::Obj(641			context642				.super_obj()643				.clone()644				.ok_or(NoSuperFound)?645				.with_this(context.this().clone().unwrap()),646		),647		Literal(LiteralType::Dollar) => {648			Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)649		}650		Literal(LiteralType::True) => Val::Bool(true),651		Literal(LiteralType::False) => Val::Bool(false),652		Literal(LiteralType::Null) => Val::Null,653		Parened(e) => evaluate(context, e)?,654		Str(v) => Val::Str(v.clone()),655		Num(v) => Val::new_checked_num(*v)?,656		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,657		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,658		Var(name) => push(659			loc.as_ref(),660			|| format!("variable <{}>", name),661			|| context.binding(name.clone())?.evaluate(),662		)?,663		Index(value, index) => {664			match (evaluate(context.clone(), value)?, evaluate(context, index)?) {665				(Val::Obj(v), Val::Str(s)) => {666					let sn = s.clone();667					push(668						loc.as_ref(),669						|| format!("field <{}> access", sn),670						|| {671							if let Some(v) = v.get(s.clone())? {672								Ok(v)673							} else if v.get("__intrinsic_namespace__".into())?.is_some() {674								Ok(Val::Func(Gc::new(FuncVal::Intrinsic(s))))675							} else {676								throw!(NoSuchField(s))677							}678						},679					)?680				}681				(Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(682					ValType::Obj,683					ValType::Str,684					n.value_type(),685				)),686687				(Val::Arr(v), Val::Num(n)) => {688					if n.fract() > f64::EPSILON {689						throw!(FractionalIndex)690					}691					v.get(n as usize)?692						.ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?693				}694				(Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),695				(Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(696					ValType::Arr,697					ValType::Num,698					n.value_type(),699				)),700701				(Val::Str(s), Val::Num(n)) => Val::Str(702					s.chars()703						.skip(n as usize)704						.take(1)705						.collect::<String>()706						.into(),707				),708				(Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(709					ValType::Str,710					ValType::Num,711					n.value_type(),712				)),713714				(v, _) => throw!(CantIndexInto(v.value_type())),715			}716		}717		LocalExpr(bindings, returned) => {718			let mut new_bindings: FxHashMap<IStr, LazyVal> = HashMap::with_capacity_and_hasher(719				bindings.len(),720				BuildHasherDefault::<FxHasher>::default(),721			);722			let future_context = Context::new_future();723			for b in bindings {724				new_bindings.insert(725					b.name.clone(),726					evaluate_binding_in_future(b, future_context.clone()),727				);728			}729			let context = context730				.extend_bound(new_bindings)731				.into_future(future_context);732			evaluate(context, &returned.clone())?733		}734		Arr(items) => {735			let mut out = Vec::with_capacity(items.len());736			for item in items {737				// TODO: Implement ArrValue::Lazy with same context for every element?738				#[derive(Trace)]739				#[trivially_drop]740				struct ArrayElement {741					context: Context,742					item: LocExpr,743				}744				impl LazyValValue for ArrayElement {745					fn get(self: Box<Self>) -> Result<Val> {746						evaluate(self.context, &self.item)747					}748				}749				out.push(LazyVal::new(Box::new(ArrayElement {750					context: context.clone(),751					item: item.clone(),752				})));753			}754			Val::Arr(out.into())755		}756		ArrComp(expr, comp_specs) => {757			let mut out = Vec::new();758			evaluate_comp(context, comp_specs, &mut |ctx| {759				out.push(evaluate(ctx, expr)?);760				Ok(())761			})?;762			Val::Arr(ArrValue::Eager(Gc::new(out)))763		}764		Obj(body) => Val::Obj(evaluate_object(context, body)?),765		ObjExtend(s, t) => evaluate_add_op(766			&evaluate(context.clone(), s)?,767			&Val::Obj(evaluate_object(context, t)?),768		)?,769		Apply(value, args, tailstrict) => {770			evaluate_apply(context, value, args, loc.as_ref(), *tailstrict)?771		}772		Function(params, body) => {773			evaluate_method(context, "anonymous".into(), params.clone(), body.clone())774		}775		Intrinsic(name) => Val::Func(Gc::new(FuncVal::Intrinsic(name.clone()))),776		AssertExpr(assert, returned) => {777			evaluate_assert(context.clone(), assert)?;778			evaluate(context, returned)?779		}780		ErrorStmt(e) => push(781			loc.as_ref(),782			|| "error statement".to_owned(),783			|| {784				throw!(RuntimeError(785					evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,786				))787			},788		)?,789		IfElse {790			cond,791			cond_then,792			cond_else,793		} => {794			if push(795				loc.as_ref(),796				|| "if condition".to_owned(),797				|| evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),798			)? {799				evaluate(context, cond_then)?800			} else {801				match cond_else {802					Some(v) => evaluate(context, v)?,803					None => Val::Null,804				}805			}806		}807		Import(path) => {808			let tmp = loc809				.clone()810				.expect("imports cannot be used without loc_data")811				.0;812			let mut import_location = tmp.to_path_buf();813			import_location.pop();814			push(815				loc.as_ref(),816				|| format!("import {:?}", path),817				|| with_state(|s| s.import_file(&import_location, path)),818			)?819		}820		ImportStr(path) => {821			let tmp = loc822				.clone()823				.expect("imports cannot be used without loc_data")824				.0;825			let mut import_location = tmp.to_path_buf();826			import_location.pop();827			Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)828		}829	})830}