From 6f17e481aac88e84f918bf4970d228fa8a8316ff Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Wed, 20 Mar 2019 12:52:07 +0200 Subject: [PATCH] Ensure no-panic for operators Partially implements #11 --- README.md | 3 +- src/error/mod.rs | 78 +++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 33 ++++++++++++++++++ src/operator/mod.rs | 82 ++++++++++++++++++++++++++++++++++++--------- 4 files changed, 178 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 324ff82..8bd2f5e 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,8 @@ See [LICENSE](LICENSE) for details. ## No Panicking -This makes extensive use of the `Result` pattern and is intended to never panic. +This crate makes extensive use of the `Result` pattern and is intended to never panic. +The *exception* are panics caused by *failed allocations*. But unfortunately, Rust does not provide any features to prove this behavior. The developer of this crate has not found a good solution to ensure no-panic behavior in any way. Please report a panic immediately if you found it as issue on [github](https://github.com/ISibboI/evalexpr/issues). diff --git a/src/error/mod.rs b/src/error/mod.rs index 4842606..7d5f617 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -96,6 +96,52 @@ pub enum Error { /// The token that follows the unmatched partial token and that cannot be matched to the partial token, or `None`, if `first` is the last partial token in the stream. second: Option, }, + + /// An addition operation performed by Rust failed. + AdditionError { + /// The first argument of the addition. + augend: Value, + /// The second argument of the addition. + addend: Value, + }, + + /// A subtraction operation performed by Rust failed. + SubtractionError { + /// The first argument of the subtraction. + minuend: Value, + /// The second argument of the subtraction. + subtrahend: Value, + }, + + /// A negation operation performed by Rust failed. + NegationError { + /// The argument of the negation. + argument: Value, + }, + + /// A multiplication operation performed by Rust failed. + MultiplicationError { + /// The first argument of the multiplication. + multiplicand: Value, + /// The second argument of the multiplication. + multiplier: Value, + }, + + /// A division operation performed by Rust failed. + DivisionError { + /// The first argument of the division. + dividend: Value, + /// The second argument of the division. + divisor: Value, + }, + + /// A modulation operation performed by Rust failed. + ModulationError { + /// The first argument of the modulation. + dividend: Value, + /// The second argument of the modulation. + divisor: Value, + }, } impl Error { @@ -109,7 +155,7 @@ impl Error { /// Constructs `Error::TypeError{actual, expected}`. pub fn type_error(actual: Value, expected: Vec) -> Self { - Error::TypeError {actual, expected} + Error::TypeError { actual, expected } } /// Constructs `Error::ExpectedInt(actual)`. @@ -138,6 +184,36 @@ impl Error { ) -> Self { Error::UnmatchedPartialToken { first, second } } + + pub(crate) fn addition_error(augend: Value, addend: Value) -> Self { + Error::AdditionError { augend, addend } + } + + pub(crate) fn subtraction_error(minuend: Value, subtrahend: Value) -> Self { + Error::SubtractionError { + minuend, + subtrahend, + } + } + + pub(crate) fn negation_error(argument: Value) -> Self { + Error::NegationError { argument } + } + + pub(crate) fn multiplication_error(multiplicand: Value, multiplier: Value) -> Self { + Error::MultiplicationError { + multiplicand, + multiplier, + } + } + + pub(crate) fn division_error(dividend: Value, divisor: Value) -> Self { + Error::DivisionError { dividend, divisor } + } + + pub(crate) fn modulation_error(dividend: Value, divisor: Value) -> Self { + Error::ModulationError { dividend, divisor } + } } /// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongOperatorArgumentAmount)` otherwise. diff --git a/src/lib.rs b/src/lib.rs index de23244..039473d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -206,6 +206,8 @@ #![warn(missing_docs)] +extern crate core; + mod configuration; pub mod error; mod function; @@ -593,4 +595,35 @@ mod test { ); assert_eq!(eval("!(()true)"), Err(Error::AppendedToLeafNode)); } + + #[test] + fn test_no_panic() { + assert!(eval(&format!( + "{} + {}", + IntType::max_value(), + IntType::max_value() + )) + .is_err()); + assert!(eval(&format!( + "-{} - {}", + IntType::max_value(), + IntType::max_value() + )) + .is_err()); + assert!(eval(&format!("-(-{} - 1)", IntType::max_value())).is_err()); + assert!(eval(&format!( + "{} * {}", + IntType::max_value(), + IntType::max_value() + )) + .is_err()); + assert!(eval(&format!("{} / {}", IntType::max_value(), 0)).is_err()); + assert!(eval(&format!("{} % {}", IntType::max_value(), 0)).is_err()); + assert!(eval(&format!( + "{} ^ {}", + IntType::max_value(), + IntType::max_value() + )) + .is_ok()); + } } diff --git a/src/operator/mod.rs b/src/operator/mod.rs index a9664ce..f92b703 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -141,9 +141,18 @@ impl Operator for Add { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int( - arguments[0].as_int().unwrap() + arguments[1].as_int().unwrap(), - )) + let result = arguments[0] + .as_int() + .unwrap() + .checked_add(arguments[1].as_int().unwrap()); + if let Some(result) = result { + Ok(Value::Int(result)) + } else { + Err(Error::addition_error( + arguments[0].clone(), + arguments[1].clone(), + )) + } } else { Ok(Value::Float( arguments[0].as_float().unwrap() + arguments[1].as_float().unwrap(), @@ -171,9 +180,18 @@ impl Operator for Sub { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int( - arguments[0].as_int().unwrap() - arguments[1].as_int().unwrap(), - )) + let result = arguments[0] + .as_int() + .unwrap() + .checked_sub(arguments[1].as_int().unwrap()); + if let Some(result) = result { + Ok(Value::Int(result)) + } else { + Err(Error::subtraction_error( + arguments[0].clone(), + arguments[1].clone(), + )) + } } else { Ok(Value::Float( arguments[0].as_float().unwrap() - arguments[1].as_float().unwrap(), @@ -200,7 +218,12 @@ impl Operator for Neg { expect_number(&arguments[0])?; if arguments[0].is_int() { - Ok(Value::Int(-arguments[0].as_int().unwrap())) + let result = arguments[0].as_int().unwrap().checked_neg(); + if let Some(result) = result { + Ok(Value::Int(result)) + } else { + Err(Error::negation_error(arguments[0].clone())) + } } else { Ok(Value::Float(-arguments[0].as_float().unwrap())) } @@ -226,9 +249,18 @@ impl Operator for Mul { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int( - arguments[0].as_int().unwrap() * arguments[1].as_int().unwrap(), - )) + let result = arguments[0] + .as_int() + .unwrap() + .checked_mul(arguments[1].as_int().unwrap()); + if let Some(result) = result { + Ok(Value::Int(result)) + } else { + Err(Error::multiplication_error( + arguments[0].clone(), + arguments[1].clone(), + )) + } } else { Ok(Value::Float( arguments[0].as_float().unwrap() * arguments[1].as_float().unwrap(), @@ -256,9 +288,18 @@ impl Operator for Div { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int( - arguments[0].as_int().unwrap() / arguments[1].as_int().unwrap(), - )) + let result = arguments[0] + .as_int() + .unwrap() + .checked_div(arguments[1].as_int().unwrap()); + if let Some(result) = result { + Ok(Value::Int(result)) + } else { + Err(Error::division_error( + arguments[0].clone(), + arguments[1].clone(), + )) + } } else { Ok(Value::Float( arguments[0].as_float().unwrap() / arguments[1].as_float().unwrap(), @@ -286,9 +327,18 @@ impl Operator for Mod { expect_number(&arguments[1])?; if arguments[0].is_int() && arguments[1].is_int() { - Ok(Value::Int( - arguments[0].as_int().unwrap() % arguments[1].as_int().unwrap(), - )) + let result = arguments[0] + .as_int() + .unwrap() + .checked_rem(arguments[1].as_int().unwrap()); + if let Some(result) = result { + Ok(Value::Int(result)) + } else { + Err(Error::modulation_error( + arguments[0].clone(), + arguments[1].clone(), + )) + } } else { Ok(Value::Float( arguments[0].as_float().unwrap() % arguments[1].as_float().unwrap(),