1use crate::ast::{BindingPattern, DictEntry, MatchArm, Node, SNode, SelectCase, TypedParam};
25
26pub fn walk_program(program: &[SNode], visitor: &mut impl FnMut(&SNode)) {
29 let mut stack = Vec::with_capacity(program.len());
30 push_nodes_reversed(program, &mut stack);
31 walk_stack(&mut stack, visitor);
32}
33
34pub fn walk_node(node: &SNode, visitor: &mut impl FnMut(&SNode)) {
36 let mut stack = vec![node];
37 walk_stack(&mut stack, visitor);
38}
39
40pub fn walk_children(node: &SNode, visitor: &mut impl FnMut(&SNode)) {
44 let mut stack = Vec::new();
45 push_children_reversed(node, &mut stack);
46 walk_stack(&mut stack, visitor);
47}
48
49fn walk_stack(stack: &mut Vec<&SNode>, visitor: &mut impl FnMut(&SNode)) {
50 while let Some(node) = stack.pop() {
51 visitor(node);
52 push_children_reversed(node, stack);
53 }
54}
55
56fn push_children_reversed<'a>(node: &'a SNode, stack: &mut Vec<&'a SNode>) {
57 let mut children = Vec::new();
58 collect_children(node, &mut children);
59 stack.extend(children.into_iter().rev());
60}
61
62fn push_nodes_reversed<'a>(nodes: &'a [SNode], stack: &mut Vec<&'a SNode>) {
63 stack.extend(nodes.iter().rev());
64}
65
66pub fn immediate_children(node: &SNode) -> Vec<&SNode> {
70 let mut children = Vec::new();
71 collect_children(node, &mut children);
72 children
73}
74
75fn collect_children<'a>(node: &'a SNode, children: &mut Vec<&'a SNode>) {
76 match &node.node {
77 Node::AttributedDecl { attributes, inner } => {
78 for attr in attributes {
79 for arg in &attr.args {
80 children.push(&arg.value);
81 }
82 }
83 children.push(inner);
84 }
85 Node::Pipeline { body, .. } | Node::OverrideDecl { body, .. } => {
86 collect_nodes(body, children);
87 }
88 Node::LetBinding { pattern, value, .. } | Node::ConstBinding { pattern, value, .. } => {
89 collect_binding_pattern(pattern, children);
90 children.push(value);
91 }
92 Node::EnumDecl { variants, .. } => {
93 for variant in variants {
94 collect_typed_param_defaults(&variant.fields, children);
95 }
96 }
97 Node::StructDecl { .. }
98 | Node::ImportDecl { .. }
99 | Node::SelectiveImport { .. }
100 | Node::TypeDecl { .. }
101 | Node::BreakStmt
102 | Node::ContinueStmt => {}
103 Node::InterfaceDecl { methods, .. } => {
104 for method in methods {
105 collect_typed_param_defaults(&method.params, children);
106 }
107 }
108 Node::ImplBlock { methods, .. } => collect_nodes(methods, children),
109 Node::IfElse {
110 condition,
111 then_body,
112 else_body,
113 ..
114 } => {
115 children.push(condition);
116 collect_nodes(then_body, children);
117 if let Some(body) = else_body {
118 collect_nodes(body, children);
119 }
120 }
121 Node::ForIn {
122 pattern,
123 iterable,
124 body,
125 } => {
126 collect_binding_pattern(pattern, children);
127 children.push(iterable);
128 collect_nodes(body, children);
129 }
130 Node::MatchExpr { value, arms } => {
131 children.push(value);
132 for arm in arms {
133 collect_match_arm(arm, children);
134 }
135 }
136 Node::WhileLoop { condition, body } => {
137 children.push(condition);
138 collect_nodes(body, children);
139 }
140 Node::Retry { count, body } => {
141 children.push(count);
142 collect_nodes(body, children);
143 }
144 Node::CostRoute { options, body } => {
145 collect_option_values(options, children);
146 collect_nodes(body, children);
147 }
148 Node::ReturnStmt { value } | Node::YieldExpr { value } => {
149 if let Some(value) = value {
150 children.push(value);
151 }
152 }
153 Node::TryCatch {
154 has_catch: _,
155 body,
156 catch_body,
157 finally_body,
158 ..
159 } => {
160 collect_nodes(body, children);
161 collect_nodes(catch_body, children);
162 if let Some(body) = finally_body {
163 collect_nodes(body, children);
164 }
165 }
166 Node::TryExpr { body }
167 | Node::SpawnExpr { body }
168 | Node::ScopeBlock { body }
169 | Node::DeferStmt { body }
170 | Node::Block(body) => collect_nodes(body, children),
171 Node::Closure { params, body, .. } => {
172 collect_typed_param_defaults(params, children);
173 collect_nodes(body, children);
174 }
175 Node::MutexBlock { key, body } => {
176 if let Some(key) = key {
177 children.push(key);
178 }
179 collect_nodes(body, children);
180 }
181 Node::FnDecl { params, body, .. } | Node::ToolDecl { params, body, .. } => {
182 collect_typed_param_defaults(params, children);
183 collect_nodes(body, children);
184 }
185 Node::SkillDecl { fields, .. } => collect_field_values(fields, children),
186 Node::EvalPackDecl {
187 fields,
188 body,
189 summarize,
190 ..
191 } => {
192 collect_field_values(fields, children);
193 collect_nodes(body, children);
194 if let Some(body) = summarize {
195 collect_nodes(body, children);
196 }
197 }
198 Node::RangeExpr { start, end, .. } => {
199 children.push(start);
200 children.push(end);
201 }
202 Node::GuardStmt {
203 condition,
204 else_body,
205 } => {
206 children.push(condition);
207 collect_nodes(else_body, children);
208 }
209 Node::RequireStmt { condition, message } => {
210 children.push(condition);
211 if let Some(message) = message {
212 children.push(message);
213 }
214 }
215 Node::DeadlineBlock { duration, body } => {
216 children.push(duration);
217 collect_nodes(body, children);
218 }
219 Node::EmitExpr { value }
220 | Node::ThrowStmt { value }
221 | Node::Spread(value)
222 | Node::TryOperator { operand: value }
223 | Node::TryStar { operand: value }
224 | Node::NonNullAssert { operand: value }
225 | Node::UnaryOp { operand: value, .. } => children.push(value),
226 Node::HitlExpr { args, .. } => {
227 for arg in args {
228 children.push(&arg.value);
229 }
230 }
231 Node::Parallel {
232 expr,
233 body,
234 options,
235 ..
236 } => {
237 children.push(expr);
238 collect_option_values(options, children);
239 collect_nodes(body, children);
240 }
241 Node::SelectExpr {
242 cases,
243 timeout,
244 default_body,
245 } => {
246 for case in cases {
247 collect_select_case(case, children);
248 }
249 if let Some((duration, body)) = timeout {
250 children.push(duration);
251 collect_nodes(body, children);
252 }
253 if let Some(body) = default_body {
254 collect_nodes(body, children);
255 }
256 }
257 Node::FunctionCall { args, .. } | Node::EnumConstruct { args, .. } => {
258 collect_nodes(args, children);
259 }
260 Node::ValueCall { callee, args } => {
261 children.push(callee);
262 collect_nodes(args, children);
263 }
264 Node::MethodCall { object, args, .. } | Node::OptionalMethodCall { object, args, .. } => {
265 children.push(object);
266 collect_nodes(args, children);
267 }
268 Node::PropertyAccess { object, .. } | Node::OptionalPropertyAccess { object, .. } => {
269 children.push(object);
270 }
271 Node::SubscriptAccess { object, index }
272 | Node::OptionalSubscriptAccess { object, index } => {
273 children.push(object);
274 children.push(index);
275 }
276 Node::SliceAccess { object, start, end } => {
277 children.push(object);
278 if let Some(start) = start {
279 children.push(start);
280 }
281 if let Some(end) = end {
282 children.push(end);
283 }
284 }
285 Node::BinaryOp { left, right, .. } => {
286 children.push(left);
287 children.push(right);
288 }
289 Node::Ternary {
290 condition,
291 true_expr,
292 false_expr,
293 } => {
294 children.push(condition);
295 children.push(true_expr);
296 children.push(false_expr);
297 }
298 Node::Assignment { target, value, .. } => {
299 children.push(target);
300 children.push(value);
301 }
302 Node::StructConstruct { fields, .. } | Node::DictLiteral(fields) => {
303 collect_dict_entries(fields, children);
304 }
305 Node::ListLiteral(items) | Node::OrPattern(items) => collect_nodes(items, children),
306 Node::InterpolatedString(_)
307 | Node::StringLiteral(_)
308 | Node::RawStringLiteral(_)
309 | Node::IntLiteral(_)
310 | Node::FloatLiteral(_)
311 | Node::BoolLiteral(_)
312 | Node::NilLiteral
313 | Node::Identifier(_)
314 | Node::DurationLiteral(_) => {}
315 }
316}
317
318fn collect_nodes<'a>(nodes: &'a [SNode], children: &mut Vec<&'a SNode>) {
319 children.extend(nodes.iter());
320}
321
322fn collect_dict_entries<'a>(entries: &'a [DictEntry], children: &mut Vec<&'a SNode>) {
323 for entry in entries {
324 children.push(&entry.key);
325 children.push(&entry.value);
326 }
327}
328
329fn collect_field_values<'a>(fields: &'a [(String, SNode)], children: &mut Vec<&'a SNode>) {
330 for (_, value) in fields {
331 children.push(value);
332 }
333}
334
335fn collect_option_values<'a>(options: &'a [(String, SNode)], children: &mut Vec<&'a SNode>) {
336 for (_, value) in options {
337 children.push(value);
338 }
339}
340
341fn collect_typed_param_defaults<'a>(params: &'a [TypedParam], children: &mut Vec<&'a SNode>) {
342 for param in params {
343 if let Some(default) = ¶m.default_value {
344 children.push(default);
345 }
346 }
347}
348
349fn collect_match_arm<'a>(arm: &'a MatchArm, children: &mut Vec<&'a SNode>) {
350 children.push(&arm.pattern);
351 if let Some(guard) = &arm.guard {
352 children.push(guard);
353 }
354 collect_nodes(&arm.body, children);
355}
356
357fn collect_select_case<'a>(case: &'a SelectCase, children: &mut Vec<&'a SNode>) {
358 children.push(&case.channel);
359 collect_nodes(&case.body, children);
360}
361
362fn collect_binding_pattern<'a>(pattern: &'a BindingPattern, children: &mut Vec<&'a SNode>) {
363 match pattern {
364 BindingPattern::Identifier(_) | BindingPattern::Pair(_, _) => {}
365 BindingPattern::Dict(fields) => {
366 for field in fields {
367 if let Some(default) = &field.default_value {
368 children.push(default);
369 }
370 }
371 }
372 BindingPattern::List(items) => {
373 for item in items {
374 if let Some(default) = &item.default_value {
375 children.push(default);
376 }
377 }
378 }
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use crate::ast::{spanned, Node, TypedParam};
386 use harn_lexer::Span;
387
388 fn dummy(node: Node) -> SNode {
389 spanned(node, Span::dummy())
390 }
391
392 #[test]
393 fn walk_program_preserves_preorder() {
394 let program = vec![dummy(Node::LetBinding {
395 pattern: BindingPattern::Identifier("x".to_string()),
396 type_ann: None,
397 value: Box::new(dummy(Node::BinaryOp {
398 op: "+".to_string(),
399 left: Box::new(dummy(Node::IntLiteral(1))),
400 right: Box::new(dummy(Node::IntLiteral(2))),
401 })),
402 is_pub: false,
403 })];
404 let mut seen = Vec::new();
405
406 walk_program(&program, &mut |node| {
407 seen.push(match &node.node {
408 Node::LetBinding { .. } => "let",
409 Node::BinaryOp { .. } => "binary",
410 Node::IntLiteral(1) => "one",
411 Node::IntLiteral(2) => "two",
412 other => panic!("unexpected node {other:?}"),
413 });
414 });
415
416 assert_eq!(seen, vec!["let", "binary", "one", "two"]);
417 }
418
419 #[test]
420 fn walk_node_handles_deep_unary_chain_iteratively() {
421 let mut node = dummy(Node::IntLiteral(0));
422 for _ in 0..10_000 {
423 node = dummy(Node::UnaryOp {
424 op: "!".to_string(),
425 operand: Box::new(node),
426 });
427 }
428
429 let mut count = 0usize;
430 walk_node(&node, &mut |_| count += 1);
431
432 assert_eq!(count, 10_001);
433 }
434
435 #[test]
436 fn walk_node_visits_typed_param_defaults() {
437 let default = dummy(Node::Identifier("fallback".to_string()));
438 let node = dummy(Node::FnDecl {
439 name: "load".to_string(),
440 type_params: Vec::new(),
441 params: vec![TypedParam {
442 name: "root".to_string(),
443 type_expr: None,
444 default_value: Some(Box::new(default)),
445 rest: false,
446 }],
447 return_type: None,
448 throws: None,
449 where_clauses: Vec::new(),
450 body: Vec::new(),
451 is_pub: false,
452 is_stream: false,
453 });
454 let mut seen = Vec::new();
455
456 walk_node(&node, &mut |node| {
457 if let Node::Identifier(name) = &node.node {
458 seen.push(name.clone());
459 }
460 });
461
462 assert_eq!(seen, vec!["fallback"]);
463 }
464}