git.delta.rocks / jrsonnet / refs/commits / 02b7991d6359

difftreelog

perf make std.substr builtin

Yaroslav Bolyukin2021-07-12parent: #d4bcb68.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			("ceil".into(), builtin_ceil),90			("log".into(), builtin_log),91			("pow".into(), builtin_pow),92			("sqrt".into(), builtin_sqrt),93			("sin".into(), builtin_sin),94			("cos".into(), builtin_cos),95			("tan".into(), builtin_tan),96			("asin".into(), builtin_asin),97			("acos".into(), builtin_acos),98			("atan".into(), builtin_atan),99			("exp".into(), builtin_exp),100			("mantissa".into(), builtin_mantissa),101			("exponent".into(), builtin_exponent),102			("extVar".into(), builtin_ext_var),103			("native".into(), builtin_native),104			("filter".into(), builtin_filter),105			("map".into(), builtin_map),106			("flatMap".into(), builtin_flatmap),107			("foldl".into(), builtin_foldl),108			("foldr".into(), builtin_foldr),109			("sortImpl".into(), builtin_sort_impl),110			("format".into(), builtin_format),111			("range".into(), builtin_range),112			("char".into(), builtin_char),113			("encodeUTF8".into(), builtin_encode_utf8),114			("md5".into(), builtin_md5),115			("base64".into(), builtin_base64),116			("trace".into(), builtin_trace),117			("join".into(), builtin_join),118			("escapeStringJson".into(), builtin_escape_string_json),119			("manifestJsonEx".into(), builtin_manifest_json_ex),120			("reverse".into(), builtin_reverse),121			("id".into(), builtin_id),122			("strReplace".into(), builtin_str_replace),123			("parseJson".into(), builtin_parse_json),124		].iter().cloned().collect()125	};126}127128fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {129	parse_args!(context, "length", args, 1, [130		0, x: ty!((string | object | array));131	], {132		Ok(match x {133			Val::Str(n) => Val::Num(n.chars().count() as f64),134			Val::Arr(a) => Val::Num(a.len() as f64),135			Val::Obj(o) => Val::Num(136				o.fields_visibility()137					.into_iter()138					.filter(|(_k, v)| *v)139					.count() as f64,140			),141			_ => unreachable!(),142		})143	})144}145146fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {147	parse_args!(context, "type", args, 1, [148		0, x: ty!(any);149	], {150		Ok(Val::Str(x.value_type().name().into()))151	})152}153154fn builtin_make_array(155	context: Context,156	_loc: Option<&ExprLocation>,157	args: &ArgsDesc,158) -> Result<Val> {159	parse_args!(context, "makeArray", args, 2, [160		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;161		1, func: ty!(function) => Val::Func;162	], {163		let mut out = Vec::with_capacity(sz as usize);164		for i in 0..sz as usize {165			out.push(LazyVal::new_resolved(func.evaluate_values(166				context.clone(),167				&[Val::Num(i as f64)]168			)?))169		}170		Ok(Val::Arr(out.into()))171	})172}173174fn builtin_codepoint(175	context: Context,176	_loc: Option<&ExprLocation>,177	args: &ArgsDesc,178) -> Result<Val> {179	parse_args!(context, "codepoint", args, 1, [180		0, str: ty!(char) => Val::Str;181	], {182		Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))183	})184}185186fn builtin_object_fields_ex(187	context: Context,188	_loc: Option<&ExprLocation>,189	args: &ArgsDesc,190) -> Result<Val> {191	parse_args!(context, "objectFieldsEx", args, 2, [192		0, obj: ty!(object) => Val::Obj;193		1, inc_hidden: ty!(boolean) => Val::Bool;194	], {195		let out = obj.fields_ex(inc_hidden);196		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))197	})198}199200fn builtin_object_has_ex(201	context: Context,202	_loc: Option<&ExprLocation>,203	args: &ArgsDesc,204) -> Result<Val> {205	parse_args!(context, "objectHasEx", args, 3, [206		0, obj: ty!(object) => Val::Obj;207		1, f: ty!(string) => Val::Str;208		2, inc_hidden: ty!(boolean) => Val::Bool;209	], {210		Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))211	})212}213214fn builtin_parse_json(215	context: Context,216	_loc: Option<&ExprLocation>,217	args: &ArgsDesc,218) -> Result<Val> {219	parse_args!(context, "parseJson", args, 1, [220		0, s: ty!(string) => Val::Str;221	], {222		let state = EvaluationState::default();223		let path = PathBuf::from("std.parseJson").into();224		state.evaluate_snippet_raw(path ,s)225	})226}227228fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {229	parse_args!(context, "slice", args, 4, [230		0, indexable: ty!((string | array));231		1, index: ty!((number | null));232		2, end: ty!((number | null));233		3, step: ty!((number | null));234	], {235		std_slice(236			indexable.into_indexable()?,237			index.try_cast_nullable_num("index")?.map(|v| v as usize),238			end.try_cast_nullable_num("end")?.map(|v| v as usize),239			step.try_cast_nullable_num("step")?.map(|v| v as usize),240		)241	})242}243244fn builtin_primitive_equals(245	context: Context,246	_loc: Option<&ExprLocation>,247	args: &ArgsDesc,248) -> Result<Val> {249	parse_args!(context, "primitiveEquals", args, 2, [250		0, a: ty!(any);251		1, b: ty!(any);252	], {253		Ok(Val::Bool(primitive_equals(&a, &b)?))254	})255}256257fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {258	parse_args!(context, "equals", args, 2, [259		0, a: ty!(any);260		1, b: ty!(any);261	], {262		Ok(Val::Bool(equals(&a, &b)?))263	})264}265266fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {267	parse_args!(context, "modulo", args, 2, [268		0, a: ty!(number) => Val::Num;269		1, b: ty!(number) => Val::Num;270	], {271		Ok(Val::Num(a % b))272	})273}274275fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {276	parse_args!(context, "mod", args, 2, [277		0, a: ty!((number | string));278		1, b: ty!(any);279	], {280		evaluate_mod_op(&a, &b)281	})282}283284fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {285	parse_args!(context, "floor", args, 1, [286		0, x: ty!(number) => Val::Num;287	], {288		Ok(Val::Num(x.floor()))289	})290}291292fn builtin_ceil(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {293	parse_args!(context, "ceil", args, 1, [294		0, x: ty!(number) => Val::Num;295	], {296		Ok(Val::Num(x.ceil()))297	})298}299300fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {301	parse_args!(context, "log", args, 1, [302		0, n: ty!(number) => Val::Num;303	], {304		Ok(Val::Num(n.ln()))305	})306}307308fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {309	parse_args!(context, "pow", args, 2, [310		0, x: ty!(number) => Val::Num;311		1, n: ty!(number) => Val::Num;312	], {313		Ok(Val::Num(x.powf(n)))314	})315}316317fn builtin_sqrt(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {318	parse_args!(context, "sqrt", args, 1, [319		0, x: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;320	], {321		Ok(Val::Num(x.sqrt()))322	})323}324325fn builtin_sin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {326	parse_args!(context, "sin", args, 1, [327		0, x: ty!(number) => Val::Num;328	], {329		Ok(Val::Num(x.sin()))330	})331}332333fn builtin_cos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {334	parse_args!(context, "cos", args, 1, [335		0, x: ty!(number) => Val::Num;336	], {337		Ok(Val::Num(x.cos()))338	})339}340341fn builtin_tan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {342	parse_args!(context, "tan", args, 1, [343		0, x: ty!(number) => Val::Num;344	], {345		Ok(Val::Num(x.tan()))346	})347}348349fn builtin_asin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {350	parse_args!(context, "asin", args, 1, [351		0, x: ty!(number) => Val::Num;352	], {353		Ok(Val::Num(x.asin()))354	})355}356357fn builtin_acos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {358	parse_args!(context, "acos", args, 1, [359		0, x: ty!(number) => Val::Num;360	], {361		Ok(Val::Num(x.acos()))362	})363}364365fn builtin_atan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {366	parse_args!(context, "atan", args, 1, [367		0, x: ty!(number) => Val::Num;368	], {369		Ok(Val::Num(x.atan()))370	})371}372373fn builtin_exp(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {374	parse_args!(context, "exp", args, 1, [375		0, x: ty!(number) => Val::Num;376	], {377		Ok(Val::Num(x.exp()))378	})379}380381fn frexp(s: f64) -> (f64, i16) {382	if 0.0 == s {383		return (s, 0);384	} else {385		let lg = s.abs().log2();386		let x = (lg - lg.floor() - 1.0).exp2();387		let exp = lg.floor() + 1.0;388		(s.signum() * x, exp as i16)389	}390}391392fn builtin_mantissa(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {393	parse_args!(context, "mantissa", args, 1, [394		0, x: ty!(number) => Val::Num;395	], {396		Ok(Val::Num(frexp(x).0))397	})398}399400fn builtin_exponent(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {401	parse_args!(context, "exponent", args, 1, [402		0, x: ty!(number) => Val::Num;403	], {404		Ok(Val::Num(frexp(x).1.into()))405	})406}407408fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {409	parse_args!(context, "extVar", args, 1, [410		0, x: ty!(string) => Val::Str;411	], {412		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)413	})414}415416fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {417	parse_args!(context, "native", args, 1, [418		0, x: ty!(string) => Val::Str;419	], {420		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))?)421	})422}423424fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {425	parse_args!(context, "filter", args, 2, [426		0, func: ty!(function) => Val::Func;427		1, arr: ty!(array) => Val::Arr;428	], {429		Ok(Val::Arr(arr.filter(|val| func430			.evaluate_values(context.clone(), &[val.clone()])?431			.try_cast_bool("filter predicate"))?))432	})433}434435fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {436	parse_args!(context, "map", args, 2, [437		0, func: ty!(function) => Val::Func;438		1, arr: ty!(array) => Val::Arr;439	], {440		Ok(Val::Arr(arr.map(|val| func441			.evaluate_values(context.clone(), &[val]))?))442	})443}444445fn builtin_flatmap(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {446	parse_args!(context, "flatMap", args, 2, [447		0, func: ty!(function) => Val::Func;448		1, arr: ty!((array | string));449	], {450		match arr {451			Val::Str(s) => {452				let mut out = String::new();453				for c in s.chars() {454					match func.evaluate_values(context.clone(), &[Val::Str(c.to_string().into())])? {455						Val::Str(o) => out.push_str(&o),456						_ => throw!(RuntimeError("in std.join all items should be strings".into())),457					};458				}459				Ok(Val::Str(out.into()))460			},461			Val::Arr(a) => {462				let mut out = Vec::new();463				for el in a.iter() {464					let el = el?;465					match func.evaluate_values(context.clone(), &[el])? {466						Val::Arr(o) => for oe in o.iter() {467							out.push(oe?)468						},469						_ => throw!(RuntimeError("in std.join all items should be arrays".into())),470					};471				}472				Ok(Val::Arr(out.into()))473			},474			_ => unreachable!(),475		}476	})477}478479fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {480	parse_args!(context, "foldl", args, 3, [481		0, func: ty!(function) => Val::Func;482		1, arr: ty!(array) => Val::Arr;483		2, init: ty!(any);484	], {485		let mut acc = init;486		for i in arr.iter() {487			acc = func.evaluate_values(context.clone(), &[acc, i?])?;488		}489		Ok(acc)490	})491}492493fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {494	parse_args!(context, "foldr", args, 3, [495		0, func: ty!(function) => Val::Func;496		1, arr: ty!(array) => Val::Arr;497		2, init: ty!(any);498	], {499		let mut acc = init;500		for i in arr.iter().rev() {501			acc = func.evaluate_values(context.clone(), &[acc, i?])?;502		}503		Ok(acc)504	})505}506507#[allow(non_snake_case)]508fn builtin_sort_impl(509	context: Context,510	_loc: Option<&ExprLocation>,511	args: &ArgsDesc,512) -> Result<Val> {513	parse_args!(context, "sort", args, 2, [514		0, arr: ty!(array) => Val::Arr;515		1, keyF: ty!(function) => Val::Func;516	], {517		if arr.len() <= 1 {518			return Ok(Val::Arr(arr))519		}520		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))521	})522}523524fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {525	parse_args!(context, "format", args, 2, [526		0, str: ty!(string) => Val::Str;527		1, vals: ty!(any)528	], {529		std_format(str, vals)530	})531}532533fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {534	parse_args!(context, "range", args, 2, [535		0, from: ty!(number) => Val::Num;536		1, to: ty!(number) => Val::Num;537	], {538		if to < from {539			return Ok(Val::Arr(ArrValue::new_eager()))540		}541		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));542		for i in from as usize..=to as usize {543			out.push(Val::Num(i as f64));544		}545		Ok(Val::Arr(out.into()))546	})547}548549fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {550	parse_args!(context, "char", args, 1, [551		0, n: ty!(number) => Val::Num;552	], {553		let mut out = String::new();554		out.push(std::char::from_u32(n as u32).ok_or_else(||555			InvalidUnicodeCodepointGot(n as u32)556		)?);557		Ok(Val::Str(out.into()))558	})559}560561fn builtin_encode_utf8(562	context: Context,563	_loc: Option<&ExprLocation>,564	args: &ArgsDesc,565) -> Result<Val> {566	parse_args!(context, "encodeUTF8", args, 1, [567		0, str: ty!(string) => Val::Str;568	], {569		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))570	})571}572573fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {574	parse_args!(context, "md5", args, 1, [575		0, str: ty!(string) => Val::Str;576	], {577		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))578	})579}580581fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {582	parse_args!(context, "trace", args, 2, [583		0, str: ty!(string) => Val::Str;584		1, rest: ty!(any);585	], {586		eprint!("TRACE:");587		if let Some(loc) = loc {588			with_state(|s|{589				let locs = s.map_source_locations(&loc.0, &[loc.1]);590				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);591			});592		}593		eprintln!(" {}", str);594		Ok(rest)595	})596}597598fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {599	parse_args!(context, "base64", args, 1, [600		0, input: ty!((string | (Array<number>)));601	], {602		Ok(Val::Str(match input {603			Val::Str(s) => {604				base64::encode(s.bytes().collect::<Vec<_>>()).into()605			},606			Val::Arr(a) => {607				base64::encode(a.iter().map(|v| {608					Ok(v?.unwrap_num()? as u8)609				}).collect::<Result<Vec<_>>>()?).into()610			},611			_ => unreachable!()612		}))613	})614}615616fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {617	parse_args!(context, "join", args, 2, [618		0, sep: ty!((string | array));619		1, arr: ty!(array) => Val::Arr;620	], {621		Ok(match sep {622			Val::Arr(joiner_items) => {623				let mut out = Vec::new();624625				let mut first = true;626				for item in arr.iter() {627					let item = item?.clone();628					if let Val::Arr(items) = item {629						if !first {630							out.reserve(joiner_items.len());631							// TODO: extend632							for item in joiner_items.iter() {633								out.push(item?);634							}635						}636						first = false;637						out.reserve(items.len());638						// TODO: extend639						for item in items.iter() {640							out.push(item?);641						}642					} else {643						throw!(RuntimeError("in std.join all items should be arrays".into()));644					}645				}646647				Val::Arr(out.into())648			},649			Val::Str(sep) => {650				let mut out = String::new();651652				let mut first = true;653				for item in arr.iter() {654					let item = item?.clone();655					if let Val::Str(item) = item {656						if !first {657							out += &sep;658						}659						first = false;660						out += &item;661					} else {662						throw!(RuntimeError("in std.join all items should be strings".into()));663					}664				}665666				Val::Str(out.into())667			},668			_ => unreachable!()669		})670	})671}672673fn builtin_escape_string_json(674	context: Context,675	_loc: Option<&ExprLocation>,676	args: &ArgsDesc,677) -> Result<Val> {678	parse_args!(context, "escapeStringJson", args, 1, [679		0, str_: ty!(string) => Val::Str;680	], {681		Ok(Val::Str(escape_string_json(&str_).into()))682	})683}684685fn builtin_manifest_json_ex(686	context: Context,687	_loc: Option<&ExprLocation>,688	args: &ArgsDesc,689) -> Result<Val> {690	parse_args!(context, "manifestJsonEx", args, 2, [691		0, value: ty!(any);692		1, indent: ty!(string) => Val::Str;693	], {694		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {695			padding: &indent,696			mtype: ManifestType::Std,697		})?.into()))698	})699}700701fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {702	parse_args!(context, "reverse", args, 1, [703		0, value: ty!(array) => Val::Arr;704	], {705		Ok(Val::Arr(value.reversed()))706	})707}708709fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {710	parse_args!(context, "id", args, 1, [711		0, v: ty!(any);712	], {713		Ok(v)714	})715}716717fn builtin_str_replace(718	context: Context,719	_loc: Option<&ExprLocation>,720	args: &ArgsDesc,721) -> Result<Val> {722	parse_args!(context, "strReplace", args, 3, [723		0, str: ty!(string) => Val::Str;724		1, from: ty!(string) => Val::Str;725		2, to: ty!(string) => Val::Str;726	], {727		let mut out = String::new();728		let mut last_idx = 0;729		while let Some(idx) = (&str[last_idx..]).find(&from as &str) {730			out.push_str(&str[last_idx..last_idx+idx]);731			out.push_str(&to);732			last_idx += idx + from.len();733		}734		if last_idx == 0 {735			return Ok(Val::Str(str))736		}737		out.push_str(&str[last_idx..]);738		Ok(Val::Str(out.into()))739	})740}741742pub fn call_builtin(743	context: Context,744	loc: Option<&ExprLocation>,745	name: &str,746	args: &ArgsDesc,747) -> Result<Val> {748	BUILTINS749		.with(|builtins| builtins.get(name).copied())750		.ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)751}
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			("substr".into(), builtin_substr),85			("primitiveEquals".into(), builtin_primitive_equals),86			("equals".into(), builtin_equals),87			("modulo".into(), builtin_modulo),88			("mod".into(), builtin_mod),89			("floor".into(), builtin_floor),90			("ceil".into(), builtin_ceil),91			("log".into(), builtin_log),92			("pow".into(), builtin_pow),93			("sqrt".into(), builtin_sqrt),94			("sin".into(), builtin_sin),95			("cos".into(), builtin_cos),96			("tan".into(), builtin_tan),97			("asin".into(), builtin_asin),98			("acos".into(), builtin_acos),99			("atan".into(), builtin_atan),100			("exp".into(), builtin_exp),101			("mantissa".into(), builtin_mantissa),102			("exponent".into(), builtin_exponent),103			("extVar".into(), builtin_ext_var),104			("native".into(), builtin_native),105			("filter".into(), builtin_filter),106			("map".into(), builtin_map),107			("flatMap".into(), builtin_flatmap),108			("foldl".into(), builtin_foldl),109			("foldr".into(), builtin_foldr),110			("sortImpl".into(), builtin_sort_impl),111			("format".into(), builtin_format),112			("range".into(), builtin_range),113			("char".into(), builtin_char),114			("encodeUTF8".into(), builtin_encode_utf8),115			("md5".into(), builtin_md5),116			("base64".into(), builtin_base64),117			("trace".into(), builtin_trace),118			("join".into(), builtin_join),119			("escapeStringJson".into(), builtin_escape_string_json),120			("manifestJsonEx".into(), builtin_manifest_json_ex),121			("reverse".into(), builtin_reverse),122			("id".into(), builtin_id),123			("strReplace".into(), builtin_str_replace),124			("parseJson".into(), builtin_parse_json),125		].iter().cloned().collect()126	};127}128129fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {130	parse_args!(context, "length", args, 1, [131		0, x: ty!((string | object | array));132	], {133		Ok(match x {134			Val::Str(n) => Val::Num(n.chars().count() as f64),135			Val::Arr(a) => Val::Num(a.len() as f64),136			Val::Obj(o) => Val::Num(137				o.fields_visibility()138					.into_iter()139					.filter(|(_k, v)| *v)140					.count() as f64,141			),142			_ => unreachable!(),143		})144	})145}146147fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {148	parse_args!(context, "type", args, 1, [149		0, x: ty!(any);150	], {151		Ok(Val::Str(x.value_type().name().into()))152	})153}154155fn builtin_make_array(156	context: Context,157	_loc: Option<&ExprLocation>,158	args: &ArgsDesc,159) -> Result<Val> {160	parse_args!(context, "makeArray", args, 2, [161		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;162		1, func: ty!(function) => Val::Func;163	], {164		let mut out = Vec::with_capacity(sz as usize);165		for i in 0..sz as usize {166			out.push(LazyVal::new_resolved(func.evaluate_values(167				context.clone(),168				&[Val::Num(i as f64)]169			)?))170		}171		Ok(Val::Arr(out.into()))172	})173}174175fn builtin_codepoint(176	context: Context,177	_loc: Option<&ExprLocation>,178	args: &ArgsDesc,179) -> Result<Val> {180	parse_args!(context, "codepoint", args, 1, [181		0, str: ty!(char) => Val::Str;182	], {183		Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))184	})185}186187fn builtin_object_fields_ex(188	context: Context,189	_loc: Option<&ExprLocation>,190	args: &ArgsDesc,191) -> Result<Val> {192	parse_args!(context, "objectFieldsEx", args, 2, [193		0, obj: ty!(object) => Val::Obj;194		1, inc_hidden: ty!(boolean) => Val::Bool;195	], {196		let out = obj.fields_ex(inc_hidden);197		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))198	})199}200201fn builtin_object_has_ex(202	context: Context,203	_loc: Option<&ExprLocation>,204	args: &ArgsDesc,205) -> Result<Val> {206	parse_args!(context, "objectHasEx", args, 3, [207		0, obj: ty!(object) => Val::Obj;208		1, f: ty!(string) => Val::Str;209		2, inc_hidden: ty!(boolean) => Val::Bool;210	], {211		Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))212	})213}214215fn builtin_parse_json(216	context: Context,217	_loc: Option<&ExprLocation>,218	args: &ArgsDesc,219) -> Result<Val> {220	parse_args!(context, "parseJson", args, 1, [221		0, s: ty!(string) => Val::Str;222	], {223		let state = EvaluationState::default();224		let path = PathBuf::from("std.parseJson").into();225		state.evaluate_snippet_raw(path ,s)226	})227}228229fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {230	parse_args!(context, "slice", args, 4, [231		0, indexable: ty!((string | array));232		1, index: ty!((number | null));233		2, end: ty!((number | null));234		3, step: ty!((number | null));235	], {236		std_slice(237			indexable.into_indexable()?,238			index.try_cast_nullable_num("index")?.map(|v| v as usize),239			end.try_cast_nullable_num("end")?.map(|v| v as usize),240			step.try_cast_nullable_num("step")?.map(|v| v as usize),241		)242	})243}244245fn builtin_substr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {246	parse_args!(context, "substr", args, 3, [247		0, str: ty!(string) => Val::Str;248		1, from: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;249		2, len: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;250	], {251		let out: String = str.chars().skip(from as usize).take(len as usize).collect();252		Ok(Val::Str(out.into()))253	})254}255256fn builtin_primitive_equals(257	context: Context,258	_loc: Option<&ExprLocation>,259	args: &ArgsDesc,260) -> Result<Val> {261	parse_args!(context, "primitiveEquals", args, 2, [262		0, a: ty!(any);263		1, b: ty!(any);264	], {265		Ok(Val::Bool(primitive_equals(&a, &b)?))266	})267}268269fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {270	parse_args!(context, "equals", args, 2, [271		0, a: ty!(any);272		1, b: ty!(any);273	], {274		Ok(Val::Bool(equals(&a, &b)?))275	})276}277278fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {279	parse_args!(context, "modulo", args, 2, [280		0, a: ty!(number) => Val::Num;281		1, b: ty!(number) => Val::Num;282	], {283		Ok(Val::Num(a % b))284	})285}286287fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {288	parse_args!(context, "mod", args, 2, [289		0, a: ty!((number | string));290		1, b: ty!(any);291	], {292		evaluate_mod_op(&a, &b)293	})294}295296fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {297	parse_args!(context, "floor", args, 1, [298		0, x: ty!(number) => Val::Num;299	], {300		Ok(Val::Num(x.floor()))301	})302}303304fn builtin_ceil(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {305	parse_args!(context, "ceil", args, 1, [306		0, x: ty!(number) => Val::Num;307	], {308		Ok(Val::Num(x.ceil()))309	})310}311312fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {313	parse_args!(context, "log", args, 1, [314		0, n: ty!(number) => Val::Num;315	], {316		Ok(Val::Num(n.ln()))317	})318}319320fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {321	parse_args!(context, "pow", args, 2, [322		0, x: ty!(number) => Val::Num;323		1, n: ty!(number) => Val::Num;324	], {325		Ok(Val::Num(x.powf(n)))326	})327}328329fn builtin_sqrt(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {330	parse_args!(context, "sqrt", args, 1, [331		0, x: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;332	], {333		Ok(Val::Num(x.sqrt()))334	})335}336337fn builtin_sin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {338	parse_args!(context, "sin", args, 1, [339		0, x: ty!(number) => Val::Num;340	], {341		Ok(Val::Num(x.sin()))342	})343}344345fn builtin_cos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {346	parse_args!(context, "cos", args, 1, [347		0, x: ty!(number) => Val::Num;348	], {349		Ok(Val::Num(x.cos()))350	})351}352353fn builtin_tan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {354	parse_args!(context, "tan", args, 1, [355		0, x: ty!(number) => Val::Num;356	], {357		Ok(Val::Num(x.tan()))358	})359}360361fn builtin_asin(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {362	parse_args!(context, "asin", args, 1, [363		0, x: ty!(number) => Val::Num;364	], {365		Ok(Val::Num(x.asin()))366	})367}368369fn builtin_acos(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {370	parse_args!(context, "acos", args, 1, [371		0, x: ty!(number) => Val::Num;372	], {373		Ok(Val::Num(x.acos()))374	})375}376377fn builtin_atan(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {378	parse_args!(context, "atan", args, 1, [379		0, x: ty!(number) => Val::Num;380	], {381		Ok(Val::Num(x.atan()))382	})383}384385fn builtin_exp(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {386	parse_args!(context, "exp", args, 1, [387		0, x: ty!(number) => Val::Num;388	], {389		Ok(Val::Num(x.exp()))390	})391}392393fn frexp(s: f64) -> (f64, i16) {394	if 0.0 == s {395		return (s, 0);396	} else {397		let lg = s.abs().log2();398		let x = (lg - lg.floor() - 1.0).exp2();399		let exp = lg.floor() + 1.0;400		(s.signum() * x, exp as i16)401	}402}403404fn builtin_mantissa(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {405	parse_args!(context, "mantissa", args, 1, [406		0, x: ty!(number) => Val::Num;407	], {408		Ok(Val::Num(frexp(x).0))409	})410}411412fn builtin_exponent(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {413	parse_args!(context, "exponent", args, 1, [414		0, x: ty!(number) => Val::Num;415	], {416		Ok(Val::Num(frexp(x).1.into()))417	})418}419420fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {421	parse_args!(context, "extVar", args, 1, [422		0, x: ty!(string) => Val::Str;423	], {424		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)425	})426}427428fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {429	parse_args!(context, "native", args, 1, [430		0, x: ty!(string) => Val::Str;431	], {432		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))?)433	})434}435436fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {437	parse_args!(context, "filter", args, 2, [438		0, func: ty!(function) => Val::Func;439		1, arr: ty!(array) => Val::Arr;440	], {441		Ok(Val::Arr(arr.filter(|val| func442			.evaluate_values(context.clone(), &[val.clone()])?443			.try_cast_bool("filter predicate"))?))444	})445}446447fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {448	parse_args!(context, "map", args, 2, [449		0, func: ty!(function) => Val::Func;450		1, arr: ty!(array) => Val::Arr;451	], {452		Ok(Val::Arr(arr.map(|val| func453			.evaluate_values(context.clone(), &[val]))?))454	})455}456457fn builtin_flatmap(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {458	parse_args!(context, "flatMap", args, 2, [459		0, func: ty!(function) => Val::Func;460		1, arr: ty!((array | string));461	], {462		match arr {463			Val::Str(s) => {464				let mut out = String::new();465				for c in s.chars() {466					match func.evaluate_values(context.clone(), &[Val::Str(c.to_string().into())])? {467						Val::Str(o) => out.push_str(&o),468						_ => throw!(RuntimeError("in std.join all items should be strings".into())),469					};470				}471				Ok(Val::Str(out.into()))472			},473			Val::Arr(a) => {474				let mut out = Vec::new();475				for el in a.iter() {476					let el = el?;477					match func.evaluate_values(context.clone(), &[el])? {478						Val::Arr(o) => for oe in o.iter() {479							out.push(oe?)480						},481						_ => throw!(RuntimeError("in std.join all items should be arrays".into())),482					};483				}484				Ok(Val::Arr(out.into()))485			},486			_ => unreachable!(),487		}488	})489}490491fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {492	parse_args!(context, "foldl", args, 3, [493		0, func: ty!(function) => Val::Func;494		1, arr: ty!(array) => Val::Arr;495		2, init: ty!(any);496	], {497		let mut acc = init;498		for i in arr.iter() {499			acc = func.evaluate_values(context.clone(), &[acc, i?])?;500		}501		Ok(acc)502	})503}504505fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {506	parse_args!(context, "foldr", args, 3, [507		0, func: ty!(function) => Val::Func;508		1, arr: ty!(array) => Val::Arr;509		2, init: ty!(any);510	], {511		let mut acc = init;512		for i in arr.iter().rev() {513			acc = func.evaluate_values(context.clone(), &[acc, i?])?;514		}515		Ok(acc)516	})517}518519#[allow(non_snake_case)]520fn builtin_sort_impl(521	context: Context,522	_loc: Option<&ExprLocation>,523	args: &ArgsDesc,524) -> Result<Val> {525	parse_args!(context, "sort", args, 2, [526		0, arr: ty!(array) => Val::Arr;527		1, keyF: ty!(function) => Val::Func;528	], {529		if arr.len() <= 1 {530			return Ok(Val::Arr(arr))531		}532		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))533	})534}535536fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {537	parse_args!(context, "format", args, 2, [538		0, str: ty!(string) => Val::Str;539		1, vals: ty!(any)540	], {541		std_format(str, vals)542	})543}544545fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {546	parse_args!(context, "range", args, 2, [547		0, from: ty!(number) => Val::Num;548		1, to: ty!(number) => Val::Num;549	], {550		if to < from {551			return Ok(Val::Arr(ArrValue::new_eager()))552		}553		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));554		for i in from as usize..=to as usize {555			out.push(Val::Num(i as f64));556		}557		Ok(Val::Arr(out.into()))558	})559}560561fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {562	parse_args!(context, "char", args, 1, [563		0, n: ty!(number) => Val::Num;564	], {565		let mut out = String::new();566		out.push(std::char::from_u32(n as u32).ok_or_else(||567			InvalidUnicodeCodepointGot(n as u32)568		)?);569		Ok(Val::Str(out.into()))570	})571}572573fn builtin_encode_utf8(574	context: Context,575	_loc: Option<&ExprLocation>,576	args: &ArgsDesc,577) -> Result<Val> {578	parse_args!(context, "encodeUTF8", args, 1, [579		0, str: ty!(string) => Val::Str;580	], {581		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))582	})583}584585fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {586	parse_args!(context, "md5", args, 1, [587		0, str: ty!(string) => Val::Str;588	], {589		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))590	})591}592593fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {594	parse_args!(context, "trace", args, 2, [595		0, str: ty!(string) => Val::Str;596		1, rest: ty!(any);597	], {598		eprint!("TRACE:");599		if let Some(loc) = loc {600			with_state(|s|{601				let locs = s.map_source_locations(&loc.0, &[loc.1]);602				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);603			});604		}605		eprintln!(" {}", str);606		Ok(rest)607	})608}609610fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {611	parse_args!(context, "base64", args, 1, [612		0, input: ty!((string | (Array<number>)));613	], {614		Ok(Val::Str(match input {615			Val::Str(s) => {616				base64::encode(s.bytes().collect::<Vec<_>>()).into()617			},618			Val::Arr(a) => {619				base64::encode(a.iter().map(|v| {620					Ok(v?.unwrap_num()? as u8)621				}).collect::<Result<Vec<_>>>()?).into()622			},623			_ => unreachable!()624		}))625	})626}627628fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {629	parse_args!(context, "join", args, 2, [630		0, sep: ty!((string | array));631		1, arr: ty!(array) => Val::Arr;632	], {633		Ok(match sep {634			Val::Arr(joiner_items) => {635				let mut out = Vec::new();636637				let mut first = true;638				for item in arr.iter() {639					let item = item?.clone();640					if let Val::Arr(items) = item {641						if !first {642							out.reserve(joiner_items.len());643							// TODO: extend644							for item in joiner_items.iter() {645								out.push(item?);646							}647						}648						first = false;649						out.reserve(items.len());650						// TODO: extend651						for item in items.iter() {652							out.push(item?);653						}654					} else {655						throw!(RuntimeError("in std.join all items should be arrays".into()));656					}657				}658659				Val::Arr(out.into())660			},661			Val::Str(sep) => {662				let mut out = String::new();663664				let mut first = true;665				for item in arr.iter() {666					let item = item?.clone();667					if let Val::Str(item) = item {668						if !first {669							out += &sep;670						}671						first = false;672						out += &item;673					} else {674						throw!(RuntimeError("in std.join all items should be strings".into()));675					}676				}677678				Val::Str(out.into())679			},680			_ => unreachable!()681		})682	})683}684685fn builtin_escape_string_json(686	context: Context,687	_loc: Option<&ExprLocation>,688	args: &ArgsDesc,689) -> Result<Val> {690	parse_args!(context, "escapeStringJson", args, 1, [691		0, str_: ty!(string) => Val::Str;692	], {693		Ok(Val::Str(escape_string_json(&str_).into()))694	})695}696697fn builtin_manifest_json_ex(698	context: Context,699	_loc: Option<&ExprLocation>,700	args: &ArgsDesc,701) -> Result<Val> {702	parse_args!(context, "manifestJsonEx", args, 2, [703		0, value: ty!(any);704		1, indent: ty!(string) => Val::Str;705	], {706		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {707			padding: &indent,708			mtype: ManifestType::Std,709		})?.into()))710	})711}712713fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {714	parse_args!(context, "reverse", args, 1, [715		0, value: ty!(array) => Val::Arr;716	], {717		Ok(Val::Arr(value.reversed()))718	})719}720721fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {722	parse_args!(context, "id", args, 1, [723		0, v: ty!(any);724	], {725		Ok(v)726	})727}728729fn builtin_str_replace(730	context: Context,731	_loc: Option<&ExprLocation>,732	args: &ArgsDesc,733) -> Result<Val> {734	parse_args!(context, "strReplace", args, 3, [735		0, str: ty!(string) => Val::Str;736		1, from: ty!(string) => Val::Str;737		2, to: ty!(string) => Val::Str;738	], {739		let mut out = String::new();740		let mut last_idx = 0;741		while let Some(idx) = (&str[last_idx..]).find(&from as &str) {742			out.push_str(&str[last_idx..last_idx+idx]);743			out.push_str(&to);744			last_idx += idx + from.len();745		}746		if last_idx == 0 {747			return Ok(Val::Str(str))748		}749		out.push_str(&str[last_idx..]);750		Ok(Val::Str(out.into()))751	})752}753754pub fn call_builtin(755	context: Context,756	loc: Option<&ExprLocation>,757	name: &str,758	args: &ArgsDesc,759) -> Result<Val> {760	BUILTINS761		.with(|builtins| builtins.get(name).copied())762		.ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)763}
modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -48,12 +48,7 @@
   toString(a)::
     if std.type(a) == 'string' then a else '' + a,
 
-  substr(str, from, len)::
-    assert std.isString(str) : 'substr first parameter should be a string, got ' + std.type(str);
-    assert std.isNumber(from) : 'substr second parameter should be a string, got ' + std.type(from);
-    assert std.isNumber(len) : 'substr third parameter should be a string, got ' + std.type(len);
-    assert len >= 0 : 'substr third parameter should be greater than zero, got ' + len;
-    std.join('', std.makeArray(std.max(0, std.min(len, std.length(str) - from)), function(i) str[i + from])),
+  substr:: $intrinsic(substr),
 
   startsWith(a, b)::
     if std.length(a) < std.length(b) then