git.delta.rocks / jrsonnet / refs/commits / 46b4dd0e38ba

difftreelog

perf make flatMap builtin

Yaroslav Bolyukin2021-07-12parent: #81202f5.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/builtin/mod.rs
1use crate::{2	equals,3	error::{Error::*, Result},4	operator::evaluate_mod_op,5	parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, EvaluationState,6	FuncVal, IndexableVal, LazyVal, Val,7};8use format::{format_arr, format_obj};9use jrsonnet_gc::Gc;10use jrsonnet_interner::IStr;11use jrsonnet_parser::{ArgsDesc, ExprLocation};12use jrsonnet_types::ty;13use std::{collections::HashMap, path::PathBuf, rc::Rc};1415pub mod stdlib;16pub use stdlib::*;1718use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};1920pub mod format;21pub mod manifest;22pub mod sort;2324pub fn std_format(str: IStr, vals: Val) -> Result<Val> {25	push(26		Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),27		|| format!("std.format of {}", str),28		|| {29			Ok(match vals {30				Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),31				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),32				o => Val::Str(format_arr(&str, &[o])?.into()),33			})34		},35	)36}3738pub fn std_slice(39	indexable: IndexableVal,40	index: Option<usize>,41	end: Option<usize>,42	step: Option<usize>,43) -> Result<Val> {44	let index = index.unwrap_or(0);45	let end = end.unwrap_or_else(|| match &indexable {46		IndexableVal::Str(_) => usize::MAX,47		IndexableVal::Arr(v) => v.len(),48	});49	let step = step.unwrap_or(1);50	match &indexable {51		IndexableVal::Str(s) => Ok(Val::Str(52			(s.chars()53				.skip(index)54				.take(end - index)55				.step_by(step)56				.collect::<String>())57			.into(),58		)),59		IndexableVal::Arr(arr) => Ok(Val::Arr(60			(arr.iter()61				.skip(index)62				.take(end - index)63				.step_by(step)64				.collect::<Result<Vec<Val>>>()?)65			.into(),66		)),67	}68}6970type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val>;7172type BuiltinsType = HashMap<Box<str>, Builtin>;7374thread_local! {75	static BUILTINS: BuiltinsType = {76		[77			("length".into(), builtin_length as Builtin),78			("type".into(), builtin_type),79			("makeArray".into(), builtin_make_array),80			("codepoint".into(), builtin_codepoint),81			("objectFieldsEx".into(), builtin_object_fields_ex),82			("objectHasEx".into(), builtin_object_has_ex),83			("slice".into(), builtin_slice),84			("primitiveEquals".into(), builtin_primitive_equals),85			("equals".into(), builtin_equals),86			("modulo".into(), builtin_modulo),87			("mod".into(), builtin_mod),88			("floor".into(), builtin_floor),89			("log".into(), builtin_log),90			("pow".into(), builtin_pow),91			("extVar".into(), builtin_ext_var),92			("native".into(), builtin_native),93			("filter".into(), builtin_filter),94			("map".into(), builtin_map),95			("foldl".into(), builtin_foldl),96			("foldr".into(), builtin_foldr),97			("sortImpl".into(), builtin_sort_impl),98			("format".into(), builtin_format),99			("range".into(), builtin_range),100			("char".into(), builtin_char),101			("encodeUTF8".into(), builtin_encode_utf8),102			("md5".into(), builtin_md5),103			("base64".into(), builtin_base64),104			("trace".into(), builtin_trace),105			("join".into(), builtin_join),106			("escapeStringJson".into(), builtin_escape_string_json),107			("manifestJsonEx".into(), builtin_manifest_json_ex),108			("reverse".into(), builtin_reverse),109			("id".into(), builtin_id),110			("strReplace".into(), builtin_str_replace),111			("parseJson".into(), builtin_parse_json),112		].iter().cloned().collect()113	};114}115116fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {117	parse_args!(context, "length", args, 1, [118		0, x: ty!((string | object | array));119	], {120		Ok(match x {121			Val::Str(n) => Val::Num(n.chars().count() as f64),122			Val::Arr(a) => Val::Num(a.len() as f64),123			Val::Obj(o) => Val::Num(124				o.fields_visibility()125					.into_iter()126					.filter(|(_k, v)| *v)127					.count() as f64,128			),129			_ => unreachable!(),130		})131	})132}133134fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {135	parse_args!(context, "type", args, 1, [136		0, x: ty!(any);137	], {138		Ok(Val::Str(x.value_type().name().into()))139	})140}141142fn builtin_make_array(143	context: Context,144	_loc: Option<&ExprLocation>,145	args: &ArgsDesc,146) -> Result<Val> {147	parse_args!(context, "makeArray", args, 2, [148		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;149		1, func: ty!(function) => Val::Func;150	], {151		let mut out = Vec::with_capacity(sz as usize);152		for i in 0..sz as usize {153			out.push(LazyVal::new_resolved(func.evaluate_values(154				context.clone(),155				&[Val::Num(i as f64)]156			)?))157		}158		Ok(Val::Arr(out.into()))159	})160}161162fn builtin_codepoint(163	context: Context,164	_loc: Option<&ExprLocation>,165	args: &ArgsDesc,166) -> Result<Val> {167	parse_args!(context, "codepoint", args, 1, [168		0, str: ty!(char) => Val::Str;169	], {170		Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))171	})172}173174fn builtin_object_fields_ex(175	context: Context,176	_loc: Option<&ExprLocation>,177	args: &ArgsDesc,178) -> Result<Val> {179	parse_args!(context, "objectFieldsEx", args, 2, [180		0, obj: ty!(object) => Val::Obj;181		1, inc_hidden: ty!(boolean) => Val::Bool;182	], {183		let out = obj.fields_ex(inc_hidden);184		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))185	})186}187188fn builtin_object_has_ex(189	context: Context,190	_loc: Option<&ExprLocation>,191	args: &ArgsDesc,192) -> Result<Val> {193	parse_args!(context, "objectHasEx", args, 3, [194		0, obj: ty!(object) => Val::Obj;195		1, f: ty!(string) => Val::Str;196		2, inc_hidden: ty!(boolean) => Val::Bool;197	], {198		Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))199	})200}201202fn builtin_parse_json(203	context: Context,204	_loc: Option<&ExprLocation>,205	args: &ArgsDesc,206) -> Result<Val> {207	parse_args!(context, "parseJson", args, 1, [208		0, s: ty!(string) => Val::Str;209	], {210		let state = EvaluationState::default();211		let path = PathBuf::from("std.parseJson").into();212		state.evaluate_snippet_raw(path ,s)213	})214}215216fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {217	parse_args!(context, "slice", args, 4, [218		0, indexable: ty!((string | array));219		1, index: ty!((number | null));220		2, end: ty!((number | null));221		3, step: ty!((number | null));222	], {223		std_slice(224			indexable.into_indexable()?,225			index.try_cast_nullable_num("index")?.map(|v| v as usize),226			end.try_cast_nullable_num("end")?.map(|v| v as usize),227			step.try_cast_nullable_num("step")?.map(|v| v as usize),228		)229	})230}231232fn builtin_primitive_equals(233	context: Context,234	_loc: Option<&ExprLocation>,235	args: &ArgsDesc,236) -> Result<Val> {237	parse_args!(context, "primitiveEquals", args, 2, [238		0, a: ty!(any);239		1, b: ty!(any);240	], {241		Ok(Val::Bool(primitive_equals(&a, &b)?))242	})243}244245fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {246	parse_args!(context, "equals", args, 2, [247		0, a: ty!(any);248		1, b: ty!(any);249	], {250		Ok(Val::Bool(equals(&a, &b)?))251	})252}253254fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {255	parse_args!(context, "modulo", args, 2, [256		0, a: ty!(number) => Val::Num;257		1, b: ty!(number) => Val::Num;258	], {259		Ok(Val::Num(a % b))260	})261}262263fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {264	parse_args!(context, "mod", args, 2, [265		0, a: ty!((number | string));266		1, b: ty!(any);267	], {268		evaluate_mod_op(&a, &b)269	})270}271272fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {273	parse_args!(context, "floor", args, 1, [274		0, x: ty!(number) => Val::Num;275	], {276		Ok(Val::Num(x.floor()))277	})278}279280fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {281	parse_args!(context, "log", args, 1, [282		0, n: ty!(number) => Val::Num;283	], {284		Ok(Val::Num(n.ln()))285	})286}287288fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {289	parse_args!(context, "pow", args, 2, [290		0, x: ty!(number) => Val::Num;291		1, n: ty!(number) => Val::Num;292	], {293		Ok(Val::Num(x.powf(n)))294	})295}296297fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {298	parse_args!(context, "extVar", args, 1, [299		0, x: ty!(string) => Val::Str;300	], {301		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)302	})303}304305fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {306	parse_args!(context, "native", args, 1, [307		0, x: ty!(string) => Val::Str;308	], {309		Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Gc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)310	})311}312313fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {314	parse_args!(context, "filter", args, 2, [315		0, func: ty!(function) => Val::Func;316		1, arr: ty!(array) => Val::Arr;317	], {318		Ok(Val::Arr(arr.filter(|val| func319			.evaluate_values(context.clone(), &[val.clone()])?320			.try_cast_bool("filter predicate"))?))321	})322}323324fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {325	parse_args!(context, "map", args, 2, [326		0, func: ty!(function) => Val::Func;327		1, arr: ty!(array) => Val::Arr;328	], {329		Ok(Val::Arr(arr.map(|val| func330			.evaluate_values(context.clone(), &[val]))?))331	})332}333334fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {335	parse_args!(context, "foldl", args, 3, [336		0, func: ty!(function) => Val::Func;337		1, arr: ty!(array) => Val::Arr;338		2, init: ty!(any);339	], {340		let mut acc = init;341		for i in arr.iter() {342			acc = func.evaluate_values(context.clone(), &[acc, i?])?;343		}344		Ok(acc)345	})346}347348fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {349	parse_args!(context, "foldr", args, 3, [350		0, func: ty!(function) => Val::Func;351		1, arr: ty!(array) => Val::Arr;352		2, init: ty!(any);353	], {354		let mut acc = init;355		for i in arr.iter().rev() {356			acc = func.evaluate_values(context.clone(), &[acc, i?])?;357		}358		Ok(acc)359	})360}361362#[allow(non_snake_case)]363fn builtin_sort_impl(364	context: Context,365	_loc: Option<&ExprLocation>,366	args: &ArgsDesc,367) -> Result<Val> {368	parse_args!(context, "sort", args, 2, [369		0, arr: ty!(array) => Val::Arr;370		1, keyF: ty!(function) => Val::Func;371	], {372		if arr.len() <= 1 {373			return Ok(Val::Arr(arr))374		}375		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))376	})377}378379fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {380	parse_args!(context, "format", args, 2, [381		0, str: ty!(string) => Val::Str;382		1, vals: ty!(any)383	], {384		std_format(str, vals)385	})386}387388fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {389	parse_args!(context, "range", args, 2, [390		0, from: ty!(number) => Val::Num;391		1, to: ty!(number) => Val::Num;392	], {393		if to < from {394			return Ok(Val::Arr(ArrValue::new_eager()))395		}396		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));397		for i in from as usize..=to as usize {398			out.push(Val::Num(i as f64));399		}400		Ok(Val::Arr(out.into()))401	})402}403404fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {405	parse_args!(context, "char", args, 1, [406		0, n: ty!(number) => Val::Num;407	], {408		let mut out = String::new();409		out.push(std::char::from_u32(n as u32).ok_or_else(||410			InvalidUnicodeCodepointGot(n as u32)411		)?);412		Ok(Val::Str(out.into()))413	})414}415416fn builtin_encode_utf8(417	context: Context,418	_loc: Option<&ExprLocation>,419	args: &ArgsDesc,420) -> Result<Val> {421	parse_args!(context, "encodeUTF8", args, 1, [422		0, str: ty!(string) => Val::Str;423	], {424		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))425	})426}427428fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {429	parse_args!(context, "md5", args, 1, [430		0, str: ty!(string) => Val::Str;431	], {432		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))433	})434}435436fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {437	parse_args!(context, "trace", args, 2, [438		0, str: ty!(string) => Val::Str;439		1, rest: ty!(any);440	], {441		eprint!("TRACE:");442		if let Some(loc) = loc {443			with_state(|s|{444				let locs = s.map_source_locations(&loc.0, &[loc.1]);445				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);446			});447		}448		eprintln!(" {}", str);449		Ok(rest)450	})451}452453fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {454	parse_args!(context, "base64", args, 1, [455		0, input: ty!((string | (Array<number>)));456	], {457		Ok(Val::Str(match input {458			Val::Str(s) => {459				base64::encode(s.bytes().collect::<Vec<_>>()).into()460			},461			Val::Arr(a) => {462				base64::encode(a.iter().map(|v| {463					Ok(v?.unwrap_num()? as u8)464				}).collect::<Result<Vec<_>>>()?).into()465			},466			_ => unreachable!()467		}))468	})469}470471fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {472	parse_args!(context, "join", args, 2, [473		0, sep: ty!((string | array));474		1, arr: ty!(array) => Val::Arr;475	], {476		Ok(match sep {477			Val::Arr(joiner_items) => {478				let mut out = Vec::new();479480				let mut first = true;481				for item in arr.iter() {482					let item = item?.clone();483					if let Val::Arr(items) = item {484						if !first {485							out.reserve(joiner_items.len());486							// TODO: extend487							for item in joiner_items.iter() {488								out.push(item?);489							}490						}491						first = false;492						out.reserve(items.len());493						// TODO: extend494						for item in items.iter() {495							out.push(item?);496						}497					} else {498						throw!(RuntimeError("in std.join all items should be arrays".into()));499					}500				}501502				Val::Arr(out.into())503			},504			Val::Str(sep) => {505				let mut out = String::new();506507				let mut first = true;508				for item in arr.iter() {509					let item = item?.clone();510					if let Val::Str(item) = item {511						if !first {512							out += &sep;513						}514						first = false;515						out += &item;516					} else {517						throw!(RuntimeError("in std.join all items should be strings".into()));518					}519				}520521				Val::Str(out.into())522			},523			_ => unreachable!()524		})525	})526}527528fn builtin_escape_string_json(529	context: Context,530	_loc: Option<&ExprLocation>,531	args: &ArgsDesc,532) -> Result<Val> {533	parse_args!(context, "escapeStringJson", args, 1, [534		0, str_: ty!(string) => Val::Str;535	], {536		Ok(Val::Str(escape_string_json(&str_).into()))537	})538}539540fn builtin_manifest_json_ex(541	context: Context,542	_loc: Option<&ExprLocation>,543	args: &ArgsDesc,544) -> Result<Val> {545	parse_args!(context, "manifestJsonEx", args, 2, [546		0, value: ty!(any);547		1, indent: ty!(string) => Val::Str;548	], {549		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {550			padding: &indent,551			mtype: ManifestType::Std,552		})?.into()))553	})554}555556fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {557	parse_args!(context, "reverse", args, 1, [558		0, value: ty!(array) => Val::Arr;559	], {560		Ok(Val::Arr(value.reversed()))561	})562}563564fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {565	parse_args!(context, "id", args, 1, [566		0, v: ty!(any);567	], {568		Ok(v)569	})570}571572fn builtin_str_replace(573	context: Context,574	_loc: Option<&ExprLocation>,575	args: &ArgsDesc,576) -> Result<Val> {577	parse_args!(context, "strReplace", args, 3, [578		0, str: ty!(string) => Val::Str;579		1, from: ty!(string) => Val::Str;580		2, to: ty!(string) => Val::Str;581	], {582		let mut out = String::new();583		let mut last_idx = 0;584		while let Some(idx) = (&str[last_idx..]).find(&from as &str) {585			out.push_str(&str[last_idx..last_idx+idx]);586			out.push_str(&to);587			last_idx += idx + from.len();588		}589		if last_idx == 0 {590			return Ok(Val::Str(str))591		}592		out.push_str(&str[last_idx..]);593		Ok(Val::Str(out.into()))594	})595}596597pub fn call_builtin(598	context: Context,599	loc: Option<&ExprLocation>,600	name: &str,601	args: &ArgsDesc,602) -> Result<Val> {603	BUILTINS604		.with(|builtins| builtins.get(name).copied())605		.ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)606}
after · crates/jrsonnet-evaluator/src/builtin/mod.rs
1use crate::{2	equals,3	error::{Error::*, Result},4	operator::evaluate_mod_op,5	parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, EvaluationState,6	FuncVal, IndexableVal, LazyVal, Val,7};8use format::{format_arr, format_obj};9use jrsonnet_gc::Gc;10use jrsonnet_interner::IStr;11use jrsonnet_parser::{ArgsDesc, ExprLocation};12use jrsonnet_types::ty;13use std::{collections::HashMap, path::PathBuf, rc::Rc};1415pub mod stdlib;16pub use stdlib::*;1718use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};1920pub mod format;21pub mod manifest;22pub mod sort;2324pub fn std_format(str: IStr, vals: Val) -> Result<Val> {25	push(26		Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),27		|| format!("std.format of {}", str),28		|| {29			Ok(match vals {30				Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),31				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),32				o => Val::Str(format_arr(&str, &[o])?.into()),33			})34		},35	)36}3738pub fn std_slice(39	indexable: IndexableVal,40	index: Option<usize>,41	end: Option<usize>,42	step: Option<usize>,43) -> Result<Val> {44	let index = index.unwrap_or(0);45	let end = end.unwrap_or_else(|| match &indexable {46		IndexableVal::Str(_) => usize::MAX,47		IndexableVal::Arr(v) => v.len(),48	});49	let step = step.unwrap_or(1);50	match &indexable {51		IndexableVal::Str(s) => Ok(Val::Str(52			(s.chars()53				.skip(index)54				.take(end - index)55				.step_by(step)56				.collect::<String>())57			.into(),58		)),59		IndexableVal::Arr(arr) => Ok(Val::Arr(60			(arr.iter()61				.skip(index)62				.take(end - index)63				.step_by(step)64				.collect::<Result<Vec<Val>>>()?)65			.into(),66		)),67	}68}6970type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val>;7172type BuiltinsType = HashMap<Box<str>, Builtin>;7374thread_local! {75	static BUILTINS: BuiltinsType = {76		[77			("length".into(), builtin_length as Builtin),78			("type".into(), builtin_type),79			("makeArray".into(), builtin_make_array),80			("codepoint".into(), builtin_codepoint),81			("objectFieldsEx".into(), builtin_object_fields_ex),82			("objectHasEx".into(), builtin_object_has_ex),83			("slice".into(), builtin_slice),84			("primitiveEquals".into(), builtin_primitive_equals),85			("equals".into(), builtin_equals),86			("modulo".into(), builtin_modulo),87			("mod".into(), builtin_mod),88			("floor".into(), builtin_floor),89			("log".into(), builtin_log),90			("pow".into(), builtin_pow),91			("extVar".into(), builtin_ext_var),92			("native".into(), builtin_native),93			("filter".into(), builtin_filter),94			("map".into(), builtin_map),95			("flatMap".into(), builtin_flatmap),96			("foldl".into(), builtin_foldl),97			("foldr".into(), builtin_foldr),98			("sortImpl".into(), builtin_sort_impl),99			("format".into(), builtin_format),100			("range".into(), builtin_range),101			("char".into(), builtin_char),102			("encodeUTF8".into(), builtin_encode_utf8),103			("md5".into(), builtin_md5),104			("base64".into(), builtin_base64),105			("trace".into(), builtin_trace),106			("join".into(), builtin_join),107			("escapeStringJson".into(), builtin_escape_string_json),108			("manifestJsonEx".into(), builtin_manifest_json_ex),109			("reverse".into(), builtin_reverse),110			("id".into(), builtin_id),111			("strReplace".into(), builtin_str_replace),112			("parseJson".into(), builtin_parse_json),113		].iter().cloned().collect()114	};115}116117fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {118	parse_args!(context, "length", args, 1, [119		0, x: ty!((string | object | array));120	], {121		Ok(match x {122			Val::Str(n) => Val::Num(n.chars().count() as f64),123			Val::Arr(a) => Val::Num(a.len() as f64),124			Val::Obj(o) => Val::Num(125				o.fields_visibility()126					.into_iter()127					.filter(|(_k, v)| *v)128					.count() as f64,129			),130			_ => unreachable!(),131		})132	})133}134135fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {136	parse_args!(context, "type", args, 1, [137		0, x: ty!(any);138	], {139		Ok(Val::Str(x.value_type().name().into()))140	})141}142143fn builtin_make_array(144	context: Context,145	_loc: Option<&ExprLocation>,146	args: &ArgsDesc,147) -> Result<Val> {148	parse_args!(context, "makeArray", args, 2, [149		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;150		1, func: ty!(function) => Val::Func;151	], {152		let mut out = Vec::with_capacity(sz as usize);153		for i in 0..sz as usize {154			out.push(LazyVal::new_resolved(func.evaluate_values(155				context.clone(),156				&[Val::Num(i as f64)]157			)?))158		}159		Ok(Val::Arr(out.into()))160	})161}162163fn builtin_codepoint(164	context: Context,165	_loc: Option<&ExprLocation>,166	args: &ArgsDesc,167) -> Result<Val> {168	parse_args!(context, "codepoint", args, 1, [169		0, str: ty!(char) => Val::Str;170	], {171		Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))172	})173}174175fn builtin_object_fields_ex(176	context: Context,177	_loc: Option<&ExprLocation>,178	args: &ArgsDesc,179) -> Result<Val> {180	parse_args!(context, "objectFieldsEx", args, 2, [181		0, obj: ty!(object) => Val::Obj;182		1, inc_hidden: ty!(boolean) => Val::Bool;183	], {184		let out = obj.fields_ex(inc_hidden);185		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))186	})187}188189fn builtin_object_has_ex(190	context: Context,191	_loc: Option<&ExprLocation>,192	args: &ArgsDesc,193) -> Result<Val> {194	parse_args!(context, "objectHasEx", args, 3, [195		0, obj: ty!(object) => Val::Obj;196		1, f: ty!(string) => Val::Str;197		2, inc_hidden: ty!(boolean) => Val::Bool;198	], {199		Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))200	})201}202203fn builtin_parse_json(204	context: Context,205	_loc: Option<&ExprLocation>,206	args: &ArgsDesc,207) -> Result<Val> {208	parse_args!(context, "parseJson", args, 1, [209		0, s: ty!(string) => Val::Str;210	], {211		let state = EvaluationState::default();212		let path = PathBuf::from("std.parseJson").into();213		state.evaluate_snippet_raw(path ,s)214	})215}216217fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {218	parse_args!(context, "slice", args, 4, [219		0, indexable: ty!((string | array));220		1, index: ty!((number | null));221		2, end: ty!((number | null));222		3, step: ty!((number | null));223	], {224		std_slice(225			indexable.into_indexable()?,226			index.try_cast_nullable_num("index")?.map(|v| v as usize),227			end.try_cast_nullable_num("end")?.map(|v| v as usize),228			step.try_cast_nullable_num("step")?.map(|v| v as usize),229		)230	})231}232233fn builtin_primitive_equals(234	context: Context,235	_loc: Option<&ExprLocation>,236	args: &ArgsDesc,237) -> Result<Val> {238	parse_args!(context, "primitiveEquals", args, 2, [239		0, a: ty!(any);240		1, b: ty!(any);241	], {242		Ok(Val::Bool(primitive_equals(&a, &b)?))243	})244}245246fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {247	parse_args!(context, "equals", args, 2, [248		0, a: ty!(any);249		1, b: ty!(any);250	], {251		Ok(Val::Bool(equals(&a, &b)?))252	})253}254255fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {256	parse_args!(context, "modulo", args, 2, [257		0, a: ty!(number) => Val::Num;258		1, b: ty!(number) => Val::Num;259	], {260		Ok(Val::Num(a % b))261	})262}263264fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {265	parse_args!(context, "mod", args, 2, [266		0, a: ty!((number | string));267		1, b: ty!(any);268	], {269		evaluate_mod_op(&a, &b)270	})271}272273fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {274	parse_args!(context, "floor", args, 1, [275		0, x: ty!(number) => Val::Num;276	], {277		Ok(Val::Num(x.floor()))278	})279}280281fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {282	parse_args!(context, "log", args, 1, [283		0, n: ty!(number) => Val::Num;284	], {285		Ok(Val::Num(n.ln()))286	})287}288289fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {290	parse_args!(context, "pow", args, 2, [291		0, x: ty!(number) => Val::Num;292		1, n: ty!(number) => Val::Num;293	], {294		Ok(Val::Num(x.powf(n)))295	})296}297298fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {299	parse_args!(context, "extVar", args, 1, [300		0, x: ty!(string) => Val::Str;301	], {302		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)303	})304}305306fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {307	parse_args!(context, "native", args, 1, [308		0, x: ty!(string) => Val::Str;309	], {310		Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Gc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)311	})312}313314fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {315	parse_args!(context, "filter", args, 2, [316		0, func: ty!(function) => Val::Func;317		1, arr: ty!(array) => Val::Arr;318	], {319		Ok(Val::Arr(arr.filter(|val| func320			.evaluate_values(context.clone(), &[val.clone()])?321			.try_cast_bool("filter predicate"))?))322	})323}324325fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {326	parse_args!(context, "map", args, 2, [327		0, func: ty!(function) => Val::Func;328		1, arr: ty!(array) => Val::Arr;329	], {330		Ok(Val::Arr(arr.map(|val| func331			.evaluate_values(context.clone(), &[val]))?))332	})333}334335fn builtin_flatmap(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {336	parse_args!(context, "flatMap", args, 2, [337		0, func: ty!(function) => Val::Func;338		1, arr: ty!((array | string));339	], {340		match arr {341			Val::Str(s) => {342				let mut out = String::new();343				for c in s.chars() {344					match func.evaluate_values(context.clone(), &[Val::Str(c.to_string().into())])? {345						Val::Str(o) => out.push_str(&o),346						_ => throw!(RuntimeError("in std.join all items should be strings".into())),347					};348				}349				Ok(Val::Str(out.into()))350			},351			Val::Arr(a) => {352				let mut out = Vec::new();353				for el in a.iter() {354					let el = el?;355					match func.evaluate_values(context.clone(), &[el])? {356						Val::Arr(o) => for oe in o.iter() {357							out.push(oe?)358						},359						_ => throw!(RuntimeError("in std.join all items should be arrays".into())),360					};361				}362				Ok(Val::Arr(out.into()))363			},364			_ => unreachable!(),365		}366	})367}368369fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {370	parse_args!(context, "foldl", args, 3, [371		0, func: ty!(function) => Val::Func;372		1, arr: ty!(array) => Val::Arr;373		2, init: ty!(any);374	], {375		let mut acc = init;376		for i in arr.iter() {377			acc = func.evaluate_values(context.clone(), &[acc, i?])?;378		}379		Ok(acc)380	})381}382383fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {384	parse_args!(context, "foldr", args, 3, [385		0, func: ty!(function) => Val::Func;386		1, arr: ty!(array) => Val::Arr;387		2, init: ty!(any);388	], {389		let mut acc = init;390		for i in arr.iter().rev() {391			acc = func.evaluate_values(context.clone(), &[acc, i?])?;392		}393		Ok(acc)394	})395}396397#[allow(non_snake_case)]398fn builtin_sort_impl(399	context: Context,400	_loc: Option<&ExprLocation>,401	args: &ArgsDesc,402) -> Result<Val> {403	parse_args!(context, "sort", args, 2, [404		0, arr: ty!(array) => Val::Arr;405		1, keyF: ty!(function) => Val::Func;406	], {407		if arr.len() <= 1 {408			return Ok(Val::Arr(arr))409		}410		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))411	})412}413414fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {415	parse_args!(context, "format", args, 2, [416		0, str: ty!(string) => Val::Str;417		1, vals: ty!(any)418	], {419		std_format(str, vals)420	})421}422423fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {424	parse_args!(context, "range", args, 2, [425		0, from: ty!(number) => Val::Num;426		1, to: ty!(number) => Val::Num;427	], {428		if to < from {429			return Ok(Val::Arr(ArrValue::new_eager()))430		}431		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));432		for i in from as usize..=to as usize {433			out.push(Val::Num(i as f64));434		}435		Ok(Val::Arr(out.into()))436	})437}438439fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {440	parse_args!(context, "char", args, 1, [441		0, n: ty!(number) => Val::Num;442	], {443		let mut out = String::new();444		out.push(std::char::from_u32(n as u32).ok_or_else(||445			InvalidUnicodeCodepointGot(n as u32)446		)?);447		Ok(Val::Str(out.into()))448	})449}450451fn builtin_encode_utf8(452	context: Context,453	_loc: Option<&ExprLocation>,454	args: &ArgsDesc,455) -> Result<Val> {456	parse_args!(context, "encodeUTF8", args, 1, [457		0, str: ty!(string) => Val::Str;458	], {459		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))460	})461}462463fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {464	parse_args!(context, "md5", args, 1, [465		0, str: ty!(string) => Val::Str;466	], {467		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))468	})469}470471fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {472	parse_args!(context, "trace", args, 2, [473		0, str: ty!(string) => Val::Str;474		1, rest: ty!(any);475	], {476		eprint!("TRACE:");477		if let Some(loc) = loc {478			with_state(|s|{479				let locs = s.map_source_locations(&loc.0, &[loc.1]);480				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);481			});482		}483		eprintln!(" {}", str);484		Ok(rest)485	})486}487488fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {489	parse_args!(context, "base64", args, 1, [490		0, input: ty!((string | (Array<number>)));491	], {492		Ok(Val::Str(match input {493			Val::Str(s) => {494				base64::encode(s.bytes().collect::<Vec<_>>()).into()495			},496			Val::Arr(a) => {497				base64::encode(a.iter().map(|v| {498					Ok(v?.unwrap_num()? as u8)499				}).collect::<Result<Vec<_>>>()?).into()500			},501			_ => unreachable!()502		}))503	})504}505506fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {507	parse_args!(context, "join", args, 2, [508		0, sep: ty!((string | array));509		1, arr: ty!(array) => Val::Arr;510	], {511		Ok(match sep {512			Val::Arr(joiner_items) => {513				let mut out = Vec::new();514515				let mut first = true;516				for item in arr.iter() {517					let item = item?.clone();518					if let Val::Arr(items) = item {519						if !first {520							out.reserve(joiner_items.len());521							// TODO: extend522							for item in joiner_items.iter() {523								out.push(item?);524							}525						}526						first = false;527						out.reserve(items.len());528						// TODO: extend529						for item in items.iter() {530							out.push(item?);531						}532					} else {533						throw!(RuntimeError("in std.join all items should be arrays".into()));534					}535				}536537				Val::Arr(out.into())538			},539			Val::Str(sep) => {540				let mut out = String::new();541542				let mut first = true;543				for item in arr.iter() {544					let item = item?.clone();545					if let Val::Str(item) = item {546						if !first {547							out += &sep;548						}549						first = false;550						out += &item;551					} else {552						throw!(RuntimeError("in std.join all items should be strings".into()));553					}554				}555556				Val::Str(out.into())557			},558			_ => unreachable!()559		})560	})561}562563fn builtin_escape_string_json(564	context: Context,565	_loc: Option<&ExprLocation>,566	args: &ArgsDesc,567) -> Result<Val> {568	parse_args!(context, "escapeStringJson", args, 1, [569		0, str_: ty!(string) => Val::Str;570	], {571		Ok(Val::Str(escape_string_json(&str_).into()))572	})573}574575fn builtin_manifest_json_ex(576	context: Context,577	_loc: Option<&ExprLocation>,578	args: &ArgsDesc,579) -> Result<Val> {580	parse_args!(context, "manifestJsonEx", args, 2, [581		0, value: ty!(any);582		1, indent: ty!(string) => Val::Str;583	], {584		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {585			padding: &indent,586			mtype: ManifestType::Std,587		})?.into()))588	})589}590591fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {592	parse_args!(context, "reverse", args, 1, [593		0, value: ty!(array) => Val::Arr;594	], {595		Ok(Val::Arr(value.reversed()))596	})597}598599fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {600	parse_args!(context, "id", args, 1, [601		0, v: ty!(any);602	], {603		Ok(v)604	})605}606607fn builtin_str_replace(608	context: Context,609	_loc: Option<&ExprLocation>,610	args: &ArgsDesc,611) -> Result<Val> {612	parse_args!(context, "strReplace", args, 3, [613		0, str: ty!(string) => Val::Str;614		1, from: ty!(string) => Val::Str;615		2, to: ty!(string) => Val::Str;616	], {617		let mut out = String::new();618		let mut last_idx = 0;619		while let Some(idx) = (&str[last_idx..]).find(&from as &str) {620			out.push_str(&str[last_idx..last_idx+idx]);621			out.push_str(&to);622			last_idx += idx + from.len();623		}624		if last_idx == 0 {625			return Ok(Val::Str(str))626		}627		out.push_str(&str[last_idx..]);628		Ok(Val::Str(out.into()))629	})630}631632pub fn call_builtin(633	context: Context,634	loc: Option<&ExprLocation>,635	name: &str,636	args: &ArgsDesc,637) -> Result<Val> {638	BUILTINS639		.with(|builtins| builtins.get(name).copied())640		.ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)641}
modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -188,14 +188,7 @@
     else
       { [k]: func(k, obj[k]) for k in std.objectFields(obj) },
 
-  flatMap(func, arr)::
-    if !std.isFunction(func) then
-      error ('std.flatMap first param must be function, got ' + std.type(func))
-    else if std.isArray(arr) then
-      std.flattenArrays(std.makeArray(std.length(arr), function(i) func(arr[i])))
-    else if std.isString(arr) then
-      std.join('', std.makeArray(std.length(arr), function(i) func(arr[i])))
-    else error ('std.flatMap second param must be array / string, got ' + std.type(arr)),
+  flatMap:: $intrinsic(flatMap),
 
   join:: $intrinsic(join),