difftreelog
refactor(evaluator) extract json to module
in: master
4 files changed
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -0,0 +1,150 @@
+use crate::error::Error::*;
+use crate::error::Result;
+use crate::{throw, Val};
+
+#[derive(PartialEq)]
+pub enum ManifestType {
+ // Applied in manifestification
+ Manifest,
+ /// Used for std.manifestJson
+ /// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest
+ Std,
+ // No line breaks, used in `obj+''`
+ ToString,
+}
+
+pub struct ManifestJsonOptions<'s> {
+ pub padding: &'s str,
+ pub mtype: ManifestType,
+}
+
+pub(crate) fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {
+ let mut out = String::new();
+ manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;
+ Ok(out)
+}
+fn manifest_json_ex_buf(
+ val: &Val,
+ buf: &mut String,
+ cur_padding: &mut String,
+ options: &ManifestJsonOptions<'_>,
+) -> Result<()> {
+ use std::fmt::Write;
+ match val.unwrap_if_lazy()? {
+ Val::Bool(v) => {
+ if v {
+ buf.push_str("true");
+ } else {
+ buf.push_str("false");
+ }
+ }
+ Val::Null => buf.push_str("null"),
+ Val::Str(s) => buf.push_str(&escape_string_json(&s)),
+ Val::Num(n) => write!(buf, "{}", n).unwrap(),
+ Val::Arr(items) => {
+ buf.push('[');
+ if !items.is_empty() {
+ if options.mtype != ManifestType::ToString {
+ buf.push('\n');
+ }
+
+ let old_len = cur_padding.len();
+ cur_padding.push_str(options.padding);
+ for (i, item) in items.iter().enumerate() {
+ if i != 0 {
+ buf.push(',');
+ if options.mtype == ManifestType::ToString {
+ buf.push(' ');
+ } else {
+ buf.push('\n');
+ }
+ }
+ buf.push_str(cur_padding);
+ manifest_json_ex_buf(item, buf, cur_padding, options)?;
+ }
+ cur_padding.truncate(old_len);
+
+ if options.mtype != ManifestType::ToString {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ }
+ } else if options.mtype == ManifestType::Std {
+ buf.push_str("\n\n");
+ buf.push_str(cur_padding);
+ } else if options.mtype == ManifestType::ToString {
+ buf.push(' ');
+ }
+ buf.push(']');
+ }
+ Val::Obj(obj) => {
+ buf.push('{');
+ let fields = obj.visible_fields();
+ if !fields.is_empty() {
+ if options.mtype != ManifestType::ToString {
+ buf.push('\n');
+ }
+
+ let old_len = cur_padding.len();
+ cur_padding.push_str(options.padding);
+ for (i, field) in fields.into_iter().enumerate() {
+ if i != 0 {
+ buf.push(',');
+ if options.mtype == ManifestType::ToString {
+ buf.push(' ');
+ } else {
+ buf.push('\n');
+ }
+ }
+ buf.push_str(cur_padding);
+ buf.push_str(&escape_string_json(&field));
+ buf.push_str(": ");
+ manifest_json_ex_buf(&obj.get(field)?.unwrap(), buf, cur_padding, options)?;
+ }
+ cur_padding.truncate(old_len);
+
+ if options.mtype != ManifestType::ToString {
+ buf.push('\n');
+ buf.push_str(cur_padding);
+ }
+ } else if options.mtype == ManifestType::Std {
+ buf.push_str("\n\n");
+ buf.push_str(cur_padding);
+ } else if options.mtype == ManifestType::ToString {
+ buf.push(' ');
+ }
+ buf.push('}');
+ }
+ Val::Func(_) | Val::Intristic(_, _) => {
+ throw!(RuntimeError("tried to manifest function".into()))
+ }
+ Val::Lazy(_) => unreachable!(),
+ };
+ Ok(())
+}
+pub fn escape_string_json(s: &str) -> String {
+ use std::fmt::Write;
+ let mut out = String::new();
+ out.push('"');
+ for c in s.chars() {
+ match c {
+ '"' => out.push_str("\\\""),
+ '\\' => out.push_str("\\\\"),
+ '\u{0008}' => out.push_str("\\b"),
+ '\u{000c}' => out.push_str("\\f"),
+ '\n' => out.push_str("\\n"),
+ '\r' => out.push_str("\\r"),
+ '\t' => out.push_str("\\t"),
+ c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {
+ write!(out, "\\u{:04x}", c as u32).unwrap()
+ }
+ c => out.push(c),
+ }
+ }
+ out.push('"');
+ out
+}
+
+#[test]
+fn json_test() {
+ assert_eq!(escape_string_json("\u{001f}"), "\"\\u001f\"")
+}
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -2,3 +2,4 @@
pub use stdlib::*;
pub mod format;
+pub mod manifest;
crates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth1use crate::{2 builtin::format::{format_arr, format_obj},3 context_creator, equals,4 error::Error::*,5 escape_string_json, future_wrapper, lazy_val, manifest_json_ex, parse_args, primitive_equals,6 push, throw,7 val::ManifestJsonOptions,8 with_state, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, ManifestType,9 ObjMember, ObjValue, Result, Val, ValType,10};11use closure::closure;12use jrsonnet_parser::{13 ArgsDesc, AssertStmt, BinaryOpType, BindSpec, CompSpec, Expr, ExprLocation, FieldMember,14 ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,15 Visibility,16};17use std::{cmp::Ordering, collections::HashMap, path::PathBuf, rc::Rc};1819pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {20 let b = b.clone();21 if let Some(params) = &b.params {22 let params = params.clone();23 (24 b.name.clone(),25 LazyBinding::Bindable(Rc::new(move |this, super_obj| {26 Ok(lazy_val!(27 closure!(clone b, clone params, clone context_creator, || Ok(evaluate_method(28 context_creator.0(this.clone(), super_obj.clone())?,29 b.name.clone(),30 params.clone(),31 b.value.clone(),32 )))33 ))34 })),35 )36 } else {37 (38 b.name.clone(),39 LazyBinding::Bindable(Rc::new(move |this, super_obj| {40 Ok(lazy_val!(closure!(clone context_creator, clone b, ||41 evaluate_named(42 context_creator.0(this.clone(), super_obj.clone())?,43 &b.value,44 b.name.clone()45 )46 )))47 })),48 )49 }50}5152pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {53 Val::Func(Rc::new(FuncDesc {54 name,55 ctx,56 params,57 body,58 }))59}6061pub fn evaluate_field_name(62 context: Context,63 field_name: &jrsonnet_parser::FieldName,64) -> Result<Option<Rc<str>>> {65 Ok(match field_name {66 jrsonnet_parser::FieldName::Fixed(n) => Some(n.clone()),67 jrsonnet_parser::FieldName::Dyn(expr) => {68 let lazy = evaluate(context, expr)?;69 let value = lazy.unwrap_if_lazy()?;70 if matches!(value, Val::Null) {71 None72 } else {73 Some(value.try_cast_str("dynamic field name")?)74 }75 }76 })77}7879pub fn evaluate_unary_op(op: UnaryOpType, b: &Val) -> Result<Val> {80 Ok(match (op, b) {81 (o, Val::Lazy(l)) => evaluate_unary_op(o, &l.evaluate()?)?,82 (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v),83 (UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n),84 (UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64),85 (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type()?)),86 })87}8889pub(crate) fn evaluate_add_op(a: &Val, b: &Val) -> Result<Val> {90 Ok(match (a, b) {91 (Val::Str(v1), Val::Str(v2)) => Val::Str(((**v1).to_owned() + &v2).into()),9293 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)94 (Val::Num(n), Val::Str(o)) => Val::Str(format!("{}{}", n, o).into()),95 (Val::Str(o), Val::Num(n)) => Val::Str(format!("{}{}", o, n).into()),9697 (Val::Str(s), o) => Val::Str(format!("{}{}", s, o.clone().into_string()?).into()),98 (o, Val::Str(s)) => Val::Str(format!("{}{}", o.clone().into_string()?, s).into()),99100 (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())),101 (Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())),102 (Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?,103 _ => throw!(BinaryOperatorDoesNotOperateOnValues(104 BinaryOpType::Add,105 a.value_type()?,106 b.value_type()?,107 )),108 })109}110111pub fn evaluate_binary_op_special(112 context: Context,113 a: &LocExpr,114 op: BinaryOpType,115 b: &LocExpr,116) -> Result<Val> {117 Ok(118 match (evaluate(context.clone(), &a)?.unwrap_if_lazy()?, op, b) {119 (Val::Bool(true), BinaryOpType::Or, _o) => Val::Bool(true),120 (Val::Bool(false), BinaryOpType::And, _o) => Val::Bool(false),121 (a, op, eb) => {122 evaluate_binary_op_normal(&a, op, &evaluate(context, eb)?.unwrap_if_lazy()?)?123 }124 },125 )126}127128pub fn evaluate_binary_op_normal(a: &Val, op: BinaryOpType, b: &Val) -> Result<Val> {129 Ok(match (a, op, b) {130 (a, BinaryOpType::Add, b) => evaluate_add_op(a, b)?,131132 (Val::Str(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::Str(v1.repeat(*v2 as usize).into()),133134 // Bool X Bool135 (Val::Bool(a), BinaryOpType::And, Val::Bool(b)) => Val::Bool(*a && *b),136 (Val::Bool(a), BinaryOpType::Or, Val::Bool(b)) => Val::Bool(*a || *b),137138 // Str X Str139 (Val::Str(v1), BinaryOpType::Lt, Val::Str(v2)) => Val::Bool(v1 < v2),140 (Val::Str(v1), BinaryOpType::Gt, Val::Str(v2)) => Val::Bool(v1 > v2),141 (Val::Str(v1), BinaryOpType::Lte, Val::Str(v2)) => Val::Bool(v1 <= v2),142 (Val::Str(v1), BinaryOpType::Gte, Val::Str(v2)) => Val::Bool(v1 >= v2),143144 // Num X Num145 (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?,146 (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => {147 if *v2 <= f64::EPSILON {148 throw!(DivisionByZero)149 }150 Val::new_checked_num(v1 / v2)?151 }152153 (Val::Num(v1), BinaryOpType::Sub, Val::Num(v2)) => Val::new_checked_num(v1 - v2)?,154155 (Val::Num(v1), BinaryOpType::Lt, Val::Num(v2)) => Val::Bool(v1 < v2),156 (Val::Num(v1), BinaryOpType::Gt, Val::Num(v2)) => Val::Bool(v1 > v2),157 (Val::Num(v1), BinaryOpType::Lte, Val::Num(v2)) => Val::Bool(v1 <= v2),158 (Val::Num(v1), BinaryOpType::Gte, Val::Num(v2)) => Val::Bool(v1 >= v2),159160 (Val::Num(v1), BinaryOpType::BitAnd, Val::Num(v2)) => {161 Val::Num(((*v1 as i32) & (*v2 as i32)) as f64)162 }163 (Val::Num(v1), BinaryOpType::BitOr, Val::Num(v2)) => {164 Val::Num(((*v1 as i32) | (*v2 as i32)) as f64)165 }166 (Val::Num(v1), BinaryOpType::BitXor, Val::Num(v2)) => {167 Val::Num(((*v1 as i32) ^ (*v2 as i32)) as f64)168 }169 (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => {170 if *v2 < 0.0 {171 throw!(RuntimeError("shift by negative exponent".into()))172 }173 Val::Num(((*v1 as i32) << (*v2 as i32)) as f64)174 }175 (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => {176 if *v2 < 0.0 {177 throw!(RuntimeError("shift by negative exponent".into()))178 }179 Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64)180 }181182 _ => throw!(BinaryOperatorDoesNotOperateOnValues(183 op,184 a.value_type()?,185 b.value_type()?,186 )),187 })188}189190future_wrapper!(HashMap<Rc<str>, LazyBinding>, FutureNewBindings);191future_wrapper!(ObjValue, FutureObjValue);192193pub fn evaluate_comp<T>(194 context: Context,195 value: &impl Fn(Context) -> Result<T>,196 specs: &[CompSpec],197) -> Result<Option<Vec<T>>> {198 Ok(match specs.get(0) {199 None => Some(vec![value(context)?]),200 Some(CompSpec::IfSpec(IfSpecData(cond))) => {201 if evaluate(context.clone(), &cond)?.try_cast_bool("if spec")? {202 evaluate_comp(context, value, &specs[1..])?203 } else {204 None205 }206 }207 Some(CompSpec::ForSpec(ForSpecData(var, expr))) => {208 match evaluate(context.clone(), &expr)?.unwrap_if_lazy()? {209 Val::Arr(list) => {210 let mut out = Vec::new();211 for item in list.iter() {212 let item = item.unwrap_if_lazy()?;213 out.push(evaluate_comp(214 context.clone().with_var(var.clone(), item.clone()),215 value,216 &specs[1..],217 )?);218 }219 Some(out.into_iter().flatten().flatten().collect())220 }221 _ => throw!(InComprehensionCanOnlyIterateOverArray),222 }223 }224 })225}226227pub fn evaluate_member_list_object(context: Context, members: &[Member]) -> Result<ObjValue> {228 let new_bindings = FutureNewBindings::new();229 let future_this = FutureObjValue::new();230 let context_creator = context_creator!(231 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {232 Ok(context.clone().extend_unbound(233 new_bindings.clone().unwrap(),234 context.dollar().clone().or_else(||this.clone()),235 Some(this.unwrap()),236 super_obj237 )?)238 })239 );240 {241 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();242 for (n, b) in members243 .iter()244 .filter_map(|m| match m {245 Member::BindStmt(b) => Some(b.clone()),246 _ => None,247 })248 .map(|b| evaluate_binding(&b, context_creator.clone()))249 {250 bindings.insert(n, b);251 }252 new_bindings.fill(bindings);253 }254255 let mut new_members = HashMap::new();256 for member in members.iter() {257 match member {258 Member::Field(FieldMember {259 name,260 plus,261 params: None,262 visibility,263 value,264 }) => {265 let name = evaluate_field_name(context.clone(), &name)?;266 if name.is_none() {267 continue;268 }269 let name = name.unwrap();270 new_members.insert(271 name.clone(),272 ObjMember {273 add: *plus,274 visibility: *visibility,275 invoke: LazyBinding::Bindable(Rc::new(276 closure!(clone name, clone value, clone context_creator, |this, super_obj| {277 Ok(LazyVal::new_resolved(evaluate(278 context_creator.0(this, super_obj)?,279 &value,280 )?))281 }),282 )),283 location: value.1.clone(),284 },285 );286 }287 Member::Field(FieldMember {288 name,289 params: Some(params),290 value,291 ..292 }) => {293 let name = evaluate_field_name(context.clone(), &name)?;294 if name.is_none() {295 continue;296 }297 let name = name.unwrap();298 new_members.insert(299 name.clone(),300 ObjMember {301 add: false,302 visibility: Visibility::Hidden,303 invoke: LazyBinding::Bindable(Rc::new(304 closure!(clone value, clone context_creator, clone params, clone name, |this, super_obj| {305 // TODO: Assert306 Ok(LazyVal::new_resolved(evaluate_method(307 context_creator.0(this, super_obj)?,308 name.clone(),309 params.clone(),310 value.clone(),311 )))312 }),313 )),314 location: value.1.clone(),315 },316 );317 }318 Member::BindStmt(_) => {}319 Member::AssertStmt(_) => {}320 }321 }322 Ok(future_this.fill(ObjValue::new(None, Rc::new(new_members))))323}324325pub fn evaluate_object(context: Context, object: &ObjBody) -> Result<ObjValue> {326 Ok(match object {327 ObjBody::MemberList(members) => evaluate_member_list_object(context, &members)?,328 ObjBody::ObjComp(obj) => {329 let future_this = FutureObjValue::new();330 let mut new_members = HashMap::new();331 for (k, v) in evaluate_comp(332 context.clone(),333 &|ctx| {334 let new_bindings = FutureNewBindings::new();335 let context_creator = context_creator!(336 closure!(clone context, clone new_bindings, |this: Option<ObjValue>, super_obj: Option<ObjValue>| {337 Ok(context.clone().extend_unbound(338 new_bindings.clone().unwrap(),339 context.dollar().clone().or_else(||this.clone()),340 None,341 super_obj342 )?)343 })344 );345 let mut bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();346 for (n, b) in obj347 .pre_locals348 .iter()349 .chain(obj.post_locals.iter())350 .map(|b| evaluate_binding(b, context_creator.clone()))351 {352 bindings.insert(n, b);353 }354 let bindings = new_bindings.fill(bindings);355 let ctx = ctx.extend_unbound(bindings, None, None, None)?;356 let key = evaluate(ctx.clone(), &obj.key)?;357 let value = LazyBinding::Bindable(Rc::new(358 closure!(clone ctx, clone obj.value, |this, _super_obj| {359 Ok(LazyVal::new_resolved(evaluate(ctx.clone().extend(HashMap::new(), None, this, None), &value)?))360 }),361 ));362363 Ok((key, value))364 },365 &obj.compspecs,366 )?367 .unwrap()368 {369 match k {370 Val::Null => {}371 Val::Str(n) => {372 new_members.insert(373 n,374 ObjMember {375 add: false,376 visibility: Visibility::Normal,377 invoke: v,378 location: obj.value.1.clone(),379 },380 );381 }382 v => throw!(FieldMustBeStringGot(v.value_type()?)),383 }384 }385386 future_this.fill(ObjValue::new(None, Rc::new(new_members)))387 }388 })389}390391/// Extracts code block and disables inlining for them392/// Fixes WASM to java bytecode compilation failing because of very large method393#[cfg(feature = "unstable")]394macro_rules! noinline {395 ($e:expr) => {396 (#![inline(never)] move || $e)()397 };398}399#[cfg(not(feature = "unstable"))]400macro_rules! noinline {401 ($e:expr) => {402 (move || $e)()403 };404}405406pub fn evaluate_apply(407 context: Context,408 value: &LocExpr,409 args: &ArgsDesc,410 loc: &Option<ExprLocation>,411 tailstrict: bool,412) -> Result<Val> {413 let lazy = evaluate(context.clone(), value)?;414 let value = lazy.unwrap_if_lazy()?;415 Ok(match value {416 Val::Intristic(ns, name) => push(417 loc,418 || format!("intristic <{}.{}> call", ns, name),419 || {420 Ok(match (&ns as &str, &name as &str) {421 // arr/string/function422 ("std", "length") => parse_args!(context, "std.length", args, 1, [423 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];424 ], {425 Ok(match x {426 Val::Str(n) => Val::Num(n.chars().count() as f64),427 Val::Arr(i) => Val::Num(i.len() as f64),428 Val::Obj(o) => Val::Num(429 o.fields_visibility()430 .into_iter()431 .filter(|(_k, v)| *v)432 .count() as f64,433 ),434 _ => unreachable!(),435 })436 })?,437 // any438 ("std", "type") => parse_args!(context, "std.type", args, 1, [439 0, x, vec![];440 ], {441 Ok(Val::Str(x.value_type()?.name().into()))442 })?,443 // length, idx=>any444 ("std", "makeArray") => {445 noinline!(parse_args!(context, "std.makeArray", args, 2, [446 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];447 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];448 ], {449 if sz < 0.0 {450 throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));451 }452 let mut out = Vec::with_capacity(sz as usize);453 for i in 0..sz as usize {454 out.push(func.evaluate_values(455 Context::new(),456 &[Val::Num(i as f64)]457 )?)458 }459 Ok(Val::Arr(Rc::new(out)))460 }))?461 }462 // string463 ("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [464 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];465 ], {466 assert!(467 str.chars().count() == 1,468 "std.codepoint should receive single char string"469 );470 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))471 })?,472 // object, includeHidden473 ("std", "objectFieldsEx") => {474 noinline!(parse_args!(context, "std.objectFieldsEx",args, 2, [475 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];476 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];477 ], {478 let mut out = obj.fields_visibility()479 .into_iter()480 .filter(|(_k, v)| *v || inc_hidden)481 .map(|(k, _v)|k)482 .collect::<Vec<_>>();483 out.sort();484 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))485 }))?486 }487 // object, field, includeHidden488 ("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [489 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];490 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];491 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];492 ], {493 Ok(Val::Bool(494 obj.fields_visibility()495 .into_iter()496 .filter(|(_k, v)| *v || inc_hidden)497 .any(|(k, _v)| *k == *f),498 ))499 })?,500 ("std", "primitiveEquals") => {501 parse_args!(context, "std.primitiveEquals", args, 2, [502 0, a, vec![];503 1, b, vec![];504 ], {505 Ok(Val::Bool(primitive_equals(&a, &b)?))506 })?507 }508 // faster509 ("std", "equals") => parse_args!(context, "std.equals", args, 2, [510 0, a, vec![];511 1, b, vec![];512 ], {513 Ok(Val::Bool(equals(&a, &b)?))514 })?,515 ("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [516 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];517 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];518 ], {519 Ok(Val::Num(a % b))520 })?,521 ("std", "floor") => parse_args!(context, "std.floor", args, 1, [522 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];523 ], {524 Ok(Val::Num(x.floor()))525 })?,526 ("std", "log") => parse_args!(context, "std.log", args, 2, [527 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];528 ], {529 Ok(Val::Num(n.ln()))530 })?,531 ("std", "trace") => parse_args!(context, "std.trace", args, 2, [532 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];533 1, rest, vec![];534 ], {535 eprint!("TRACE:");536 if let Some(loc) = loc {537 with_state(|s|{538 let locs = s.map_source_locations(&loc.0, &[loc.1]);539 eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);540 });541 }542 eprintln!(" {}", str);543 Ok(rest)544 })?,545 ("std", "pow") => parse_args!(context, "std.modulo", args, 2, [546 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];547 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];548 ], {549 Ok(Val::Num(x.powf(n)))550 })?,551 ("std", "extVar") => parse_args!(context, "std.extVar", args, 2, [552 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];553 ], {554 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(555 || UndefinedExternalVariable(x),556 )?)557 })?,558 ("std", "filter") => noinline!(parse_args!(context, "std.filter", args, 2, [559 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];560 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];561 ], {562 Ok(Val::Arr(Rc::new(563 arr.iter()564 .cloned()565 .filter(|e| {566 func567 .evaluate_values(context.clone(), &[e.clone()])568 .unwrap()569 .try_cast_bool("filter predicate")570 .unwrap()571 })572 .collect(),573 )))574 }))?,575 // faster576 ("std", "foldl") => noinline!(parse_args!(context, "std.foldl", args, 3, [577 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];578 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];579 2, init, vec![];580 ], {581 let mut acc = init;582 for i in arr.iter().cloned() {583 acc = func.evaluate_values(context.clone(), &[acc, i])?;584 }585 Ok(acc)586 }))?,587 // faster588 ("std", "foldr") => noinline!(parse_args!(context, "std.foldr", args, 3, [589 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];590 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];591 2, init, vec![];592 ], {593 let mut acc = init;594 for i in arr.iter().rev().cloned() {595 acc = func.evaluate_values(context.clone(), &[acc, i])?;596 }597 Ok(acc)598 }))?,599 // faster600 #[allow(non_snake_case)]601 ("std", "sortImpl") => noinline!(parse_args!(context, "std.sort", args, 2, [602 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];603 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];604 ], {605 if arr.len() <= 1 {606 return Ok(Val::Arr(arr))607 }608 let mut new_arr = arr.iter().cloned().collect::<Vec<_>>();609 match keyF.evaluate_values(context.clone(), &[new_arr[0].clone()])? {610 Val::Str(_) => {611 let mut err = None;612 new_arr.sort_by_cached_key(|k| {613 match keyF.evaluate_values(context.clone(), &[k.clone()]) {614 Ok(Val::Str(v)) => v,615 Ok(_) => {616 err = Some(LocError::new(RuntimeError("types of all array elements should equal".into())));617 "".into()618 }619 Err(e) => {620 err = Some(e);621 "".into()622 }623 }624 });625 if let Some(e) = err {626 return Err(e);627 }628 },629 Val::Num(_) => {630 let mut err = None;631 new_arr.sort_unstable_by(|a, b| {632 match (keyF.evaluate_values(context.clone(), &[a.clone()]), keyF.evaluate_values(context.clone(), &[b.clone()])) {633 (Ok(Val::Num(a)), Ok(Val::Num(b))) => a.partial_cmp(&b).unwrap(),634 (Ok(_a), Ok(_b)) => {635 err = Some(RuntimeError("types of all array elements should equal".into()).into());636 Ordering::Equal637 }638 (Err(e), _) | (_, Err(e)) => {639 err = Some(e);640 Ordering::Equal641 }642 }643 });644 if let Some(e) = err {645 return Err(e);646 }647 },648 _ => throw!(RuntimeError("keys should be number or string".into()))649 }650 Ok(Val::Arr(Rc::new(new_arr)))651 }))?,652 // faster653 ("std", "format") => parse_args!(context, "std.format", args, 2, [654 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];655 1, vals, vec![]656 ], {657 push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{658 Ok(match vals {659 Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),660 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),661 o => Val::Str(format_arr(&str, &[o])?.into()),662 })663 })664 })?,665 // faster666 ("std", "range") => parse_args!(context, "std.range", args, 2, [667 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];668 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];669 ], {670 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));671 for i in from as usize..=to as usize {672 out.push(Val::Num(i as f64));673 }674 Ok(Val::Arr(Rc::new(out)))675 })?,676 ("std", "char") => parse_args!(context, "std.char", args, 1, [677 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];678 ], {679 let mut out = String::new();680 out.push(std::char::from_u32(n as u32).ok_or_else(||681 InvalidUnicodeCodepointGot(n as u32)682 )?);683 Ok(Val::Str(out.into()))684 })?,685 ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [686 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];687 ], {688 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))689 })?,690 ("std", "md5") => noinline!(parse_args!(context, "std.md5", args, 1, [691 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];692 ], {693 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))694 }))?,695 // faster696 ("std", "base64") => parse_args!(context, "std.base64", args, 1, [697 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];698 ], {699 Ok(Val::Str(match input {700 Val::Str(s) => {701 base64::encode(s.bytes().collect::<Vec<_>>()).into()702 },703 Val::Arr(a) => {704 base64::encode(a.iter().map(|v| {705 Ok(v.clone().try_cast_num("base64 array")? as u8)706 }).collect::<Result<Vec<_>>>()?).into()707 },708 _ => unreachable!()709 }))710 })?,711 // faster712 ("std", "join") => noinline!(parse_args!(context, "std.join", args, 2, [713 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];714 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];715 ], {716 Ok(match sep {717 Val::Arr(joiner_items) => {718 let mut out = Vec::new();719720 let mut first = true;721 for item in arr.iter().cloned() {722 if let Val::Arr(items) = item.unwrap_if_lazy()? {723 if !first {724 out.reserve(joiner_items.len());725 out.extend(joiner_items.iter().cloned());726 }727 first = false;728 out.reserve(items.len());729 out.extend(items.iter().cloned());730 } else {731 throw!(RuntimeError("in std.join all items should be arrays".into()));732 }733 }734735 Val::Arr(Rc::new(out))736 },737 Val::Str(sep) => {738 let mut out = String::new();739740 let mut first = true;741 for item in arr.iter().cloned() {742 if let Val::Str(item) = item.unwrap_if_lazy()? {743 if !first {744 out += &sep;745 }746 first = false;747 out += &item;748 } else {749 throw!(RuntimeError("in std.join all items should be strings".into()));750 }751 }752753 Val::Str(out.into())754 },755 _ => unreachable!()756 })757 }))?,758 // Faster759 ("std", "escapeStringJson") => {760 parse_args!(context, "std.escapeStringJson", args, 1, [761 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];762 ], {763 Ok(Val::Str(escape_string_json(&str_).into()))764 })?765 }766 // Faster767 ("std", "manifestJsonEx") => {768 parse_args!(context, "std.manifestJsonEx", args, 2, [769 0, value, vec![];770 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];771 ], {772 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {773 padding: &indent,774 mtype: ManifestType::Std,775 })?.into()))776 })?777 }778 (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())),779 })780 },781 )?,782 Val::Func(f) => {783 let body = || f.evaluate(context, args, tailstrict);784 if tailstrict {785 body()?786 } else {787 push(loc, || format!("function <{}> call", f.name), body)?788 }789 }790 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),791 })792}793794pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: Rc<str>) -> Result<Val> {795 use Expr::*;796 let LocExpr(expr, _loc) = lexpr;797 Ok(match &**expr {798 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),799 _ => evaluate(context, lexpr)?,800 })801}802803pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {804 use Expr::*;805 let LocExpr(expr, loc) = expr;806 Ok(match &**expr {807 Literal(LiteralType::This) => Val::Obj(808 context809 .this()810 .clone()811 .ok_or_else(|| CantUseSelfOutsideOfObject)?,812 ),813 Literal(LiteralType::Dollar) => Val::Obj(814 context815 .dollar()816 .clone()817 .ok_or_else(|| NoTopLevelObjectFound)?,818 ),819 Literal(LiteralType::True) => Val::Bool(true),820 Literal(LiteralType::False) => Val::Bool(false),821 Literal(LiteralType::Null) => Val::Null,822 Parened(e) => evaluate(context, e)?,823 Str(v) => Val::Str(v.clone()),824 Num(v) => Val::new_checked_num(*v)?,825 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,826 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,827 Var(name) => push(828 loc,829 || format!("variable <{}>", name),830 || Ok(Val::Lazy(context.binding(name.clone())?).unwrap_if_lazy()?),831 )?,832 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {833 let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;834 context835 .super_obj()836 .clone()837 .expect("no super found")838 .get_raw(name, &context.this().clone().expect("no this found"))?839 .expect("value not found")840 }841 Index(value, index) => {842 match (843 evaluate(context.clone(), value)?.unwrap_if_lazy()?,844 evaluate(context, index)?,845 ) {846 (Val::Obj(v), Val::Str(s)) => {847 let sn = s.clone();848 push(849 &loc,850 || format!("field <{}> access", sn),851 || {852 if let Some(v) = v.get(s.clone())? {853 Ok(v.unwrap_if_lazy()?)854 } else if let Some(Val::Str(n)) =855 v.get("__intristic_namespace__".into())?856 {857 Ok(Val::Intristic(n, s))858 } else {859 throw!(NoSuchField(s))860 }861 },862 )?863 }864 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(865 ValType::Obj,866 ValType::Str,867 n.value_type()?,868 )),869870 (Val::Arr(v), Val::Num(n)) => {871 if n.fract() > f64::EPSILON {872 throw!(FractionalIndex)873 }874 v.get(n as usize)875 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?876 .clone()877 .unwrap_if_lazy()?878 }879 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),880 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(881 ValType::Arr,882 ValType::Num,883 n.value_type()?,884 )),885886 (Val::Str(s), Val::Num(n)) => Val::Str(887 s.chars()888 .skip(n as usize)889 .take(1)890 .collect::<String>()891 .into(),892 ),893 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(894 ValType::Str,895 ValType::Num,896 n.value_type()?,897 )),898899 (v, _) => throw!(CantIndexInto(v.value_type()?)),900 }901 }902 LocalExpr(bindings, returned) => {903 let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();904 let future_context = Context::new_future();905906 let context_creator = context_creator!(907 closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))908 );909910 for (k, v) in bindings911 .iter()912 .map(|b| evaluate_binding(b, context_creator.clone()))913 {914 new_bindings.insert(k, v);915 }916917 let context = context918 .extend_unbound(new_bindings, None, None, None)?919 .into_future(future_context);920 evaluate(context, &returned.clone())?921 }922 Arr(items) => {923 let mut out = Vec::with_capacity(items.len());924 for item in items {925 out.push(Val::Lazy(lazy_val!(926 closure!(clone context, clone item, || {927 evaluate(context.clone(), &item)928 })929 )));930 }931 Val::Arr(Rc::new(out))932 }933 ArrComp(expr, compspecs) => Val::Arr(934 // First compspec should be forspec, so no "None" possible here935 Rc::new(evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap()),936 ),937 Obj(body) => Val::Obj(evaluate_object(context, body)?),938 ObjExtend(s, t) => evaluate_add_op(939 &evaluate(context.clone(), s)?,940 &Val::Obj(evaluate_object(context, t)?),941 )?,942 Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,943 Function(params, body) => {944 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())945 }946 AssertExpr(AssertStmt(value, msg), returned) => {947 let assertion_result = push(948 &value.1,949 || "assertion condition".to_owned(),950 || {951 evaluate(context.clone(), &value)?952 .try_cast_bool("assertion condition should be boolean")953 },954 )?;955 if assertion_result {956 evaluate(context, returned)?957 } else if let Some(msg) = msg {958 throw!(AssertionFailed(evaluate(context, msg)?));959 } else {960 throw!(AssertionFailed(Val::Null));961 }962 }963 ErrorStmt(e) => push(964 &loc,965 || "error statement".to_owned(),966 || {967 throw!(RuntimeError(968 evaluate(context, e)?.try_cast_str("error text should be string")?,969 ))970 },971 )?,972 IfElse {973 cond,974 cond_then,975 cond_else,976 } => {977 if evaluate(context.clone(), &cond.0)?978 .try_cast_bool("if condition should be boolean")?979 {980 evaluate(context, cond_then)?981 } else {982 match cond_else {983 Some(v) => evaluate(context, v)?,984 None => Val::Null,985 }986 }987 }988 Import(path) => {989 let mut tmp = loc990 .clone()991 .expect("imports can't be used without loc_data")992 .0;993 let import_location = Rc::make_mut(&mut tmp);994 import_location.pop();995 push(996 loc,997 || format!("import {:?}", path),998 || with_state(|s| s.import_file(&import_location, path)),999 )?1000 }1001 ImportStr(path) => {1002 let mut tmp = loc1003 .clone()1004 .expect("imports can't be used without loc_data")1005 .0;1006 let import_location = Rc::make_mut(&mut tmp);1007 import_location.pop();1008 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)1009 }1010 Literal(LiteralType::Super) => throw!(StandaloneSuper),1011 })1012}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, primitive_equals, push, throw, with_state, Context,9 ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, ObjMember, ObjValue, Result, Val,10 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 // Can't use generic json serialization way, because it depends on number to string concatenation (std.jsonnet:890)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 // Bool X Bool136 (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 // Str X Str140 (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 // Num X Num146 (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 // TODO: Assert307 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}391392/// Extracts code block and disables inlining for them393/// Fixes WASM to java bytecode compilation failing because of very large method394#[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 // arr/string/function423 ("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 // any439 ("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 // length, idx=>any445 ("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 // string464 ("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 // object, includeHidden474 ("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 // object, field, includeHidden489 ("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 // faster510 ("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, 2, [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", "filter") => noinline!(parse_args!(context, "std.filter", args, 2, [560 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];561 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];562 ], {563 Ok(Val::Arr(Rc::new(564 arr.iter()565 .cloned()566 .filter(|e| {567 func568 .evaluate_values(context.clone(), &[e.clone()])569 .unwrap()570 .try_cast_bool("filter predicate")571 .unwrap()572 })573 .collect(),574 )))575 }))?,576 // faster577 ("std", "foldl") => noinline!(parse_args!(context, "std.foldl", args, 3, [578 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];579 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];580 2, init, vec![];581 ], {582 let mut acc = init;583 for i in arr.iter().cloned() {584 acc = func.evaluate_values(context.clone(), &[acc, i])?;585 }586 Ok(acc)587 }))?,588 // faster589 ("std", "foldr") => noinline!(parse_args!(context, "std.foldr", args, 3, [590 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];591 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];592 2, init, vec![];593 ], {594 let mut acc = init;595 for i in arr.iter().rev().cloned() {596 acc = func.evaluate_values(context.clone(), &[acc, i])?;597 }598 Ok(acc)599 }))?,600 // faster601 #[allow(non_snake_case)]602 ("std", "sortImpl") => noinline!(parse_args!(context, "std.sort", args, 2, [603 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];604 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];605 ], {606 if arr.len() <= 1 {607 return Ok(Val::Arr(arr))608 }609 let mut new_arr = arr.iter().cloned().collect::<Vec<_>>();610 match keyF.evaluate_values(context.clone(), &[new_arr[0].clone()])? {611 Val::Str(_) => {612 let mut err = None;613 new_arr.sort_by_cached_key(|k| {614 match keyF.evaluate_values(context.clone(), &[k.clone()]) {615 Ok(Val::Str(v)) => v,616 Ok(_) => {617 err = Some(LocError::new(RuntimeError("types of all array elements should equal".into())));618 "".into()619 }620 Err(e) => {621 err = Some(e);622 "".into()623 }624 }625 });626 if let Some(e) = err {627 return Err(e);628 }629 },630 Val::Num(_) => {631 let mut err = None;632 new_arr.sort_unstable_by(|a, b| {633 match (keyF.evaluate_values(context.clone(), &[a.clone()]), keyF.evaluate_values(context.clone(), &[b.clone()])) {634 (Ok(Val::Num(a)), Ok(Val::Num(b))) => a.partial_cmp(&b).unwrap(),635 (Ok(_a), Ok(_b)) => {636 err = Some(RuntimeError("types of all array elements should equal".into()).into());637 Ordering::Equal638 }639 (Err(e), _) | (_, Err(e)) => {640 err = Some(e);641 Ordering::Equal642 }643 }644 });645 if let Some(e) = err {646 return Err(e);647 }648 },649 _ => throw!(RuntimeError("keys should be number or string".into()))650 }651 Ok(Val::Arr(Rc::new(new_arr)))652 }))?,653 // faster654 ("std", "format") => parse_args!(context, "std.format", args, 2, [655 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];656 1, vals, vec![]657 ], {658 push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{659 Ok(match vals {660 Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),661 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),662 o => Val::Str(format_arr(&str, &[o])?.into()),663 })664 })665 })?,666 // faster667 ("std", "range") => parse_args!(context, "std.range", args, 2, [668 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];669 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];670 ], {671 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));672 for i in from as usize..=to as usize {673 out.push(Val::Num(i as f64));674 }675 Ok(Val::Arr(Rc::new(out)))676 })?,677 ("std", "char") => parse_args!(context, "std.char", args, 1, [678 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];679 ], {680 let mut out = String::new();681 out.push(std::char::from_u32(n as u32).ok_or_else(||682 InvalidUnicodeCodepointGot(n as u32)683 )?);684 Ok(Val::Str(out.into()))685 })?,686 ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [687 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];688 ], {689 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))690 })?,691 ("std", "md5") => noinline!(parse_args!(context, "std.md5", args, 1, [692 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];693 ], {694 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))695 }))?,696 // faster697 ("std", "base64") => parse_args!(context, "std.base64", args, 1, [698 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];699 ], {700 Ok(Val::Str(match input {701 Val::Str(s) => {702 base64::encode(s.bytes().collect::<Vec<_>>()).into()703 },704 Val::Arr(a) => {705 base64::encode(a.iter().map(|v| {706 Ok(v.clone().try_cast_num("base64 array")? as u8)707 }).collect::<Result<Vec<_>>>()?).into()708 },709 _ => unreachable!()710 }))711 })?,712 // faster713 ("std", "join") => noinline!(parse_args!(context, "std.join", args, 2, [714 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];715 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];716 ], {717 Ok(match sep {718 Val::Arr(joiner_items) => {719 let mut out = Vec::new();720721 let mut first = true;722 for item in arr.iter().cloned() {723 if let Val::Arr(items) = item.unwrap_if_lazy()? {724 if !first {725 out.reserve(joiner_items.len());726 out.extend(joiner_items.iter().cloned());727 }728 first = false;729 out.reserve(items.len());730 out.extend(items.iter().cloned());731 } else {732 throw!(RuntimeError("in std.join all items should be arrays".into()));733 }734 }735736 Val::Arr(Rc::new(out))737 },738 Val::Str(sep) => {739 let mut out = String::new();740741 let mut first = true;742 for item in arr.iter().cloned() {743 if let Val::Str(item) = item.unwrap_if_lazy()? {744 if !first {745 out += &sep;746 }747 first = false;748 out += &item;749 } else {750 throw!(RuntimeError("in std.join all items should be strings".into()));751 }752 }753754 Val::Str(out.into())755 },756 _ => unreachable!()757 })758 }))?,759 // Faster760 ("std", "escapeStringJson") => {761 parse_args!(context, "std.escapeStringJson", args, 1, [762 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];763 ], {764 Ok(Val::Str(escape_string_json(&str_).into()))765 })?766 }767 // Faster768 ("std", "manifestJsonEx") => {769 parse_args!(context, "std.manifestJsonEx", args, 2, [770 0, value, vec![];771 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];772 ], {773 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {774 padding: &indent,775 mtype: ManifestType::Std,776 })?.into()))777 })?778 }779 (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())),780 })781 },782 )?,783 Val::Func(f) => {784 let body = || f.evaluate(context, args, tailstrict);785 if tailstrict {786 body()?787 } else {788 push(loc, || format!("function <{}> call", f.name), body)?789 }790 }791 v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),792 })793}794795pub fn evaluate_named(context: Context, lexpr: &LocExpr, name: Rc<str>) -> Result<Val> {796 use Expr::*;797 let LocExpr(expr, _loc) = lexpr;798 Ok(match &**expr {799 Function(params, body) => evaluate_method(context, name, params.clone(), body.clone()),800 _ => evaluate(context, lexpr)?,801 })802}803804pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {805 use Expr::*;806 let LocExpr(expr, loc) = expr;807 Ok(match &**expr {808 Literal(LiteralType::This) => Val::Obj(809 context810 .this()811 .clone()812 .ok_or_else(|| CantUseSelfOutsideOfObject)?,813 ),814 Literal(LiteralType::Dollar) => Val::Obj(815 context816 .dollar()817 .clone()818 .ok_or_else(|| NoTopLevelObjectFound)?,819 ),820 Literal(LiteralType::True) => Val::Bool(true),821 Literal(LiteralType::False) => Val::Bool(false),822 Literal(LiteralType::Null) => Val::Null,823 Parened(e) => evaluate(context, e)?,824 Str(v) => Val::Str(v.clone()),825 Num(v) => Val::new_checked_num(*v)?,826 BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,827 UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,828 Var(name) => push(829 loc,830 || format!("variable <{}>", name),831 || Ok(Val::Lazy(context.binding(name.clone())?).unwrap_if_lazy()?),832 )?,833 Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {834 let name = evaluate(context.clone(), index)?.try_cast_str("object index")?;835 context836 .super_obj()837 .clone()838 .expect("no super found")839 .get_raw(name, &context.this().clone().expect("no this found"))?840 .expect("value not found")841 }842 Index(value, index) => {843 match (844 evaluate(context.clone(), value)?.unwrap_if_lazy()?,845 evaluate(context, index)?,846 ) {847 (Val::Obj(v), Val::Str(s)) => {848 let sn = s.clone();849 push(850 &loc,851 || format!("field <{}> access", sn),852 || {853 if let Some(v) = v.get(s.clone())? {854 Ok(v.unwrap_if_lazy()?)855 } else if let Some(Val::Str(n)) =856 v.get("__intristic_namespace__".into())?857 {858 Ok(Val::Intristic(n, s))859 } else {860 throw!(NoSuchField(s))861 }862 },863 )?864 }865 (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot(866 ValType::Obj,867 ValType::Str,868 n.value_type()?,869 )),870871 (Val::Arr(v), Val::Num(n)) => {872 if n.fract() > f64::EPSILON {873 throw!(FractionalIndex)874 }875 v.get(n as usize)876 .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))?877 .clone()878 .unwrap_if_lazy()?879 }880 (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)),881 (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot(882 ValType::Arr,883 ValType::Num,884 n.value_type()?,885 )),886887 (Val::Str(s), Val::Num(n)) => Val::Str(888 s.chars()889 .skip(n as usize)890 .take(1)891 .collect::<String>()892 .into(),893 ),894 (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot(895 ValType::Str,896 ValType::Num,897 n.value_type()?,898 )),899900 (v, _) => throw!(CantIndexInto(v.value_type()?)),901 }902 }903 LocalExpr(bindings, returned) => {904 let mut new_bindings: HashMap<Rc<str>, LazyBinding> = HashMap::new();905 let future_context = Context::new_future();906907 let context_creator = context_creator!(908 closure!(clone future_context, |_, _| Ok(future_context.clone().unwrap()))909 );910911 for (k, v) in bindings912 .iter()913 .map(|b| evaluate_binding(b, context_creator.clone()))914 {915 new_bindings.insert(k, v);916 }917918 let context = context919 .extend_unbound(new_bindings, None, None, None)?920 .into_future(future_context);921 evaluate(context, &returned.clone())?922 }923 Arr(items) => {924 let mut out = Vec::with_capacity(items.len());925 for item in items {926 out.push(Val::Lazy(lazy_val!(927 closure!(clone context, clone item, || {928 evaluate(context.clone(), &item)929 })930 )));931 }932 Val::Arr(Rc::new(out))933 }934 ArrComp(expr, compspecs) => Val::Arr(935 // First compspec should be forspec, so no "None" possible here936 Rc::new(evaluate_comp(context, &|ctx| evaluate(ctx, expr), compspecs)?.unwrap()),937 ),938 Obj(body) => Val::Obj(evaluate_object(context, body)?),939 ObjExtend(s, t) => evaluate_add_op(940 &evaluate(context.clone(), s)?,941 &Val::Obj(evaluate_object(context, t)?),942 )?,943 Apply(value, args, tailstrict) => evaluate_apply(context, value, args, loc, *tailstrict)?,944 Function(params, body) => {945 evaluate_method(context, "anonymous".into(), params.clone(), body.clone())946 }947 AssertExpr(AssertStmt(value, msg), returned) => {948 let assertion_result = push(949 &value.1,950 || "assertion condition".to_owned(),951 || {952 evaluate(context.clone(), &value)?953 .try_cast_bool("assertion condition should be boolean")954 },955 )?;956 if assertion_result {957 evaluate(context, returned)?958 } else if let Some(msg) = msg {959 throw!(AssertionFailed(evaluate(context, msg)?));960 } else {961 throw!(AssertionFailed(Val::Null));962 }963 }964 ErrorStmt(e) => push(965 &loc,966 || "error statement".to_owned(),967 || {968 throw!(RuntimeError(969 evaluate(context, e)?.try_cast_str("error text should be string")?,970 ))971 },972 )?,973 IfElse {974 cond,975 cond_then,976 cond_else,977 } => {978 if evaluate(context.clone(), &cond.0)?979 .try_cast_bool("if condition should be boolean")?980 {981 evaluate(context, cond_then)?982 } else {983 match cond_else {984 Some(v) => evaluate(context, v)?,985 None => Val::Null,986 }987 }988 }989 Import(path) => {990 let mut tmp = loc991 .clone()992 .expect("imports can't be used without loc_data")993 .0;994 let import_location = Rc::make_mut(&mut tmp);995 import_location.pop();996 push(997 loc,998 || format!("import {:?}", path),999 || with_state(|s| s.import_file(&import_location, path)),1000 )?1001 }1002 ImportStr(path) => {1003 let mut tmp = loc1004 .clone()1005 .expect("imports can't be used without loc_data")1006 .0;1007 let import_location = Rc::make_mut(&mut tmp);1008 import_location.pop();1009 Val::Str(with_state(|s| s.import_file_str(&import_location, path))?)1010 }1011 Literal(LiteralType::Super) => throw!(StandaloneSuper),1012 })1013}crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/val.rs
+++ b/crates/jrsonnet-evaluator/src/val.rs
@@ -1,4 +1,5 @@
use crate::{
+ builtin::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},
error::Error::*,
evaluate,
function::{parse_function_call, parse_function_call_map, place_args},
@@ -351,152 +352,5 @@
Ok(true)
}
(a, b) => Ok(primitive_equals(&a, &b)?),
- }
-}
-
-#[derive(PartialEq)]
-pub enum ManifestType {
- // Applied in manifestification
- Manifest,
- /// Used for std.manifestJson
- /// Empty array/objects extends to "[\n\n]" instead of "[ ]" as in manifest
- Std,
- // No line breaks, used in `obj+''`
- ToString,
-}
-
-pub struct ManifestJsonOptions<'s> {
- pub padding: &'s str,
- pub mtype: ManifestType,
-}
-
-pub fn manifest_json_ex(val: &Val, options: &ManifestJsonOptions<'_>) -> Result<String> {
- let mut out = String::new();
- manifest_json_ex_buf(val, &mut out, &mut String::new(), options)?;
- Ok(out)
-}
-fn manifest_json_ex_buf(
- val: &Val,
- buf: &mut String,
- cur_padding: &mut String,
- options: &ManifestJsonOptions<'_>,
-) -> Result<()> {
- use std::fmt::Write;
- match val.unwrap_if_lazy()? {
- Val::Bool(v) => {
- if v {
- buf.push_str("true");
- } else {
- buf.push_str("false");
- }
- }
- Val::Null => buf.push_str("null"),
- Val::Str(s) => buf.push_str(&escape_string_json(&s)),
- Val::Num(n) => write!(buf, "{}", n).unwrap(),
- Val::Arr(items) => {
- buf.push('[');
- if !items.is_empty() {
- if options.mtype != ManifestType::ToString {
- buf.push('\n');
- }
-
- let old_len = cur_padding.len();
- cur_padding.push_str(options.padding);
- for (i, item) in items.iter().enumerate() {
- if i != 0 {
- buf.push(',');
- if options.mtype == ManifestType::ToString {
- buf.push(' ');
- } else {
- buf.push('\n');
- }
- }
- buf.push_str(cur_padding);
- manifest_json_ex_buf(item, buf, cur_padding, options)?;
- }
- cur_padding.truncate(old_len);
-
- if options.mtype != ManifestType::ToString {
- buf.push('\n');
- buf.push_str(cur_padding);
- }
- } else if options.mtype == ManifestType::Std {
- buf.push_str("\n\n");
- buf.push_str(cur_padding);
- } else if options.mtype == ManifestType::ToString {
- buf.push(' ');
- }
- buf.push(']');
- }
- Val::Obj(obj) => {
- buf.push('{');
- let fields = obj.visible_fields();
- if !fields.is_empty() {
- if options.mtype != ManifestType::ToString {
- buf.push('\n');
- }
-
- let old_len = cur_padding.len();
- cur_padding.push_str(options.padding);
- for (i, field) in fields.into_iter().enumerate() {
- if i != 0 {
- buf.push(',');
- if options.mtype == ManifestType::ToString {
- buf.push(' ');
- } else {
- buf.push('\n');
- }
- }
- buf.push_str(cur_padding);
- buf.push_str(&escape_string_json(&field));
- buf.push_str(": ");
- manifest_json_ex_buf(&obj.get(field)?.unwrap(), buf, cur_padding, options)?;
- }
- cur_padding.truncate(old_len);
-
- if options.mtype != ManifestType::ToString {
- buf.push('\n');
- buf.push_str(cur_padding);
- }
- } else if options.mtype == ManifestType::Std {
- buf.push_str("\n\n");
- buf.push_str(cur_padding);
- } else if options.mtype == ManifestType::ToString {
- buf.push(' ');
- }
- buf.push('}');
- }
- Val::Func(_) | Val::Intristic(_, _) => {
- throw!(RuntimeError("tried to manifest function".into()))
- }
- Val::Lazy(_) => unreachable!(),
- };
- Ok(())
-}
-pub fn escape_string_json(s: &str) -> String {
- use std::fmt::Write;
- let mut out = String::new();
- out.push('"');
- for c in s.chars() {
- match c {
- '"' => out.push_str("\\\""),
- '\\' => out.push_str("\\\\"),
- '\u{0008}' => out.push_str("\\b"),
- '\u{000c}' => out.push_str("\\f"),
- '\n' => out.push_str("\\n"),
- '\r' => out.push_str("\\r"),
- '\t' => out.push_str("\\t"),
- c if c < 32 as char || (c >= 127 as char && c <= 159 as char) => {
- write!(out, "\\u{:04x}", c as u32).unwrap()
- }
- c => out.push(c),
- }
}
- out.push('"');
- out
-}
-
-#[test]
-fn json_test() {
- assert_eq!(escape_string_json("\u{001f}"), "\"\\u001f\"")
}