From 3da6019daefbbe25f67bce921773b0626e47ebea Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Wed, 27 Mar 2019 16:38:59 +0100 Subject: [PATCH] Add `Context` trait * Rename `HashMapConfiguration` to `HashMapContext` * Rename `EmptyConfiguration` to `EmptyContext` * Implement `Context` for both predefined contexts * Update tests and doctests Relates to #22 --- Cargo.toml | 2 +- src/configuration/mod.rs | 70 ------------------- src/context/mod.rs | 110 ++++++++++++++++++++++++++++++ src/error/display.rs | 1 + src/error/mod.rs | 38 +++++++++-- src/interface/mod.rs | 22 +++--- src/lib.rs | 33 +++++---- src/operator/mod.rs | 142 ++++++++++++++++++++++++++++++++------- src/tree/mod.rs | 31 +++++---- src/value/mod.rs | 1 + src/value/value_type.rs | 35 ++++++++++ tests/integration.rs | 50 +++++++------- 12 files changed, 370 insertions(+), 165 deletions(-) delete mode 100644 src/configuration/mod.rs create mode 100644 src/context/mod.rs create mode 100644 src/value/value_type.rs diff --git a/Cargo.toml b/Cargo.toml index 3ec85b3..6b79a15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "evalexpr" -version = "1.2.1-alpha.0" +version = "2.0.0-alpha.0" description = "A powerful arithmetic and boolean expression evaluator" keywords = ["expression", "evaluate", "evaluator", "arithmetic", "boolean"] categories = ["parsing", "game-engines"] diff --git a/src/configuration/mod.rs b/src/configuration/mod.rs deleted file mode 100644 index b4ed6b3..0000000 --- a/src/configuration/mod.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::value::Value; -use function::Function; -use std::collections::HashMap; - -/// A configuration for an expression tree. -/// -/// A configuration defines methods to retrieve values and functions for literals in an expression tree. -/// This crate implements two basic variants, the `EmptyConfiguration`, that returns `None` for each identifier, and the `HashMapConfiguration`, that stores its mappings in hash maps. -pub trait Configuration { - /// Returns the value that is linked to the given identifier. - fn get_value(&self, identifier: &str) -> Option<&Value>; - - /// Returns the function that is linked to the given identifier. - fn get_function(&self, identifier: &str) -> Option<&Function>; -} - -/// A configuration that returns `None` for each identifier. -pub struct EmptyConfiguration; - -impl Configuration for EmptyConfiguration { - fn get_value(&self, _identifier: &str) -> Option<&Value> { - None - } - - fn get_function(&self, _identifier: &str) -> Option<&Function> { - None - } -} - -/// A configuration that stores its mappings in hash maps. -/// -/// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.* -pub struct HashMapConfiguration { - variables: HashMap, - functions: HashMap, -} - -impl HashMapConfiguration { - /// Constructs a `HashMapConfiguration` with no mappings. - pub fn new() -> Self { - Self { - variables: Default::default(), - functions: Default::default(), - } - } - - /// Adds a variable mapping to the configuration. - /// - /// *Value and function mappings are stored independently, meaning that there can be a function and a variable with the same identifier.* - pub fn insert_variable, V: Into>(&mut self, identifier: S, value: V) { - self.variables.insert(identifier.into(), value.into()); - } - - /// Adds a function mappign to the configuration. - /// - /// *Value and function mappings are stored independently, meaning that there can be a function and a variable with the same identifier.* - pub fn insert_function>(&mut self, identifier: S, function: Function) { - self.functions.insert(identifier.into(), function); - } -} - -impl Configuration for HashMapConfiguration { - fn get_value(&self, identifier: &str) -> Option<&Value> { - self.variables.get(identifier) - } - - fn get_function(&self, identifier: &str) -> Option<&Function> { - self.functions.get(identifier) - } -} diff --git a/src/context/mod.rs b/src/context/mod.rs new file mode 100644 index 0000000..9af07cf --- /dev/null +++ b/src/context/mod.rs @@ -0,0 +1,110 @@ +use std::collections::HashMap; + +use EvalexprError; +use EvalexprResult; +use function::Function; +use value::value_type::ValueType; + +use crate::value::Value; + +/// A configuration for an expression tree. +/// +/// A configuration defines methods to retrieve values and functions for literals in an expression tree. +/// This crate implements two basic variants, the `EmptyContext`, that returns `None` for each identifier, and the `HashMapContext`, that stores its mappings in hash maps. +pub trait Configuration { + /// Returns the value that is linked to the given identifier. + fn get_value(&self, identifier: &str) -> Option<&Value>; + + /// Returns the function that is linked to the given identifier. + fn get_function(&self, identifier: &str) -> Option<&Function>; +} + +/// A context for an expression tree. +/// +/// In addition to all functionality of a `Configuration`, a context also allows the manipulation of values and functions. +/// This crate implements two basic variants, the `EmptyContext`, that returns `Err` for each identifier, and the `HashMapContext`, that stores its mappings in hash maps. +/// The HashMapContext is type-safe and returns an error if the user tries to assign a value of a different type than before to an identifier. +pub trait Context: Configuration { + /// Links the given value to the given identifier. + fn set_value, V: Into>(&mut self, identifier: S, value: V) -> EvalexprResult<()>; + + /// Links the given function to the given identifier. + fn set_function>(&mut self, identifier: S, function: Function) -> EvalexprResult<()>; +} + +/// A configuration that returns `None` for each identifier. +pub struct EmptyContext; + +impl Configuration for EmptyContext { + fn get_value(&self, _identifier: &str) -> Option<&Value> { + None + } + + fn get_function(&self, _identifier: &str) -> Option<&Function> { + None + } +} + +impl Context for EmptyContext { + fn set_value, V: Into>(&mut self, _identifier: S, _value: V) -> EvalexprResult<()> { + Err(EvalexprError::ContextNotManipulable) + } + + fn set_function>(&mut self, _identifier: S, _function: Function) -> EvalexprResult<()> { + Err(EvalexprError::ContextNotManipulable) + } +} + +/// A context that stores its mappings in hash maps. +/// +/// *Value and function mappings are stored independently, meaning that there can be a function and a value with the same identifier.* +/// +/// This context is type-safe, meaning that an identifier that is assigned a value of some type once cannot be assigned a value of another type. +pub struct HashMapContext { + variables: HashMap, + functions: HashMap, +} + +impl HashMapContext { + /// Constructs a `HashMapContext` with no mappings. + pub fn new() -> Self { + Self { + variables: Default::default(), + functions: Default::default(), + } + } +} + +impl Configuration for HashMapContext { + fn get_value(&self, identifier: &str) -> Option<&Value> { + self.variables.get(identifier) + } + + fn get_function(&self, identifier: &str) -> Option<&Function> { + self.functions.get(identifier) + } +} + +impl Context for HashMapContext { + fn set_value, V: Into>(&mut self, identifier: S, value: V) -> Result<(), EvalexprError> { + let identifier = identifier.into(); + let value = value.into(); + if let Some(existing_value) = self.variables.get_mut(&identifier) { + if ValueType::from(&existing_value) == ValueType::from(&value) { + *existing_value = value; + return Ok(()); + } else { + return Err(EvalexprError::expected_type(existing_value, value)); + } + } + + // Implicit else, because `self.variables` and `identifier` are not unborrowed in else + self.variables.insert(identifier, value); + Ok(()) + } + + fn set_function>(&mut self, identifier: S, function: Function) -> Result<(), EvalexprError> { + self.functions.insert(identifier.into(), function); + Ok(()) + } +} \ No newline at end of file diff --git a/src/error/display.rs b/src/error/display.rs index aaadc7d..22682da 100644 --- a/src/error/display.rs +++ b/src/error/display.rs @@ -85,6 +85,7 @@ impl fmt::Display for EvalexprError { ModulationError { dividend, divisor } => { write!(f, "Error modulating {} % {}", dividend, divisor) }, + ContextNotManipulable => write!(f, "Cannot manipulate context"), CustomMessage(message) => write!(f, "Error: {}", message), } } diff --git a/src/error/mod.rs b/src/error/mod.rs index 7c08f9a..b4b4101 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -7,6 +7,7 @@ use token::PartialToken; use value::TupleType; +use value::value_type::ValueType; use crate::value::Value; @@ -76,6 +77,8 @@ pub enum EvalexprError { AppendedToLeafNode, /// Tried to append a child to a node such that the precedence of the child is not higher. + /// This error should never occur. + /// If it does, please file a bug report. PrecedenceViolation, /// A `VariableIdentifier` operation did not find its value in the configuration. @@ -155,6 +158,9 @@ pub enum EvalexprError { divisor: Value, }, + /// A `set_`-function was called on a `Context` that does not allow modifications. + ContextNotManipulable, + /// A custom error explained by its message. CustomMessage(String), } @@ -203,6 +209,17 @@ impl EvalexprError { EvalexprError::ExpectedTuple { actual } } + /// Constructs an error that expresses that the type of `expected` was expected, but `actual` was found. + pub(crate) fn expected_type(expected: &Value, actual: Value) -> Self { + match ValueType::from(expected) { + ValueType::String => Self::expected_string(actual), + ValueType::Int => Self::expected_int(actual), + ValueType::Float => Self::expected_float(actual), + ValueType::Boolean => Self::expected_boolean(actual), + ValueType::Tuple => Self::expected_tuple(actual), + } + } + pub(crate) fn unmatched_partial_token( first: PartialToken, second: Option, @@ -242,20 +259,30 @@ impl EvalexprError { } /// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongOperatorArgumentAmount)` otherwise. -pub(crate) fn expect_operator_argument_amount(actual: usize, expected: usize) -> Result<(), EvalexprError> { +pub(crate) fn expect_operator_argument_amount( + actual: usize, + expected: usize, +) -> Result<(), EvalexprError> { if actual == expected { Ok(()) } else { - Err(EvalexprError::wrong_operator_argument_amount(actual, expected)) + Err(EvalexprError::wrong_operator_argument_amount( + actual, expected, + )) } } /// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongFunctionArgumentAmount)` otherwise. -pub(crate) fn expect_function_argument_amount(actual: usize, expected: usize) -> Result<(), EvalexprError> { +pub(crate) fn expect_function_argument_amount( + actual: usize, + expected: usize, +) -> Result<(), EvalexprError> { if actual == expected { Ok(()) } else { - Err(EvalexprError::wrong_function_argument_amount(actual, expected)) + Err(EvalexprError::wrong_function_argument_amount( + actual, expected, + )) } } @@ -279,4 +306,5 @@ pub fn expect_boolean(actual: &Value) -> Result { impl std::error::Error for EvalexprError {} -pub type EvalexprResult = Result; \ No newline at end of file +/// Standard result type used by this crate. +pub type EvalexprResult = Result; diff --git a/src/interface/mod.rs b/src/interface/mod.rs index e8ef2ba..a7b96e0 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -1,5 +1,5 @@ use Configuration; -use EmptyConfiguration; +use EmptyContext; use EvalexprError; use FloatType; use IntType; @@ -21,7 +21,7 @@ use value::TupleType; /// /// *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) + eval_with_configuration(string, &EmptyContext) } /// Evaluate the given expression string with the given configuration. @@ -31,10 +31,10 @@ pub fn eval(string: &str) -> Result { /// ```rust /// use evalexpr::*; /// -/// let mut configuration = HashMapConfiguration::new(); -/// configuration.insert_variable("one", 1); -/// configuration.insert_variable("two", 2); -/// configuration.insert_variable("three", 3); +/// let mut configuration = HashMapContext::new(); +/// configuration.set_value("one", 1).unwrap(); // Do proper error handling here +/// configuration.set_value("two", 2).unwrap(); // Do proper error handling here +/// configuration.set_value("three", 3).unwrap(); // Do proper error handling here /// assert_eq!(eval_with_configuration("one + two + three", &configuration), Ok(Value::from(6))); /// ``` /// @@ -58,14 +58,14 @@ pub fn eval_with_configuration( /// /// let precomputed = build_operator_tree("one + two + three").unwrap(); // Do proper error handling here /// -/// let mut configuration = HashMapConfiguration::new(); -/// configuration.insert_variable("one", 1); -/// configuration.insert_variable("two", 2); -/// configuration.insert_variable("three", 3); +/// let mut configuration = HashMapContext::new(); +/// configuration.set_value("one", 1).unwrap(); // Do proper error handling here +/// configuration.set_value("two", 2).unwrap(); // Do proper error handling here +/// configuration.set_value("three", 3).unwrap(); // Do proper error handling here /// /// assert_eq!(precomputed.eval_with_configuration(&configuration), Ok(Value::from(6))); /// -/// configuration.insert_variable("three", 5); +/// configuration.set_value("three", 5).unwrap(); // Do proper error handling here /// assert_eq!(precomputed.eval_with_configuration(&configuration), Ok(Value::from(8))); /// ``` /// diff --git a/src/lib.rs b/src/lib.rs index 04d6a6b..f523f63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,10 +35,10 @@ //! use evalexpr::*; //! use evalexpr::error::expect_number; //! -//! let mut configuration = HashMapConfiguration::new(); -//! configuration.insert_variable("five", 5); -//! configuration.insert_variable("twelve", 12); -//! configuration.insert_function("f", Function::new(Some(1) /* argument amount */, Box::new(|arguments| { +//! let mut configuration = HashMapContext::new(); +//! configuration.set_value("five", 5).unwrap(); // Do proper error handling here +//! configuration.set_value("twelve", 12).unwrap(); // Do proper error handling here +//! configuration.set_function("f", Function::new(Some(1) /* argument amount */, Box::new(|arguments| { //! if let Value::Int(int) = arguments[0] { //! Ok(Value::Int(int / 2)) //! } else if let Value::Float(float) = arguments[0] { @@ -46,8 +46,8 @@ //! } else { //! Err(EvalexprError::expected_number(arguments[0].clone())) //! } -//! }))); -//! configuration.insert_function("avg", Function::new(Some(2) /* argument amount */, Box::new(|arguments| { +//! }))).unwrap(); // Do proper error handling here +//! configuration.set_function("avg", Function::new(Some(2) /* argument amount */, Box::new(|arguments| { //! expect_number(&arguments[0])?; //! expect_number(&arguments[1])?; //! @@ -56,7 +56,7 @@ //! } else { //! Ok(Value::Float((arguments[0].as_float()? + arguments[1].as_float()?) / 2.0)) //! } -//! }))); +//! }))).unwrap(); // Do proper error handling here //! //! assert_eq!(eval_with_configuration("five + 8 > f(twelve)", &configuration), Ok(Value::from(true))); //! // `eval_with_configuration` returns a variant of the `Value` enum, @@ -73,13 +73,13 @@ //! //! let precompiled = build_operator_tree("a * b - c > 5").unwrap(); // Do proper error handling here //! -//! let mut configuration = HashMapConfiguration::new(); -//! configuration.insert_variable("a", 6); -//! configuration.insert_variable("b", 2); -//! configuration.insert_variable("c", 3); +//! let mut configuration = HashMapContext::new(); +//! configuration.set_value("a", 6).unwrap(); // Do proper error handling here +//! configuration.set_value("b", 2).unwrap(); // Do proper error handling here +//! configuration.set_value("c", 3).unwrap(); // Do proper error handling here //! assert_eq!(precompiled.eval_with_configuration(&configuration), Ok(Value::from(true))); //! -//! configuration.insert_variable("c", 8); +//! configuration.set_value("c", 8).unwrap(); // Do proper error handling here //! assert_eq!(precompiled.eval_with_configuration(&configuration), Ok(Value::from(false))); //! // `Node::eval_with_configuration` returns a variant of the `Value` enum, //! // while `Node::eval_[type]_with_configuration` returns the respective type directly. @@ -241,8 +241,8 @@ //! extern crate ron; //! use evalexpr::*; //! -//! let mut configuration = HashMapConfiguration::new(); -//! configuration.insert_variable("five", 5); +//! let mut configuration = HashMapContext::new(); +//! configuration.set_value("five", 5).unwrap(); // Do proper error handling here //! //! // In ron format, strings are surrounded by " //! let serialized_free = "\"five * five\""; @@ -269,14 +269,14 @@ extern crate ron; #[cfg(feature = "serde")] extern crate serde; -pub use configuration::{Configuration, EmptyConfiguration, HashMapConfiguration}; +pub use context::{Configuration, Context, EmptyContext, HashMapContext}; pub use error::{EvalexprError, EvalexprResult}; pub use function::Function; pub use interface::*; pub use tree::Node; pub use value::{FloatType, IntType, Value}; -mod configuration; +mod context; pub mod error; #[cfg(feature = "serde")] mod feature_serde; @@ -288,4 +288,3 @@ mod tree; mod value; // Exports - diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 56b9f3b..0b40a27 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -2,7 +2,7 @@ use std::fmt::{Debug, Display}; use function::builtin::builtin_function; -use crate::{configuration::Configuration, error::*, value::Value}; +use crate::{context::Configuration, error::*, value::Value}; mod display; @@ -29,7 +29,11 @@ pub trait Operator: Debug + Display { fn argument_amount(&self) -> usize; /// Evaluates the operator with the given arguments and configuration. - fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result; + fn eval( + &self, + arguments: &[Value], + configuration: &Configuration, + ) -> Result; } #[derive(Debug)] @@ -118,7 +122,11 @@ impl Operator for RootNode { 1 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 1)?; Ok(arguments[0].clone()) } @@ -137,7 +145,11 @@ impl Operator for Add { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -173,7 +185,11 @@ impl Operator for Sub { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -209,7 +225,11 @@ impl Operator for Neg { 1 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 1)?; expect_number(&arguments[0])?; @@ -239,7 +259,11 @@ impl Operator for Mul { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -275,7 +299,11 @@ impl Operator for Div { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -311,7 +339,11 @@ impl Operator for Mod { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -347,7 +379,11 @@ impl Operator for Exp { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -374,7 +410,11 @@ impl Operator for Eq { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; if arguments[0] == arguments[1] { @@ -398,7 +438,11 @@ impl Operator for Neq { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; if arguments[0] != arguments[1] { @@ -422,7 +466,11 @@ impl Operator for Gt { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -456,7 +504,11 @@ impl Operator for Lt { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -490,7 +542,11 @@ impl Operator for Geq { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -524,7 +580,11 @@ impl Operator for Leq { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -558,7 +618,11 @@ impl Operator for And { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; let a = expect_boolean(&arguments[0])?; let b = expect_boolean(&arguments[1])?; @@ -584,7 +648,11 @@ impl Operator for Or { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 2)?; let a = expect_boolean(&arguments[0])?; let b = expect_boolean(&arguments[1])?; @@ -610,7 +678,11 @@ impl Operator for Not { 1 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 1)?; let a = expect_boolean(&arguments[0])?; @@ -635,7 +707,11 @@ impl Operator for Tuple { 2 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { if let Value::Tuple(tuple) = &arguments[0] { let mut tuple = tuple.clone(); if let Value::Tuple(tuple2) = &arguments[1] { @@ -672,7 +748,11 @@ impl Operator for Const { 0 } - fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + _configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 0)?; Ok(self.value.clone()) @@ -692,11 +772,17 @@ impl Operator for VariableIdentifier { 0 } - fn eval(&self, _arguments: &[Value], configuration: &Configuration) -> Result { + fn eval( + &self, + _arguments: &[Value], + configuration: &Configuration, + ) -> Result { if let Some(value) = configuration.get_value(&self.identifier).cloned() { Ok(value) } else { - Err(EvalexprError::VariableIdentifierNotFound(self.identifier.clone())) + Err(EvalexprError::VariableIdentifierNotFound( + self.identifier.clone(), + )) } } } @@ -714,7 +800,11 @@ impl Operator for FunctionIdentifier { 1 } - fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result { + fn eval( + &self, + arguments: &[Value], + configuration: &Configuration, + ) -> Result { expect_operator_argument_amount(arguments.len(), 1)?; let arguments = if let Value::Tuple(arguments) = &arguments[0] { @@ -728,7 +818,9 @@ impl Operator for FunctionIdentifier { } else if let Some(builtin_function) = builtin_function(&self.identifier) { builtin_function.call(arguments) } else { - Err(EvalexprError::FunctionIdentifierNotFound(self.identifier.clone())) + Err(EvalexprError::FunctionIdentifierNotFound( + self.identifier.clone(), + )) } } } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index c65137b..2578ccc 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,10 +1,10 @@ -use EmptyConfiguration; +use EmptyContext; use FloatType; use IntType; use token::Token; use value::TupleType; -use crate::{configuration::Configuration, error::EvalexprError, operator::*, value::Value}; +use crate::{context::Configuration, error::EvalexprError, operator::*, value::Value}; mod display; @@ -19,8 +19,8 @@ mod display; /// ```rust /// use evalexpr::*; /// -/// let mut configuration = HashMapConfiguration::new(); -/// configuration.insert_variable("alpha", 2); +/// let mut configuration = HashMapContext::new(); +/// configuration.set_value("alpha", 2).unwrap(); // Do proper error handling here /// let node = build_operator_tree("1 + alpha").unwrap(); // Do proper error handling here /// assert_eq!(node.eval_with_configuration(&configuration), Ok(Value::from(3))); /// ``` @@ -46,7 +46,10 @@ impl Node { /// Evaluates the operator tree rooted at this node with the given configuration. /// /// Fails, if one of the operators in the expression tree fails. - pub fn eval_with_configuration(&self, configuration: &Configuration) -> Result { + pub fn eval_with_configuration( + &self, + configuration: &Configuration, + ) -> Result { let mut arguments = Vec::new(); for child in self.children() { arguments.push(child.eval_with_configuration(configuration)?); @@ -58,7 +61,7 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval(&self) -> Result { - self.eval_with_configuration(&EmptyConfiguration) + self.eval_with_configuration(&EmptyContext) } /// Evaluates the operator tree rooted at this node into a string with an the given configuration. @@ -135,35 +138,35 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_string(&self) -> Result { - self.eval_string_with_configuration(&EmptyConfiguration) + self.eval_string_with_configuration(&EmptyContext) } /// Evaluates the operator tree rooted at this node into a float with an empty configuration. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_float(&self) -> Result { - self.eval_float_with_configuration(&EmptyConfiguration) + self.eval_float_with_configuration(&EmptyContext) } /// Evaluates the operator tree rooted at this node into an integer with an empty configuration. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_int(&self) -> Result { - self.eval_int_with_configuration(&EmptyConfiguration) + self.eval_int_with_configuration(&EmptyContext) } /// Evaluates the operator tree rooted at this node into a boolean with an empty configuration. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_boolean(&self) -> Result { - self.eval_boolean_with_configuration(&EmptyConfiguration) + self.eval_boolean_with_configuration(&EmptyContext) } /// Evaluates the operator tree rooted at this node into a tuple with an empty configuration. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_tuple(&self) -> Result { - self.eval_tuple_with_configuration(&EmptyConfiguration) + self.eval_tuple_with_configuration(&EmptyContext) } fn children(&self) -> &[Node] { @@ -178,7 +181,11 @@ impl Node { self.children().len() == self.operator().argument_amount() } - fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> Result<(), EvalexprError> { + fn insert_back_prioritized( + &mut self, + node: Node, + is_root_node: bool, + ) -> Result<(), EvalexprError> { if self.operator().precedence() < node.operator().precedence() || is_root_node // Right-to-left chaining || (self.operator().precedence() == node.operator().precedence() && !self.operator().is_left_to_right() && !node.operator().is_left_to_right()) diff --git a/src/value/mod.rs b/src/value/mod.rs index 9073546..88792cb 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,6 +1,7 @@ use error::EvalexprError; mod display; +pub mod value_type; /// The type used to represent integers in `Value::Int`. pub type IntType = i64; diff --git a/src/value/value_type.rs b/src/value/value_type.rs new file mode 100644 index 0000000..ded6cc0 --- /dev/null +++ b/src/value/value_type.rs @@ -0,0 +1,35 @@ +use Value; + +/// The type of a `Value`. +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum ValueType { + String, + Float, + Int, + Boolean, + Tuple, +} + +impl From<&Value> for ValueType { + fn from(value: &Value) -> Self { + match value { + Value::String(_) => ValueType::String, + Value::Float(_) => ValueType::Float, + Value::Int(_) => ValueType::Int, + Value::Boolean(_) => ValueType::Boolean, + Value::Tuple(_) => ValueType::Tuple, + } + } +} + +impl From<&mut Value> for ValueType { + fn from(value: &mut Value) -> Self { + From::<&Value>::from(value) + } +} + +impl From<&&mut Value> for ValueType { + fn from(value: &&mut Value) -> Self { + From::<&Value>::from(*value) + } +} \ No newline at end of file diff --git a/tests/integration.rs b/tests/integration.rs index 0ad0ddf..40cf21f 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -10,7 +10,9 @@ fn test_unary_examples() { assert_eq!(eval("false"), Ok(Value::Boolean(false))); assert_eq!( eval("blub"), - Err(EvalexprError::VariableIdentifierNotFound("blub".to_string())) + Err(EvalexprError::VariableIdentifierNotFound( + "blub".to_string() + )) ); assert_eq!(eval("-3"), Ok(Value::Int(-3))); assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6))); @@ -97,13 +99,13 @@ fn test_boolean_examples() { #[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)); + let mut configuration = HashMapContext::new(); + configuration.set_value("tr", Value::Boolean(true)).unwrap(); + configuration.set_value("fa", Value::Boolean(false)).unwrap(); + configuration.set_value("five", Value::Int(5)).unwrap(); + configuration.set_value("six", Value::Int(6)).unwrap(); + configuration.set_value("half", Value::Float(0.5)).unwrap(); + configuration.set_value("zero", Value::Int(0)).unwrap(); assert_eq!( eval_with_configuration("tr", &configuration), @@ -133,8 +135,8 @@ fn test_with_configuration() { #[test] fn test_functions() { - let mut configuration = HashMapConfiguration::new(); - configuration.insert_function( + let mut configuration = HashMapContext::new(); + configuration.set_function( "sub2".to_string(), Function::new( Some(1), @@ -148,8 +150,8 @@ fn test_functions() { } }), ), - ); - configuration.insert_variable("five".to_string(), Value::Int(5)); + ).unwrap(); + configuration.set_value("five".to_string(), Value::Int(5)).unwrap(); assert_eq!( eval_with_configuration("sub2 5", &configuration), @@ -175,8 +177,8 @@ fn test_functions() { #[test] fn test_n_ary_functions() { - let mut configuration = HashMapConfiguration::new(); - configuration.insert_function( + let mut configuration = HashMapContext::new(); + configuration.set_function( "sub2", Function::new( Some(1), @@ -190,8 +192,8 @@ fn test_n_ary_functions() { } }), ), - ); - configuration.insert_function( + ).unwrap(); + configuration.set_function( "avg", Function::new( Some(2), @@ -208,8 +210,8 @@ fn test_n_ary_functions() { } }), ), - ); - configuration.insert_function( + ).unwrap(); + configuration.set_function( "muladd", Function::new( Some(3), @@ -230,15 +232,15 @@ fn test_n_ary_functions() { } }), ), - ); - configuration.insert_function( + ).unwrap(); + configuration.set_function( "count", Function::new( None, Box::new(|arguments| Ok(Value::Int(arguments.len() as IntType))), ), - ); - configuration.insert_variable("five".to_string(), Value::Int(5)); + ).unwrap(); + configuration.set_value("five".to_string(), Value::Int(5)).unwrap(); assert_eq!( eval_with_configuration("avg(7, 5)", &configuration), @@ -339,8 +341,8 @@ fn test_no_panic() { #[test] fn test_shortcut_functions() { - let mut configuration = HashMapConfiguration::new(); - configuration.insert_variable("string", Value::from("a string")); + let mut configuration = HashMapContext::new(); + configuration.set_value("string", Value::from("a string")).unwrap(); // assert_eq!(eval_string("???")); assert_eq!(