Skip to main content

bel/parser/
references.rs

1use std::collections::HashSet;
2
3use crate::common::ast::{Expr, IdedExpr};
4
5/// A collection of all the references that an expression makes to variables and functions.
6pub struct ExpressionReferences<'expr> {
7    variables: HashSet<&'expr str>,
8    functions: HashSet<&'expr str>,
9}
10
11impl ExpressionReferences<'_> {
12    /// Returns true if the expression references the provided variable name.
13    ///
14    /// # Example
15    /// ```rust
16    /// # use bel::parser::Parser;
17    /// let expression = Parser::new().parse("foo.bar == true").unwrap();
18    /// let references = expression.references();
19    /// assert!(references.has_variable("foo"));
20    /// ```
21    pub fn has_variable(&self, name: impl AsRef<str>) -> bool {
22        self.variables.contains(name.as_ref())
23    }
24
25    /// Returns true if the expression references the provided function name.
26    ///
27    /// # Example
28    /// ```rust
29    /// # use bel::parser::Parser;
30    /// let expression = Parser::new().parse("length(foo) > 0").unwrap();
31    /// let references = expression.references();
32    /// assert!(references.has_function("length"));
33    /// ```
34    pub fn has_function(&self, name: impl AsRef<str>) -> bool {
35        self.functions.contains(name.as_ref())
36    }
37
38    /// Returns a list of all variables referenced in the expression.
39    ///
40    /// # Example
41    /// ```rust
42    /// # use bel::parser::Parser;
43    /// let expression = Parser::new().parse("foo.bar == true").unwrap();
44    /// let references = expression.references();
45    /// assert_eq!(vec!["foo"], references.variables());
46    /// ```
47    pub fn variables(&self) -> Vec<&str> {
48        self.variables.iter().copied().collect()
49    }
50
51    /// Returns a list of all functions referenced in the expression.
52    ///
53    /// # Example
54    /// ```rust
55    /// # use bel::parser::Parser;
56    /// let expression = Parser::new().parse("length(foo) > 0").unwrap();
57    /// let references = expression.references();
58    /// assert!(references.functions().contains(&"_>_"));
59    /// assert!(references.functions().contains(&"length"));
60    /// ```
61    pub fn functions(&self) -> Vec<&str> {
62        self.functions.iter().copied().collect()
63    }
64}
65
66impl IdedExpr {
67    /// Returns a set of all variables and functions referenced in the expression.
68    ///
69    /// # Example
70    /// ```rust
71    /// # use bel::parser::Parser;
72    /// let expression = Parser::new().parse("foo && length(foo) > 0").unwrap();
73    /// let references = expression.references();
74    ///
75    /// assert!(references.has_variable("foo"));
76    /// assert!(references.has_function("length"));
77    /// ```
78    pub fn references(&self) -> ExpressionReferences<'_> {
79        let mut variables = HashSet::new();
80        let mut functions = HashSet::new();
81        self._references(&mut variables, &mut functions);
82        ExpressionReferences {
83            variables,
84            functions,
85        }
86    }
87
88    /// Internal recursive function to collect all variable and function references in the expression.
89    fn _references<'expr>(&'expr self, variables: &mut HashSet<&'expr str>, functions: &mut HashSet<&'expr str>) {
90        match &self.expr {
91            Expr::Unspecified => {}
92            Expr::Call(call) => {
93                functions.insert(&call.func_name);
94                if let Some(target) = &call.target {
95                    target._references(variables, functions);
96                }
97                for arg in &call.args {
98                    arg._references(variables, functions);
99                }
100            }
101            Expr::Comprehension(comp) => {
102                comp.iter_range._references(variables, functions);
103                comp.accu_init._references(variables, functions);
104                comp.loop_cond._references(variables, functions);
105                comp.loop_step._references(variables, functions);
106                comp.result._references(variables, functions);
107            }
108            Expr::Ident(name) => {
109                // todo! Might want to make this "smarter" (are we in a comprehension?) and better encode these in const
110                if !name.starts_with('@') {
111                    variables.insert(name);
112                }
113            }
114            Expr::List(list) => {
115                for elem in &list.elements {
116                    elem._references(variables, functions);
117                }
118            }
119            Expr::Literal(_) => {}
120            Expr::Map(map) => {
121                for entry in &map.entries {
122                    match &entry.expr {
123                        crate::common::ast::EntryExpr::StructField(field) => {
124                            field.value._references(variables, functions);
125                        }
126                        crate::common::ast::EntryExpr::MapEntry(map_entry) => {
127                            map_entry.key._references(variables, functions);
128                            map_entry.value._references(variables, functions);
129                        }
130                    }
131                }
132            }
133            Expr::Select(select) => {
134                select.operand._references(variables, functions);
135            }
136            Expr::Struct(struct_expr) => {
137                for entry in &struct_expr.entries {
138                    match &entry.expr {
139                        crate::common::ast::EntryExpr::StructField(field) => {
140                            field.value._references(variables, functions);
141                        }
142                        crate::common::ast::EntryExpr::MapEntry(map_entry) => {
143                            map_entry.key._references(variables, functions);
144                            map_entry.value._references(variables, functions);
145                        }
146                    }
147                }
148            }
149        }
150    }
151}