1use std::convert::TryFrom;23use crate::{4 builtin::{std_slice, BUILTINS},5 error::Error::*,6 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},7 gc::TraceBox,8 push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,9 FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder,10 ObjectAssertion, Result, Val,11};12use gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;14use jrsonnet_parser::{15 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,16 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,17};18use jrsonnet_types::ValType;19pub mod operator;2021pub fn evaluate_binding_in_future(22 b: &BindSpec,23 context_creator: FutureWrapper<Context>,24) -> LazyVal {25 let b = b.clone();26 if let Some(params) = &b.params {27 let params = params.clone();2829 #[derive(Trace)]30 struct LazyMethodBinding {31 context_creator: FutureWrapper<Context>,32 name: IStr,33 params: ParamsDesc,34 value: LocExpr,35 }36 impl LazyValValue for LazyMethodBinding {37 fn get(self: Box<Self>) -> Result<Val> {38 Ok(evaluate_method(39 self.context_creator.unwrap(),40 self.name,41 self.params,42 self.value,43 ))44 }45 }4647 LazyVal::new(TraceBox(Box::new(LazyMethodBinding {48 context_creator,49 name: b.name.clone(),50 params,51 value: b.value.clone(),52 })))53 } else {54 #[derive(Trace)]55 struct LazyNamedBinding {56 context_creator: FutureWrapper<Context>,57 name: IStr,58 value: LocExpr,59 }60 impl LazyValValue for LazyNamedBinding {61 fn get(self: Box<Self>) -> Result<Val> {62 evaluate_named(self.context_creator.unwrap(), &self.value, self.name)63 }64 }65 LazyVal::new(TraceBox(Box::new(LazyNamedBinding {66 context_creator,67 name: b.name.clone(),68 value: b.value,69 })))70 }71}7273pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (IStr, LazyBinding) {74 let b = b.clone();75 if let Some(params) = &b.params {76 let params = params.clone();7778 #[derive(Trace)]79 struct BindableMethodLazyVal {80 this: Option<ObjValue>,81 super_obj: Option<ObjValue>,8283 context_creator: ContextCreator,84 name: IStr,85 params: ParamsDesc,86 value: LocExpr,87 }88 impl LazyValValue for BindableMethodLazyVal {89 fn get(self: Box<Self>) -> Result<Val> {90 Ok(evaluate_method(91 self.context_creator.create(self.this, self.super_obj)?,92 self.name,93 self.params,94 self.value,95 ))96 }97 }9899 #[derive(Trace)]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(TraceBox(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(Cc::new(TraceBox(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 struct BindableNamedLazyVal {132 this: Option<ObjValue>,133 super_obj: Option<ObjValue>,134135 context_creator: ContextCreator,136 name: IStr,137 value: LocExpr,138 }139 impl LazyValValue for BindableNamedLazyVal {140 fn get(self: Box<Self>) -> Result<Val> {141 evaluate_named(142 self.context_creator.create(self.this, self.super_obj)?,143 &self.value,144 self.name,145 )146 }147 }148149 #[derive(Trace)]150 struct BindableNamed {151 context_creator: ContextCreator,152 name: IStr,153 value: LocExpr,154 }155 impl Bindable for BindableNamed {156 fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {157 Ok(LazyVal::new(TraceBox(Box::new(BindableNamedLazyVal {158 this,159 super_obj,160161 context_creator: self.context_creator.clone(),162 name: self.name.clone(),163 value: self.value.clone(),164 }))))165 }166 }167168 (169 b.name.clone(),170 LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {171 context_creator,172 name: b.name.clone(),173 value: b.value.clone(),174 })))),175 )176 }177}178179pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {180 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {181 name,182 ctx,183 params,184 body,185 })))186}187188pub fn evaluate_field_name(189 context: Context,190 field_name: &jrsonnet_parser::FieldName,191) -> Result<Option<IStr>> {192 Ok(match field_name {193 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),194 jrsonnet_parser::FieldName::Dyn(expr) => push_frame(195 Some(&expr.1),196 || "evaluating field name".to_string(),197 || {198 let value = evaluate(context, expr)?;199 if matches!(value, Val::Null) {200 Ok(None)201 } else {202 Ok(Some(IStr::try_from(value)?))203 }204 },205 )?,206 })207}208209pub fn evaluate_comp(210 context: Context,211 specs: &[CompSpec],212 callback: &mut impl FnMut(Context) -> Result<()>,213) -> Result<()> {214 match specs.get(0) {215 None => callback(context)?,216 Some(CompSpec::IfSpec(IfSpecData(cond))) => {217 if bool::try_from(evaluate(context.clone(), cond)?)? {218 evaluate_comp(context, &specs[1..], callback)?219 }220 }221 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {222 Val::Arr(list) => {223 for item in list.iter() {224 evaluate_comp(225 context.clone().with_var(var.clone(), item?.clone()),226 &specs[1..],227 callback,228 )?229 }230 }231 _ => throw!(InComprehensionCanOnlyIterateOverArray),232 },233 }234 Ok(())235}236237pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {238 let new_bindings = FutureWrapper::new();239 let future_this = FutureWrapper::new();240 let context_creator = ContextCreator(context.clone(), new_bindings.clone());241 {242 let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());243 for (n, b) in members244 .iter()245 .filter_map(|m| match m {246 Member::BindStmt(b) => Some(b.clone()),247 _ => None,248 })249 .map(|b| evaluate_binding(&b, context_creator.clone()))250 {251 bindings.insert(n, b);252 }253 new_bindings.fill(bindings);254 }255256 let mut builder = ObjValueBuilder::new();257 for member in members.iter() {258 match member {259 Member::Field(FieldMember {260 name,261 plus,262 params: None,263 visibility,264 value,265 }) => {266 let name = evaluate_field_name(context.clone(), name)?;267 if name.is_none() {268 continue;269 }270 let name = name.unwrap();271272 #[derive(Trace)]273 struct ObjMemberBinding {274 context_creator: ContextCreator,275 value: LocExpr,276 name: IStr,277 }278 impl Bindable for ObjMemberBinding {279 fn bind(280 &self,281 this: Option<ObjValue>,282 super_obj: Option<ObjValue>,283 ) -> Result<LazyVal> {284 Ok(LazyVal::new_resolved(evaluate_named(285 self.context_creator.create(this, super_obj)?,286 &self.value,287 self.name.clone(),288 )?))289 }290 }291 builder292 .member(name.clone())293 .with_add(*plus)294 .with_visibility(*visibility)295 .with_location(value.1.clone())296 .bindable(TraceBox(Box::new(ObjMemberBinding {297 context_creator: context_creator.clone(),298 value: value.clone(),299 name,300 })));301 }302 Member::Field(FieldMember {303 name,304 params: Some(params),305 value,306 ..307 }) => {308 let name = evaluate_field_name(context.clone(), name)?;309 if name.is_none() {310 continue;311 }312 let name = name.unwrap();313 #[derive(Trace)]314 struct ObjMemberBinding {315 context_creator: ContextCreator,316 value: LocExpr,317 params: ParamsDesc,318 name: IStr,319 }320 impl Bindable for ObjMemberBinding {321 fn bind(322 &self,323 this: Option<ObjValue>,324 super_obj: Option<ObjValue>,325 ) -> Result<LazyVal> {326 Ok(LazyVal::new_resolved(evaluate_method(327 self.context_creator.create(this, super_obj)?,328 self.name.clone(),329 self.params.clone(),330 self.value.clone(),331 )))332 }333 }334 builder335 .member(name.clone())336 .hide()337 .with_location(value.1.clone())338 .bindable(TraceBox(Box::new(ObjMemberBinding {339 context_creator: context_creator.clone(),340 value: value.clone(),341 params: params.clone(),342 name,343 })));344 }345 Member::BindStmt(_) => {}346 Member::AssertStmt(stmt) => {347 #[derive(Trace)]348 struct ObjectAssert {349 context_creator: ContextCreator,350 assert: AssertStmt,351 }352 impl ObjectAssertion for ObjectAssert {353 fn run(354 &self,355 this: Option<ObjValue>,356 super_obj: Option<ObjValue>,357 ) -> Result<()> {358 let ctx = self.context_creator.create(this, super_obj)?;359 evaluate_assert(ctx, &self.assert)360 }361 }362 builder.assert(TraceBox(Box::new(ObjectAssert {363 context_creator: context_creator.clone(),364 assert: stmt.clone(),365 })));366 }367 }368 }369 let this = builder.build();370 future_this.fill(this.clone());371 Ok(this)372}373374pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {375 Ok(match object {376 ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,377 ObjBody::ObjComp(obj) => {378 let future_this = FutureWrapper::new();379 let mut builder = ObjValueBuilder::new();380 evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {381 let new_bindings = FutureWrapper::new();382 let context_creator = ContextCreator(context.clone(), new_bindings.clone());383 let mut bindings: GcHashMap<IStr, LazyBinding> =384 GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());385 for (n, b) in obj386 .pre_locals387 .iter()388 .chain(obj.post_locals.iter())389 .map(|b| evaluate_binding(b, context_creator.clone()))390 {391 bindings.insert(n, b);392 }393 new_bindings.fill(bindings.clone());394 let ctx = ctx.extend_unbound(bindings, None, None, None)?;395 let key = evaluate(ctx.clone(), &obj.key)?;396397 match key {398 Val::Null => {}399 Val::Str(n) => {400 #[derive(Trace)]401 struct ObjCompBinding {402 context: Context,403 value: LocExpr,404 }405 impl Bindable for ObjCompBinding {406 fn bind(407 &self,408 this: Option<ObjValue>,409 _super_obj: Option<ObjValue>,410 ) -> Result<LazyVal> {411 Ok(LazyVal::new_resolved(evaluate(412 self.context413 .clone()414 .extend(GcHashMap::new(), None, this, None),415 &self.value,416 )?))417 }418 }419 builder420 .member(n)421 .with_location(obj.value.1.clone())422 .with_add(obj.plus)423 .bindable(TraceBox(Box::new(ObjCompBinding {424 context: ctx,425 value: obj.value.clone(),426 })));427 }428 v => throw!(FieldMustBeStringGot(v.value_type())),429 }430431 Ok(())432 })?;433434 let this = builder.build();435 future_this.fill(this.clone());436 this437 }438 })439}440441pub fn evaluate_apply(442 context: Context,443 value: &LocExpr,444 args: &ArgsDesc,445 loc: Option<&ExprLocation>,446 tailstrict: bool,447) -> Result<Val> {448 let value = evaluate(context.clone(), value)?;449 Ok(match value {450 Val::Func(f) => {451 let body = || f.evaluate(context, loc, args, tailstrict);452 if tailstrict {453 body()?454 } else {455 push_frame(loc, || format!("function <{}> call", f.name()), body)?456 }457 }458 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),459 })460}461462pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {463 let value = &assertion.0;464 let msg = &assertion.1;465 let assertion_result = push_frame(466 Some(&value.1),467 || "assertion condition".to_owned(),468 || bool::try_from(evaluate(context.clone(), value)?),469 )?;470 if !assertion_result {471 push_frame(472 Some(&value.1),473 || "assertion failure".to_owned(),474 || {475 if let Some(msg) = msg {476 throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));477 } else {478 throw!(AssertionFailed(Val::Null.to_string()?));479 }480 },481 )?482 }483 Ok(())484}485486pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {487 use Expr::*;488 let LocExpr(expr, _loc) = lexpr;489 Ok(match &**expr {490 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),491 _ => evaluate(context, lexpr)?,492 })493}494495pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {496 use Expr::*;497 let LocExpr(expr, loc) = expr;498 499 Ok(match &**expr {500 Literal(LiteralType::This) => {501 Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)502 }503 Literal(LiteralType::Super) => Val::Obj(504 context505 .super_obj()506 .clone()507 .ok_or(NoSuperFound)?508 .with_this(context.this().clone().unwrap()),509 ),510 Literal(LiteralType::Dollar) => {511 Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)512 }513 Literal(LiteralType::True) => Val::Bool(true),514 Literal(LiteralType::False) => Val::Bool(false),515 Literal(LiteralType::Null) => Val::Null,516 Parened(e) => evaluate(context, e)?,517 Str(v) => Val::Str(v.clone()),518 Num(v) => Val::new_checked_num(*v)?,519 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,520 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,521 Var(name) => push_frame(522 Some(loc),523 || format!("variable <{}> access", name),524 || context.binding(name.clone())?.evaluate(),525 )?,526 Index(value, index) => {527 match (evaluate(context.clone(), value)?, evaluate(context, index)?) {528 (Val::Obj(v), Val::Str(s)) => {529 let sn = s.clone();530 push_frame(531 Some(loc),532 || format!("field <{}> access", sn),533 || {534 if let Some(v) = v.get(s.clone())? {535 Ok(v)536 } else {537 throw!(NoSuchField(s))538 }539 },540 )?541 }542 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(543 ValType::Obj,544 ValType::Str,545 n.value_type(),546 )),547548 (Val::Arr(v), Val::Num(n)) => {549 if n.fract() > f64::EPSILON {550 throw!(FractionalIndex)551 }552 v.get(n as usize)?553 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?554 }555 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),556 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(557 ValType::Arr,558 ValType::Num,559 n.value_type(),560 )),561562 (Val::Str(s), Val::Num(n)) => Val::Str(563 s.chars()564 .skip(n as usize)565 .take(1)566 .collect::<String>()567 .into(),568 ),569 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(570 ValType::Str,571 ValType::Num,572 n.value_type(),573 )),574575 (v, _) => throw!(CantIndexInto(v.value_type())),576 }577 }578 LocalExpr(bindings, returned) => {579 let mut new_bindings: GcHashMap<IStr, LazyVal> =580 GcHashMap::with_capacity(bindings.len());581 let future_context = Context::new_future();582 for b in bindings {583 new_bindings.insert(584 b.name.clone(),585 evaluate_binding_in_future(b, future_context.clone()),586 );587 }588 let context = context589 .extend_bound(new_bindings)590 .into_future(future_context);591 evaluate(context, &returned.clone())?592 }593 Arr(items) => {594 let mut out = Vec::with_capacity(items.len());595 for item in items {596 597 #[derive(Trace)]598 struct ArrayElement {599 context: Context,600 item: LocExpr,601 }602 impl LazyValValue for ArrayElement {603 fn get(self: Box<Self>) -> Result<Val> {604 evaluate(self.context, &self.item)605 }606 }607 out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {608 context: context.clone(),609 item: item.clone(),610 }))));611 }612 Val::Arr(out.into())613 }614 ArrComp(expr, comp_specs) => {615 let mut out = Vec::new();616 evaluate_comp(context, comp_specs, &mut |ctx| {617 out.push(evaluate(ctx, expr)?);618 Ok(())619 })?;620 Val::Arr(ArrValue::Eager(Cc::new(out)))621 }622 Obj(body) => Val::Obj(evaluate_object(context, body)?),623 ObjExtend(s, t) => evaluate_add_op(624 &evaluate(context.clone(), s)?,625 &Val::Obj(evaluate_object(context, t)?),626 )?,627 Apply(value, args, tailstrict) => {628 evaluate_apply(context, value, args, Some(loc), *tailstrict)?629 }630 Function(params, body) => {631 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())632 }633 Intrinsic(name) => Val::Func(FuncVal::StaticBuiltin(634 BUILTINS635 .with(|b| b.get(name).copied())636 .ok_or_else(|| IntrinsicNotFound(name.clone()))?,637 )),638 AssertExpr(assert, returned) => {639 evaluate_assert(context.clone(), assert)?;640 evaluate(context, returned)?641 }642 ErrorStmt(e) => push_frame(643 Some(loc),644 || "error statement".to_owned(),645 || throw!(RuntimeError(IStr::try_from(evaluate(context, e)?)?,)),646 )?,647 IfElse {648 cond,649 cond_then,650 cond_else,651 } => {652 if push_frame(653 Some(loc),654 || "if condition".to_owned(),655 || bool::try_from(evaluate(context.clone(), &cond.0)?),656 )? {657 evaluate(context, cond_then)?658 } else {659 match cond_else {660 Some(v) => evaluate(context, v)?,661 None => Val::Null,662 }663 }664 }665 Slice(value, desc) => {666 let indexable = evaluate(context.clone(), value)?;667668 fn parse_num(669 context: &Context,670 expr: Option<&LocExpr>,671 desc: &'static str,672 ) -> Result<Option<usize>> {673 Ok(match expr {674 Some(s) => evaluate(context.clone(), s)?675 .try_cast_nullable_num(desc)?676 .map(|v| v as usize),677 None => None,678 })679 }680681 let start = parse_num(&context, desc.start.as_ref(), "start")?;682 let end = parse_num(&context, desc.end.as_ref(), "end")?;683 let step = parse_num(&context, desc.step.as_ref(), "step")?;684685 std_slice(indexable.into_indexable()?, start, end, step)?686 }687 Import(path) => {688 let tmp = loc.clone().0;689 let mut import_location = tmp.to_path_buf();690 import_location.pop();691 push_frame(692 Some(loc),693 || format!("import {:?}", path),694 || with_state(|s| s.import_file(&import_location, path)),695 )?696 }697 ImportStr(path) => {698 let tmp = loc.clone().0;699 let mut import_location = tmp.to_path_buf();700 import_location.pop();701 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)702 }703 })704}