1use pom::parser::*;
2use std::str::{self, FromStr};
3
4#[derive(Debug)]
5pub enum BinOp {
6 Equal, NotEqual, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, In, NotIn, Is, IsNot, SubSetOf, SuperSetOf, IntersectionOf, NotIntersectionOf, }
21
22#[derive(Debug, Clone)]
23pub enum SimpleValue {
24 Number(f64),
25 Str(String),
26 Bool(bool),
27 None,
28 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}