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
1use crate::{1use crate::{
2 equals,2 equals,
3 error::{Error::*, Result},3 error::{Error::*, Result},
4 evaluate, parse_args, primitive_equals, push, throw, with_state, Context, FuncVal, Val,4 evaluate, parse_args, primitive_equals, push, throw,
5 typed::CheckType,
5 ValType,6 with_state, ArrValue, Context, FuncVal, LazyVal, Val,
6};7};
7use format::{format_arr, format_obj};8use format::{format_arr, format_obj};
8use jrsonnet_parser::{ArgsDesc, ExprLocation};9use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};
9use manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};10use jrsonnet_types::{ty, ComplexValType, ValType};
10use std::{path::PathBuf, rc::Rc};11use std::{collections::HashMap, path::PathBuf, rc::Rc};
1112
12pub mod stdlib;13pub mod stdlib;
13pub use stdlib::*;14pub use stdlib::*;
1415
16use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};
17
15pub mod format;18pub mod format;
16pub mod manifest;19pub mod manifest;
17pub mod sort;20pub mod sort;
22 || format!("std.format of {}", str),25 || format!("std.format of {}", str),
23 || {26 || {
24 Ok(match vals {27 Ok(match vals {
25 Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()),28 Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),
26 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),29 Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),
27 o => Val::Str(format_arr(&str, &[o])?.into()),30 o => Val::Str(format_arr(&str, &[o])?.into()),
28 })31 })
29 },32 },
30 )33 )
31}34}
3235
36thread_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 out
41 };
42}
43
44fn 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}
61
33#[allow(clippy::cognitive_complexity)]62#[allow(clippy::cognitive_complexity)]
34pub fn call_builtin(63pub fn call_builtin(
35 context: Context,64 context: Context,
38 args: &ArgsDesc,67 args: &ArgsDesc,
39) -> Result<Val> {68) -> Result<Val> {
40 Ok(match name as &str {69 Ok(match name as &str {
41 // arr/string/function70 "length" => parse_args!(context, "length", args, 1, [
42 "length" => parse_args!(context, "std.length", args, 1, [
43 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj];71 0, x: ty!((str | obj | [any]));
44 ], {72 ], {
45 Ok(match x {73 Ok(match x {
46 Val::Str(n) => Val::Num(n.chars().count() as f64),74 Val::Str(n) => Val::Num(n.chars().count() as f64),
47 Val::Arr(i) => Val::Num(i.len() as f64),75 Val::Arr(a) => Val::Num(a.len() as f64),
48 Val::Obj(o) => Val::Num(76 Val::Obj(o) => Val::Num(
49 o.fields_visibility()77 o.fields_visibility()
50 .into_iter()78 .into_iter()
54 _ => unreachable!(),82 _ => unreachable!(),
55 })83 })
56 })?,84 })?,
57 // any85 "type" => parse_args!(context, "type", args, 1, [
58 "type" => parse_args!(context, "std.type", args, 1, [
59 0, x, vec![];86 0, x: ty!(any);
60 ], {87 ], {
61 Ok(Val::Str(x.value_type()?.name().into()))88 Ok(Val::Str(x.value_type().name().into()))
62 })?,89 })?,
63 // length, idx=>any90 "makeArray" => parse_args!(context, "makeArray", args, 2, [
64 "makeArray" => parse_args!(context, "std.makeArray", args, 2, [
65 0, sz: [Val::Num]!!Val::Num, vec![ValType::Num];91 0, sz: ty!(number((Some(0.0))..(None))) => Val::Num;
66 1, func: [Val::Func]!!Val::Func, vec![ValType::Func];92 1, func: ty!(fn.any) => Val::Func;
67 ], {93 ], {
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);94 let mut out = Vec::with_capacity(sz as usize);
72 for i in 0..sz as usize {95 for i in 0..sz as usize {
73 out.push(func.evaluate_values(96 out.push(LazyVal::new_resolved(func.evaluate_values(
74 Context::new(),97 Context::new(),
75 &[Val::Num(i as f64)]98 &[Val::Num(i as f64)]
76 )?)99 )?))
77 }100 }
78 Ok(Val::Arr(Rc::new(out)))101 Ok(Val::Arr(out.into()))
79 })?,102 })?,
80 // string103 "codepoint" => parse_args!(context, "codepoint", args, 1, [
81 "codepoint" => parse_args!(context, "std.codepoint", args, 1, [
82 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];104 0, str: ty!(char) => Val::Str;
83 ], {105 ], {
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))106 Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64))
89 })?,107 })?,
90 // object, includeHidden108 "objectFieldsEx" => parse_args!(context, "objectFieldsEx", args, 2, [
91 "objectFieldsEx" => parse_args!(context, "std.objectFieldsEx",args, 2, [
92 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];109 0, obj: ty!(obj) => Val::Obj;
93 1, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];110 1, inc_hidden: ty!(bool) => Val::Bool;
94 ], {111 ], {
95 let mut out = obj.fields_visibility()112 let mut out = obj.fields_visibility()
96 .into_iter()113 .into_iter()
97 .filter(|(_k, v)| *v || inc_hidden)114 .filter(|(_k, v)| *v || inc_hidden)
98 .map(|(k, _v)|k)115 .map(|(k, _v)|k)
99 .collect::<Vec<_>>();116 .collect::<Vec<_>>();
100 out.sort();117 out.sort();
101 Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect())))118 Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))
102 })?,119 })?,
103 // object, field, includeHidden120 "objectHasEx" => parse_args!(context, "objectHasEx", args, 3, [
104 "objectHasEx" => parse_args!(context, "std.objectHasEx", args, 3, [
105 0, obj: [Val::Obj]!!Val::Obj, vec![ValType::Obj];121 0, obj: ty!(obj) => Val::Obj;
106 1, f: [Val::Str]!!Val::Str, vec![ValType::Str];122 1, f: ty!(str) => Val::Str;
107 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool];123 2, inc_hidden: ty!(bool) => Val::Bool;
108 ], {124 ], {
109 Ok(Val::Bool(125 Ok(Val::Bool(
110 obj.fields_visibility()126 obj.fields_visibility()
113 .any(|(k, _v)| *k == *f),129 .any(|(k, _v)| *k == *f),
114 ))130 ))
115 })?,131 })?,
116
117 // faster132 // faster
118 "slice" => parse_args!(context, "slice", args, 4, [133 "slice" => parse_args!(context, "slice", args, 4, [
119 0, indexable: [Val::Str | Val::Arr], vec![ValType::Str, ValType::Arr];134 0, indexable: ty!((str | [any]));
120 1, index, vec![ValType::Num, ValType::Null];135 1, index: ty!((num | null));
121 2, end, vec![ValType::Num, ValType::Null];136 2, end: ty!((num | null));
122 3, step, vec![ValType::Num, ValType::Null];137 3, step: ty!((num | null));
123 ], {138 ], {
124 let index = match index {139 let index = match index {
125 Val::Num(v) => v as usize,140 Val::Num(v) => v as usize,
145 Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))160 Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))
146 }161 }
147 Val::Arr(arr) => {162 Val::Arr(arr) => {
148 Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).cloned().collect::<Vec<Val>>()).into()))163 Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))
149 }164 }
150 _ => unreachable!()165 _ => unreachable!()
151 }166 }
152 })?,167 })?,
153 "primitiveEquals" => parse_args!(context, "std.primitiveEquals", args, 2, [168 "primitiveEquals" => parse_args!(context, "primitiveEquals", args, 2, [
154 0, a, vec![];169 0, a: ty!(any);
155 1, b, vec![];170 1, b: ty!(any);
156 ], {171 ], {
157 Ok(Val::Bool(primitive_equals(&a, &b)?))172 Ok(Val::Bool(primitive_equals(&a, &b)?))
158 })?,173 })?,
159 // faster174 // faster
160 "equals" => parse_args!(context, "std.equals", args, 2, [175 "equals" => parse_args!(context, "equals", args, 2, [
161 0, a, vec![];176 0, a: ty!(any);
162 1, b, vec![];177 1, b: ty!(any);
163 ], {178 ], {
164 Ok(Val::Bool(equals(&a, &b)?))179 Ok(Val::Bool(equals(&a, &b)?))
165 })?,180 })?,
166 "mod" => parse_args!(context, "std.mod", args, 2, [181 "modulo" => parse_args!(context, "modulo", args, 2, [
167 0, a: [Val::Num | Val::Str], vec![ValType::Num, ValType::Str];182 0, a: ty!(num) => Val::Num;
168 1, b, vec![];183 1, b: ty!(num) => Val::Num;
169 ], {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 ], {
170 match (a, b) {191 match (a, b) {
171 (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),192 (Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),
172 (Val::Str(str), vals) => std_format(str, vals),193 (Val::Str(str), vals) => std_format(str, vals),
173 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(jrsonnet_parser::BinaryOpType::Mod, a.value_type()?, b.value_type()?))194 (a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))
174 }195 }
175 })?,196 })?,
176 "modulo" => parse_args!(context, "std.modulo", args, 2, [197 "floor" => parse_args!(context, "floor", args, 1, [
177 0, a: [Val::Num]!!Val::Num, vec![ValType::Num];198 0, x: ty!(num) => Val::Num;
178 1, b: [Val::Num]!!Val::Num, vec![ValType::Num];
179 ], {199 ], {
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()))200 Ok(Val::Num(x.floor()))
186 })?,201 })?,
187 "log" => parse_args!(context, "std.log", args, 2, [202 "log" => parse_args!(context, "log", args, 1, [
188 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];203 0, n: ty!(num) => Val::Num;
189 ], {204 ], {
190 Ok(Val::Num(n.ln()))205 Ok(Val::Num(n.ln()))
191 })?,206 })?,
192 "trace" => parse_args!(context, "std.trace", args, 2, [207 "trace" => parse_args!(context, "trace", args, 2, [
193 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];208 0, str: ty!(str) => Val::Str;
194 1, rest, vec![];209 1, rest: ty!(any);
195 ], {210 ], {
196 eprint!("TRACE:");211 eprint!("TRACE:");
197 if let Some(loc) = loc {212 if let Some(loc) = loc {
203 eprintln!(" {}", str);218 eprintln!(" {}", str);
204 Ok(rest)219 Ok(rest)
205 })?,220 })?,
206 "pow" => parse_args!(context, "std.modulo", args, 2, [221 "pow" => parse_args!(context, "pow", args, 2, [
207 0, x: [Val::Num]!!Val::Num, vec![ValType::Num];222 0, x: ty!(num) => Val::Num;
208 1, n: [Val::Num]!!Val::Num, vec![ValType::Num];223 1, n: ty!(num) => Val::Num;
209 ], {224 ], {
210 Ok(Val::Num(x.powf(n)))225 Ok(Val::Num(x.powf(n)))
211 })?,226 })?,
212 "extVar" => parse_args!(context, "std.extVar", args, 1, [227 "extVar" => parse_args!(context, "extVar", args, 1, [
213 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];228 0, x: ty!(str) => Val::Str;
214 ], {229 ], {
215 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)230 Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)
216 })?,231 })?,
217 "native" => parse_args!(context, "std.native", args, 1, [232 "native" => parse_args!(context, "native", args, 1, [
218 0, x: [Val::Str]!!Val::Str, vec![ValType::Str];233 0, x: ty!(str) => Val::Str;
219 ], {234 ], {
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))?)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))?)
221 })?,236 })?,
222 "filter" => parse_args!(context, "std.filter", args, 2, [237 "filter" => parse_args!(context, "filter", args, 2, [
223 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];238 0, func: ty!(fn.any) => Val::Func;
224 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];239 1, arr: ty!([any]) => Val::Arr;
225 ], {240 ], {
226 Ok(Val::Arr(Rc::new(241 let mut out = Vec::new();
227 arr.iter()242 for item in arr.iter() {
228 .cloned()243 let item = item?;
229 .filter(|e| {
230 func244 if func
231 .evaluate_values(context.clone(), &[e.clone()])245 .evaluate_values(context.clone(), &[item.clone()])?
232 .unwrap()246 .try_cast_bool("filter predicate")? {
233 .try_cast_bool("filter predicate")
234 .unwrap()247 out.push(item);
235 })248 }
236 .collect(),249 }
237 )))250 Ok(Val::Arr(out.into()))
238 })?,251 })?,
239 // faster252 "foldl" => parse_args!(context, "foldl", args, 3, [
240 "foldl" => parse_args!(context, "std.foldl", args, 3, [
241 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];253 0, func: ty!(fn.any) => Val::Func;
242 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];254 1, arr: ty!([any]) => Val::Arr;
243 2, init, vec![];255 2, init: ty!(any);
244 ], {256 ], {
245 let mut acc = init;257 let mut acc = init;
246 for i in arr.iter().cloned() {258 for i in arr.iter() {
247 acc = func.evaluate_values(context.clone(), &[acc, i])?;259 acc = func.evaluate_values(context.clone(), &[acc, i?])?;
248 }260 }
249 Ok(acc)261 Ok(acc)
250 })?,262 })?,
251 // faster263 "foldr" => parse_args!(context, "foldr", args, 3, [
252 "foldr" => parse_args!(context, "std.foldr", args, 3, [
253 0, func: [Val::Func]!!Val::Func, vec![ValType::Func];264 0, func: ty!(fn.any) => Val::Func;
254 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];265 1, arr: ty!([any]) => Val::Arr;
255 2, init, vec![];266 2, init: ty!(any);
256 ], {267 ], {
257 let mut acc = init;268 let mut acc = init;
258 for i in arr.iter().rev().cloned() {269 for i in arr.iter().rev() {
259 acc = func.evaluate_values(context.clone(), &[acc, i])?;270 acc = func.evaluate_values(context.clone(), &[acc, i?])?;
260 }271 }
261 Ok(acc)272 Ok(acc)
262 })?,273 })?,
263 // faster
264 #[allow(non_snake_case)]274 #[allow(non_snake_case)]
265 "sortImpl" => parse_args!(context, "std.sort", args, 2, [275 "sortImpl" => parse_args!(context, "sort", args, 2, [
266 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];276 0, arr: ty!([any]) => Val::Arr;
267 1, keyF: [Val::Func]!!Val::Func, vec![ValType::Func];277 1, keyF: ty!(fn.any) => Val::Func;
268 ], {278 ], {
269 if arr.len() <= 1 {279 if arr.len() <= 1 {
270 return Ok(Val::Arr(arr))280 return Ok(Val::Arr(arr))
271 }281 }
272 Ok(Val::Arr(sort::sort(context, arr, &keyF)?))282 Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))
273 })?,283 })?,
274 // faster284 // faster
275 "format" => parse_args!(context, "std.format", args, 2, [285 "format" => parse_args!(context, "format", args, 2, [
276 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];286 0, str: ty!(str) => Val::Str;
277 1, vals, vec![]287 1, vals: ty!(any)
278 ], {288 ], {
279 std_format(str, vals)289 std_format(str, vals)
280 })?,290 })?,
281 // faster291 "range" => parse_args!(context, "range", args, 2, [
282 "range" => parse_args!(context, "std.range", args, 2, [
283 0, from: [Val::Num]!!Val::Num, vec![ValType::Num];292 0, from: ty!(num) => Val::Num;
284 1, to: [Val::Num]!!Val::Num, vec![ValType::Num];293 1, to: ty!(num) => Val::Num;
285 ], {294 ], {
286 let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));295 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 {296 for i in from as usize..=to as usize {
288 out.push(Val::Num(i as f64));297 out.push(Val::Num(i as f64));
289 }298 }
290 Ok(Val::Arr(Rc::new(out)))299 Ok(Val::Arr(out.into()))
291 })?,300 })?,
292 "char" => parse_args!(context, "std.char", args, 1, [301 "char" => parse_args!(context, "char", args, 1, [
293 0, n: [Val::Num]!!Val::Num, vec![ValType::Num];302 0, n: ty!(num) => Val::Num;
294 ], {303 ], {
295 let mut out = String::new();304 let mut out = String::new();
296 out.push(std::char::from_u32(n as u32).ok_or_else(||305 out.push(std::char::from_u32(n as u32).ok_or_else(||
297 InvalidUnicodeCodepointGot(n as u32)306 InvalidUnicodeCodepointGot(n as u32)
298 )?);307 )?);
299 Ok(Val::Str(out.into()))308 Ok(Val::Str(out.into()))
300 })?,309 })?,
301 "encodeUTF8" => parse_args!(context, "std.encodeUtf8", args, 1, [310 "encodeUTF8" => parse_args!(context, "encodeUTF8", args, 1, [
302 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];311 0, str: ty!(str) => Val::Str;
303 ], {312 ], {
304 Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())))313 Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))
305 })?,314 })?,
306 "md5" => parse_args!(context, "std.md5", args, 1, [315 "md5" => parse_args!(context, "md5", args, 1, [
307 0, str: [Val::Str]!!Val::Str, vec![ValType::Str];316 0, str: ty!(str) => Val::Str;
308 ], {317 ], {
309 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))318 Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))
310 })?,319 })?,
311 // faster320 "base64" => parse_args!(context, "base64", args, 1, [
312 "base64" => parse_args!(context, "std.base64", args, 1, [
313 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str];321 0, input: ty!((str | [num]));
314 ], {322 ], {
315 Ok(Val::Str(match input {323 Ok(Val::Str(match input {
316 Val::Str(s) => {324 Val::Str(s) => {
317 base64::encode(s.bytes().collect::<Vec<_>>()).into()325 base64::encode(s.bytes().collect::<Vec<_>>()).into()
318 },326 },
319 Val::Arr(a) => {327 Val::Arr(a) => {
320 base64::encode(a.iter().map(|v| {328 base64::encode(a.iter().map(|v| {
321 Ok(v.clone().try_cast_num("base64 array")? as u8)329 Ok(v?.clone().unwrap_num()? as u8)
322 }).collect::<Result<Vec<_>>>()?).into()330 }).collect::<Result<Vec<_>>>()?).into()
323 },331 },
324 _ => unreachable!()332 _ => unreachable!()
325 }))333 }))
326 })?,334 })?,
327 // faster335 "join" => parse_args!(context, "join", args, 2, [
328 "join" => parse_args!(context, "std.join", args, 2, [
329 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr];336 0, sep: ty!((str | [any]));
330 1, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];337 1, arr: ty!([any]) => Val::Arr;
331 ], {338 ], {
332 Ok(match sep {339 Ok(match sep {
333 Val::Arr(joiner_items) => {340 Val::Arr(joiner_items) => {
334 let mut out = Vec::new();341 let mut out = Vec::new();
335342
336 let mut first = true;343 let mut first = true;
337 for item in arr.iter().cloned() {344 for item in arr.iter() {
345 let item = item?.clone();
338 if let Val::Arr(items) = item.unwrap_if_lazy()? {346 if let Val::Arr(items) = item {
339 if !first {347 if !first {
340 out.reserve(joiner_items.len());348 out.reserve(joiner_items.len());
341 out.extend(joiner_items.iter().cloned());349 // TODO: extend
350 for item in joiner_items.iter() {
351 out.push(item?);
352 }
342 }353 }
343 first = false;354 first = false;
344 out.reserve(items.len());355 out.reserve(items.len());
345 out.extend(items.iter().cloned());356 // TODO: extend
357 for item in items.iter() {
358 out.push(item?);
359 }
346 } else {360 } else {
347 throw!(RuntimeError("in std.join all items should be arrays".into()));361 throw!(RuntimeError("in std.join all items should be arrays".into()));
348 }362 }
349 }363 }
350364
351 Val::Arr(Rc::new(out))365 Val::Arr(out.into())
352 },366 },
353 Val::Str(sep) => {367 Val::Str(sep) => {
354 let mut out = String::new();368 let mut out = String::new();
355369
356 let mut first = true;370 let mut first = true;
357 for item in arr.iter().cloned() {371 for item in arr.iter() {
372 let item = item?.clone();
358 if let Val::Str(item) = item.unwrap_if_lazy()? {373 if let Val::Str(item) = item {
359 if !first {374 if !first {
360 out += &sep;375 out += &sep;
361 }376 }
371 _ => unreachable!()386 _ => unreachable!()
372 })387 })
373 })?,388 })?,
374 // Faster389 // faster
375 "escapeStringJson" => parse_args!(context, "std.escapeStringJson", args, 1, [390 "escapeStringJson" => parse_args!(context, "escapeStringJson", args, 1, [
376 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str];391 0, str_: ty!(str) => Val::Str;
377 ], {392 ], {
378 Ok(Val::Str(escape_string_json(&str_).into()))393 Ok(Val::Str(escape_string_json(&str_).into()))
379 })?,394 })?,
380 // Faster395 // faster
381 "manifestJsonEx" => parse_args!(context, "std.manifestJsonEx", args, 2, [396 "manifestJsonEx" => parse_args!(context, "manifestJsonEx", args, 2, [
382 0, value, vec![];397 0, value: ty!(any);
383 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str];398 1, indent: ty!(str) => Val::Str;
384 ], {399 ], {
385 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {400 Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {
386 padding: &indent,401 padding: &indent,
387 mtype: ManifestType::Std,402 mtype: ManifestType::Std,
388 })?.into()))403 })?.into()))
389 })?,404 })?,
390 // Faster405 // faster
391 "reverse" => parse_args!(context, "std.reverse", args, 1, [406 "reverse" => parse_args!(context, "reverse", args, 1, [
392 0, arr: [Val::Arr]!!Val::Arr, vec![ValType::Arr];407 0, value: ty!([any]) => Val::Arr;
393 ], {408 ], {
394 let mut marr = arr;409 Ok(Val::Arr(value.reversed()))
395 Rc::make_mut(&mut marr).reverse();
396 Ok(Val::Arr(marr))
397 })?,410 })?,
398 "id" => parse_args!(context, "std.id", args, 1, [411 "id" => parse_args!(context, "id", args, 1, [
399 0, v, vec![];412 0, v: ty!(any);
400 ], {413 ], {
401 Ok(v)414 Ok(v)
402 })?,415 })?,
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
 		)?;