git.delta.rocks / jrsonnet / refs/commits / b55d14a56b86

difftreelog

refactor move builtins to HashMap

Yaroslav Bolyukin2021-01-06parent: #a7f8b8f.patch.diff
in: master

1 file 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	evaluate, parse_args, primitive_equals, push, throw,5	typed::CheckType,6	with_state, ArrValue, Context, FuncVal, LazyVal, Val,7};8use format::{format_arr, format_obj};9use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};10use jrsonnet_types::ty;11use std::{path::PathBuf, rc::Rc};1213pub mod stdlib;14pub use stdlib::*;1516use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};1718pub mod format;19pub mod manifest;20pub mod sort;2122fn std_format(str: Rc<str>, vals: Val) -> Result<Val> {23	push(24		&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),25		|| format!("std.format of {}", str),26		|| {27			Ok(match vals {28				Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),29				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),30				o => Val::Str(format_arr(&str, &[o])?.into()),31			})32		},33	)34}3536#[allow(clippy::cognitive_complexity)]37pub fn call_builtin(38	context: Context,39	loc: &Option<ExprLocation>,40	name: &str,41	args: &ArgsDesc,42) -> Result<Val> {43	Ok(match name as &str {44		"length" => parse_args!(context, "length", args, 1, [45			0, x: ty!((str | obj | [any]));46		], {47			Ok(match x {48				Val::Str(n) => Val::Num(n.chars().count() as f64),49				Val::Arr(a) => Val::Num(a.len() as f64),50				Val::Obj(o) => Val::Num(51					o.fields_visibility()52						.into_iter()53						.filter(|(_k, v)| *v)54						.count() as f64,55				),56				_ => unreachable!(),57			})58		})?,59		"type" => parse_args!(context, "type", args, 1, [60			0, x: ty!(any);61		], {62			Ok(Val::Str(x.value_type().name().into()))63		})?,64		"makeArray" => parse_args!(context, "makeArray", args, 2, [65			0, sz: ty!(number((Some(0.0))..(None))) => Val::Num;66			1, func: ty!(fn.any) => Val::Func;67		], {68			let mut out = Vec::with_capacity(sz as usize);69			for i in 0..sz as usize {70				out.push(LazyVal::new_resolved(func.evaluate_values(71					Context::new(),72					&[Val::Num(i as f64)]73				)?))74			}75			Ok(Val::Arr(out.into()))76		})?,77		"codepoint" => parse_args!(context, "codepoint", args, 1, [78			0, str: ty!(char) => Val::Str;79		], {80			Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))81		})?,82		"objectFieldsEx" => parse_args!(context, "objectFieldsEx", args, 2, [83			0, obj: ty!(obj) => Val::Obj;84			1, inc_hidden: ty!(bool) => Val::Bool;85		], {86			let mut out = obj.fields_visibility()87				.into_iter()88				.filter(|(_k, v)| *v || inc_hidden)89				.map(|(k, _v)|k)90				.collect::<Vec<_>>();91			out.sort();92			Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))93		})?,94		"objectHasEx" => parse_args!(context, "objectHasEx", args, 3, [95			0, obj: ty!(obj) => Val::Obj;96			1, f: ty!(str) => Val::Str;97			2, inc_hidden: ty!(bool) => Val::Bool;98		], {99			Ok(Val::Bool(100				obj.fields_visibility()101					.into_iter()102					.filter(|(_k, v)| *v || inc_hidden)103					.any(|(k, _v)| *k == *f),104			))105		})?,106		// faster107		"slice" => parse_args!(context, "slice", args, 4, [108			0, indexable: ty!((str | [any]));109			1, index: ty!((num | null));110			2, end: ty!((num | null));111			3, step: ty!((num | null));112		], {113			let index = match index {114				Val::Num(v) => v as usize,115				Val::Null => 0,116				_ => unreachable!(),117			};118			let end = match end {119				Val::Num(v) => v as usize,120				Val::Null => match &indexable {121					Val::Str(s) => s.chars().count(),122					Val::Arr(v) => v.len(),123					_ => unreachable!()124				},125				_ => unreachable!()126			};127			let step = match step {128				Val::Num(v) => v as usize,129				Val::Null => 1,130				_ => unreachable!()131			};132			match &indexable {133				Val::Str(s) => {134					Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))135				}136				Val::Arr(arr) => {137					Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))138				}139				_ => unreachable!()140			}141		})?,142		"primitiveEquals" => parse_args!(context, "primitiveEquals", args, 2, [143			0, a: ty!(any);144			1, b: ty!(any);145		], {146			Ok(Val::Bool(primitive_equals(&a, &b)?))147		})?,148		// faster149		"equals" => parse_args!(context, "equals", args, 2, [150			0, a: ty!(any);151			1, b: ty!(any);152		], {153			Ok(Val::Bool(equals(&a, &b)?))154		})?,155		"modulo" => parse_args!(context, "modulo", args, 2, [156			0, a: ty!(num) => Val::Num;157			1, b: ty!(num) => Val::Num;158		], {159			Ok(Val::Num(a % b))160		})?,161		"mod" => parse_args!(context, "mod", args, 2, [162			0, a: ty!((num | str));163			1, b: ty!(any);164		], {165			match (a, b) {166				(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),167				(Val::Str(str), vals) => std_format(str, vals),168				(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))169			}170		})?,171		"floor" => parse_args!(context, "floor", args, 1, [172			0, x: ty!(num) => Val::Num;173		], {174			Ok(Val::Num(x.floor()))175		})?,176		"log" => parse_args!(context, "log", args, 1, [177			0, n: ty!(num) => Val::Num;178		], {179			Ok(Val::Num(n.ln()))180		})?,181		"trace" => parse_args!(context, "trace", args, 2, [182			0, str: ty!(str) => Val::Str;183			1, rest: ty!(any);184		], {185			eprint!("TRACE:");186			if let Some(loc) = loc {187				with_state(|s|{188					let locs = s.map_source_locations(&loc.0, &[loc.1]);189					eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);190				});191			}192			eprintln!(" {}", str);193			Ok(rest)194		})?,195		"pow" => parse_args!(context, "pow", args, 2, [196			0, x: ty!(num) => Val::Num;197			1, n: ty!(num) => Val::Num;198		], {199			Ok(Val::Num(x.powf(n)))200		})?,201		"extVar" => parse_args!(context, "extVar", args, 1, [202			0, x: ty!(str) => Val::Str;203		], {204			Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)205		})?,206		"native" => parse_args!(context, "native", args, 1, [207			0, x: ty!(str) => Val::Str;208		], {209			Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)210		})?,211		"filter" => parse_args!(context, "filter", args, 2, [212			0, func: ty!(fn.any) => Val::Func;213			1, arr: ty!([any]) => Val::Arr;214		], {215			let mut out = Vec::new();216			for item in arr.iter() {217				let item = item?;218				if func219							.evaluate_values(context.clone(), &[item.clone()])?220							.try_cast_bool("filter predicate")? {221								out.push(item);222							}223			}224			Ok(Val::Arr(out.into()))225		})?,226		"foldl" => parse_args!(context, "foldl", args, 3, [227			0, func: ty!(fn.any) => Val::Func;228			1, arr: ty!([any]) => Val::Arr;229			2, init: ty!(any);230		], {231			let mut acc = init;232			for i in arr.iter() {233				acc = func.evaluate_values(context.clone(), &[acc, i?])?;234			}235			Ok(acc)236		})?,237		"foldr" => parse_args!(context, "foldr", args, 3, [238			0, func: ty!(fn.any) => Val::Func;239			1, arr: ty!([any]) => Val::Arr;240			2, init: ty!(any);241		], {242			let mut acc = init;243			for i in arr.iter().rev() {244				acc = func.evaluate_values(context.clone(), &[acc, i?])?;245			}246			Ok(acc)247		})?,248		#[allow(non_snake_case)]249		"sortImpl" => parse_args!(context, "sort", args, 2, [250			0, arr: ty!([any]) => Val::Arr;251			1, keyF: ty!(fn.any) => Val::Func;252		], {253			if arr.len() <= 1 {254				return Ok(Val::Arr(arr))255			}256			Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))257		})?,258		// faster259		"format" => parse_args!(context, "format", args, 2, [260			0, str: ty!(str) => Val::Str;261			1, vals: ty!(any)262		], {263			std_format(str, vals)264		})?,265		"range" => parse_args!(context, "range", args, 2, [266			0, from: ty!(num) => Val::Num;267			1, to: ty!(num) => Val::Num;268		], {269			let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));270			for i in from as usize..=to as usize {271				out.push(Val::Num(i as f64));272			}273			Ok(Val::Arr(out.into()))274		})?,275		"char" => parse_args!(context, "char", args, 1, [276			0, n: ty!(num) => Val::Num;277		], {278			let mut out = String::new();279			out.push(std::char::from_u32(n as u32).ok_or_else(||280				InvalidUnicodeCodepointGot(n as u32)281			)?);282			Ok(Val::Str(out.into()))283		})?,284		"encodeUTF8" => parse_args!(context, "encodeUTF8", args, 1, [285			0, str: ty!(str) => Val::Str;286		], {287			Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))288		})?,289		"md5" => parse_args!(context, "md5", args, 1, [290			0, str: ty!(str) => Val::Str;291		], {292			Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))293		})?,294		"base64" => parse_args!(context, "base64", args, 1, [295			0, input: ty!((str | [num]));296		], {297			Ok(Val::Str(match input {298				Val::Str(s) => {299					base64::encode(s.bytes().collect::<Vec<_>>()).into()300				},301				Val::Arr(a) => {302					base64::encode(a.iter().map(|v| {303						Ok(v?.clone().unwrap_num()? as u8)304					}).collect::<Result<Vec<_>>>()?).into()305				},306				_ => unreachable!()307			}))308		})?,309		"join" => parse_args!(context, "join", args, 2, [310			0, sep: ty!((str | [any]));311			1, arr: ty!([any]) => Val::Arr;312		], {313			Ok(match sep {314				Val::Arr(joiner_items) => {315					let mut out = Vec::new();316317					let mut first = true;318					for item in arr.iter() {319						let item = item?.clone();320						if let Val::Arr(items) = item {321							if !first {322								out.reserve(joiner_items.len());323								// TODO: extend324								for item in joiner_items.iter() {325									out.push(item?);326								}327							}328							first = false;329							out.reserve(items.len());330							// TODO: extend331							for item in items.iter() {332								out.push(item?);333							}334						} else {335							throw!(RuntimeError("in std.join all items should be arrays".into()));336						}337					}338339					Val::Arr(out.into())340				},341				Val::Str(sep) => {342					let mut out = String::new();343344					let mut first = true;345					for item in arr.iter() {346						let item = item?.clone();347						if let Val::Str(item) = item {348							if !first {349								out += &sep;350							}351							first = false;352							out += &item;353						} else {354							throw!(RuntimeError("in std.join all items should be strings".into()));355						}356					}357358					Val::Str(out.into())359				},360				_ => unreachable!()361			})362		})?,363		// faster364		"escapeStringJson" => parse_args!(context, "escapeStringJson", args, 1, [365			0, str_: ty!(str) => Val::Str;366		], {367			Ok(Val::Str(escape_string_json(&str_).into()))368		})?,369		// faster370		"manifestJsonEx" => parse_args!(context, "manifestJsonEx", args, 2, [371			0, value: ty!(any);372			1, indent: ty!(str) => Val::Str;373		], {374			Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {375				padding: &indent,376				mtype: ManifestType::Std,377			})?.into()))378		})?,379		// faster380		"reverse" => parse_args!(context, "reverse", args, 1, [381			0, value: ty!([any]) => Val::Arr;382		], {383			Ok(Val::Arr(value.reversed()))384		})?,385		"id" => parse_args!(context, "id", args, 1, [386			0, v: ty!(any);387		], {388			Ok(v)389		})?,390		name => throw!(IntrinsicNotFound(name.into())),391	})392}
after · crates/jrsonnet-evaluator/src/builtin/mod.rs
1use crate::{2	equals,3	error::{Error::*, Result},4	evaluate, parse_args, primitive_equals, push, throw,5	typed::CheckType,6	with_state, ArrValue, Context, FuncVal, LazyVal, Val,7};8use format::{format_arr, format_obj};9use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};10use jrsonnet_types::ty;11use std::{collections::HashMap, path::PathBuf, rc::Rc};1213pub mod stdlib;14pub use stdlib::*;1516use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};1718pub mod format;19pub mod manifest;20pub mod sort;2122fn std_format(str: Rc<str>, vals: Val) -> Result<Val> {23	push(24		&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),25		|| format!("std.format of {}", str),26		|| {27			Ok(match vals {28				Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),29				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),30				o => Val::Str(format_arr(&str, &[o])?.into()),31			})32		},33	)34}3536type Builtin = fn(context: Context, loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val>;3738type BuiltinsType = HashMap<Box<str>, Builtin>;3940thread_local! {41	static BUILTINS: BuiltinsType = {42		[43			("length".into(), builtin_length as Builtin),44			("type".into(), builtin_type),45			("makeArray".into(), builtin_make_array),46			("codepoint".into(), builtin_codepoint),47			("objectFieldsEx".into(), builtin_object_fields_ex),48			("objectHasEx".into(), builtin_object_has_ex),49			("slice".into(), builtin_slice),50			("primitiveEquals".into(), builtin_primitive_equals),51			("equals".into(), builtin_equals),52			("modulo".into(), builtin_modulo),53			("mod".into(), builtin_mod),54			("floor".into(), builtin_floor),55			("log".into(), builtin_log),56			("pow".into(), builtin_pow),57			("extVar".into(), builtin_ext_var),58			("native".into(), builtin_native),59			("filter".into(), builtin_filter),60			("foldl".into(), builtin_foldl),61			("foldr".into(), builtin_foldr),62			("sortImpl".into(), builtin_sort_impl),63			("format".into(), builtin_format),64			("range".into(), builtin_range),65			("char".into(), builtin_char),66			("encodeUTF8".into(), builtin_encode_utf8),67			("md5".into(), builtin_md5),68			("base64".into(), builtin_base64),69			("trace".into(), builtin_trace),70			("join".into(), builtin_join),71			("escapeStringJson".into(), builtin_escape_string_json),72			("manifestJsonEx".into(), builtin_manifest_json_ex),73			("reverse".into(), builtin_reverse),74			("id".into(), builtin_id),75		].iter().cloned().collect()76	};77}7879fn builtin_length(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {80	parse_args!(context, "length", args, 1, [81		0, x: ty!((str | obj | [any]));82	], {83		Ok(match x {84			Val::Str(n) => Val::Num(n.chars().count() as f64),85			Val::Arr(a) => Val::Num(a.len() as f64),86			Val::Obj(o) => Val::Num(87				o.fields_visibility()88					.into_iter()89					.filter(|(_k, v)| *v)90					.count() as f64,91			),92			_ => unreachable!(),93		})94	})95}9697fn builtin_type(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {98	parse_args!(context, "type", args, 1, [99		0, x: ty!(any);100	], {101		Ok(Val::Str(x.value_type().name().into()))102	})103}104105fn builtin_make_array(106	context: Context,107	_loc: &Option<ExprLocation>,108	args: &ArgsDesc,109) -> Result<Val> {110	parse_args!(context, "makeArray", args, 2, [111		0, sz: ty!(number((Some(0.0))..(None))) => Val::Num;112		1, func: ty!(fn.any) => Val::Func;113	], {114		let mut out = Vec::with_capacity(sz as usize);115		for i in 0..sz as usize {116			out.push(LazyVal::new_resolved(func.evaluate_values(117				Context::new(),118				&[Val::Num(i as f64)]119			)?))120		}121		Ok(Val::Arr(out.into()))122	})123}124125fn builtin_codepoint(126	context: Context,127	_loc: &Option<ExprLocation>,128	args: &ArgsDesc,129) -> Result<Val> {130	parse_args!(context, "codepoint", args, 1, [131		0, str: ty!(char) => Val::Str;132	], {133		Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))134	})135}136137fn builtin_object_fields_ex(138	context: Context,139	_loc: &Option<ExprLocation>,140	args: &ArgsDesc,141) -> Result<Val> {142	parse_args!(context, "objectFieldsEx", args, 2, [143		0, obj: ty!(obj) => Val::Obj;144		1, inc_hidden: ty!(bool) => Val::Bool;145	], {146		let mut out = obj.fields_visibility()147			.into_iter()148			.filter(|(_k, v)| *v || inc_hidden)149			.map(|(k, _v)|k)150			.collect::<Vec<_>>();151		out.sort();152		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))153	})154}155156fn builtin_object_has_ex(157	context: Context,158	_loc: &Option<ExprLocation>,159	args: &ArgsDesc,160) -> Result<Val> {161	parse_args!(context, "objectHasEx", args, 3, [162		0, obj: ty!(obj) => Val::Obj;163		1, f: ty!(str) => Val::Str;164		2, inc_hidden: ty!(bool) => Val::Bool;165	], {166		Ok(Val::Bool(167			obj.fields_visibility()168				.into_iter()169				.filter(|(_k, v)| *v || inc_hidden)170				.any(|(k, _v)| *k == *f),171		))172	})173}174175// faster176fn builtin_slice(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {177	parse_args!(context, "slice", args, 4, [178		0, indexable: ty!((str | [any]));179		1, index: ty!((num | null));180		2, end: ty!((num | null));181		3, step: ty!((num | null));182	], {183		let index = match index {184			Val::Num(v) => v as usize,185			Val::Null => 0,186			_ => unreachable!(),187		};188		let end = match end {189			Val::Num(v) => v as usize,190			Val::Null => match &indexable {191				Val::Str(s) => s.chars().count(),192				Val::Arr(v) => v.len(),193				_ => unreachable!()194			},195			_ => unreachable!()196		};197		let step = match step {198			Val::Num(v) => v as usize,199			Val::Null => 1,200			_ => unreachable!()201		};202		match &indexable {203			Val::Str(s) => {204				Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))205			}206			Val::Arr(arr) => {207				Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))208			}209			_ => unreachable!()210		}211	})212}213214// faster215fn builtin_primitive_equals(216	context: Context,217	_loc: &Option<ExprLocation>,218	args: &ArgsDesc,219) -> Result<Val> {220	parse_args!(context, "primitiveEquals", args, 2, [221		0, a: ty!(any);222		1, b: ty!(any);223	], {224		Ok(Val::Bool(primitive_equals(&a, &b)?))225	})226}227228// faster229fn builtin_equals(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {230	parse_args!(context, "equals", args, 2, [231		0, a: ty!(any);232		1, b: ty!(any);233	], {234		Ok(Val::Bool(equals(&a, &b)?))235	})236}237238fn builtin_modulo(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {239	parse_args!(context, "modulo", args, 2, [240		0, a: ty!(num) => Val::Num;241		1, b: ty!(num) => Val::Num;242	], {243		Ok(Val::Num(a % b))244	})245}246247fn builtin_mod(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {248	parse_args!(context, "mod", args, 2, [249		0, a: ty!((num | str));250		1, b: ty!(any);251	], {252		match (a, b) {253			(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),254			(Val::Str(str), vals) => std_format(str, vals),255			(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))256		}257	})258}259260fn builtin_floor(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {261	parse_args!(context, "floor", args, 1, [262		0, x: ty!(num) => Val::Num;263	], {264		Ok(Val::Num(x.floor()))265	})266}267268fn builtin_log(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {269	parse_args!(context, "log", args, 1, [270		0, n: ty!(num) => Val::Num;271	], {272		Ok(Val::Num(n.ln()))273	})274}275276fn builtin_pow(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {277	parse_args!(context, "pow", args, 2, [278		0, x: ty!(num) => Val::Num;279		1, n: ty!(num) => Val::Num;280	], {281		Ok(Val::Num(x.powf(n)))282	})283}284285fn builtin_ext_var(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {286	parse_args!(context, "extVar", args, 1, [287		0, x: ty!(str) => Val::Str;288	], {289		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)290	})291}292293fn builtin_native(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {294	parse_args!(context, "native", args, 1, [295		0, x: ty!(str) => Val::Str;296	], {297		Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)298	})299}300301fn builtin_filter(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {302	parse_args!(context, "filter", args, 2, [303		0, func: ty!(fn.any) => Val::Func;304		1, arr: ty!([any]) => Val::Arr;305	], {306		let mut out = Vec::new();307		for item in arr.iter() {308			let item = item?;309			if func310						.evaluate_values(context.clone(), &[item.clone()])?311						.try_cast_bool("filter predicate")? {312							out.push(item);313						}314		}315		Ok(Val::Arr(out.into()))316	})317}318319fn builtin_foldl(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {320	parse_args!(context, "foldl", args, 3, [321		0, func: ty!(fn.any) => Val::Func;322		1, arr: ty!([any]) => Val::Arr;323		2, init: ty!(any);324	], {325		let mut acc = init;326		for i in arr.iter() {327			acc = func.evaluate_values(context.clone(), &[acc, i?])?;328		}329		Ok(acc)330	})331}332333fn builtin_foldr(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {334	parse_args!(context, "foldr", args, 3, [335		0, func: ty!(fn.any) => Val::Func;336		1, arr: ty!([any]) => Val::Arr;337		2, init: ty!(any);338	], {339		let mut acc = init;340		for i in arr.iter().rev() {341			acc = func.evaluate_values(context.clone(), &[acc, i?])?;342		}343		Ok(acc)344	})345}346347#[allow(non_snake_case)]348fn builtin_sort_impl(349	context: Context,350	_loc: &Option<ExprLocation>,351	args: &ArgsDesc,352) -> Result<Val> {353	parse_args!(context, "sort", args, 2, [354		0, arr: ty!([any]) => Val::Arr;355		1, keyF: ty!(fn.any) => Val::Func;356	], {357		if arr.len() <= 1 {358			return Ok(Val::Arr(arr))359		}360		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))361	})362}363364// faster365fn builtin_format(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {366	parse_args!(context, "format", args, 2, [367		0, str: ty!(str) => Val::Str;368		1, vals: ty!(any)369	], {370		std_format(str, vals)371	})372}373374fn builtin_range(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {375	parse_args!(context, "range", args, 2, [376		0, from: ty!(num) => Val::Num;377		1, to: ty!(num) => Val::Num;378	], {379		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));380		for i in from as usize..=to as usize {381			out.push(Val::Num(i as f64));382		}383		Ok(Val::Arr(out.into()))384	})385}386387fn builtin_char(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {388	parse_args!(context, "char", args, 1, [389		0, n: ty!(num) => Val::Num;390	], {391		let mut out = String::new();392		out.push(std::char::from_u32(n as u32).ok_or_else(||393			InvalidUnicodeCodepointGot(n as u32)394		)?);395		Ok(Val::Str(out.into()))396	})397}398399fn builtin_encode_utf8(400	context: Context,401	_loc: &Option<ExprLocation>,402	args: &ArgsDesc,403) -> Result<Val> {404	parse_args!(context, "encodeUTF8", args, 1, [405		0, str: ty!(str) => Val::Str;406	], {407		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))408	})409}410411fn builtin_md5(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {412	parse_args!(context, "md5", args, 1, [413		0, str: ty!(str) => Val::Str;414	], {415		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))416	})417}418419fn builtin_trace(context: Context, loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {420	parse_args!(context, "trace", args, 2, [421		0, str: ty!(str) => Val::Str;422		1, rest: ty!(any);423	], {424		eprint!("TRACE:");425		if let Some(loc) = loc {426			with_state(|s|{427				let locs = s.map_source_locations(&loc.0, &[loc.1]);428				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);429			});430		}431		eprintln!(" {}", str);432		Ok(rest)433	})434}435436fn builtin_base64(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {437	parse_args!(context, "base64", args, 1, [438		0, input: ty!((str | [num]));439	], {440		Ok(Val::Str(match input {441			Val::Str(s) => {442				base64::encode(s.bytes().collect::<Vec<_>>()).into()443			},444			Val::Arr(a) => {445				base64::encode(a.iter().map(|v| {446					Ok(v?.clone().unwrap_num()? as u8)447				}).collect::<Result<Vec<_>>>()?).into()448			},449			_ => unreachable!()450		}))451	})452}453454fn builtin_join(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {455	parse_args!(context, "join", args, 2, [456		0, sep: ty!((str | [any]));457		1, arr: ty!([any]) => Val::Arr;458	], {459		Ok(match sep {460			Val::Arr(joiner_items) => {461				let mut out = Vec::new();462463				let mut first = true;464				for item in arr.iter() {465					let item = item?.clone();466					if let Val::Arr(items) = item {467						if !first {468							out.reserve(joiner_items.len());469							// TODO: extend470							for item in joiner_items.iter() {471								out.push(item?);472							}473						}474						first = false;475						out.reserve(items.len());476						// TODO: extend477						for item in items.iter() {478							out.push(item?);479						}480					} else {481						throw!(RuntimeError("in std.join all items should be arrays".into()));482					}483				}484485				Val::Arr(out.into())486			},487			Val::Str(sep) => {488				let mut out = String::new();489490				let mut first = true;491				for item in arr.iter() {492					let item = item?.clone();493					if let Val::Str(item) = item {494						if !first {495							out += &sep;496						}497						first = false;498						out += &item;499					} else {500						throw!(RuntimeError("in std.join all items should be strings".into()));501					}502				}503504				Val::Str(out.into())505			},506			_ => unreachable!()507		})508	})509}510511// faster512fn builtin_escape_string_json(513	context: Context,514	_loc: &Option<ExprLocation>,515	args: &ArgsDesc,516) -> Result<Val> {517	parse_args!(context, "escapeStringJson", args, 1, [518		0, str_: ty!(str) => Val::Str;519	], {520		Ok(Val::Str(escape_string_json(&str_).into()))521	})522}523524// faster525fn builtin_manifest_json_ex(526	context: Context,527	_loc: &Option<ExprLocation>,528	args: &ArgsDesc,529) -> Result<Val> {530	parse_args!(context, "manifestJsonEx", args, 2, [531		0, value: ty!(any);532		1, indent: ty!(str) => Val::Str;533	], {534		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {535			padding: &indent,536			mtype: ManifestType::Std,537		})?.into()))538	})539}540541// faster542fn builtin_reverse(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {543	parse_args!(context, "reverse", args, 1, [544		0, value: ty!([any]) => Val::Arr;545	], {546		Ok(Val::Arr(value.reversed()))547	})548}549550fn builtin_id(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {551	parse_args!(context, "id", args, 1, [552		0, v: ty!(any);553	], {554		Ok(v)555	})556}557558#[allow(clippy::cognitive_complexity)]559pub fn call_builtin(560	context: Context,561	loc: &Option<ExprLocation>,562	name: &str,563	args: &ArgsDesc,564) -> Result<Val> {565	if let Some(f) = BUILTINS.with(|builtins| builtins.get(name).map(|f| *f)) {566		return Ok(f(context, loc, args)?);567	}568	throw!(IntrinsicNotFound(name.into()))569}