git.delta.rocks / jrsonnet / refs/commits / 407517ff4770

difftreelog

refactor rewrite builtins to new type system

Yaroslav Bolyukin2020-12-01parent: #028057c.patch.diff
in: master

8 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -169,6 +169,7 @@
  "indexmap",
  "jrsonnet-parser",
  "jrsonnet-stdlib",
+ "jrsonnet-types",
  "md5",
  "pathdiff",
  "rustc-hash",
@@ -195,6 +196,10 @@
 version = "0.3.3"
 
 [[package]]
+name = "jrsonnet-types"
+version = "0.3.2"
+
+[[package]]
 name = "jsonnet"
 version = "0.3.3"
 dependencies = [
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,8 +4,9 @@
 	"crates/jrsonnet-evaluator",
 	"crates/jrsonnet-stdlib",
 	"crates/jrsonnet-cli",
+	"crates/jrsonnet-types",
 	"bindings/jsonnet",
-	"cmds/jrsonnet"
+	"cmds/jrsonnet",
 ]
 
 [profile.test]
modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -26,6 +26,7 @@
 [dependencies]
 jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.3.3" }
 jrsonnet-stdlib = { path = "../jrsonnet-stdlib", version = "0.3.3" }
+jrsonnet-types = { path = "../jrsonnet-types", version = "0.3.3" }
 pathdiff = "0.2.0"
 
 closure = "0.3.0"
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/builtin/mod.rs
1use crate::{2	equals,3	error::{Error::*, Result},4	evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,5	ValType,6};7use format::{format_arr, format_obj};8use jrsonnet_parser::{ArgsDesc, ExprLocation};9use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};10use std::{path::PathBuf, rc::Rc};1112pub mod stdlib;13pub use stdlib::*;1415pub mod format;16pub mod manifest;17pub mod sort;1819fn std_format(str: Rc<str>, vals: Val) -> Result<Val> {20	push(21		&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),22		|| format!("std.format of {}", str),23		|| {24			Ok(match vals {25				Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),26				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),27				o => Val::Str(format_arr(&str, &[o])?.into()),28			})29		},30	)31}3233#[allow(clippy::cognitive_complexity)]34pub fn call_builtin(35	context: Context,36	loc: &Option<ExprLocation>,37	name: &str,38	args: &ArgsDesc,39) -> Result<Val> {40	Ok(match name as &str {41		// arr/string/function42		"length" => parse_args!(context, "std.length", args, 1, [43			0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];44		], {45			Ok(match x {46				Val::Str(n) => Val::Num(n.chars().count() as f64),47				Val::Arr(i) => Val::Num(i.len() as f64),48				Val::Obj(o) => Val::Num(49					o.fields_visibility()50						.into_iter()51						.filter(|(_k, v)| *v)52						.count() as f64,53				),54				_ => unreachable!(),55			})56		})?,57		// any58		"type" => parse_args!(context, "std.type", args, 1, [59			0, x, vec![];60		], {61			Ok(Val::Str(x.value_type()?.name().into()))62		})?,63		// length, idx=>any64		"makeArray" => parse_args!(context, "std.makeArray", args, 2, [65			0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];66			1, func: [Val::Func]!!Val::Func, vec![ValType::Func];67		], {68			if sz < 0.0 {69				throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));70			}71			let mut out = Vec::with_capacity(sz as usize);72			for i in 0..sz as usize {73				out.push(func.evaluate_values(74					Context::new(),75					&[Val::Num(i as f64)]76				)?)77			}78			Ok(Val::Arr(Rc::new(out)))79		})?,80		// string81		"codepoint" => parse_args!(context, "std.codepoint", args, 1, [82			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];83		], {84			assert!(85				str.chars().count() == 1,86				"std.codepoint should receive single char string"87			);88			Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))89		})?,90		// object, includeHidden91		"objectFieldsEx" => parse_args!(context, "std.objectFieldsEx",args, 2, [92			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];93			1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];94		], {95			let mut out = obj.fields_visibility()96				.into_iter()97				.filter(|(_k, v)| *v || inc_hidden)98				.map(|(k, _v)|k)99				.collect::<Vec<_>>();100			out.sort();101			Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))102		})?,103		// object, field, includeHidden104		"objectHasEx" => parse_args!(context, "std.objectHasEx", args, 3, [105			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];106			1, f: [Val::Str]!!Val::Str, vec![ValType::Str];107			2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];108		], {109			Ok(Val::Bool(110				obj.fields_visibility()111					.into_iter()112					.filter(|(_k, v)| *v || inc_hidden)113					.any(|(k, _v)| *k == *f),114			))115		})?,116117		// faster118		"slice" => parse_args!(context, "slice", args, 4, [119			0, indexable: [Val::Str | Val::Arr], vec![ValType::Str, ValType::Arr];120			1, index, vec![ValType::Num, ValType::Null];121			2, end, vec![ValType::Num, ValType::Null];122			3, step, vec![ValType::Num, ValType::Null];123		], {124			let index = match index {125				Val::Num(v) => v as usize,126				Val::Null => 0,127				_ => unreachable!(),128			};129			let end = match end {130				Val::Num(v) => v as usize,131				Val::Null => match &indexable {132					Val::Str(s) => s.chars().count(),133					Val::Arr(v) => v.len(),134					_ => unreachable!()135				},136				_ => unreachable!()137			};138			let step = match step {139				Val::Num(v) => v as usize,140				Val::Null => 1,141				_ => unreachable!()142			};143			match &indexable {144				Val::Str(s) => {145					Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))146				}147				Val::Arr(arr) => {148					Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).cloned().collect::<Vec<Val>>()).into()))149				}150				_ => unreachable!()151			}152		})?,153		"primitiveEquals" => parse_args!(context, "std.primitiveEquals", args, 2, [154			0, a, vec![];155			1, b, vec![];156		], {157			Ok(Val::Bool(primitive_equals(&a, &b)?))158		})?,159		// faster160		"equals" => parse_args!(context, "std.equals", args, 2, [161			0, a, vec![];162			1, b, vec![];163		], {164			Ok(Val::Bool(equals(&a, &b)?))165		})?,166		"mod" => parse_args!(context, "std.mod", args, 2, [167			0, a: [Val::Num | Val::Str], vec![ValType::Num, ValType::Str];168			1, b, vec![];169		], {170			match (a, b) {171				(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),172				(Val::Str(str), vals) => std_format(str, vals),173				(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(jrsonnet_parser::BinaryOpType::Mod, a.value_type()?, b.value_type()?))174			}175		})?,176		"modulo" => parse_args!(context, "std.modulo", args, 2, [177			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];178			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];179		], {180			Ok(Val::Num(a % b))181		})?,182		"floor" => parse_args!(context, "std.floor", args, 1, [183			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];184		], {185			Ok(Val::Num(x.floor()))186		})?,187		"log" => parse_args!(context, "std.log", args, 2, [188			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];189		], {190			Ok(Val::Num(n.ln()))191		})?,192		"trace" => parse_args!(context, "std.trace", args, 2, [193			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];194			1, rest, vec![];195		], {196			eprint!("TRACE:");197			if let Some(loc) = loc {198				with_state(|s|{199					let locs = s.map_source_locations(&loc.0, &[loc.1]);200					eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);201				});202			}203			eprintln!(" {}", str);204			Ok(rest)205		})?,206		"pow" => parse_args!(context, "std.modulo", args, 2, [207			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];208			1, n: [Val::Num]!!Val::Num, vec![ValType::Num];209		], {210			Ok(Val::Num(x.powf(n)))211		})?,212		"extVar" => parse_args!(context, "std.extVar", args, 1, [213			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];214		], {215			Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)216		})?,217		"native" => parse_args!(context, "std.native", args, 1, [218			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];219		], {220			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))?)221		})?,222		"filter" => parse_args!(context, "std.filter", args, 2, [223			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];224			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];225		], {226			Ok(Val::Arr(Rc::new(227				arr.iter()228					.cloned()229					.filter(|e| {230						func231							.evaluate_values(context.clone(), &[e.clone()])232							.unwrap()233							.try_cast_bool("filter predicate")234							.unwrap()235					})236					.collect(),237			)))238		})?,239		// faster240		"foldl" => parse_args!(context, "std.foldl", args, 3, [241			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];242			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];243			2, init, vec![];244		], {245			let mut acc = init;246			for i in arr.iter().cloned() {247				acc = func.evaluate_values(context.clone(), &[acc, i])?;248			}249			Ok(acc)250		})?,251		// faster252		"foldr" => parse_args!(context, "std.foldr", args, 3, [253			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];254			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];255			2, init, vec![];256		], {257			let mut acc = init;258			for i in arr.iter().rev().cloned() {259				acc = func.evaluate_values(context.clone(), &[acc, i])?;260			}261			Ok(acc)262		})?,263		// faster264		#[allow(non_snake_case)]265		"sortImpl" => parse_args!(context, "std.sort", args, 2, [266			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];267			1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];268		], {269			if arr.len() <= 1 {270				return Ok(Val::Arr(arr))271			}272			Ok(Val::Arr(sort::sort(context, arr, &keyF)?))273		})?,274		// faster275		"format" => parse_args!(context, "std.format", args, 2, [276			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];277			1, vals, vec![]278		], {279			std_format(str, vals)280		})?,281		// faster282		"range" => parse_args!(context, "std.range", args, 2, [283			0, from: [Val::Num]!!Val::Num, vec![ValType::Num];284			1, to: [Val::Num]!!Val::Num, vec![ValType::Num];285		], {286			let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));287			for i in from as usize..=to as usize {288				out.push(Val::Num(i as f64));289			}290			Ok(Val::Arr(Rc::new(out)))291		})?,292		"char" => parse_args!(context, "std.char", args, 1, [293			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];294		], {295			let mut out = String::new();296			out.push(std::char::from_u32(n as u32).ok_or_else(||297				InvalidUnicodeCodepointGot(n as u32)298			)?);299			Ok(Val::Str(out.into()))300		})?,301		"encodeUTF8" => parse_args!(context, "std.encodeUtf8", args, 1, [302			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];303		], {304			Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))305		})?,306		"md5" => parse_args!(context, "std.md5", args, 1, [307			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];308		], {309			Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))310		})?,311		// faster312		"base64" => parse_args!(context, "std.base64", args, 1, [313			0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];314		], {315			Ok(Val::Str(match input {316				Val::Str(s) => {317					base64::encode(s.bytes().collect::<Vec<_>>()).into()318				},319				Val::Arr(a) => {320					base64::encode(a.iter().map(|v| {321						Ok(v.clone().try_cast_num("base64 array")? as u8)322					}).collect::<Result<Vec<_>>>()?).into()323				},324				_ => unreachable!()325			}))326		})?,327		// faster328		"join" => parse_args!(context, "std.join", args, 2, [329			0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];330			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];331		], {332			Ok(match sep {333				Val::Arr(joiner_items) => {334					let mut out = Vec::new();335336					let mut first = true;337					for item in arr.iter().cloned() {338						if let Val::Arr(items) = item.unwrap_if_lazy()? {339							if !first {340								out.reserve(joiner_items.len());341								out.extend(joiner_items.iter().cloned());342							}343							first = false;344							out.reserve(items.len());345							out.extend(items.iter().cloned());346						} else {347							throw!(RuntimeError("in std.join all items should be arrays".into()));348						}349					}350351					Val::Arr(Rc::new(out))352				},353				Val::Str(sep) => {354					let mut out = String::new();355356					let mut first = true;357					for item in arr.iter().cloned() {358						if let Val::Str(item) = item.unwrap_if_lazy()? {359							if !first {360								out += &sep;361							}362							first = false;363							out += &item;364						} else {365							throw!(RuntimeError("in std.join all items should be strings".into()));366						}367					}368369					Val::Str(out.into())370				},371				_ => unreachable!()372			})373		})?,374		// Faster375		"escapeStringJson" => parse_args!(context, "std.escapeStringJson", args, 1, [376			0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];377		], {378			Ok(Val::Str(escape_string_json(&str_).into()))379		})?,380		// Faster381		"manifestJsonEx" => parse_args!(context, "std.manifestJsonEx", args, 2, [382			0, value, vec![];383			1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];384		], {385			Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {386				padding: &indent,387				mtype: ManifestType::Std,388			})?.into()))389		})?,390		// Faster391		"reverse" => parse_args!(context, "std.reverse", args, 1, [392			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];393		], {394			let mut marr = arr;395			Rc::make_mut(&mut marr).reverse();396			Ok(Val::Arr(marr))397		})?,398		"id" => parse_args!(context, "std.id", args, 1, [399			0, v, vec![];400		], {401			Ok(v)402		})?,403		name => throw!(IntrinsicNotFound(name.into())),404	})405}
after · crates/jrsonnet-evaluator/src/builtin/mod.rs
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, ComplexValType, ValType};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}3536thread_local! {37	pub static INTRINSICS: HashMap<&'static str, fn(Context, &Option<ExprLocation>, &ArgsDesc) -> Result<Val>> = {38		let mut out: HashMap<&'static str, _> = HashMap::new();39		out.insert("length", intrinsic_length);40		out41	};42}4344fn intrinsic_length(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {45	Ok(parse_args!(context, "length", args, 1, [46		0, x: ty!((str | obj | [any]));47	], {48		Ok(match x {49			Val::Str(n) => Val::Num(n.chars().count() as f64),50			Val::Arr(a) => Val::Num(a.len() as f64),51			Val::Obj(o) => Val::Num(52				o.fields_visibility()53					.into_iter()54					.filter(|(_k, v)| *v)55					.count() as f64,56			),57			_ => unreachable!(),58		})59	})?)60}6162#[allow(clippy::cognitive_complexity)]63pub fn call_builtin(64	context: Context,65	loc: &Option<ExprLocation>,66	name: &str,67	args: &ArgsDesc,68) -> Result<Val> {69	Ok(match name as &str {70		"length" => parse_args!(context, "length", args, 1, [71			0, x: ty!((str | obj | [any]));72		], {73			Ok(match x {74				Val::Str(n) => Val::Num(n.chars().count() as f64),75				Val::Arr(a) => Val::Num(a.len() as f64),76				Val::Obj(o) => Val::Num(77					o.fields_visibility()78						.into_iter()79						.filter(|(_k, v)| *v)80						.count() as f64,81				),82				_ => unreachable!(),83			})84		})?,85		"type" => parse_args!(context, "type", args, 1, [86			0, x: ty!(any);87		], {88			Ok(Val::Str(x.value_type().name().into()))89		})?,90		"makeArray" => parse_args!(context, "makeArray", args, 2, [91			0, sz: ty!(number((Some(0.0))..(None))) => Val::Num;92			1, func: ty!(fn.any) => Val::Func;93		], {94			let mut out = Vec::with_capacity(sz as usize);95			for i in 0..sz as usize {96				out.push(LazyVal::new_resolved(func.evaluate_values(97					Context::new(),98					&[Val::Num(i as f64)]99				)?))100			}101			Ok(Val::Arr(out.into()))102		})?,103		"codepoint" => parse_args!(context, "codepoint", args, 1, [104			0, str: ty!(char) => Val::Str;105		], {106			Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))107		})?,108		"objectFieldsEx" => parse_args!(context, "objectFieldsEx", args, 2, [109			0, obj: ty!(obj) => Val::Obj;110			1, inc_hidden: ty!(bool) => Val::Bool;111		], {112			let mut out = obj.fields_visibility()113				.into_iter()114				.filter(|(_k, v)| *v || inc_hidden)115				.map(|(k, _v)|k)116				.collect::<Vec<_>>();117			out.sort();118			Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))119		})?,120		"objectHasEx" => parse_args!(context, "objectHasEx", args, 3, [121			0, obj: ty!(obj) => Val::Obj;122			1, f: ty!(str) => Val::Str;123			2, inc_hidden: ty!(bool) => Val::Bool;124		], {125			Ok(Val::Bool(126				obj.fields_visibility()127					.into_iter()128					.filter(|(_k, v)| *v || inc_hidden)129					.any(|(k, _v)| *k == *f),130			))131		})?,132		// faster133		"slice" => parse_args!(context, "slice", args, 4, [134			0, indexable: ty!((str | [any]));135			1, index: ty!((num | null));136			2, end: ty!((num | null));137			3, step: ty!((num | null));138		], {139			let index = match index {140				Val::Num(v) => v as usize,141				Val::Null => 0,142				_ => unreachable!(),143			};144			let end = match end {145				Val::Num(v) => v as usize,146				Val::Null => match &indexable {147					Val::Str(s) => s.chars().count(),148					Val::Arr(v) => v.len(),149					_ => unreachable!()150				},151				_ => unreachable!()152			};153			let step = match step {154				Val::Num(v) => v as usize,155				Val::Null => 1,156				_ => unreachable!()157			};158			match &indexable {159				Val::Str(s) => {160					Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))161				}162				Val::Arr(arr) => {163					Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))164				}165				_ => unreachable!()166			}167		})?,168		"primitiveEquals" => parse_args!(context, "primitiveEquals", args, 2, [169			0, a: ty!(any);170			1, b: ty!(any);171		], {172			Ok(Val::Bool(primitive_equals(&a, &b)?))173		})?,174		// faster175		"equals" => parse_args!(context, "equals", args, 2, [176			0, a: ty!(any);177			1, b: ty!(any);178		], {179			Ok(Val::Bool(equals(&a, &b)?))180		})?,181		"modulo" => parse_args!(context, "modulo", args, 2, [182			0, a: ty!(num) => Val::Num;183			1, b: ty!(num) => Val::Num;184		], {185			Ok(Val::Num(a % b))186		})?,187		"mod" => parse_args!(context, "mod", args, 2, [188			0, a: ty!((num | str));189			1, b: ty!(any);190		], {191			match (a, b) {192				(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),193				(Val::Str(str), vals) => std_format(str, vals),194				(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))195			}196		})?,197		"floor" => parse_args!(context, "floor", args, 1, [198			0, x: ty!(num) => Val::Num;199		], {200			Ok(Val::Num(x.floor()))201		})?,202		"log" => parse_args!(context, "log", args, 1, [203			0, n: ty!(num) => Val::Num;204		], {205			Ok(Val::Num(n.ln()))206		})?,207		"trace" => parse_args!(context, "trace", args, 2, [208			0, str: ty!(str) => Val::Str;209			1, rest: ty!(any);210		], {211			eprint!("TRACE:");212			if let Some(loc) = loc {213				with_state(|s|{214					let locs = s.map_source_locations(&loc.0, &[loc.1]);215					eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);216				});217			}218			eprintln!(" {}", str);219			Ok(rest)220		})?,221		"pow" => parse_args!(context, "pow", args, 2, [222			0, x: ty!(num) => Val::Num;223			1, n: ty!(num) => Val::Num;224		], {225			Ok(Val::Num(x.powf(n)))226		})?,227		"extVar" => parse_args!(context, "extVar", args, 1, [228			0, x: ty!(str) => Val::Str;229		], {230			Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)231		})?,232		"native" => parse_args!(context, "native", args, 1, [233			0, x: ty!(str) => Val::Str;234		], {235			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))?)236		})?,237		"filter" => parse_args!(context, "filter", args, 2, [238			0, func: ty!(fn.any) => Val::Func;239			1, arr: ty!([any]) => Val::Arr;240		], {241			let mut out = Vec::new();242			for item in arr.iter() {243				let item = item?;244				if func245							.evaluate_values(context.clone(), &[item.clone()])?246							.try_cast_bool("filter predicate")? {247								out.push(item);248							}249			}250			Ok(Val::Arr(out.into()))251		})?,252		"foldl" => parse_args!(context, "foldl", args, 3, [253			0, func: ty!(fn.any) => Val::Func;254			1, arr: ty!([any]) => Val::Arr;255			2, init: ty!(any);256		], {257			let mut acc = init;258			for i in arr.iter() {259				acc = func.evaluate_values(context.clone(), &[acc, i?])?;260			}261			Ok(acc)262		})?,263		"foldr" => parse_args!(context, "foldr", args, 3, [264			0, func: ty!(fn.any) => Val::Func;265			1, arr: ty!([any]) => Val::Arr;266			2, init: ty!(any);267		], {268			let mut acc = init;269			for i in arr.iter().rev() {270				acc = func.evaluate_values(context.clone(), &[acc, i?])?;271			}272			Ok(acc)273		})?,274		#[allow(non_snake_case)]275		"sortImpl" => parse_args!(context, "sort", args, 2, [276			0, arr: ty!([any]) => Val::Arr;277			1, keyF: ty!(fn.any) => Val::Func;278		], {279			if arr.len() <= 1 {280				return Ok(Val::Arr(arr))281			}282			Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))283		})?,284		// faster285		"format" => parse_args!(context, "format", args, 2, [286			0, str: ty!(str) => Val::Str;287			1, vals: ty!(any)288		], {289			std_format(str, vals)290		})?,291		"range" => parse_args!(context, "range", args, 2, [292			0, from: ty!(num) => Val::Num;293			1, to: ty!(num) => Val::Num;294		], {295			let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));296			for i in from as usize..=to as usize {297				out.push(Val::Num(i as f64));298			}299			Ok(Val::Arr(out.into()))300		})?,301		"char" => parse_args!(context, "char", args, 1, [302			0, n: ty!(num) => Val::Num;303		], {304			let mut out = String::new();305			out.push(std::char::from_u32(n as u32).ok_or_else(||306				InvalidUnicodeCodepointGot(n as u32)307			)?);308			Ok(Val::Str(out.into()))309		})?,310		"encodeUTF8" => parse_args!(context, "encodeUTF8", args, 1, [311			0, str: ty!(str) => Val::Str;312		], {313			Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))314		})?,315		"md5" => parse_args!(context, "md5", args, 1, [316			0, str: ty!(str) => Val::Str;317		], {318			Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))319		})?,320		"base64" => parse_args!(context, "base64", args, 1, [321			0, input: ty!((str | [num]));322		], {323			Ok(Val::Str(match input {324				Val::Str(s) => {325					base64::encode(s.bytes().collect::<Vec<_>>()).into()326				},327				Val::Arr(a) => {328					base64::encode(a.iter().map(|v| {329						Ok(v?.clone().unwrap_num()? as u8)330					}).collect::<Result<Vec<_>>>()?).into()331				},332				_ => unreachable!()333			}))334		})?,335		"join" => parse_args!(context, "join", args, 2, [336			0, sep: ty!((str | [any]));337			1, arr: ty!([any]) => Val::Arr;338		], {339			Ok(match sep {340				Val::Arr(joiner_items) => {341					let mut out = Vec::new();342343					let mut first = true;344					for item in arr.iter() {345						let item = item?.clone();346						if let Val::Arr(items) = item {347							if !first {348								out.reserve(joiner_items.len());349								// TODO: extend350								for item in joiner_items.iter() {351									out.push(item?);352								}353							}354							first = false;355							out.reserve(items.len());356							// TODO: extend357							for item in items.iter() {358								out.push(item?);359							}360						} else {361							throw!(RuntimeError("in std.join all items should be arrays".into()));362						}363					}364365					Val::Arr(out.into())366				},367				Val::Str(sep) => {368					let mut out = String::new();369370					let mut first = true;371					for item in arr.iter() {372						let item = item?.clone();373						if let Val::Str(item) = item {374							if !first {375								out += &sep;376							}377							first = false;378							out += &item;379						} else {380							throw!(RuntimeError("in std.join all items should be strings".into()));381						}382					}383384					Val::Str(out.into())385				},386				_ => unreachable!()387			})388		})?,389		// faster390		"escapeStringJson" => parse_args!(context, "escapeStringJson", args, 1, [391			0, str_: ty!(str) => Val::Str;392		], {393			Ok(Val::Str(escape_string_json(&str_).into()))394		})?,395		// faster396		"manifestJsonEx" => parse_args!(context, "manifestJsonEx", args, 2, [397			0, value: ty!(any);398			1, indent: ty!(str) => Val::Str;399		], {400			Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {401				padding: &indent,402				mtype: ManifestType::Std,403			})?.into()))404		})?,405		// faster406		"reverse" => parse_args!(context, "reverse", args, 1, [407			0, value: ty!([any]) => Val::Arr;408		], {409			Ok(Val::Arr(value.reversed()))410		})?,411		"id" => parse_args!(context, "id", args, 1, [412			0, v: ty!(any);413		], {414			Ok(v)415		})?,416		name => throw!(IntrinsicNotFound(name.into())),417	})418}
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,8 +1,9 @@
 use crate::{
 	builtin::{format::FormatError, sort::SortError},
-	ValType,
+	typed::TypeLocError,
 };
 use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
+use jrsonnet_types::ValType;
 use std::{path::PathBuf, rc::Rc};
 use thiserror::Error;
 
@@ -117,6 +118,8 @@
 
 	#[error("format error: {0}")]
 	Format(#[from] FormatError),
+	#[error("type error: {0}")]
+	TypeError(TypeLocError),
 	#[error("sort error: {0}")]
 	Sort(#[from] SortError),
 }
@@ -144,6 +147,9 @@
 	pub const fn error(&self) -> &Error {
 		&(self.0).0
 	}
+	pub fn error_mut(&mut self) -> &mut Error {
+		&mut (self.0).0
+	}
 	pub const fn trace(&self) -> &StackTrace {
 		&(self.0).1
 	}
modifiedcrates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -143,9 +143,8 @@
 #[macro_export]
 macro_rules! parse_args {
 	($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [
-		$($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)?
+		$($id: expr, $name: ident: $ty: expr $(=>$match: path)?);+ $(;)?
 	], $handler:block) => {{
-		use crate::{throw, error::Error::*};
 		let args = $args;
 		if args.len() > $total_args {
 			throw!(TooManyArgsFunctionHas($total_args));
@@ -160,47 +159,19 @@
 					throw!(IntrinsicArgumentReorderingIsNotSupportedYet);
 				}
 			}
-			let $name = evaluate($ctx.clone(), &$name.1)?;
+			let $name = push(&None, || format!("evaluating argument"), || {
+				let value = evaluate($ctx.clone(), &$name.1)?;
+				$ty.check(&value)?;
+				Ok(value)
+			})?;
 			$(
-				match $name {
-					$($p(_))|+ => {},
-					_ => throw!(TypeMismatch(
-						concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"),
-						$nt, $name.value_type()?
-					)),
+				let $name = if let $match(v) = $name {
+					v
+				} else {
+					unreachable!();
 				};
-				$(
-					let $name = match $name {
-						$a(v) => v,
-						_ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)),
-					};
-				)*
-			)*
+			)?
 		)+
 		($handler as crate::Result<_>)
 	}};
-}
-
-#[test]
-fn test() -> Result<()> {
-	use crate::val::ValType;
-	use jrsonnet_parser::*;
-	let state = crate::EvaluationState::default();
-	let evaluator = state.with_stdlib();
-	let ctx = evaluator.create_default_context()?;
-	evaluator.run_in_state(|| {
-		parse_args!(ctx, "test", ArgsDesc(vec![
-			Arg(None, el!(Expr::Num(2.0))),
-			Arg(Some("b".into()), el!(Expr::Num(1.0))),
-		]), 2, [
-			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];
-			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];
-		], {
-			assert!((a - 2.0).abs() <= f64::EPSILON);
-			assert!((b - 1.0).abs() <= f64::EPSILON);
-			Ok(())
-		})
-		.unwrap();
-		Ok(())
-	})
 }
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -14,6 +14,7 @@
 pub mod native;
 mod obj;
 pub mod trace;
+mod typed;
 mod val;
 
 pub use ctx::*;
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -65,7 +65,7 @@
 			out,
 			"{}:{}-{}:{}",
 			start.line,
-			end.column - 1,
+			end.column.saturating_sub(1),
 			start.line,
 			end.column
 		)?;