git.delta.rocks / jrsonnet / refs/commits / 72f4bf90cdcb

difftreelog

source

crates/jrsonnet-evaluator/src/builtin/mod.rs14.6 KiBsourcehistory
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_interner::IStr;10use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};11use jrsonnet_types::ty;12use std::{collections::HashMap, path::PathBuf, rc::Rc};1314pub mod stdlib;15pub use stdlib::*;1617use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};1819pub mod format;20pub mod manifest;21pub mod sort;2223fn std_format(str: IStr, vals: Val) -> Result<Val> {24	push(25		&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),26		|| format!("std.format of {}", str),27		|| {28			Ok(match vals {29				Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),30				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),31				o => Val::Str(format_arr(&str, &[o])?.into()),32			})33		},34	)35}3637type Builtin = fn(context: Context, loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val>;3839type BuiltinsType = HashMap<Box<str>, Builtin>;4041thread_local! {42	static BUILTINS: BuiltinsType = {43		[44			("length".into(), builtin_length as Builtin),45			("type".into(), builtin_type),46			("makeArray".into(), builtin_make_array),47			("codepoint".into(), builtin_codepoint),48			("objectFieldsEx".into(), builtin_object_fields_ex),49			("objectHasEx".into(), builtin_object_has_ex),50			("slice".into(), builtin_slice),51			("primitiveEquals".into(), builtin_primitive_equals),52			("equals".into(), builtin_equals),53			("modulo".into(), builtin_modulo),54			("mod".into(), builtin_mod),55			("floor".into(), builtin_floor),56			("log".into(), builtin_log),57			("pow".into(), builtin_pow),58			("extVar".into(), builtin_ext_var),59			("native".into(), builtin_native),60			("filter".into(), builtin_filter),61			("foldl".into(), builtin_foldl),62			("foldr".into(), builtin_foldr),63			("sortImpl".into(), builtin_sort_impl),64			("format".into(), builtin_format),65			("range".into(), builtin_range),66			("char".into(), builtin_char),67			("encodeUTF8".into(), builtin_encode_utf8),68			("md5".into(), builtin_md5),69			("base64".into(), builtin_base64),70			("trace".into(), builtin_trace),71			("join".into(), builtin_join),72			("escapeStringJson".into(), builtin_escape_string_json),73			("manifestJsonEx".into(), builtin_manifest_json_ex),74			("reverse".into(), builtin_reverse),75			("id".into(), builtin_id),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!((str | obj | [any]));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!(number((Some(0.0))..(None))) => Val::Num;113		1, func: ty!(fn.any) => 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().take(1).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!(obj) => Val::Obj;145		1, inc_hidden: ty!(bool) => Val::Bool;146	], {147		let mut out = obj.fields_visibility()148			.into_iter()149			.filter(|(_k, v)| *v || inc_hidden)150			.map(|(k, _v)|k)151			.collect::<Vec<_>>();152		out.sort();153		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))154	})155}156157fn builtin_object_has_ex(158	context: Context,159	_loc: &Option<ExprLocation>,160	args: &ArgsDesc,161) -> Result<Val> {162	parse_args!(context, "objectHasEx", args, 3, [163		0, obj: ty!(obj) => Val::Obj;164		1, f: ty!(str) => Val::Str;165		2, inc_hidden: ty!(bool) => Val::Bool;166	], {167		Ok(Val::Bool(168			obj.fields_visibility()169				.into_iter()170				.filter(|(_k, v)| *v || inc_hidden)171				.any(|(k, _v)| *k == *f),172		))173	})174}175176// faster177fn builtin_slice(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {178	parse_args!(context, "slice", args, 4, [179		0, indexable: ty!((str | [any]));180		1, index: ty!((num | null));181		2, end: ty!((num | null));182		3, step: ty!((num | null));183	], {184		let index = match index {185			Val::Num(v) => v as usize,186			Val::Null => 0,187			_ => unreachable!(),188		};189		let end = match end {190			Val::Num(v) => v as usize,191			Val::Null => match &indexable {192				Val::Str(s) => s.chars().count(),193				Val::Arr(v) => v.len(),194				_ => unreachable!()195			},196			_ => unreachable!()197		};198		let step = match step {199			Val::Num(v) => v as usize,200			Val::Null => 1,201			_ => unreachable!()202		};203		match &indexable {204			Val::Str(s) => {205				Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))206			}207			Val::Arr(arr) => {208				Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))209			}210			_ => unreachable!()211		}212	})213}214215// faster216fn builtin_primitive_equals(217	context: Context,218	_loc: &Option<ExprLocation>,219	args: &ArgsDesc,220) -> Result<Val> {221	parse_args!(context, "primitiveEquals", args, 2, [222		0, a: ty!(any);223		1, b: ty!(any);224	], {225		Ok(Val::Bool(primitive_equals(&a, &b)?))226	})227}228229// faster230fn builtin_equals(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {231	parse_args!(context, "equals", args, 2, [232		0, a: ty!(any);233		1, b: ty!(any);234	], {235		Ok(Val::Bool(equals(&a, &b)?))236	})237}238239fn builtin_modulo(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {240	parse_args!(context, "modulo", args, 2, [241		0, a: ty!(num) => Val::Num;242		1, b: ty!(num) => Val::Num;243	], {244		Ok(Val::Num(a % b))245	})246}247248fn builtin_mod(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {249	parse_args!(context, "mod", args, 2, [250		0, a: ty!((num | str));251		1, b: ty!(any);252	], {253		match (a, b) {254			(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),255			(Val::Str(str), vals) => std_format(str, vals),256			(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))257		}258	})259}260261fn builtin_floor(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {262	parse_args!(context, "floor", args, 1, [263		0, x: ty!(num) => Val::Num;264	], {265		Ok(Val::Num(x.floor()))266	})267}268269fn builtin_log(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {270	parse_args!(context, "log", args, 1, [271		0, n: ty!(num) => Val::Num;272	], {273		Ok(Val::Num(n.ln()))274	})275}276277fn builtin_pow(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {278	parse_args!(context, "pow", args, 2, [279		0, x: ty!(num) => Val::Num;280		1, n: ty!(num) => Val::Num;281	], {282		Ok(Val::Num(x.powf(n)))283	})284}285286fn builtin_ext_var(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {287	parse_args!(context, "extVar", args, 1, [288		0, x: ty!(str) => Val::Str;289	], {290		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)291	})292}293294fn builtin_native(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {295	parse_args!(context, "native", args, 1, [296		0, x: ty!(str) => Val::Str;297	], {298		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))?)299	})300}301302fn builtin_filter(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {303	parse_args!(context, "filter", args, 2, [304		0, func: ty!(fn.any) => Val::Func;305		1, arr: ty!([any]) => Val::Arr;306	], {307		let mut out = Vec::new();308		for item in arr.iter() {309			let item = item?;310			if func311						.evaluate_values(context.clone(), &[item.clone()])?312						.try_cast_bool("filter predicate")? {313							out.push(item);314						}315		}316		Ok(Val::Arr(out.into()))317	})318}319320fn builtin_foldl(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {321	parse_args!(context, "foldl", args, 3, [322		0, func: ty!(fn.any) => Val::Func;323		1, arr: ty!([any]) => Val::Arr;324		2, init: ty!(any);325	], {326		let mut acc = init;327		for i in arr.iter() {328			acc = func.evaluate_values(context.clone(), &[acc, i?])?;329		}330		Ok(acc)331	})332}333334fn builtin_foldr(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {335	parse_args!(context, "foldr", args, 3, [336		0, func: ty!(fn.any) => Val::Func;337		1, arr: ty!([any]) => Val::Arr;338		2, init: ty!(any);339	], {340		let mut acc = init;341		for i in arr.iter().rev() {342			acc = func.evaluate_values(context.clone(), &[acc, i?])?;343		}344		Ok(acc)345	})346}347348#[allow(non_snake_case)]349fn builtin_sort_impl(350	context: Context,351	_loc: &Option<ExprLocation>,352	args: &ArgsDesc,353) -> Result<Val> {354	parse_args!(context, "sort", args, 2, [355		0, arr: ty!([any]) => Val::Arr;356		1, keyF: ty!(fn.any) => Val::Func;357	], {358		if arr.len() <= 1 {359			return Ok(Val::Arr(arr))360		}361		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))362	})363}364365// faster366fn builtin_format(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {367	parse_args!(context, "format", args, 2, [368		0, str: ty!(str) => Val::Str;369		1, vals: ty!(any)370	], {371		std_format(str, vals)372	})373}374375fn builtin_range(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {376	parse_args!(context, "range", args, 2, [377		0, from: ty!(num) => Val::Num;378		1, to: ty!(num) => Val::Num;379	], {380		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));381		for i in from as usize..=to as usize {382			out.push(Val::Num(i as f64));383		}384		Ok(Val::Arr(out.into()))385	})386}387388fn builtin_char(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {389	parse_args!(context, "char", args, 1, [390		0, n: ty!(num) => Val::Num;391	], {392		let mut out = String::new();393		out.push(std::char::from_u32(n as u32).ok_or_else(||394			InvalidUnicodeCodepointGot(n as u32)395		)?);396		Ok(Val::Str(out.into()))397	})398}399400fn builtin_encode_utf8(401	context: Context,402	_loc: &Option<ExprLocation>,403	args: &ArgsDesc,404) -> Result<Val> {405	parse_args!(context, "encodeUTF8", args, 1, [406		0, str: ty!(str) => Val::Str;407	], {408		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))409	})410}411412fn builtin_md5(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {413	parse_args!(context, "md5", args, 1, [414		0, str: ty!(str) => Val::Str;415	], {416		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))417	})418}419420fn builtin_trace(context: Context, loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {421	parse_args!(context, "trace", args, 2, [422		0, str: ty!(str) => Val::Str;423		1, rest: ty!(any);424	], {425		eprint!("TRACE:");426		if let Some(loc) = loc {427			with_state(|s|{428				let locs = s.map_source_locations(&loc.0, &[loc.1]);429				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);430			});431		}432		eprintln!(" {}", str);433		Ok(rest)434	})435}436437fn builtin_base64(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {438	parse_args!(context, "base64", args, 1, [439		0, input: ty!((str | [num]));440	], {441		Ok(Val::Str(match input {442			Val::Str(s) => {443				base64::encode(s.bytes().collect::<Vec<_>>()).into()444			},445			Val::Arr(a) => {446				base64::encode(a.iter().map(|v| {447					Ok(v?.clone().unwrap_num()? as u8)448				}).collect::<Result<Vec<_>>>()?).into()449			},450			_ => unreachable!()451		}))452	})453}454455fn builtin_join(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {456	parse_args!(context, "join", args, 2, [457		0, sep: ty!((str | [any]));458		1, arr: ty!([any]) => Val::Arr;459	], {460		Ok(match sep {461			Val::Arr(joiner_items) => {462				let mut out = Vec::new();463464				let mut first = true;465				for item in arr.iter() {466					let item = item?.clone();467					if let Val::Arr(items) = item {468						if !first {469							out.reserve(joiner_items.len());470							// TODO: extend471							for item in joiner_items.iter() {472								out.push(item?);473							}474						}475						first = false;476						out.reserve(items.len());477						// TODO: extend478						for item in items.iter() {479							out.push(item?);480						}481					} else {482						throw!(RuntimeError("in std.join all items should be arrays".into()));483					}484				}485486				Val::Arr(out.into())487			},488			Val::Str(sep) => {489				let mut out = String::new();490491				let mut first = true;492				for item in arr.iter() {493					let item = item?.clone();494					if let Val::Str(item) = item {495						if !first {496							out += &sep;497						}498						first = false;499						out += &item;500					} else {501						throw!(RuntimeError("in std.join all items should be strings".into()));502					}503				}504505				Val::Str(out.into())506			},507			_ => unreachable!()508		})509	})510}511512// faster513fn builtin_escape_string_json(514	context: Context,515	_loc: &Option<ExprLocation>,516	args: &ArgsDesc,517) -> Result<Val> {518	parse_args!(context, "escapeStringJson", args, 1, [519		0, str_: ty!(str) => Val::Str;520	], {521		Ok(Val::Str(escape_string_json(&str_).into()))522	})523}524525// faster526fn builtin_manifest_json_ex(527	context: Context,528	_loc: &Option<ExprLocation>,529	args: &ArgsDesc,530) -> Result<Val> {531	parse_args!(context, "manifestJsonEx", args, 2, [532		0, value: ty!(any);533		1, indent: ty!(str) => Val::Str;534	], {535		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {536			padding: &indent,537			mtype: ManifestType::Std,538		})?.into()))539	})540}541542// faster543fn builtin_reverse(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {544	parse_args!(context, "reverse", args, 1, [545		0, value: ty!([any]) => Val::Arr;546	], {547		Ok(Val::Arr(value.reversed()))548	})549}550551fn builtin_id(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {552	parse_args!(context, "id", args, 1, [553		0, v: ty!(any);554	], {555		Ok(v)556	})557}558559#[allow(clippy::cognitive_complexity)]560pub fn call_builtin(561	context: Context,562	loc: &Option<ExprLocation>,563	name: &str,564	args: &ArgsDesc,565) -> Result<Val> {566	if let Some(f) = BUILTINS.with(|builtins| builtins.get(name).map(|f| *f)) {567		return Ok(f(context, loc, args)?);568	}569	throw!(IntrinsicNotFound(name.into()))570}