diff --git a/src/context/mod.rs b/src/context/mod.rs index e07f9c7..3161027 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -103,7 +103,7 @@ impl Context for HashMapContext { /// /// let ctx = evalexpr::context_map! { /// "x" => 8, -/// "f" => Function::new(None, Box::new(|_| Ok(42.into()) )) +/// "f" => Function::new(Box::new(|_| Ok(42.into()) )) /// }.unwrap(); /// /// assert_eq!(eval_with_context("x + f()", &ctx), Ok(50.into())); diff --git a/src/error/mod.rs b/src/error/mod.rs index 9b73b47..a053371 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -353,6 +353,14 @@ pub fn expect_boolean(actual: &Value) -> EvalexprResult { } } +/// Returns `Ok(&[Value])` if the given value is a `Value::Tuple`, or `Err(Error::ExpectedTuple)` otherwise. +pub fn expect_tuple(actual: &Value) -> EvalexprResult<&TupleType> { + match actual { + Value::Tuple(tuple) => Ok(tuple), + _ => Err(EvalexprError::expected_tuple(actual.clone())), + } +} + impl std::error::Error for EvalexprError {} /// Standard result type used by this crate. diff --git a/src/function/builtin.rs b/src/function/builtin.rs index 1ff1256..f1c92c1 100644 --- a/src/function/builtin.rs +++ b/src/function/builtin.rs @@ -10,8 +10,8 @@ use Value; pub fn builtin_function(identifier: &str) -> Option { match identifier { "min" => Some(Function::new( - None, - Box::new(|arguments| { + Box::new(|argument| { + let arguments = expect_tuple(argument)?; let mut min_int = IntType::max_value(); let mut min_float = 1.0f64 / 0.0f64; debug_assert!(min_float.is_infinite()); @@ -34,8 +34,8 @@ pub fn builtin_function(identifier: &str) -> Option { }), )), "max" => Some(Function::new( - None, - Box::new(|arguments| { + Box::new(|argument| { + let arguments = expect_tuple(argument)?; let mut max_int = IntType::min_value(); let mut max_float = -1.0f64 / 0.0f64; debug_assert!(max_float.is_infinite()); @@ -59,9 +59,8 @@ pub fn builtin_function(identifier: &str) -> Option { )), "len" => Some(Function::new( - Some(1), - Box::new(|arguments| { - let subject = expect_string(&arguments[0])?; + Box::new(|argument| { + let subject = expect_string(argument)?; Ok(Value::from(subject.len() as i64)) }), )), @@ -69,8 +68,9 @@ pub fn builtin_function(identifier: &str) -> Option { // string functions #[cfg(feature = "regex_support")] "str::regex_matches" => Some(Function::new( - Some(2), - Box::new(|arguments| { + Box::new(|argument| { + let arguments = expect_tuple(argument)?; + let subject = expect_string(&arguments[0])?; let re_str = expect_string(&arguments[1])?; match Regex::new(re_str) { @@ -84,8 +84,9 @@ pub fn builtin_function(identifier: &str) -> Option { )), #[cfg(feature = "regex_support")] "str::regex_replace" => Some(Function::new( - Some(3), - Box::new(|arguments| { + Box::new(|argument| { + let arguments = expect_tuple(argument)?; + let subject = expect_string(&arguments[0])?; let re_str = expect_string(&arguments[1])?; let repl = expect_string(&arguments[2])?; @@ -99,23 +100,20 @@ pub fn builtin_function(identifier: &str) -> Option { }), )), "str::to_lowercase" => Some(Function::new( - Some(1), - Box::new(|arguments| { - let subject = expect_string(&arguments[0])?; + Box::new(|argument| { + let subject = expect_string(argument)?; Ok(Value::from(subject.to_lowercase())) }), )), "str::to_uppercase" => Some(Function::new( - Some(1), - Box::new(|arguments| { - let subject = expect_string(&arguments[0])?; + Box::new(|argument| { + let subject = expect_string(argument)?; Ok(Value::from(subject.to_uppercase())) }), )), "str::trim" => Some(Function::new( - Some(1), - Box::new(|arguments| { - let subject = expect_string(&arguments[0])?; + Box::new(|argument| { + let subject = expect_string(argument)?; Ok(Value::from(subject.trim())) }), )), diff --git a/src/function/mod.rs b/src/function/mod.rs index b861adb..ccab8f0 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -1,6 +1,6 @@ use std::fmt; -use error::{self, EvalexprResult}; +use error::{EvalexprResult}; use value::Value; pub(crate) mod builtin; @@ -14,39 +14,29 @@ pub(crate) mod builtin; /// use evalexpr::*; /// /// let mut context = HashMapContext::new(); -/// context.set_function("id".into(), Function::new(Some(1), Box::new(|arguments| { -/// Ok(arguments[0].clone()) +/// context.set_function("id".into(), Function::new(Box::new(|argument| { +/// Ok(argument.clone()) /// }))).unwrap(); // Do proper error handling here /// assert_eq!(eval_with_context("id(4)", &context), Ok(Value::from(4))); /// ``` pub struct Function { - argument_amount: Option, - function: Box EvalexprResult>, + function: Box EvalexprResult>, } impl Function { /// Creates a user-defined function. /// - /// The `argument_amount` is the amount of arguments this function takes. - /// It is verified before the actual function is executed, assuming it is not `None`. - /// - /// The `function` is a boxed function that takes a slice of values and returns a `EvalexprResult`. + /// The `function` is a boxed function that takes a `Value` and returns a `EvalexprResult`. pub fn new( - argument_amount: Option, - function: Box EvalexprResult>, + function: Box EvalexprResult>, ) -> Self { Self { - argument_amount, function, } } - pub(crate) fn call(&self, arguments: &[Value]) -> EvalexprResult { - if let Some(argument_amount) = self.argument_amount { - error::expect_function_argument_amount(arguments.len(), argument_amount)?; - } - - (self.function)(arguments) + pub(crate) fn call(&self, argument: &Value) -> EvalexprResult { + (self.function)(argument) } } @@ -54,8 +44,7 @@ impl fmt::Debug for Function { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!( f, - "Function {{ argument_amount: {:?}, function: [...] }}", - self.argument_amount + "Function {{ [...] }}" ) } } diff --git a/src/lib.rs b/src/lib.rs index 9d4d343..88a0155 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,21 +49,22 @@ //! //! ```rust //! use evalexpr::*; -//! use evalexpr::error::expect_number; +//! use evalexpr::error::{expect_number, expect_tuple}; //! //! let context = context_map! { //! "five" => 5, //! "twelve" => 12, -//! "f" => Function::new(Some(1) /* argument amount */, Box::new(|arguments| { -//! if let Value::Int(int) = arguments[0] { +//! "f" => Function::new(Box::new(|argument| { +//! if let Value::Int(int) = argument { //! Ok(Value::Int(int / 2)) -//! } else if let Value::Float(float) = arguments[0] { +//! } else if let Value::Float(float) = argument { //! Ok(Value::Float(float / 2.0)) //! } else { -//! Err(EvalexprError::expected_number(arguments[0].clone())) +//! Err(EvalexprError::expected_number(argument.clone())) //! } //! })), -//! "avg" => Function::new(Some(2) /* argument amount */, Box::new(|arguments| { +//! "avg" => Function::new(Box::new(|argument| { +//! let arguments = expect_tuple(argument)?; //! expect_number(&arguments[0])?; //! expect_number(&arguments[1])?; //! @@ -343,7 +344,7 @@ //! Ok(free) => assert_eq!(free.eval_with_context(&context), Ok(Value::from(25))), //! Err(error) => { //! () // Handle error -//! }, +//! } //! } //! # } //! ``` diff --git a/src/operator/mod.rs b/src/operator/mod.rs index de59d87..bb675dc 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -4,7 +4,7 @@ use crate::{context::Context, error::*, value::Value}; mod display; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Operator { RootNode, @@ -91,23 +91,34 @@ impl Operator { } } + /// Returns true if chains of this operator should be flattened into one operator with many arguments. + // Make this a const fn once #57563 is resolved + pub(crate) fn is_sequence(&self) -> bool { + use crate::operator::Operator::*; + match self { + Tuple | Chain => true, + _ => false, + } + } + /// True if this operator is a leaf, meaning it accepts no arguments. // Make this a const fn once #57563 is resolved pub(crate) fn is_leaf(&self) -> bool { - self.max_argument_amount() == 0 + self.max_argument_amount() == Some(0) } /// Returns the maximum amount of arguments required by this operator. // Make this a const fn once #57563 is resolved - pub(crate) fn max_argument_amount(&self) -> usize { + pub(crate) fn max_argument_amount(&self) -> Option { use crate::operator::Operator::*; match self { Add | Sub | Mul | Div | Mod | Exp | Eq | Neq | Gt | Lt | Geq | Leq | And | Or - | Tuple | Assign | Chain => 2, - Not | Neg | RootNode => 1, - Const { value: _ } => 0, - VariableIdentifier { identifier: _ } => 0, - FunctionIdentifier { identifier: _ } => 1, + | Assign => Some(2), + Tuple | Chain => None, + Not | Neg | RootNode => Some(1), + Const { value: _ } => Some(0), + VariableIdentifier { identifier: _ } => Some(0), + FunctionIdentifier { identifier: _ } => Some(1), } } @@ -410,27 +421,7 @@ impl Operator { } }, Tuple => { - expect_operator_argument_amount(arguments.len(), 2)?; - if let Value::Tuple(tuple) = &arguments[0] { - let mut tuple = tuple.clone(); - if let Value::Tuple(tuple2) = &arguments[1] { - tuple.extend(tuple2.iter().cloned()); - } else { - tuple.push(arguments[1].clone()); - } - Ok(Value::from(tuple)) - } else { - if let Value::Tuple(tuple) = &arguments[1] { - let mut tuple = tuple.clone(); - tuple.insert(0, arguments[0].clone()); - Ok(Value::from(tuple)) - } else { - Ok(Value::from(vec![ - arguments[0].clone(), - arguments[1].clone(), - ])) - } - } + Ok(Value::Tuple(arguments.into())) }, Assign => Err(EvalexprError::ContextNotManipulable), Chain => { @@ -438,7 +429,7 @@ impl Operator { return Err(EvalexprError::wrong_operator_argument_amount(0, 1)); } - Ok(arguments.get(1).cloned().unwrap_or(Value::Empty)) + Ok(arguments.last().cloned().unwrap_or(Value::Empty)) }, Const { value } => { expect_operator_argument_amount(arguments.len(), 0)?; @@ -456,12 +447,7 @@ impl Operator { }, FunctionIdentifier { identifier } => { expect_operator_argument_amount(arguments.len(), 1)?; - - let arguments = if let Value::Tuple(arguments) = &arguments[0] { - arguments - } else { - arguments - }; + let arguments = &arguments[0]; if let Some(function) = context.get_function(&identifier) { function.call(arguments) diff --git a/src/tree/mod.rs b/src/tree/mod.rs index a60f2b6..55c5b79 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -11,6 +11,7 @@ use crate::{ operator::*, value::Value, }; +use std::mem; mod display; mod iter; @@ -32,10 +33,10 @@ mod iter; /// assert_eq!(node.eval_with_context(&context), Ok(Value::from(3))); /// ``` /// -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Node { - children: Vec, operator: Operator, + children: Vec, } impl Node { @@ -362,10 +363,11 @@ impl Node { } fn has_enough_children(&self) -> bool { - self.children().len() == self.operator().max_argument_amount() + Some(self.children().len()) == self.operator().max_argument_amount() } fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> EvalexprResult<()> { + // println!("Inserting {:?} into {:?}", node.operator, self.operator()); 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()) @@ -379,11 +381,13 @@ impl Node { || (self.children.last().unwrap().operator().precedence() == node.operator().precedence() && !self.children.last().unwrap().operator().is_left_to_right() && !node.operator().is_left_to_right()) { + // println!("Recursing into {:?}", self.children.last().unwrap().operator()); self.children .last_mut() .unwrap() .insert_back_prioritized(node, false) } else { + // println!("Rotating"); if node.operator().is_leaf() { return Err(EvalexprError::AppendedToLeafNode); } @@ -396,6 +400,7 @@ impl Node { Ok(()) } } else { + // println!("Inserting as specified"); self.children.push(node); Ok(()) } @@ -405,8 +410,63 @@ impl Node { } } +fn collapse_root_stack_to(root_stack: &mut Vec, mut root: Node, collapse_goal: &Node) -> EvalexprResult { + loop { + if let Some(mut potential_higher_root) = root_stack.pop() { + // TODO I'm not sure about this >, as I have no example for different sequence operators with the same precedence + if potential_higher_root.operator().precedence() > collapse_goal.operator().precedence() { + potential_higher_root.children.push(root); + root = potential_higher_root; + } else { + root_stack.push(potential_higher_root); + break; + } + } else { + // This is the only way the topmost root node could have been removed + return Err(EvalexprError::UnmatchedRBrace); + } + } + + Ok(root) +} + +fn collapse_all_sequences(root_stack: &mut Vec) -> EvalexprResult<()> { + // println!("Collapsing all sequences"); + // println!("Initial root stack is: {:?}", root_stack); + let mut root = if let Some(root) = root_stack.pop() { + root + } else { + return Err(EvalexprError::UnmatchedRBrace); + }; + + loop { + // println!("Root is: {:?}", root); + if root.operator() == &Operator::RootNode { + root_stack.push(root); + break; + } + + if let Some(mut potential_higher_root) = root_stack.pop() { + if root.operator().is_sequence() { + potential_higher_root.children.push(root); + root = potential_higher_root; + } else { + root_stack.push(potential_higher_root); + root_stack.push(root); + break; + } + } else { + // This is the only way the topmost root node could have been removed + return Err(EvalexprError::UnmatchedRBrace); + } + } + + // println!("Root stack after collapsing all sequences is: {:?}", root_stack); + Ok(()) +} + pub(crate) fn tokens_to_operator_tree(tokens: Vec) -> EvalexprResult { - let mut root = vec![Node::root_node()]; + let mut root_stack = vec![Node::root_node()]; let mut last_token_is_rightsided_value = false; let mut token_iter = tokens.iter().peekable(); @@ -438,14 +498,15 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec) -> EvalexprResult Some(Node::new(Operator::Not)), Token::LBrace => { - root.push(Node::root_node()); + root_stack.push(Node::root_node()); None }, Token::RBrace => { - if root.len() < 2 { + if root_stack.len() <= 1 { return Err(EvalexprError::UnmatchedRBrace); } else { - root.pop() + collapse_all_sequences(&mut root_stack)?; + root_stack.pop() } }, @@ -470,9 +531,58 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec) -> EvalexprResult Some(Node::new(Operator::value(Value::String(string)))), }; - if let Some(node) = node { - if let Some(root) = root.last_mut() { - root.insert_back_prioritized(node, true)?; + if let Some(mut node) = node { + // Need to pop and then repush here, because Rust 1.33.0 cannot release the mutable borrow of root_stack before the end of this complete if-statement + if let Some(mut root) = root_stack.pop() { + if node.operator().is_sequence() { + // println!("Found a sequence operator"); + // println!("Stack before sequence operation: {:?}, {:?}", root_stack, root); + // If root.operator() and node.operator() are of the same variant, ... + if mem::discriminant(root.operator()) == mem::discriminant(node.operator()) { + // ... we create a new root node for the next expression in the sequence + root.children.push(Node::root_node()); + root_stack.push(root); + } else if root.operator() == &Operator::RootNode { + // If the current root is an actual root node, we start a new sequence + node.children.push(root); + node.children.push(Node::root_node()); + root_stack.push(Node::root_node()); + root_stack.push(node); + } else { + // Otherwise, we combine the sequences based on their precedences + // TODO I'm not sure about this <, as I have no example for different sequence operators with the same precedence + if root.operator().precedence() < node.operator().precedence() { + // If the new sequence has a higher precedence, it is part of the last element of the current root sequence + if let Some(last_root_child) = root.children.pop() { + node.children.push(last_root_child); + node.children.push(Node::root_node()); + root_stack.push(root); + root_stack.push(node); + } else { + // Once a sequence has been pushed on top of the stack, it also gets a child + unreachable!() + } + } else { + // If the new sequence doesn't have a higher precedence, then all sequences with a higher precedence are collapsed below this one + root = collapse_root_stack_to(&mut root_stack, root, &node)?; + node.children.push(root); + root_stack.push(node); + } + } + // println!("Stack after sequence operation: {:?}", root_stack); + } else if root.operator().is_sequence() { + if let Some(mut last_root_child) = root.children.pop() { + last_root_child.insert_back_prioritized(node, true)?; + root.children.push(last_root_child); + root_stack.push(root); + } else { + // Once a sequence has been pushed on top of the stack, it also gets a child + unreachable!() + } + } else { + root.insert_back_prioritized(node, true)?; + root_stack.push(root); + } } else { return Err(EvalexprError::UnmatchedRBrace); } @@ -481,9 +591,12 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec) -> EvalexprResult 1 { + // In the end, all sequences are implicitly terminated + collapse_all_sequences(&mut root_stack)?; + + if root_stack.len() > 1 { Err(EvalexprError::UnmatchedLBrace) - } else if let Some(root) = root.pop() { + } else if let Some(root) = root_stack.pop() { Ok(root) } else { Err(EvalexprError::UnmatchedRBrace) diff --git a/src/value/mod.rs b/src/value/mod.rs index b2e8cee..e12bef9 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -194,6 +194,12 @@ impl From for EvalexprResult { } } +impl From<()> for Value { + fn from(_: ()) -> Self { + Value::Empty + } +} + #[cfg(test)] mod tests { use value::{TupleType, Value}; diff --git a/tests/integration.rs b/tests/integration.rs index 0300454..3cf6274 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -138,14 +138,13 @@ fn test_functions() { .set_function( "sub2".to_string(), Function::new( - Some(1), - Box::new(|arguments| { - if let Value::Int(int) = arguments[0] { + Box::new(|argument| { + if let Value::Int(int) = argument { Ok(Value::Int(int - 2)) - } else if let Value::Float(float) = arguments[0] { + } else if let Value::Float(float) = argument { Ok(Value::Float(float - 2.0)) } else { - Err(EvalexprError::expected_number(arguments[0].clone())) + Err(EvalexprError::expected_number(argument.clone())) } }), ), @@ -172,14 +171,13 @@ fn test_n_ary_functions() { .set_function( "sub2".into(), Function::new( - Some(1), - Box::new(|arguments| { - if let Value::Int(int) = arguments[0] { + Box::new(|argument| { + if let Value::Int(int) = argument { Ok(Value::Int(int - 2)) - } else if let Value::Float(float) = arguments[0] { + } else if let Value::Float(float) = argument { Ok(Value::Float(float - 2.0)) } else { - Err(EvalexprError::expected_number(arguments[0].clone())) + Err(EvalexprError::expected_number(argument.clone())) } }), ), @@ -189,8 +187,8 @@ fn test_n_ary_functions() { .set_function( "avg".into(), Function::new( - Some(2), - Box::new(|arguments| { + Box::new(|argument| { + let arguments = expect_tuple(argument)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -209,8 +207,8 @@ fn test_n_ary_functions() { .set_function( "muladd".into(), Function::new( - Some(3), - Box::new(|arguments| { + Box::new(|argument| { + let arguments = expect_tuple(argument)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; expect_number(&arguments[2])?; @@ -233,16 +231,11 @@ fn test_n_ary_functions() { .set_function( "count".into(), Function::new( - None, Box::new(|arguments| { - if arguments.len() == 1 { - if arguments[0] == Value::Empty { - Ok(Value::Int(0)) - } else { - Ok(Value::Int(1)) - } - } else { - Ok(Value::Int(arguments.len() as IntType)) + match arguments { + Value::Tuple(tuple) => Ok(Value::from(tuple.len() as IntType)), + Value::Empty => Ok(Value::from(0)), + _ => Ok(Value::from(1)), } }), ), @@ -251,6 +244,7 @@ fn test_n_ary_functions() { context .set_value("five".to_string(), Value::Int(5)) .unwrap(); + context.set_function("function_four".into(), Function::new(Box::new(|_| {Ok(Value::Int(4))}))).unwrap(); assert_eq!(eval_with_context("avg(7, 5)", &context), Ok(Value::Int(6))); assert_eq!( @@ -270,11 +264,13 @@ fn test_n_ary_functions() { Ok(Value::Int(14)) ); assert_eq!(eval_with_context("count()", &context), Ok(Value::Int(0))); + assert_eq!(eval_with_context("count((1, 2, 3))", &context), Ok(Value::Int(3))); assert_eq!( eval_with_context("count(3, 5.5, 2)", &context), Ok(Value::Int(3)) ); assert_eq!(eval_with_context("count 5", &context), Ok(Value::Int(1))); + assert_eq!(eval_with_context("function_four()", &context), Ok(Value::Int(4))); } #[test] @@ -605,3 +601,51 @@ fn test_serde() { assert_eq!(manual_tree.eval(), serde_tree.eval()); } } + +#[test] +fn test_tuple_definitions() { + assert_eq!(eval_empty("()"), Ok(())); + assert_eq!(eval_int("(3)"), Ok(3)); + assert_eq!( + eval_tuple("(3, 4)"), + Ok(vec![Value::from(3), Value::from(4)]) + ); + assert_eq!( + eval_tuple("2, (5, 6)"), + Ok(vec![ + Value::from(2), + Value::from(vec![Value::from(5), Value::from(6)]) + ]) + ); + assert_eq!(eval_tuple("1, 2"), Ok(vec![Value::from(1), Value::from(2)])); + assert_eq!( + eval_tuple("1, 2, 3, 4"), + Ok(vec![ + Value::from(1), + Value::from(2), + Value::from(3), + Value::from(4) + ]) + ); + assert_eq!( + eval_tuple("(1, 2, 3), 5, 6, (true, false, 0)"), + Ok(vec![ + Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]), + Value::from(5), + Value::from(6), + Value::from(vec![Value::from(true), Value::from(false), Value::from(0)]) + ]) + ); + assert_eq!( + eval_tuple("1, (2)"), + Ok(vec![Value::from(1), Value::from(2)]) + ); + assert_eq!( + eval_tuple("1, ()"), + Ok(vec![Value::from(1), Value::from(())]) + ); + assert_eq!( + eval_tuple("1, ((2))"), + Ok(vec![Value::from(1), Value::from(2)]) + ); +}