Skip to content

Commit

Permalink
proper treatment for arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
dzhibas committed Feb 9, 2024
1 parent 1e2833b commit e16f590
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 15 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.1.9"
version = "0.1.10"
edition = "2021"

[lints.rust]
Expand Down
10 changes: 7 additions & 3 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub enum ComparisonOp {
MoreEq,
LessEq,
NotEq,
In,
}

impl ComparisonOp {
Expand All @@ -42,7 +41,6 @@ impl ComparisonOp {
"<" => ComparisonOp::Less,
"<=" => ComparisonOp::LessEq,
"!=" | "<>" => ComparisonOp::NotEq,
"in" => ComparisonOp::In,
_ => unreachable!(),
}
}
Expand All @@ -56,7 +54,6 @@ impl fmt::Display for ComparisonOp {
ComparisonOp::MoreEq => write!(f, ">="),
ComparisonOp::LessEq => write!(f, "<="),
ComparisonOp::NotEq => write!(f, "<>"),
ComparisonOp::In => write!(f, "in"),
}
}
}
Expand All @@ -77,11 +74,18 @@ impl LogicOp {
}
}

#[derive(Debug, Clone)]
pub enum ArrayOp {
In,
NotIn,
}

#[derive(Debug)]
pub enum AstNode {
Variable(Atom),
Constant(Atom),
List(Vec<Atom>),
Compare(Box<AstNode>, ComparisonOp, Box<AstNode>),
Array(Box<AstNode>, ArrayOp, Box<AstNode>),
Logic(Box<AstNode>, LogicOp, Box<AstNode>),
}
57 changes: 47 additions & 10 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ use nom::{
error::ParseError,
multi::{many0, many0_count, separated_list0},
number::complete::double,
sequence::{delimited, pair, tuple},
sequence::{delimited, pair, preceded, tuple},
IResult,
};

use crate::ast::{AstNode, Atom, ComparisonOp, LogicOp};
use crate::ast::{ArrayOp, AstNode, Atom, ComparisonOp, LogicOp};

/// Took from nom recipes
fn ws<'a, F: 'a, O, E: ParseError<&'a str>>(
Expand Down Expand Up @@ -86,7 +86,6 @@ fn parse_comparison_op(i: &str) -> IResult<&str, ComparisonOp> {
map(tag("<"), |_| ComparisonOp::Less),
map(tag(">="), |_| ComparisonOp::MoreEq),
map(tag(">"), |_| ComparisonOp::More),
map(tag("in"), |_| ComparisonOp::In),
))(i)
}

Expand All @@ -113,24 +112,50 @@ fn parse_constant(i: &str) -> IResult<&str, AstNode> {
map(parse_atom, AstNode::Constant)(i)
}

fn parse_array_op(i: &str) -> IResult<&str, ArrayOp> {
alt((
map(tag_no_case("not in"), |_| ArrayOp::NotIn),
map(tag_no_case("in"), |_| ArrayOp::In),
))(i)
}

fn parse_array_expr(i: &str) -> IResult<&str, AstNode> {
let parser = tuple((parse_variable_node, ws(parse_array_op), parse_list));
map(parser, |(var, op, val)| {
AstNode::Array(Box::new(var), op, Box::new(val))
})(i)
}

fn parse_compare_expr(i: &str) -> IResult<&str, AstNode> {
let parser = tuple((
parse_variable_node,
ws(parse_comparison_op),
alt((parse_constant, parse_list)),
));
let parser = tuple((parse_variable_node, ws(parse_comparison_op), parse_constant));
map(parser, |(var, op, val)| {
AstNode::Compare(Box::new(var), op, Box::new(val))
})(i)
}

fn parse_compare_or_array_expr(i: &str) -> IResult<&str, AstNode> {
alt((parse_array_expr, parse_compare_expr))(i)
}

fn parse_logic_expr(i: &str) -> IResult<&str, AstNode> {
let parser = tuple((parse_compare_expr, ws(parse_logic_op), parse_compare_expr));
/// a=b AND b not in (1,2,3)
let parser = tuple((
parse_compare_or_array_expr,
ws(parse_logic_op),
parse_compare_or_array_expr,
));
map(parser, |(var, op, val)| {
AstNode::Logic(Box::new(var), op, Box::new(val))
})(i)
}

fn parse(input: &str) -> IResult<&str, Vec<AstNode>> {
alt((
many0(ws(parse_logic_expr)),
map(parse_compare_or_array_expr, |v| vec![v]),
))(input)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -220,9 +245,21 @@ mod tests {
assert_eq!(i, "");
}

#[test]
fn test_more_complext_not_in() {
assert_eq!(
parse_logic_expr("a=3 && c = 3 || d not in in (2,4,5)").is_ok(),
true
);
let (i, v) = parse("a=3 && c = 3 || d not in in (2,4,5)").unwrap();
// dbg!(i, v);
}

#[test]
fn test_list_bug() {
/// this should not be allowed as array should have either in () or not in ()
let a = "a == 2 and b >= (1,2,3)";
let (i, v) = parse_logic_expr(a).unwrap();
let res = parse_logic_expr(a);
assert_eq!(res.is_err(), true);
}
}

0 comments on commit e16f590

Please sign in to comment.