diff --git a/src/bin/expressive.rs b/src/bin/expressive.rs new file mode 100644 index 0000000..ac1ee3f --- /dev/null +++ b/src/bin/expressive.rs @@ -0,0 +1,7 @@ +pub fn main() -> Result<(), Box> { + let args = std::env::args().skip(1).collect::>().join(" "); + + println!("{}", evalexpr::eval(&args)?); + + Ok(()) +} diff --git a/src/context/mod.rs b/src/context/mod.rs index 8d562c0..0cf7d84 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -4,7 +4,7 @@ //! This crate implements two basic variants, the `EmptyContext`, that returns `None` for each identifier and cannot be manipulated, 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. -use std::{collections::HashMap, iter}; +use std::collections::BTreeMap; use crate::{ function::Function, @@ -27,7 +27,7 @@ pub trait Context { /// A context that allows to assign to variables. pub trait ContextWithMutableVariables: Context { /// Sets the variable with the given identifier to the given value. - fn set_value(&mut self, _identifier: String, _value: Value) -> EvalexprResult<()> { + fn set_value(&mut self, _identifier: &str, _value: Value) -> EvalexprResult<()> { Err(EvalexprError::ContextNotMutable) } } @@ -40,105 +40,81 @@ pub trait ContextWithMutableFunctions: Context { } } -/// A context that allows to iterate over its variable names with their values. -/// -/// **Note:** this trait will change after GATs are stabilised, because then we can get rid of the lifetime in the trait definition. -pub trait IterateVariablesContext { - /// The iterator type for iterating over variable name-value pairs. - type VariableIterator<'a>: Iterator - where - Self: 'a; - /// The iterator type for iterating over variable names. - type VariableNameIterator<'a>: Iterator - where - Self: 'a; - - /// Returns an iterator over pairs of variable names and values. - fn iter_variables(&self) -> Self::VariableIterator<'_>; - - /// Returns an iterator over variable names. - fn iter_variable_names(&self) -> Self::VariableNameIterator<'_>; -} - /// 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. #[derive(Clone, Debug, Default)] -pub struct ContextData { - map_name: String, - - variables: HashMap, - - functions: HashMap, +pub struct VariableMap { + variables: BTreeMap, } -impl ContextData { - /// Constructs a `HashMapContext` with no mappings. +impl PartialEq for VariableMap { + fn eq(&self, other: &Self) -> bool { + if self.variables.len() != other.variables.len() { + return false; + } + + for variable in &self.variables { + for other in &other.variables { + if variable != other { + return false; + } + } + } + + true + } +} + +impl VariableMap { + /// Create a new instace. pub fn new() -> Self { Default::default() } } -impl Context for ContextData { +impl Context for VariableMap { fn get_value(&self, identifier: &str) -> Option<&Value> { self.variables.get(identifier) } fn call_function(&self, identifier: &str, argument: &Value) -> EvalexprResult { match identifier { - "map" => { - let map = Value::Map(self.variables.clone()); - self.variables.insert(identifier.to_string(), map); - Ok(Value::Empty) - }, _ => todo!(), } } - } -impl ContextWithMutableVariables for ContextData { - fn set_value(&mut self, identifier: String, value: Value) -> EvalexprResult<()> { - if let Some(existing_value) = self.variables.get_mut(&identifier) { - if ValueType::from(&existing_value) == ValueType::from(&value) { - *existing_value = value; - return Ok(()); +impl ContextWithMutableVariables for VariableMap { + fn set_value(&mut self, identifier: &str, value: Value) -> EvalexprResult<()> { + println!("{:#?}", self); + println!("{}", identifier); + + let split = identifier.split_once("."); + if let Some((map_name, next_identifier)) = split { + if let Some(map_value) = self.variables.get_mut(map_name) { + if let Value::Map(map) = map_value { + map.set_value(next_identifier, value)?; + } else { + return Err(EvalexprError::ExpectedMap { + actual: map_value.clone(), + }); + } } else { - return Err(EvalexprError::expected_type(existing_value, value)); + let mut new_map = BTreeMap::new(); + new_map.insert(next_identifier.to_string(), value); + let map_var = Value::Map(VariableMap { variables: new_map }); + self.variables.insert(map_name.to_string(), map_var); } + } else { + self.variables.insert(identifier.to_string(), value); } - // Implicit else, because `self.variables` and `identifier` are not unborrowed in else - self.variables.insert(identifier, value); Ok(()) } } -impl ContextWithMutableFunctions for ContextData { +impl ContextWithMutableFunctions for VariableMap { fn set_function(&mut self, identifier: String, function: Function) -> EvalexprResult<()> { - self.functions.insert(identifier, function); - Ok(()) - } -} - -impl IterateVariablesContext for ContextData { - type VariableIterator<'a> = std::iter::Map< - std::collections::hash_map::Iter<'a, String, Value>, - fn((&String, &Value)) -> (String, Value), - >; - type VariableNameIterator<'a> = - std::iter::Cloned>; - - fn iter_variables(&self) -> Self::VariableIterator<'_> { - self.variables - .iter() - .map(|(string, value)| (string.clone(), value.clone())) - } - - fn iter_variable_names(&self) -> Self::VariableNameIterator<'_> { - self.variables.keys().cloned() + todo!() } } @@ -179,7 +155,7 @@ macro_rules! context_map { // Create a context, then recurse to add the values in it ( $($tt:tt)* ) => {{ - let mut context = $crate::HashMapContext::new(); + let mut context = $crate::VariableMap::new(); $crate::context_map!((&mut context) $($tt)*) .map(|_| context) }}; diff --git a/src/function/builtin.rs b/src/function/builtin.rs index fb64060..9fc66b7 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -5,7 +5,10 @@ use crate::{ value::{FloatType, IntType}, EvalexprError, Function, Value, ValueType, }; -use std::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}; +use std::{ + collections::HashSet, + ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr}, +}; macro_rules! simple_math { ($func:ident) => { diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 3650255..b64f8cf 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -1,6 +1,6 @@ use crate::{ token, tree, value::TupleType, Context, ContextWithMutableVariables, EmptyType, EvalexprError, - EvalexprResult, FloatType, ContextData, IntType, Node, Value, EMPTY_VALUE, + EvalexprResult, FloatType, IntType, Node, Value, VariableMap, EMPTY_VALUE, }; /// Evaluate the given expression string. @@ -15,7 +15,7 @@ use crate::{ /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval(string: &str) -> EvalexprResult { - eval_with_context_mut(string, &mut ContextData::new()) + eval_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string with the given context. @@ -91,21 +91,21 @@ pub fn build_operator_tree(string: &str) -> EvalexprResult { /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_string(string: &str) -> EvalexprResult { - eval_string_with_context_mut(string, &mut ContextData::new()) + eval_string_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string into an integer. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_int(string: &str) -> EvalexprResult { - eval_int_with_context_mut(string, &mut ContextData::new()) + eval_int_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string into a float. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_float(string: &str) -> EvalexprResult { - eval_float_with_context_mut(string, &mut ContextData::new()) + eval_float_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string into a float. @@ -113,28 +113,28 @@ pub fn eval_float(string: &str) -> EvalexprResult { /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_number(string: &str) -> EvalexprResult { - eval_number_with_context_mut(string, &mut ContextData::new()) + eval_number_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string into a boolean. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_boolean(string: &str) -> EvalexprResult { - eval_boolean_with_context_mut(string, &mut ContextData::new()) + eval_boolean_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string into a tuple. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_tuple(string: &str) -> EvalexprResult { - eval_tuple_with_context_mut(string, &mut ContextData::new()) + eval_tuple_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string into an empty value. /// /// *See the [crate doc](index.html) for more examples and explanations of the expression format.* pub fn eval_empty(string: &str) -> EvalexprResult { - eval_empty_with_context_mut(string, &mut ContextData::new()) + eval_empty_with_context_mut(string, &mut VariableMap::new()) } /// Evaluate the given expression string into a string with the given context. diff --git a/src/lib.rs b/src/lib.rs index e352b4a..2bf53a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -560,9 +560,7 @@ extern crate serde; extern crate serde_derive; pub use crate::{ - context::{ - Context, ContextWithMutableFunctions, ContextWithMutableVariables, ContextData, IterateVariablesContext, - }, + context::{Context, ContextWithMutableFunctions, ContextWithMutableVariables, VariableMap}, error::{EvalexprError, EvalexprResult}, function::Function, interface::*, diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 31f13b2..9c8825d 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -459,8 +459,7 @@ impl Operator { let arguments = &arguments[0]; match context.call_function(identifier, arguments) { - Err(EvalexprError::FunctionIdentifierNotFound(_)) => - { + Err(EvalexprError::FunctionIdentifierNotFound(_)) => { if let Some(builtin_function) = builtin_function(identifier) { builtin_function.call(arguments) } else { @@ -486,7 +485,7 @@ impl Operator { Assign => { expect_operator_argument_amount(arguments.len(), 2)?; let target = arguments[0].as_string()?; - context.set_value(target, arguments[1].clone())?; + context.set_value(&target, arguments[1].clone())?; Ok(Value::Empty) }, @@ -515,7 +514,7 @@ impl Operator { self ), }?; - context.set_value(target, result)?; + context.set_value(&target, result)?; Ok(Value::Empty) }, diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 5422305..8c13bc4 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,7 +1,7 @@ use crate::{ token::Token, value::{TupleType, EMPTY_VALUE}, - Context, ContextWithMutableVariables, EmptyType, FloatType, ContextData, IntType, + Context, ContextWithMutableVariables, EmptyType, FloatType, IntType, VariableMap, }; use crate::{ @@ -346,7 +346,7 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval(&self) -> EvalexprResult { - self.eval_with_context_mut(&mut ContextData::new()) + self.eval_with_context_mut(&mut VariableMap::new()) } /// Evaluates the operator tree rooted at this node into a string with an the given context. @@ -532,21 +532,21 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_string(&self) -> EvalexprResult { - self.eval_string_with_context_mut(&mut ContextData::new()) + self.eval_string_with_context_mut(&mut VariableMap::new()) } /// Evaluates the operator tree rooted at this node into a float. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_float(&self) -> EvalexprResult { - self.eval_float_with_context_mut(&mut ContextData::new()) + self.eval_float_with_context_mut(&mut VariableMap::new()) } /// Evaluates the operator tree rooted at this node into an integer. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_int(&self) -> EvalexprResult { - self.eval_int_with_context_mut(&mut ContextData::new()) + self.eval_int_with_context_mut(&mut VariableMap::new()) } /// Evaluates the operator tree rooted at this node into a float. @@ -554,28 +554,28 @@ impl Node { /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_number(&self) -> EvalexprResult { - self.eval_number_with_context_mut(&mut ContextData::new()) + self.eval_number_with_context_mut(&mut VariableMap::new()) } /// Evaluates the operator tree rooted at this node into a boolean. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_boolean(&self) -> EvalexprResult { - self.eval_boolean_with_context_mut(&mut ContextData::new()) + self.eval_boolean_with_context_mut(&mut VariableMap::new()) } /// Evaluates the operator tree rooted at this node into a tuple. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_tuple(&self) -> EvalexprResult { - self.eval_tuple_with_context_mut(&mut ContextData::new()) + self.eval_tuple_with_context_mut(&mut VariableMap::new()) } /// Evaluates the operator tree rooted at this node into an empty value. /// /// Fails, if one of the operators in the expression tree fails. pub fn eval_empty(&self) -> EvalexprResult { - self.eval_empty_with_context_mut(&mut ContextData::new()) + self.eval_empty_with_context_mut(&mut VariableMap::new()) } /// Returns the children of this node as a slice. diff --git a/src/value/mod.rs b/src/value/mod.rs index d598557..4e5f986 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,5 +1,11 @@ -use crate::error::{EvalexprError, EvalexprResult}; -use std::{convert::TryFrom, collections::HashMap}; +use crate::{ + error::{EvalexprError, EvalexprResult}, + VariableMap, +}; +use std::{ + collections::{BTreeMap, HashMap}, + convert::TryFrom, +}; mod display; pub mod value_type; @@ -37,7 +43,7 @@ pub enum Value { /// An empty value. Empty, /// Collection of key-value pairs. - Map(HashMap), + Map(VariableMap), } impl Value { diff --git a/tests/integration.rs b/tests/integration.rs index f43761e..82b99c0 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -110,7 +110,7 @@ fn test_boolean_examples() { #[test] fn test_with_context() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); context .set_value("tr".into(), Value::Boolean(true)) .unwrap(); @@ -144,7 +144,7 @@ fn test_with_context() { #[test] fn test_functions() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); context .set_function( "sub2".to_string(), @@ -175,7 +175,7 @@ fn test_functions() { #[test] fn test_n_ary_functions() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); context .set_function( "sub2".into(), @@ -282,7 +282,7 @@ fn test_n_ary_functions() { #[test] fn test_capturing_functions() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); // this variable is captured by the function let three = 3; context @@ -601,7 +601,7 @@ fn test_no_panic() { #[test] fn test_shortcut_functions() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); context .set_value("string".into(), Value::from("a string")) .unwrap(); @@ -1284,7 +1284,7 @@ fn test_whitespace() { #[test] fn test_assignment() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); assert_eq!( eval_empty_with_context_mut("int = 3", &mut context), Ok(EMPTY_VALUE) @@ -1324,7 +1324,7 @@ fn test_assignment() { #[test] fn test_expression_chaining() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); assert_eq!( eval_int_with_context_mut("a = 5; a = a + 2; a", &mut context), Ok(7) @@ -1333,7 +1333,7 @@ fn test_expression_chaining() { #[test] fn test_strings() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); assert_eq!(eval("\"string\""), Ok(Value::from("string"))); assert_eq!( eval_with_context_mut("a = \"a string\"", &mut context), @@ -1441,7 +1441,7 @@ fn test_implicit_context() { #[test] fn test_operator_assignments() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); assert_eq!(eval_empty_with_context_mut("a = 5", &mut context), Ok(())); assert_eq!(eval_empty_with_context_mut("a += 5", &mut context), Ok(())); assert_eq!(eval_empty_with_context_mut("a -= 5", &mut context), Ok(())); @@ -1463,7 +1463,7 @@ fn test_operator_assignments() { Ok(()) ); - let mut context = ContextData::new(); + let mut context = VariableMap::new(); assert_eq!(eval_int_with_context_mut("a = 5; a", &mut context), Ok(5)); assert_eq!(eval_int_with_context_mut("a += 3; a", &mut context), Ok(8)); assert_eq!(eval_int_with_context_mut("a -= 5; a", &mut context), Ok(3)); @@ -1643,7 +1643,7 @@ fn test_hashmap_context_type_safety() { #[test] fn test_hashmap_context_clone_debug() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); // this variable is captured by the function let three = 3; context @@ -2228,7 +2228,7 @@ fn assignment_lhs_is_identifier() { let tree = build_operator_tree("a = 1").unwrap(); let operators: Vec<_> = tree.iter().map(|node| node.operator().clone()).collect(); - let mut context = ContextData::new(); + let mut context = VariableMap::new(); tree.eval_with_context_mut(&mut context).unwrap(); assert_eq!(context.get_value("a"), Some(&Value::Int(1))); @@ -2250,7 +2250,7 @@ fn assignment_lhs_is_identifier() { #[test] fn test_variable_assignment_and_iteration() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); eval_with_context_mut("a = 5; b = 5.0", &mut context).unwrap(); let mut variables: Vec<_> = context.iter_variables().collect(); @@ -2278,7 +2278,7 @@ fn test_negative_power() { #[test] fn test_builtin_functions_context() { - let mut context = ContextData::new(); + let mut context = VariableMap::new(); // Builtin functions are enabled by default for HashMapContext. assert_eq!(eval_with_context("max(1,3)", &context), Ok(Value::from(3))); // Disabling builtin function in Context.