Refactor lib.rs
* Move tests into integration test folder * Move crate methods into interface module
This commit is contained in:
parent
3bcb9b2770
commit
36a65c470b
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
* Removed integration tests from shipped crate
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
72
src/interface/mod.rs
Normal file
72
src/interface/mod.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use token;
|
||||||
|
use tree;
|
||||||
|
use Configuration;
|
||||||
|
use EmptyConfiguration;
|
||||||
|
use Error;
|
||||||
|
use Node;
|
||||||
|
use Value;
|
||||||
|
|
||||||
|
/// Evaluate the given expression string.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use evalexpr::*;
|
||||||
|
///
|
||||||
|
/// assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6)));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
||||||
|
pub fn eval(string: &str) -> Result<Value, Error> {
|
||||||
|
eval_with_configuration(string, &EmptyConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate the given expression string with the given configuration.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use evalexpr::*;
|
||||||
|
///
|
||||||
|
/// let mut configuration = HashMapConfiguration::new();
|
||||||
|
/// configuration.insert_variable("one", 1);
|
||||||
|
/// configuration.insert_variable("two", 2);
|
||||||
|
/// configuration.insert_variable("three", 3);
|
||||||
|
/// assert_eq!(eval_with_configuration("one + two + three", &configuration), Ok(Value::from(6)));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
||||||
|
pub fn eval_with_configuration(
|
||||||
|
string: &str,
|
||||||
|
configuration: &Configuration,
|
||||||
|
) -> Result<Value, Error> {
|
||||||
|
tree::tokens_to_operator_tree(token::tokenize(string)?)?.eval_with_configuration(configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the operator tree for the given expression string.
|
||||||
|
///
|
||||||
|
/// The operator tree can later on be evaluated directly.
|
||||||
|
/// This saves runtime if a single expression should be evaluated multiple times, for example with differing configurations.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use evalexpr::*;
|
||||||
|
///
|
||||||
|
/// let precomputed = build_operator_tree("one + two + three").unwrap();
|
||||||
|
///
|
||||||
|
/// let mut configuration = HashMapConfiguration::new();
|
||||||
|
/// configuration.insert_variable("one", 1);
|
||||||
|
/// configuration.insert_variable("two", 2);
|
||||||
|
/// configuration.insert_variable("three", 3);
|
||||||
|
///
|
||||||
|
/// assert_eq!(precomputed.eval_with_configuration(&configuration), Ok(Value::from(6)));
|
||||||
|
///
|
||||||
|
/// configuration.insert_variable("three", 5);
|
||||||
|
/// assert_eq!(precomputed.eval_with_configuration(&configuration), Ok(Value::from(8)));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
||||||
|
pub fn build_operator_tree(string: &str) -> Result<Node, Error> {
|
||||||
|
tree::tokens_to_operator_tree(token::tokenize(string)?)
|
||||||
|
}
|
408
src/lib.rs
408
src/lib.rs
@ -211,6 +211,7 @@ extern crate core;
|
|||||||
mod configuration;
|
mod configuration;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod function;
|
mod function;
|
||||||
|
mod interface;
|
||||||
mod operator;
|
mod operator;
|
||||||
mod token;
|
mod token;
|
||||||
mod tree;
|
mod tree;
|
||||||
@ -221,409 +222,6 @@ mod value;
|
|||||||
pub use configuration::{Configuration, EmptyConfiguration, HashMapConfiguration};
|
pub use configuration::{Configuration, EmptyConfiguration, HashMapConfiguration};
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use function::Function;
|
pub use function::Function;
|
||||||
|
pub use interface::*;
|
||||||
pub use tree::Node;
|
pub use tree::Node;
|
||||||
pub use value::Value;
|
pub use value::{FloatType, IntType, Value};
|
||||||
|
|
||||||
/// Evaluate the given expression string.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use evalexpr::*;
|
|
||||||
///
|
|
||||||
/// assert_eq!(eval("1 + 2 + 3"), Ok(Value::from(6)));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
|
||||||
pub fn eval(string: &str) -> Result<Value, Error> {
|
|
||||||
eval_with_configuration(string, &EmptyConfiguration)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate the given expression string with the given configuration.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use evalexpr::*;
|
|
||||||
///
|
|
||||||
/// let mut configuration = HashMapConfiguration::new();
|
|
||||||
/// configuration.insert_variable("one", 1);
|
|
||||||
/// configuration.insert_variable("two", 2);
|
|
||||||
/// configuration.insert_variable("three", 3);
|
|
||||||
/// assert_eq!(eval_with_configuration("one + two + three", &configuration), Ok(Value::from(6)));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
|
||||||
pub fn eval_with_configuration(
|
|
||||||
string: &str,
|
|
||||||
configuration: &Configuration,
|
|
||||||
) -> Result<Value, Error> {
|
|
||||||
tree::tokens_to_operator_tree(token::tokenize(string)?)?.eval_with_configuration(configuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the operator tree for the given expression string.
|
|
||||||
///
|
|
||||||
/// The operator tree can later on be evaluated directly.
|
|
||||||
/// This saves runtime if a single expression should be evaluated multiple times, for example with differing configurations.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use evalexpr::*;
|
|
||||||
///
|
|
||||||
/// let precomputed = build_operator_tree("one + two + three").unwrap();
|
|
||||||
///
|
|
||||||
/// let mut configuration = HashMapConfiguration::new();
|
|
||||||
/// configuration.insert_variable("one", 1);
|
|
||||||
/// configuration.insert_variable("two", 2);
|
|
||||||
/// configuration.insert_variable("three", 3);
|
|
||||||
///
|
|
||||||
/// assert_eq!(precomputed.eval_with_configuration(&configuration), Ok(Value::from(6)));
|
|
||||||
///
|
|
||||||
/// configuration.insert_variable("three", 5);
|
|
||||||
/// assert_eq!(precomputed.eval_with_configuration(&configuration), Ok(Value::from(8)));
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
|
|
||||||
pub fn build_operator_tree(string: &str) -> Result<Node, Error> {
|
|
||||||
tree::tokens_to_operator_tree(token::tokenize(string)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use crate::{eval, value::Value};
|
|
||||||
use configuration::HashMapConfiguration;
|
|
||||||
use error::{expect_number, Error};
|
|
||||||
use eval_with_configuration;
|
|
||||||
use value::IntType;
|
|
||||||
use Function;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_unary_examples() {
|
|
||||||
assert_eq!(eval("3"), Ok(Value::Int(3)));
|
|
||||||
assert_eq!(eval("3.3"), Ok(Value::Float(3.3)));
|
|
||||||
assert_eq!(eval("true"), Ok(Value::Boolean(true)));
|
|
||||||
assert_eq!(eval("false"), Ok(Value::Boolean(false)));
|
|
||||||
assert_eq!(
|
|
||||||
eval("blub"),
|
|
||||||
Err(Error::VariableIdentifierNotFound("blub".to_string()))
|
|
||||||
);
|
|
||||||
assert_eq!(eval("-3"), Ok(Value::Int(-3)));
|
|
||||||
assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6)));
|
|
||||||
assert_eq!(eval("----3"), Ok(Value::Int(3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_binary_examples() {
|
|
||||||
assert_eq!(eval("1+3"), Ok(Value::Int(4)));
|
|
||||||
assert_eq!(eval("3+1"), Ok(Value::Int(4)));
|
|
||||||
assert_eq!(eval("3-5"), Ok(Value::Int(-2)));
|
|
||||||
assert_eq!(eval("5-3"), Ok(Value::Int(2)));
|
|
||||||
assert_eq!(eval("5 / 4"), Ok(Value::Int(1)));
|
|
||||||
assert_eq!(eval("5 *3"), Ok(Value::Int(15)));
|
|
||||||
assert_eq!(eval("1.0+3"), Ok(Value::Float(4.0)));
|
|
||||||
assert_eq!(eval("3.0+1"), Ok(Value::Float(4.0)));
|
|
||||||
assert_eq!(eval("3-5.0"), Ok(Value::Float(-2.0)));
|
|
||||||
assert_eq!(eval("5-3.0"), Ok(Value::Float(2.0)));
|
|
||||||
assert_eq!(eval("5 / 4.0"), Ok(Value::Float(1.25)));
|
|
||||||
assert_eq!(eval("5.0 *3"), Ok(Value::Float(15.0)));
|
|
||||||
assert_eq!(eval("5.0 *-3"), Ok(Value::Float(-15.0)));
|
|
||||||
assert_eq!(eval("5.0 *- 3"), Ok(Value::Float(-15.0)));
|
|
||||||
assert_eq!(eval("5.0 * -3"), Ok(Value::Float(-15.0)));
|
|
||||||
assert_eq!(eval("5.0 * - 3"), Ok(Value::Float(-15.0)));
|
|
||||||
assert_eq!(eval("-5.0 *-3"), Ok(Value::Float(15.0)));
|
|
||||||
assert_eq!(eval("3+-1"), Ok(Value::Int(2)));
|
|
||||||
assert_eq!(eval("-3-5"), Ok(Value::Int(-8)));
|
|
||||||
assert_eq!(eval("-5--3"), Ok(Value::Int(-2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_arithmetic_precedence_examples() {
|
|
||||||
assert_eq!(eval("1+3-2"), Ok(Value::Int(2)));
|
|
||||||
assert_eq!(eval("3+1*5"), Ok(Value::Int(8)));
|
|
||||||
assert_eq!(eval("2*3-5"), Ok(Value::Int(1)));
|
|
||||||
assert_eq!(eval("5-3/3"), Ok(Value::Int(4)));
|
|
||||||
assert_eq!(eval("5 / 4*2"), Ok(Value::Int(2)));
|
|
||||||
assert_eq!(eval("1-5 *3/15"), Ok(Value::Int(0)));
|
|
||||||
assert_eq!(eval("15/7/2.0"), Ok(Value::Float(1.0)));
|
|
||||||
assert_eq!(eval("15.0/7/2"), Ok(Value::Float(15.0 / 7.0 / 2.0)));
|
|
||||||
assert_eq!(eval("15.0/-7/2"), Ok(Value::Float(15.0 / -7.0 / 2.0)));
|
|
||||||
assert_eq!(eval("-15.0/7/2"), Ok(Value::Float(-15.0 / 7.0 / 2.0)));
|
|
||||||
assert_eq!(eval("-15.0/7/-2"), Ok(Value::Float(-15.0 / 7.0 / -2.0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_braced_examples() {
|
|
||||||
assert_eq!(eval("(1)"), Ok(Value::Int(1)));
|
|
||||||
assert_eq!(eval("( 1.0 )"), Ok(Value::Float(1.0)));
|
|
||||||
assert_eq!(eval("( true)"), Ok(Value::Boolean(true)));
|
|
||||||
assert_eq!(eval("( -1 )"), Ok(Value::Int(-1)));
|
|
||||||
assert_eq!(eval("-(1)"), Ok(Value::Int(-1)));
|
|
||||||
assert_eq!(eval("-(1 + 3) * 7"), Ok(Value::Int(-28)));
|
|
||||||
assert_eq!(eval("(1 * 1) - 3"), Ok(Value::Int(-2)));
|
|
||||||
assert_eq!(eval("4 / (2 * 2)"), Ok(Value::Int(1)));
|
|
||||||
assert_eq!(eval("7/(7/(7/(7/(7/(7)))))"), Ok(Value::Int(1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_mod_examples() {
|
|
||||||
assert_eq!(eval("1 % 4"), Ok(Value::Int(1)));
|
|
||||||
assert_eq!(eval("6 % 4"), Ok(Value::Int(2)));
|
|
||||||
assert_eq!(eval("1 % 4 + 2"), Ok(Value::Int(3)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pow_examples() {
|
|
||||||
assert_eq!(eval("1 ^ 4"), Ok(Value::Float(1.0)));
|
|
||||||
assert_eq!(eval("6 ^ 4"), Ok(Value::Float(6.0f64.powf(4.0))));
|
|
||||||
assert_eq!(eval("1 ^ 4 + 2"), Ok(Value::Float(3.0)));
|
|
||||||
assert_eq!(eval("2 ^ (4 + 2)"), Ok(Value::Float(64.0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_boolean_examples() {
|
|
||||||
assert_eq!(eval("true && false"), Ok(Value::Boolean(false)));
|
|
||||||
assert_eq!(
|
|
||||||
eval("true && false || true && true"),
|
|
||||||
Ok(Value::Boolean(true))
|
|
||||||
);
|
|
||||||
assert_eq!(eval("5 > 4 && 1 <= 1"), Ok(Value::Boolean(true)));
|
|
||||||
assert_eq!(eval("5.0 <= 4.9 || !(4 > 3.5)"), Ok(Value::Boolean(false)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_with_configuration() {
|
|
||||||
let mut configuration = HashMapConfiguration::new();
|
|
||||||
configuration.insert_variable("tr".to_string(), Value::Boolean(true));
|
|
||||||
configuration.insert_variable("fa".to_string(), Value::Boolean(false));
|
|
||||||
configuration.insert_variable("five".to_string(), Value::Int(5));
|
|
||||||
configuration.insert_variable("six".to_string(), Value::Int(6));
|
|
||||||
configuration.insert_variable("half".to_string(), Value::Float(0.5));
|
|
||||||
configuration.insert_variable("zero".to_string(), Value::Int(0));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("tr", &configuration),
|
|
||||||
Ok(Value::Boolean(true))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("fa", &configuration),
|
|
||||||
Ok(Value::Boolean(false))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("tr && false", &configuration),
|
|
||||||
Ok(Value::Boolean(false))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("five + six", &configuration),
|
|
||||||
Ok(Value::Int(11))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("five * half", &configuration),
|
|
||||||
Ok(Value::Float(2.5))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("five < six && true", &configuration),
|
|
||||||
Ok(Value::Boolean(true))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_functions() {
|
|
||||||
let mut configuration = HashMapConfiguration::new();
|
|
||||||
configuration.insert_function(
|
|
||||||
"sub2".to_string(),
|
|
||||||
Function::new(
|
|
||||||
Some(1),
|
|
||||||
Box::new(|arguments| {
|
|
||||||
if let Value::Int(int) = arguments[0] {
|
|
||||||
Ok(Value::Int(int - 2))
|
|
||||||
} else if let Value::Float(float) = arguments[0] {
|
|
||||||
Ok(Value::Float(float - 2.0))
|
|
||||||
} else {
|
|
||||||
Err(Error::expected_number(arguments[0].clone()))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
configuration.insert_variable("five".to_string(), Value::Int(5));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("sub2 5", &configuration),
|
|
||||||
Ok(Value::Int(3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("sub2(5)", &configuration),
|
|
||||||
Ok(Value::Int(3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("sub2 five", &configuration),
|
|
||||||
Ok(Value::Int(3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("sub2(five)", &configuration),
|
|
||||||
Ok(Value::Int(3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("sub2(3) + five", &configuration),
|
|
||||||
Ok(Value::Int(6))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_n_ary_functions() {
|
|
||||||
let mut configuration = HashMapConfiguration::new();
|
|
||||||
configuration.insert_function(
|
|
||||||
"sub2",
|
|
||||||
Function::new(
|
|
||||||
Some(1),
|
|
||||||
Box::new(|arguments| {
|
|
||||||
if let Value::Int(int) = arguments[0] {
|
|
||||||
Ok(Value::Int(int - 2))
|
|
||||||
} else if let Value::Float(float) = arguments[0] {
|
|
||||||
Ok(Value::Float(float - 2.0))
|
|
||||||
} else {
|
|
||||||
Err(Error::expected_number(arguments[0].clone()))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
configuration.insert_function(
|
|
||||||
"avg",
|
|
||||||
Function::new(
|
|
||||||
Some(2),
|
|
||||||
Box::new(|arguments| {
|
|
||||||
expect_number(&arguments[0])?;
|
|
||||||
expect_number(&arguments[1])?;
|
|
||||||
|
|
||||||
if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
|
|
||||||
Ok(Value::Int((a + b) / 2))
|
|
||||||
} else {
|
|
||||||
Ok(Value::Float(
|
|
||||||
(arguments[0].as_float()? + arguments[1].as_float()?) / 2.0,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
configuration.insert_function(
|
|
||||||
"muladd",
|
|
||||||
Function::new(
|
|
||||||
Some(3),
|
|
||||||
Box::new(|arguments| {
|
|
||||||
expect_number(&arguments[0])?;
|
|
||||||
expect_number(&arguments[1])?;
|
|
||||||
expect_number(&arguments[2])?;
|
|
||||||
|
|
||||||
if let (Value::Int(a), Value::Int(b), Value::Int(c)) =
|
|
||||||
(&arguments[0], &arguments[1], &arguments[2])
|
|
||||||
{
|
|
||||||
Ok(Value::Int(a * b + c))
|
|
||||||
} else {
|
|
||||||
Ok(Value::Float(
|
|
||||||
arguments[0].as_float()? * arguments[1].as_float()?
|
|
||||||
+ arguments[2].as_float()?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
configuration.insert_function(
|
|
||||||
"count",
|
|
||||||
Function::new(
|
|
||||||
None,
|
|
||||||
Box::new(|arguments| Ok(Value::Int(arguments.len() as IntType))),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
configuration.insert_variable("five".to_string(), Value::Int(5));
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("avg(7, 5)", &configuration),
|
|
||||||
Ok(Value::Int(6))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("avg(sub2 5, 5)", &configuration),
|
|
||||||
Ok(Value::Int(4))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("sub2(avg(3, 6))", &configuration),
|
|
||||||
Ok(Value::Int(2))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("sub2 avg(3, 6)", &configuration),
|
|
||||||
Ok(Value::Int(2))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("muladd(3, 6, -4)", &configuration),
|
|
||||||
Ok(Value::Int(14))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("count()", &configuration),
|
|
||||||
Err(Error::wrong_operator_argument_amount(0, 1))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("count(3, 5.5, 2)", &configuration),
|
|
||||||
Ok(Value::Int(3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("count 5", &configuration),
|
|
||||||
Ok(Value::Int(1))
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("min(4.0, 3)", &configuration),
|
|
||||||
Ok(Value::Int(3))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval_with_configuration("max(4.0, 3)", &configuration),
|
|
||||||
Ok(Value::Float(4.0))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_errors() {
|
|
||||||
assert_eq!(
|
|
||||||
eval("-true"),
|
|
||||||
Err(Error::expected_number(Value::Boolean(true)))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval("1-true"),
|
|
||||||
Err(Error::expected_number(Value::Boolean(true)))
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
eval("true-"),
|
|
||||||
Err(Error::wrong_operator_argument_amount(1, 2))
|
|
||||||
);
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
338
tests/integration.rs
Normal file
338
tests/integration.rs
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
extern crate evalexpr;
|
||||||
|
|
||||||
|
use evalexpr::{error::*, *};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unary_examples() {
|
||||||
|
assert_eq!(eval("3"), Ok(Value::Int(3)));
|
||||||
|
assert_eq!(eval("3.3"), Ok(Value::Float(3.3)));
|
||||||
|
assert_eq!(eval("true"), Ok(Value::Boolean(true)));
|
||||||
|
assert_eq!(eval("false"), Ok(Value::Boolean(false)));
|
||||||
|
assert_eq!(
|
||||||
|
eval("blub"),
|
||||||
|
Err(Error::VariableIdentifierNotFound("blub".to_string()))
|
||||||
|
);
|
||||||
|
assert_eq!(eval("-3"), Ok(Value::Int(-3)));
|
||||||
|
assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6)));
|
||||||
|
assert_eq!(eval("----3"), Ok(Value::Int(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_binary_examples() {
|
||||||
|
assert_eq!(eval("1+3"), Ok(Value::Int(4)));
|
||||||
|
assert_eq!(eval("3+1"), Ok(Value::Int(4)));
|
||||||
|
assert_eq!(eval("3-5"), Ok(Value::Int(-2)));
|
||||||
|
assert_eq!(eval("5-3"), Ok(Value::Int(2)));
|
||||||
|
assert_eq!(eval("5 / 4"), Ok(Value::Int(1)));
|
||||||
|
assert_eq!(eval("5 *3"), Ok(Value::Int(15)));
|
||||||
|
assert_eq!(eval("1.0+3"), Ok(Value::Float(4.0)));
|
||||||
|
assert_eq!(eval("3.0+1"), Ok(Value::Float(4.0)));
|
||||||
|
assert_eq!(eval("3-5.0"), Ok(Value::Float(-2.0)));
|
||||||
|
assert_eq!(eval("5-3.0"), Ok(Value::Float(2.0)));
|
||||||
|
assert_eq!(eval("5 / 4.0"), Ok(Value::Float(1.25)));
|
||||||
|
assert_eq!(eval("5.0 *3"), Ok(Value::Float(15.0)));
|
||||||
|
assert_eq!(eval("5.0 *-3"), Ok(Value::Float(-15.0)));
|
||||||
|
assert_eq!(eval("5.0 *- 3"), Ok(Value::Float(-15.0)));
|
||||||
|
assert_eq!(eval("5.0 * -3"), Ok(Value::Float(-15.0)));
|
||||||
|
assert_eq!(eval("5.0 * - 3"), Ok(Value::Float(-15.0)));
|
||||||
|
assert_eq!(eval("-5.0 *-3"), Ok(Value::Float(15.0)));
|
||||||
|
assert_eq!(eval("3+-1"), Ok(Value::Int(2)));
|
||||||
|
assert_eq!(eval("-3-5"), Ok(Value::Int(-8)));
|
||||||
|
assert_eq!(eval("-5--3"), Ok(Value::Int(-2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_arithmetic_precedence_examples() {
|
||||||
|
assert_eq!(eval("1+3-2"), Ok(Value::Int(2)));
|
||||||
|
assert_eq!(eval("3+1*5"), Ok(Value::Int(8)));
|
||||||
|
assert_eq!(eval("2*3-5"), Ok(Value::Int(1)));
|
||||||
|
assert_eq!(eval("5-3/3"), Ok(Value::Int(4)));
|
||||||
|
assert_eq!(eval("5 / 4*2"), Ok(Value::Int(2)));
|
||||||
|
assert_eq!(eval("1-5 *3/15"), Ok(Value::Int(0)));
|
||||||
|
assert_eq!(eval("15/7/2.0"), Ok(Value::Float(1.0)));
|
||||||
|
assert_eq!(eval("15.0/7/2"), Ok(Value::Float(15.0 / 7.0 / 2.0)));
|
||||||
|
assert_eq!(eval("15.0/-7/2"), Ok(Value::Float(15.0 / -7.0 / 2.0)));
|
||||||
|
assert_eq!(eval("-15.0/7/2"), Ok(Value::Float(-15.0 / 7.0 / 2.0)));
|
||||||
|
assert_eq!(eval("-15.0/7/-2"), Ok(Value::Float(-15.0 / 7.0 / -2.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_braced_examples() {
|
||||||
|
assert_eq!(eval("(1)"), Ok(Value::Int(1)));
|
||||||
|
assert_eq!(eval("( 1.0 )"), Ok(Value::Float(1.0)));
|
||||||
|
assert_eq!(eval("( true)"), Ok(Value::Boolean(true)));
|
||||||
|
assert_eq!(eval("( -1 )"), Ok(Value::Int(-1)));
|
||||||
|
assert_eq!(eval("-(1)"), Ok(Value::Int(-1)));
|
||||||
|
assert_eq!(eval("-(1 + 3) * 7"), Ok(Value::Int(-28)));
|
||||||
|
assert_eq!(eval("(1 * 1) - 3"), Ok(Value::Int(-2)));
|
||||||
|
assert_eq!(eval("4 / (2 * 2)"), Ok(Value::Int(1)));
|
||||||
|
assert_eq!(eval("7/(7/(7/(7/(7/(7)))))"), Ok(Value::Int(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mod_examples() {
|
||||||
|
assert_eq!(eval("1 % 4"), Ok(Value::Int(1)));
|
||||||
|
assert_eq!(eval("6 % 4"), Ok(Value::Int(2)));
|
||||||
|
assert_eq!(eval("1 % 4 + 2"), Ok(Value::Int(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pow_examples() {
|
||||||
|
assert_eq!(eval("1 ^ 4"), Ok(Value::Float(1.0)));
|
||||||
|
assert_eq!(eval("6 ^ 4"), Ok(Value::Float(6.0f64.powf(4.0))));
|
||||||
|
assert_eq!(eval("1 ^ 4 + 2"), Ok(Value::Float(3.0)));
|
||||||
|
assert_eq!(eval("2 ^ (4 + 2)"), Ok(Value::Float(64.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_boolean_examples() {
|
||||||
|
assert_eq!(eval("true && false"), Ok(Value::Boolean(false)));
|
||||||
|
assert_eq!(
|
||||||
|
eval("true && false || true && true"),
|
||||||
|
Ok(Value::Boolean(true))
|
||||||
|
);
|
||||||
|
assert_eq!(eval("5 > 4 && 1 <= 1"), Ok(Value::Boolean(true)));
|
||||||
|
assert_eq!(eval("5.0 <= 4.9 || !(4 > 3.5)"), Ok(Value::Boolean(false)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_with_configuration() {
|
||||||
|
let mut configuration = HashMapConfiguration::new();
|
||||||
|
configuration.insert_variable("tr".to_string(), Value::Boolean(true));
|
||||||
|
configuration.insert_variable("fa".to_string(), Value::Boolean(false));
|
||||||
|
configuration.insert_variable("five".to_string(), Value::Int(5));
|
||||||
|
configuration.insert_variable("six".to_string(), Value::Int(6));
|
||||||
|
configuration.insert_variable("half".to_string(), Value::Float(0.5));
|
||||||
|
configuration.insert_variable("zero".to_string(), Value::Int(0));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("tr", &configuration),
|
||||||
|
Ok(Value::Boolean(true))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("fa", &configuration),
|
||||||
|
Ok(Value::Boolean(false))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("tr && false", &configuration),
|
||||||
|
Ok(Value::Boolean(false))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("five + six", &configuration),
|
||||||
|
Ok(Value::Int(11))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("five * half", &configuration),
|
||||||
|
Ok(Value::Float(2.5))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("five < six && true", &configuration),
|
||||||
|
Ok(Value::Boolean(true))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_functions() {
|
||||||
|
let mut configuration = HashMapConfiguration::new();
|
||||||
|
configuration.insert_function(
|
||||||
|
"sub2".to_string(),
|
||||||
|
Function::new(
|
||||||
|
Some(1),
|
||||||
|
Box::new(|arguments| {
|
||||||
|
if let Value::Int(int) = arguments[0] {
|
||||||
|
Ok(Value::Int(int - 2))
|
||||||
|
} else if let Value::Float(float) = arguments[0] {
|
||||||
|
Ok(Value::Float(float - 2.0))
|
||||||
|
} else {
|
||||||
|
Err(Error::expected_number(arguments[0].clone()))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
configuration.insert_variable("five".to_string(), Value::Int(5));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2 5", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2(5)", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2 five", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2(five)", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2(3) + five", &configuration),
|
||||||
|
Ok(Value::Int(6))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_n_ary_functions() {
|
||||||
|
let mut configuration = HashMapConfiguration::new();
|
||||||
|
configuration.insert_function(
|
||||||
|
"sub2",
|
||||||
|
Function::new(
|
||||||
|
Some(1),
|
||||||
|
Box::new(|arguments| {
|
||||||
|
if let Value::Int(int) = arguments[0] {
|
||||||
|
Ok(Value::Int(int - 2))
|
||||||
|
} else if let Value::Float(float) = arguments[0] {
|
||||||
|
Ok(Value::Float(float - 2.0))
|
||||||
|
} else {
|
||||||
|
Err(Error::expected_number(arguments[0].clone()))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
configuration.insert_function(
|
||||||
|
"avg",
|
||||||
|
Function::new(
|
||||||
|
Some(2),
|
||||||
|
Box::new(|arguments| {
|
||||||
|
expect_number(&arguments[0])?;
|
||||||
|
expect_number(&arguments[1])?;
|
||||||
|
|
||||||
|
if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
|
||||||
|
Ok(Value::Int((a + b) / 2))
|
||||||
|
} else {
|
||||||
|
Ok(Value::Float(
|
||||||
|
(arguments[0].as_float()? + arguments[1].as_float()?) / 2.0,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
configuration.insert_function(
|
||||||
|
"muladd",
|
||||||
|
Function::new(
|
||||||
|
Some(3),
|
||||||
|
Box::new(|arguments| {
|
||||||
|
expect_number(&arguments[0])?;
|
||||||
|
expect_number(&arguments[1])?;
|
||||||
|
expect_number(&arguments[2])?;
|
||||||
|
|
||||||
|
if let (Value::Int(a), Value::Int(b), Value::Int(c)) =
|
||||||
|
(&arguments[0], &arguments[1], &arguments[2])
|
||||||
|
{
|
||||||
|
Ok(Value::Int(a * b + c))
|
||||||
|
} else {
|
||||||
|
Ok(Value::Float(
|
||||||
|
arguments[0].as_float()? * arguments[1].as_float()?
|
||||||
|
+ arguments[2].as_float()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
configuration.insert_function(
|
||||||
|
"count",
|
||||||
|
Function::new(
|
||||||
|
None,
|
||||||
|
Box::new(|arguments| Ok(Value::Int(arguments.len() as IntType))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
configuration.insert_variable("five".to_string(), Value::Int(5));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("avg(7, 5)", &configuration),
|
||||||
|
Ok(Value::Int(6))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("avg(sub2 5, 5)", &configuration),
|
||||||
|
Ok(Value::Int(4))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2(avg(3, 6))", &configuration),
|
||||||
|
Ok(Value::Int(2))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("sub2 avg(3, 6)", &configuration),
|
||||||
|
Ok(Value::Int(2))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("muladd(3, 6, -4)", &configuration),
|
||||||
|
Ok(Value::Int(14))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("count()", &configuration),
|
||||||
|
Err(Error::WrongOperatorArgumentAmount {
|
||||||
|
actual: 0,
|
||||||
|
expected: 1
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("count(3, 5.5, 2)", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("count 5", &configuration),
|
||||||
|
Ok(Value::Int(1))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("min(4.0, 3)", &configuration),
|
||||||
|
Ok(Value::Int(3))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval_with_configuration("max(4.0, 3)", &configuration),
|
||||||
|
Ok(Value::Float(4.0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_errors() {
|
||||||
|
assert_eq!(
|
||||||
|
eval("-true"),
|
||||||
|
Err(Error::expected_number(Value::Boolean(true)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval("1-true"),
|
||||||
|
Err(Error::expected_number(Value::Boolean(true)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
eval("true-"),
|
||||||
|
Err(Error::WrongOperatorArgumentAmount {
|
||||||
|
actual: 1,
|
||||||
|
expected: 2
|
||||||
|
})
|
||||||
|
);
|
||||||
|
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());
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user