From 8eee21ff38d16c7302c543dcb344e5ea9675d90d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 28 Feb 2021 14:38:34 +0800 Subject: [PATCH] Use object map as catch error value. --- CHANGELOG.md | 4 +++ src/bin/rhai-repl.rs | 3 +- src/bin/rhai-run.rs | 3 +- src/engine.rs | 46 ++++++++++++++++++++++++--- src/result.rs | 74 +++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 117 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2b88cd..5b093301 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,20 @@ Bug fixes --------- * Errors in native Rust functions now contain the correct function call positions. +* Fixed error types in `EvalAltResult::ErrorMismatchDataType` which were swapped. Breaking changes ---------------- * Zero step in the `range` function now raises an error instead of creating an infinite stream. +* Error variable captured by `catch` is now an _object map_ containing error fields. +* `EvalAltResult::clear_position` is renamed `EvalAltResult::take_position` and returns the position taken. Enhancements ------------ * `range` function now supports negative step and decreasing streams (i.e. to < from). +* More information is provided to the error variable captured by the `catch` statement in an _object map_. Version 0.19.13 diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 0f911027..c5c91a18 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -13,8 +13,7 @@ use std::{ /// Pretty-print error. fn print_error(input: &str, mut err: EvalAltResult) { let lines: Vec<_> = input.trim().split('\n').collect(); - let pos = err.position(); - err.clear_position(); + let pos = err.take_position(); let line_no = if lines.len() > 1 { if pos.is_none() { diff --git a/src/bin/rhai-run.rs b/src/bin/rhai-run.rs index 24e60452..2ffe8423 100644 --- a/src/bin/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -23,8 +23,7 @@ fn eprint_error(input: &str, mut err: EvalAltResult) { let lines: Vec<_> = input.split('\n').collect(); // Print error - let pos = err.position(); - err.clear_position(); + let pos = err.take_position(); if pos.is_none() { // No position diff --git a/src/engine.rs b/src/engine.rs index 5d1957d8..705ef918 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -422,8 +422,8 @@ impl<'a> Target<'a> { // Replace the character at the specified index position let new_ch = new_val.as_char().map_err(|err| { Box::new(EvalAltResult::ErrorMismatchDataType( - err.to_string(), "char".to_string(), + err.to_string(), pos, )) })?; @@ -2245,19 +2245,55 @@ impl Engine { Err(err) if err.is_pseudo_error() => Err(err), Err(err) if !err.is_catchable() => Err(err), Err(mut err) => { - let value = match *err { + let err_value = match *err { EvalAltResult::ErrorRuntime(ref x, _) => x.clone(), + + #[cfg(feature = "no_object")] _ => { - err.set_position(Position::NONE); + err.take_position(); err.to_string().into() } + #[cfg(not(feature = "no_object"))] + _ => { + use crate::INT; + + let mut err_map: Map = Default::default(); + let err_pos = err.take_position(); + + err_map.insert("message".into(), err.to_string().into()); + + if let Some(ref source) = state.source { + err_map.insert("source".into(), source.clone().into()); + } + + if err_pos.is_none() { + // No position info + } else { + err_map.insert( + "line".into(), + (err_pos.line().unwrap() as INT).into(), + ); + err_map.insert( + "position".into(), + if err_pos.is_beginning_of_line() { + 0 + } else { + err_pos.position().unwrap() as INT + } + .into(), + ); + } + + err.dump_fields(&mut err_map); + err_map.into() + } }; let orig_scope_len = scope.len(); state.scope_level += 1; if let Some(Ident { name, .. }) = err_var { - scope.push(unsafe_cast_var_name_to_lifetime(&name), value); + scope.push(unsafe_cast_var_name_to_lifetime(&name), err_value); } let result = @@ -2589,8 +2625,8 @@ impl Engine { #[inline(always)] pub(crate) fn make_type_mismatch_err(&self, typ: &str, pos: Position) -> Box { EvalAltResult::ErrorMismatchDataType( - typ.into(), self.map_type_name(type_name::()).into(), + typ.into(), pos, ) .into() diff --git a/src/result.rs b/src/result.rs index 59adae47..c344729e 100644 --- a/src/result.rs +++ b/src/result.rs @@ -274,7 +274,7 @@ impl> From for Box { impl EvalAltResult { /// Is this a pseudo error? A pseudo error is one that does not occur naturally. /// - /// [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return] are pseudo errors. + /// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors. pub fn is_pseudo_error(&self) -> bool { match self { Self::LoopBreak(_, _) | Self::Return(_, _) => true, @@ -344,6 +344,69 @@ impl EvalAltResult { } } /// Get the [position][Position] of this error. + #[cfg(not(feature = "no_object"))] + pub(crate) fn dump_fields(&self, map: &mut crate::Map) { + map.insert( + "error".into(), + format!("{:?}", self).split('(').next().unwrap().into(), + ); + + match self { + Self::LoopBreak(_, _) | Self::Return(_, _) => (), + + Self::ErrorSystem(_, _) + | Self::ErrorParsing(_, _) + | Self::ErrorUnboundThis(_) + | Self::ErrorFor(_) + | Self::ErrorInExpr(_) + | Self::ErrorArithmetic(_, _) + | Self::ErrorTooManyOperations(_) + | Self::ErrorTooManyModules(_) + | Self::ErrorStackOverflow(_) + | Self::ErrorRuntime(_, _) => (), + + Self::ErrorFunctionNotFound(f, _) => { + map.insert("function".into(), f.into()); + } + Self::ErrorInFunctionCall(f, s, _, _) => { + map.insert("function".into(), f.into()); + map.insert("source".into(), s.into()); + } + Self::ErrorInModule(m, _, _) => { + map.insert("module".into(), m.into()); + } + Self::ErrorMismatchDataType(r, a, _) | Self::ErrorMismatchOutputType(r, a, _) => { + map.insert("requested".into(), r.into()); + map.insert("actual".into(), a.into()); + } + Self::ErrorArrayBounds(n, i, _) | Self::ErrorStringBounds(n, i, _) => { + map.insert("length".into(), (*n as INT).into()); + map.insert("index".into(), (*i as INT).into()); + } + Self::ErrorIndexingType(t, _) => { + map.insert("type".into(), t.into()); + } + Self::ErrorVariableNotFound(v, _) + | Self::ErrorDataRace(v, _) + | Self::ErrorAssignmentToConstant(v, _) => { + map.insert("variable".into(), v.into()); + } + Self::ErrorModuleNotFound(m, _) => { + map.insert("module".into(), m.into()); + } + Self::ErrorDotExpr(p, _) => { + map.insert("property".into(), p.into()); + } + + Self::ErrorDataTooLarge(t, _) => { + map.insert("type".into(), t.into()); + } + Self::ErrorTerminated(t, _) => { + map.insert("token".into(), t.clone()); + } + }; + } + /// Get the [position][Position] of this error. pub fn position(&self) -> Position { match self { Self::ErrorSystem(_, _) => Position::NONE, @@ -376,10 +439,13 @@ impl EvalAltResult { | Self::Return(_, pos) => *pos, } } - /// Clear the [position][Position] information of this error. - pub fn clear_position(&mut self) -> &mut Self { + /// Remove the [position][Position] information from this error and return it. + /// + /// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards. + pub fn take_position(&mut self) -> Position { + let pos = self.position(); self.set_position(Position::NONE); - self + pos } /// Override the [position][Position] of this error. pub fn set_position(&mut self, new_position: Position) {