cas_parser/parser/
iter.rs

1use super::ast::expr::Expr;
2
3/// An iterator that iteratively traverses the tree of expressions in left-to-right post-order
4/// (i.e. depth-first).
5///
6/// This iterator is created by [`Expr::post_order_iter`].
7pub struct ExprIter<'a> {
8    stack: Vec<&'a Expr>,
9    last_visited: Option<&'a Expr>,
10}
11
12impl<'a> ExprIter<'a> {
13    /// Creates a new iterator that traverses the tree of expressions in left-to-right post-order
14    /// (i.e. depth-first).
15    pub fn new(expr: &'a Expr) -> Self {
16        Self {
17            stack: vec![expr],
18            last_visited: None,
19        }
20    }
21
22    /// Pops the current expression in the stack and marks it as the last visited expression.
23    fn visit(&mut self) -> Option<&'a Expr> {
24        self.last_visited = Some(self.stack.pop()?);
25        self.last_visited
26    }
27
28    /// Returns true if the given expression matches the last visited expression.
29    fn is_last_visited(&self, expr: &'a Expr) -> bool {
30        match self.last_visited {
31            Some(last_visited) => std::ptr::eq(last_visited, expr),
32            None => false,
33        }
34    }
35}
36
37impl<'a> Iterator for ExprIter<'a> {
38    type Item = &'a Expr;
39
40    fn next(&mut self) -> Option<Self::Item> {
41        loop {
42            let expr = self.stack.last()?;
43            match expr {
44                Expr::Literal(_) => return self.visit(),
45                Expr::Paren(paren) => {
46                    if self.is_last_visited(&paren.expr) {
47                        return self.visit();
48                    }
49                    self.stack.push(&paren.expr);
50                },
51                Expr::Block(_) => return self.visit(), // NOTE: inner statements are not visited
52                Expr::If(if_expr) => {
53                    if let Some(else_expr) = &if_expr.else_expr {
54                        if self.is_last_visited(else_expr) {
55                            return self.visit();
56                        }
57                        self.stack.push(else_expr);
58                        self.stack.push(&if_expr.then_expr);
59                        self.stack.push(&if_expr.condition);
60                    } else {
61                        if self.is_last_visited(&if_expr.then_expr) {
62                            return self.visit();
63                        }
64                        self.stack.push(&if_expr.then_expr);
65                        self.stack.push(&if_expr.condition);
66                    }
67                },
68                Expr::Loop(loop_expr) => {
69                    if self.is_last_visited(&loop_expr.body) {
70                        return self.visit();
71                    }
72                    self.stack.push(&loop_expr.body);
73                },
74                Expr::While(while_expr) => {
75                    if self.is_last_visited(&while_expr.body) {
76                        return self.visit();
77                    }
78                    self.stack.push(&while_expr.body);
79                    self.stack.push(&while_expr.condition);
80                },
81                Expr::Break(break_expr) => {
82                    if let Some(value) = &break_expr.value {
83                        if self.is_last_visited(value) {
84                            return self.visit();
85                        }
86                        self.stack.push(value);
87                    } else {
88                        return self.visit();
89                    }
90                },
91                Expr::Continue(_) => return self.visit(),
92                Expr::Call(call) => {
93                    if call.args.is_empty() || self.is_last_visited(call.args.last().unwrap()) {
94                        return self.visit();
95                    }
96                    for arg in call.args.iter().rev() {
97                        self.stack.push(arg);
98                    }
99                },
100                Expr::Unary(unary) => {
101                    if self.is_last_visited(&unary.operand) {
102                        return self.visit();
103                    }
104                    self.stack.push(&unary.operand);
105                },
106                Expr::Binary(binary) => {
107                    if self.is_last_visited(&binary.rhs) {
108                        return self.visit();
109                    }
110                    self.stack.push(&binary.rhs);
111                    self.stack.push(&binary.lhs);
112                },
113                Expr::Assign(assign) => {
114                    if self.is_last_visited(&assign.value) {
115                        return self.visit();
116                    }
117                    self.stack.push(&assign.value);
118                },
119            }
120        }
121    }
122}