coolrule/
parser.rs

1use pom::parser::*;
2use std::str::{self, FromStr};
3
4#[derive(Debug)]
5pub enum BinOp {
6    Equal,              // =, ==, eq
7    NotEqual,           // !=, ne, ≠
8    GreaterThan,        // >, gt
9    GreaterThanOrEqual, // >=, ge, ≥
10    LessThan,           // <, lt
11    LessThanOrEqual,    // <=, le, ≤
12    In,                 // in, ∈
13    NotIn,              // notin, ∉
14    Is,                 // is
15    IsNot,              // isnot
16    SubSetOf,           // ⊆
17    SuperSetOf,         // ⊇
18    IntersectionOf,     // ∩
19    NotIntersectionOf,  // not∩
20}
21
22#[derive(Debug, Clone)]
23pub enum SimpleValue {
24    Number(f64),
25    Str(String),
26    Bool(bool),
27    None,
28    // The path to a context value
29    // e.g. `foo.bar` -> [`foo`, `bar`]
30    PropertyPath(Vec<String>),
31}
32
33#[derive(Debug)]
34pub enum PropertyVal {
35    SimpleValue(SimpleValue),
36    Group(Vec<SimpleValue>),
37}
38
39#[derive(Debug)]
40pub enum BooleanCondition {
41    Comparison(PropertyVal, BinOp, PropertyVal),
42    Group(Box<BooleanExpression>),
43}
44
45#[derive(Debug)]
46pub enum AndOr {
47    And,
48    Or,
49}
50
51#[derive(Debug)]
52pub struct BooleanExpression {
53    pub initial: BooleanCondition,
54    pub conditions: Vec<(AndOr, BooleanCondition)>,
55}
56
57fn space<'a>() -> Parser<'a, u8, ()> {
58    one_of(b" \t\r\n").repeat(0..).discard()
59}
60
61fn property_path<'a>() -> Parser<'a, u8, Vec<Vec<u8>>> {
62    let ascii = one_of(b"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
63    list(ascii.repeat(1..), sym(b'.'))
64}
65
66fn lparen<'a>() -> Parser<'a, u8, ()> {
67    seq(b"(").discard()
68}
69
70fn rparen<'a>() -> Parser<'a, u8, ()> {
71    seq(b")").discard()
72}
73
74fn binary_op<'a>() -> Parser<'a, u8, BinOp> {
75    (seq(b"==") | seq(b"=") | seq(b"eq")).map(|_| BinOp::Equal)
76        | (seq(b"!=") | seq(b"ne") | seq("≠".as_bytes())).map(|_| BinOp::NotEqual)
77        | (seq(b">=") | seq(b"ge") | seq("≥".as_bytes())).map(|_| BinOp::GreaterThanOrEqual)
78        | (seq(b">") | seq(b"gt")).map(|_| BinOp::GreaterThan)
79        | (seq(b"<=") | seq(b"le") | seq("≤".as_bytes())).map(|_| BinOp::LessThanOrEqual)
80        | (seq(b"<") | seq(b"lt")).map(|_| BinOp::LessThan)
81        | (seq(b"in") | seq("∈".as_bytes())).map(|_| BinOp::In)
82        | (seq(b"notin") | seq("∉".as_bytes())).map(|_| BinOp::NotIn)
83        | seq(b"isnot").map(|_| BinOp::IsNot)
84        | seq(b"is").map(|_| BinOp::Is)
85        | seq("⊆".as_bytes()).map(|_| BinOp::SubSetOf)
86        | seq("⊇".as_bytes()).map(|_| BinOp::SuperSetOf)
87        | seq("∩".as_bytes()).map(|_| BinOp::IntersectionOf)
88        | seq("not∩".as_bytes()).map(|_| BinOp::NotIntersectionOf)
89}
90
91fn real_number<'a>() -> Parser<'a, u8, f64> {
92    let integer = one_of(b"123456789") - one_of(b"0123456789").repeat(0..) | sym(b'0');
93    let frac = sym(b'.') + one_of(b"0123456789").repeat(1..);
94    let exp = one_of(b"eE") + one_of(b"+-").opt() + one_of(b"0123456789").repeat(1..);
95    let number = sym(b'-').opt() + integer + frac.opt() + exp.opt();
96    number
97        .collect()
98        .convert(str::from_utf8)
99        .convert(|s| f64::from_str(&s))
100}
101
102fn integer<'a>() -> Parser<'a, u8, u8> {
103    one_of(b"123456789") - one_of(b"0123456789").repeat(0..) | sym(b'0')
104}
105
106fn str<'a>() -> Parser<'a, u8, String> {
107    (sym(b'"') * none_of(b"\"").repeat(0..) - sym(b'"')).convert(String::from_utf8)
108}
109
110fn bool<'a>() -> Parser<'a, u8, SimpleValue> {
111    ((seq(b"t") | seq(b"T"))
112        + (seq(b"r") | seq(b"R"))
113        + (seq(b"u") | seq(b"U"))
114        + (seq(b"e") | seq(b"E")))
115    .map(|_| SimpleValue::Bool(true))
116        | ((seq(b"f") | seq(b"F"))
117            + (seq(b"a") | seq(b"A"))
118            + (seq(b"l") | seq(b"L"))
119            + (seq(b"s") | seq(b"S"))
120            + (seq(b"e") | seq(b"E")))
121        .map(|_| SimpleValue::Bool(true))
122}
123
124fn none<'a>() -> Parser<'a, u8, u8> {
125    ((seq(b"n") | seq(b"N"))
126        + (seq(b"o") | seq(b"O"))
127        + (seq(b"n") | seq(b"N"))
128        + (seq(b"e") | seq(b"E")))
129    .map(|_| 0)
130}
131
132fn simple_value<'a>() -> Parser<'a, u8, SimpleValue> {
133    space()
134        * (real_number().map(|f| SimpleValue::Number(f))
135            | integer().map(|i| SimpleValue::Number(i.into()))
136            | str().map(|s| SimpleValue::Str(s))
137            | bool()
138            | none().map(|_| SimpleValue::None)
139            | property_path().map(|p| {
140                SimpleValue::PropertyPath(
141                    p.iter()
142                        .map(|byte_vec| String::from_utf8_lossy(byte_vec).into_owned())
143                        .collect(),
144                )
145            }))
146        - space()
147}
148
149fn property_val<'a>() -> Parser<'a, u8, PropertyVal> {
150    space()
151        * ((lparen() * list(simple_value(), sym(b',') * space()) - rparen())
152            .map(|g| PropertyVal::Group(g))
153            | simple_value().map(|s| PropertyVal::SimpleValue(s)))
154        - space()
155}
156
157fn and<'a>() -> Parser<'a, u8, u8> {
158    ((seq(b"a") | seq(b"A")) + (seq(b"n") | seq(b"N")) + (seq(b"d") | seq(b"D"))).map(|_| 0)
159}
160
161fn or<'a>() -> Parser<'a, u8, u8> {
162    ((seq(b"o") | seq(b"O")) + (seq(b"r") | seq(b"R"))).map(|_| 0)
163}
164
165fn and_or<'a>() -> Parser<'a, u8, AndOr> {
166    and().map(|_| AndOr::And) | or().map(|_| AndOr::Or)
167}
168
169fn boolean_condition<'a>() -> Parser<'a, u8, BooleanCondition> {
170    space()
171        * ((property_val() + binary_op() + property_val())
172            .map(|((lval, bin_op), rval)| BooleanCondition::Comparison(lval, bin_op, rval))
173            | (lparen() * call(boolean_expression) - rparen()).map(|boolean_expression| {
174                BooleanCondition::Group(Box::new(boolean_expression))
175            }))
176        - space()
177}
178
179fn boolean_expression<'a>() -> Parser<'a, u8, BooleanExpression> {
180    (boolean_condition() + (and_or()) + call(boolean_expression)).map(
181        |((boolean_condition, and_or_initial), boolean_expression)| BooleanExpression {
182            initial: boolean_condition,
183            conditions: vec![(
184                and_or_initial,
185                BooleanCondition::Group(Box::new(boolean_expression)),
186            )],
187        },
188    ) | boolean_condition().map(|boolean_condition| BooleanExpression {
189        initial: boolean_condition,
190        conditions: vec![],
191    })
192}
193
194pub fn parse<'a>(input: &str) -> Result<BooleanExpression, pom::Error> {
195    (space() * boolean_expression() - end()).parse(input.as_bytes())
196}
197
198#[test]
199fn test_parse() {
200    let valid_exprs = [
201        "5 > 3",
202        "3.5 >= 5",
203        "true == true",
204        "true == True",
205        "false == False",
206        "None is None",
207        "5 > 3 and 3 > 1",
208        "(1=1 or 2=2) and (3 = 3)",
209        "foo = \"bar\" AND baz > 10",
210        "foo = \"bar\" OR baz > 10",
211        "foo.bar = \"bar\"",
212        "foo.bar isnot none",
213        "x in (5, 6, 7)",
214        "(3, 4) not∩ (3, 4, 5)",
215    ];
216
217    let mut pass = true;
218    for expr in valid_exprs.iter() {
219        match parse(expr) {
220            Ok(_) => (),
221            Err(e) => {
222                println!("{expr}");
223                match e {
224                    pom::Error::Mismatch { message, position } => {
225                        let spaces = " ".repeat(position);
226                        println!("{} {message}", format!("{}{}", spaces, "^"))
227                    }
228                    _ => {
229                        println!("{e}")
230                    }
231                }
232                pass = false;
233            }
234        };
235    }
236    assert!(pass);
237}