From 4fd86751dcfa6cd804a7f709a7f1517eda255a3c Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Thu, 13 Apr 2023 14:42:39 +0300 Subject: [PATCH] Fix unary operator precedence. Before, unary operators that appeared after an operator with higher precedence would be executed after that operator. However, in evalexpr, the two unary operators are prefix operators negation and not, and prefix operators always happen before operators directly left of them. --- src/operator/mod.rs | 5 +++++ src/tree/mod.rs | 20 ++++++++++++++------ tests/integration.rs | 11 ++++++----- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 5f2be51..13920e1 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -173,6 +173,11 @@ impl Operator { } } + /// Returns true if this operator is unary, i.e. it requires exactly one argument. + pub(crate) fn is_unary(&self) -> bool { + self.max_argument_amount() == Some(1) && *self != Operator::RootNode + } + /// Evaluates the operator with the given arguments and context. pub(crate) fn eval( &self, diff --git a/src/tree/mod.rs b/src/tree/mod.rs index c225994..a655386 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -459,8 +459,13 @@ impl Node { } 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 + println!( + "Inserting {:?} into {:?}, is_root_node = {is_root_node}", + node.operator(), + self.operator() + ); + println!("Self is {:?}", self); + if self.operator().precedence() < node.operator().precedence() || node.operator().is_unary() || 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()) { @@ -471,19 +476,22 @@ impl Node { let last_child_operator = self.children.last().unwrap().operator(); if last_child_operator.precedence() - < node.operator().precedence() + < node.operator().precedence() || node.operator().is_unary() // Right-to-left chaining || (last_child_operator.precedence() == node.operator().precedence() && !last_child_operator.is_left_to_right() && !node.operator().is_left_to_right()) { - // println!("Recursing into {:?}", self.children.last().unwrap().operator()); + println!( + "Recursing into {:?}", + self.children.last().unwrap().operator() + ); // Unwrap cannot fail because is_leaf being false and has_enough_children being true implies that the operator wants and has at least one child self.children .last_mut() .unwrap() .insert_back_prioritized(node, false) } else { - // println!("Rotating"); + println!("Rotating"); if node.operator().is_leaf() { return Err(EvalexprError::AppendedToLeafNode); } @@ -521,7 +529,7 @@ impl Node { Ok(()) } } else { - // println!("Inserting as specified"); + println!("Inserting as specified"); self.children.push(node); Ok(()) } diff --git a/tests/integration.rs b/tests/integration.rs index 1496477..5786f2f 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -2148,10 +2148,11 @@ fn test_variable_assignment_and_iteration() { #[test] fn test_negative_power() { - assert_eq!(eval("3^-2"), Ok(Value::Float(1.0/9.0))); - assert_eq!(eval("3^(-2)"), Ok(Value::Float(1.0/9.0))); + println!("{:?}", build_operator_tree("3^-2").unwrap()); + assert_eq!(eval("3^-2"), Ok(Value::Float(1.0 / 9.0))); + assert_eq!(eval("3^(-2)"), Ok(Value::Float(1.0 / 9.0))); assert_eq!(eval("-3^2"), Ok(Value::Float(-9.0))); assert_eq!(eval("-(3)^2"), Ok(Value::Float(-9.0))); - assert_eq!(eval("(-3)^-2"), Ok(Value::Float(1.0/9.0))); - assert_eq!(eval("-(3^-2)"), Ok(Value::Float(-1.0/9.0))); -} \ No newline at end of file + assert_eq!(eval("(-3)^-2"), Ok(Value::Float(1.0 / 9.0))); + assert_eq!(eval("-(3^-2)"), Ok(Value::Float(-1.0 / 9.0))); +}