From 36a65c470b5f81e2f226059129f3114d203c3b54 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Wed, 20 Mar 2019 15:47:33 +0200 Subject: [PATCH] Refactor lib.rs * Move tests into integration test folder * Move crate methods into interface module --- CHANGELOG.md | 2 + src/interface/mod.rs | 72 ++++++++ src/lib.rs | 408 +------------------------------------------ tests/integration.rs | 338 +++++++++++++++++++++++++++++++++++ 4 files changed, 415 insertions(+), 405 deletions(-) create mode 100644 src/interface/mod.rs create mode 100644 tests/integration.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b07e5..222cf48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Removed + * Removed integration tests from shipped crate + ### Changed ### Fixed diff --git a/src/interface/mod.rs b/src/interface/mod.rs new file mode 100644 index 0000000..4445fcb --- /dev/null +++ b/src/interface/mod.rs @@ -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 { + 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 { + 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 { + tree::tokens_to_operator_tree(token::tokenize(string)?) +} diff --git a/src/lib.rs b/src/lib.rs index 4b1ea57..5b892df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -211,6 +211,7 @@ extern crate core; mod configuration; pub mod error; mod function; +mod interface; mod operator; mod token; mod tree; @@ -221,409 +222,6 @@ mod value; pub use configuration::{Configuration, EmptyConfiguration, HashMapConfiguration}; pub use error::Error; pub use function::Function; +pub use interface::*; pub use tree::Node; -pub use value::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 { - 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 { - 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 { - 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()); - } -} +pub use value::{FloatType, IntType, Value}; diff --git a/tests/integration.rs b/tests/integration.rs new file mode 100644 index 0000000..36c5f74 --- /dev/null +++ b/tests/integration.rs @@ -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()); +}