1use std::collections::HashSet;
2use std::sync::Arc;
3
4#[derive(Debug, Eq, PartialEq, Clone)]
5pub enum RelationOp {
6 LessThan,
7 LessThanEq,
8 GreaterThan,
9 GreaterThanEq,
10 Equals,
11 NotEquals,
12 In,
13}
14
15#[derive(Debug, Eq, PartialEq, Clone)]
16pub enum ArithmeticOp {
17 Add,
18 Subtract,
19 Divide,
20 Multiply,
21 Modulus,
22}
23
24#[derive(Debug, Eq, PartialEq, Clone)]
25pub enum UnaryOp {
26 Not,
27 DoubleNot,
28 Minus,
29 DoubleMinus,
30}
31
32#[derive(Debug, PartialEq, Clone)]
33pub enum Expression {
34 Arithmetic(Box<Expression>, ArithmeticOp, Box<Expression>),
35 Relation(Box<Expression>, RelationOp, Box<Expression>),
36
37 Ternary(Box<Expression>, Box<Expression>, Box<Expression>),
38 Or(Box<Expression>, Box<Expression>),
39 And(Box<Expression>, Box<Expression>),
40 Unary(UnaryOp, Box<Expression>),
41
42 Member(Box<Expression>, Box<Member>),
43 FunctionCall(Box<Expression>, Option<Box<Expression>>, Vec<Expression>),
44
45 List(Vec<Expression>),
46 Map(Vec<(Expression, Expression)>),
47
48 Atom(Atom),
49 Ident(Arc<String>),
50}
51
52#[derive(Debug, PartialEq, Clone)]
53pub enum Member {
54 Attribute(Arc<String>),
55 Index(Box<Expression>),
56 Fields(Vec<(Arc<String>, Expression)>),
57}
58
59#[derive(Debug, PartialEq, Clone)]
60pub enum Atom {
61 Int(i64),
62 UInt(u64),
63 Float(f64),
64 String(Arc<String>),
65 Bytes(Arc<Vec<u8>>),
66 Bool(bool),
67 Null,
68}
69
70pub struct ExpressionReferences<'expr> {
72 variables: HashSet<&'expr str>,
73 functions: HashSet<&'expr str>,
74}
75
76impl<'expr> ExpressionReferences<'expr> {
77 pub fn has_variable(&self, name: impl AsRef<str>) -> bool {
87 self.variables.contains(name.as_ref())
88 }
89
90 pub fn has_function(&self, name: impl AsRef<str>) -> bool {
100 self.functions.contains(name.as_ref())
101 }
102
103 pub fn variables(&self) -> Vec<&str> {
113 self.variables.iter().copied().collect()
114 }
115
116 pub fn functions(&self) -> Vec<&str> {
126 self.functions.iter().copied().collect()
127 }
128}
129
130impl Expression {
131 pub fn references(&self) -> ExpressionReferences {
145 let mut variables = HashSet::new();
146 let mut functions = HashSet::new();
147 self._references(&mut variables, &mut functions);
148 ExpressionReferences {
149 variables,
150 functions,
151 }
152 }
153
154 fn _references<'expr>(
156 &'expr self,
157 variables: &mut HashSet<&'expr str>,
158 functions: &mut HashSet<&'expr str>,
159 ) {
160 match self {
161 Expression::Arithmetic(e1, _, e2)
162 | Expression::Relation(e1, _, e2)
163 | Expression::Ternary(e1, _, e2)
164 | Expression::Or(e1, e2)
165 | Expression::And(e1, e2) => {
166 e1._references(variables, functions);
167 e2._references(variables, functions);
168 }
169 Expression::Unary(_, e) => {
170 e._references(variables, functions);
171 }
172 Expression::Member(e, _) => {
173 e._references(variables, functions);
174 }
175 Expression::FunctionCall(name, target, args) => {
176 if let Expression::Ident(v) = &**name {
177 functions.insert(v.as_str());
178 }
179 if let Some(target) = target {
180 target._references(variables, functions);
181 }
182 for e in args {
183 e._references(variables, functions);
184 }
185 }
186 Expression::List(e) => {
187 for e in e {
188 e._references(variables, functions);
189 }
190 }
191 Expression::Map(v) => {
192 for (e1, e2) in v {
193 e1._references(variables, functions);
194 e2._references(variables, functions);
195 }
196 }
197 Expression::Atom(_) => {}
198 Expression::Ident(v) => {
199 variables.insert(v.as_str());
200 }
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use crate::parse;
208
209 #[test]
210 fn test_references() {
211 let expr =
212 parse("foo.bar.baz == true && size(foo) > 0 && foo[0] == 1 && bar.startsWith('a')")
213 .unwrap();
214 let refs = expr.references();
215 assert!(refs.has_variable("foo"));
216 assert!(refs.has_variable("bar"));
217 assert_eq!(refs.variables.len(), 2);
218
219 assert!(refs.has_function("size"));
220 assert!(refs.has_function("startsWith"));
221 assert_eq!(refs.functions.len(), 2);
222 }
223}