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

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