difftreelog
perf unify function calls
in: master
8 files changed
crates/jrsonnet-evaluator/build.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/build.rs
+++ b/crates/jrsonnet-evaluator/build.rs
@@ -39,7 +39,7 @@
if **name == *"join" || **name == *"manifestJsonEx" ||
**name == *"escapeStringJson" || **name == *"equals" ||
**name == *"base64" || **name == *"foldl" || **name == *"foldr" ||
- **name == *"sortImpl" || **name == *"format" || **name == *"range"
+ **name == *"sortImpl" || **name == *"format" || **name == *"range" || **name == *"reverse"
)
})
.collect(),
crates/jrsonnet-evaluator/src/builtin/manifest.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/manifest.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/manifest.rs
@@ -117,9 +117,7 @@
}
buf.push('}');
}
- Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _) => {
- throw!(RuntimeError("tried to manifest function".into()))
- }
+ Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
Val::Lazy(_) => unreachable!(),
};
Ok(())
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,5 +1,354 @@
+use crate::{
+ equals,
+ error::{Error::*, Result},
+ evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,
+ ValType,
+};
+use format::{format_arr, format_obj};
+use jrsonnet_parser::{ArgsDesc, ExprLocation};
+use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};
+use std::{path::PathBuf, rc::Rc};
+
pub mod stdlib;
pub use stdlib::*;
pub mod format;
pub mod manifest;
+pub mod sort;
+
+pub fn call_builtin(
+ context: Context,
+ loc: &Option<ExprLocation>,
+ ns: &str,
+ name: &str,
+ args: &ArgsDesc,
+) -> Result<Val> {
+ Ok(match (ns, &name as &str) {
+ // arr/string/function
+ ("std", "length") => parse_args!(context, "std.length", args, 1, [
+ 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];
+ ], {
+ Ok(match x {
+ Val::Str(n) => Val::Num(n.chars().count() as f64),
+ Val::Arr(i) => Val::Num(i.len() as f64),
+ Val::Obj(o) => Val::Num(
+ o.fields_visibility()
+ .into_iter()
+ .filter(|(_k, v)| *v)
+ .count() as f64,
+ ),
+ _ => unreachable!(),
+ })
+ })?,
+ // any
+ ("std", "type") => parse_args!(context, "std.type", args, 1, [
+ 0, x, vec![];
+ ], {
+ Ok(Val::Str(x.value_type()?.name().into()))
+ })?,
+ // length, idx=>any
+ ("std", "makeArray") => parse_args!(context, "std.makeArray", args, 2, [
+ 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];
+ 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];
+ ], {
+ if sz < 0.0 {
+ throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));
+ }
+ let mut out = Vec::with_capacity(sz as usize);
+ for i in 0..sz as usize {
+ out.push(func.evaluate_values(
+ Context::new(),
+ &[Val::Num(i as f64)]
+ )?)
+ }
+ Ok(Val::Arr(Rc::new(out)))
+ })?,
+ // string
+ ("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [
+ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+ ], {
+ assert!(
+ str.chars().count() == 1,
+ "std.codepoint should receive single char string"
+ );
+ Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))
+ })?,
+ // object, includeHidden
+ ("std", "objectFieldsEx") => parse_args!(context, "std.objectFieldsEx",args, 2, [
+ 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
+ 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
+ ], {
+ let mut out = obj.fields_visibility()
+ .into_iter()
+ .filter(|(_k, v)| *v || inc_hidden)
+ .map(|(k, _v)|k)
+ .collect::<Vec<_>>();
+ out.sort();
+ Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))
+ })?,
+ // object, field, includeHidden
+ ("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [
+ 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
+ 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];
+ 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
+ ], {
+ Ok(Val::Bool(
+ obj.fields_visibility()
+ .into_iter()
+ .filter(|(_k, v)| *v || inc_hidden)
+ .any(|(k, _v)| *k == *f),
+ ))
+ })?,
+ ("std", "primitiveEquals") => parse_args!(context, "std.primitiveEquals", args, 2, [
+ 0, a, vec![];
+ 1, b, vec![];
+ ], {
+ Ok(Val::Bool(primitive_equals(&a, &b)?))
+ })?,
+ // faster
+ ("std", "equals") => parse_args!(context, "std.equals", args, 2, [
+ 0, a, vec![];
+ 1, b, vec![];
+ ], {
+ Ok(Val::Bool(equals(&a, &b)?))
+ })?,
+ ("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [
+ 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];
+ 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];
+ ], {
+ Ok(Val::Num(a % b))
+ })?,
+ ("std", "floor") => parse_args!(context, "std.floor", args, 1, [
+ 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
+ ], {
+ Ok(Val::Num(x.floor()))
+ })?,
+ ("std", "log") => parse_args!(context, "std.log", args, 2, [
+ 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
+ ], {
+ Ok(Val::Num(n.ln()))
+ })?,
+ ("std", "trace") => parse_args!(context, "std.trace", args, 2, [
+ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+ 1, rest, vec![];
+ ], {
+ eprint!("TRACE:");
+ if let Some(loc) = loc {
+ with_state(|s|{
+ let locs = s.map_source_locations(&loc.0, &[loc.1]);
+ eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);
+ });
+ }
+ eprintln!(" {}", str);
+ Ok(rest)
+ })?,
+ ("std", "pow") => parse_args!(context, "std.modulo", args, 2, [
+ 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
+ 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];
+ ], {
+ Ok(Val::Num(x.powf(n)))
+ })?,
+ ("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [
+ 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
+ ], {
+ Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(
+ || UndefinedExternalVariable(x),
+ )?)
+ })?,
+ ("std", "native") => parse_args!(context, "std.native", args, 1, [
+ 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
+ ], {
+ Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(FuncVal::NativeExt(x.clone(), v))).ok_or_else(
+ || UndefinedExternalFunction(x),
+ )?)
+ })?,
+ ("std", "filter") => parse_args!(context, "std.filter", args, 2, [
+ 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
+ 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+ ], {
+ Ok(Val::Arr(Rc::new(
+ arr.iter()
+ .cloned()
+ .filter(|e| {
+ func
+ .evaluate_values(context.clone(), &[e.clone()])
+ .unwrap()
+ .try_cast_bool("filter predicate")
+ .unwrap()
+ })
+ .collect(),
+ )))
+ })?,
+ // faster
+ ("std", "foldl") => parse_args!(context, "std.foldl", args, 3, [
+ 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
+ 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+ 2, init, vec![];
+ ], {
+ let mut acc = init;
+ for i in arr.iter().cloned() {
+ acc = func.evaluate_values(context.clone(), &[acc, i])?;
+ }
+ Ok(acc)
+ })?,
+ // faster
+ ("std", "foldr") => parse_args!(context, "std.foldr", args, 3, [
+ 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
+ 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+ 2, init, vec![];
+ ], {
+ let mut acc = init;
+ for i in arr.iter().rev().cloned() {
+ acc = func.evaluate_values(context.clone(), &[acc, i])?;
+ }
+ Ok(acc)
+ })?,
+ // faster
+ #[allow(non_snake_case)]
+ ("std", "sortImpl") => parse_args!(context, "std.sort", args, 2, [
+ 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+ 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];
+ ], {
+ if arr.len() <= 1 {
+ return Ok(Val::Arr(arr))
+ }
+ Ok(Val::Arr(sort::sort(context, arr, keyF)?))
+ })?,
+ // faster
+ ("std", "format") => parse_args!(context, "std.format", args, 2, [
+ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+ 1, vals, vec![]
+ ], {
+ push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{
+ Ok(match vals {
+ Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),
+ Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),
+ o => Val::Str(format_arr(&str, &[o])?.into()),
+ })
+ })
+ })?,
+ // faster
+ ("std", "range") => parse_args!(context, "std.range", args, 2, [
+ 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];
+ 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];
+ ], {
+ let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));
+ for i in from as usize..=to as usize {
+ out.push(Val::Num(i as f64));
+ }
+ Ok(Val::Arr(Rc::new(out)))
+ })?,
+ ("std", "char") => parse_args!(context, "std.char", args, 1, [
+ 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
+ ], {
+ let mut out = String::new();
+ out.push(std::char::from_u32(n as u32).ok_or_else(||
+ InvalidUnicodeCodepointGot(n as u32)
+ )?);
+ Ok(Val::Str(out.into()))
+ })?,
+ ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [
+ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+ ], {
+ Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))
+ })?,
+ ("std", "md5") => parse_args!(context, "std.md5", args, 1, [
+ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+ ], {
+ Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))
+ })?,
+ // faster
+ ("std", "base64") => parse_args!(context, "std.base64", args, 1, [
+ 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];
+ ], {
+ Ok(Val::Str(match input {
+ Val::Str(s) => {
+ base64::encode(s.bytes().collect::<Vec<_>>()).into()
+ },
+ Val::Arr(a) => {
+ base64::encode(a.iter().map(|v| {
+ Ok(v.clone().try_cast_num("base64 array")? as u8)
+ }).collect::<Result<Vec<_>>>()?).into()
+ },
+ _ => unreachable!()
+ }))
+ })?,
+ // faster
+ ("std", "join") => parse_args!(context, "std.join", args, 2, [
+ 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];
+ 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+ ], {
+ Ok(match sep {
+ Val::Arr(joiner_items) => {
+ let mut out = Vec::new();
+
+ let mut first = true;
+ for item in arr.iter().cloned() {
+ if let Val::Arr(items) = item.unwrap_if_lazy()? {
+ if !first {
+ out.reserve(joiner_items.len());
+ out.extend(joiner_items.iter().cloned());
+ }
+ first = false;
+ out.reserve(items.len());
+ out.extend(items.iter().cloned());
+ } else {
+ throw!(RuntimeError("in std.join all items should be arrays".into()));
+ }
+ }
+
+ Val::Arr(Rc::new(out))
+ },
+ Val::Str(sep) => {
+ let mut out = String::new();
+
+ let mut first = true;
+ for item in arr.iter().cloned() {
+ if let Val::Str(item) = item.unwrap_if_lazy()? {
+ if !first {
+ out += &sep;
+ }
+ first = false;
+ out += &item;
+ } else {
+ throw!(RuntimeError("in std.join all items should be strings".into()));
+ }
+ }
+
+ Val::Str(out.into())
+ },
+ _ => unreachable!()
+ })
+ })?,
+ // Faster
+ ("std", "escapeStringJson") => parse_args!(context, "std.escapeStringJson", args, 1, [
+ 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];
+ ], {
+ Ok(Val::Str(escape_string_json(&str_).into()))
+ })?,
+ // Faster
+ ("std", "manifestJsonEx") => parse_args!(context, "std.manifestJsonEx", args, 2, [
+ 0, value, vec![];
+ 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];
+ ], {
+ Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {
+ padding: &indent,
+ mtype: ManifestType::Std,
+ })?.into()))
+ })?,
+ // Faster
+ ("std", "reverse") => parse_args!(context, "std.reverse", args, 1, [
+ 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+ ], {
+ let mut marr = arr;
+ Rc::make_mut(&mut marr).reverse();
+ Ok(Val::Arr(marr))
+ })?,
+ ("std", "id") => parse_args!(context, "std.id", args, 1, [
+ 0, v, vec![];
+ ], {
+ Ok(v)
+ })?,
+ (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())),
+ })
+}
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,4 +1,7 @@
-use crate::{builtin::format::FormatError, ValType};
+use crate::{
+ builtin::{format::FormatError, sort::SortError},
+ ValType,
+};
use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
use std::{path::PathBuf, rc::Rc};
@@ -69,6 +72,7 @@
InvalidUnicodeCodepointGot(u32),
Format(FormatError),
+ Sort(SortError),
}
impl From<Error> for LocError {
fn from(e: Error) -> Self {
crates/jrsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate.rs
@@ -1,13 +1,7 @@
use crate::{
- builtin::{
- format::{format_arr, format_obj},
- manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType},
- },
- context_creator, equals,
- error::Error::*,
- future_wrapper, lazy_val, parse_args, parse_function_call, primitive_equals, push, throw,
- with_state, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, ObjMember,
- ObjValue, Result, Val, ValType,
+ context_creator, error::Error::*, future_wrapper, lazy_val, push, throw, with_state, Context,
+ ContextCreator, FuncDesc, FuncVal, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val,
+ ValType,
};
use closure::closure;
use jrsonnet_parser::{
@@ -15,7 +9,7 @@
ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType,
Visibility,
};
-use std::{cmp::Ordering, collections::HashMap, path::PathBuf, rc::Rc};
+use std::{collections::HashMap, rc::Rc};
pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc<str>, LazyBinding) {
let b = b.clone();
@@ -51,12 +45,12 @@
}
pub fn evaluate_method(ctx: Context, name: Rc<str>, params: ParamsDesc, body: LocExpr) -> Val {
- Val::Func(Rc::new(FuncDesc {
+ Val::Func(FuncVal::Normal(Rc::new(FuncDesc {
name,
ctx,
params,
body,
- }))
+ })))
}
pub fn evaluate_field_name(
@@ -389,21 +383,6 @@
})
}
-/// Extracts code block and disables inlining for them
-/// Fixes WASM to java bytecode compilation failing because of very large method
-#[cfg(feature = "unstable")]
-macro_rules! noinline {
- ($e:expr) => {
- (#![inline(never)] move || $e)()
- };
-}
-#[cfg(not(feature = "unstable"))]
-macro_rules! noinline {
- ($e:expr) => {
- (move || $e)()
- };
-}
-
pub fn evaluate_apply(
context: Context,
value: &LocExpr,
@@ -414,397 +393,12 @@
let lazy = evaluate(context.clone(), value)?;
let value = lazy.unwrap_if_lazy()?;
Ok(match value {
- Val::Intristic(ns, name) => push(
- loc,
- || format!("intristic <{}.{}> call", ns, name),
- || {
- Ok(match (&ns as &str, &name as &str) {
- // arr/string/function
- ("std", "length") => parse_args!(context, "std.length", args, 1, [
- 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];
- ], {
- Ok(match x {
- Val::Str(n) => Val::Num(n.chars().count() as f64),
- Val::Arr(i) => Val::Num(i.len() as f64),
- Val::Obj(o) => Val::Num(
- o.fields_visibility()
- .into_iter()
- .filter(|(_k, v)| *v)
- .count() as f64,
- ),
- _ => unreachable!(),
- })
- })?,
- // any
- ("std", "type") => parse_args!(context, "std.type", args, 1, [
- 0, x, vec![];
- ], {
- Ok(Val::Str(x.value_type()?.name().into()))
- })?,
- // length, idx=>any
- ("std", "makeArray") => {
- noinline!(parse_args!(context, "std.makeArray", args, 2, [
- 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];
- 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];
- ], {
- if sz < 0.0 {
- throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));
- }
- let mut out = Vec::with_capacity(sz as usize);
- for i in 0..sz as usize {
- out.push(func.evaluate_values(
- Context::new(),
- &[Val::Num(i as f64)]
- )?)
- }
- Ok(Val::Arr(Rc::new(out)))
- }))?
- }
- // string
- ("std", "codepoint") => parse_args!(context, "std.codepoint", args, 1, [
- 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
- ], {
- assert!(
- str.chars().count() == 1,
- "std.codepoint should receive single char string"
- );
- Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))
- })?,
- // object, includeHidden
- ("std", "objectFieldsEx") => {
- noinline!(parse_args!(context, "std.objectFieldsEx",args, 2, [
- 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
- 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
- ], {
- let mut out = obj.fields_visibility()
- .into_iter()
- .filter(|(_k, v)| *v || inc_hidden)
- .map(|(k, _v)|k)
- .collect::<Vec<_>>();
- out.sort();
- Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))
- }))?
- }
- // object, field, includeHidden
- ("std", "objectHasEx") => parse_args!(context, "std.objectHasEx", args, 3, [
- 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
- 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];
- 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
- ], {
- Ok(Val::Bool(
- obj.fields_visibility()
- .into_iter()
- .filter(|(_k, v)| *v || inc_hidden)
- .any(|(k, _v)| *k == *f),
- ))
- })?,
- ("std", "primitiveEquals") => {
- parse_args!(context, "std.primitiveEquals", args, 2, [
- 0, a, vec![];
- 1, b, vec![];
- ], {
- Ok(Val::Bool(primitive_equals(&a, &b)?))
- })?
- }
- // faster
- ("std", "equals") => parse_args!(context, "std.equals", args, 2, [
- 0, a, vec![];
- 1, b, vec![];
- ], {
- Ok(Val::Bool(equals(&a, &b)?))
- })?,
- ("std", "modulo") => parse_args!(context, "std.modulo", args, 2, [
- 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];
- 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];
- ], {
- Ok(Val::Num(a % b))
- })?,
- ("std", "floor") => parse_args!(context, "std.floor", args, 1, [
- 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
- ], {
- Ok(Val::Num(x.floor()))
- })?,
- ("std", "log") => parse_args!(context, "std.log", args, 2, [
- 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
- ], {
- Ok(Val::Num(n.ln()))
- })?,
- ("std", "trace") => parse_args!(context, "std.trace", args, 2, [
- 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
- 1, rest, vec![];
- ], {
- eprint!("TRACE:");
- if let Some(loc) = loc {
- with_state(|s|{
- let locs = s.map_source_locations(&loc.0, &[loc.1]);
- eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);
- });
- }
- eprintln!(" {}", str);
- Ok(rest)
- })?,
- ("std", "pow") => parse_args!(context, "std.modulo", args, 2, [
- 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
- 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];
- ], {
- Ok(Val::Num(x.powf(n)))
- })?,
- ("std", "extVar") => parse_args!(context, "std.extVar", args, 1, [
- 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
- ], {
- Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else(
- || UndefinedExternalVariable(x),
- )?)
- })?,
- ("std", "native") => parse_args!(context, "std.native", args, 1, [
- 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
- ], {
- Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::NativeExt(x.clone(), v)).ok_or_else(
- || UndefinedExternalFunction(x),
- )?)
- })?,
- ("std", "filter") => noinline!(parse_args!(context, "std.filter", args, 2, [
- 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
- 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
- ], {
- Ok(Val::Arr(Rc::new(
- arr.iter()
- .cloned()
- .filter(|e| {
- func
- .evaluate_values(context.clone(), &[e.clone()])
- .unwrap()
- .try_cast_bool("filter predicate")
- .unwrap()
- })
- .collect(),
- )))
- }))?,
- // faster
- ("std", "foldl") => noinline!(parse_args!(context, "std.foldl", args, 3, [
- 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
- 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
- 2, init, vec![];
- ], {
- let mut acc = init;
- for i in arr.iter().cloned() {
- acc = func.evaluate_values(context.clone(), &[acc, i])?;
- }
- Ok(acc)
- }))?,
- // faster
- ("std", "foldr") => noinline!(parse_args!(context, "std.foldr", args, 3, [
- 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
- 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
- 2, init, vec![];
- ], {
- let mut acc = init;
- for i in arr.iter().rev().cloned() {
- acc = func.evaluate_values(context.clone(), &[acc, i])?;
- }
- Ok(acc)
- }))?,
- // faster
- #[allow(non_snake_case)]
- ("std", "sortImpl") => noinline!(parse_args!(context, "std.sort", args, 2, [
- 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
- 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];
- ], {
- if arr.len() <= 1 {
- return Ok(Val::Arr(arr))
- }
- let mut new_arr = arr.iter().cloned().collect::<Vec<_>>();
- match keyF.evaluate_values(context.clone(), &[new_arr[0].clone()])? {
- Val::Str(_) => {
- let mut err = None;
- new_arr.sort_by_cached_key(|k| {
- match keyF.evaluate_values(context.clone(), &[k.clone()]) {
- Ok(Val::Str(v)) => v,
- Ok(_) => {
- err = Some(LocError::new(RuntimeError("types of all array elements should equal".into())));
- "".into()
- }
- Err(e) => {
- err = Some(e);
- "".into()
- }
- }
- });
- if let Some(e) = err {
- return Err(e);
- }
- },
- Val::Num(_) => {
- let mut err = None;
- new_arr.sort_unstable_by(|a, b| {
- match (keyF.evaluate_values(context.clone(), &[a.clone()]), keyF.evaluate_values(context.clone(), &[b.clone()])) {
- (Ok(Val::Num(a)), Ok(Val::Num(b))) => a.partial_cmp(&b).unwrap(),
- (Ok(_a), Ok(_b)) => {
- err = Some(RuntimeError("types of all array elements should equal".into()).into());
- Ordering::Equal
- }
- (Err(e), _) | (_, Err(e)) => {
- err = Some(e);
- Ordering::Equal
- }
- }
- });
- if let Some(e) = err {
- return Err(e);
- }
- },
- _ => throw!(RuntimeError("keys should be number or string".into()))
- }
- Ok(Val::Arr(Rc::new(new_arr)))
- }))?,
- // faster
- ("std", "format") => parse_args!(context, "std.format", args, 2, [
- 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
- 1, vals, vec![]
- ], {
- push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{
- Ok(match vals {
- Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),
- Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),
- o => Val::Str(format_arr(&str, &[o])?.into()),
- })
- })
- })?,
- // faster
- ("std", "range") => parse_args!(context, "std.range", args, 2, [
- 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];
- 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];
- ], {
- let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));
- for i in from as usize..=to as usize {
- out.push(Val::Num(i as f64));
- }
- Ok(Val::Arr(Rc::new(out)))
- })?,
- ("std", "char") => parse_args!(context, "std.char", args, 1, [
- 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
- ], {
- let mut out = String::new();
- out.push(std::char::from_u32(n as u32).ok_or_else(||
- InvalidUnicodeCodepointGot(n as u32)
- )?);
- Ok(Val::Str(out.into()))
- })?,
- ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [
- 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
- ], {
- Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))
- })?,
- ("std", "md5") => noinline!(parse_args!(context, "std.md5", args, 1, [
- 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
- ], {
- Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))
- }))?,
- // faster
- ("std", "base64") => parse_args!(context, "std.base64", args, 1, [
- 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];
- ], {
- Ok(Val::Str(match input {
- Val::Str(s) => {
- base64::encode(s.bytes().collect::<Vec<_>>()).into()
- },
- Val::Arr(a) => {
- base64::encode(a.iter().map(|v| {
- Ok(v.clone().try_cast_num("base64 array")? as u8)
- }).collect::<Result<Vec<_>>>()?).into()
- },
- _ => unreachable!()
- }))
- })?,
- // faster
- ("std", "join") => noinline!(parse_args!(context, "std.join", args, 2, [
- 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];
- 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
- ], {
- Ok(match sep {
- Val::Arr(joiner_items) => {
- let mut out = Vec::new();
-
- let mut first = true;
- for item in arr.iter().cloned() {
- if let Val::Arr(items) = item.unwrap_if_lazy()? {
- if !first {
- out.reserve(joiner_items.len());
- out.extend(joiner_items.iter().cloned());
- }
- first = false;
- out.reserve(items.len());
- out.extend(items.iter().cloned());
- } else {
- throw!(RuntimeError("in std.join all items should be arrays".into()));
- }
- }
-
- Val::Arr(Rc::new(out))
- },
- Val::Str(sep) => {
- let mut out = String::new();
-
- let mut first = true;
- for item in arr.iter().cloned() {
- if let Val::Str(item) = item.unwrap_if_lazy()? {
- if !first {
- out += &sep;
- }
- first = false;
- out += &item;
- } else {
- throw!(RuntimeError("in std.join all items should be strings".into()));
- }
- }
-
- Val::Str(out.into())
- },
- _ => unreachable!()
- })
- }))?,
- // Faster
- ("std", "escapeStringJson") => {
- parse_args!(context, "std.escapeStringJson", args, 1, [
- 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];
- ], {
- Ok(Val::Str(escape_string_json(&str_).into()))
- })?
- }
- // Faster
- ("std", "manifestJsonEx") => {
- parse_args!(context, "std.manifestJsonEx", args, 2, [
- 0, value, vec![];
- 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];
- ], {
- Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {
- padding: &indent,
- mtype: ManifestType::Std,
- })?.into()))
- })?
- }
- (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())),
- })
- },
- )?,
- Val::NativeExt(n, f) => push(
- loc,
- || format!("native <{}> call", n),
- || {
- let args = parse_function_call(context, None, &f.params, args, true)?;
- let mut out_args = Vec::with_capacity(f.params.len());
- for p in f.params.0.iter() {
- out_args.push(args.binding(p.0.clone())?.evaluate()?);
- }
- Ok(f.call(&out_args)?)
- },
- )?,
Val::Func(f) => {
- let body = || f.evaluate(context, args, tailstrict);
+ let body = || f.evaluate(context, loc, args, tailstrict);
if tailstrict {
body()?
} else {
- push(loc, || format!("function <{}> call", f.name), body)?
+ push(loc, || format!("function <{}> call", f.name()), body)?
}
}
v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)),
@@ -874,7 +468,7 @@
} else if let Some(Val::Str(n)) =
v.get("__intristic_namespace__".into())?
{
- Ok(Val::Intristic(n, s))
+ Ok(Val::Func(FuncVal::Intristic(n, s)))
} else {
throw!(NoSuchField(s))
}
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -40,9 +40,7 @@
}
Value::Object(out)
}
- Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _) => {
- throw!(RuntimeError("tried to manifest function".into()))
- }
+ Val::Func(_) => throw!(RuntimeError("tried to manifest function".into())),
})
}
}
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use crate::{2 builtin::{3 call_builtin,4 manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},5 },6 error::Error::*,7 evaluate,8 function::{parse_function_call, parse_function_call_map, place_args},9 native::NativeCallback,10 throw, with_state, Context, ObjValue, Result,11};12use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, ExprLocation, LocExpr, ParamsDesc};13use std::{14 cell::RefCell,15 collections::HashMap,16 fmt::{Debug, Display},17 rc::Rc,18};1920enum LazyValInternals {21 Computed(Val),22 Waiting(Box<dyn Fn() -> Result<Val>>),23}24#[derive(Clone)]25pub struct LazyVal(Rc<RefCell<LazyValInternals>>);26impl LazyVal {27 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {28 LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))29 }30 pub fn new_resolved(val: Val) -> Self {31 LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))32 }33 pub fn evaluate(&self) -> Result<Val> {34 let new_value = match &*self.0.borrow() {35 LazyValInternals::Computed(v) => return Ok(v.clone()),36 LazyValInternals::Waiting(f) => f()?,37 };38 *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());39 Ok(new_value)40 }41}4243#[macro_export]44macro_rules! lazy_val {45 ($f: expr) => {46 $crate::LazyVal::new(Box::new($f))47 };48}49#[macro_export]50macro_rules! resolved_lazy_val {51 ($f: expr) => {52 $crate::LazyVal::new_resolved($f)53 };54}55impl Debug for LazyVal {56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {57 write!(f, "Lazy")58 }59}60impl PartialEq for LazyVal {61 fn eq(&self, other: &Self) -> bool {62 Rc::ptr_eq(&self.0, &other.0)63 }64}6566#[derive(Debug, PartialEq)]67pub struct FuncDesc {68 pub name: Rc<str>,69 pub ctx: Context,70 pub params: ParamsDesc,71 pub body: LocExpr,72}7374#[derive(Debug, Clone)]75pub enum FuncVal {76 /// Plain function implemented in jsonnet77 Normal(Rc<FuncDesc>),78 /// Standard library function79 Intristic(Rc<str>, Rc<str>),80 /// Library functions implemented in native81 NativeExt(Rc<str>, Rc<NativeCallback>),82}83impl PartialEq for FuncVal {84 fn eq(&self, other: &Self) -> bool {85 match (self, other) {86 (FuncVal::Normal(a), FuncVal::Normal(b)) => a == b,87 (FuncVal::Intristic(ans, an), FuncVal::Intristic(bns, bn)) => ans == bns && an == bn,88 (FuncVal::NativeExt(an, _), FuncVal::NativeExt(bn, _)) => an == bn,89 (..) => false,90 }91 }92}93impl FuncVal {94 pub fn is_ident(&self) -> bool {95 matches!(&self, FuncVal::Intristic(ns, n) if ns as &str == "std" && n as &str == "id")96 }97 pub fn name(&self) -> Rc<str> {98 match self {99 FuncVal::Normal(normal) => normal.name.clone(),100 FuncVal::Intristic(ns, name) => format!("intristic.{}.{}", ns, name).into(),101 FuncVal::NativeExt(n, _) => format!("native.{}", n).into(),102 }103 }104 pub fn evaluate(105 &self,106 call_ctx: Context,107 loc: &Option<ExprLocation>,108 args: &ArgsDesc,109 tailstrict: bool,110 ) -> Result<Val> {111 match self {112 FuncVal::Normal(func) => {113 let ctx = parse_function_call(114 call_ctx,115 Some(func.ctx.clone()),116 &func.params,117 args,118 tailstrict,119 )?;120 evaluate(ctx, &func.body)121 }122 FuncVal::Intristic(ns, name) => call_builtin(call_ctx, loc, &ns, &name, args),123 FuncVal::NativeExt(_name, handler) => {124 let args = parse_function_call(call_ctx, None, &handler.params, args, true)?;125 let mut out_args = Vec::with_capacity(handler.params.len());126 for p in handler.params.0.iter() {127 out_args.push(args.binding(p.0.clone())?.evaluate()?);128 }129 Ok(handler.call(&out_args)?)130 }131 }132 }133134 pub fn evaluate_map(135 &self,136 call_ctx: Context,137 args: &HashMap<Rc<str>, Val>,138 tailstrict: bool,139 ) -> Result<Val> {140 match self {141 FuncVal::Normal(func) => {142 let ctx = parse_function_call_map(143 call_ctx,144 Some(func.ctx.clone()),145 &func.params,146 args,147 tailstrict,148 )?;149 evaluate(ctx, &func.body)150 }151 FuncVal::Intristic(_, _) => todo!(),152 FuncVal::NativeExt(_, _) => todo!(),153 }154 }155156 pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {157 match self {158 FuncVal::Normal(func) => {159 let ctx = place_args(call_ctx, Some(func.ctx.clone()), &func.params, args)?;160 evaluate(ctx, &func.body)161 }162 FuncVal::Intristic(_, _) => todo!(),163 FuncVal::NativeExt(_, _) => todo!(),164 }165 }166}167168#[derive(Debug, Clone, Copy, PartialEq)]169pub enum ValType {170 Bool,171 Null,172 Str,173 Num,174 Arr,175 Obj,176 Func,177}178impl ValType {179 pub fn name(&self) -> &'static str {180 use ValType::*;181 match self {182 Bool => "boolean",183 Null => "null",184 Str => "string",185 Num => "number",186 Arr => "array",187 Obj => "object",188 Func => "function",189 }190 }191}192impl Display for ValType {193 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {194 write!(f, "{}", self.name())195 }196}197198#[derive(Clone)]199pub enum ManifestFormat {200 YamlStream(Box<ManifestFormat>),201 Yaml(usize),202 Json(usize),203 String,204}205206#[derive(Debug, Clone)]207pub enum Val {208 Bool(bool),209 Null,210 Str(Rc<str>),211 Num(f64),212 Lazy(LazyVal),213 Arr(Rc<Vec<Val>>),214 Obj(ObjValue),215 Func(FuncVal),216}217macro_rules! matches_unwrap {218 ($e: expr, $p: pat, $r: expr) => {219 match $e {220 $p => $r,221 _ => panic!("no match"),222 }223 };224}225impl Val {226 /// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity227 pub fn new_checked_num(num: f64) -> Result<Val> {228 if num.is_finite() {229 Ok(Val::Num(num))230 } else {231 throw!(RuntimeError("overflow".into()))232 }233 }234235 pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {236 let this_type = self.value_type()?;237 if this_type != val_type {238 throw!(TypeMismatch(context, vec![val_type], this_type))239 } else {240 Ok(())241 }242 }243 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {244 self.assert_type(context, ValType::Bool)?;245 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))246 }247 pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {248 self.assert_type(context, ValType::Str)?;249 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))250 }251 pub fn try_cast_num(self, context: &'static str) -> Result<f64> {252 self.assert_type(context, ValType::Num)?;253 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))254 }255 pub fn inplace_unwrap(&mut self) -> Result<()> {256 while let Val::Lazy(lazy) = self {257 *self = lazy.evaluate()?;258 }259 Ok(())260 }261 pub fn unwrap_if_lazy(&self) -> Result<Self> {262 Ok(if let Val::Lazy(v) = self {263 v.evaluate()?.unwrap_if_lazy()?264 } else {265 self.clone()266 })267 }268 pub fn value_type(&self) -> Result<ValType> {269 Ok(match self {270 Val::Str(..) => ValType::Str,271 Val::Num(..) => ValType::Num,272 Val::Arr(..) => ValType::Arr,273 Val::Obj(..) => ValType::Obj,274 Val::Bool(_) => ValType::Bool,275 Val::Null => ValType::Null,276 Val::Func(..) => ValType::Func,277 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,278 })279 }280281 pub fn to_string(&self) -> Result<Rc<str>> {282 Ok(match self.unwrap_if_lazy()? {283 Val::Bool(true) => "true".into(),284 Val::Bool(false) => "false".into(),285 Val::Null => "null".into(),286 Val::Str(s) => s,287 v => manifest_json_ex(288 &v,289 &ManifestJsonOptions {290 padding: &"",291 mtype: ManifestType::ToString,292 },293 )?294 .into(),295 })296 }297298 /// Expects value to be object, outputs (key, manifested value) pairs299 pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(Rc<str>, Rc<str>)>> {300 let obj = match self {301 Val::Obj(obj) => obj,302 _ => throw!(MultiManifestOutputIsNotAObject),303 };304 let keys = obj.visible_fields();305 let mut out = Vec::with_capacity(keys.len());306 for key in keys {307 let value = obj308 .get(key.clone())?309 .expect("item in object")310 .manifest(ty)?;311 out.push((key, value));312 }313 Ok(out)314 }315316 /// Expects value to be array, outputs manifested values317 pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<Rc<str>>> {318 let arr = match self {319 Val::Arr(a) => a,320 _ => throw!(StreamManifestOutputIsNotAArray),321 };322 let mut out = Vec::with_capacity(arr.len());323 for i in arr.iter() {324 out.push(i.manifest(ty)?);325 }326 Ok(out)327 }328329 pub fn manifest(&self, ty: &ManifestFormat) -> Result<Rc<str>> {330 Ok(match ty {331 ManifestFormat::YamlStream(format) => {332 let arr = match self {333 Val::Arr(a) => a,334 _ => throw!(StreamManifestOutputIsNotAArray),335 };336 let mut out = String::new();337338 match format as &ManifestFormat {339 ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),340 ManifestFormat::String => throw!(StreamManifestCannotNestString),341 _ => {}342 };343344 if !arr.is_empty() {345 for v in arr.iter() {346 out.push_str("---\n");347 out.push_str(&v.manifest(format)?);348 out.push_str("\n");349 }350 out.push_str("...");351 }352353 out.into()354 }355 ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,356 ManifestFormat::Json(padding) => self.to_json(*padding)?,357 ManifestFormat::String => match self {358 Val::Str(s) => s.clone(),359 _ => throw!(StringManifestOutputIsNotAString),360 },361 })362 }363364 /// For manifestification365 pub fn to_json(&self, padding: usize) -> Result<Rc<str>> {366 manifest_json_ex(367 self,368 &ManifestJsonOptions {369 padding: &" ".repeat(padding),370 mtype: if padding == 0 {371 ManifestType::Minify372 } else {373 ManifestType::Manifest374 },375 },376 )377 .map(|s| s.into())378 }379380 /// Calls std.manifestJson381 #[cfg(feature = "faster")]382 pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {383 manifest_json_ex(384 &self,385 &ManifestJsonOptions {386 padding: &" ".repeat(padding),387 mtype: ManifestType::Std,388 },389 )390 .map(|s| s.into())391 }392393 /// Calls std.manifestJson394 #[cfg(not(feature = "faster"))]395 pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {396 with_state(|s| {397 let ctx = s398 .create_default_context()?399 .with_var("__tmp__to_json__".into(), self.clone())?;400 Ok(evaluate(401 ctx,402 &el!(Expr::Apply(403 el!(Expr::Index(404 el!(Expr::Var("std".into())),405 el!(Expr::Str("manifestJsonEx".into()))406 )),407 ArgsDesc(vec![408 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),409 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))410 ]),411 false412 )),413 )?414 .try_cast_str("to json")?)415 })416 }417 pub fn to_yaml(&self, padding: usize) -> Result<Rc<str>> {418 with_state(|s| {419 let ctx = s420 .create_default_context()?421 .with_var("__tmp__to_json__".into(), self.clone());422 Ok(evaluate(423 ctx,424 &el!(Expr::Apply(425 el!(Expr::Index(426 el!(Expr::Var("std".into())),427 el!(Expr::Str("manifestYamlDoc".into()))428 )),429 ArgsDesc(vec![430 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),431 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))432 ]),433 false434 )),435 )?436 .try_cast_str("to json")?)437 })438 }439}440441fn is_function_like(val: &Val) -> bool {442 matches!(val, Val::Func(_))443}444445/// Implements std.primitiveEquals builtin446pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {447 Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {448 (Val::Bool(a), Val::Bool(b)) => a == b,449 (Val::Null, Val::Null) => true,450 (Val::Str(a), Val::Str(b)) => a == b,451 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,452 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(453 "primitiveEquals operates on primitive types, got array".into(),454 )),455 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(456 "primitiveEquals operates on primitive types, got object".into(),457 )),458 (a, b) if is_function_like(&a) && is_function_like(&b) => {459 throw!(RuntimeError("cannot test equality of functions".into()))460 }461 (_, _) => false,462 })463}464465/// Native implementation of std.equals466pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {467 let val_a = val_a.unwrap_if_lazy()?;468 let val_b = val_b.unwrap_if_lazy()?;469470 if val_a.value_type()? != val_b.value_type()? {471 return Ok(false);472 }473 match (val_a, val_b) {474 // Cant test for ptr equality, because all fields needs to be evaluated475 (Val::Arr(a), Val::Arr(b)) => {476 if a.len() != b.len() {477 return Ok(false);478 }479 for (a, b) in a.iter().zip(b.iter()) {480 if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {481 return Ok(false);482 }483 }484 Ok(true)485 }486 (Val::Obj(a), Val::Obj(b)) => {487 let fields = a.visible_fields();488 if fields != b.visible_fields() {489 return Ok(false);490 }491 for field in fields {492 if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {493 return Ok(false);494 }495 }496 Ok(true)497 }498 (a, b) => Ok(primitive_equals(&a, &b)?),499 }500}crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -2,7 +2,7 @@
__intristic_namespace__:: 'std',
local std = self,
- local id = function(x) x,
+ local id = std.id,
isString(v):: std.type(v) == 'string',
isNumber(v):: std.type(v) == 'number',