1use crate::{2 builtin::{3 format::{format_arr, format_obj},4 manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType},5 },6 context_creator, equals,7 error::Error::*,8 future_wrapper, lazy_val, parse_args, parse_function_call, primitive_equals, push, throw,9 with_state, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, ObjMember,10 ObjValue, Result, Val, ValType,11};12use closure::closure;13use jrsonnet_parser::{14 ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,15 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,16 Visibility,17};18use std::{cmp::Ordering, collections::HashMap, path::PathBuf, rc::Rc};1920pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {21 let b = b.clone();22 if let Some(params) = &b.params {23 let params = params.clone();24 (25 b.name.clone(),26 LazyBinding::Bindable(Rc::new(move |this, super_obj| {27 Ok(lazy_val!(28 closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(29 context_creator.0(this.clone(), super_obj.clone())?,30 b.name.clone(),31 params.clone(),32 b.value.clone(),33 )))34 ))35 })),36 )37 } else {38 (39 b.name.clone(),40 LazyBinding::Bindable(Rc::new(move |this, super_obj| {41 Ok(lazy_val!(closure!(clone context_creator, clone b, ||42 evaluate_named(43 context_creator.0(this.clone(), super_obj.clone())?,44 &b.value,45 b.name.clone()46 )47 )))48 })),49 )50 }51}5253pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {54 Val::Func(Rc::new(FuncDesc {55 name,56 ctx,57 params,58 body,59 }))60}6162pub fn evaluate_field_name(63 context: Context,64 field_name: &jrsonnet_parser::FieldName,65) -> Result<Option<Rc<str>>> {66 Ok(match field_name {67 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),68 jrsonnet_parser::FieldName::Dyn(expr) => {69 let lazy = evaluate(context, expr)?;70 let value = lazy.unwrap_if_lazy()?;71 if matches!(value, Val::Null) {72 None73 } else {74 Some(value.try_cast_str("dynamic field name")?)75 }76 }77 })78}7980pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {81 Ok(match (op, b) {82 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,83 (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),84 (UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),85 (UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),86 (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type()?)),87 })88}8990pub(crate) fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {91 Ok(match (a, b) {92 (Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + &v2).into()),9394 95 (Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),96 (Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),9798 (Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().into_string()?).into()),99 (o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().into_string()?, s).into()),100101 (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),102 (Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())),103 (Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,104 _ => throw!(BinaryOperatorDoesNotOperateOnValues(105 BinaryOpType::Add,106 a.value_type()?,107 b.value_type()?,108 )),109 })110}111112pub fn evaluate_binary_op_special(113 context: Context,114 a: &LocExpr,115 op: BinaryOpType,116 b: &LocExpr,117) -> Result<Val> {118 Ok(119 match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {120 (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),121 (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),122 (a, op, eb) => {123 evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?124 }125 },126 )127}128129pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {130 Ok(match (a, op, b) {131 (a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,132133 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),134135 136 (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),137 (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),138139 140 (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),141 (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),142 (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),143 (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),144145 146 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,147 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {148 if *v2 <= f64::EPSILON {149 throw!(DivisionByZero)150 }151 Val::new_checked_num(v1 / v2)?152 }153154 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,155156 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),157 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),158 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),159 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),160161 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {162 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)163 }164 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {165 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)166 }167 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {168 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)169 }170 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {171 if *v2 < 0.0 {172 throw!(RuntimeError("shift by negative exponent".into()))173 }174 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)175 }176 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {177 if *v2 < 0.0 {178 throw!(RuntimeError("shift by negative exponent".into()))179 }180 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)181 }182183 _ => throw!(BinaryOperatorDoesNotOperateOnValues(184 op,185 a.value_type()?,186 b.value_type()?,187 )),188 })189}190191future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);192future_wrapper!(ObjValue, FutureObjValue);193194pub fn evaluate_comp<T>(195 context: Context,196 value: &impl Fn(Context) -> Result<T>,197 specs: &[CompSpec],198) -> Result<Option<Vec<T>>> {199 Ok(match specs.get(0) {200 None => Some(vec![value(context)?]),201 Some(CompSpec::IfSpec(IfSpecData(cond))) => {202 if evaluate(context.clone(), &cond)?.try_cast_bool("if spec")? {203 evaluate_comp(context, value, &specs[1..])?204 } else {205 None206 }207 }208 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {209 match evaluate(context.clone(), &expr)?.unwrap_if_lazy()? {210 Val::Arr(list) => {211 let mut out = Vec::new();212 for item in list.iter() {213 let item = item.unwrap_if_lazy()?;214 out.push(evaluate_comp(215 context.clone().with_var(var.clone(), item.clone()),216 value,217 &specs[1..],218 )?);219 }220 Some(out.into_iter().flatten().flatten().collect())221 }222 _ => throw!(InComprehensionCanOnlyIterateOverArray),223 }224 }225 })226}227228pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {229 let new_bindings = FutureNewBindings::new();230 let future_this = FutureObjValue::new();231 let context_creator = context_creator!(232 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {233 Ok(context.clone().extend_unbound(234 new_bindings.clone().unwrap(),235 context.dollar().clone().or_else(||this.clone()),236 Some(this.unwrap()),237 super_obj238 )?)239 })240 );241 {242 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();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 new_members = HashMap::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();271 new_members.insert(272 name.clone(),273 ObjMember {274 add: *plus,275 visibility: *visibility,276 invoke: LazyBinding::Bindable(Rc::new(277 closure!(clone name, clone value, clone context_creator, |this, super_obj| {278 Ok(LazyVal::new_resolved(evaluate(279 context_creator.0(this, super_obj)?,280 &value,281 )?))282 }),283 )),284 location: value.1.clone(),285 },286 );287 }288 Member::Field(FieldMember {289 name,290 params: Some(params),291 value,292 ..293 }) => {294 let name = evaluate_field_name(context.clone(), &name)?;295 if name.is_none() {296 continue;297 }298 let name = name.unwrap();299 new_members.insert(300 name.clone(),301 ObjMember {302 add: false,303 visibility: Visibility::Hidden,304 invoke: LazyBinding::Bindable(Rc::new(305 closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| {306 307 Ok(LazyVal::new_resolved(evaluate_method(308 context_creator.0(this, super_obj)?,309 name.clone(),310 params.clone(),311 value.clone(),312 )))313 }),314 )),315 location: value.1.clone(),316 },317 );318 }319 Member::BindStmt(_) => {}320 Member::AssertStmt(_) => {}321 }322 }323 Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))324}325326pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {327 Ok(match object {328 ObjBody::MemberList(members) => evaluate_member_list_object(context, &members)?,329 ObjBody::ObjComp(obj) => {330 let future_this = FutureObjValue::new();331 let mut new_members = HashMap::new();332 for (k, v) in evaluate_comp(333 context.clone(),334 &|ctx| {335 let new_bindings = FutureNewBindings::new();336 let context_creator = context_creator!(337 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {338 Ok(context.clone().extend_unbound(339 new_bindings.clone().unwrap(),340 context.dollar().clone().or_else(||this.clone()),341 None,342 super_obj343 )?)344 })345 );346 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();347 for (n, b) in obj348 .pre_locals349 .iter()350 .chain(obj.post_locals.iter())351 .map(|b| evaluate_binding(b, context_creator.clone()))352 {353 bindings.insert(n, b);354 }355 let bindings = new_bindings.fill(bindings);356 let ctx = ctx.extend_unbound(bindings, None, None, None)?;357 let key = evaluate(ctx.clone(), &obj.key)?;358 let value = LazyBinding::Bindable(Rc::new(359 closure!(clone ctx, clone obj.value, |this, _super_obj| {360 Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(HashMap::new(), None, this, None), &value)?))361 }),362 ));363364 Ok((key, value))365 },366 &obj.compspecs,367 )?368 .unwrap()369 {370 match k {371 Val::Null => {}372 Val::Str(n) => {373 new_members.insert(374 n,375 ObjMember {376 add: false,377 visibility: Visibility::Normal,378 invoke: v,379 location: obj.value.1.clone(),380 },381 );382 }383 v => throw!(FieldMustBeStringGot(v.value_type()?)),384 }385 }386387 future_this.fill(ObjValue::new(None, Rc::new(new_members)))388 }389 })390}391392393394#[cfg(feature = "unstable")]395macro_rules! noinline {396 ($e:expr) => {397 (#![inline(never)] move || $e)()398 };399}400#[cfg(not(feature = "unstable"))]401macro_rules! noinline {402 ($e:expr) => {403 (move || $e)()404 };405}406407pub fn evaluate_apply(408 context: Context,409 value: &LocExpr,410 args: &ArgsDesc,411 loc: &Option<ExprLocation>,412 tailstrict: bool,413) -> Result<Val> {414 let lazy = evaluate(context.clone(), value)?;415 let value = lazy.unwrap_if_lazy()?;416 Ok(match value {417 Val::Intristic(ns, name) => push(418 loc,419 || format!("intristic <{}.{}> call", ns, name),420 || {421 Ok(match (&ns as &str, &name as &str) {422 423 ("std", "length") => parse_args!(context, "std.length", args, 1, [424 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];425 ], {426 Ok(match x {427 Val::Str(n) => Val::Num(n.chars().count() as f64),428 Val::Arr(i) => Val::Num(i.len() as f64),429 Val::Obj(o) => Val::Num(430 o.fields_visibility()431 .into_iter()432 .filter(|(_k, v)| *v)433 .count() as f64,434 ),435 _ => unreachable!(),436 })437 })?,438 439 ("std", "type") => parse_args!(context, "std.type", args, 1, [440 0, x, vec![];441 ], {442 Ok(Val::Str(x.value_type()?.name().into()))443 })?,444 445 ("std", "makeArray") => {446 noinline!(parse_args!(context, "std.makeArray", args, 2, [447 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];448 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];449 ], {450 if sz < 0.0 {451 throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));452 }453 let mut out = Vec::with_capacity(sz as usize);454 for i in 0..sz as usize {455 out.push(func.evaluate_values(456 Context::new(),457 &[Val::Num(i as f64)]458 )?)459 }460 Ok(Val::Arr(Rc::new(out)))461 }))?462 }463 464 ("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [465 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];466 ], {467 assert!(468 str.chars().count() == 1,469 "std.codepoint should receive single char string"470 );471 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))472 })?,473 474 ("std", "objectFieldsEx") => {475 noinline!(parse_args!(context, "std.objectFieldsEx",args, 2, [476 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];477 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];478 ], {479 let mut out = obj.fields_visibility()480 .into_iter()481 .filter(|(_k, v)| *v || inc_hidden)482 .map(|(k, _v)|k)483 .collect::<Vec<_>>();484 out.sort();485 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))486 }))?487 }488 489 ("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [490 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];491 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];492 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];493 ], {494 Ok(Val::Bool(495 obj.fields_visibility()496 .into_iter()497 .filter(|(_k, v)| *v || inc_hidden)498 .any(|(k, _v)| *k == *f),499 ))500 })?,501 ("std", "primitiveEquals") => {502 parse_args!(context, "std.primitiveEquals", args, 2, [503 0, a, vec![];504 1, b, vec![];505 ], {506 Ok(Val::Bool(primitive_equals(&a, &b)?))507 })?508 }509 510 ("std", "equals") => parse_args!(context, "std.equals", args, 2, [511 0, a, vec![];512 1, b, vec![];513 ], {514 Ok(Val::Bool(equals(&a, &b)?))515 })?,516 ("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [517 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];518 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];519 ], {520 Ok(Val::Num(a % b))521 })?,522 ("std", "floor") => parse_args!(context, "std.floor", args, 1, [523 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];524 ], {525 Ok(Val::Num(x.floor()))526 })?,527 ("std", "log") => parse_args!(context, "std.log", args, 2, [528 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];529 ], {530 Ok(Val::Num(n.ln()))531 })?,532 ("std", "trace") => parse_args!(context, "std.trace", args, 2, [533 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];534 1, rest, vec![];535 ], {536 eprint!("TRACE:");537 if let Some(loc) = loc {538 with_state(|s|{539 let locs = s.map_source_locations(&loc.0, &[loc.1]);540 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);541 });542 }543 eprintln!(" {}", str);544 Ok(rest)545 })?,546 ("std", "pow") => parse_args!(context, "std.modulo", args, 2, [547 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];548 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];549 ], {550 Ok(Val::Num(x.powf(n)))551 })?,552 ("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [553 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];554 ], {555 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(556 || UndefinedExternalVariable(x),557 )?)558 })?,559 ("std", "native") => parse_args!(context, "std.native", args, 1, [560 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];561 ], {562 Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::NativeExt(x.clone(), v)).ok_or_else(563 || UndefinedExternalFunction(x),564 )?)565 })?,566 ("std", "filter") => noinline!(parse_args!(context, "std.filter", args, 2, [567 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];568 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];569 ], {570 Ok(Val::Arr(Rc::new(571 arr.iter()572 .cloned()573 .filter(|e| {574 func575 .evaluate_values(context.clone(), &[e.clone()])576 .unwrap()577 .try_cast_bool("filter predicate")578 .unwrap()579 })580 .collect(),581 )))582 }))?,583 584 ("std", "foldl") => noinline!(parse_args!(context, "std.foldl", args, 3, [585 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];586 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];587 2, init, vec![];588 ], {589 let mut acc = init;590 for i in arr.iter().cloned() {591 acc = func.evaluate_values(context.clone(), &[acc, i])?;592 }593 Ok(acc)594 }))?,595 596 ("std", "foldr") => noinline!(parse_args!(context, "std.foldr", args, 3, [597 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];598 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];599 2, init, vec![];600 ], {601 let mut acc = init;602 for i in arr.iter().rev().cloned() {603 acc = func.evaluate_values(context.clone(), &[acc, i])?;604 }605 Ok(acc)606 }))?,607 608 #[allow(non_snake_case)]609 ("std", "sortImpl") => noinline!(parse_args!(context, "std.sort", args, 2, [610 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];611 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];612 ], {613 if arr.len() <= 1 {614 return Ok(Val::Arr(arr))615 }616 let mut new_arr = arr.iter().cloned().collect::<Vec<_>>();617 match keyF.evaluate_values(context.clone(), &[new_arr[0].clone()])? {618 Val::Str(_) => {619 let mut err = None;620 new_arr.sort_by_cached_key(|k| {621 match keyF.evaluate_values(context.clone(), &[k.clone()]) {622 Ok(Val::Str(v)) => v,623 Ok(_) => {624 err = Some(LocError::new(RuntimeError("types of all array elements should equal".into())));625 "".into()626 }627 Err(e) => {628 err = Some(e);629 "".into()630 }631 }632 });633 if let Some(e) = err {634 return Err(e);635 }636 },637 Val::Num(_) => {638 let mut err = None;639 new_arr.sort_unstable_by(|a, b| {640 match (keyF.evaluate_values(context.clone(), &[a.clone()]), keyF.evaluate_values(context.clone(), &[b.clone()])) {641 (Ok(Val::Num(a)), Ok(Val::Num(b))) => a.partial_cmp(&b).unwrap(),642 (Ok(_a), Ok(_b)) => {643 err = Some(RuntimeError("types of all array elements should equal".into()).into());644 Ordering::Equal645 }646 (Err(e), _) | (_, Err(e)) => {647 err = Some(e);648 Ordering::Equal649 }650 }651 });652 if let Some(e) = err {653 return Err(e);654 }655 },656 _ => throw!(RuntimeError("keys should be number or string".into()))657 }658 Ok(Val::Arr(Rc::new(new_arr)))659 }))?,660 661 ("std", "format") => parse_args!(context, "std.format", args, 2, [662 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];663 1, vals, vec![]664 ], {665 push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{666 Ok(match vals {667 Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),668 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),669 o => Val::Str(format_arr(&str, &[o])?.into()),670 })671 })672 })?,673 674 ("std", "range") => parse_args!(context, "std.range", args, 2, [675 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];676 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];677 ], {678 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));679 for i in from as usize..=to as usize {680 out.push(Val::Num(i as f64));681 }682 Ok(Val::Arr(Rc::new(out)))683 })?,684 ("std", "char") => parse_args!(context, "std.char", args, 1, [685 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];686 ], {687 let mut out = String::new();688 out.push(std::char::from_u32(n as u32).ok_or_else(||689 InvalidUnicodeCodepointGot(n as u32)690 )?);691 Ok(Val::Str(out.into()))692 })?,693 ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [694 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];695 ], {696 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))697 })?,698 ("std", "md5") => noinline!(parse_args!(context, "std.md5", args, 1, [699 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];700 ], {701 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))702 }))?,703 704 ("std", "base64") => parse_args!(context, "std.base64", args, 1, [705 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];706 ], {707 Ok(Val::Str(match input {708 Val::Str(s) => {709 base64::encode(s.bytes().collect::<Vec<_>>()).into()710 },711 Val::Arr(a) => {712 base64::encode(a.iter().map(|v| {713 Ok(v.clone().try_cast_num("base64 array")? as u8)714 }).collect::<Result<Vec<_>>>()?).into()715 },716 _ => unreachable!()717 }))718 })?,719 720 ("std", "join") => noinline!(parse_args!(context, "std.join", args, 2, [721 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];722 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];723 ], {724 Ok(match sep {725 Val::Arr(joiner_items) => {726 let mut out = Vec::new();727728 let mut first = true;729 for item in arr.iter().cloned() {730 if let Val::Arr(items) = item.unwrap_if_lazy()? {731 if !first {732 out.reserve(joiner_items.len());733 out.extend(joiner_items.iter().cloned());734 }735 first = false;736 out.reserve(items.len());737 out.extend(items.iter().cloned());738 } else {739 throw!(RuntimeError("in std.join all items should be arrays".into()));740 }741 }742743 Val::Arr(Rc::new(out))744 },745 Val::Str(sep) => {746 let mut out = String::new();747748 let mut first = true;749 for item in arr.iter().cloned() {750 if let Val::Str(item) = item.unwrap_if_lazy()? {751 if !first {752 out += &sep;753 }754 first = false;755 out += &item;756 } else {757 throw!(RuntimeError("in std.join all items should be strings".into()));758 }759 }760761 Val::Str(out.into())762 },763 _ => unreachable!()764 })765 }))?,766 767 ("std", "escapeStringJson") => {768 parse_args!(context, "std.escapeStringJson", args, 1, [769 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];770 ], {771 Ok(Val::Str(escape_string_json(&str_).into()))772 })?773 }774 775 ("std", "manifestJsonEx") => {776 parse_args!(context, "std.manifestJsonEx", args, 2, [777 0, value, vec![];778 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];779 ], {780 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {781 padding: &indent,782 mtype: ManifestType::Std,783 })?.into()))784 })?785 }786 (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())),787 })788 },789 )?,790 Val::NativeExt(n, f) => push(791 loc,792 || format!("native <{}> call", n),793 || {794 let args = parse_function_call(context, None, &f.params, args, true)?;795 let mut out_args = Vec::with_capacity(f.params.len());796 for p in f.params.0.iter() {797 out_args.push(args.binding(p.0.clone())?.evaluate()?);798 }799 Ok(f.call(&out_args)?)800 },801 )?,802 Val::Func(f) => {803 let body = || f.evaluate(context, args, tailstrict);804 if tailstrict {805 body()?806 } else {807 push(loc, || format!("function <{}> call", f.name), body)?808 }809 }810 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),811 })812}813814pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: Rc<str>) -> Result<Val> {815 use Expr::*;816 let LocExpr(expr, _loc) = lexpr;817 Ok(match &**expr {818 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),819 _ => evaluate(context, lexpr)?,820 })821}822823pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {824 use Expr::*;825 let LocExpr(expr, loc) = expr;826 Ok(match &**expr {827 Literal(LiteralType::This) => Val::Obj(828 context829 .this()830 .clone()831 .ok_or_else(|| CantUseSelfOutsideOfObject)?,832 ),833 Literal(LiteralType::Dollar) => Val::Obj(834 context835 .dollar()836 .clone()837 .ok_or_else(|| NoTopLevelObjectFound)?,838 ),839 Literal(LiteralType::True) => Val::Bool(true),840 Literal(LiteralType::False) => Val::Bool(false),841 Literal(LiteralType::Null) => Val::Null,842 Parened(e) => evaluate(context, e)?,843 Str(v) => Val::Str(v.clone()),844 Num(v) => Val::new_checked_num(*v)?,845 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,846 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,847 Var(name) => push(848 loc,849 || format!("variable <{}>", name),850 || Ok(Val::Lazy(context.binding(name.clone())?).unwrap_if_lazy()?),851 )?,852 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {853 let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;854 context855 .super_obj()856 .clone()857 .expect("no super found")858 .get_raw(name, &context.this().clone().expect("no this found"))?859 .expect("value not found")860 }861 Index(value, index) => {862 match (863 evaluate(context.clone(), value)?.unwrap_if_lazy()?,864 evaluate(context, index)?,865 ) {866 (Val::Obj(v), Val::Str(s)) => {867 let sn = s.clone();868 push(869 &loc,870 || format!("field <{}> access", sn),871 || {872 if let Some(v) = v.get(s.clone())? {873 Ok(v.unwrap_if_lazy()?)874 } else if let Some(Val::Str(n)) =875 v.get("__intristic_namespace__".into())?876 {877 Ok(Val::Intristic(n, s))878 } else {879 throw!(NoSuchField(s))880 }881 },882 )?883 }884 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(885 ValType::Obj,886 ValType::Str,887 n.value_type()?,888 )),889890 (Val::Arr(v), Val::Num(n)) => {891 if n.fract() > f64::EPSILON {892 throw!(FractionalIndex)893 }894 v.get(n as usize)895 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?896 .clone()897 .unwrap_if_lazy()?898 }899 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),900 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(901 ValType::Arr,902 ValType::Num,903 n.value_type()?,904 )),905906 (Val::Str(s), Val::Num(n)) => Val::Str(907 s.chars()908 .skip(n as usize)909 .take(1)910 .collect::<String>()911 .into(),912 ),913 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(914 ValType::Str,915 ValType::Num,916 n.value_type()?,917 )),918919 (v, _) => throw!(CantIndexInto(v.value_type()?)),920 }921 }922 LocalExpr(bindings, returned) => {923 let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();924 let future_context = Context::new_future();925926 let context_creator = context_creator!(927 closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))928 );929930 for (k, v) in bindings931 .iter()932 .map(|b| evaluate_binding(b, context_creator.clone()))933 {934 new_bindings.insert(k, v);935 }936937 let context = context938 .extend_unbound(new_bindings, None, None, None)?939 .into_future(future_context);940 evaluate(context, &returned.clone())?941 }942 Arr(items) => {943 let mut out = Vec::with_capacity(items.len());944 for item in items {945 out.push(Val::Lazy(lazy_val!(946 closure!(clone context, clone item, || {947 evaluate(context.clone(), &item)948 })949 )));950 }951 Val::Arr(Rc::new(out))952 }953 ArrComp(expr, compspecs) => Val::Arr(954 955 Rc::new(evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap()),956 ),957 Obj(body) => Val::Obj(evaluate_object(context, body)?),958 ObjExtend(s, t) => evaluate_add_op(959 &evaluate(context.clone(), s)?,960 &Val::Obj(evaluate_object(context, t)?),961 )?,962 Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,963 Function(params, body) => {964 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())965 }966 AssertExpr(AssertStmt(value, msg), returned) => {967 let assertion_result = push(968 &value.1,969 || "assertion condition".to_owned(),970 || {971 evaluate(context.clone(), &value)?972 .try_cast_bool("assertion condition should be boolean")973 },974 )?;975 if assertion_result {976 evaluate(context, returned)?977 } else if let Some(msg) = msg {978 throw!(AssertionFailed(evaluate(context, msg)?));979 } else {980 throw!(AssertionFailed(Val::Null));981 }982 }983 ErrorStmt(e) => push(984 &loc,985 || "error statement".to_owned(),986 || {987 throw!(RuntimeError(988 evaluate(context, e)?.try_cast_str("error text should be string")?,989 ))990 },991 )?,992 IfElse {993 cond,994 cond_then,995 cond_else,996 } => {997 if evaluate(context.clone(), &cond.0)?998 .try_cast_bool("if condition should be boolean")?999 {1000 evaluate(context, cond_then)?1001 } else {1002 match cond_else {1003 Some(v) => evaluate(context, v)?,1004 None => Val::Null,1005 }1006 }1007 }1008 Import(path) => {1009 let mut tmp = loc1010 .clone()1011 .expect("imports can't be used without loc_data")1012 .0;1013 let import_location = Rc::make_mut(&mut tmp);1014 import_location.pop();1015 push(1016 loc,1017 || format!("import {:?}", path),1018 || with_state(|s| s.import_file(&import_location, path)),1019 )?1020 }1021 ImportStr(path) => {1022 let mut tmp = loc1023 .clone()1024 .expect("imports can't be used without loc_data")1025 .0;1026 let import_location = Rc::make_mut(&mut tmp);1027 import_location.pop();1028 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)1029 }1030 Literal(LiteralType::Super) => throw!(StandaloneSuper),1031 })1032}