Add Context trait

* Rename `HashMapConfiguration` to `HashMapContext`
 * Rename `EmptyConfiguration` to `EmptyContext`
 * Implement `Context` for both predefined contexts
 * Update tests and doctests

Relates to #22
This commit is contained in:
Sebastian Schmidt 2019-03-27 16:38:59 +01:00
parent fd879193b6
commit 3da6019dae
12 changed files with 370 additions and 165 deletions

View File

@ -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"]

View File

@ -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<String, Value>,
functions: HashMap<String, Function>,
}
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<S: Into<String>, V: Into<Value>>(&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<S: Into<String>>(&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)
}
}

110
src/context/mod.rs Normal file
View File

@ -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<S: Into<String>, V: Into<Value>>(&mut self, identifier: S, value: V) -> EvalexprResult<()>;
/// Links the given function to the given identifier.
fn set_function<S: Into<String>>(&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<S: Into<String>, V: Into<Value>>(&mut self, _identifier: S, _value: V) -> EvalexprResult<()> {
Err(EvalexprError::ContextNotManipulable)
}
fn set_function<S: Into<String>>(&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<String, Value>,
functions: HashMap<String, Function>,
}
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<S: Into<String>, V: Into<Value>>(&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<S: Into<String>>(&mut self, identifier: S, function: Function) -> Result<(), EvalexprError> {
self.functions.insert(identifier.into(), function);
Ok(())
}
}

View File

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

View File

@ -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<PartialToken>,
@ -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<bool, EvalexprError> {
impl std::error::Error for EvalexprError {}
pub type EvalexprResult<T> = Result<T, EvalexprError>;
/// Standard result type used by this crate.
pub type EvalexprResult<T> = Result<T, EvalexprError>;

View File

@ -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<Value, EvalexprError> {
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<Value, EvalexprError> {
/// ```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)));
/// ```
///

View File

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

View File

@ -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<Value, EvalexprError>;
fn eval(
&self,
arguments: &[Value],
configuration: &Configuration,
) -> Result<Value, EvalexprError>;
}
#[derive(Debug)]
@ -118,7 +122,11 @@ impl Operator for RootNode {
1
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
_configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
_arguments: &[Value],
configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
fn eval(
&self,
arguments: &[Value],
configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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(),
))
}
}
}

View File

@ -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<Value, EvalexprError> {
pub fn eval_with_configuration(
&self,
configuration: &Configuration,
) -> Result<Value, EvalexprError> {
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<Value, EvalexprError> {
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<String, EvalexprError> {
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<FloatType, EvalexprError> {
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<IntType, EvalexprError> {
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<bool, EvalexprError> {
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<TupleType, EvalexprError> {
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())

View File

@ -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;

35
src/value/value_type.rs Normal file
View File

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

View File

@ -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!(