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::manifest::{manifest_json_ex, ManifestJsonOptions, ManifestType},3 error::Error::*,4 evaluate,5 function::{parse_function_call, parse_function_call_map, place_args},6 native::NativeCallback,7 throw, with_state, Context, ObjValue, Result,8};9use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc};10use std::{11 cell::RefCell,12 collections::HashMap,13 fmt::{Debug, Display},14 rc::Rc,15};1617enum LazyValInternals {18 Computed(Val),19 Waiting(Box<dyn Fn() -> Result<Val>>),20}21#[derive(Clone)]22pub struct LazyVal(Rc<RefCell<LazyValInternals>>);23impl LazyVal {24 pub fn new(f: Box<dyn Fn() -> Result<Val>>) -> Self {25 LazyVal(Rc::new(RefCell::new(LazyValInternals::Waiting(f))))26 }27 pub fn new_resolved(val: Val) -> Self {28 LazyVal(Rc::new(RefCell::new(LazyValInternals::Computed(val))))29 }30 pub fn evaluate(&self) -> Result<Val> {31 let new_value = match &*self.0.borrow() {32 LazyValInternals::Computed(v) => return Ok(v.clone()),33 LazyValInternals::Waiting(f) => f()?,34 };35 *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());36 Ok(new_value)37 }38}3940#[macro_export]41macro_rules! lazy_val {42 ($f: expr) => {43 $crate::LazyVal::new(Box::new($f))44 };45}46#[macro_export]47macro_rules! resolved_lazy_val {48 ($f: expr) => {49 $crate::LazyVal::new_resolved($f)50 };51}52impl Debug for LazyVal {53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {54 write!(f, "Lazy")55 }56}57impl PartialEq for LazyVal {58 fn eq(&self, other: &Self) -> bool {59 Rc::ptr_eq(&self.0, &other.0)60 }61}6263#[derive(Debug, PartialEq)]64pub struct FuncDesc {65 pub name: Rc<str>,66 pub ctx: Context,67 pub params: ParamsDesc,68 pub body: LocExpr,69}70impl FuncDesc {71 /// This function is always inlined to make tailstrict work72 pub fn evaluate(&self, call_ctx: Context, args: &ArgsDesc, tailstrict: bool) -> Result<Val> {73 let ctx = parse_function_call(74 call_ctx,75 Some(self.ctx.clone()),76 &self.params,77 args,78 tailstrict,79 )?;80 evaluate(ctx, &self.body)81 }8283 pub fn evaluate_map(84 &self,85 call_ctx: Context,86 args: &HashMap<Rc<str>, Val>,87 tailstrict: bool,88 ) -> Result<Val> {89 let ctx = parse_function_call_map(90 call_ctx,91 Some(self.ctx.clone()),92 &self.params,93 args,94 tailstrict,95 )?;96 evaluate(ctx, &self.body)97 }9899 pub fn evaluate_values(&self, call_ctx: Context, args: &[Val]) -> Result<Val> {100 let ctx = place_args(call_ctx, Some(self.ctx.clone()), &self.params, args)?;101 evaluate(ctx, &self.body)102 }103}104105#[derive(Debug, Clone, Copy, PartialEq)]106pub enum ValType {107 Bool,108 Null,109 Str,110 Num,111 Arr,112 Obj,113 Func,114}115impl ValType {116 pub fn name(&self) -> &'static str {117 use ValType::*;118 match self {119 Bool => "boolean",120 Null => "null",121 Str => "string",122 Num => "number",123 Arr => "array",124 Obj => "object",125 Func => "function",126 }127 }128}129impl Display for ValType {130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {131 write!(f, "{}", self.name())132 }133}134135#[derive(Clone)]136pub enum ManifestFormat {137 YamlStream(Box<ManifestFormat>),138 Yaml(usize),139 Json(usize),140 String,141}142143#[derive(Debug, Clone)]144pub enum Val {145 Bool(bool),146 Null,147 Str(Rc<str>),148 Num(f64),149 Lazy(LazyVal),150 Arr(Rc<Vec<Val>>),151 Obj(ObjValue),152 Func(Rc<FuncDesc>),153154 // Library functions implemented in native155 Intristic(Rc<str>, Rc<str>),156 NativeExt(Rc<str>, Rc<NativeCallback>),157}158macro_rules! matches_unwrap {159 ($e: expr, $p: pat, $r: expr) => {160 match $e {161 $p => $r,162 _ => panic!("no match"),163 }164 };165}166impl Val {167 /// Creates Val::Num after checking for overflow. As numbers are f64, we can just check for finity168 pub fn new_checked_num(num: f64) -> Result<Val> {169 if num.is_finite() {170 Ok(Val::Num(num))171 } else {172 throw!(RuntimeError("overflow".into()))173 }174 }175176 pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> {177 let this_type = self.value_type()?;178 if this_type != val_type {179 throw!(TypeMismatch(context, vec![val_type], this_type))180 } else {181 Ok(())182 }183 }184 pub fn try_cast_bool(self, context: &'static str) -> Result<bool> {185 self.assert_type(context, ValType::Bool)?;186 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Bool(v), v))187 }188 pub fn try_cast_str(self, context: &'static str) -> Result<Rc<str>> {189 self.assert_type(context, ValType::Str)?;190 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Str(v), v))191 }192 pub fn try_cast_num(self, context: &'static str) -> Result<f64> {193 self.assert_type(context, ValType::Num)?;194 Ok(matches_unwrap!(self.unwrap_if_lazy()?, Val::Num(v), v))195 }196 pub fn unwrap_if_lazy(&self) -> Result<Self> {197 Ok(if let Val::Lazy(v) = self {198 v.evaluate()?.unwrap_if_lazy()?199 } else {200 self.clone()201 })202 }203 pub fn value_type(&self) -> Result<ValType> {204 Ok(match self {205 Val::Str(..) => ValType::Str,206 Val::Num(..) => ValType::Num,207 Val::Arr(..) => ValType::Arr,208 Val::Obj(..) => ValType::Obj,209 Val::Bool(_) => ValType::Bool,210 Val::Null => ValType::Null,211 Val::Func(..) | Val::Intristic(_, _) | Val::NativeExt(_, _) => ValType::Func,212 Val::Lazy(_) => self.clone().unwrap_if_lazy()?.value_type()?,213 })214 }215216 pub fn to_string(&self) -> Result<Rc<str>> {217 Ok(match self.unwrap_if_lazy()? {218 Val::Bool(true) => "true".into(),219 Val::Bool(false) => "false".into(),220 Val::Null => "null".into(),221 Val::Str(s) => s,222 v => manifest_json_ex(223 &v,224 &ManifestJsonOptions {225 padding: &"",226 mtype: ManifestType::ToString,227 },228 )?229 .into(),230 })231 }232233 /// Expects value to be object, outputs (key, manifested value) pairs234 pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(Rc<str>, Rc<str>)>> {235 let obj = match self {236 Val::Obj(obj) => obj,237 _ => throw!(MultiManifestOutputIsNotAObject),238 };239 let keys = obj.visible_fields();240 let mut out = Vec::with_capacity(keys.len());241 for key in keys {242 let value = obj243 .get(key.clone())?244 .expect("item in object")245 .manifest(ty)?;246 out.push((key, value));247 }248 Ok(out)249 }250251 /// Expects value to be array, outputs manifested values252 pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<Rc<str>>> {253 let arr = match self {254 Val::Arr(a) => a,255 _ => throw!(StreamManifestOutputIsNotAArray),256 };257 let mut out = Vec::with_capacity(arr.len());258 for i in arr.iter() {259 out.push(i.manifest(ty)?);260 }261 Ok(out)262 }263264 pub fn manifest(&self, ty: &ManifestFormat) -> Result<Rc<str>> {265 Ok(match ty {266 ManifestFormat::YamlStream(format) => {267 let arr = match self {268 Val::Arr(a) => a,269 _ => throw!(StreamManifestOutputIsNotAArray),270 };271 let mut out = String::new();272273 match format as &ManifestFormat {274 ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),275 ManifestFormat::String => throw!(StreamManifestCannotNestString),276 _ => {}277 };278279 if !arr.is_empty() {280 for v in arr.iter() {281 out.push_str("---\n");282 out.push_str(&v.manifest(format)?);283 out.push_str("\n");284 }285 out.push_str("...");286 }287288 out.into()289 }290 ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,291 ManifestFormat::Json(padding) => self.to_json(*padding)?,292 ManifestFormat::String => match self {293 Val::Str(s) => s.clone(),294 _ => throw!(StringManifestOutputIsNotAString),295 },296 })297 }298299 /// For manifestification300 pub fn to_json(&self, padding: usize) -> Result<Rc<str>> {301 manifest_json_ex(302 self,303 &ManifestJsonOptions {304 padding: &" ".repeat(padding),305 mtype: if padding == 0 {306 ManifestType::Minify307 } else {308 ManifestType::Manifest309 },310 },311 )312 .map(|s| s.into())313 }314315 /// Calls std.manifestJson316 #[cfg(feature = "faster")]317 pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {318 manifest_json_ex(319 &self,320 &ManifestJsonOptions {321 padding: &" ".repeat(padding),322 mtype: ManifestType::Std,323 },324 )325 .map(|s| s.into())326 }327328 /// Calls std.manifestJson329 #[cfg(not(feature = "faster"))]330 pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {331 with_state(|s| {332 let ctx = s333 .create_default_context()?334 .with_var("__tmp__to_json__".into(), self.clone())?;335 Ok(evaluate(336 ctx,337 &el!(Expr::Apply(338 el!(Expr::Index(339 el!(Expr::Var("std".into())),340 el!(Expr::Str("manifestJsonEx".into()))341 )),342 ArgsDesc(vec![343 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),344 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))345 ]),346 false347 )),348 )?349 .try_cast_str("to json")?)350 })351 }352 pub fn to_yaml(&self, padding: usize) -> Result<Rc<str>> {353 with_state(|s| {354 let ctx = s355 .create_default_context()?356 .with_var("__tmp__to_json__".into(), self.clone());357 Ok(evaluate(358 ctx,359 &el!(Expr::Apply(360 el!(Expr::Index(361 el!(Expr::Var("std".into())),362 el!(Expr::Str("manifestYamlDoc".into()))363 )),364 ArgsDesc(vec![365 Arg(None, el!(Expr::Var("__tmp__to_json__".into()))),366 Arg(None, el!(Expr::Str(" ".repeat(padding).into())))367 ]),368 false369 )),370 )?371 .try_cast_str("to json")?)372 })373 }374}375376fn is_function_like(val: &Val) -> bool {377 matches!(val, Val::Func(_) | Val::Intristic(_, _) | Val::NativeExt(_, _))378}379380/// Implements std.primitiveEquals builtin381pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {382 Ok(match (val_a.unwrap_if_lazy()?, val_b.unwrap_if_lazy()?) {383 (Val::Bool(a), Val::Bool(b)) => a == b,384 (Val::Null, Val::Null) => true,385 (Val::Str(a), Val::Str(b)) => a == b,386 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,387 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(388 "primitiveEquals operates on primitive types, got array".into(),389 )),390 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(391 "primitiveEquals operates on primitive types, got object".into(),392 )),393 (a, b) if is_function_like(&a) && is_function_like(&b) => {394 throw!(RuntimeError("cannot test equality of functions".into()))395 }396 (_, _) => false,397 })398}399400/// Native implementation of std.equals401pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {402 let val_a = val_a.unwrap_if_lazy()?;403 let val_b = val_b.unwrap_if_lazy()?;404405 if val_a.value_type()? != val_b.value_type()? {406 return Ok(false);407 }408 match (val_a, val_b) {409 // Cant test for ptr equality, because all fields needs to be evaluated410 (Val::Arr(a), Val::Arr(b)) => {411 if a.len() != b.len() {412 return Ok(false);413 }414 for (a, b) in a.iter().zip(b.iter()) {415 if !equals(&a.unwrap_if_lazy()?, &b.unwrap_if_lazy()?)? {416 return Ok(false);417 }418 }419 Ok(true)420 }421 (Val::Obj(a), Val::Obj(b)) => {422 let fields = a.visible_fields();423 if fields != b.visible_fields() {424 return Ok(false);425 }426 for field in fields {427 if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {428 return Ok(false);429 }430 }431 Ok(true)432 }433 (a, b) => Ok(primitive_equals(&a, &b)?),434 }435}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',