From 07e28a3ae1442bdf78684018f25935a77e86a1fd Mon Sep 17 00:00:00 2001 From: dzhibas Date: Sat, 17 Feb 2024 15:03:30 +0100 Subject: [PATCH] eval finished with function calls --- Cargo.lock | 2 +- Cargo.toml | 2 +- TODO.md | 2 +- src/eval.rs | 108 +++++++++++++++++++++++----------------------------- 4 files changed, 50 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4377bbe..c567db2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,7 +25,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bool_expr_parser_nom" -version = "0.2.8" +version = "0.3.0" dependencies = [ "chrono", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 6478a08..c49272a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bool_expr_parser_nom" -version = "0.2.8" +version = "0.3.0" edition = "2021" authors = ["Nikolajus Krauklis "] diff --git a/TODO.md b/TODO.md index 0a87044..f8d74e3 100644 --- a/TODO.md +++ b/TODO.md @@ -3,4 +3,4 @@ - [+] Scopes and negated scopes. ex.: a=b and !(c=d or g=z) - [+] either lower() or upper() function calls or case insensitive string comparison operators - [+] support for single quote strings -- [ ] evaluation with provided context +- [+] evaluation with provided context diff --git a/src/eval.rs b/src/eval.rs index a552236..e8ec6de 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -2,8 +2,30 @@ use std::collections::HashMap; use crate::ast::{ArrayOp, AstNode, Atom, ComparisonOp, FnCall, LogicOp}; +fn get_variable_value_from_context<'a>( + variable: &'a AstNode, + context: &'a HashMap<&str, Atom>, +) -> Option { + let res = match variable { + AstNode::Variable(Atom::Variable(v)) => context.get(v.as_str()), + AstNode::Constant(Atom::Variable(v)) => context.get(v.as_str()), + AstNode::Function(op, v) => { + let value = get_variable_value_from_context(v, context); + if let Some(v) = value { + let vv = match op { + FnCall::Upper => Atom::String(v.to_string().to_uppercase()), + FnCall::Lower => Atom::String(v.to_string().to_lowercase()), + }; + return Some(vv); + } + None + } + _ => None, + }; + res.cloned() +} + pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result { - let mut inner_context = context.clone(); let mut result = false; result = match expr { // true || false @@ -13,9 +35,9 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result { - let context_val = inner_context.get(var.as_str().unwrap()); + let context_val = get_variable_value_from_context(var, context); let val_content = match val.as_ref() { AstNode::Constant(a) => Some(a), _ => None, } .unwrap(); - if let Some(c_val) = context_val { + if let Some(c_val) = &context_val { match op { ComparisonOp::More => c_val > val_content, ComparisonOp::MoreEq => c_val >= val_content, @@ -44,12 +66,12 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result { + AstNode::Array(var_expr, op, list) => { let mut result = false; if let AstNode::List(vec_list) = list.as_ref() { - if let AstNode::Variable(Atom::Variable(var)) = var.as_ref() { - let var_value = inner_context.get(&var.as_str()); - if let Some(search_value) = var_value { + if let AstNode::Variable(Atom::Variable(var)) = var_expr.as_ref() { + let var_value = get_variable_value_from_context(var_expr, context); + if let Some(search_value) = &var_value { match op { ArrayOp::In => { // check if this value is in the list @@ -77,51 +99,15 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result { - let expr1_eval = eval(expr1, &inner_context).unwrap(); - let expr2_eval = eval(expr2, &inner_context).unwrap(); + let expr1_eval = eval(expr1, context).unwrap(); + let expr2_eval = eval(expr2, context).unwrap(); match op { LogicOp::And => expr1_eval && expr2_eval, LogicOp::Or => expr1_eval || expr2_eval, } } - AstNode::Function(func, variable) => match func { - FnCall::Upper => { - if let AstNode::Variable(Atom::Variable(var)) = variable.as_ref() { - if let Some(v) = inner_context.get_mut(var.as_str()) { - let out = match v { - Atom::String(val) => Atom::String(val.to_uppercase()), - Atom::Number(val) => Atom::Number(*val), - Atom::Float(val) => Atom::Float(*val), - Atom::Boolean(val) => Atom::Boolean(*val), - Atom::Variable(val) => Atom::Variable(val.to_uppercase()), - Atom::Date(val) => Atom::Date(*val), - Atom::DateTime(val) => Atom::DateTime(val.to_string()), - }; - *v = out; - } - } - false - } - FnCall::Lower => { - if let AstNode::Variable(Atom::Variable(var)) = variable.as_ref() { - if let Some(v) = inner_context.get_mut(var.as_str()) { - let out = match v { - Atom::String(val) => Atom::String(val.to_lowercase()), - Atom::Number(val) => Atom::Number(*val), - Atom::Float(val) => Atom::Float(*val), - Atom::Boolean(val) => Atom::Boolean(*val), - Atom::Variable(val) => Atom::Variable(val.to_lowercase()), - Atom::Date(val) => Atom::Date(*val), - Atom::DateTime(val) => Atom::DateTime(val.to_string()), - }; - *v = out; - } - } - false - } - }, AstNode::Scope { expr, negate } => { - let res = eval(expr, &inner_context).unwrap(); + let res = eval(expr, context).unwrap(); match negate { true => !res, false => res, @@ -175,18 +161,18 @@ mod tests { #[test] fn testing_function_calls() { - // let (i, expr) = parse("lower(countryCode)==LT && city='Palanga'").unwrap(); - // assert_eq!( - // true, - // eval( - // &expr, - // &HashMap::from([ - // ("countryCode", Atom::String("LT".to_string())), - // ("city", Atom::String("Palanga".to_string())) - // ]) - // ) - // .unwrap() - // ); + let (i, expr) = parse("lower(countryCode)==lt && upper(city)='PALANGA'").unwrap(); + assert_eq!( + true, + eval( + &expr, + &HashMap::from([ + ("countryCode", Atom::String("LT".to_string())), + ("city", Atom::String("Palanga".to_string())) + ]) + ) + .unwrap() + ); } #[test] @@ -212,7 +198,7 @@ mod tests { .unwrap() ); - let (i, expr) = parse("((country == Netherlands))").unwrap(); + let (i, expr) = parse("((lower(country) == netherlands))").unwrap(); assert_eq!( true, eval(