git.delta.rocks / jrsonnet / refs/commits / 747b2b782cb8

difftreelog

perf make std.splitLimit builtin

Yaroslav Bolyukin2021-07-12parent: #02b7991.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			("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
@@ -123,21 +123,7 @@
     assert std.length(c) == 1 : 'std.split second parameter should have length 1, got ' + std.length(c);
     std.splitLimit(str, c, -1),
 
-  splitLimit(str, c, maxsplits)::
-    assert std.isString(str) : 'std.splitLimit first parameter should be a string, got ' + std.type(str);
-    assert std.isString(c) : 'std.splitLimit second parameter should be a string, got ' + std.type(c);
-    assert std.length(c) == 1 : 'std.splitLimit second parameter should have length 1, got ' + std.length(c);
-    assert std.isNumber(maxsplits) : 'std.splitLimit third parameter should be a number, got ' + std.type(maxsplits);
-    local aux(str, delim, i, arr, v) =
-      local c = str[i];
-      local i2 = i + 1;
-      if i >= std.length(str) then
-        arr + [v]
-      else if c == delim && (maxsplits == -1 || std.length(arr) < maxsplits) then
-        aux(str, delim, i2, arr + [v], '') tailstrict
-      else
-        aux(str, delim, i2, arr, v + c) tailstrict;
-    aux(str, c, 0, [], ''),
+  splitLimit:: $intrinsic(splitLimit),
 
   strReplace:: $intrinsic(strReplace),