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:
parent
fd879193b6
commit
3da6019dae
@ -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"]
|
||||
|
@ -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
110
src/context/mod.rs
Normal 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(())
|
||||
}
|
||||
}
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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>;
|
||||
|
@ -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)));
|
||||
/// ```
|
||||
///
|
||||
|
33
src/lib.rs
33
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
|
||||
|
||||
|
@ -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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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
35
src/value/value_type.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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!(
|
||||
|
Loading…
Reference in New Issue
Block a user