--- a/bindings/jsonnet/src/import.rs +++ b/bindings/jsonnet/src/import.rs @@ -1,7 +1,8 @@ //! Import resolution manipulation utilities use jrsonnet_evaluator::{ - create_error, create_error_result, Error, EvaluationState, ImportResolver, Result, + error::{Error::*, Result}, + throw, EvaluationState, ImportResolver, }; use std::{ any::Any, @@ -55,10 +56,9 @@ let result_str = result_raw.to_str().unwrap(); assert!(success == 0 || success == 1); if success == 0 { + unsafe { CString::from_raw(result_ptr) }; let result = result_str.to_owned(); - let err = Err(create_error(Error::ImportCallbackError(result))); - unsafe { CString::from_raw(result_ptr) }; - return err; + throw!(ImportCallbackError(result)); } let found_here_raw = unsafe { CStr::from_ptr(found_here) }; @@ -121,15 +121,14 @@ return Ok(Rc::new(cloned)); } } - create_error_result(Error::ImportFileNotFound(from.clone(), path.clone())) + throw!(ImportFileNotFound(from.clone(), path.clone())) } } fn load_file_contents(&self, id: &PathBuf) -> Result> { - let mut file = - File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?; + let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?; let mut out = String::new(); file.read_to_string(&mut out) - .map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?; + .map_err(|_e| ImportBadFileUtf8(id.clone()))?; Ok(out.into()) } unsafe fn as_any(&self) -> &dyn Any { --- a/cmds/jrsonnet/src/main.rs +++ b/cmds/jrsonnet/src/main.rs @@ -1,6 +1,6 @@ use clap::Clap; use jrsonnet_cli::{ConfigureState, GeneralOpts, InputOpts, ManifestOpts}; -use jrsonnet_evaluator::{EvaluationState, Result}; +use jrsonnet_evaluator::{error::Result, EvaluationState}; use std::{path::PathBuf, rc::Rc}; #[global_allocator] --- a/crates/jrsonnet-cli/src/ext.rs +++ b/crates/jrsonnet-cli/src/ext.rs @@ -1,6 +1,6 @@ use crate::ConfigureState; use clap::Clap; -use jrsonnet_evaluator::{EvaluationState, Result}; +use jrsonnet_evaluator::{error::Result, EvaluationState}; use std::str::FromStr; #[derive(Clone)] --- a/crates/jrsonnet-cli/src/lib.rs +++ b/crates/jrsonnet-cli/src/lib.rs @@ -9,7 +9,7 @@ pub use trace::*; use clap::Clap; -use jrsonnet_evaluator::{EvaluationState, FileImportResolver, Result}; +use jrsonnet_evaluator::{error::Result, EvaluationState, FileImportResolver}; use std::path::PathBuf; pub trait ConfigureState { --- a/crates/jrsonnet-cli/src/manifest.rs +++ b/crates/jrsonnet-cli/src/manifest.rs @@ -1,6 +1,6 @@ use crate::ConfigureState; use clap::Clap; -use jrsonnet_evaluator::{EvaluationState, ManifestFormat, Result}; +use jrsonnet_evaluator::{error::Result, EvaluationState, ManifestFormat}; use std::str::FromStr; pub enum ManifestFormatName { --- a/crates/jrsonnet-cli/src/tla.rs +++ b/crates/jrsonnet-cli/src/tla.rs @@ -1,6 +1,6 @@ use crate::{ConfigureState, ExtStr}; use clap::Clap; -use jrsonnet_evaluator::{EvaluationState, Result}; +use jrsonnet_evaluator::{error::Result, EvaluationState}; #[derive(Clap)] // #[clap(help_heading = "TOP LEVEL ARGUMENTS")] --- a/crates/jrsonnet-cli/src/trace.rs +++ b/crates/jrsonnet-cli/src/trace.rs @@ -1,8 +1,9 @@ use crate::ConfigureState; use clap::Clap; use jrsonnet_evaluator::{ + error::Result, trace::{CompactFormat, ExplainingFormat, PathResolver}, - EvaluationState, Result, + EvaluationState, }; use std::str::FromStr; --- a/crates/jrsonnet-evaluator/src/builtin/format.rs +++ b/crates/jrsonnet-evaluator/src/builtin/format.rs @@ -1,31 +1,32 @@ //! faster std.format impl #![allow(clippy::too_many_arguments)] -use crate::{ - create_error, create_error_result, to_string, Error, LocError, ObjValue, Val, ValType, -}; +use crate::{error::Error::*, throw, to_string, LocError, ObjValue, Result, Val, ValType}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum FormatError { TruncatedFormatCode, UnrecognizedConversionType(char), - ValueError(LocError), NotEnoughValues, CannotUseStarWidthWithObject, MappingKeysRequired, - NoSuchField(Rc), + NoSuchFormatField(Rc), } -impl From for FormatError { - fn from(e: LocError) -> Self { - Self::ValueError(e) + +impl From for LocError { + fn from(e: FormatError) -> Self { + Self::new(Format(e)) } } + use std::rc::Rc; use FormatError::*; -pub fn try_parse_mapping_key(str: &str) -> Result<(&str, &str), FormatError> { +type ParseResult<'t, T> = std::result::Result<(T, &'t str), FormatError>; + +pub fn try_parse_mapping_key(str: &str) -> ParseResult<&str> { if str.is_empty() { return Err(TruncatedFormatCode); } @@ -84,7 +85,7 @@ pub sign: bool, } -pub fn try_parse_cflags(str: &str) -> Result<(CFlags, &str), FormatError> { +pub fn try_parse_cflags(str: &str) -> ParseResult { if str.is_empty() { return Err(TruncatedFormatCode); } @@ -113,7 +114,7 @@ Star, Fixed(usize), } -pub fn try_parse_field_width(str: &str) -> Result<(Width, &str), FormatError> { +pub fn try_parse_field_width(str: &str) -> ParseResult { if str.is_empty() { return Err(TruncatedFormatCode); } @@ -134,7 +135,7 @@ Ok((Width::Fixed(out), &str[digits..])) } -pub fn try_parse_precision(str: &str) -> Result<(Option, &str), FormatError> { +pub fn try_parse_precision(str: &str) -> ParseResult> { if str.is_empty() { return Err(TruncatedFormatCode); } @@ -147,7 +148,7 @@ } // Only skips -pub fn try_parse_length_modifier(str: &str) -> Result<&str, FormatError> { +pub fn try_parse_length_modifier(str: &str) -> ParseResult<()> { if str.is_empty() { return Err(TruncatedFormatCode); } @@ -159,10 +160,10 @@ return Err(TruncatedFormatCode); } } - Ok(&str[idx..]) + Ok(((), &str[idx..])) } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum ConvTypeV { Decimal, Octal, @@ -179,7 +180,7 @@ caps: bool, } -pub fn parse_conversion_type(str: &str) -> Result<(ConvType, &str), FormatError> { +pub fn parse_conversion_type(str: &str) -> ParseResult { if str.is_empty() { return Err(TruncatedFormatCode); } @@ -214,7 +215,7 @@ convtype: ConvTypeV, caps: bool, } -pub fn parse_code(str: &str) -> Result<(Code, &str), FormatError> { +pub fn parse_code(str: &str) -> ParseResult { if str.is_empty() { return Err(TruncatedFormatCode); } @@ -222,7 +223,7 @@ let (cflags, str) = try_parse_cflags(str)?; let (width, str) = try_parse_field_width(str)?; let (precision, str) = try_parse_precision(str)?; - let str = try_parse_length_modifier(str)?; + let (_, str) = try_parse_length_modifier(str)?; let (convtype, str) = parse_conversion_type(str)?; Ok(( @@ -243,7 +244,7 @@ String(&'s str), Code(Code<'s>), } -pub fn parse_codes(mut str: &str) -> Result, FormatError> { +pub fn parse_codes(mut str: &str) -> Result> { let mut bytes = str.as_bytes(); let mut out = vec![]; let mut offset = 0; @@ -453,7 +454,7 @@ code: &Code, width: usize, precision: Option, -) -> Result<(), FormatError> { +) -> Result<()> { let clfags = &code.cflags; let (fpprec, iprec) = match precision { Some(v) => (v, v), @@ -565,22 +566,22 @@ ConvTypeV::Char => match value.clone().unwrap_if_lazy()? { Val::Num(n) => tmp_out.push( std::char::from_u32(n as u32) - .ok_or_else(|| create_error(Error::InvalidUnicodeCodepointGot(n as u32)))?, + .ok_or_else(|| InvalidUnicodeCodepointGot(n as u32))?, ), Val::Str(s) => { if s.chars().count() != 1 { - create_error_result(Error::RuntimeError( + throw!(RuntimeError( format!("%c expected 1 char string, got {}", s.chars().count()).into(), - ))?; + )); } tmp_out.push_str(&s); } _ => { - create_error_result(Error::TypeMismatch( + throw!(TypeMismatch( "%c requires number/string", vec![ValType::Num, ValType::Str], value.value_type()?, - ))?; + )); } }, ConvTypeV::Percent => tmp_out.push('%'), @@ -603,7 +604,7 @@ Ok(()) } -pub fn format_arr(str: &str, mut values: &[Val]) -> Result { +pub fn format_arr(str: &str, mut values: &[Val]) -> Result { let codes = parse_codes(&str)?; let mut out = String::new(); @@ -616,7 +617,7 @@ let width = match c.width { Width::Star => { if values.is_empty() { - return Err(FormatError::NotEnoughValues); + throw!(NotEnoughValues); } let value = &values[0]; values = &values[1..]; @@ -627,7 +628,7 @@ let precision = match c.precision { Some(Width::Star) => { if values.is_empty() { - return Err(FormatError::NotEnoughValues); + throw!(NotEnoughValues); } let value = &values[0]; values = &values[1..]; @@ -642,7 +643,7 @@ &Val::Null } else { if values.is_empty() { - return Err(FormatError::NotEnoughValues); + throw!(NotEnoughValues); } let value = &values[0]; values = &values[1..]; @@ -657,7 +658,7 @@ Ok(out) } -pub fn format_obj(str: &str, values: &ObjValue) -> Result { +pub fn format_obj(str: &str, values: &ObjValue) -> Result { let codes = parse_codes(&str)?; let mut out = String::new(); @@ -670,17 +671,17 @@ // TODO: Operate on ref let f: Rc = c.mkey.into(); if f.is_empty() { - return Err(FormatError::MappingKeysRequired); + throw!(MappingKeysRequired); } let width = match c.width { Width::Star => { - return Err(FormatError::CannotUseStarWidthWithObject); + throw!(CannotUseStarWidthWithObject); } Width::Fixed(n) => n, }; let precision = match c.precision { Some(Width::Star) => { - return Err(FormatError::CannotUseStarWidthWithObject); + throw!(CannotUseStarWidthWithObject); } Some(Width::Fixed(n)) => Some(n), None => None, @@ -688,7 +689,7 @@ let value = if let Some(v) = values.get(f.clone())? { v } else { - return Err(FormatError::NoSuchField(f)); + throw!(NoSuchFormatField(f)); }; format_code(&mut out, &value, &c, width, precision)?; --- a/crates/jrsonnet-evaluator/src/ctx.rs +++ b/crates/jrsonnet-evaluator/src/ctx.rs @@ -1,5 +1,5 @@ use crate::{ - create_error, future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val, Error, + error::Error::*, future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val, LazyBinding, LazyVal, ObjValue, Result, Val, }; use std::{ @@ -61,11 +61,12 @@ } pub fn binding(&self, name: Rc) -> Result { - self.0 + Ok(self + .0 .bindings .get(&name) .cloned() - .ok_or_else(|| create_error(Error::UnknownVariable(name))) + .ok_or_else(|| UnknownVariable(name))?) } pub fn into_future(self, ctx: FutureContext) -> Context { { --- a/crates/jrsonnet-evaluator/src/error.rs +++ b/crates/jrsonnet-evaluator/src/error.rs @@ -1,4 +1,4 @@ -use crate::{Val, ValType}; +use crate::{builtin::format::FormatError, Val, ValType}; use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType}; use std::{path::PathBuf, rc::Rc}; @@ -61,7 +61,14 @@ ImportCallbackError(String), InvalidUnicodeCodepointGot(u32), + + Format(FormatError), } +impl From for LocError { + fn from(e: Error) -> Self { + Self(e, StackTrace(vec![])) + } +} #[derive(Clone, Debug)] pub struct StackTraceElement { @@ -73,4 +80,17 @@ #[derive(Debug, Clone)] pub struct LocError(pub Error, pub StackTrace); +impl LocError { + pub fn new(e: Error) -> Self { + Self(e, StackTrace(vec![])) + } +} + pub type Result = std::result::Result; + +#[macro_export] +macro_rules! throw { + ($e: expr) => { + return Err($e.into()); + }; +} --- a/crates/jrsonnet-evaluator/src/evaluate.rs +++ b/crates/jrsonnet-evaluator/src/evaluate.rs @@ -1,9 +1,10 @@ use crate::{ builtin::format::{format_arr, format_obj}, - context_creator, create_error, create_error_result, equals, escape_string_json, future_wrapper, - lazy_val, manifest_json_ex, parse_args, primitive_equals, push, with_state, Context, - ContextCreator, Error, FuncDesc, LazyBinding, LazyVal, ObjMember, ObjValue, Result, Val, - ValType, + context_creator, equals, + error::Error::*, + escape_string_json, future_wrapper, lazy_val, manifest_json_ex, parse_args, primitive_equals, + push, throw, with_state, Context, ContextCreator, FuncDesc, LazyBinding, LazyVal, LocError, + ObjMember, ObjValue, Result, Val, ValType, }; use closure::closure; use jrsonnet_parser::{ @@ -11,7 +12,7 @@ ForSpecData, IfSpecData, LiteralType, LocExpr, Member, ObjBody, ParamsDesc, UnaryOpType, Visibility, }; -use std::{cmp::Ordering, collections::HashMap, rc::Rc}; +use std::{cmp::Ordering, collections::HashMap, path::PathBuf, rc::Rc}; pub fn evaluate_binding(b: &BindSpec, context_creator: ContextCreator) -> (Rc, LazyBinding) { let b = b.clone(); @@ -79,10 +80,7 @@ (UnaryOpType::Not, Val::Bool(v)) => Val::Bool(!v), (UnaryOpType::Minus, Val::Num(n)) => Val::Num(-*n), (UnaryOpType::BitNot, Val::Num(n)) => Val::Num(!(*n as i32) as f64), - (op, o) => create_error_result(Error::UnaryOperatorDoesNotOperateOnType( - op, - o.value_type()?, - ))?, + (op, o) => throw!(UnaryOperatorDoesNotOperateOnType(op, o.value_type()?)), }) } @@ -100,11 +98,11 @@ (Val::Obj(v1), Val::Obj(v2)) => Val::Obj(v2.with_super(v1.clone())), (Val::Arr(a), Val::Arr(b)) => Val::Arr(Rc::new([&a[..], &b[..]].concat())), (Val::Num(v1), Val::Num(v2)) => Val::new_checked_num(v1 + v2)?, - _ => create_error_result(Error::BinaryOperatorDoesNotOperateOnValues( + _ => throw!(BinaryOperatorDoesNotOperateOnValues( BinaryOpType::Add, a.value_type()?, b.value_type()?, - ))?, + )), }) } @@ -145,7 +143,7 @@ (Val::Num(v1), BinaryOpType::Mul, Val::Num(v2)) => Val::new_checked_num(v1 * v2)?, (Val::Num(v1), BinaryOpType::Div, Val::Num(v2)) => { if *v2 <= f64::EPSILON { - create_error_result(crate::Error::DivisionByZero)? + throw!(DivisionByZero) } Val::new_checked_num(v1 / v2)? } @@ -168,22 +166,22 @@ } (Val::Num(v1), BinaryOpType::Lhs, Val::Num(v2)) => { if *v2 < 0.0 { - create_error_result(Error::RuntimeError("shift by negative exponent".into()))? + throw!(RuntimeError("shift by negative exponent".into())) } Val::Num(((*v1 as i32) << (*v2 as i32)) as f64) } (Val::Num(v1), BinaryOpType::Rhs, Val::Num(v2)) => { if *v2 < 0.0 { - create_error_result(Error::RuntimeError("shift by negative exponent".into()))? + throw!(RuntimeError("shift by negative exponent".into())) } Val::Num(((*v1 as i32) >> (*v2 as i32)) as f64) } - _ => create_error_result(Error::BinaryOperatorDoesNotOperateOnValues( + _ => throw!(BinaryOperatorDoesNotOperateOnValues( op, a.value_type()?, b.value_type()?, - ))?, + )), }) } @@ -218,7 +216,7 @@ } Some(out.into_iter().flatten().flatten().collect()) } - _ => create_error_result(Error::InComprehensionCanOnlyIterateOverArray)?, + _ => throw!(InComprehensionCanOnlyIterateOverArray), } } }) @@ -379,7 +377,7 @@ }, ); } - v => create_error_result(Error::FieldMustBeStringGot(v.value_type()?))?, + v => throw!(FieldMustBeStringGot(v.value_type()?)), } } @@ -409,7 +407,7 @@ Ok(match value { Val::Intristic(ns, name) => match (&ns as &str, &name as &str) { // arr/string/function - ("std", "length") => noinline!(parse_args!(context, "std.length", args, 1, [ + ("std", "length") => parse_args!(context, "std.length", args, 1, [ 0, x: [Val::Str|Val::Arr|Val::Obj], vec![ValType::Str, ValType::Arr, ValType::Obj]; ], { Ok(match x { @@ -423,20 +421,20 @@ ), _ => unreachable!(), }) - }))?, + })?, // any ("std", "type") => parse_args!(context, "std.type", args, 1, [ 0, x, vec![]; ], { - Val::Str(x.value_type()?.name().into()) - }), + Ok(Val::Str(x.value_type()?.name().into())) + })?, // length, idx=>any ("std", "makeArray") => noinline!(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]; ], { if sz < 0.0 { - create_error_result(crate::error::Error::RuntimeError(format!("makeArray requires size >= 0, got {}", sz).into()))?; + 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 { @@ -455,8 +453,8 @@ str.chars().count() == 1, "std.codepoint should receive single char string" ); - Val::Num(str.chars().take(1).next().unwrap() as u32 as f64) - }), + Ok(Val::Num(str.chars().take(1).next().unwrap() as u32 as f64)) + })?, // object, includeHidden ("std", "objectFieldsEx") => { noinline!(parse_args!(context, "std.objectFieldsEx",args, 2, [ @@ -478,42 +476,42 @@ 1, f: [Val::Str]!!Val::Str, vec![ValType::Str]; 2, inc_hidden: [Val::Bool]!!Val::Bool, vec![ValType::Bool]; ], { - Val::Bool( + Ok(Val::Bool( obj.fields_visibility() .into_iter() .filter(|(_k, v)| *v || inc_hidden) .any(|(k, _v)| *k == *f), - ) - }), + )) + })?, ("std", "primitiveEquals") => parse_args!(context, "std.primitiveEquals", args, 2, [ 0, a, vec![]; 1, b, vec![]; ], { - Val::Bool(primitive_equals(&a, &b)?) - }), + Ok(Val::Bool(primitive_equals(&a, &b)?)) + })?, // faster ("std", "equals") => parse_args!(context, "std.equals", args, 2, [ 0, a, vec![]; 1, b, vec![]; ], { - Val::Bool(equals(&a, &b)?) - }), + Ok(Val::Bool(equals(&a, &b)?)) + })?, ("std", "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]; ], { - Val::Num(a % b) - }), + Ok(Val::Num(a % b)) + })?, ("std", "floor") => parse_args!(context, "std.floor", args, 1, [ 0, x: [Val::Num]!!Val::Num, vec![ValType::Num]; ], { - Val::Num(x.floor()) - }), + Ok(Val::Num(x.floor())) + })?, ("std", "log") => parse_args!(context, "std.log", args, 2, [ 0, n: [Val::Num]!!Val::Num, vec![ValType::Num]; ], { - Val::Num(n.ln()) - }), + Ok(Val::Num(n.ln())) + })?, ("std", "trace") => parse_args!(context, "std.trace", args, 2, [ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str]; 1, rest, vec![]; @@ -526,21 +524,21 @@ }); } eprintln!(" {}", str); - rest - }), + Ok(rest) + })?, ("std", "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]; ], { - Val::Num(x.powf(n)) - }), + Ok(Val::Num(x.powf(n))) + })?, ("std", "extVar") => parse_args!(context, "std.extVar", args, 2, [ 0, x: [Val::Str]!!Val::Str, vec![ValType::Str]; ], { - with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else( - || create_error(crate::Error::UndefinedExternalVariable(x)), - )? - }), + Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or_else( + || UndefinedExternalVariable(x), + )?) + })?, ("std", "filter") => noinline!(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]; @@ -583,6 +581,7 @@ Ok(acc) }))?, // faster + #[allow(non_snake_case)] ("std", "sortImpl") => noinline!(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]; @@ -598,7 +597,7 @@ match keyF.evaluate_values(context.clone(), &[k.clone()]) { Ok(Val::Str(v)) => v, Ok(_) => { - err = Some(create_error(crate::error::Error::RuntimeError("types of all array elements should equal".into()))); + err = Some(LocError::new(RuntimeError("types of all array elements should equal".into()))); "".into() } Err(e) => { @@ -617,7 +616,7 @@ match (keyF.evaluate_values(context.clone(), &[a.clone()]), keyF.evaluate_values(context.clone(), &[b.clone()])) { (Ok(Val::Num(a)), Ok(Val::Num(b))) => a.partial_cmp(&b).unwrap(), (Ok(_a), Ok(_b)) => { - err = Some(create_error(crate::error::Error::RuntimeError("types of all array elements should equal".into()))); + err = Some(RuntimeError("types of all array elements should equal".into()).into()); Ordering::Equal } (Err(e), _) | (_, Err(e)) => { @@ -630,21 +629,23 @@ return Err(e); } }, - _ => return Err(create_error(crate::error::Error::RuntimeError("keys should be number or string".into()))) + _ => throw!(RuntimeError("keys should be number or string".into())) } Ok(Val::Arr(Rc::new(new_arr))) }))?, // faster ("std", "format") => parse_args!(context, "std.format", args, 2, [ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str]; - 1, vals: [Val::Arr|Val::Obj], vec![ValType::Arr, ValType::Obj]; + 1, vals, vec![] ], { - match vals { - Val::Arr(vals) => Val::Str(format_arr(&str, &vals).unwrap().into()), - Val::Obj(obj) => Val::Str(format_obj(&str, &obj).unwrap().into()), - _ => unreachable!() - } - }), + push(&Some(ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)), ||format!("std.format of {}", str), ||{ + Ok(match vals { + Val::Arr(vals) => Val::Str(format_arr(&str, &vals)?.into()), + Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()), + o => Val::Str(format_arr(&str, &[o])?.into()), + }) + }) + })?, // faster ("std", "range") => parse_args!(context, "std.range", args, 2, [ 0, from: [Val::Num]!!Val::Num, vec![ValType::Num]; @@ -654,22 +655,22 @@ for i in from as usize..=to as usize { out.push(Val::Num(i as f64)); } - Val::Arr(Rc::new(out)) - }), + Ok(Val::Arr(Rc::new(out))) + })?, ("std", "char") => parse_args!(context, "std.char", args, 1, [ 0, n: [Val::Num]!!Val::Num, vec![ValType::Num]; ], { let mut out = String::new(); out.push(std::char::from_u32(n as u32).ok_or_else(|| - create_error(crate::error::Error::InvalidUnicodeCodepointGot(n as u32)) + InvalidUnicodeCodepointGot(n as u32) )?); Ok(Val::Str(out.into())) })?, ("std", "encodeUTF8") => parse_args!(context, "std.encodeUtf8", args, 1, [ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str]; ], { - Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect())) - }), + Ok(Val::Arr(Rc::new(str.bytes().map(|b| Val::Num(b as f64)).collect()))) + })?, ("std", "md5") => noinline!(parse_args!(context, "std.md5", args, 1, [ 0, str: [Val::Str]!!Val::Str, vec![ValType::Str]; ], { @@ -679,7 +680,7 @@ ("std", "base64") => parse_args!(context, "std.base64", args, 1, [ 0, input: [Val::Str | Val::Arr], vec![ValType::Arr, ValType::Str]; ], { - Val::Str(match input { + Ok(Val::Str(match input { Val::Str(s) => { base64::encode(s.bytes().collect::>()).into() }, @@ -689,8 +690,8 @@ }).collect::>>()?).into() }, _ => unreachable!() - }) - }), + })) + })?, // faster ("std", "join") => noinline!(parse_args!(context, "std.join", args, 2, [ 0, sep: [Val::Str|Val::Arr], vec![ValType::Str, ValType::Arr]; @@ -711,7 +712,7 @@ out.reserve(items.len()); out.extend(items.iter().cloned()); } else { - create_error_result(crate::Error::RuntimeError("in std.join all items should be arrays".into()))?; + throw!(RuntimeError("in std.join all items should be arrays".into())); } } @@ -729,7 +730,7 @@ first = false; out += &item; } else { - create_error_result(crate::Error::RuntimeError("in std.join all items should be strings".into()))?; + throw!(RuntimeError("in std.join all items should be strings".into())); } } @@ -742,18 +743,16 @@ ("std", "escapeStringJson") => parse_args!(context, "std.escapeStringJson", args, 1, [ 0, str_: [Val::Str]!!Val::Str, vec![ValType::Str]; ], { - Val::Str(escape_string_json(&str_).into()) - }), + Ok(Val::Str(escape_string_json(&str_).into())) + })?, // Faster ("std", "manifestJsonEx") => parse_args!(context, "std.manifestJsonEx", args, 2, [ 0, value, vec![]; 1, indent: [Val::Str]!!Val::Str, vec![ValType::Str]; ], { - Val::Str(manifest_json_ex(&value, &indent)?.into()) - }), - (ns, name) => { - create_error_result(crate::Error::IntristicNotFound(ns.into(), name.into()))? - } + Ok(Val::Str(manifest_json_ex(&value, &indent)?.into())) + })?, + (ns, name) => throw!(IntristicNotFound(ns.into(), name.into())), }, Val::Func(f) => { let body = || f.evaluate(context, args, tailstrict); @@ -763,7 +762,7 @@ push(loc, || format!("function <{}> call", f.name), body)? } } - v => create_error_result(crate::Error::OnlyFunctionsCanBeCalledGot(v.value_type()?))?, + v => throw!(OnlyFunctionsCanBeCalledGot(v.value_type()?)), }) } @@ -784,13 +783,13 @@ context .this() .clone() - .ok_or_else(|| create_error(crate::Error::CantUseSelfOutsideOfObject))?, + .ok_or_else(|| CantUseSelfOutsideOfObject)?, ), Literal(LiteralType::Dollar) => Val::Obj( context .dollar() .clone() - .ok_or_else(|| create_error(crate::Error::NoTopLevelObjectFound))?, + .ok_or_else(|| NoTopLevelObjectFound)?, ), Literal(LiteralType::True) => Val::Bool(true), Literal(LiteralType::False) => Val::Bool(false), @@ -825,34 +824,30 @@ } else if let Some(Val::Str(n)) = v.get("__intristic_namespace__".into())? { Val::Intristic(n, s) } else { - create_error_result(crate::Error::NoSuchField(s))? + throw!(NoSuchField(s)) } } - (Val::Obj(_), n) => create_error_result(crate::Error::ValueIndexMustBeTypeGot( + (Val::Obj(_), n) => throw!(ValueIndexMustBeTypeGot( ValType::Obj, ValType::Str, n.value_type()?, - ))?, + )), (Val::Arr(v), Val::Num(n)) => { if n.fract() > f64::EPSILON { - create_error_result(crate::Error::FractionalIndex)? + throw!(FractionalIndex) } v.get(n as usize) - .ok_or_else(|| { - create_error(crate::Error::ArrayBoundsError(n as usize, v.len())) - })? + .ok_or_else(|| ArrayBoundsError(n as usize, v.len()))? .clone() .unwrap_if_lazy()? - } - (Val::Arr(_), Val::Str(n)) => { - create_error_result(crate::Error::AttemptedIndexAnArrayWithString(n))? } - (Val::Arr(_), n) => create_error_result(crate::Error::ValueIndexMustBeTypeGot( + (Val::Arr(_), Val::Str(n)) => throw!(AttemptedIndexAnArrayWithString(n)), + (Val::Arr(_), n) => throw!(ValueIndexMustBeTypeGot( ValType::Arr, ValType::Num, n.value_type()?, - ))?, + )), (Val::Str(s), Val::Num(n)) => Val::Str( s.chars() @@ -861,13 +856,13 @@ .collect::() .into(), ), - (Val::Str(_), n) => create_error_result(crate::Error::ValueIndexMustBeTypeGot( + (Val::Str(_), n) => throw!(ValueIndexMustBeTypeGot( ValType::Str, ValType::Num, n.value_type()?, - ))?, + )), - (v, _) => create_error_result(crate::Error::CantIndexInto(v.value_type()?))?, + (v, _) => throw!(CantIndexInto(v.value_type()?)), } } LocalExpr(bindings, returned) => { @@ -926,18 +921,18 @@ if assertion_result { evaluate(context, returned)? } else if let Some(msg) = msg { - create_error_result(crate::Error::AssertionFailed(evaluate(context, msg)?))? + throw!(AssertionFailed(evaluate(context, msg)?)); } else { - create_error_result(crate::Error::AssertionFailed(Val::Null))? + throw!(AssertionFailed(Val::Null)); } } - Error(e) => push( + ErrorStmt(e) => push( &loc, || "error statement".to_owned(), || { - create_error_result(crate::Error::RuntimeError( + throw!(RuntimeError( evaluate(context, e)?.try_cast_str("error text should be string")?, - ))? + )) }, )?, IfElse { @@ -978,6 +973,6 @@ import_location.pop(); Val::Str(with_state(|s| s.import_file_str(&import_location, path))?) } - Literal(LiteralType::Super) => return create_error_result(crate::Error::StandaloneSuper), + Literal(LiteralType::Super) => throw!(StandaloneSuper), }) } --- a/crates/jrsonnet-evaluator/src/function.rs +++ b/crates/jrsonnet-evaluator/src/function.rs @@ -1,7 +1,4 @@ -use crate::{ - create_error, create_error_result, evaluate, lazy_val, resolved_lazy_val, Context, Error, - Result, Val, -}; +use crate::{error::Error::*, evaluate, lazy_val, resolved_lazy_val, throw, Context, Result, Val}; use closure::closure; use jrsonnet_parser::{ArgsDesc, ParamsDesc}; use std::{collections::HashMap, rc::Rc}; @@ -30,16 +27,16 @@ params .iter() .position(|p| *p.0 == *name) - .ok_or_else(|| create_error(Error::UnknownFunctionParameter(name.clone())))? + .ok_or_else(|| UnknownFunctionParameter(name.clone()))? } else { id }; if idx >= params.len() { - create_error_result(Error::TooManyArgsFunctionHas(params.len()))?; + throw!(TooManyArgsFunctionHas(params.len())); } if positioned_args[idx].is_some() { - create_error_result(Error::BindingParameterASecondTime(params[idx].0.clone()))?; + throw!(BindingParameterASecondTime(params[idx].0.clone())); } positioned_args[idx] = Some(arg.1.clone()); } @@ -50,8 +47,7 @@ } else if let Some(default) = &p.1 { (body_ctx.clone().expect(NO_DEFAULT_CONTEXT), default) } else { - create_error_result(Error::FunctionParameterNotBoundInCall(p.0.clone()))?; - unreachable!() + throw!(FunctionParameterNotBoundInCall(p.0.clone())); }; let val = if tailstrict { resolved_lazy_val!(evaluate(ctx, expr)?) @@ -74,15 +70,16 @@ let mut out = HashMap::new(); let mut positioned_args = vec![None; params.0.len()]; for (name, val) in args.iter() { - let idx = params.iter().position(|p| *p.0 == **name).ok_or_else(|| { - create_error(Error::UnknownFunctionParameter((&name as &str).to_owned())) - })?; + let idx = params + .iter() + .position(|p| *p.0 == **name) + .ok_or_else(|| UnknownFunctionParameter((&name as &str).to_owned()))?; if idx >= params.len() { - create_error_result(Error::TooManyArgsFunctionHas(params.len()))?; + throw!(TooManyArgsFunctionHas(params.len())); } if positioned_args[idx].is_some() { - create_error_result(Error::BindingParameterASecondTime(params[idx].0.clone()))?; + throw!(BindingParameterASecondTime(params[idx].0.clone())); } positioned_args[idx] = Some(val.clone()); } @@ -104,8 +101,7 @@ }) } } else { - create_error_result(Error::FunctionParameterNotBoundInCall(p.0.clone()))?; - unreachable!() + throw!(FunctionParameterNotBoundInCall(p.0.clone())); }; out.insert(p.0.clone(), val); } @@ -123,7 +119,7 @@ let mut positioned_args = vec![None; params.0.len()]; for (id, arg) in args.iter().enumerate() { if id >= params.len() { - create_error_result(Error::TooManyArgsFunctionHas(params.len()))?; + throw!(TooManyArgsFunctionHas(params.len())); } positioned_args[id] = Some(arg); } @@ -134,8 +130,7 @@ } else if let Some(default) = &p.1 { evaluate(ctx.clone(), default)? } else { - create_error_result(Error::FunctionParameterNotBoundInCall(p.0.clone()))?; - unreachable!() + throw!(FunctionParameterNotBoundInCall(p.0.clone())); }; out.insert(p.0.clone(), resolved_lazy_val!(val)); } @@ -148,36 +143,39 @@ ($ctx: expr, $fn_name: expr, $args: expr, $total_args: expr, [ $($id: expr, $name: ident $(: [$($p: path)|+] $(!! $a: path)?)?, $nt: expr);+ $(;)? ], $handler:block) => {{ - use crate::Error; + use crate::{throw, error::Error::*}; let args = $args; if args.len() > $total_args { - create_error_result(Error::TooManyArgsFunctionHas($total_args))?; + throw!(TooManyArgsFunctionHas($total_args)); } $( if args.len() <= $id { - create_error_result(Error::FunctionParameterNotBoundInCall(stringify!($name).into()))?; + throw!(FunctionParameterNotBoundInCall(stringify!($name).into())); } let $name = &args[$id]; if $name.0.is_some() { if $name.0.as_ref().unwrap() != stringify!($name) { - create_error_result(Error::IntristicArgumentReorderingIsNotSupportedYet)?; + throw!(IntristicArgumentReorderingIsNotSupportedYet); } } let $name = evaluate($ctx.clone(), &$name.1)?; $( match $name { $($p(_))|+ => {}, - _ => create_error_result(Error::TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?))?, + _ => throw!(TypeMismatch( + concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), + $nt, $name.value_type()? + )), }; $( let $name = match $name { $a(v) => v, - _ => create_error_result(Error::TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?))?, + _ =>throw!(TypeMismatch(concat!($fn_name, " ", stringify!($id), "nd (", stringify!($name), ") argument"), $nt, $name.value_type()?)), }; )* )* )+ - $handler + ($handler as crate::Result<_>) }}; } @@ -198,7 +196,9 @@ ], { assert!((a - 2.0).abs() <= f64::EPSILON); assert!((b - 1.0).abs() <= f64::EPSILON); - }); + Ok(()) + }) + .unwrap(); Ok(()) }) } --- a/crates/jrsonnet-evaluator/src/import.rs +++ b/crates/jrsonnet-evaluator/src/import.rs @@ -1,7 +1,6 @@ -use crate::create_error_result; use crate::{ - create_error, - error::{Error, Result}, + error::{Error::*, Result}, + throw, }; use fs::File; use std::fs; @@ -28,7 +27,7 @@ pub struct DummyImportResolver; impl ImportResolver for DummyImportResolver { fn resolve_file(&self, from: &PathBuf, path: &PathBuf) -> Result> { - create_error_result(Error::ImportNotSupported(from.clone(), path.clone())) + throw!(ImportNotSupported(from.clone(), path.clone())) } fn load_file_contents(&self, _resolved: &PathBuf) -> Result> { // Can be only caused by library direct consumer, not by supplied jsonnet @@ -65,15 +64,14 @@ return Ok(Rc::new(cloned)); } } - create_error_result(Error::ImportFileNotFound(from.clone(), path.clone())) + throw!(ImportFileNotFound(from.clone(), path.clone())) } } fn load_file_contents(&self, id: &PathBuf) -> Result> { - let mut file = - File::open(id).map_err(|_e| create_error(Error::ResolvedFileNotFound(id.clone())))?; + let mut file = File::open(id).map_err(|_e| ResolvedFileNotFound(id.clone()))?; let mut out = String::new(); file.read_to_string(&mut out) - .map_err(|_e| create_error(Error::ImportBadFileUtf8(id.clone())))?; + .map_err(|_e| ImportBadFileUtf8(id.clone()))?; Ok(out.into()) } unsafe fn as_any(&self) -> &dyn Any { --- a/crates/jrsonnet-evaluator/src/lib.rs +++ b/crates/jrsonnet-evaluator/src/lib.rs @@ -10,7 +10,7 @@ mod builtin; mod ctx; mod dynamic; -mod error; +pub mod error; mod evaluate; mod function; mod import; @@ -21,7 +21,7 @@ pub use ctx::*; pub use dynamic::*; -pub use error::*; +use error::{Error::*, LocError, Result, StackTraceElement}; pub use evaluate::*; pub use function::parse_function_call; pub use import::*; @@ -131,13 +131,7 @@ } pub(crate) fn with_state(f: impl FnOnce(&EvaluationState) -> T) -> T { EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap())) -} -pub fn create_error(err: Error) -> LocError { - LocError(err, StackTrace(vec![])) } -pub fn create_error_result(err: Error) -> Result { - Err(LocError(err, StackTrace(vec![]))) -} pub(crate) fn push( e: &Option, frame_desc: impl FnOnce() -> String, @@ -167,12 +161,10 @@ loc_data: true, }, ) - .map_err(|error| { - create_error(Error::ImportSyntaxError { - error, - path, - source_code, - }) + .map_err(|error| ImportSyntaxError { + error, + path, + source_code, })?, )?; @@ -292,7 +284,7 @@ if *stack_depth > self.max_stack() { // Error creation uses data, so i drop guard here drop(data); - return Err(create_error(Error::StackOverflow)); + throw!(StackOverflow); } else { *stack_depth += 1; } @@ -340,7 +332,7 @@ ManifestFormat::Json(padding) => val.into_json(padding)?, ManifestFormat::None => match val { Val::Str(s) => s, - _ => return Err(create_error(Error::StringManifestOutputIsNotAString)), + _ => throw!(StringManifestOutputIsNotAString), }, }) }) @@ -476,7 +468,7 @@ #[cfg(test)] pub mod tests { use super::Val; - use crate::{create_error, primitive_equals, EvaluationState}; + use crate::{error::Error::*, primitive_equals, EvaluationState}; use jrsonnet_parser::*; use std::{path::PathBuf, rc::Rc}; @@ -493,7 +485,7 @@ state.push( &ExprLocation(Rc::new(PathBuf::from("test2.jsonnet")), 30, 40), || "inner".to_owned(), - || Err(create_error(crate::error::Error::RuntimeError("".into()))), + || Err(RuntimeError("".into()).into()), )?; Ok(()) }, --- a/crates/jrsonnet-evaluator/src/obj.rs +++ b/crates/jrsonnet-evaluator/src/obj.rs @@ -11,11 +11,13 @@ pub location: Option, } +// Field => This +type CacheKey = (Rc, usize); #[derive(Debug)] pub struct ObjValueInternals { super_obj: Option, this_entries: Rc, ObjMember>>, - value_cache: RefCell, usize), Option>>, + value_cache: RefCell>>, } #[derive(Clone)] pub struct ObjValue(pub(crate) Rc); --- a/crates/jrsonnet-evaluator/src/val.rs +++ b/crates/jrsonnet-evaluator/src/val.rs @@ -1,7 +1,8 @@ use crate::{ - create_error_result, evaluate, + error::Error::*, + evaluate, function::{parse_function_call, parse_function_call_map, place_args}, - with_state, Context, Error, ObjValue, Result, + throw, with_state, Context, ObjValue, Result, }; use jrsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParamsDesc}; use std::{ @@ -157,14 +158,14 @@ if num.is_finite() { Ok(Val::Num(num)) } else { - create_error_result(Error::RuntimeError("overflow".into())) + throw!(RuntimeError("overflow".into())) } } pub fn assert_type(&self, context: &'static str, val_type: ValType) -> Result<()> { let this_type = self.value_type()?; if this_type != val_type { - create_error_result(Error::TypeMismatch(context, vec![val_type], this_type)) + throw!(TypeMismatch(context, vec![val_type], this_type)) } else { Ok(()) } @@ -263,15 +264,15 @@ (Val::Null, Val::Null) => true, (Val::Str(a), Val::Str(b)) => a == b, (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON, - (Val::Arr(_), Val::Arr(_)) => create_error_result(Error::RuntimeError( + (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError( "primitiveEquals operates on primitive types, got array".into(), - ))?, - (Val::Obj(_), Val::Obj(_)) => create_error_result(Error::RuntimeError( + )), + (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError( "primitiveEquals operates on primitive types, got object".into(), - ))?, - (a, b) if is_function_like(&a) && is_function_like(&b) => create_error_result( - Error::RuntimeError("cannot test equality of functions".into()), - )?, + )), + (a, b) if is_function_like(&a) && is_function_like(&b) => { + throw!(RuntimeError("cannot test equality of functions".into())) + } (_, _) => false, }) } @@ -376,7 +377,7 @@ buf.push('}'); } Val::Func(_) | Val::Intristic(_, _) => { - create_error_result(Error::RuntimeError("tried to manifest function".into()))? + throw!(RuntimeError("tried to manifest function".into())) } Val::Lazy(_) => unreachable!(), }; --- a/crates/jrsonnet-parser/src/expr.rs +++ b/crates/jrsonnet-parser/src/expr.rs @@ -258,7 +258,7 @@ /// importStr "file.txt" ImportStr(PathBuf), /// error "I'm broken" - Error(LocExpr), + ErrorStmt(LocExpr), /// a(b, c) Apply(LocExpr, ArgsDesc, bool), /// a[b] --- a/crates/jrsonnet-parser/src/lib.rs +++ b/crates/jrsonnet-parser/src/lib.rs @@ -197,7 +197,7 @@ / l(s,) / l(s,) - / l(s,) + / l(s,) rule slice_part(s: &ParserSettings) -> Option = e:(_ e:expr(s) _{e})? {e}