--- a/crates/jsonnet-evaluator/Cargo.toml +++ b/crates/jsonnet-evaluator/Cargo.toml @@ -7,8 +7,10 @@ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["serialized-stdlib"] +default = ["serialized-stdlib", "faster"] serialized-stdlib = ["serde", "bincode"] +# Replace some standard library functions with faster implementations +faster = [] [dependencies] jsonnet-parser = { path = "../jsonnet-parser" } --- a/crates/jsonnet-evaluator/build.rs +++ b/crates/jsonnet-evaluator/build.rs @@ -1,11 +1,14 @@ use bincode::serialize; -use jsonnet_parser::{parse, ParserSettings}; +use jsonnet_parser::{ + parse, Expr, FieldMember, FieldName, LocExpr, Member, ObjBody, ParserSettings, +}; use jsonnet_stdlib::STDLIB_STR; use std::{ env, fs::File, io::Write, path::{Path, PathBuf}, + rc::Rc, }; fn main() { @@ -18,6 +21,32 @@ ) .expect("parse"); + let parsed = if cfg!(feature = "faster") { + let LocExpr(expr, location) = parsed; + LocExpr( + Rc::new(match Rc::try_unwrap(expr).unwrap() { + Expr::Obj(ObjBody::MemberList(members)) => Expr::Obj(ObjBody::MemberList( + members + .into_iter() + .filter(|p| { + !matches!( + p, + Member::Field(FieldMember { + name: FieldName::Fixed(name), + .. + }) if name == "join" + ) + }) + .collect(), + )), + _ => panic!("std value should be object"), + }), + location, + ) + } else { + parsed + }; + let out_dir = env::var("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("stdlib.bincode"); let mut f = File::create(&dest_path).unwrap(); --- a/crates/jsonnet-evaluator/src/evaluate.rs +++ b/crates/jsonnet-evaluator/src/evaluate.rs @@ -661,6 +661,76 @@ panic!("bad extVar call"); } } + ("std", "filter") => { + assert_eq!(args.len(), 2); + if let (Val::Func(predicate), Val::Arr(arr)) = ( + evaluate(context, &args[0].1)?, + evaluate(context, &args[1].1)?, + ) { + Val::Arr( + arr.into_iter() + .filter(|e| { + predicate + .evaluate_values(&context, &[e.clone()]) + .unwrap() + .try_cast_bool("filter predicate") + .unwrap() + }) + .collect(), + ) + } else { + panic!("bad filter call"); + } + } + // faster + ("std", "join") => { + assert_eq!(args.len(), 2); + let joiner = evaluate(context, &args[0].1)?.unwrap_if_lazy()?; + let items = evaluate(context, &args[1].1)?.unwrap_if_lazy()?; + println!("Before"); + let result = match (joiner, items) { + (Val::Arr(joiner_items), Val::Arr(items)) => { + // TODO: Minimal size should be known + let mut out = Vec::new(); + + let mut first = true; + for item in items { + if let Val::Arr(items) = item.unwrap_if_lazy()? { + if !first { + out.extend(joiner_items.iter().cloned()); + } + first = false; + out.extend(items); + } else { + panic!("all array items should be arrays") + } + } + + Val::Arr(out) + } + (Val::Str(joiner), Val::Arr(items)) => { + let mut out = String::new(); + + let mut first = true; + for item in items { + if let Val::Str(item) = item.unwrap_if_lazy()? { + if !first { + out += &joiner; + } + first = false; + out += &item; + } else { + panic!("all array items should be strings") + } + } + + Val::Str(out) + } + (joiner, items) => panic!("bad join call: {:?} {:?}", joiner, items), + }; + println!("After"); + result + } (ns, name) => panic!("Intristic not found: {}.{}", ns, name), }, Val::Func(f) => {