diff --git a/CHANGELOG b/CHANGELOG index ac1d100..2710aa5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +0.5.3 +----- +Make errors more useful + 0.5.2 ----- Make extensions a feature that can be disabled diff --git a/Cargo.lock b/Cargo.lock index 8566d0d..f0469d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -509,7 +509,7 @@ dependencies = [ [[package]] name = "lavendeux-parser" -version = "0.5.2" +version = "0.5.3" dependencies = [ "chrono", "derive_more", diff --git a/Cargo.toml b/Cargo.toml index e61953f..6b1ecea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ categories = ["parser-implementations", "development-tools", "command-line-utili homepage = "https://rscarson.github.io/Lavendeux/" repository = "https://github.com/rscarson/lavendeux-parser" readme = "readme.md" -version = "0.5.2" +version = "0.5.3" edition = "2021" [features] diff --git a/src/errors.rs b/src/errors.rs index c0ab720..7989426 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,4 +1,5 @@ extern crate pest; +use crate::Token; use std::error::Error; use std::fmt; @@ -21,10 +22,10 @@ pub enum ParserError { Pest(PestError), /// An error caused by attempting to parse an invalid integer value - ParseInt(std::num::ParseIntError), + ParseInt(ParseIntegerError), /// An error caused by attempting to parse an invalid floating point value - ParseFloat(std::num::ParseFloatError), + ParseFloat(ParseFloatingPointError), /// An error caused by attempting to use a value of the wrong type in a calculation ValueType(ValueTypeError), @@ -135,37 +136,92 @@ impl fmt::Display for ExpectedTypes { /// Occurs when parsing the grammar of an expression fails #[derive(Debug, Clone)] -pub struct PestError {cause: String} +pub struct PestError {pos: Option, cause: String} error_type!(PestError, { /// Create a new instance of the error /// /// # Arguments /// * `cause` - Underlying parsing error - pub fn new (cause: &str) -> Self { - Self { cause: cause.to_string() } + pub fn new(cause: &str) -> Self { + Self::new_with_index(None, cause) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `cause` - Underlying parsing error + pub fn new_with_token(token: &Token, cause: &str) -> Self { + Self::new_with_index(Some(token.index()), cause) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `cause` - Underlying parsing error + pub fn new_with_index(pos: Option, cause: &str) -> Self { + Self { pos, cause: cause.to_string() } } /// Return the cause of the error pub fn cause(&self) -> &str { &self.cause } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", "unable to parse expression") + write!(f, "{}", "unexpected token in expression")?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when a recursive function goes too deep #[derive(Debug, Clone)] -pub struct StackError {} +pub struct StackError {pos: Option} error_type!(StackError, { /// Create a new instance of the error pub fn new() -> Self { - Self { } + Self::new_with_index(None) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `cause` - Underlying parsing error + pub fn new_with_token(token: &Token) -> Self { + Self::new_with_index(Some(token.index())) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + pub fn new_with_index(pos: Option) -> Self { + Self {pos} + } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", "recursive function went too deep") + write!(f, "{}", "recursive function went too deep")?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); impl Default for StackError { diff --git a/src/errors/functions.rs b/src/errors/functions.rs index 72c9b51..5946709 100644 --- a/src/errors/functions.rs +++ b/src/errors/functions.rs @@ -1,78 +1,162 @@ use std::error::Error; use std::fmt; - +use crate::Token; use super::ExpectedTypes; use super::error_macro; /// Occurs when a JS extension throws an unexpected error #[derive(Debug, Clone)] -pub struct ScriptError {error: String} +pub struct ScriptError {pos: Option, error: String} error_macro::error_type!(ScriptError, { /// Create a new instance of the error /// /// # Arguments /// * `error` - Underlying error - pub fn new (error: &str) -> Self { - Self { error: error.to_string() } + pub fn new(error: &str) -> Self { + Self::new_with_index(None, error) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `error` - Underlying error + pub fn new_with_token(token: &Token, error: &str) -> Self { + Self::new_with_index(Some(token.index()), error) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `error` - Underlying error + pub fn new_with_index(pos: Option, error: &str) -> Self { + Self { pos, error: error.to_string() } } /// Error source pub fn error(&self) -> &str { &self.error } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "error executing extension: {}", self.error) + write!(f, "error executing extension: {}", self.error)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when a decorator was not found #[derive(Debug, Clone)] -pub struct DecoratorNameError {name: String} +pub struct DecoratorNameError {pos: Option, name: String} error_macro::error_type!(DecoratorNameError, { /// Create a new instance of the error /// /// # Arguments /// * `name` - Decorator name - pub fn new (name: &str) -> Self { - Self { name: name.to_string() } + pub fn new(name: &str) -> Self { + Self::new_with_index(None, name) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `name` - Decorator name + pub fn new_with_token(token: &Token, name: &str) -> Self { + Self::new_with_index(Some(token.index()), name) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `name` - Decorator name + pub fn new_with_index(pos: Option, name: &str) -> Self { + Self { pos, name: name.to_string() } } /// Decorator name pub fn name(&self) -> &str { &self.name } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "no such decorator {}", self.name) + write!(f, "no such decorator {}", self.name)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when a function was not found #[derive(Debug, Clone)] -pub struct FunctionNameError {name: String} +pub struct FunctionNameError {pos: Option, name: String} error_macro::error_type!(FunctionNameError, { /// Create a new instance of the error /// /// # Arguments /// * `name` - Function name - pub fn new (name: &str) -> Self { - Self { name: name.to_string() } + pub fn new(name: &str) -> Self { + Self::new_with_index(None, name) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `name` - Function name + pub fn new_with_token(token: &Token, name: &str) -> Self { + Self::new_with_index(Some(token.index()), name) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `name` - Function name + pub fn new_with_index(pos: Option, name: &str) -> Self { + Self { pos, name: name.to_string() } } /// Function name pub fn name(&self) -> &str { &self.name } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "no such function {}", self.name) + write!(f, "no such function {}", self.name)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when supplying a function with the wrong type of argument #[derive(Debug, Clone)] -pub struct FunctionArgTypeError {arg: usize, expected: ExpectedTypes, signature: String} +pub struct FunctionArgTypeError {pos: Option, arg: usize, expected: ExpectedTypes, signature: String} error_macro::error_type!(FunctionArgTypeError, { /// Create a new instance of the error /// @@ -81,7 +165,29 @@ error_macro::error_type!(FunctionArgTypeError, { /// * `arg` - 1-indexed argument number /// * `expected` - Expected type of argument pub fn new (signature: &str, arg: usize, expected: ExpectedTypes) -> Self { - Self { arg, expected, signature: signature.to_string() } + Self::new_with_index(None, signature, arg, expected) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `signature` - Function call signature + /// * `arg` - 1-indexed argument number + /// * `expected` - Expected type of argument + pub fn new_with_token(token: &Token, signature: &str, arg: usize, expected: ExpectedTypes) -> Self { + Self::new_with_index(Some(token.index()), signature, arg, expected) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `signature` - Function call signature + /// * `arg` - 1-indexed argument number + /// * `expected` - Expected type of argument + pub fn new_with_index(pos: Option, signature: &str, arg: usize, expected: ExpectedTypes) -> Self { + Self { pos, arg, expected, signature: signature.to_string() } } /// Function call signature @@ -98,23 +204,53 @@ error_macro::error_type!(FunctionArgTypeError, { pub fn arg(&self) -> usize { self.arg } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: invalid type for argument {} (expected {})", self.signature, self.arg, self.expected) + write!(f, "{}: invalid type for argument {} (expected {})", self.signature, self.arg, self.expected)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when a function argument causes an overflow #[derive(Debug, Clone)] -pub struct FunctionArgOverFlowError {arg: usize, signature: String} +pub struct FunctionArgOverFlowError {pos: Option, arg: usize, signature: String} error_macro::error_type!(FunctionArgOverFlowError, { /// Create a new instance of the error /// /// # Arguments /// * `signature` - Function call signature /// * `arg` - 1-indexed argument number - pub fn new (signature: &str, arg: usize) -> Self { - Self { arg, signature: signature.to_string() } + pub fn new(signature: &str, arg: usize) -> Self { + Self::new_with_index(None, signature, arg) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `signature` - Function call signature + /// * `arg` - 1-indexed argument number + pub fn new_with_token(token: &Token, signature: &str, arg: usize) -> Self { + Self::new_with_index(Some(token.index()), signature, arg) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `signature` - Function call signature + /// * `arg` - 1-indexed argument number + pub fn new_with_index(pos: Option, signature: &str, arg: usize) -> Self { + Self { pos, arg, signature: signature.to_string() } } /// Function call signature @@ -126,15 +262,25 @@ error_macro::error_type!(FunctionArgOverFlowError, { pub fn arg(&self) -> usize { self.arg } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: overflow in argument {}", self.signature, self.arg) + write!(f, "{}: overflow in argument {}", self.signature, self.arg)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when a function is called with the wrong number of arguments #[derive(Debug, Clone)] -pub struct FunctionNArgError {min: usize, max: usize, signature: String} +pub struct FunctionNArgError {pos: Option, min: usize, max: usize, signature: String} error_macro::error_type!(FunctionNArgError, { /// Create a new instance of the error /// @@ -142,8 +288,30 @@ error_macro::error_type!(FunctionNArgError, { /// * `signature` - Function call signature /// * `min` - Smallest allowed number of arguments /// * `max` - Largest allowed number of arguments - pub fn new (signature: &str, min: usize, max: usize) -> Self { - Self { min, max, signature: signature.to_string() } + pub fn new(signature: &str, min: usize, max: usize) -> Self { + Self::new_with_index(None, signature, min, max ) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `signature` - Function call signature + /// * `min` - Smallest allowed number of arguments + /// * `max` - Largest allowed number of arguments + pub fn new_with_token(token: &Token, signature: &str, min: usize, max: usize) -> Self { + Self::new_with_index(Some(token.index()), signature, min, max) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `signature` - Function call signature + /// * `min` - Smallest allowed number of arguments + /// * `max` - Largest allowed number of arguments + pub fn new_with_index(pos: Option, signature: &str, min: usize, max: usize) -> Self { + Self { pos, min, max, signature: signature.to_string() } } /// Function call signature @@ -160,12 +328,23 @@ error_macro::error_type!(FunctionNArgError, { pub fn max(&self) -> usize { self.max } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.min == self.max { - write!(f, "{}: expected {} args", self.signature, self.min) + write!(f, "{}: expected {} args", self.signature, self.min)?; } else { - write!(f, "{}: expected {}-{} args", self.signature, self.min, self.max) + write!(f, "{}: expected {}-{} args", self.signature, self.min, self.max)?; } + + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); \ No newline at end of file diff --git a/src/errors/values.rs b/src/errors/values.rs index afea364..94ff2e2 100644 --- a/src/errors/values.rs +++ b/src/errors/values.rs @@ -1,22 +1,141 @@ extern crate pest; +use crate::Token; use std::error::Error; use std::fmt; use super::ExpectedTypes; use super::error_macro; +/// Occurs when attempting to parse an invalid integer +#[derive(Debug, Clone)] +pub struct ParseIntegerError {pos: Option, cause: String} +error_macro::error_type!(ParseIntegerError, { + /// Create a new instance of the error + pub fn new(cause: &str) -> Self { + Self::new_with_index(None, cause) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `cause` - Underlying parsing error + pub fn new_with_token(token: &Token, cause: &str) -> Self { + Self::new_with_index(Some(token.index()), cause) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + pub fn new_with_index(pos: Option, cause: &str) -> Self { + Self {pos, cause: cause.to_string()} + } + + /// Return the cause of the error + pub fn cause(&self) -> &str { + &self.cause + } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } +}, { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "could not parse integer: {}", self.cause)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) + } +}); + +/// Occurs when attempting to parse an invalid float +#[derive(Debug, Clone)] +pub struct ParseFloatingPointError {pos: Option, cause: String} +error_macro::error_type!(ParseFloatingPointError, { + /// Create a new instance of the error + pub fn new(cause: &str) -> Self { + Self::new_with_index(None, cause) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `cause` - Underlying parsing error + pub fn new_with_token(token: &Token, cause: &str) -> Self { + Self::new_with_index(Some(token.index()), cause) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + pub fn new_with_index(pos: Option, cause: &str) -> Self { + Self {pos, cause: cause.to_string()} + } + + /// Return the cause of the error + pub fn cause(&self) -> &str { + &self.cause + } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } +}, { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "could not parse float")?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) + } +}); + /// Occurs when a calculation causes an underflow #[derive(Debug, Clone)] -pub struct UnderflowError {} +pub struct UnderflowError {pos: Option} error_macro::error_type!(UnderflowError, { /// Create a new instance of the error - pub fn new () -> Self { - Self {} + pub fn new() -> Self { + Self::new_with_index(None) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + pub fn new_with_token(token: &Token) -> Self { + Self::new_with_index(Some(token.index())) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + pub fn new_with_index(pos: Option) -> Self { + Self {pos} + } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "arithmetic underflow") + write!(f, "arithmetic underflow")?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); impl Default for UnderflowError { @@ -25,17 +144,43 @@ impl Default for UnderflowError { } } -/// Occurs when a calculation causes an underflow +/// Occurs when a calculation causes an overflow #[derive(Debug, Clone)] -pub struct OverflowError {} +pub struct OverflowError {pos: Option} error_macro::error_type!(OverflowError, { /// Create a new instance of the error - pub fn new () -> Self { - Self {} + pub fn new() -> Self { + Self::new_with_index(None) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + pub fn new_with_token(token: &Token) -> Self { + Self::new_with_index(Some(token.index())) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + pub fn new_with_index(pos: Option) -> Self { + Self {pos} + } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "arithmetic overflow") + write!(f, "arithmetic overflow")?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); impl Default for OverflowError { @@ -46,66 +191,150 @@ impl Default for OverflowError { /// Occurs when attempting an operation with the wrong type of value #[derive(Debug, Clone)] -pub struct ValueTypeError {expected: ExpectedTypes} +pub struct ValueTypeError {pos: Option, expected: ExpectedTypes} error_macro::error_type!(ValueTypeError, { /// Create a new instance of the error /// /// # Arguments /// * `expected` - Expected type of value - pub fn new (expected: ExpectedTypes) -> Self { - Self {expected} + pub fn new(expected: ExpectedTypes) -> Self { + Self::new_with_index(None, expected) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `expected` - Expected type of value + pub fn new_with_token(token: &Token, expected: ExpectedTypes) -> Self { + Self::new_with_index(Some(token.index()), expected) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `expected` - Expected type of value + pub fn new_with_index(pos: Option, expected: ExpectedTypes) -> Self { + Self {pos, expected} } /// The expected value type pub fn expected(&self) -> ExpectedTypes { self.expected.clone() } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "invalid type for value (expected {})", self.expected) + write!(f, "invalid type for value (expected {})", self.expected)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when attempting to overwrite a constant #[derive(Debug, Clone)] -pub struct ConstantValueError {name: String} +pub struct ConstantValueError {pos: Option, name: String} error_macro::error_type!(ConstantValueError, { /// Create a new instance of the error /// /// # Arguments /// * `name` - Constant that could not be overwritten - pub fn new (name: String) -> Self { - Self {name} + pub fn new(name: &str) -> Self { + Self::new_with_index(None, name) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `name` - Constant that could not be overwritten + pub fn new_with_token(token: &Token, name: &str) -> Self { + Self::new_with_index(Some(token.index()), name) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `name` - Constant that could not be overwritten + pub fn new_with_index(pos: Option, name: &str) -> Self { + Self {pos, name: name.to_string()} } /// The constant's name pub fn name(&self) -> &str { &self.name } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "cannot assign to constant '{}'", self.name) + write!(f, "cannot assign to constant '{}'", self.name)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); /// Occurs when attempting to use an unknown variable #[derive(Debug, Clone)] -pub struct VariableNameError {name: String} +pub struct VariableNameError {pos: Option, name: String} error_macro::error_type!(VariableNameError, { /// Create a new instance of the error /// /// # Arguments /// * `name` - Variable that was used - pub fn new (name: String) -> Self { - Self {name} + pub fn new(name: &str) -> Self { + Self::new_with_index(None, name) + } + + /// Create a new instance of the error caused by a token + /// + /// # Arguments + /// * `token` - Token causing the error + /// * `name` - Variable that was used + pub fn new_with_token(token: &Token, name: &str) -> Self { + Self::new_with_index(Some(token.index()), name) + } + + /// Create a new instance of the error at a specific position + /// + /// # Arguments + /// * `pos` - Index at which the error occured + /// * `name` - Variable that was used + pub fn new_with_index(pos: Option, name: &str) -> Self { + Self {pos, name: name.to_string()} } /// The variable's name pub fn name(&self) -> &str { &self.name } + + /// Return the location at which the error occured + pub fn pos(&self) -> Option { + self.pos + } }, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "uninitialized variable '{}' referenced", self.name) + write!(f, "uninitialized variable '{}' referenced", self.name)?; + if let Some(pos) = self.pos { + write!(f, " at position {}", pos)?; + } + + fmt::Result::Ok(()) } }); diff --git a/src/handlers.rs b/src/handlers.rs index 2018b90..a423439 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -83,7 +83,7 @@ fn expression_handler(token: &mut Token, state: &mut ParserState) -> Option { if state.constants.contains_key(token.child(0).unwrap().text()) { - return Some(ParserError::ContantValue(ConstantValueError::new(token.child(0).unwrap().text().to_string()))) + return Some(ParserError::ContantValue(ConstantValueError::new_with_token(token, token.child(0).unwrap().text()))) } else { state.variables.insert(token.child(0).unwrap().text().to_string(), token.child(2).unwrap().value()); token.set_value(token.child(2).unwrap().value()); diff --git a/src/handlers/boolean.rs b/src/handlers/boolean.rs index 212e5eb..e056516 100644 --- a/src/handlers/boolean.rs +++ b/src/handlers/boolean.rs @@ -44,6 +44,8 @@ pub fn bool_expression_handler(token: &mut Token, _state: &mut ParserState) -> O Rule::ne => token.set_value(Value::Boolean(l.as_float().unwrap() != r.as_float().unwrap())), _ => {} } + } else { + token.set_value(Value::Boolean(false)); } i += 2; diff --git a/src/handlers/callable.rs b/src/handlers/callable.rs index b422b57..d308e5d 100644 --- a/src/handlers/callable.rs +++ b/src/handlers/callable.rs @@ -44,7 +44,7 @@ pub fn call_expression_handler(token: &mut Token, state: &mut ParserState) -> Op // User-defined functions if let Some(f) = state.user_functions.get(name) { if args.len() != f.arguments.len() { - return Some(ParserError::FunctionNArg(FunctionNArgError::new(&f.name, f.arguments.len(), f.arguments.len()))); + return Some(ParserError::FunctionNArg(FunctionNArgError::new_with_token(token, &f.name, f.arguments.len(), f.arguments.len()))); } if let Some(mut inner_state) = state.spawn_inner() { @@ -63,12 +63,12 @@ pub fn call_expression_handler(token: &mut Token, state: &mut ParserState) -> Op Err(e) => { return Some(e); } } } else { - return Some(ParserError::Stack(StackError::new())); + return Some(ParserError::Stack(StackError::new_with_token(token))); } } } - return Some(ParserError::FunctionName(FunctionNameError::new(name))); + return Some(ParserError::FunctionName(FunctionNameError::new_with_token(token, name))); } None diff --git a/src/handlers/math.rs b/src/handlers/math.rs index a7412f6..4d37289 100644 --- a/src/handlers/math.rs +++ b/src/handlers/math.rs @@ -2,7 +2,6 @@ use crate::token::{Rule, Token}; use crate::value::{Value, IntegerType, FloatType}; use crate::state::ParserState; use crate::errors::*; -use std::panic; type IntHandler = fn(l:IntegerType, r:IntegerType) -> Option; type FloatHandler = fn(l:FloatType, r:FloatType) -> FloatType; @@ -29,17 +28,17 @@ fn integer_type_checked_pow(l:IntegerType, r:IntegerType) -> Option /// * `l` - Left value /// * `r` - Right value /// * `handler` - checked_* function -fn perform_int_calculation(l: Value, r: Value, handler: IntHandler) -> Result { +fn perform_int_calculation(expression: &Token, l: Value, r: Value, handler: IntHandler) -> Result { // Perform datatype conversions let lv = l.as_int(); let rv = r.as_int(); if matches!(lv, None) || matches!(rv, None) { - return Err(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))) + return Err(ParserError::ValueType(ValueTypeError::new_with_token(expression, ExpectedTypes::IntOrFloat))) } // Detect overflow and return resulting value match handler(lv.unwrap(), rv.unwrap()) { Some(n) => Ok(Value::Integer(n)), - None => Err(ParserError::Overflow(OverflowError{})) + None => Err(ParserError::Overflow(OverflowError::new_with_token(expression))) } } @@ -49,19 +48,19 @@ fn perform_int_calculation(l: Value, r: Value, handler: IntHandler) -> Result Result { +fn perform_float_calculation(expression: &Token, l: Value, r: Value, handler: FloatHandler) -> Result { // Perform datatype conversions let lv = l.as_float(); let rv = r.as_float(); if matches!(lv, None) || matches!(rv, None) { - return Err(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))) + return Err(ParserError::ValueType(ValueTypeError::new_with_token(expression, ExpectedTypes::IntOrFloat))) } // Detect overflow let r = handler(lv.unwrap(), rv.unwrap()); if r == FloatType::INFINITY { - return Err(ParserError::Overflow(OverflowError{})) + return Err(ParserError::Overflow(OverflowError::new_with_token(expression))) } else if r == FloatType::NEG_INFINITY { - return Err(ParserError::Underflow(UnderflowError{})) + return Err(ParserError::Underflow(UnderflowError::new_with_token(expression))) } // Return resulting value @@ -75,42 +74,41 @@ fn perform_float_calculation(l: Value, r: Value, handler: FloatHandler) -> Resul /// * `r` - Right value /// * `i_handler` - integer handler function /// * `f_handler` - float handler function -fn perform_binary_calculation(l: Value, r: Value, i_handler: IntHandler, f_handler: FloatHandler) -> Result { +fn perform_binary_calculation(expression: &Token, l: Value, r: Value, i_handler: IntHandler, f_handler: FloatHandler) -> Result { if l.is_float() || r.is_float() { - match perform_float_calculation(l, r, f_handler) { + match perform_float_calculation(expression, l, r, f_handler) { Ok(n) => Ok(n), Err(e) => Err(e) } } else { - match perform_int_calculation(l, r, i_handler) { + match perform_int_calculation(expression, l, r, i_handler) { Ok(n) => Ok(n), Err(e) => Err(e) } } } -/// Perform a factorial +/// Perform a checked factorial /// /// # Arguments /// * `input` - input value -pub fn factorial(input: Value) -> Result { - if let Some(n) = input.as_int() { - match n { - 0 => Ok(Value::Integer(1)), - 1.. => { - match panic::catch_unwind(|| { - (1..n+1).product() - }) { - Ok(p) => Ok(Value::Integer(p)), - Err(_) => Err(ParserError::Overflow( - OverflowError::new() - )) +pub fn factorial(input: IntegerType) -> Option { + match input { + 0 => Some(1), + 1.. => { + let mut acc : IntegerType = 1; + for i in 1..=input { + if let Some(acc_) = acc.checked_mul(i as IntegerType) { + acc = acc_; + } else { + return None } - }, - _ => factorial(Value::Integer(-n)) - } - } else { - Err(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))) + } + + Some(acc) + }, + + _ => None } } @@ -142,20 +140,20 @@ pub fn math_expression_handler(token: &mut Token, _state: &mut ParserState) -> O match token.value() { Value::Integer(n) => token.set_value(Value::Integer(-n)), Value::Float(n) => token.set_value(Value::Float(-n)), - _ => return Some(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))) + _ => return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::IntOrFloat))) } } else if token.child(idx).unwrap().rule() == Rule::not { match token.value() { Value::Integer(n) => { match trim_binary(Value::Integer(!n), n) { Some(v) => token.set_value(v), - None => return Some(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))) + None => return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::IntOrFloat))) } }, Value::Boolean(n) => { token.set_value(Value::Boolean(!n)); }, - _ => return Some(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))) + _ => return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::IntOrFloat))) } } } @@ -168,9 +166,13 @@ pub fn math_expression_handler(token: &mut Token, _state: &mut ParserState) -> O let mut i = 1; while i < token.children().len() { if token.child(i).unwrap().rule() == Rule::factorial { - match factorial(token.value()) { - Ok(n) => token.set_value(n), - Err(e) => return Some(e) + if let Some(input) = token.value().as_int() { + match factorial(input) { + Some(n) => token.set_value(Value::Integer(n)), + None => return Some(ParserError::Overflow(OverflowError::new_with_token(token))) + } + } else { + return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::Int))) } } @@ -185,7 +187,7 @@ pub fn math_expression_handler(token: &mut Token, _state: &mut ParserState) -> O if token.children().len() > 1 { let mut i = 2; while i < token.children().len() { - match perform_binary_calculation(token.value(), token.child(i).unwrap().value(), integer_type_checked_pow, FloatType::powf) { + match perform_binary_calculation(token, token.value(), token.child(i).unwrap().value(), integer_type_checked_pow, FloatType::powf) { Ok(n) => token.set_value(n), Err(e) => return Some(e) } @@ -207,17 +209,17 @@ pub fn math_expression_handler(token: &mut Token, _state: &mut ParserState) -> O Rule::multiply => IntegerType::checked_mul, Rule::divide => IntegerType::checked_div, Rule::modulus => IntegerType::checked_rem_euclid, - _ => return Some(ParserError::Pest(PestError::new("internal error"))) + _ => return Some(ParserError::Pest(PestError::new_with_token(token, "internal error"))) }; let fh = match token.child(i - 1).unwrap().rule() { Rule::multiply => |l: FloatType, r: FloatType| l * r, Rule::divide => |l: FloatType, r: FloatType| l / r, Rule::modulus => FloatType::rem_euclid, - _ => return Some(ParserError::Pest(PestError::new("internal error"))) + _ => return Some(ParserError::Pest(PestError::new_with_token(token, "internal error"))) }; - match perform_binary_calculation(token.value(), token.child(i).unwrap().value(), ih, fh) { + match perform_binary_calculation(token, token.value(), token.child(i).unwrap().value(), ih, fh) { Ok(n) => token.set_value(n), Err(e) => return Some(e) } @@ -240,7 +242,7 @@ pub fn math_expression_handler(token: &mut Token, _state: &mut ParserState) -> O token.set_value(Value::String(format!("{}{}", token.value().as_string(), token.child(i).unwrap().value().as_string()))); } else { match perform_binary_calculation( - token.value(), token.child(i).unwrap().value(), + token, token.value(), token.child(i).unwrap().value(), IntegerType::checked_add, |l: FloatType, r: FloatType| l + r ) { Ok(n) => token.set_value(n), @@ -251,7 +253,7 @@ pub fn math_expression_handler(token: &mut Token, _state: &mut ParserState) -> O Rule::minus => { match perform_binary_calculation( - token.value(), token.child(i).unwrap().value(), + token, token.value(), token.child(i).unwrap().value(), IntegerType::checked_sub, |l: FloatType, r: FloatType| l - r ) { Ok(n) => token.set_value(n), @@ -259,7 +261,7 @@ pub fn math_expression_handler(token: &mut Token, _state: &mut ParserState) -> O }; }, - _ => return Some(ParserError::Pest(PestError::new("internal error"))) + _ => return Some(ParserError::Pest(PestError::new_with_token(token, "internal error"))) } i += 2; @@ -286,14 +288,14 @@ pub fn bitwise_expression_handler(token: &mut Token, _state: &mut ParserState) - let ih = match token.child(i - 1).unwrap().rule() { Rule::lshift => |l:IntegerType, r:IntegerType| Some(l << r), Rule::rshift => |l:IntegerType, r:IntegerType| Some(l >> r), - _ => return Some(ParserError::Pest(PestError::new("internal error"))) + _ => return Some(ParserError::Pest(PestError::new_with_token(token, "internal error"))) }; if token.value().is_float() || token.child(i).unwrap().value().is_float() { - return Some(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))); + return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::IntOrFloat))); } - match perform_int_calculation(token.value(), token.child(i).unwrap().value(), ih) { + match perform_int_calculation(token, token.value(), token.child(i).unwrap().value(), ih) { Ok(n) => token.set_value(n), Err(e) => return Some(e) } @@ -312,10 +314,10 @@ pub fn bitwise_expression_handler(token: &mut Token, _state: &mut ParserState) - let mut i = 2; while i < token.children().len() { if token.value().is_float() || token.child(i).unwrap().value().is_float() { - return Some(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::IntOrFloat))); + return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::IntOrFloat))); } - match perform_int_calculation(token.value(), token.child(i).unwrap().value(), |l:IntegerType, r:IntegerType| Some(l & r)) { + match perform_int_calculation(token, token.value(), token.child(i).unwrap().value(), |l:IntegerType, r:IntegerType| Some(l & r)) { Ok(n) => token.set_value(n), Err(e) => return Some(e) } @@ -334,10 +336,10 @@ pub fn bitwise_expression_handler(token: &mut Token, _state: &mut ParserState) - let mut i = 2; while i < token.children().len() { if token.value().is_float() || token.child(i).unwrap().value().is_float() { - return Some(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::Int))); + return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::Int))); } - match perform_int_calculation(token.value(), token.child(i).unwrap().value(), |l:IntegerType, r:IntegerType| Some(l ^ r)) { + match perform_int_calculation(token, token.value(), token.child(i).unwrap().value(), |l:IntegerType, r:IntegerType| Some(l ^ r)) { Ok(n) => token.set_value(n), Err(e) => return Some(e) } @@ -356,10 +358,10 @@ pub fn bitwise_expression_handler(token: &mut Token, _state: &mut ParserState) - let mut i = 2; while i < token.children().len() { if token.value().is_float() || token.child(i).unwrap().value().is_float() { - return Some(ParserError::ValueType(ValueTypeError::new(ExpectedTypes::Int))); + return Some(ParserError::ValueType(ValueTypeError::new_with_token(token, ExpectedTypes::Int))); } - match perform_int_calculation(token.value(), token.child(i).unwrap().value(), |l:IntegerType, r:IntegerType| Some(l | r)) { + match perform_int_calculation(token, token.value(), token.child(i).unwrap().value(), |l:IntegerType, r:IntegerType| Some(l | r)) { Ok(n) => token.set_value(n), Err(e) => return Some(e) } diff --git a/src/handlers/values.rs b/src/handlers/values.rs index e54f92a..562a9ae 100644 --- a/src/handlers/values.rs +++ b/src/handlers/values.rs @@ -9,7 +9,7 @@ use crate::errors::*; /// * `input` - Source string /// * `prefix` - Number prefix to remove from the string /// * `base` - Numeric base -fn parse_radix(input: &str, prefix: &[&str], base: u32) -> Result { +fn parse_radix(input: &str, prefix: &[&str], base: u32) -> Result { let mut trimmed = input.to_string(); for p in prefix { trimmed = trimmed.trim_start_matches(p).to_string(); @@ -17,7 +17,7 @@ fn parse_radix(input: &str, prefix: &[&str], base: u32) -> Result Ok(n), - Err(e) => Err(ParserError::ParseInt(e)) + Err(e) => Err(e) } } @@ -26,27 +26,27 @@ pub fn value_handler(token: &mut Token, state: &mut ParserState) -> Option { match parse_radix(token.text(), &["0x","0X"], 16) { Ok(n) => token.set_value(Value::Integer(n)), - Err(e) => return Some(e) + Err(e) => return Some(ParserError::ParseInt(ParseIntegerError::new_with_index(Some(token.index()), &e.to_string()))) } }, Rule::bin => { match parse_radix(token.text(), &["0b","0B"], 2) { Ok(n) => token.set_value(Value::Integer(n)), - Err(e) => return Some(e) + Err(e) => return Some(ParserError::ParseInt(ParseIntegerError::new_with_index(Some(token.index()), &e.to_string()))) } }, Rule::oct => { match parse_radix(token.text(), &["0o","0O"], 8) { Ok(n) => token.set_value(Value::Integer(n)), - Err(e) => return Some(e) + Err(e) => return Some(ParserError::ParseInt(ParseIntegerError::new_with_index(Some(token.index()), &e.to_string()))) } }, Rule::sci|Rule::float => match token.text().replace(',', "").parse::() { Ok(n) => token.set_value(Value::Float(n)), - Err(e) => return Some(ParserError::ParseFloat(e)), + Err(e) => return Some(ParserError::ParseFloat(ParseFloatingPointError::new_with_index(Some(token.index()), &e.to_string()))), }, Rule::boolean => { @@ -70,12 +70,12 @@ pub fn value_handler(token: &mut Token, state: &mut ParserState) -> Option return Some(ParserError::ParseFloat(e)), + Err(e) => return Some(ParserError::ParseFloat(ParseFloatingPointError::new_with_index(Some(token.index()), &e.to_string()))), }, Rule::int => match token.text().replace(',', "").parse::() { Ok(n) => token.set_value(Value::Integer(n)), - Err(e) => return Some(ParserError::ParseInt(e)), + Err(e) => return Some(ParserError::ParseInt(ParseIntegerError::new_with_index(Some(token.index()), &e.to_string()))), }, Rule::string => { @@ -101,7 +101,7 @@ pub fn value_handler(token: &mut Token, state: &mut ParserState) -> Option { token.set_value(token.child(0).unwrap().value()); if matches!(token.value(), Value::None) { - return Some(ParserError::VariableName(VariableNameError::new(token.text().to_string()))); + return Some(ParserError::VariableName(VariableNameError::new(token.text()))); } }, diff --git a/src/lib.rs b/src/lib.rs index da627f7..238a4d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ //! } //! ``` //! Extensions give a more flexible way of adding functionality at runtime. Extensions are written in javascript. -#![doc(html_root_url = "https://docs.rs/lavendeux-parser/0.5.2")] +#![doc(html_root_url = "https://docs.rs/lavendeux-parser/0.5.3")] #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] diff --git a/src/state.rs b/src/state.rs index 23ce027..e610bcc 100644 --- a/src/state.rs +++ b/src/state.rs @@ -7,7 +7,7 @@ use super::decorators; #[cfg(feature = "extensions")] use super::extensions; -const MAX_STACK_DEPTH: usize = 100; +const MAX_STACK_DEPTH: usize = 50; /// Holds the properties of a function assigned inside an expression #[derive(Clone)]