Skip to content

Commit

Permalink
eval finished with function calls
Browse files Browse the repository at this point in the history
  • Loading branch information
dzhibas committed Feb 17, 2024
1 parent 531f7d6 commit 07e28a3
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 64 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bool_expr_parser_nom"
version = "0.2.8"
version = "0.3.0"
edition = "2021"
authors = ["Nikolajus Krauklis <[email protected]>"]

Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
108 changes: 47 additions & 61 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Atom> {
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<bool, &'a str> {
let mut inner_context = context.clone();
let mut result = false;
result = match expr {
// true || false
Expand All @@ -13,24 +35,24 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result<bool, &
result = *v;
}
if let Atom::Variable(v) = var {
let context_val = inner_context.get(v.as_str());
let context_val = get_variable_value_from_context(expr, context);
if let Some(Atom::Boolean(inner)) = context_val {
result = *inner;
result = inner;
}
}
result
}
// a == 3
// a < 3
AstNode::Compare(var, op, val) => {
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,
Expand All @@ -44,12 +66,12 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result<bool, &
}
}
// x in (1, 2, 3)
AstNode::Array(var, op, list) => {
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
Expand Down Expand Up @@ -77,51 +99,15 @@ pub fn eval<'a>(expr: &AstNode, context: &HashMap<&str, Atom>) -> Result<bool, &
result
}
AstNode::Logic(expr1, op, expr2) => {
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,
Expand Down Expand Up @@ -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]
Expand All @@ -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(
Expand Down

0 comments on commit 07e28a3

Please sign in to comment.