--- 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 = [ --- 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] --- 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" --- 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, &ArgsDesc) -> Result> = { + let mut out: HashMap<&'static str, _> = HashMap::new(); + out.insert("length", intrinsic_length); + out + }; +} + +fn intrinsic_length(context: Context, _loc: &Option, args: &ArgsDesc) -> Result { + 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 { 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::>(); out.sort(); - Ok(Val::Arr(Rc::new(out.into_iter().map(Val::Str).collect()))) + Ok(Val::Arr(out.into_iter().map(Val::Str).collect::>().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::()).into())) } Val::Arr(arr) => { - Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).cloned().collect::>()).into())) + Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::>>()?).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::>()).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::>>()?).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) })?, --- 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 } --- 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(()) - }) } --- 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::*; --- 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 )?;