Skip to main content

yog_script_analyze/ast/
walk.rs

1//! Walking the tree: an expression's direct sub-expressions, a
2//! declaration's members — for passes that look at every one.
3
4use super::{Decl, DeclKind, Element, Expr, ExprKind, Name, Pattern, Stmt, StrSeg};
5
6impl Expr {
7    /// Each expression directly inside this one, in reading order; a
8    /// lambda's body is not (its code runs later, on its own).
9    pub fn each_child<'e>(&'e self, mut f: impl FnMut(&'e Expr)) {
10        match &self.kind {
11            ExprKind::Str(segs) => segs.iter().for_each(|s| {
12                if let StrSeg::Expr(e) = s {
13                    f(e);
14                }
15            }),
16            ExprKind::Tuple { items, .. } | ExprKind::Macro { args: items, .. } => {
17                items.iter().for_each(f)
18            }
19            ExprKind::Collection(items) => items.iter().for_each(|i| match i {
20                Element::Item(e) | Element::Spread(e) => f(e),
21                Element::Entry(k, v) => {
22                    f(k);
23                    f(v);
24                }
25            }),
26            ExprKind::New { args, init, .. } => {
27                args.iter().flatten().for_each(|a| f(&a.value));
28                init.iter().for_each(|(_, v)| f(v));
29            }
30            ExprKind::Call { callee, args } => {
31                f(callee);
32                args.iter().for_each(|a| f(&a.value));
33            }
34            ExprKind::Binary { left, right, .. } => {
35                f(left);
36                f(right);
37            }
38            ExprKind::Range { from, to, .. } => {
39                f(from);
40                f(to);
41            }
42            ExprKind::Index { expr, args, .. } => {
43                f(expr);
44                args.iter().for_each(f);
45            }
46            ExprKind::NewArray { sizes, init, .. } => {
47                sizes.iter().for_each(&mut f);
48                init.iter().flatten().for_each(|i| i.each_value(&mut f));
49            }
50            ExprKind::Conditional {
51                cond,
52                then,
53                otherwise,
54            } => {
55                f(cond);
56                f(then);
57                f(otherwise);
58            }
59            ExprKind::Switch { expr, arms } => {
60                f(expr);
61                for arm in arms {
62                    arm.guard.iter().for_each(&mut f);
63                    f(&arm.value);
64                }
65            }
66            ExprKind::With { expr, inits } => {
67                f(expr);
68                inits.iter().for_each(|(_, v)| f(v));
69            }
70            _ => self.each_wrapped(f),
71        }
72    }
73
74    /// The one expression a wrapping form holds (`-x`, `x.Y`, `move x`, …).
75    fn each_wrapped<'e>(&'e self, mut f: impl FnMut(&'e Expr)) {
76        match &self.kind {
77            ExprKind::NameOf(e)
78            | ExprKind::NotNull(e)
79            | ExprKind::Move(e)
80            | ExprKind::Checked { expr: e, .. }
81            | ExprKind::Unary { expr: e, .. }
82            | ExprKind::Cast { expr: e, .. }
83            | ExprKind::Is { expr: e, .. }
84            | ExprKind::As { expr: e, .. }
85            | ExprKind::Has { expr: e, .. }
86            | ExprKind::TypeArgs { expr: e, .. }
87            | ExprKind::Member { expr: e, .. } => f(e),
88            _ => {}
89        }
90    }
91}
92
93impl DeclKind {
94    /// The declarations inside this one: a type's, a mod's, a namespace's
95    /// members.
96    pub fn members(&self) -> &[Decl] {
97        match self {
98            DeclKind::Namespace {
99                body: Some(body), ..
100            }
101            | DeclKind::Mod { body, .. }
102            | DeclKind::Type { body, .. }
103            | DeclKind::Enum { body, .. }
104            | DeclKind::Content { body, .. }
105            | DeclKind::Component { body, .. }
106            | DeclKind::Extend { body, .. }
107            | DeclKind::Screen { body, .. }
108            | DeclKind::Command { body, .. } => body,
109            _ => &[],
110        }
111    }
112}
113
114impl Pattern {
115    /// The names the pattern binds (`let e`, `Monster m`), in order.
116    pub fn each_binding<'p>(&'p self, f: &mut impl FnMut(&'p Name)) {
117        match self {
118            Pattern::Let(n) | Pattern::Type { bind: Some(n), .. } => f(n),
119            Pattern::Variant { args: all, .. } | Pattern::Tuple(all) | Pattern::List(all, _) => {
120                all.iter().for_each(|p| p.each_binding(f));
121            }
122            Pattern::Slice(Some(p)) => p.each_binding(f),
123            Pattern::Property { fields, .. } => fields.iter().for_each(|(_, p)| p.each_binding(f)),
124            Pattern::Or(a, b) | Pattern::And(a, b) => {
125                a.each_binding(f);
126                b.each_binding(f);
127            }
128            Pattern::Not(p) => p.each_binding(f),
129            _ => {}
130        }
131    }
132}
133
134impl Stmt {
135    /// Each expression it holds, its inner statements' too (a local
136    /// function's code aside: its own).
137    pub fn each_expr(&self, f: &mut dyn FnMut(&Expr)) {
138        match self {
139            // Its own code, apart: what it uses isn't the block's statements'.
140            Stmt::Function(_) => {}
141            Stmt::Switch { expr, sections } => {
142                f(expr);
143                for s in sections {
144                    for (_, guard) in s.labels.iter().flatten() {
145                        guard.iter().for_each(&mut *f);
146                    }
147                    s.body.iter().for_each(|x| x.each_expr(f));
148                }
149            }
150            Stmt::Local { value, .. } => value.iter().for_each(&mut *f),
151            Stmt::Assign { place, value, .. } => {
152                f(place);
153                f(value);
154            }
155            Stmt::If {
156                cond,
157                then,
158                otherwise,
159            } => {
160                f(cond);
161                then.each_expr(f);
162                if let Some(o) = otherwise {
163                    o.each_expr(f);
164                }
165            }
166            Stmt::Foreach { iter, body, .. } => {
167                f(iter);
168                body.each_expr(f);
169            }
170            Stmt::For {
171                init,
172                cond,
173                step,
174                body,
175            } => {
176                if let Some(i) = init {
177                    i.each_expr(f);
178                }
179                cond.iter().for_each(&mut *f);
180                if let Some(st) = step {
181                    st.each_expr(f);
182                }
183                body.each_expr(f);
184            }
185            Stmt::While { cond, body } | Stmt::DoWhile { body, cond } => {
186                f(cond);
187                body.each_expr(f);
188            }
189            Stmt::Block(b) | Stmt::Unsafe(b, _) | Stmt::Checked(b, _) => b.stmts.iter().for_each(|s| s.each_expr(f)),
190            Stmt::Return(v, _) | Stmt::Skip(v, _) | Stmt::Yield(v, _) => v.iter().for_each(&mut *f),
191            Stmt::Lock { target, body, .. } => {
192                f(target);
193                body.each_expr(f);
194            }
195            Stmt::Expr(e) | Stmt::Value(e) => f(e),
196            Stmt::Goto(crate::ast::Goto::Case(e), _) => f(e),
197            Stmt::Break(_) | Stmt::Continue(_) | Stmt::Label(_) | Stmt::Goto(..) => {}
198        }
199    }
200}
201
202impl Stmt {
203    /// The statements directly inside it (a local function's aside: its own code).
204    pub fn inner(&self) -> Vec<&Stmt> {
205        match self {
206            Stmt::Block(b) | Stmt::Unsafe(b, _) | Stmt::Checked(b, _) => b.stmts.iter().collect(),
207            Stmt::If {
208                then, otherwise, ..
209            } => std::iter::once(&**then).chain(otherwise.as_deref()).collect(),
210            Stmt::Foreach { body, .. }
211            | Stmt::While { body, .. }
212            | Stmt::DoWhile { body, .. }
213            | Stmt::Lock { body, .. } => vec![&**body],
214            Stmt::For {
215                init, step, body, ..
216            } => init.as_deref().into_iter().chain(step.as_deref()).chain([&**body]).collect(),
217            Stmt::Switch { sections, .. } => sections.iter().flat_map(|s| &s.body).collect(),
218            _ => Vec::new(),
219        }
220    }
221
222    /// Whether it or a statement inside it is `found` (looking into a
223    /// statement only when `enter` lets it).
224    pub fn any_inside(&self, found: &dyn Fn(&Stmt) -> bool, enter: &dyn Fn(&Stmt) -> bool) -> bool {
225        found(self) || (enter(self) && self.inner().iter().any(|s| s.any_inside(found, enter)))
226    }
227
228    /// Whether a label is in it: a `goto` may jump there.
229    pub fn has_labels(&self) -> bool {
230        self.any_inside(&|s| matches!(s, Stmt::Label(_)), &|_| true)
231    }
232
233    /// Whether a `goto case` or `goto default` of the `switch` it's in is
234    /// in it (not one of a `switch` inside it).
235    pub fn goes_to_case(&self) -> bool {
236        self.any_inside(
237            &|s| matches!(s, Stmt::Goto(super::Goto::Case(_) | super::Goto::Default, _)),
238            &|s| !matches!(s, Stmt::Switch { .. }),
239        )
240    }
241}