1use crate::{2 builtin::std_slice,3 error::Error::*,4 evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},5 gc::TraceBox,6 push_frame, throw, with_state, ArrValue, Bindable, Context, ContextCreator, FuncDesc, FuncVal,7 FutureWrapper, GcHashMap, LazyBinding, LazyVal, LazyValValue, ObjValue, ObjValueBuilder,8 ObjectAssertion, Result, Val,9};10use gcmodule::{Cc, Trace};11use jrsonnet_interner::IStr;12use jrsonnet_parser::{13 ArgsDesc, AssertStmt, BindSpec, CompSpec, Expr, ExprLocation, FieldMember, ForSpecData,14 IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc,15};16use jrsonnet_types::ValType;17pub mod operator;1819pub fn evaluate_binding_in_future(20 b: &BindSpec,21 context_creator: FutureWrapper<Context>,22) -> LazyVal {23 let b = b.clone();24 if let Some(params) = &b.params {25 let params = params.clone();2627 #[derive(Trace)]28 struct LazyMethodBinding {29 context_creator: FutureWrapper<Context>,30 name: IStr,31 params: ParamsDesc,32 value: LocExpr,33 }34 impl LazyValValue for LazyMethodBinding {35 fn get(self: Box<Self>) -> Result<Val> {36 Ok(evaluate_method(37 self.context_creator.unwrap(),38 self.name,39 self.params,40 self.value,41 ))42 }43 }4445 LazyVal::new(TraceBox(Box::new(LazyMethodBinding {46 context_creator,47 name: b.name.clone(),48 params,49 value: b.value.clone(),50 })))51 } else {52 #[derive(Trace)]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(TraceBox(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 struct BindableMethodLazyVal {78 this: Option<ObjValue>,79 super_obj: Option<ObjValue>,8081 context_creator: ContextCreator,82 name: IStr,83 params: ParamsDesc,84 value: LocExpr,85 }86 impl LazyValValue for BindableMethodLazyVal {87 fn get(self: Box<Self>) -> Result<Val> {88 Ok(evaluate_method(89 self.context_creator.create(self.this, self.super_obj)?,90 self.name,91 self.params,92 self.value,93 ))94 }95 }9697 #[derive(Trace)]98 struct BindableMethod {99 context_creator: ContextCreator,100 name: IStr,101 params: ParamsDesc,102 value: LocExpr,103 }104 impl Bindable for BindableMethod {105 fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {106 Ok(LazyVal::new(TraceBox(Box::new(BindableMethodLazyVal {107 this,108 super_obj,109110 context_creator: self.context_creator.clone(),111 name: self.name.clone(),112 params: self.params.clone(),113 value: self.value.clone(),114 }))))115 }116 }117118 (119 b.name.clone(),120 LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableMethod {121 context_creator,122 name: b.name.clone(),123 params,124 value: b.value.clone(),125 })))),126 )127 } else {128 #[derive(Trace)]129 struct BindableNamedLazyVal {130 this: Option<ObjValue>,131 super_obj: Option<ObjValue>,132133 context_creator: ContextCreator,134 name: IStr,135 value: LocExpr,136 }137 impl LazyValValue for BindableNamedLazyVal {138 fn get(self: Box<Self>) -> Result<Val> {139 evaluate_named(140 self.context_creator.create(self.this, self.super_obj)?,141 &self.value,142 self.name,143 )144 }145 }146147 #[derive(Trace)]148 struct BindableNamed {149 context_creator: ContextCreator,150 name: IStr,151 value: LocExpr,152 }153 impl Bindable for BindableNamed {154 fn bind(&self, this: Option<ObjValue>, super_obj: Option<ObjValue>) -> Result<LazyVal> {155 Ok(LazyVal::new(TraceBox(Box::new(BindableNamedLazyVal {156 this,157 super_obj,158159 context_creator: self.context_creator.clone(),160 name: self.name.clone(),161 value: self.value.clone(),162 }))))163 }164 }165166 (167 b.name.clone(),168 LazyBinding::Bindable(Cc::new(TraceBox(Box::new(BindableNamed {169 context_creator,170 name: b.name.clone(),171 value: b.value.clone(),172 })))),173 )174 }175}176177pub fn evaluate_method(ctx: Context, name: IStr, params: ParamsDesc, body: LocExpr) -> Val {178 Val::Func(Cc::new(FuncVal::Normal(FuncDesc {179 name,180 ctx,181 params,182 body,183 })))184}185186pub fn evaluate_field_name(187 context: Context,188 field_name: &jrsonnet_parser::FieldName,189) -> Result<Option<IStr>> {190 Ok(match field_name {191 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),192 jrsonnet_parser::FieldName::Dyn(expr) => {193 let value = evaluate(context, expr)?;194 if matches!(value, Val::Null) {195 None196 } else {197 Some(value.try_cast_str("dynamic field name")?)198 }199 }200 })201}202203pub fn evaluate_comp(204 context: Context,205 specs: &[CompSpec],206 callback: &mut impl FnMut(Context) -> Result<()>,207) -> Result<()> {208 match specs.get(0) {209 None => callback(context)?,210 Some(CompSpec::IfSpec(IfSpecData(cond))) => {211 if evaluate(context.clone(), cond)?.try_cast_bool("if spec")? {212 evaluate_comp(context, &specs[1..], callback)?213 }214 }215 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => match evaluate(context.clone(), expr)? {216 Val::Arr(list) => {217 for item in list.iter() {218 evaluate_comp(219 context.clone().with_var(var.clone(), item?.clone()),220 &specs[1..],221 callback,222 )?223 }224 }225 _ => throw!(InComprehensionCanOnlyIterateOverArray),226 },227 }228 Ok(())229}230231pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {232 let new_bindings = FutureWrapper::new();233 let future_this = FutureWrapper::new();234 let context_creator = ContextCreator(context.clone(), new_bindings.clone());235 {236 let mut bindings: GcHashMap<IStr, LazyBinding> = GcHashMap::with_capacity(members.len());237 for (n, b) in members238 .iter()239 .filter_map(|m| match m {240 Member::BindStmt(b) => Some(b.clone()),241 _ => None,242 })243 .map(|b| evaluate_binding(&b, context_creator.clone()))244 {245 bindings.insert(n, b);246 }247 new_bindings.fill(bindings);248 }249250 let mut builder = ObjValueBuilder::new();251 for member in members.iter() {252 match member {253 Member::Field(FieldMember {254 name,255 plus,256 params: None,257 visibility,258 value,259 }) => {260 let name = evaluate_field_name(context.clone(), name)?;261 if name.is_none() {262 continue;263 }264 let name = name.unwrap();265266 #[derive(Trace)]267 struct ObjMemberBinding {268 context_creator: ContextCreator,269 value: LocExpr,270 name: IStr,271 }272 impl Bindable for ObjMemberBinding {273 fn bind(274 &self,275 this: Option<ObjValue>,276 super_obj: Option<ObjValue>,277 ) -> Result<LazyVal> {278 Ok(LazyVal::new_resolved(evaluate_named(279 self.context_creator.create(this, super_obj)?,280 &self.value,281 self.name.clone(),282 )?))283 }284 }285 builder286 .member(name.clone())287 .with_add(*plus)288 .with_visibility(*visibility)289 .with_location(value.1.clone())290 .bindable(TraceBox(Box::new(ObjMemberBinding {291 context_creator: context_creator.clone(),292 value: value.clone(),293 name,294 })));295 }296 Member::Field(FieldMember {297 name,298 params: Some(params),299 value,300 ..301 }) => {302 let name = evaluate_field_name(context.clone(), name)?;303 if name.is_none() {304 continue;305 }306 let name = name.unwrap();307 #[derive(Trace)]308 struct ObjMemberBinding {309 context_creator: ContextCreator,310 value: LocExpr,311 params: ParamsDesc,312 name: IStr,313 }314 impl Bindable for ObjMemberBinding {315 fn bind(316 &self,317 this: Option<ObjValue>,318 super_obj: Option<ObjValue>,319 ) -> Result<LazyVal> {320 Ok(LazyVal::new_resolved(evaluate_method(321 self.context_creator.create(this, super_obj)?,322 self.name.clone(),323 self.params.clone(),324 self.value.clone(),325 )))326 }327 }328 builder329 .member(name.clone())330 .hide()331 .with_location(value.1.clone())332 .bindable(TraceBox(Box::new(ObjMemberBinding {333 context_creator: context_creator.clone(),334 value: value.clone(),335 params: params.clone(),336 name,337 })));338 }339 Member::BindStmt(_) => {}340 Member::AssertStmt(stmt) => {341 #[derive(Trace)]342 struct ObjectAssert {343 context_creator: ContextCreator,344 assert: AssertStmt,345 }346 impl ObjectAssertion for ObjectAssert {347 fn run(348 &self,349 this: Option<ObjValue>,350 super_obj: Option<ObjValue>,351 ) -> Result<()> {352 let ctx = self.context_creator.create(this, super_obj)?;353 evaluate_assert(ctx, &self.assert)354 }355 }356 builder.assert(TraceBox(Box::new(ObjectAssert {357 context_creator: context_creator.clone(),358 assert: stmt.clone(),359 })));360 }361 }362 }363 let this = builder.build();364 future_this.fill(this.clone());365 Ok(this)366}367368pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {369 Ok(match object {370 ObjBody::MemberList(members) => evaluate_member_list_object(context, members)?,371 ObjBody::ObjComp(obj) => {372 let future_this = FutureWrapper::new();373 let mut builder = ObjValueBuilder::new();374 evaluate_comp(context.clone(), &obj.compspecs, &mut |ctx| {375 let new_bindings = FutureWrapper::new();376 let context_creator = ContextCreator(context.clone(), new_bindings.clone());377 let mut bindings: GcHashMap<IStr, LazyBinding> =378 GcHashMap::with_capacity(obj.pre_locals.len() + obj.post_locals.len());379 for (n, b) in obj380 .pre_locals381 .iter()382 .chain(obj.post_locals.iter())383 .map(|b| evaluate_binding(b, context_creator.clone()))384 {385 bindings.insert(n, b);386 }387 new_bindings.fill(bindings.clone());388 let ctx = ctx.extend_unbound(bindings, None, None, None)?;389 let key = evaluate(ctx.clone(), &obj.key)?;390391 match key {392 Val::Null => {}393 Val::Str(n) => {394 #[derive(Trace)]395 struct ObjCompBinding {396 context: Context,397 value: LocExpr,398 }399 impl Bindable for ObjCompBinding {400 fn bind(401 &self,402 this: Option<ObjValue>,403 _super_obj: Option<ObjValue>,404 ) -> Result<LazyVal> {405 Ok(LazyVal::new_resolved(evaluate(406 self.context407 .clone()408 .extend(GcHashMap::new(), None, this, None),409 &self.value,410 )?))411 }412 }413 builder414 .member(n)415 .with_location(obj.value.1.clone())416 .with_add(obj.plus)417 .bindable(TraceBox(Box::new(ObjCompBinding {418 context: ctx,419 value: obj.value.clone(),420 })));421 }422 v => throw!(FieldMustBeStringGot(v.value_type())),423 }424425 Ok(())426 })?;427428 let this = builder.build();429 future_this.fill(this.clone());430 this431 }432 })433}434435pub fn evaluate_apply(436 context: Context,437 value: &LocExpr,438 args: &ArgsDesc,439 loc: &ExprLocation,440 tailstrict: bool,441) -> Result<Val> {442 let value = evaluate(context.clone(), value)?;443 Ok(match value {444 Val::Func(f) => {445 let body = || f.evaluate(context, loc, args, tailstrict);446 if tailstrict {447 body()?448 } else {449 push_frame(loc, || format!("function <{}> call", f.name()), body)?450 }451 }452 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type())),453 })454}455456pub fn evaluate_assert(context: Context, assertion: &AssertStmt) -> Result<()> {457 let value = &assertion.0;458 let msg = &assertion.1;459 let assertion_result = push_frame(460 &value.1,461 || "assertion condition".to_owned(),462 || {463 evaluate(context.clone(), value)?464 .try_cast_bool("assertion condition should be of type `boolean`")465 },466 )?;467 if !assertion_result {468 push_frame(469 &value.1,470 || "assertion failure".to_owned(),471 || {472 if let Some(msg) = msg {473 throw!(AssertionFailed(evaluate(context, msg)?.to_string()?));474 } else {475 throw!(AssertionFailed(Val::Null.to_string()?));476 }477 },478 )?479 }480 Ok(())481}482483pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: IStr) -> Result<Val> {484 use Expr::*;485 let LocExpr(expr, _loc) = lexpr;486 Ok(match &**expr {487 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),488 _ => evaluate(context, lexpr)?,489 })490}491492pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {493 use Expr::*;494 let LocExpr(expr, loc) = expr;495 496 Ok(match &**expr {497 Literal(LiteralType::This) => {498 Val::Obj(context.this().clone().ok_or(CantUseSelfOutsideOfObject)?)499 }500 Literal(LiteralType::Super) => Val::Obj(501 context502 .super_obj()503 .clone()504 .ok_or(NoSuperFound)?505 .with_this(context.this().clone().unwrap()),506 ),507 Literal(LiteralType::Dollar) => {508 Val::Obj(context.dollar().clone().ok_or(NoTopLevelObjectFound)?)509 }510 Literal(LiteralType::True) => Val::Bool(true),511 Literal(LiteralType::False) => Val::Bool(false),512 Literal(LiteralType::Null) => Val::Null,513 Parened(e) => evaluate(context, e)?,514 Str(v) => Val::Str(v.clone()),515 Num(v) => Val::new_checked_num(*v)?,516 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, v1, *o, v2)?,517 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,518 Var(name) => push_frame(519 loc,520 || format!("variable <{}> access", name),521 || context.binding(name.clone())?.evaluate(),522 )?,523 Index(value, index) => {524 match (evaluate(context.clone(), value)?, evaluate(context, index)?) {525 (Val::Obj(v), Val::Str(s)) => {526 let sn = s.clone();527 push_frame(528 loc,529 || format!("field <{}> access", sn),530 || {531 if let Some(v) = v.get(s.clone())? {532 Ok(v)533 } else {534 throw!(NoSuchField(s))535 }536 },537 )?538 }539 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(540 ValType::Obj,541 ValType::Str,542 n.value_type(),543 )),544545 (Val::Arr(v), Val::Num(n)) => {546 if n.fract() > f64::EPSILON {547 throw!(FractionalIndex)548 }549 v.get(n as usize)?550 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?551 }552 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),553 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(554 ValType::Arr,555 ValType::Num,556 n.value_type(),557 )),558559 (Val::Str(s), Val::Num(n)) => Val::Str(560 s.chars()561 .skip(n as usize)562 .take(1)563 .collect::<String>()564 .into(),565 ),566 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(567 ValType::Str,568 ValType::Num,569 n.value_type(),570 )),571572 (v, _) => throw!(CantIndexInto(v.value_type())),573 }574 }575 LocalExpr(bindings, returned) => {576 let mut new_bindings: GcHashMap<IStr, LazyVal> =577 GcHashMap::with_capacity(bindings.len());578 let future_context = Context::new_future();579 for b in bindings {580 new_bindings.insert(581 b.name.clone(),582 evaluate_binding_in_future(b, future_context.clone()),583 );584 }585 let context = context586 .extend_bound(new_bindings)587 .into_future(future_context);588 evaluate(context, &returned.clone())?589 }590 Arr(items) => {591 let mut out = Vec::with_capacity(items.len());592 for item in items {593 594 #[derive(Trace)]595 struct ArrayElement {596 context: Context,597 item: LocExpr,598 }599 impl LazyValValue for ArrayElement {600 fn get(self: Box<Self>) -> Result<Val> {601 evaluate(self.context, &self.item)602 }603 }604 out.push(LazyVal::new(TraceBox(Box::new(ArrayElement {605 context: context.clone(),606 item: item.clone(),607 }))));608 }609 Val::Arr(out.into())610 }611 ArrComp(expr, comp_specs) => {612 let mut out = Vec::new();613 evaluate_comp(context, comp_specs, &mut |ctx| {614 out.push(evaluate(ctx, expr)?);615 Ok(())616 })?;617 Val::Arr(ArrValue::Eager(Cc::new(out)))618 }619 Obj(body) => Val::Obj(evaluate_object(context, body)?),620 ObjExtend(s, t) => evaluate_add_op(621 &evaluate(context.clone(), s)?,622 &Val::Obj(evaluate_object(context, t)?),623 )?,624 Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,625 Function(params, body) => {626 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())627 }628 Intrinsic(name) => Val::Func(Cc::new(FuncVal::Intrinsic(name.clone()))),629 AssertExpr(assert, returned) => {630 evaluate_assert(context.clone(), assert)?;631 evaluate(context, returned)?632 }633 ErrorStmt(e) => push_frame(634 loc,635 || "error statement".to_owned(),636 || {637 throw!(RuntimeError(638 evaluate(context, e)?.try_cast_str("error text should be of type `string`")?,639 ))640 },641 )?,642 IfElse {643 cond,644 cond_then,645 cond_else,646 } => {647 if push_frame(648 loc,649 || "if condition".to_owned(),650 || evaluate(context.clone(), &cond.0)?.try_cast_bool("in if condition"),651 )? {652 evaluate(context, cond_then)?653 } else {654 match cond_else {655 Some(v) => evaluate(context, v)?,656 None => Val::Null,657 }658 }659 }660 Slice(value, desc) => {661 let indexable = evaluate(context.clone(), value)?;662663 fn parse_num(664 context: &Context,665 expr: Option<&LocExpr>,666 desc: &'static str,667 ) -> Result<Option<usize>> {668 Ok(match expr {669 Some(s) => evaluate(context.clone(), s)?670 .try_cast_nullable_num(desc)?671 .map(|v| v as usize),672 None => None,673 })674 }675676 let start = parse_num(&context, desc.start.as_ref(), "start")?;677 let end = parse_num(&context, desc.end.as_ref(), "end")?;678 let step = parse_num(&context, desc.step.as_ref(), "step")?;679680 std_slice(indexable.into_indexable()?, start, end, step)?681 }682 Import(path) => {683 let tmp = loc.clone().0;684 let mut import_location = tmp.to_path_buf();685 import_location.pop();686 push_frame(687 loc,688 || format!("import {:?}", path),689 || with_state(|s| s.import_file(&import_location, path)),690 )?691 }692 ImportStr(path) => {693 let tmp = loc.clone().0;694 let mut import_location = tmp.to_path_buf();695 import_location.pop();696 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)697 }698 })699}