Skip to main content

cedar_policy_core/ast/
expr_iterator.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use super::{Expr, ExprKind};
18
19/// This structure implements the iterator used to traverse subexpressions of an
20/// expression.
21#[derive(Debug)]
22pub struct ExprIterator<'a, T = ()> {
23    /// The stack of expressions that need to be visited. To get the next
24    /// expression, the iterator will pop from the stack. If the stack is empty,
25    /// then the iterator is finished. Otherwise, any subexpressions of that
26    /// expression are then pushed onto the stack, and the popped expression is
27    /// returned.
28    expression_stack: Vec<&'a Expr<T>>,
29}
30
31impl<'a, T> ExprIterator<'a, T> {
32    /// Construct an expr iterator
33    pub fn new(expr: &'a Expr<T>) -> Self {
34        Self {
35            expression_stack: vec![expr],
36        }
37    }
38}
39
40impl<'a, T> Iterator for ExprIterator<'a, T> {
41    type Item = &'a Expr<T>;
42
43    fn next(&mut self) -> Option<Self::Item> {
44        let next_expr = self.expression_stack.pop()?;
45        match next_expr.expr_kind() {
46            ExprKind::Lit(_) => (),
47            ExprKind::Unknown(_) => (),
48            ExprKind::Slot(_) => (),
49            ExprKind::Var(_) => (),
50            ExprKind::If {
51                test_expr,
52                then_expr,
53                else_expr,
54            } => {
55                self.expression_stack.push(test_expr);
56                self.expression_stack.push(then_expr);
57                self.expression_stack.push(else_expr);
58            }
59            ExprKind::And { left, right } | ExprKind::Or { left, right } => {
60                self.expression_stack.push(left);
61                self.expression_stack.push(right);
62            }
63            ExprKind::UnaryApp { arg, .. } => {
64                self.expression_stack.push(arg);
65            }
66            ExprKind::BinaryApp { arg1, arg2, .. } => {
67                self.expression_stack.push(arg1);
68                self.expression_stack.push(arg2);
69            }
70            ExprKind::GetAttr { expr, attr: _ }
71            | ExprKind::HasAttr { expr, attr: _ }
72            | ExprKind::Like { expr, pattern: _ }
73            | ExprKind::Is {
74                expr,
75                entity_type: _,
76            } => {
77                self.expression_stack.push(expr);
78            }
79            ExprKind::ExtensionFunctionApp { args: exprs, .. } | ExprKind::Set(exprs) => {
80                self.expression_stack.extend(exprs.as_ref());
81            }
82            ExprKind::Record(map) => {
83                self.expression_stack.extend(map.values());
84            }
85            #[cfg(feature = "tolerant-ast")]
86            ExprKind::Error { .. } => (),
87        }
88        Some(next_expr)
89    }
90}
91
92#[cfg(test)]
93mod test {
94    use std::collections::HashSet;
95
96    use crate::ast::{BinaryOp, Expr, SlotId, UnaryOp, Var};
97
98    #[test]
99    fn literals() {
100        let e = Expr::val(true);
101        let v: HashSet<_> = e.subexpressions().collect();
102
103        assert_eq!(v.len(), 1);
104        assert!(v.contains(&Expr::val(true)));
105    }
106
107    #[test]
108    fn slots() {
109        let e = Expr::slot(SlotId::principal());
110        let v: HashSet<_> = e.subexpressions().collect();
111        assert_eq!(v.len(), 1);
112        assert!(v.contains(&Expr::slot(SlotId::principal())));
113    }
114
115    #[test]
116    fn variables() {
117        let e = Expr::var(Var::Principal);
118        let v: HashSet<_> = e.subexpressions().collect();
119        let s = HashSet::from([&e]);
120        assert_eq!(v, s);
121    }
122
123    #[test]
124    fn ite() {
125        let e = Expr::ite(Expr::val(true), Expr::val(false), Expr::val(0));
126        let v: HashSet<_> = e.subexpressions().collect();
127        assert_eq!(
128            v,
129            HashSet::from([&e, &Expr::val(true), &Expr::val(false), &Expr::val(0)])
130        );
131    }
132
133    #[test]
134    fn and() {
135        // Using `1 && false` because `true && false` would be simplified to
136        // `false` by `Expr::and`.
137        let e = Expr::and(Expr::val(1), Expr::val(false));
138        println!("{e:?}");
139        let v: HashSet<_> = e.subexpressions().collect();
140        assert_eq!(v, HashSet::from([&e, &Expr::val(1), &Expr::val(false)]));
141    }
142
143    #[test]
144    fn or() {
145        // Using `1 || false` because `true || false` would be simplified to
146        // `true` by `Expr::or`.
147        let e = Expr::or(Expr::val(1), Expr::val(false));
148        let v: HashSet<_> = e.subexpressions().collect();
149        assert_eq!(v, HashSet::from([&e, &Expr::val(1), &Expr::val(false)]));
150    }
151
152    #[test]
153    fn unary() {
154        let e = Expr::unary_app(UnaryOp::Not, Expr::val(false));
155        assert_eq!(
156            e.subexpressions().collect::<HashSet<_>>(),
157            HashSet::from([&e, &Expr::val(false)])
158        );
159    }
160
161    #[test]
162    fn binary() {
163        let e = Expr::binary_app(BinaryOp::Eq, Expr::val(false), Expr::val(true));
164        assert_eq!(
165            e.subexpressions().collect::<HashSet<_>>(),
166            HashSet::from([&e, &Expr::val(false), &Expr::val(true)])
167        );
168    }
169
170    #[test]
171    fn ext() {
172        let e = Expr::call_extension_fn(
173            "test".parse().unwrap(),
174            vec![Expr::val(false), Expr::val(true)],
175        );
176        assert_eq!(
177            e.subexpressions().collect::<HashSet<_>>(),
178            HashSet::from([&e, &Expr::val(false), &Expr::val(true)])
179        );
180    }
181
182    #[test]
183    fn has_attr() {
184        let e = Expr::has_attr(Expr::val(false), "test".into());
185        assert_eq!(
186            e.subexpressions().collect::<HashSet<_>>(),
187            HashSet::from([&e, &Expr::val(false)])
188        );
189    }
190
191    #[test]
192    fn get_attr() {
193        let e = Expr::get_attr(Expr::val(false), "test".into());
194        assert_eq!(
195            e.subexpressions().collect::<HashSet<_>>(),
196            HashSet::from([&e, &Expr::val(false)])
197        );
198    }
199
200    #[test]
201    fn set() {
202        let e = Expr::set(vec![Expr::val(false), Expr::val(true)]);
203        assert_eq!(
204            e.subexpressions().collect::<HashSet<_>>(),
205            HashSet::from([&e, &Expr::val(false), &Expr::val(true)])
206        );
207    }
208
209    #[test]
210    fn set_duplicates() {
211        let e = Expr::set(vec![Expr::val(true), Expr::val(true)]);
212        let v: Vec<_> = e.subexpressions().collect();
213        assert_eq!(v.len(), 3);
214        assert!(v.contains(&&Expr::val(true)));
215    }
216
217    #[test]
218    fn record() {
219        let e = Expr::record(vec![
220            ("test".into(), Expr::val(true)),
221            ("another".into(), Expr::val(false)),
222        ])
223        .unwrap();
224        assert_eq!(
225            e.subexpressions().collect::<HashSet<_>>(),
226            HashSet::from([&e, &Expr::val(false), &Expr::val(true)])
227        );
228    }
229
230    #[test]
231    fn is() {
232        let e = Expr::is_entity_type(Expr::val(1), "T".parse().unwrap());
233        assert_eq!(
234            e.subexpressions().collect::<HashSet<_>>(),
235            HashSet::from([&e, &Expr::val(1)])
236        );
237    }
238
239    #[test]
240    fn duplicates() {
241        let e = Expr::ite(Expr::val(true), Expr::val(true), Expr::val(true));
242        let v: Vec<_> = e.subexpressions().collect();
243        assert_eq!(v.len(), 4);
244        assert!(v.contains(&&e));
245        assert!(v.contains(&&Expr::val(true)));
246    }
247
248    #[test]
249    fn deeply_nested() {
250        let e = Expr::get_attr(
251            Expr::get_attr(Expr::and(Expr::val(1), Expr::val(0)), "attr2".into()),
252            "attr1".into(),
253        );
254        let set: HashSet<_> = e.subexpressions().collect();
255        assert!(set.contains(&e));
256        assert!(set.contains(&Expr::get_attr(
257            Expr::and(Expr::val(1), Expr::val(0)),
258            "attr2".into()
259        )));
260        assert!(set.contains(&Expr::and(Expr::val(1), Expr::val(0))));
261        assert!(set.contains(&Expr::val(1)));
262        assert!(set.contains(&Expr::val(0)));
263    }
264}