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