Refine position display in error messages.
This commit is contained in:
@@ -871,9 +871,7 @@ impl Engine {
|
||||
|
||||
// If new functions are defined within the eval string, it is an error
|
||||
if ast.lib().num_fn() != 0 {
|
||||
return Err(Box::new(EvalAltResult::ErrorParsing(
|
||||
ParseErrorType::WrongFnDefinition.into_err(Position::none()),
|
||||
)));
|
||||
return Err(ParseErrorType::WrongFnDefinition.into());
|
||||
}
|
||||
|
||||
let statements = mem::take(ast.statements_mut());
|
||||
|
191
src/error.rs
191
src/error.rs
@@ -1,5 +1,6 @@
|
||||
//! Module containing error definitions for the parsing process.
|
||||
|
||||
use crate::result::EvalAltResult;
|
||||
use crate::token::Position;
|
||||
|
||||
use crate::stdlib::{boxed::Box, char, error::Error, fmt, string::String};
|
||||
@@ -123,113 +124,111 @@ impl ParseErrorType {
|
||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||
ParseError(Box::new(self), pos)
|
||||
}
|
||||
|
||||
pub(crate) fn desc(&self) -> &str {
|
||||
match self {
|
||||
Self::BadInput(p) => p,
|
||||
Self::UnexpectedEOF => "Script is incomplete",
|
||||
Self::UnknownOperator(_) => "Unknown operator",
|
||||
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
|
||||
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
||||
Self::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
||||
Self::MalformedInExpr(_) => "Invalid 'in' expression",
|
||||
Self::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
||||
Self::ForbiddenConstantExpr(_) => "Expecting a constant",
|
||||
Self::PropertyExpected => "Expecting name of a property",
|
||||
Self::VariableExpected => "Expecting name of a variable",
|
||||
Self::ExprExpected(_) => "Expecting an expression",
|
||||
Self::FnMissingName => "Expecting name in function declaration",
|
||||
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||
Self::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
||||
Self::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||
Self::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||
Self::DuplicatedExport(_) => "Duplicated variable/function in export statement",
|
||||
Self::WrongExport => "Export statement can only appear at global level",
|
||||
Self::AssignmentToCopy => "Only a copy of the value is change with this assignment",
|
||||
Self::AssignmentToConstant(_) => "Cannot assign to a constant value",
|
||||
Self::ExprTooDeep => "Expression exceeds maximum complexity",
|
||||
Self::LoopBreak => "Break statement should only be used inside a loop"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseErrorType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
|
||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })
|
||||
}
|
||||
Self::ForbiddenConstantExpr(s) => {
|
||||
write!(f, "Expecting a constant to assign to '{}'", s)
|
||||
}
|
||||
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||
|
||||
Self::MalformedIndexExpr(s) => {
|
||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })
|
||||
}
|
||||
|
||||
Self::MalformedInExpr(s) => write!(f, "{}", if s.is_empty() { self.desc() } else { s }),
|
||||
|
||||
Self::DuplicatedProperty(s) => {
|
||||
write!(f, "Duplicated property '{}' for object map literal", s)
|
||||
}
|
||||
|
||||
Self::ExprExpected(s) => write!(f, "Expecting {} expression", s),
|
||||
|
||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
||||
|
||||
Self::FnMissingBody(s) => {
|
||||
write!(f, "Expecting body statement block for function '{}'", s)
|
||||
}
|
||||
|
||||
Self::FnDuplicatedParam(s, arg) => {
|
||||
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)
|
||||
}
|
||||
|
||||
Self::DuplicatedExport(s) => write!(
|
||||
f,
|
||||
"Duplicated variable/function '{}' in export statement",
|
||||
s
|
||||
),
|
||||
|
||||
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
||||
|
||||
Self::AssignmentToConstant(s) if s.is_empty() => write!(f, "{}", self.desc()),
|
||||
Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant '{}'", s),
|
||||
_ => write!(f, "{}", self.desc()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error when parsing a script.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
pub struct ParseError(pub(crate) Box<ParseErrorType>, pub(crate) Position);
|
||||
|
||||
impl ParseError {
|
||||
/// Get the parse error.
|
||||
pub fn error_type(&self) -> &ParseErrorType {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Get the location in the script of the error.
|
||||
pub fn position(&self) -> Position {
|
||||
self.1
|
||||
}
|
||||
|
||||
pub(crate) fn desc(&self) -> &str {
|
||||
match self.0.as_ref() {
|
||||
ParseErrorType::BadInput(p) => p,
|
||||
ParseErrorType::UnexpectedEOF => "Script is incomplete",
|
||||
ParseErrorType::UnknownOperator(_) => "Unknown operator",
|
||||
ParseErrorType::MissingToken(_, _) => "Expecting a certain token that is missing",
|
||||
ParseErrorType::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
||||
ParseErrorType::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
||||
ParseErrorType::MalformedInExpr(_) => "Invalid 'in' expression",
|
||||
ParseErrorType::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
||||
ParseErrorType::ForbiddenConstantExpr(_) => "Expecting a constant",
|
||||
ParseErrorType::PropertyExpected => "Expecting name of a property",
|
||||
ParseErrorType::VariableExpected => "Expecting name of a variable",
|
||||
ParseErrorType::ExprExpected(_) => "Expecting an expression",
|
||||
ParseErrorType::FnMissingName => "Expecting name in function declaration",
|
||||
ParseErrorType::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||
ParseErrorType::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
||||
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||
ParseErrorType::DuplicatedExport(_) => "Duplicated variable/function in export statement",
|
||||
ParseErrorType::WrongExport => "Export statement can only appear at global level",
|
||||
ParseErrorType::AssignmentToCopy => "Only a copy of the value is change with this assignment",
|
||||
ParseErrorType::AssignmentToConstant(_) => "Cannot assign to a constant value",
|
||||
ParseErrorType::ExprTooDeep => "Expression exceeds maximum complexity",
|
||||
ParseErrorType::LoopBreak => "Break statement should only be used inside a loop"
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct ParseError(pub Box<ParseErrorType>, pub Position);
|
||||
|
||||
impl Error for ParseError {}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0.as_ref() {
|
||||
ParseErrorType::BadInput(s) | ParseErrorType::MalformedCallExpr(s) => {
|
||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
||||
}
|
||||
ParseErrorType::ForbiddenConstantExpr(s) => {
|
||||
write!(f, "Expecting a constant to assign to '{}'", s)?
|
||||
}
|
||||
ParseErrorType::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s)?,
|
||||
|
||||
ParseErrorType::MalformedIndexExpr(s) => {
|
||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
||||
}
|
||||
|
||||
ParseErrorType::MalformedInExpr(s) => {
|
||||
write!(f, "{}", if s.is_empty() { self.desc() } else { s })?
|
||||
}
|
||||
|
||||
ParseErrorType::DuplicatedProperty(s) => {
|
||||
write!(f, "Duplicated property '{}' for object map literal", s)?
|
||||
}
|
||||
|
||||
ParseErrorType::ExprExpected(s) => write!(f, "Expecting {} expression", s)?,
|
||||
|
||||
ParseErrorType::FnMissingParams(s) => {
|
||||
write!(f, "Expecting parameters for function '{}'", s)?
|
||||
}
|
||||
|
||||
ParseErrorType::FnMissingBody(s) => {
|
||||
write!(f, "Expecting body statement block for function '{}'", s)?
|
||||
}
|
||||
|
||||
ParseErrorType::FnDuplicatedParam(s, arg) => {
|
||||
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)?
|
||||
}
|
||||
|
||||
ParseErrorType::DuplicatedExport(s) => write!(
|
||||
f,
|
||||
"Duplicated variable/function '{}' in export statement",
|
||||
s
|
||||
)?,
|
||||
|
||||
ParseErrorType::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s)?,
|
||||
|
||||
ParseErrorType::AssignmentToConstant(s) if s.is_empty() => {
|
||||
write!(f, "{}", self.desc())?
|
||||
}
|
||||
ParseErrorType::AssignmentToConstant(s) => {
|
||||
write!(f, "Cannot assign to constant '{}'", s)?
|
||||
}
|
||||
_ => write!(f, "{}", self.desc())?,
|
||||
}
|
||||
fmt::Display::fmt(&self.0, f)?;
|
||||
|
||||
// Do not write any position if None
|
||||
if !self.1.is_none() {
|
||||
// Do not write any position if None
|
||||
Ok(())
|
||||
} else {
|
||||
write!(f, " ({})", self.1)
|
||||
write!(f, " ({})", self.1)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseErrorType> for Box<EvalAltResult> {
|
||||
fn from(err: ParseErrorType) -> Self {
|
||||
Box::new(EvalAltResult::ErrorParsing(err, Position::none()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for Box<EvalAltResult> {
|
||||
fn from(err: ParseError) -> Self {
|
||||
Box::new(EvalAltResult::ErrorParsing(*err.0, err.1))
|
||||
}
|
||||
}
|
||||
|
148
src/result.rs
148
src/result.rs
@@ -1,7 +1,7 @@
|
||||
//! Module containing error definitions for the evaluation process.
|
||||
|
||||
use crate::any::Dynamic;
|
||||
use crate::error::ParseError;
|
||||
use crate::error::ParseErrorType;
|
||||
use crate::parser::INT;
|
||||
use crate::token::Position;
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::stdlib::path::PathBuf;
|
||||
#[derive(Debug)]
|
||||
pub enum EvalAltResult {
|
||||
/// Syntax error.
|
||||
ErrorParsing(ParseError),
|
||||
ErrorParsing(ParseErrorType, Position),
|
||||
|
||||
/// Error reading from a script file. Wrapped value is the path of the script file.
|
||||
///
|
||||
@@ -101,7 +101,7 @@ impl EvalAltResult {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Self::ErrorReadingScriptFile(_, _, _) => "Cannot read from script file",
|
||||
|
||||
Self::ErrorParsing(p) => p.desc(),
|
||||
Self::ErrorParsing(p, _) => p.desc(),
|
||||
Self::ErrorInFunctionCall(_, _, _) => "Error in called function",
|
||||
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
||||
Self::ErrorBooleanArgMismatch(_, _) => "Boolean operator expects boolean operands",
|
||||
@@ -153,95 +153,89 @@ impl Error for EvalAltResult {}
|
||||
impl fmt::Display for EvalAltResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let desc = self.desc();
|
||||
let pos = self.position();
|
||||
|
||||
match self {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Self::ErrorReadingScriptFile(path, pos, err) if pos.is_none() => {
|
||||
write!(f, "{} '{}': {}", desc, path.display(), err)
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Self::ErrorReadingScriptFile(path, pos, err) => {
|
||||
write!(f, "{} '{}': {} ({})", desc, path.display(), err, pos)
|
||||
Self::ErrorReadingScriptFile(path, _, err) => {
|
||||
write!(f, "{} '{}': {}", desc, path.display(), err)?
|
||||
}
|
||||
|
||||
Self::ErrorParsing(p) => write!(f, "Syntax error: {}", p),
|
||||
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
||||
|
||||
Self::ErrorInFunctionCall(s, err, pos) => {
|
||||
write!(f, "Error in call to function '{}' ({}): {}", s, pos, err)
|
||||
Self::ErrorInFunctionCall(s, err, _) => {
|
||||
write!(f, "Error in call to function '{}' : {}", s, err)?
|
||||
}
|
||||
|
||||
Self::ErrorFunctionNotFound(s, pos)
|
||||
| Self::ErrorVariableNotFound(s, pos)
|
||||
| Self::ErrorModuleNotFound(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos),
|
||||
Self::ErrorFunctionNotFound(s, _)
|
||||
| Self::ErrorVariableNotFound(s, _)
|
||||
| Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
||||
|
||||
Self::ErrorDotExpr(s, pos) if !s.is_empty() => write!(f, "{} {} ({})", desc, s, pos),
|
||||
Self::ErrorDotExpr(s, _) if !s.is_empty() => write!(f, "{} {}", desc, s)?,
|
||||
|
||||
Self::ErrorIndexingType(_, pos)
|
||||
| Self::ErrorNumericIndexExpr(pos)
|
||||
| Self::ErrorStringIndexExpr(pos)
|
||||
| Self::ErrorImportExpr(pos)
|
||||
| Self::ErrorLogicGuard(pos)
|
||||
| Self::ErrorFor(pos)
|
||||
| Self::ErrorAssignmentToUnknownLHS(pos)
|
||||
| Self::ErrorInExpr(pos)
|
||||
| Self::ErrorDotExpr(_, pos)
|
||||
| Self::ErrorTooManyOperations(pos)
|
||||
| Self::ErrorTooManyModules(pos)
|
||||
| Self::ErrorStackOverflow(pos)
|
||||
| Self::ErrorTerminated(pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorIndexingType(_, _)
|
||||
| Self::ErrorNumericIndexExpr(_)
|
||||
| Self::ErrorStringIndexExpr(_)
|
||||
| Self::ErrorImportExpr(_)
|
||||
| Self::ErrorLogicGuard(_)
|
||||
| Self::ErrorFor(_)
|
||||
| Self::ErrorAssignmentToUnknownLHS(_)
|
||||
| Self::ErrorInExpr(_)
|
||||
| Self::ErrorDotExpr(_, _)
|
||||
| Self::ErrorTooManyOperations(_)
|
||||
| Self::ErrorTooManyModules(_)
|
||||
| Self::ErrorStackOverflow(_)
|
||||
| Self::ErrorTerminated(_) => write!(f, "{}", desc)?,
|
||||
|
||||
Self::ErrorRuntime(s, pos) => {
|
||||
write!(f, "{} ({})", if s.is_empty() { desc } else { s }, pos)
|
||||
Self::ErrorRuntime(s, _) => write!(f, "{}", if s.is_empty() { desc } else { s })?,
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
||||
Self::ErrorMismatchOutputType(s, _) => write!(f, "{}: {}", desc, s)?,
|
||||
Self::ErrorArithmetic(s, _) => write!(f, "{}", s)?,
|
||||
|
||||
Self::ErrorLoopBreak(_, _) => write!(f, "{}", desc)?,
|
||||
Self::Return(_, _) => write!(f, "{}", desc)?,
|
||||
|
||||
Self::ErrorBooleanArgMismatch(op, _) => {
|
||||
write!(f, "{} operator expects boolean operands", op)?
|
||||
}
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, pos) => write!(f, "{}: '{}' ({})", desc, s, pos),
|
||||
Self::ErrorMismatchOutputType(s, pos) => write!(f, "{}: {} ({})", desc, s, pos),
|
||||
Self::ErrorArithmetic(s, pos) => write!(f, "{} ({})", s, pos),
|
||||
|
||||
Self::ErrorLoopBreak(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::Return(_, pos) => write!(f, "{} ({})", desc, pos),
|
||||
|
||||
Self::ErrorBooleanArgMismatch(op, pos) => {
|
||||
write!(f, "{} operator expects boolean operands ({})", op, pos)
|
||||
Self::ErrorCharMismatch(_) => write!(f, "string indexing expects a character value")?,
|
||||
Self::ErrorArrayBounds(_, index, _) if *index < 0 => {
|
||||
write!(f, "{}: {} < 0", desc, index)?
|
||||
}
|
||||
Self::ErrorCharMismatch(pos) => {
|
||||
write!(f, "string indexing expects a character value ({})", pos)
|
||||
}
|
||||
Self::ErrorArrayBounds(_, index, pos) if *index < 0 => {
|
||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
||||
}
|
||||
Self::ErrorArrayBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorArrayBounds(1, index, pos) => write!(
|
||||
Self::ErrorArrayBounds(0, _, _) => write!(f, "{}", desc)?,
|
||||
Self::ErrorArrayBounds(1, index, _) => write!(
|
||||
f,
|
||||
"Array index {} is out of bounds: only one element in the array ({})",
|
||||
index, pos
|
||||
),
|
||||
Self::ErrorArrayBounds(max, index, pos) => write!(
|
||||
"Array index {} is out of bounds: only one element in the array",
|
||||
index
|
||||
)?,
|
||||
Self::ErrorArrayBounds(max, index, _) => write!(
|
||||
f,
|
||||
"Array index {} is out of bounds: only {} elements in the array ({})",
|
||||
index, max, pos
|
||||
),
|
||||
Self::ErrorStringBounds(_, index, pos) if *index < 0 => {
|
||||
write!(f, "{}: {} < 0 ({})", desc, index, pos)
|
||||
"Array index {} is out of bounds: only {} elements in the array",
|
||||
index, max
|
||||
)?,
|
||||
Self::ErrorStringBounds(_, index, _) if *index < 0 => {
|
||||
write!(f, "{}: {} < 0", desc, index)?
|
||||
}
|
||||
Self::ErrorStringBounds(0, _, pos) => write!(f, "{} ({})", desc, pos),
|
||||
Self::ErrorStringBounds(1, index, pos) => write!(
|
||||
Self::ErrorStringBounds(0, _, _) => write!(f, "{}", desc)?,
|
||||
Self::ErrorStringBounds(1, index, _) => write!(
|
||||
f,
|
||||
"String index {} is out of bounds: only one character in the string ({})",
|
||||
index, pos
|
||||
),
|
||||
Self::ErrorStringBounds(max, index, pos) => write!(
|
||||
"String index {} is out of bounds: only one character in the string",
|
||||
index
|
||||
)?,
|
||||
Self::ErrorStringBounds(max, index, _) => write!(
|
||||
f,
|
||||
"String index {} is out of bounds: only {} characters in the string ({})",
|
||||
index, max, pos
|
||||
),
|
||||
"String index {} is out of bounds: only {} characters in the string",
|
||||
index, max
|
||||
)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for Box<EvalAltResult> {
|
||||
fn from(err: ParseError) -> Self {
|
||||
Box::new(EvalAltResult::ErrorParsing(err))
|
||||
// Do not write any position if None
|
||||
if !pos.is_none() {
|
||||
write!(f, " ({})", pos)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,9 +255,8 @@ impl EvalAltResult {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Self::ErrorReadingScriptFile(_, pos, _) => *pos,
|
||||
|
||||
Self::ErrorParsing(err) => err.position(),
|
||||
|
||||
Self::ErrorFunctionNotFound(_, pos)
|
||||
Self::ErrorParsing(_, pos)
|
||||
| Self::ErrorFunctionNotFound(_, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, pos)
|
||||
| Self::ErrorBooleanArgMismatch(_, pos)
|
||||
| Self::ErrorCharMismatch(pos)
|
||||
@@ -299,9 +292,8 @@ impl EvalAltResult {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Self::ErrorReadingScriptFile(_, pos, _) => *pos = new_position,
|
||||
|
||||
Self::ErrorParsing(err) => err.1 = new_position,
|
||||
|
||||
Self::ErrorFunctionNotFound(_, pos)
|
||||
Self::ErrorParsing(_, pos)
|
||||
| Self::ErrorFunctionNotFound(_, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, pos)
|
||||
| Self::ErrorBooleanArgMismatch(_, pos)
|
||||
| Self::ErrorCharMismatch(pos)
|
||||
|
Reference in New Issue
Block a user