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
--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -1,17 +1,20 @@
 use crate::{
 	equals,
 	error::{Error::*, Result},
-	evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,
-	ValType,
+	evaluate, parse_args, primitive_equals, push, throw,
+	typed::CheckType,
+	with_state, ArrValue, Context, FuncVal, LazyVal, Val,
 };
 use format::{format_arr, format_obj};
-use jrsonnet_parser::{ArgsDesc, ExprLocation};
-use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};
-use std::{path::PathBuf, rc::Rc};
+use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};
+use jrsonnet_types::{ty, ComplexValType, ValType};
+use std::{collections::HashMap, path::PathBuf, rc::Rc};
 
 pub mod stdlib;
 pub use stdlib::*;
 
+use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};
+
 pub mod format;
 pub mod manifest;
 pub mod sort;
@@ -22,7 +25,7 @@
 		|| format!("std.format of {}", str),
 		|| {
 			Ok(match vals {
-				Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),
+				Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),
 				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),
 				o => Val::Str(format_arr(&str, &[o])?.into()),
 			})
@@ -30,6 +33,32 @@
 	)
 }
 
+thread_local! {
+	pub static INTRINSICS: HashMap<&'static str, fn(Context, &Option<ExprLocation>, &ArgsDesc) -> Result<Val>> = {
+		let mut out: HashMap<&'static str, _> = HashMap::new();
+		out.insert("length", intrinsic_length);
+		out
+	};
+}
+
+fn intrinsic_length(context: Context, _loc: &Option<ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+	Ok(parse_args!(context, "length", args, 1, [
+		0, x: ty!((str | obj | [any]));
+	], {
+		Ok(match x {
+			Val::Str(n) => Val::Num(n.chars().count() as f64),
+			Val::Arr(a) => Val::Num(a.len() as f64),
+			Val::Obj(o) => Val::Num(
+				o.fields_visibility()
+					.into_iter()
+					.filter(|(_k, v)| *v)
+					.count() as f64,
+			),
+			_ => unreachable!(),
+		})
+	})?)
+}
+
 #[allow(clippy::cognitive_complexity)]
 pub fn call_builtin(
 	context: Context,
@@ -38,13 +67,12 @@
 	args: &ArgsDesc,
 ) -> Result<Val> {
 	Ok(match name as &str {
-		// arr/string/function
-		"length" => parse_args!(context, "std.length", args, 1, [
-			0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];
+		"length" => parse_args!(context, "length", args, 1, [
+			0, x: ty!((str | obj | [any]));
 		], {
 			Ok(match x {
 				Val::Str(n) => Val::Num(n.chars().count() as f64),
-				Val::Arr(i) => Val::Num(i.len() as f64),
+				Val::Arr(a) => Val::Num(a.len() as f64),
 				Val::Obj(o) => Val::Num(
 					o.fields_visibility()
 						.into_iter()
@@ -54,43 +82,32 @@
 				_ => unreachable!(),
 			})
 		})?,
-		// any
-		"type" => parse_args!(context, "std.type", args, 1, [
-			0, x, vec![];
+		"type" => parse_args!(context, "type", args, 1, [
+			0, x: ty!(any);
 		], {
-			Ok(Val::Str(x.value_type()?.name().into()))
+			Ok(Val::Str(x.value_type().name().into()))
 		})?,
-		// length, idx=>any
-		"makeArray" => parse_args!(context, "std.makeArray", args, 2, [
-			0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];
-			1, func: [Val::Func]!!Val::Func, vec![ValType::Func];
+		"makeArray" => parse_args!(context, "makeArray", args, 2, [
+			0, sz: ty!(number((Some(0.0))..(None))) => Val::Num;
+			1, func: ty!(fn.any) => Val::Func;
 		], {
-			if sz < 0.0 {
-				throw!(RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()));
-			}
 			let mut out = Vec::with_capacity(sz as usize);
 			for i in 0..sz as usize {
-				out.push(func.evaluate_values(
+				out.push(LazyVal::new_resolved(func.evaluate_values(
 					Context::new(),
 					&[Val::Num(i as f64)]
-				)?)
+				)?))
 			}
-			Ok(Val::Arr(Rc::new(out)))
+			Ok(Val::Arr(out.into()))
 		})?,
-		// string
-		"codepoint" => parse_args!(context, "std.codepoint", args, 1, [
-			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+		"codepoint" => parse_args!(context, "codepoint", args, 1, [
+			0, str: ty!(char) => Val::Str;
 		], {
-			assert!(
-				str.chars().count() == 1,
-				"std.codepoint should receive single char string"
-			);
 			Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))
 		})?,
-		// object, includeHidden
-		"objectFieldsEx" => parse_args!(context, "std.objectFieldsEx",args, 2, [
-			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
-			1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
+		"objectFieldsEx" => parse_args!(context, "objectFieldsEx", args, 2, [
+			0, obj: ty!(obj) => Val::Obj;
+			1, inc_hidden: ty!(bool) => Val::Bool;
 		], {
 			let mut out = obj.fields_visibility()
 				.into_iter()
@@ -98,13 +115,12 @@
 				.map(|(k, _v)|k)
 				.collect::<Vec<_>>();
 			out.sort();
-			Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))
+			Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))
 		})?,
-		// object, field, includeHidden
-		"objectHasEx" => parse_args!(context, "std.objectHasEx", args, 3, [
-			0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];
-			1, f: [Val::Str]!!Val::Str, vec![ValType::Str];
-			2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];
+		"objectHasEx" => parse_args!(context, "objectHasEx", args, 3, [
+			0, obj: ty!(obj) => Val::Obj;
+			1, f: ty!(str) => Val::Str;
+			2, inc_hidden: ty!(bool) => Val::Bool;
 		], {
 			Ok(Val::Bool(
 				obj.fields_visibility()
@@ -113,13 +129,12 @@
 					.any(|(k, _v)| *k == *f),
 			))
 		})?,
-
 		// faster
 		"slice" => parse_args!(context, "slice", args, 4, [
-			0, indexable: [Val::Str | Val::Arr], vec![ValType::Str, ValType::Arr];
-			1, index, vec![ValType::Num, ValType::Null];
-			2, end, vec![ValType::Num, ValType::Null];
-			3, step, vec![ValType::Num, ValType::Null];
+			0, indexable: ty!((str | [any]));
+			1, index: ty!((num | null));
+			2, end: ty!((num | null));
+			3, step: ty!((num | null));
 		], {
 			let index = match index {
 				Val::Num(v) => v as usize,
@@ -145,53 +160,53 @@
 					Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))
 				}
 				Val::Arr(arr) => {
-					Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).cloned().collect::<Vec<Val>>()).into()))
+					Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))
 				}
 				_ => unreachable!()
 			}
 		})?,
-		"primitiveEquals" => parse_args!(context, "std.primitiveEquals", args, 2, [
-			0, a, vec![];
-			1, b, vec![];
+		"primitiveEquals" => parse_args!(context, "primitiveEquals", args, 2, [
+			0, a: ty!(any);
+			1, b: ty!(any);
 		], {
 			Ok(Val::Bool(primitive_equals(&a, &b)?))
 		})?,
 		// faster
-		"equals" => parse_args!(context, "std.equals", args, 2, [
-			0, a, vec![];
-			1, b, vec![];
+		"equals" => parse_args!(context, "equals", args, 2, [
+			0, a: ty!(any);
+			1, b: ty!(any);
 		], {
 			Ok(Val::Bool(equals(&a, &b)?))
 		})?,
-		"mod" => parse_args!(context, "std.mod", args, 2, [
-			0, a: [Val::Num | Val::Str], vec![ValType::Num, ValType::Str];
-			1, b, vec![];
+		"modulo" => parse_args!(context, "modulo", args, 2, [
+			0, a: ty!(num) => Val::Num;
+			1, b: ty!(num) => Val::Num;
+		], {
+			Ok(Val::Num(a % b))
+		})?,
+		"mod" => parse_args!(context, "mod", args, 2, [
+			0, a: ty!((num | str));
+			1, b: ty!(any);
 		], {
 			match (a, b) {
 				(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),
 				(Val::Str(str), vals) => std_format(str, vals),
-				(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(jrsonnet_parser::BinaryOpType::Mod, a.value_type()?, b.value_type()?))
+				(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))
 			}
 		})?,
-		"modulo" => parse_args!(context, "std.modulo", args, 2, [
-			0, a: [Val::Num]!!Val::Num, vec![ValType::Num];
-			1, b: [Val::Num]!!Val::Num, vec![ValType::Num];
-		], {
-			Ok(Val::Num(a % b))
-		})?,
-		"floor" => parse_args!(context, "std.floor", args, 1, [
-			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
+		"floor" => parse_args!(context, "floor", args, 1, [
+			0, x: ty!(num) => Val::Num;
 		], {
 			Ok(Val::Num(x.floor()))
 		})?,
-		"log" => parse_args!(context, "std.log", args, 2, [
-			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
+		"log" => parse_args!(context, "log", args, 1, [
+			0, n: ty!(num) => Val::Num;
 		], {
 			Ok(Val::Num(n.ln()))
 		})?,
-		"trace" => parse_args!(context, "std.trace", args, 2, [
-			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
-			1, rest, vec![];
+		"trace" => parse_args!(context, "trace", args, 2, [
+			0, str: ty!(str) => Val::Str;
+			1, rest: ty!(any);
 		], {
 			eprint!("TRACE:");
 			if let Some(loc) = loc {
@@ -203,94 +218,88 @@
 			eprintln!(" {}", str);
 			Ok(rest)
 		})?,
-		"pow" => parse_args!(context, "std.modulo", args, 2, [
-			0, x: [Val::Num]!!Val::Num, vec![ValType::Num];
-			1, n: [Val::Num]!!Val::Num, vec![ValType::Num];
+		"pow" => parse_args!(context, "pow", args, 2, [
+			0, x: ty!(num) => Val::Num;
+			1, n: ty!(num) => Val::Num;
 		], {
 			Ok(Val::Num(x.powf(n)))
 		})?,
-		"extVar" => parse_args!(context, "std.extVar", args, 1, [
-			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
+		"extVar" => parse_args!(context, "extVar", args, 1, [
+			0, x: ty!(str) => Val::Str;
 		], {
 			Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)
 		})?,
-		"native" => parse_args!(context, "std.native", args, 1, [
-			0, x: [Val::Str]!!Val::Str, vec![ValType::Str];
+		"native" => parse_args!(context, "native", args, 1, [
+			0, x: ty!(str) => Val::Str;
 		], {
 			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))?)
 		})?,
-		"filter" => parse_args!(context, "std.filter", args, 2, [
-			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
-			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+		"filter" => parse_args!(context, "filter", args, 2, [
+			0, func: ty!(fn.any) => Val::Func;
+			1, arr: ty!([any]) => Val::Arr;
 		], {
-			Ok(Val::Arr(Rc::new(
-				arr.iter()
-					.cloned()
-					.filter(|e| {
-						func
-							.evaluate_values(context.clone(), &[e.clone()])
-							.unwrap()
-							.try_cast_bool("filter predicate")
-							.unwrap()
-					})
-					.collect(),
-			)))
+			let mut out = Vec::new();
+			for item in arr.iter() {
+				let item = item?;
+				if func
+							.evaluate_values(context.clone(), &[item.clone()])?
+							.try_cast_bool("filter predicate")? {
+								out.push(item);
+							}
+			}
+			Ok(Val::Arr(out.into()))
 		})?,
-		// faster
-		"foldl" => parse_args!(context, "std.foldl", args, 3, [
-			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
-			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
-			2, init, vec![];
+		"foldl" => parse_args!(context, "foldl", args, 3, [
+			0, func: ty!(fn.any) => Val::Func;
+			1, arr: ty!([any]) => Val::Arr;
+			2, init: ty!(any);
 		], {
 			let mut acc = init;
-			for i in arr.iter().cloned() {
-				acc = func.evaluate_values(context.clone(), &[acc, i])?;
+			for i in arr.iter() {
+				acc = func.evaluate_values(context.clone(), &[acc, i?])?;
 			}
 			Ok(acc)
 		})?,
-		// faster
-		"foldr" => parse_args!(context, "std.foldr", args, 3, [
-			0, func: [Val::Func]!!Val::Func, vec![ValType::Func];
-			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
-			2, init, vec![];
+		"foldr" => parse_args!(context, "foldr", args, 3, [
+			0, func: ty!(fn.any) => Val::Func;
+			1, arr: ty!([any]) => Val::Arr;
+			2, init: ty!(any);
 		], {
 			let mut acc = init;
-			for i in arr.iter().rev().cloned() {
-				acc = func.evaluate_values(context.clone(), &[acc, i])?;
+			for i in arr.iter().rev() {
+				acc = func.evaluate_values(context.clone(), &[acc, i?])?;
 			}
 			Ok(acc)
 		})?,
-		// faster
 		#[allow(non_snake_case)]
-		"sortImpl" => parse_args!(context, "std.sort", args, 2, [
-			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
-			1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];
+		"sortImpl" => parse_args!(context, "sort", args, 2, [
+			0, arr: ty!([any]) => Val::Arr;
+			1, keyF: ty!(fn.any) => Val::Func;
 		], {
 			if arr.len() <= 1 {
 				return Ok(Val::Arr(arr))
 			}
-			Ok(Val::Arr(sort::sort(context, arr, &keyF)?))
+			Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))
 		})?,
 		// faster
-		"format" => parse_args!(context, "std.format", args, 2, [
-			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
-			1, vals, vec![]
+		"format" => parse_args!(context, "format", args, 2, [
+			0, str: ty!(str) => Val::Str;
+			1, vals: ty!(any)
 		], {
 			std_format(str, vals)
 		})?,
-		// faster
-		"range" => parse_args!(context, "std.range", args, 2, [
-			0, from: [Val::Num]!!Val::Num, vec![ValType::Num];
-			1, to: [Val::Num]!!Val::Num, vec![ValType::Num];
+		"range" => parse_args!(context, "range", args, 2, [
+			0, from: ty!(num) => Val::Num;
+			1, to: ty!(num) => Val::Num;
 		], {
 			let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));
 			for i in from as usize..=to as usize {
 				out.push(Val::Num(i as f64));
 			}
-			Ok(Val::Arr(Rc::new(out)))
+			Ok(Val::Arr(out.into()))
 		})?,
-		"char" => parse_args!(context, "std.char", args, 1, [
-			0, n: [Val::Num]!!Val::Num, vec![ValType::Num];
+		"char" => parse_args!(context, "char", args, 1, [
+			0, n: ty!(num) => Val::Num;
 		], {
 			let mut out = String::new();
 			out.push(std::char::from_u32(n as u32).ok_or_else(||
@@ -298,19 +307,18 @@
 			)?);
 			Ok(Val::Str(out.into()))
 		})?,
-		"encodeUTF8" => parse_args!(context, "std.encodeUtf8", args, 1, [
-			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+		"encodeUTF8" => parse_args!(context, "encodeUTF8", args, 1, [
+			0, str: ty!(str) => Val::Str;
 		], {
-			Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))
+			Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))
 		})?,
-		"md5" => parse_args!(context, "std.md5", args, 1, [
-			0, str: [Val::Str]!!Val::Str, vec![ValType::Str];
+		"md5" => parse_args!(context, "md5", args, 1, [
+			0, str: ty!(str) => Val::Str;
 		], {
 			Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))
 		})?,
-		// faster
-		"base64" => parse_args!(context, "std.base64", args, 1, [
-			0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];
+		"base64" => parse_args!(context, "base64", args, 1, [
+			0, input: ty!((str | [num]));
 		], {
 			Ok(Val::Str(match input {
 				Val::Str(s) => {
@@ -318,44 +326,51 @@
 				},
 				Val::Arr(a) => {
 					base64::encode(a.iter().map(|v| {
-						Ok(v.clone().try_cast_num("base64 array")? as u8)
+						Ok(v?.clone().unwrap_num()? as u8)
 					}).collect::<Result<Vec<_>>>()?).into()
 				},
 				_ => unreachable!()
 			}))
 		})?,
-		// faster
-		"join" => parse_args!(context, "std.join", args, 2, [
-			0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];
-			1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+		"join" => parse_args!(context, "join", args, 2, [
+			0, sep: ty!((str | [any]));
+			1, arr: ty!([any]) => Val::Arr;
 		], {
 			Ok(match sep {
 				Val::Arr(joiner_items) => {
 					let mut out = Vec::new();
 
 					let mut first = true;
-					for item in arr.iter().cloned() {
-						if let Val::Arr(items) = item.unwrap_if_lazy()? {
+					for item in arr.iter() {
+						let item = item?.clone();
+						if let Val::Arr(items) = item {
 							if !first {
 								out.reserve(joiner_items.len());
-								out.extend(joiner_items.iter().cloned());
+								// TODO: extend
+								for item in joiner_items.iter() {
+									out.push(item?);
+								}
 							}
 							first = false;
 							out.reserve(items.len());
-							out.extend(items.iter().cloned());
+							// TODO: extend
+							for item in items.iter() {
+								out.push(item?);
+							}
 						} else {
 							throw!(RuntimeError("in std.join all items should be arrays".into()));
 						}
 					}
 
-					Val::Arr(Rc::new(out))
+					Val::Arr(out.into())
 				},
 				Val::Str(sep) => {
 					let mut out = String::new();
 
 					let mut first = true;
-					for item in arr.iter().cloned() {
-						if let Val::Str(item) = item.unwrap_if_lazy()? {
+					for item in arr.iter() {
+						let item = item?.clone();
+						if let Val::Str(item) = item {
 							if !first {
 								out += &sep;
 							}
@@ -371,32 +386,30 @@
 				_ => unreachable!()
 			})
 		})?,
-		// Faster
-		"escapeStringJson" => parse_args!(context, "std.escapeStringJson", args, 1, [
-			0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];
+		// faster
+		"escapeStringJson" => parse_args!(context, "escapeStringJson", args, 1, [
+			0, str_: ty!(str) => Val::Str;
 		], {
 			Ok(Val::Str(escape_string_json(&str_).into()))
 		})?,
-		// Faster
-		"manifestJsonEx" => parse_args!(context, "std.manifestJsonEx", args, 2, [
-			0, value, vec![];
-			1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];
+		// faster
+		"manifestJsonEx" => parse_args!(context, "manifestJsonEx", args, 2, [
+			0, value: ty!(any);
+			1, indent: ty!(str) => Val::Str;
 		], {
 			Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {
 				padding: &indent,
 				mtype: ManifestType::Std,
 			})?.into()))
 		})?,
-		// Faster
-		"reverse" => parse_args!(context, "std.reverse", args, 1, [
-			0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];
+		// faster
+		"reverse" => parse_args!(context, "reverse", args, 1, [
+			0, value: ty!([any]) => Val::Arr;
 		], {
-			let mut marr = arr;
-			Rc::make_mut(&mut marr).reverse();
-			Ok(Val::Arr(marr))
+			Ok(Val::Arr(value.reversed()))
 		})?,
-		"id" => parse_args!(context, "std.id", args, 1, [
-			0, v, vec![];
+		"id" => parse_args!(context, "id", args, 1, [
+			0, v: ty!(any);
 		], {
 			Ok(v)
 		})?,
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
before · crates/jrsonnet-evaluator/src/trace/mod.rs
1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::path::PathBuf;67/// The way paths should be displayed8pub enum PathResolver {9	/// Only filename10	FileName,11	/// Absolute path12	Absolute,13	/// Path relative to base directory14	Relative(PathBuf),15}1617impl PathResolver {18	pub fn resolve(&self, from: &PathBuf) -> String {19		match self {20			Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21			Self::Absolute => from.to_string_lossy().into_owned(),22			Self::Relative(base) => {23				if from.is_relative() {24					return from.to_string_lossy().into_owned();25				}26				pathdiff::diff_paths(from, base)27					.unwrap()28					.to_string_lossy()29					.into_owned()30			}31		}32	}33}3435/// Implements pretty-printing of traces36pub trait TraceFormat {37	fn write_trace(38		&self,39		out: &mut dyn std::fmt::Write,40		evaluation_state: &EvaluationState,41		error: &LocError,42	) -> Result<(), std::fmt::Error>;43	// fn print_trace(44	// 	&self,45	// 	evaluation_state: &EvaluationState,46	// 	error: &LocError,47	// ) -> Result<(), std::fmt::Error> {48	// 	self.write_trace(&mut std::fmt::stdout(), evaluation_state, error)49	// }50}5152fn print_code_location(53	out: &mut impl std::fmt::Write,54	start: &CodeLocation,55	end: &CodeLocation,56) -> Result<(), std::fmt::Error> {57	if start.line == end.line {58		if start.column == end.column {59			write!(out, "{}:{}", start.line, end.column - 1)?;60		} else {61			write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;62		}63	} else {64		write!(65			out,66			"{}:{}-{}:{}",67			start.line,68			end.column - 1,69			start.line,70			end.column71		)?;72	}73	Ok(())74}7576/// vanilla-like jsonnet formatting77pub struct CompactFormat {78	pub resolver: PathResolver,79	pub padding: usize,80}8182impl TraceFormat for CompactFormat {83	fn write_trace(84		&self,85		out: &mut dyn std::fmt::Write,86		evaluation_state: &EvaluationState,87		error: &LocError,88	) -> Result<(), std::fmt::Error> {89		writeln!(out, "{}", error.error())?;90		if let Error::ImportSyntaxError {91			path,92			source_code,93			error,94		} = error.error()95		{96			use std::fmt::Write;97			let mut n = self.resolver.resolve(path);98			let mut offset = error.location.offset;99			let is_eof = if offset >= source_code.len() {100				offset = source_code.len() - 1;101				true102			} else {103				false104			};105			let mut location = offset_to_location(source_code, &[offset])106				.into_iter()107				.next()108				.unwrap();109			if is_eof {110				location.column += 1;111			}112113			write!(n, ":").unwrap();114			print_code_location(&mut n, &location, &location).unwrap();115			write!(out, "{:<p$}{}", "", n, p = self.padding,)?;116		}117		let file_names = error118			.trace()119			.0120			.iter()121			.map(|el| {122				let resolved_path = self.resolver.resolve(&el.location.0);123				// TODO: Process all trace elements first124				let location = evaluation_state125					.map_source_locations(&el.location.0, &[el.location.1, el.location.2]);126				(resolved_path, location)127			})128			.map(|(mut n, location)| {129				use std::fmt::Write;130				write!(n, ":").unwrap();131				print_code_location(&mut n, &location[0], &location[1]).unwrap();132				n133			})134			.collect::<Vec<_>>();135		let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);136		for (i, (el, file)) in error.trace().0.iter().zip(file_names).enumerate() {137			if i != 0 {138				writeln!(out)?;139			}140			write!(141				out,142				"{:<p$}{:<w$}: {}",143				"",144				file,145				el.desc,146				p = self.padding,147				w = align148			)?;149		}150		Ok(())151	}152}153154pub struct JSFormat;155impl TraceFormat for JSFormat {156	fn write_trace(157		&self,158		out: &mut dyn std::fmt::Write,159		evaluation_state: &EvaluationState,160		error: &LocError,161	) -> Result<(), std::fmt::Error> {162		writeln!(out, "{}", error.error())?;163		for (i, item) in error.trace().0.iter().enumerate() {164			if i != 0 {165				writeln!(out)?;166			}167			let desc = &item.desc;168			let source = item.location.clone();169			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);170171			write!(172				out,173				"    at {} ({}:{}:{})",174				desc,175				source.0.to_str().unwrap(),176				start_end[0].line,177				start_end[0].column,178			)?;179		}180		Ok(())181	}182}183184/// rustc-like trace displaying185#[cfg(feature = "explaining-traces")]186pub struct ExplainingFormat {187	pub resolver: PathResolver,188}189#[cfg(feature = "explaining-traces")]190impl TraceFormat for ExplainingFormat {191	fn write_trace(192		&self,193		out: &mut dyn std::fmt::Write,194		evaluation_state: &EvaluationState,195		error: &LocError,196	) -> Result<(), std::fmt::Error> {197		writeln!(out, "{}", error.error())?;198		if let Error::ImportSyntaxError {199			path,200			source_code,201			error,202		} = error.error()203		{204			let mut offset = error.location.offset;205			if offset >= source_code.len() {206				offset = source_code.len() - 1;207			}208			let mut location = offset_to_location(source_code, &[offset])209				.into_iter()210				.next()211				.unwrap();212			if location.column >= 1 {213				location.column -= 1;214			}215216			self.print_snippet(217				out,218				source_code,219				path,220				&location,221				&location,222				"^ syntax error",223			)?;224		}225		let trace = &error.trace();226		for item in trace.0.iter() {227			let desc = &item.desc;228			let source = item.location.clone();229			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);230231			self.print_snippet(232				out,233				&evaluation_state.get_source(&source.0).unwrap(),234				&source.0,235				&start_end[0],236				&start_end[1],237				desc,238			)?;239		}240		Ok(())241	}242}243244impl ExplainingFormat {245	fn print_snippet(246		&self,247		out: &mut dyn std::fmt::Write,248		source: &str,249		origin: &PathBuf,250		start: &CodeLocation,251		end: &CodeLocation,252		desc: &str,253	) -> Result<(), std::fmt::Error> {254		use annotate_snippets::{255			display_list::{DisplayList, FormatOptions},256			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},257		};258259		let source_fragment: String = source260			.chars()261			.skip(start.line_start_offset)262			.take(end.line_end_offset - end.line_start_offset)263			.collect();264265		let origin = self.resolver.resolve(origin);266		let snippet = Snippet {267			opt: FormatOptions {268				color: true,269				..Default::default()270			},271			title: None,272			footer: vec![],273			slices: vec![Slice {274				source: &source_fragment,275				line_start: start.line,276				origin: Some(&origin),277				fold: false,278				annotations: vec![SourceAnnotation {279					label: desc,280					annotation_type: AnnotationType::Error,281					range: (282						start.offset - start.line_start_offset,283						end.offset - start.line_start_offset,284					),285				}],286			}],287		};288289		let dl = DisplayList::from(snippet);290		writeln!(out, "{}", dl)?;291292		Ok(())293	}294}