1use crate::ast::{BindingPattern, DictEntry, MatchArm, Node, SNode, SelectCase};
25
26pub fn walk_program(program: &[SNode], visitor: &mut impl FnMut(&SNode)) {
29 for node in program {
30 walk_node(node, visitor);
31 }
32}
33
34pub fn walk_node(node: &SNode, visitor: &mut impl FnMut(&SNode)) {
36 visitor(node);
37 walk_children(node, visitor);
38}
39
40pub fn walk_children(node: &SNode, visitor: &mut impl FnMut(&SNode)) {
44 match &node.node {
45 Node::AttributedDecl { attributes, inner } => {
46 for attr in attributes {
47 for arg in &attr.args {
48 walk_node(&arg.value, visitor);
49 }
50 }
51 walk_node(inner, visitor);
52 }
53 Node::Pipeline { body, .. } | Node::OverrideDecl { body, .. } => {
54 walk_nodes(body, visitor);
55 }
56 Node::LetBinding { pattern, value, .. } | Node::VarBinding { pattern, value, .. } => {
57 walk_binding_pattern(pattern, visitor);
58 walk_node(value, visitor);
59 }
60 Node::ConstBinding { value, .. } => {
61 walk_node(value, visitor);
62 }
63 Node::EnumDecl { .. }
64 | Node::StructDecl { .. }
65 | Node::InterfaceDecl { .. }
66 | Node::ImportDecl { .. }
67 | Node::SelectiveImport { .. }
68 | Node::TypeDecl { .. }
69 | Node::BreakStmt
70 | Node::ContinueStmt => {}
71 Node::ImplBlock { methods, .. } => walk_nodes(methods, visitor),
72 Node::IfElse {
73 condition,
74 then_body,
75 else_body,
76 } => {
77 walk_node(condition, visitor);
78 walk_nodes(then_body, visitor);
79 if let Some(body) = else_body {
80 walk_nodes(body, visitor);
81 }
82 }
83 Node::ForIn {
84 pattern,
85 iterable,
86 body,
87 } => {
88 walk_binding_pattern(pattern, visitor);
89 walk_node(iterable, visitor);
90 walk_nodes(body, visitor);
91 }
92 Node::MatchExpr { value, arms } => {
93 walk_node(value, visitor);
94 for arm in arms {
95 walk_match_arm(arm, visitor);
96 }
97 }
98 Node::WhileLoop { condition, body } => {
99 walk_node(condition, visitor);
100 walk_nodes(body, visitor);
101 }
102 Node::Retry { count, body } => {
103 walk_node(count, visitor);
104 walk_nodes(body, visitor);
105 }
106 Node::CostRoute { options, body } => {
107 walk_option_values(options, visitor);
108 walk_nodes(body, visitor);
109 }
110 Node::ReturnStmt { value } | Node::YieldExpr { value } => {
111 if let Some(value) = value {
112 walk_node(value, visitor);
113 }
114 }
115 Node::TryCatch {
116 has_catch: _,
117 body,
118 catch_body,
119 finally_body,
120 ..
121 } => {
122 walk_nodes(body, visitor);
123 walk_nodes(catch_body, visitor);
124 if let Some(body) = finally_body {
125 walk_nodes(body, visitor);
126 }
127 }
128 Node::TryExpr { body }
129 | Node::SpawnExpr { body }
130 | Node::DeferStmt { body }
131 | Node::MutexBlock { body }
132 | Node::Block(body)
133 | Node::Closure { body, .. } => walk_nodes(body, visitor),
134 Node::FnDecl { body, .. } | Node::ToolDecl { body, .. } => {
135 walk_nodes(body, visitor);
136 }
137 Node::SkillDecl { fields, .. } => walk_field_values(fields, visitor),
138 Node::EvalPackDecl {
139 fields,
140 body,
141 summarize,
142 ..
143 } => {
144 walk_field_values(fields, visitor);
145 walk_nodes(body, visitor);
146 if let Some(body) = summarize {
147 walk_nodes(body, visitor);
148 }
149 }
150 Node::RangeExpr { start, end, .. } => {
151 walk_node(start, visitor);
152 walk_node(end, visitor);
153 }
154 Node::GuardStmt {
155 condition,
156 else_body,
157 } => {
158 walk_node(condition, visitor);
159 walk_nodes(else_body, visitor);
160 }
161 Node::RequireStmt { condition, message } => {
162 walk_node(condition, visitor);
163 if let Some(message) = message {
164 walk_node(message, visitor);
165 }
166 }
167 Node::DeadlineBlock { duration, body } => {
168 walk_node(duration, visitor);
169 walk_nodes(body, visitor);
170 }
171 Node::EmitExpr { value }
172 | Node::ThrowStmt { value }
173 | Node::Spread(value)
174 | Node::TryOperator { operand: value }
175 | Node::TryStar { operand: value }
176 | Node::UnaryOp { operand: value, .. } => walk_node(value, visitor),
177 Node::HitlExpr { args, .. } => {
178 for arg in args {
179 walk_node(&arg.value, visitor);
180 }
181 }
182 Node::Parallel {
183 expr,
184 body,
185 options,
186 ..
187 } => {
188 walk_node(expr, visitor);
189 walk_option_values(options, visitor);
190 walk_nodes(body, visitor);
191 }
192 Node::SelectExpr {
193 cases,
194 timeout,
195 default_body,
196 } => {
197 for case in cases {
198 walk_select_case(case, visitor);
199 }
200 if let Some((duration, body)) = timeout {
201 walk_node(duration, visitor);
202 walk_nodes(body, visitor);
203 }
204 if let Some(body) = default_body {
205 walk_nodes(body, visitor);
206 }
207 }
208 Node::FunctionCall { args, .. } | Node::EnumConstruct { args, .. } => {
209 walk_nodes(args, visitor);
210 }
211 Node::MethodCall { object, args, .. } | Node::OptionalMethodCall { object, args, .. } => {
212 walk_node(object, visitor);
213 walk_nodes(args, visitor);
214 }
215 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
216 walk_node(object, visitor);
217 }
218 Node::SubscriptAccess { object, index }
219 | Node::OptionalSubscriptAccess { object, index } => {
220 walk_node(object, visitor);
221 walk_node(index, visitor);
222 }
223 Node::SliceAccess { object, start, end } => {
224 walk_node(object, visitor);
225 if let Some(start) = start {
226 walk_node(start, visitor);
227 }
228 if let Some(end) = end {
229 walk_node(end, visitor);
230 }
231 }
232 Node::BinaryOp { left, right, .. } => {
233 walk_node(left, visitor);
234 walk_node(right, visitor);
235 }
236 Node::Ternary {
237 condition,
238 true_expr,
239 false_expr,
240 } => {
241 walk_node(condition, visitor);
242 walk_node(true_expr, visitor);
243 walk_node(false_expr, visitor);
244 }
245 Node::Assignment { target, value, .. } => {
246 walk_node(target, visitor);
247 walk_node(value, visitor);
248 }
249 Node::StructConstruct { fields, .. } | Node::DictLiteral(fields) => {
250 walk_dict_entries(fields, visitor);
251 }
252 Node::ListLiteral(items) | Node::OrPattern(items) => walk_nodes(items, visitor),
253 Node::InterpolatedString(_)
254 | Node::StringLiteral(_)
255 | Node::RawStringLiteral(_)
256 | Node::IntLiteral(_)
257 | Node::FloatLiteral(_)
258 | Node::BoolLiteral(_)
259 | Node::NilLiteral
260 | Node::Identifier(_)
261 | Node::DurationLiteral(_) => {}
262 }
263}
264
265fn walk_nodes(nodes: &[SNode], visitor: &mut impl FnMut(&SNode)) {
266 for node in nodes {
267 walk_node(node, visitor);
268 }
269}
270
271fn walk_dict_entries(entries: &[DictEntry], visitor: &mut impl FnMut(&SNode)) {
272 for entry in entries {
273 walk_node(&entry.key, visitor);
274 walk_node(&entry.value, visitor);
275 }
276}
277
278fn walk_field_values(fields: &[(String, SNode)], visitor: &mut impl FnMut(&SNode)) {
279 for (_, value) in fields {
280 walk_node(value, visitor);
281 }
282}
283
284fn walk_option_values(options: &[(String, SNode)], visitor: &mut impl FnMut(&SNode)) {
285 for (_, value) in options {
286 walk_node(value, visitor);
287 }
288}
289
290fn walk_match_arm(arm: &MatchArm, visitor: &mut impl FnMut(&SNode)) {
291 walk_node(&arm.pattern, visitor);
292 if let Some(guard) = &arm.guard {
293 walk_node(guard, visitor);
294 }
295 walk_nodes(&arm.body, visitor);
296}
297
298fn walk_select_case(case: &SelectCase, visitor: &mut impl FnMut(&SNode)) {
299 walk_node(&case.channel, visitor);
300 walk_nodes(&case.body, visitor);
301}
302
303fn walk_binding_pattern(pattern: &BindingPattern, visitor: &mut impl FnMut(&SNode)) {
304 match pattern {
305 BindingPattern::Identifier(_) | BindingPattern::Pair(_, _) => {}
306 BindingPattern::Dict(fields) => {
307 for field in fields {
308 if let Some(default) = &field.default_value {
309 walk_node(default, visitor);
310 }
311 }
312 }
313 BindingPattern::List(items) => {
314 for item in items {
315 if let Some(default) = &item.default_value {
316 walk_node(default, visitor);
317 }
318 }
319 }
320 }
321}