--- a/crates/jrsonnet-evaluator/README.md +++ b/crates/jrsonnet-evaluator/README.md @@ -8,12 +8,10 @@ - `serialized-stdlib` - serializes standard library AST using serde - - slower than `codegenerated-stdlib` at runtime, but have no compilation speed penality + - used by default - none - leaves only stdlib source code in binary, processing them same way as user supplied data - slowest (as it involves parsing of standard library source code) - -Because of `codegenerated-stdlib` compilation slowdown, `serialized-stdlib` is used by default ### Benchmark --- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -126,6 +126,10 @@ ("strReplace".into(), builtin_str_replace), ("splitLimit".into(), builtin_splitlimit), ("parseJson".into(), builtin_parse_json), + ("asciiUpper".into(), builtin_ascii_upper), + ("asciiLower".into(), builtin_ascii_lower), + ("member".into(), builtin_member), + ("count".into(), builtin_count), ].iter().cloned().collect() }; } @@ -828,6 +832,70 @@ }) } +fn builtin_ascii_upper( + context: Context, + _loc: Option<&ExprLocation>, + args: &ArgsDesc, +) -> Result { + parse_args!(context, "asciiUpper", args, 1, [ + 0, str: ty!(string) => Val::Str; + ], { + Ok(Val::Str(str.to_ascii_uppercase().into())) + }) +} + +fn builtin_ascii_lower( + context: Context, + _loc: Option<&ExprLocation>, + args: &ArgsDesc, +) -> Result { + parse_args!(context, "asciiLower", args, 1, [ + 0, str: ty!(string) => Val::Str; + ], { + Ok(Val::Str(str.to_ascii_lowercase().into())) + }) +} + +fn builtin_member(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result { + parse_args!(context, "member", args, 2, [ + 0, arr: ty!((array | string)); + 1, x: ty!(any); + ], { + match arr { + Val::Str(s) => { + let x = x.try_cast_str("x should be string")?; + Ok(Val::Bool(!x.is_empty() && s.contains(&*x))) + } + Val::Arr(a) => { + for item in a.iter() { + let item = item?; + if equals(&item, &x)? { + return Ok(Val::Bool(true)); + } + } + Ok(Val::Bool(false)) + } + _ => unreachable!(), + } + }) +} + +fn builtin_count(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result { + parse_args!(context, "count", args, 2, [ + 0, arr: ty!(array) => Val::Arr; + 1, x: ty!(any); + ], { + let mut count = 0; + for item in arr.iter() { + let item = item?; + if equals(&item, &x)? { + count += 1; + } + } + Ok(Val::Num(count as f64)) + }) +} + pub fn call_builtin( context: Context, loc: Option<&ExprLocation>, --- a/crates/jrsonnet-evaluator/src/builtin/stdlib.rs +++ b/crates/jrsonnet-evaluator/src/builtin/stdlib.rs @@ -5,15 +5,6 @@ /// To avoid parsing again when issued from the same thread #[allow(unreachable_code)] static PARSED_STDLIB: LocExpr = { - #[cfg(feature = "codegenerated-stdlib")] - { - #[allow(clippy::all)] - return { - use jrsonnet_parser::*; - include!(concat!(env!("OUT_DIR"), "/stdlib.rs")) - }; - } - #[cfg(feature = "serialized-stdlib")] { // Should not panic, stdlib.bincode is generated in build.rs --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -1045,4 +1045,27 @@ .unwrap_err(); assert_eq!(error.error().to_string(), "assert failed: is number"); } + + #[test] + fn test_ascii_upper_lower() { + assert_eval!(r#"std.assertEqual(std.asciiUpper("aBc😀"), "ABC😀")"#); + assert_eval!(r#"std.assertEqual(std.asciiLower("aBc😀"), "abc😀")"#); + } + + #[test] + fn test_member() { + assert_eval!(r#"!std.member("", "")"#); + assert_eval!(r#"std.member("abc", "a")"#); + assert_eval!(r#"!std.member("abc", "d")"#); + assert_eval!(r#"!std.member([], "")"#); + assert_eval!(r#"std.member(["a", "b", "c"], "a")"#); + assert_eval!(r#"!std.member(["a", "b", "c"], "d")"#); + } + + #[test] + fn test_count() { + assert_eval!(r#"std.assertEqual(std.count([], ""), 0)"#); + assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "d"), 0)"#); + assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "a"), 2)"#); + } } --- a/crates/jrsonnet-stdlib/src/std.jsonnet +++ b/crates/jrsonnet-stdlib/src/std.jsonnet @@ -128,21 +128,9 @@ strReplace:: $intrinsic(strReplace), - asciiUpper(str):: - local cp = std.codepoint; - local up_letter(c) = if cp(c) >= 97 && cp(c) < 123 then - std.char(cp(c) - 32) - else - c; - std.join('', std.map(up_letter, std.stringChars(str))), + asciiUpper:: $intrinsic(asciiUpper), - asciiLower(str):: - local cp = std.codepoint; - local down_letter(c) = if cp(c) >= 65 && cp(c) < 91 then - std.char(cp(c) + 32) - else - c; - std.join('', std.map(down_letter, std.stringChars(str))), + asciiLower:: $intrinsic(asciiLower), range:: $intrinsic(range), @@ -155,14 +143,9 @@ slice:: $intrinsic(slice), - member(arr, x):: - if std.isArray(arr) then - std.count(arr, x) > 0 - else if std.isString(arr) then - std.length(std.findSubstr(x, arr)) > 0 - else error 'std.member first argument must be an array or a string', + member:: $intrinsic(member), - count(arr, x):: std.length(std.filter(function(v) v == x, arr)), + count:: $intrinsic(count), mod:: $intrinsic(mod),