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 ## 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. 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. 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). 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. /// 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>, 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 { impl Error {
@ -109,7 +155,7 @@ impl Error {
/// Constructs `Error::TypeError{actual, expected}`. /// Constructs `Error::TypeError{actual, expected}`.
pub fn type_error(actual: Value, expected: Vec<Value>) -> Self { pub fn type_error(actual: Value, expected: Vec<Value>) -> Self {
Error::TypeError {actual, expected} Error::TypeError { actual, expected }
} }
/// Constructs `Error::ExpectedInt(actual)`. /// Constructs `Error::ExpectedInt(actual)`.
@ -138,6 +184,36 @@ impl Error {
) -> Self { ) -> Self {
Error::UnmatchedPartialToken { first, second } 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. /// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongOperatorArgumentAmount)` otherwise.

View File

@ -206,6 +206,8 @@
#![warn(missing_docs)] #![warn(missing_docs)]
extern crate core;
mod configuration; mod configuration;
pub mod error; pub mod error;
mod function; mod function;
@ -593,4 +595,35 @@ mod test {
); );
assert_eq!(eval("!(()true)"), Err(Error::AppendedToLeafNode)); 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])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int( let result = arguments[0]
arguments[0].as_int().unwrap() + arguments[1].as_int().unwrap(), .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 { } else {
Ok(Value::Float( Ok(Value::Float(
arguments[0].as_float().unwrap() + arguments[1].as_float().unwrap(), arguments[0].as_float().unwrap() + arguments[1].as_float().unwrap(),
@ -171,9 +180,18 @@ impl Operator for Sub {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int( let result = arguments[0]
arguments[0].as_int().unwrap() - arguments[1].as_int().unwrap(), .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 { } else {
Ok(Value::Float( Ok(Value::Float(
arguments[0].as_float().unwrap() - arguments[1].as_float().unwrap(), arguments[0].as_float().unwrap() - arguments[1].as_float().unwrap(),
@ -200,7 +218,12 @@ impl Operator for Neg {
expect_number(&arguments[0])?; expect_number(&arguments[0])?;
if arguments[0].is_int() { 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 { } else {
Ok(Value::Float(-arguments[0].as_float().unwrap())) Ok(Value::Float(-arguments[0].as_float().unwrap()))
} }
@ -226,9 +249,18 @@ impl Operator for Mul {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int( let result = arguments[0]
arguments[0].as_int().unwrap() * arguments[1].as_int().unwrap(), .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 { } else {
Ok(Value::Float( Ok(Value::Float(
arguments[0].as_float().unwrap() * arguments[1].as_float().unwrap(), arguments[0].as_float().unwrap() * arguments[1].as_float().unwrap(),
@ -256,9 +288,18 @@ impl Operator for Div {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int( let result = arguments[0]
arguments[0].as_int().unwrap() / arguments[1].as_int().unwrap(), .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 { } else {
Ok(Value::Float( Ok(Value::Float(
arguments[0].as_float().unwrap() / arguments[1].as_float().unwrap(), arguments[0].as_float().unwrap() / arguments[1].as_float().unwrap(),
@ -286,9 +327,18 @@ impl Operator for Mod {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int( let result = arguments[0]
arguments[0].as_int().unwrap() % arguments[1].as_int().unwrap(), .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 { } else {
Ok(Value::Float( Ok(Value::Float(
arguments[0].as_float().unwrap() % arguments[1].as_float().unwrap(), arguments[0].as_float().unwrap() % arguments[1].as_float().unwrap(),