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

difftreelog

feat std.ceil builtin

Yaroslav Bolyukin2021-07-12parent: #46b4dd0.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			("log".into(), builtin_log),90			("pow".into(), builtin_pow),91			("extVar".into(), builtin_ext_var),92			("native".into(), builtin_native),93			("filter".into(), builtin_filter),94			("map".into(), builtin_map),95			("flatMap".into(), builtin_flatmap),96			("foldl".into(), builtin_foldl),97			("foldr".into(), builtin_foldr),98			("sortImpl".into(), builtin_sort_impl),99			("format".into(), builtin_format),100			("range".into(), builtin_range),101			("char".into(), builtin_char),102			("encodeUTF8".into(), builtin_encode_utf8),103			("md5".into(), builtin_md5),104			("base64".into(), builtin_base64),105			("trace".into(), builtin_trace),106			("join".into(), builtin_join),107			("escapeStringJson".into(), builtin_escape_string_json),108			("manifestJsonEx".into(), builtin_manifest_json_ex),109			("reverse".into(), builtin_reverse),110			("id".into(), builtin_id),111			("strReplace".into(), builtin_str_replace),112			("parseJson".into(), builtin_parse_json),113		].iter().cloned().collect()114	};115}116117fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {118	parse_args!(context, "length", args, 1, [119		0, x: ty!((string | object | array));120	], {121		Ok(match x {122			Val::Str(n) => Val::Num(n.chars().count() as f64),123			Val::Arr(a) => Val::Num(a.len() as f64),124			Val::Obj(o) => Val::Num(125				o.fields_visibility()126					.into_iter()127					.filter(|(_k, v)| *v)128					.count() as f64,129			),130			_ => unreachable!(),131		})132	})133}134135fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {136	parse_args!(context, "type", args, 1, [137		0, x: ty!(any);138	], {139		Ok(Val::Str(x.value_type().name().into()))140	})141}142143fn builtin_make_array(144	context: Context,145	_loc: Option<&ExprLocation>,146	args: &ArgsDesc,147) -> Result<Val> {148	parse_args!(context, "makeArray", args, 2, [149		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;150		1, func: ty!(function) => Val::Func;151	], {152		let mut out = Vec::with_capacity(sz as usize);153		for i in 0..sz as usize {154			out.push(LazyVal::new_resolved(func.evaluate_values(155				context.clone(),156				&[Val::Num(i as f64)]157			)?))158		}159		Ok(Val::Arr(out.into()))160	})161}162163fn builtin_codepoint(164	context: Context,165	_loc: Option<&ExprLocation>,166	args: &ArgsDesc,167) -> Result<Val> {168	parse_args!(context, "codepoint", args, 1, [169		0, str: ty!(char) => Val::Str;170	], {171		Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))172	})173}174175fn builtin_object_fields_ex(176	context: Context,177	_loc: Option<&ExprLocation>,178	args: &ArgsDesc,179) -> Result<Val> {180	parse_args!(context, "objectFieldsEx", args, 2, [181		0, obj: ty!(object) => Val::Obj;182		1, inc_hidden: ty!(boolean) => Val::Bool;183	], {184		let out = obj.fields_ex(inc_hidden);185		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))186	})187}188189fn builtin_object_has_ex(190	context: Context,191	_loc: Option<&ExprLocation>,192	args: &ArgsDesc,193) -> Result<Val> {194	parse_args!(context, "objectHasEx", args, 3, [195		0, obj: ty!(object) => Val::Obj;196		1, f: ty!(string) => Val::Str;197		2, inc_hidden: ty!(boolean) => Val::Bool;198	], {199		Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))200	})201}202203fn builtin_parse_json(204	context: Context,205	_loc: Option<&ExprLocation>,206	args: &ArgsDesc,207) -> Result<Val> {208	parse_args!(context, "parseJson", args, 1, [209		0, s: ty!(string) => Val::Str;210	], {211		let state = EvaluationState::default();212		let path = PathBuf::from("std.parseJson").into();213		state.evaluate_snippet_raw(path ,s)214	})215}216217fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {218	parse_args!(context, "slice", args, 4, [219		0, indexable: ty!((string | array));220		1, index: ty!((number | null));221		2, end: ty!((number | null));222		3, step: ty!((number | null));223	], {224		std_slice(225			indexable.into_indexable()?,226			index.try_cast_nullable_num("index")?.map(|v| v as usize),227			end.try_cast_nullable_num("end")?.map(|v| v as usize),228			step.try_cast_nullable_num("step")?.map(|v| v as usize),229		)230	})231}232233fn builtin_primitive_equals(234	context: Context,235	_loc: Option<&ExprLocation>,236	args: &ArgsDesc,237) -> Result<Val> {238	parse_args!(context, "primitiveEquals", args, 2, [239		0, a: ty!(any);240		1, b: ty!(any);241	], {242		Ok(Val::Bool(primitive_equals(&a, &b)?))243	})244}245246fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {247	parse_args!(context, "equals", args, 2, [248		0, a: ty!(any);249		1, b: ty!(any);250	], {251		Ok(Val::Bool(equals(&a, &b)?))252	})253}254255fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {256	parse_args!(context, "modulo", args, 2, [257		0, a: ty!(number) => Val::Num;258		1, b: ty!(number) => Val::Num;259	], {260		Ok(Val::Num(a % b))261	})262}263264fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {265	parse_args!(context, "mod", args, 2, [266		0, a: ty!((number | string));267		1, b: ty!(any);268	], {269		evaluate_mod_op(&a, &b)270	})271}272273fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {274	parse_args!(context, "floor", args, 1, [275		0, x: ty!(number) => Val::Num;276	], {277		Ok(Val::Num(x.floor()))278	})279}280281fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {282	parse_args!(context, "log", args, 1, [283		0, n: ty!(number) => Val::Num;284	], {285		Ok(Val::Num(n.ln()))286	})287}288289fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {290	parse_args!(context, "pow", args, 2, [291		0, x: ty!(number) => Val::Num;292		1, n: ty!(number) => Val::Num;293	], {294		Ok(Val::Num(x.powf(n)))295	})296}297298fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {299	parse_args!(context, "extVar", args, 1, [300		0, x: ty!(string) => Val::Str;301	], {302		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)303	})304}305306fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {307	parse_args!(context, "native", args, 1, [308		0, x: ty!(string) => Val::Str;309	], {310		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))?)311	})312}313314fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {315	parse_args!(context, "filter", args, 2, [316		0, func: ty!(function) => Val::Func;317		1, arr: ty!(array) => Val::Arr;318	], {319		Ok(Val::Arr(arr.filter(|val| func320			.evaluate_values(context.clone(), &[val.clone()])?321			.try_cast_bool("filter predicate"))?))322	})323}324325fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {326	parse_args!(context, "map", args, 2, [327		0, func: ty!(function) => Val::Func;328		1, arr: ty!(array) => Val::Arr;329	], {330		Ok(Val::Arr(arr.map(|val| func331			.evaluate_values(context.clone(), &[val]))?))332	})333}334335fn builtin_flatmap(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {336	parse_args!(context, "flatMap", args, 2, [337		0, func: ty!(function) => Val::Func;338		1, arr: ty!((array | string));339	], {340		match arr {341			Val::Str(s) => {342				let mut out = String::new();343				for c in s.chars() {344					match func.evaluate_values(context.clone(), &[Val::Str(c.to_string().into())])? {345						Val::Str(o) => out.push_str(&o),346						_ => throw!(RuntimeError("in std.join all items should be strings".into())),347					};348				}349				Ok(Val::Str(out.into()))350			},351			Val::Arr(a) => {352				let mut out = Vec::new();353				for el in a.iter() {354					let el = el?;355					match func.evaluate_values(context.clone(), &[el])? {356						Val::Arr(o) => for oe in o.iter() {357							out.push(oe?)358						},359						_ => throw!(RuntimeError("in std.join all items should be arrays".into())),360					};361				}362				Ok(Val::Arr(out.into()))363			},364			_ => unreachable!(),365		}366	})367}368369fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {370	parse_args!(context, "foldl", args, 3, [371		0, func: ty!(function) => Val::Func;372		1, arr: ty!(array) => Val::Arr;373		2, init: ty!(any);374	], {375		let mut acc = init;376		for i in arr.iter() {377			acc = func.evaluate_values(context.clone(), &[acc, i?])?;378		}379		Ok(acc)380	})381}382383fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {384	parse_args!(context, "foldr", args, 3, [385		0, func: ty!(function) => Val::Func;386		1, arr: ty!(array) => Val::Arr;387		2, init: ty!(any);388	], {389		let mut acc = init;390		for i in arr.iter().rev() {391			acc = func.evaluate_values(context.clone(), &[acc, i?])?;392		}393		Ok(acc)394	})395}396397#[allow(non_snake_case)]398fn builtin_sort_impl(399	context: Context,400	_loc: Option<&ExprLocation>,401	args: &ArgsDesc,402) -> Result<Val> {403	parse_args!(context, "sort", args, 2, [404		0, arr: ty!(array) => Val::Arr;405		1, keyF: ty!(function) => Val::Func;406	], {407		if arr.len() <= 1 {408			return Ok(Val::Arr(arr))409		}410		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))411	})412}413414fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {415	parse_args!(context, "format", args, 2, [416		0, str: ty!(string) => Val::Str;417		1, vals: ty!(any)418	], {419		std_format(str, vals)420	})421}422423fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {424	parse_args!(context, "range", args, 2, [425		0, from: ty!(number) => Val::Num;426		1, to: ty!(number) => Val::Num;427	], {428		if to < from {429			return Ok(Val::Arr(ArrValue::new_eager()))430		}431		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));432		for i in from as usize..=to as usize {433			out.push(Val::Num(i as f64));434		}435		Ok(Val::Arr(out.into()))436	})437}438439fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {440	parse_args!(context, "char", args, 1, [441		0, n: ty!(number) => Val::Num;442	], {443		let mut out = String::new();444		out.push(std::char::from_u32(n as u32).ok_or_else(||445			InvalidUnicodeCodepointGot(n as u32)446		)?);447		Ok(Val::Str(out.into()))448	})449}450451fn builtin_encode_utf8(452	context: Context,453	_loc: Option<&ExprLocation>,454	args: &ArgsDesc,455) -> Result<Val> {456	parse_args!(context, "encodeUTF8", args, 1, [457		0, str: ty!(string) => Val::Str;458	], {459		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))460	})461}462463fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {464	parse_args!(context, "md5", args, 1, [465		0, str: ty!(string) => Val::Str;466	], {467		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))468	})469}470471fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {472	parse_args!(context, "trace", args, 2, [473		0, str: ty!(string) => Val::Str;474		1, rest: ty!(any);475	], {476		eprint!("TRACE:");477		if let Some(loc) = loc {478			with_state(|s|{479				let locs = s.map_source_locations(&loc.0, &[loc.1]);480				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);481			});482		}483		eprintln!(" {}", str);484		Ok(rest)485	})486}487488fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {489	parse_args!(context, "base64", args, 1, [490		0, input: ty!((string | (Array<number>)));491	], {492		Ok(Val::Str(match input {493			Val::Str(s) => {494				base64::encode(s.bytes().collect::<Vec<_>>()).into()495			},496			Val::Arr(a) => {497				base64::encode(a.iter().map(|v| {498					Ok(v?.unwrap_num()? as u8)499				}).collect::<Result<Vec<_>>>()?).into()500			},501			_ => unreachable!()502		}))503	})504}505506fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {507	parse_args!(context, "join", args, 2, [508		0, sep: ty!((string | array));509		1, arr: ty!(array) => Val::Arr;510	], {511		Ok(match sep {512			Val::Arr(joiner_items) => {513				let mut out = Vec::new();514515				let mut first = true;516				for item in arr.iter() {517					let item = item?.clone();518					if let Val::Arr(items) = item {519						if !first {520							out.reserve(joiner_items.len());521							// TODO: extend522							for item in joiner_items.iter() {523								out.push(item?);524							}525						}526						first = false;527						out.reserve(items.len());528						// TODO: extend529						for item in items.iter() {530							out.push(item?);531						}532					} else {533						throw!(RuntimeError("in std.join all items should be arrays".into()));534					}535				}536537				Val::Arr(out.into())538			},539			Val::Str(sep) => {540				let mut out = String::new();541542				let mut first = true;543				for item in arr.iter() {544					let item = item?.clone();545					if let Val::Str(item) = item {546						if !first {547							out += &sep;548						}549						first = false;550						out += &item;551					} else {552						throw!(RuntimeError("in std.join all items should be strings".into()));553					}554				}555556				Val::Str(out.into())557			},558			_ => unreachable!()559		})560	})561}562563fn builtin_escape_string_json(564	context: Context,565	_loc: Option<&ExprLocation>,566	args: &ArgsDesc,567) -> Result<Val> {568	parse_args!(context, "escapeStringJson", args, 1, [569		0, str_: ty!(string) => Val::Str;570	], {571		Ok(Val::Str(escape_string_json(&str_).into()))572	})573}574575fn builtin_manifest_json_ex(576	context: Context,577	_loc: Option<&ExprLocation>,578	args: &ArgsDesc,579) -> Result<Val> {580	parse_args!(context, "manifestJsonEx", args, 2, [581		0, value: ty!(any);582		1, indent: ty!(string) => Val::Str;583	], {584		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {585			padding: &indent,586			mtype: ManifestType::Std,587		})?.into()))588	})589}590591fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {592	parse_args!(context, "reverse", args, 1, [593		0, value: ty!(array) => Val::Arr;594	], {595		Ok(Val::Arr(value.reversed()))596	})597}598599fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {600	parse_args!(context, "id", args, 1, [601		0, v: ty!(any);602	], {603		Ok(v)604	})605}606607fn builtin_str_replace(608	context: Context,609	_loc: Option<&ExprLocation>,610	args: &ArgsDesc,611) -> Result<Val> {612	parse_args!(context, "strReplace", args, 3, [613		0, str: ty!(string) => Val::Str;614		1, from: ty!(string) => Val::Str;615		2, to: ty!(string) => Val::Str;616	], {617		let mut out = String::new();618		let mut last_idx = 0;619		while let Some(idx) = (&str[last_idx..]).find(&from as &str) {620			out.push_str(&str[last_idx..last_idx+idx]);621			out.push_str(&to);622			last_idx += idx + from.len();623		}624		if last_idx == 0 {625			return Ok(Val::Str(str))626		}627		out.push_str(&str[last_idx..]);628		Ok(Val::Str(out.into()))629	})630}631632pub fn call_builtin(633	context: Context,634	loc: Option<&ExprLocation>,635	name: &str,636	args: &ArgsDesc,637) -> Result<Val> {638	BUILTINS639		.with(|builtins| builtins.get(name).copied())640		.ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)641}
modifiedcrates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/std.jsonnet
+++ b/crates/jrsonnet-stdlib/src/std.jsonnet
@@ -12,6 +12,7 @@
   primitiveEquals:: $intrinsic(primitiveEquals),
   modulo:: $intrinsic(modulo),
   floor:: $intrinsic(floor),
+  ceil:: $intrinsic(ceil),
   log:: $intrinsic(log),
   pow:: $intrinsic(pow),
   extVar:: $intrinsic(extVar),