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 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 269 (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 273 (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 279 (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 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}