Ensure no-panic for operators

Partially implements #11
This commit is contained in:
Sebastian Schmidt 2019-03-20 12:52:07 +02:00
parent ace2da1f3e
commit 6f17e481aa
4 changed files with 178 additions and 18 deletions

View File

@ -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).

View File

@ -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<PartialToken>,
},
/// 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<Value>) -> 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.

View File

@ -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());
}
}

View File

@ -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(),