From b17b0aa96e1b041d07d2893820842a50c70da68d Mon Sep 17 00:00:00 2001 From: dzhibas Date: Fri, 16 Feb 2024 22:01:16 +0100 Subject: [PATCH] in array and variable==string comparison --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/ast.rs | 20 +++++++++++-- src/eval.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 98 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7533e9a..d97feee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,7 +25,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bool_expr_parser_nom" -version = "0.2.4" +version = "0.2.5" dependencies = [ "chrono", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index e5610a7..6916e3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bool_expr_parser_nom" -version = "0.2.4" +version = "0.2.5" edition = "2021" authors = ["Nikolajus Krauklis "] diff --git a/src/ast.rs b/src/ast.rs index 20bf6a2..9f46e73 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -2,8 +2,7 @@ use core::fmt; use chrono::NaiveDate; -/// TODO: add date and datetime as its common -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum Atom { String(String), Number(i32), @@ -14,6 +13,23 @@ pub enum Atom { DateTime(String), } +impl PartialEq for Atom { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Atom::String(s1), Atom::String(s2)) => s1 == s2, + (Atom::Variable(v1), Atom::Variable(v2)) => v1 == v2, + (Atom::String(v1), Atom::Variable(v2)) => v1 == v2, + (Atom::Variable(v1), Atom::String(v2)) => v1 == v2, + (Atom::Number(n1), Atom::Number(n2)) => n1 == n2, + (Atom::Float(f1), Atom::Float(f2)) => f1 == f2, + (Atom::Boolean(b1), Atom::Boolean(b2)) => b1 == b2, + (Atom::Date(d1), Atom::Date(d2)) => d1 == d2, + (Atom::DateTime(t1), Atom::DateTime(t2)) => t1 == t2, + _ => false, + } + } +} + impl PartialOrd for Atom { fn partial_cmp(&self, other: &Self) -> Option { match self { diff --git a/src/eval.rs b/src/eval.rs index 8a17ab0..9ddcc00 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -1,10 +1,11 @@ use std::collections::HashMap; -use crate::ast::{AstNode, Atom, ComparisonOp}; +use crate::ast::{ArrayOp, AstNode, Atom, ComparisonOp}; pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result { let mut result = false; result = match expr { + // true || false AstNode::Constant(var) => { let mut result = false; if let Atom::Boolean(v) = var { @@ -18,9 +19,9 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result { - // check var in context - // compare with val let context_val = context.get(var.as_str().unwrap()); let val_content = match val.as_ref() { AstNode::Constant(a) => Some(a), @@ -41,6 +42,39 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result { + 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 = context.get(&var.as_str()); + if let Some(search_value) = var_value { + match op { + ArrayOp::In => { + // check if this value is in the list + for i in vec_list.iter() { + if search_value == i { + result = true; + break; + } + } + } + ArrayOp::NotIn => { + // a not in (c,d) + let mut found = false; + for i in vec_list.iter() { + if search_value == i { + found = true; + } + } + result = !found; + } + } + } + } + } + result + } _ => false, }; Ok(result) @@ -51,6 +85,47 @@ mod tests { use super::*; + #[test] + fn array_eval_test() { + let context = HashMap::from([ + ("x", Atom::Number(10)), + ("y", Atom::String("tree".to_string())), + ]); + let (i, expr) = parse("y in ('one', 'two', 'tree')").unwrap(); + let res = eval(&expr, &context).unwrap(); + assert_eq!(res, true); + + assert_eq!( + false, + eval( + &expr, + &HashMap::from([("y", Atom::String("four".to_string())),]) + ) + .unwrap() + ); + + assert_eq!( + true, + eval( + &parse("y not in ('one','two','tree')").unwrap().1, + &HashMap::from([("y", Atom::String("four".to_string())),]) + ) + .unwrap() + ); + } + + #[test] + fn compare_variable_with_string_in_array_test() { + assert_eq!( + true, + eval( + &parse("y in (one,two,tree)").unwrap().1, + &HashMap::from([("y", Atom::String("two".to_string())),]) + ) + .unwrap() + ); + } + #[test] fn test_comparison_expr_eval() { let context = HashMap::from([