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