From 1721e163b0431ee87a495ab8793a3e8cb7c5835d Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 13 Apr 2019 19:11:09 +0200 Subject: [PATCH 1/9] Make `Operator::max_argument_amount` optional Relates to #44 --- src/operator/mod.rs | 96 ++++++++++++++++++++++----------------------- src/tree/mod.rs | 2 +- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/operator/mod.rs b/src/operator/mod.rs index e367546..e0ff92d 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -23,12 +23,12 @@ pub trait Operator: Debug + Display { /// True if this operator is a leaf, meaning it accepts no arguments. // Make this a const fn once #57563 is resolved 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 - fn max_argument_amount(&self) -> usize; + fn max_argument_amount(&self) -> Option; /// Evaluates the operator with the given arguments and context. fn eval(&self, arguments: &[Value], context: &dyn Context) -> EvalexprResult; @@ -127,8 +127,8 @@ impl Operator for RootNode { 200 } - fn max_argument_amount(&self) -> usize { - 1 + fn max_argument_amount(&self) -> Option { + Some(1) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -145,8 +145,8 @@ impl Operator for Add { 95 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -182,8 +182,8 @@ impl Operator for Sub { 95 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -214,8 +214,8 @@ impl Operator for Neg { 110 } - fn max_argument_amount(&self) -> usize { - 1 + fn max_argument_amount(&self) -> Option { + Some(1) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -240,8 +240,8 @@ impl Operator for Mul { 100 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -272,8 +272,8 @@ impl Operator for Div { 100 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -304,8 +304,8 @@ impl Operator for Mod { 100 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -336,8 +336,8 @@ impl Operator for Exp { 120 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -359,8 +359,8 @@ impl Operator for Eq { 80 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -379,8 +379,8 @@ impl Operator for Neq { 80 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -399,8 +399,8 @@ impl Operator for Gt { 80 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -435,8 +435,8 @@ impl Operator for Lt { 80 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -471,8 +471,8 @@ impl Operator for Geq { 80 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -507,8 +507,8 @@ impl Operator for Leq { 80 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -543,8 +543,8 @@ impl Operator for And { 75 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -565,8 +565,8 @@ impl Operator for Or { 70 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -587,8 +587,8 @@ impl Operator for Not { 110 } - fn max_argument_amount(&self) -> usize { - 1 + fn max_argument_amount(&self) -> Option { + Some(1) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -608,8 +608,8 @@ impl Operator for Tuple { 40 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -646,8 +646,8 @@ impl Operator for Assign { false } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, _arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -668,8 +668,8 @@ impl Operator for Chain { 0 } - fn max_argument_amount(&self) -> usize { - 2 + fn max_argument_amount(&self) -> Option { + Some(2) } fn eval(&self, arguments: &[Value], _context: &Context) -> Result { @@ -686,8 +686,8 @@ impl Operator for Const { 200 } - fn max_argument_amount(&self) -> usize { - 0 + fn max_argument_amount(&self) -> Option { + Some(0) } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { @@ -702,8 +702,8 @@ impl Operator for VariableIdentifier { 200 } - fn max_argument_amount(&self) -> usize { - 0 + fn max_argument_amount(&self) -> Option { + Some(0) } fn eval(&self, _arguments: &[Value], context: &Context) -> EvalexprResult { @@ -730,8 +730,8 @@ impl Operator for FunctionIdentifier { false } - fn max_argument_amount(&self) -> usize { - 1 + fn max_argument_amount(&self) -> Option { + Some(1) } fn eval(&self, arguments: &[Value], context: &Context) -> EvalexprResult { diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 775fcb6..c6d2956 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -316,7 +316,7 @@ 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<()> { From eb5c6e99bd1331a495475aa6d7ea931861248499 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 13 Apr 2019 20:04:36 +0200 Subject: [PATCH 2/9] Add flattening of operator chains when building operator tree Relates to #44 --- src/operator/mod.rs | 119 +++++++++++++++++++++++++++++++++++++++++-- src/tree/mod.rs | 3 ++ src/value/mod.rs | 4 ++ tests/integration.rs | 14 +++++ 4 files changed, 136 insertions(+), 4 deletions(-) diff --git a/src/operator/mod.rs b/src/operator/mod.rs index e0ff92d..84ca661 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -7,6 +7,10 @@ use crate::{context::Context, error::*, value::Value}; mod display; pub trait Operator: Debug + Display { + /// A numeric id to compare operator types. + // Make this a const fn once #57563 is resolved + fn id(&self) -> i32; + /// Returns the precedence of the operator. /// A high precedence means that the operator has priority to be deeper in the tree. // Make this a const fn once #57563 is resolved @@ -20,6 +24,12 @@ pub trait Operator: Debug + Display { true } + /// 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 + fn is_flatten_chains(&self) -> bool { + false + } + /// True if this operator is a leaf, meaning it accepts no arguments. // Make this a const fn once #57563 is resolved fn is_leaf(&self) -> bool { @@ -123,6 +133,10 @@ impl FunctionIdentifier { } impl Operator for RootNode { + fn id(&self) -> i32 { + 1 + } + fn precedence(&self) -> i32 { 200 } @@ -141,6 +155,10 @@ impl Operator for RootNode { } impl Operator for Add { + fn id(&self) -> i32 { + 2 + } + fn precedence(&self) -> i32 { 95 } @@ -178,6 +196,10 @@ impl Operator for Add { } impl Operator for Sub { + fn id(&self) -> i32 { + 3 + } + fn precedence(&self) -> i32 { 95 } @@ -210,6 +232,10 @@ impl Operator for Sub { } impl Operator for Neg { + fn id(&self) -> i32 { + 4 + } + fn precedence(&self) -> i32 { 110 } @@ -236,6 +262,10 @@ impl Operator for Neg { } impl Operator for Mul { + fn id(&self) -> i32 { + 5 + } + fn precedence(&self) -> i32 { 100 } @@ -268,6 +298,10 @@ impl Operator for Mul { } impl Operator for Div { + fn id(&self) -> i32 { + 6 + } + fn precedence(&self) -> i32 { 100 } @@ -300,6 +334,10 @@ impl Operator for Div { } impl Operator for Mod { + fn id(&self) -> i32 { + 7 + } + fn precedence(&self) -> i32 { 100 } @@ -332,6 +370,10 @@ impl Operator for Mod { } impl Operator for Exp { + fn id(&self) -> i32 { + 8 + } + fn precedence(&self) -> i32 { 120 } @@ -355,6 +397,10 @@ impl Operator for Exp { } impl Operator for Eq { + fn id(&self) -> i32 { + 9 + } + fn precedence(&self) -> i32 { 80 } @@ -375,6 +421,10 @@ impl Operator for Eq { } impl Operator for Neq { + fn id(&self) -> i32 { + 10 + } + fn precedence(&self) -> i32 { 80 } @@ -395,6 +445,10 @@ impl Operator for Neq { } impl Operator for Gt { + fn id(&self) -> i32 { + 11 + } + fn precedence(&self) -> i32 { 80 } @@ -431,6 +485,10 @@ impl Operator for Gt { } impl Operator for Lt { + fn id(&self) -> i32 { + 12 + } + fn precedence(&self) -> i32 { 80 } @@ -467,6 +525,10 @@ impl Operator for Lt { } impl Operator for Geq { + fn id(&self) -> i32 { + 13 + } + fn precedence(&self) -> i32 { 80 } @@ -503,6 +565,10 @@ impl Operator for Geq { } impl Operator for Leq { + fn id(&self) -> i32 { + 14 + } + fn precedence(&self) -> i32 { 80 } @@ -539,6 +605,10 @@ impl Operator for Leq { } impl Operator for And { + fn id(&self) -> i32 { + 15 + } + fn precedence(&self) -> i32 { 75 } @@ -561,6 +631,10 @@ impl Operator for And { } impl Operator for Or { + fn id(&self) -> i32 { + 16 + } + fn precedence(&self) -> i32 { 70 } @@ -583,6 +657,10 @@ impl Operator for Or { } impl Operator for Not { + fn id(&self) -> i32 { + 17 + } + fn precedence(&self) -> i32 { 110 } @@ -604,17 +682,26 @@ impl Operator for Not { } impl Operator for Tuple { + fn id(&self) -> i32 { + 18 + } + fn precedence(&self) -> i32 { 40 } fn max_argument_amount(&self) -> Option { - Some(2) + None } fn eval(&self, arguments: &[Value], _context: &Context) -> EvalexprResult { - expect_operator_argument_amount(arguments.len(), 2)?; - if let Value::Tuple(tuple) = &arguments[0] { + if arguments.len() < 2 { + return Err(EvalexprError::wrong_operator_argument_amount(arguments.len(), 2)); + } + + Ok(Value::Tuple(arguments.iter().cloned().collect())) + + /*if let Value::Tuple(tuple) = &arguments[0] { let mut tuple = tuple.clone(); if let Value::Tuple(tuple2) = &arguments[1] { tuple.extend(tuple2.iter().cloned()); @@ -633,11 +720,15 @@ impl Operator for Tuple { arguments[1].clone(), ])) } - } + }*/ } } impl Operator for Assign { + fn id(&self) -> i32 { + 19 + } + fn precedence(&self) -> i32 { 50 } @@ -664,10 +755,18 @@ impl Operator for Assign { } impl Operator for Chain { + fn id(&self) -> i32 { + 20 + } + fn precedence(&self) -> i32 { 0 } + fn is_flatten_chains(&self) -> bool { + true + } + fn max_argument_amount(&self) -> Option { Some(2) } @@ -682,6 +781,10 @@ impl Operator for Chain { } impl Operator for Const { + fn id(&self) -> i32 { + 21 + } + fn precedence(&self) -> i32 { 200 } @@ -698,6 +801,10 @@ impl Operator for Const { } impl Operator for VariableIdentifier { + fn id(&self) -> i32 { + 22 + } + fn precedence(&self) -> i32 { 200 } @@ -722,6 +829,10 @@ impl Operator for VariableIdentifier { } impl Operator for FunctionIdentifier { + fn id(&self) -> i32 { + 23 + } + fn precedence(&self) -> i32 { 190 } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index c6d2956..6aff4ed 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -337,6 +337,9 @@ impl Node { .last_mut() .unwrap() .insert_back_prioritized(node, false) + } else if self.children.last().unwrap().operator().id() == node.operator().id() && node.operator().is_flatten_chains() && !self.children.last().unwrap().has_enough_children() { + // The operators will be chained together, and the next value will be added to this nodes last child. + Ok(()) } else { if node.operator().is_leaf() { return Err(EvalexprError::AppendedToLeafNode); diff --git a/src/value/mod.rs b/src/value/mod.rs index b2e8cee..17aafe2 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -194,6 +194,10 @@ 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 2c90c9f..5fbd5a2 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -614,3 +614,17 @@ 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)])); +} From 2af40309f6b1431d3f687ae35b23e9bd928bf353 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 13 Apr 2019 20:31:47 +0200 Subject: [PATCH 3/9] Replace manual `type_id` with builtin function of trait `Any` Relates to #44 --- src/operator/mod.rs | 96 --------------------------------------------- src/tree/mod.rs | 4 +- 2 files changed, 3 insertions(+), 97 deletions(-) diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 84ca661..59dd3e6 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -7,10 +7,6 @@ use crate::{context::Context, error::*, value::Value}; mod display; pub trait Operator: Debug + Display { - /// A numeric id to compare operator types. - // Make this a const fn once #57563 is resolved - fn id(&self) -> i32; - /// Returns the precedence of the operator. /// A high precedence means that the operator has priority to be deeper in the tree. // Make this a const fn once #57563 is resolved @@ -133,10 +129,6 @@ impl FunctionIdentifier { } impl Operator for RootNode { - fn id(&self) -> i32 { - 1 - } - fn precedence(&self) -> i32 { 200 } @@ -155,10 +147,6 @@ impl Operator for RootNode { } impl Operator for Add { - fn id(&self) -> i32 { - 2 - } - fn precedence(&self) -> i32 { 95 } @@ -196,10 +184,6 @@ impl Operator for Add { } impl Operator for Sub { - fn id(&self) -> i32 { - 3 - } - fn precedence(&self) -> i32 { 95 } @@ -232,10 +216,6 @@ impl Operator for Sub { } impl Operator for Neg { - fn id(&self) -> i32 { - 4 - } - fn precedence(&self) -> i32 { 110 } @@ -262,10 +242,6 @@ impl Operator for Neg { } impl Operator for Mul { - fn id(&self) -> i32 { - 5 - } - fn precedence(&self) -> i32 { 100 } @@ -298,10 +274,6 @@ impl Operator for Mul { } impl Operator for Div { - fn id(&self) -> i32 { - 6 - } - fn precedence(&self) -> i32 { 100 } @@ -334,10 +306,6 @@ impl Operator for Div { } impl Operator for Mod { - fn id(&self) -> i32 { - 7 - } - fn precedence(&self) -> i32 { 100 } @@ -370,10 +338,6 @@ impl Operator for Mod { } impl Operator for Exp { - fn id(&self) -> i32 { - 8 - } - fn precedence(&self) -> i32 { 120 } @@ -397,10 +361,6 @@ impl Operator for Exp { } impl Operator for Eq { - fn id(&self) -> i32 { - 9 - } - fn precedence(&self) -> i32 { 80 } @@ -421,10 +381,6 @@ impl Operator for Eq { } impl Operator for Neq { - fn id(&self) -> i32 { - 10 - } - fn precedence(&self) -> i32 { 80 } @@ -445,10 +401,6 @@ impl Operator for Neq { } impl Operator for Gt { - fn id(&self) -> i32 { - 11 - } - fn precedence(&self) -> i32 { 80 } @@ -485,10 +437,6 @@ impl Operator for Gt { } impl Operator for Lt { - fn id(&self) -> i32 { - 12 - } - fn precedence(&self) -> i32 { 80 } @@ -525,10 +473,6 @@ impl Operator for Lt { } impl Operator for Geq { - fn id(&self) -> i32 { - 13 - } - fn precedence(&self) -> i32 { 80 } @@ -565,10 +509,6 @@ impl Operator for Geq { } impl Operator for Leq { - fn id(&self) -> i32 { - 14 - } - fn precedence(&self) -> i32 { 80 } @@ -605,10 +545,6 @@ impl Operator for Leq { } impl Operator for And { - fn id(&self) -> i32 { - 15 - } - fn precedence(&self) -> i32 { 75 } @@ -631,10 +567,6 @@ impl Operator for And { } impl Operator for Or { - fn id(&self) -> i32 { - 16 - } - fn precedence(&self) -> i32 { 70 } @@ -657,10 +589,6 @@ impl Operator for Or { } impl Operator for Not { - fn id(&self) -> i32 { - 17 - } - fn precedence(&self) -> i32 { 110 } @@ -682,10 +610,6 @@ impl Operator for Not { } impl Operator for Tuple { - fn id(&self) -> i32 { - 18 - } - fn precedence(&self) -> i32 { 40 } @@ -725,10 +649,6 @@ impl Operator for Tuple { } impl Operator for Assign { - fn id(&self) -> i32 { - 19 - } - fn precedence(&self) -> i32 { 50 } @@ -755,10 +675,6 @@ impl Operator for Assign { } impl Operator for Chain { - fn id(&self) -> i32 { - 20 - } - fn precedence(&self) -> i32 { 0 } @@ -781,10 +697,6 @@ impl Operator for Chain { } impl Operator for Const { - fn id(&self) -> i32 { - 21 - } - fn precedence(&self) -> i32 { 200 } @@ -801,10 +713,6 @@ impl Operator for Const { } impl Operator for VariableIdentifier { - fn id(&self) -> i32 { - 22 - } - fn precedence(&self) -> i32 { 200 } @@ -829,10 +737,6 @@ impl Operator for VariableIdentifier { } impl Operator for FunctionIdentifier { - fn id(&self) -> i32 { - 23 - } - fn precedence(&self) -> i32 { 190 } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 6aff4ed..88136de 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -11,6 +11,8 @@ use crate::{ operator::*, value::Value, }; +use std::error::Error; +use std::any::Any; mod display; mod iter; @@ -337,7 +339,7 @@ impl Node { .last_mut() .unwrap() .insert_back_prioritized(node, false) - } else if self.children.last().unwrap().operator().id() == node.operator().id() && node.operator().is_flatten_chains() && !self.children.last().unwrap().has_enough_children() { + } else if self.children.last().unwrap().operator().type_id() == node.operator().type_id() && node.operator().is_flatten_chains() && !self.children.last().unwrap().has_enough_children() { // The operators will be chained together, and the next value will be added to this nodes last child. Ok(()) } else { From 531b7b72a0b50e96a65b4b9206b4f239f4339647 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 13 Apr 2019 19:11:09 +0200 Subject: [PATCH 4/9] Make `Operator::max_argument_amount` optional Relates to #44 --- src/operator/mod.rs | 14 +++++++------- src/tree/mod.rs | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/operator/mod.rs b/src/operator/mod.rs index a86eb53..a94dc9c 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -94,20 +94,20 @@ impl Operator { /// 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, + | Tuple | Assign | Chain => Some(2), + Not | Neg | RootNode => Some(1), + Const { value: _ } => Some(0), + VariableIdentifier { identifier: _ } => Some(0), + FunctionIdentifier { identifier: _ } => Some(1), } } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 0d728d2..7e9f8b8 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -362,7 +362,7 @@ 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<()> { From d576cec9b9472d89ff31731ecb248d67f9a886eb Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 13 Apr 2019 20:04:36 +0200 Subject: [PATCH 5/9] Add flattening of operator chains when building operator tree Relates to #44 --- src/operator/mod.rs | 9 +++++++++ src/tree/mod.rs | 3 +++ src/value/mod.rs | 4 ++++ tests/integration.rs | 14 ++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/src/operator/mod.rs b/src/operator/mod.rs index a94dc9c..1be227d 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -91,6 +91,15 @@ 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 + fn is_flatten_chains(&self) -> bool { + match self { + Operator::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 { diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 7e9f8b8..e9f50d8 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -383,6 +383,9 @@ impl Node { .last_mut() .unwrap() .insert_back_prioritized(node, false) + } else if self.children.last().unwrap().operator().id() == node.operator().id() && node.operator().is_flatten_chains() && !self.children.last().unwrap().has_enough_children() { + // The operators will be chained together, and the next value will be added to this nodes last child. + Ok(()) } else { if node.operator().is_leaf() { return Err(EvalexprError::AppendedToLeafNode); diff --git a/src/value/mod.rs b/src/value/mod.rs index b2e8cee..17aafe2 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -194,6 +194,10 @@ 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 4aedc2a..35428a2 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -605,3 +605,17 @@ 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)])); +} From 2e929ae0fe5dcdc257bdc45554f7e3f017943038 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 13 Apr 2019 20:31:47 +0200 Subject: [PATCH 6/9] Replace manual `type_id` with builtin function of trait `Any` Relates to #44 --- src/operator/mod.rs | 5 +++-- src/tree/mod.rs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 1be227d..a131b55 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -95,7 +95,7 @@ impl Operator { // Make this a const fn once #57563 is resolved fn is_flatten_chains(&self) -> bool { match self { - Operator::Chain => true, + Operator::Tuple => true, _ => false, } } @@ -112,7 +112,8 @@ impl Operator { use crate::operator::Operator::*; match self { Add | Sub | Mul | Div | Mod | Exp | Eq | Neq | Gt | Lt | Geq | Leq | And | Or - | Tuple | Assign | Chain => Some(2), + | Assign | Chain => Some(2), + Tuple => None, Not | Neg | RootNode => Some(1), Const { value: _ } => Some(0), VariableIdentifier { identifier: _ } => Some(0), diff --git a/src/tree/mod.rs b/src/tree/mod.rs index e9f50d8..b763e9b 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -11,6 +11,8 @@ use crate::{ operator::*, value::Value, }; +use std::error::Error; +use std::any::Any; mod display; mod iter; @@ -383,7 +385,7 @@ impl Node { .last_mut() .unwrap() .insert_back_prioritized(node, false) - } else if self.children.last().unwrap().operator().id() == node.operator().id() && node.operator().is_flatten_chains() && !self.children.last().unwrap().has_enough_children() { + } else if self.children.last().unwrap().operator().type_id() == node.operator().type_id() && node.operator().is_flatten_chains() && !self.children.last().unwrap().has_enough_children() { // The operators will be chained together, and the next value will be added to this nodes last child. Ok(()) } else { From 6f77471354969528cacdc8c3c4a7a6795664f2d5 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 22 Apr 2019 19:08:55 +0200 Subject: [PATCH 7/9] Fixed aggregation operator Manage sequence operators, which currently are `Chain` and `Token` on the stack without ever inserting unfinished sequence operator nodes into another node. Relates to #44 --- src/context/mod.rs | 2 +- src/operator/mod.rs | 19 +++--- src/tree/mod.rs | 140 ++++++++++++++++++++++++++++++++++++++----- src/value/mod.rs | 4 +- tests/integration.rs | 53 +++++++++++++--- 5 files changed, 185 insertions(+), 33 deletions(-) diff --git a/src/context/mod.rs b/src/context/mod.rs index 9988ebd..e07f9c7 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(None, Box::new(|_| Ok(42.into()) )) /// }.unwrap(); /// /// assert_eq!(eval_with_context("x + f()", &ctx), Ok(50.into())); diff --git a/src/operator/mod.rs b/src/operator/mod.rs index a131b55..8725b5d 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, @@ -93,9 +93,10 @@ 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 - fn is_flatten_chains(&self) -> bool { + pub(crate) fn is_sequence(&self) -> bool { + use crate::operator::Operator::*; match self { - Operator::Tuple => true, + Tuple | Chain => true, _ => false, } } @@ -112,8 +113,8 @@ impl Operator { use crate::operator::Operator::*; match self { Add | Sub | Mul | Div | Mod | Exp | Eq | Neq | Gt | Lt | Geq | Leq | And | Or - | Assign | Chain => Some(2), - Tuple => None, + | Assign => Some(2), + Tuple | Chain => None, Not | Neg | RootNode => Some(1), Const { value: _ } => Some(0), VariableIdentifier { identifier: _ } => Some(0), @@ -420,7 +421,9 @@ impl Operator { } } Tuple => { - expect_operator_argument_amount(arguments.len(), 2)?; + Ok(Value::Tuple(arguments.into())) + + /*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] { @@ -440,7 +443,7 @@ impl Operator { arguments[1].clone(), ])) } - } + }*/ } Assign => Err(EvalexprError::ContextNotManipulable), Chain => { @@ -448,7 +451,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)?; diff --git a/src/tree/mod.rs b/src/tree/mod.rs index b763e9b..2904440 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -11,8 +11,7 @@ use crate::{ operator::*, value::Value, }; -use std::error::Error; -use std::any::Any; +use std::mem; mod display; mod iter; @@ -34,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 { @@ -368,6 +367,7 @@ 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 // Right-to-left chaining || (self.operator().precedence() == node.operator().precedence() && !self.operator().is_left_to_right() && !node.operator().is_left_to_right()) @@ -381,14 +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 if self.children.last().unwrap().operator().type_id() == node.operator().type_id() && node.operator().is_flatten_chains() && !self.children.last().unwrap().has_enough_children() { - // The operators will be chained together, and the next value will be added to this nodes last child. - Ok(()) } else { + println!("Rotating"); if node.operator().is_leaf() { return Err(EvalexprError::AppendedToLeafNode); } @@ -401,6 +400,7 @@ impl Node { Ok(()) } } else { + println!("Inserting as specified"); self.children.push(node); Ok(()) } @@ -410,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(); @@ -443,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() } } @@ -475,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); } @@ -486,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 17aafe2..e12bef9 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -195,7 +195,9 @@ impl From for EvalexprResult { } impl From<()> for Value { - fn from(_: ()) -> Self { Value::Empty } + fn from(_: ()) -> Self { + Value::Empty + } } #[cfg(test)] diff --git a/tests/integration.rs b/tests/integration.rs index 35428a2..02bc439 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -251,6 +251,7 @@ fn test_n_ary_functions() { context .set_value("five".to_string(), Value::Int(5)) .unwrap(); + context.set_function("function_four".into(), Function::new(Some(0), Box::new(|_| {Ok(Value::Int(4))}))).unwrap(); assert_eq!(eval_with_context("avg(7, 5)", &context), Ok(Value::Int(6))); assert_eq!( @@ -265,16 +266,20 @@ fn test_n_ary_functions() { eval_with_context("sub2 avg(3, 6)", &context), Ok(Value::Int(2)) ); + dbg!(build_operator_tree("muladd(3, 6, -4)").unwrap()); assert_eq!( eval_with_context("muladd(3, 6, -4)", &context), 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(1))); 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))); + assert_eq!(eval_with_context("function_four(())", &context), Err(EvalexprError::WrongFunctionArgumentAmount{expected: 0, actual: 1})); } #[test] @@ -610,12 +615,46 @@ fn test_serde() { 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("(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)])); + 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)]) + ); } From 2ca7209d22e8b6f3c84edbe41470c545c4d2835c Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Sat, 4 May 2019 13:43:05 +0200 Subject: [PATCH 8/9] Remove debug output --- src/tree/mod.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 2904440..b081b94 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -367,7 +367,7 @@ impl Node { } fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> EvalexprResult<()> { - println!("Inserting {:?} into {:?}", node.operator, self.operator()); + // 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()) @@ -381,13 +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()); + // println!("Recursing into {:?}", self.children.last().unwrap().operator()); self.children .last_mut() .unwrap() .insert_back_prioritized(node, false) } else { - println!("Rotating"); + // println!("Rotating"); if node.operator().is_leaf() { return Err(EvalexprError::AppendedToLeafNode); } @@ -400,7 +400,7 @@ impl Node { Ok(()) } } else { - println!("Inserting as specified"); + // println!("Inserting as specified"); self.children.push(node); Ok(()) } @@ -431,8 +431,8 @@ fn collapse_root_stack_to(root_stack: &mut Vec, mut root: Node, collapse_g } fn collapse_all_sequences(root_stack: &mut Vec) -> EvalexprResult<()> { - println!("Collapsing all sequences"); - println!("Initial root stack is: {:?}", root_stack); + // println!("Collapsing all sequences"); + // println!("Initial root stack is: {:?}", root_stack); let mut root = if let Some(root) = root_stack.pop() { root } else { @@ -440,7 +440,7 @@ fn collapse_all_sequences(root_stack: &mut Vec) -> EvalexprResult<()> { }; loop { - println!("Root is: {:?}", root); + // println!("Root is: {:?}", root); if root.operator() == &Operator::RootNode { root_stack.push(root); break; @@ -461,7 +461,7 @@ fn collapse_all_sequences(root_stack: &mut Vec) -> EvalexprResult<()> { } } - println!("Root stack after collapsing all sequences is: {:?}", root_stack); + // println!("Root stack after collapsing all sequences is: {:?}", root_stack); Ok(()) } @@ -535,8 +535,8 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec) -> EvalexprResult) -> EvalexprResult Date: Sat, 4 May 2019 13:43:29 +0200 Subject: [PATCH 9/9] Remove automatic function argument decomposition Relates to #52 --- src/context/mod.rs | 2 +- src/error/mod.rs | 8 ++++++++ src/function/builtin.rs | 38 +++++++++++++++++----------------- src/function/mod.rs | 29 +++++++++----------------- src/lib.rs | 15 +++++++------- src/operator/mod.rs | 7 +------ tests/integration.rs | 45 +++++++++++++++++------------------------ 7 files changed, 63 insertions(+), 81 deletions(-) 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 957e42a..bdbb1ac 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 mut context = HashMapContext::new(); //! context.set_value("five".into(), 5.into()).unwrap(); // Do proper error handling here //! context.set_value("twelve".into(), 12.into()).unwrap(); // Do proper error handling here -//! context.set_function("f".into(), Function::new(Some(1) /* argument amount */, Box::new(|arguments| { -//! if let Value::Int(int) = arguments[0] { +//! context.set_function("f".into(), 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())) //! } //! }))).unwrap(); // Do proper error handling here -//! context.set_function("avg".into(), Function::new(Some(2) /* argument amount */, Box::new(|arguments| { +//! context.set_function("avg".into(), Function::new(Box::new(|argument| { +//! let arguments = expect_tuple(argument)?; //! expect_number(&arguments[0])?; //! expect_number(&arguments[1])?; //! @@ -339,7 +340,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 8725b5d..007fdad 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -469,12 +469,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/tests/integration.rs b/tests/integration.rs index 02bc439..93bae42 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,7 +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(Some(0), Box::new(|_| {Ok(Value::Int(4))}))).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!( @@ -266,20 +259,18 @@ fn test_n_ary_functions() { eval_with_context("sub2 avg(3, 6)", &context), Ok(Value::Int(2)) ); - dbg!(build_operator_tree("muladd(3, 6, -4)").unwrap()); assert_eq!( eval_with_context("muladd(3, 6, -4)", &context), 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(1))); + 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))); - assert_eq!(eval_with_context("function_four(())", &context), Err(EvalexprError::WrongFunctionArgumentAmount{expected: 0, actual: 1})); } #[test]