git.delta.rocks / jrsonnet / refs/commits / 7b5cb9f1353f

difftreelog

source

crates/jrsonnet-evaluator/src/builtin/mod.rs15.2 KiBsourcehistory
1use crate::{2	equals,3	error::{Error::*, Result},4	parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, FuncVal, LazyVal,5	Val,6};7use format::{format_arr, format_obj};8use jrsonnet_interner::IStr;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: IStr, 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			("strReplace".into(), builtin_str_replace),76		].iter().cloned().collect()77	};78}7980fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {81	parse_args!(context, "length", args, 1, [82		0, x: ty!((string | object | array));83	], {84		Ok(match x {85			Val::Str(n) => Val::Num(n.chars().count() as f64),86			Val::Arr(a) => Val::Num(a.len() as f64),87			Val::Obj(o) => Val::Num(88				o.fields_visibility()89					.into_iter()90					.filter(|(_k, v)| *v)91					.count() as f64,92			),93			_ => unreachable!(),94		})95	})96}9798fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {99	parse_args!(context, "type", args, 1, [100		0, x: ty!(any);101	], {102		Ok(Val::Str(x.value_type().name().into()))103	})104}105106fn builtin_make_array(107	context: Context,108	_loc: Option<&ExprLocation>,109	args: &ArgsDesc,110) -> Result<Val> {111	parse_args!(context, "makeArray", args, 2, [112		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;113		1, func: ty!(function) => Val::Func;114	], {115		let mut out = Vec::with_capacity(sz as usize);116		for i in 0..sz as usize {117			out.push(LazyVal::new_resolved(func.evaluate_values(118				Context::new(),119				&[Val::Num(i as f64)]120			)?))121		}122		Ok(Val::Arr(out.into()))123	})124}125126fn builtin_codepoint(127	context: Context,128	_loc: Option<&ExprLocation>,129	args: &ArgsDesc,130) -> Result<Val> {131	parse_args!(context, "codepoint", args, 1, [132		0, str: ty!(char) => Val::Str;133	], {134		Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))135	})136}137138fn builtin_object_fields_ex(139	context: Context,140	_loc: Option<&ExprLocation>,141	args: &ArgsDesc,142) -> Result<Val> {143	parse_args!(context, "objectFieldsEx", args, 2, [144		0, obj: ty!(object) => Val::Obj;145		1, inc_hidden: ty!(boolean) => Val::Bool;146	], {147		let out = obj.fields_ex(inc_hidden);148		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))149	})150}151152fn builtin_object_has_ex(153	context: Context,154	_loc: Option<&ExprLocation>,155	args: &ArgsDesc,156) -> Result<Val> {157	parse_args!(context, "objectHasEx", args, 3, [158		0, obj: ty!(object) => Val::Obj;159		1, f: ty!(string) => Val::Str;160		2, inc_hidden: ty!(boolean) => Val::Bool;161	], {162		Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))163	})164}165166// faster167fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {168	parse_args!(context, "slice", args, 4, [169		0, indexable: ty!((string | array));170		1, index: ty!((number | null));171		2, end: ty!((number | null));172		3, step: ty!((number | null));173	], {174		let index = match index {175			Val::Num(v) => v as usize,176			Val::Null => 0,177			_ => unreachable!(),178		};179		let end = match end {180			Val::Num(v) => v as usize,181			Val::Null => match &indexable {182				Val::Str(s) => s.chars().count(),183				Val::Arr(v) => v.len(),184				_ => unreachable!()185			},186			_ => unreachable!()187		};188		let step = match step {189			Val::Num(v) => v as usize,190			Val::Null => 1,191			_ => unreachable!()192		};193		match &indexable {194			Val::Str(s) => {195				Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))196			}197			Val::Arr(arr) => {198				Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))199			}200			_ => unreachable!()201		}202	})203}204205// faster206fn builtin_primitive_equals(207	context: Context,208	_loc: Option<&ExprLocation>,209	args: &ArgsDesc,210) -> Result<Val> {211	parse_args!(context, "primitiveEquals", args, 2, [212		0, a: ty!(any);213		1, b: ty!(any);214	], {215		Ok(Val::Bool(primitive_equals(&a, &b)?))216	})217}218219// faster220fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {221	parse_args!(context, "equals", args, 2, [222		0, a: ty!(any);223		1, b: ty!(any);224	], {225		Ok(Val::Bool(equals(&a, &b)?))226	})227}228229fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {230	parse_args!(context, "modulo", args, 2, [231		0, a: ty!(number) => Val::Num;232		1, b: ty!(number) => Val::Num;233	], {234		Ok(Val::Num(a % b))235	})236}237238fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {239	parse_args!(context, "mod", args, 2, [240		0, a: ty!((number | string));241		1, b: ty!(any);242	], {243		match (a, b) {244			(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),245			(Val::Str(str), vals) => std_format(str, vals),246			(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))247		}248	})249}250251fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {252	parse_args!(context, "floor", args, 1, [253		0, x: ty!(number) => Val::Num;254	], {255		Ok(Val::Num(x.floor()))256	})257}258259fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {260	parse_args!(context, "log", args, 1, [261		0, n: ty!(number) => Val::Num;262	], {263		Ok(Val::Num(n.ln()))264	})265}266267fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {268	parse_args!(context, "pow", args, 2, [269		0, x: ty!(number) => Val::Num;270		1, n: ty!(number) => Val::Num;271	], {272		Ok(Val::Num(x.powf(n)))273	})274}275276fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {277	parse_args!(context, "extVar", args, 1, [278		0, x: ty!(string) => Val::Str;279	], {280		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)281	})282}283284fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {285	parse_args!(context, "native", args, 1, [286		0, x: ty!(string) => Val::Str;287	], {288		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))?)289	})290}291292fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {293	parse_args!(context, "filter", args, 2, [294		0, func: ty!(function) => Val::Func;295		1, arr: ty!(array) => Val::Arr;296	], {297		let mut out = Vec::new();298		for item in arr.iter() {299			let item = item?;300			if func301						.evaluate_values(context.clone(), &[item.clone()])?302						.try_cast_bool("filter predicate")? {303							out.push(item);304						}305		}306		Ok(Val::Arr(out.into()))307	})308}309310fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {311	parse_args!(context, "foldl", args, 3, [312		0, func: ty!(function) => Val::Func;313		1, arr: ty!(array) => Val::Arr;314		2, init: ty!(any);315	], {316		let mut acc = init;317		for i in arr.iter() {318			acc = func.evaluate_values(context.clone(), &[acc, i?])?;319		}320		Ok(acc)321	})322}323324fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {325	parse_args!(context, "foldr", args, 3, [326		0, func: ty!(function) => Val::Func;327		1, arr: ty!(array) => Val::Arr;328		2, init: ty!(any);329	], {330		let mut acc = init;331		for i in arr.iter().rev() {332			acc = func.evaluate_values(context.clone(), &[acc, i?])?;333		}334		Ok(acc)335	})336}337338#[allow(non_snake_case)]339fn builtin_sort_impl(340	context: Context,341	_loc: Option<&ExprLocation>,342	args: &ArgsDesc,343) -> Result<Val> {344	parse_args!(context, "sort", args, 2, [345		0, arr: ty!(array) => Val::Arr;346		1, keyF: ty!(function) => Val::Func;347	], {348		if arr.len() <= 1 {349			return Ok(Val::Arr(arr))350		}351		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))352	})353}354355// faster356fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {357	parse_args!(context, "format", args, 2, [358		0, str: ty!(string) => Val::Str;359		1, vals: ty!(any)360	], {361		std_format(str, vals)362	})363}364365fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {366	parse_args!(context, "range", args, 2, [367		0, from: ty!(number) => Val::Num;368		1, to: ty!(number) => Val::Num;369	], {370		if to < from {371			return Ok(Val::Arr(ArrValue::new_eager()))372		}373		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));374		for i in from as usize..=to as usize {375			out.push(Val::Num(i as f64));376		}377		Ok(Val::Arr(out.into()))378	})379}380381fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {382	parse_args!(context, "char", args, 1, [383		0, n: ty!(number) => Val::Num;384	], {385		let mut out = String::new();386		out.push(std::char::from_u32(n as u32).ok_or_else(||387			InvalidUnicodeCodepointGot(n as u32)388		)?);389		Ok(Val::Str(out.into()))390	})391}392393fn builtin_encode_utf8(394	context: Context,395	_loc: Option<&ExprLocation>,396	args: &ArgsDesc,397) -> Result<Val> {398	parse_args!(context, "encodeUTF8", args, 1, [399		0, str: ty!(string) => Val::Str;400	], {401		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))402	})403}404405fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {406	parse_args!(context, "md5", args, 1, [407		0, str: ty!(string) => Val::Str;408	], {409		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))410	})411}412413fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {414	parse_args!(context, "trace", args, 2, [415		0, str: ty!(string) => Val::Str;416		1, rest: ty!(any);417	], {418		eprint!("TRACE:");419		if let Some(loc) = loc {420			with_state(|s|{421				let locs = s.map_source_locations(&loc.0, &[loc.1]);422				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);423			});424		}425		eprintln!(" {}", str);426		Ok(rest)427	})428}429430fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {431	parse_args!(context, "base64", args, 1, [432		0, input: ty!((string | (Array<number>)));433	], {434		Ok(Val::Str(match input {435			Val::Str(s) => {436				base64::encode(s.bytes().collect::<Vec<_>>()).into()437			},438			Val::Arr(a) => {439				base64::encode(a.iter().map(|v| {440					Ok(v?.unwrap_num()? as u8)441				}).collect::<Result<Vec<_>>>()?).into()442			},443			_ => unreachable!()444		}))445	})446}447448fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {449	parse_args!(context, "join", args, 2, [450		0, sep: ty!((string | array));451		1, arr: ty!(array) => Val::Arr;452	], {453		Ok(match sep {454			Val::Arr(joiner_items) => {455				let mut out = Vec::new();456457				let mut first = true;458				for item in arr.iter() {459					let item = item?.clone();460					if let Val::Arr(items) = item {461						if !first {462							out.reserve(joiner_items.len());463							// TODO: extend464							for item in joiner_items.iter() {465								out.push(item?);466							}467						}468						first = false;469						out.reserve(items.len());470						// TODO: extend471						for item in items.iter() {472							out.push(item?);473						}474					} else {475						throw!(RuntimeError("in std.join all items should be arrays".into()));476					}477				}478479				Val::Arr(out.into())480			},481			Val::Str(sep) => {482				let mut out = String::new();483484				let mut first = true;485				for item in arr.iter() {486					let item = item?.clone();487					if let Val::Str(item) = item {488						if !first {489							out += &sep;490						}491						first = false;492						out += &item;493					} else {494						throw!(RuntimeError("in std.join all items should be strings".into()));495					}496				}497498				Val::Str(out.into())499			},500			_ => unreachable!()501		})502	})503}504505// faster506fn builtin_escape_string_json(507	context: Context,508	_loc: Option<&ExprLocation>,509	args: &ArgsDesc,510) -> Result<Val> {511	parse_args!(context, "escapeStringJson", args, 1, [512		0, str_: ty!(string) => Val::Str;513	], {514		Ok(Val::Str(escape_string_json(&str_).into()))515	})516}517518// faster519fn builtin_manifest_json_ex(520	context: Context,521	_loc: Option<&ExprLocation>,522	args: &ArgsDesc,523) -> Result<Val> {524	parse_args!(context, "manifestJsonEx", args, 2, [525		0, value: ty!(any);526		1, indent: ty!(string) => Val::Str;527	], {528		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {529			padding: &indent,530			mtype: ManifestType::Std,531		})?.into()))532	})533}534535// faster536fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {537	parse_args!(context, "reverse", args, 1, [538		0, value: ty!(array) => Val::Arr;539	], {540		Ok(Val::Arr(value.reversed()))541	})542}543544fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {545	parse_args!(context, "id", args, 1, [546		0, v: ty!(any);547	], {548		Ok(v)549	})550}551552// faster553fn builtin_str_replace(554	context: Context,555	_loc: Option<&ExprLocation>,556	args: &ArgsDesc,557) -> Result<Val> {558	parse_args!(context, "strReplace", args, 3, [559		0, str: ty!(string) => Val::Str;560		1, from: ty!(string) => Val::Str;561		2, to: ty!(string) => Val::Str;562	], {563		let mut out = String::new();564		let mut last_idx = 0;565		while let Some(idx) = (&str[last_idx..]).find(&from as &str) {566			out.push_str(&str[last_idx..last_idx+idx]);567			out.push_str(&to);568			last_idx += idx + from.len();569		}570		if last_idx == 0 {571			return Ok(Val::Str(str))572		}573		out.push_str(&str[last_idx..]);574		Ok(Val::Str(out.into()))575	})576}577578pub fn call_builtin(579	context: Context,580	loc: Option<&ExprLocation>,581	name: &str,582	args: &ArgsDesc,583) -> Result<Val> {584	if let Some(f) = BUILTINS.with(|builtins| builtins.get(name).copied()) {585		return Ok(f(context, loc, args)?);586	}587	throw!(IntrinsicNotFound(name.into()))588}