1use crate::{Argument, Expr, Program, Stmt};
15
16pub trait Visitor {
23 fn visit_program(&mut self, program: &Program) {
24 walk_program(self, program);
25 }
26
27 fn visit_stmt(&mut self, stmt: &Stmt) {
28 walk_stmt(self, stmt);
29 }
30
31 fn visit_expr(&mut self, expr: &Expr) {
32 walk_expr(self, expr);
33 }
34}
35
36pub fn walk_program<V: Visitor + ?Sized>(v: &mut V, program: &Program) {
37 for stmt in &program.statements {
38 v.visit_stmt(stmt);
39 }
40}
41
42pub fn walk_block<V: Visitor + ?Sized>(v: &mut V, body: &[Stmt]) {
43 for stmt in body {
44 v.visit_stmt(stmt);
45 }
46}
47
48pub fn walk_stmt<V: Visitor + ?Sized>(v: &mut V, stmt: &Stmt) {
49 match stmt {
50 Stmt::VarDecl { initializer, .. } => {
51 if let Some(init) = initializer {
52 v.visit_expr(init);
53 }
54 }
55 Stmt::Assignment { target, value } => {
56 v.visit_expr(target);
57 v.visit_expr(value);
58 }
59 Stmt::TupleAssignment { value, .. } => {
60 v.visit_expr(value);
61 }
62 Stmt::Expression(expr) => {
63 v.visit_expr(expr);
64 }
65 Stmt::If {
66 condition,
67 then_branch,
68 else_if_branches,
69 else_branch,
70 } => {
71 v.visit_expr(condition);
72 walk_block(v, then_branch);
73 for (cond, body) in else_if_branches {
74 v.visit_expr(cond);
75 walk_block(v, body);
76 }
77 if let Some(body) = else_branch {
78 walk_block(v, body);
79 }
80 }
81 Stmt::For { from, to, body, .. } => {
82 v.visit_expr(from);
83 v.visit_expr(to);
84 walk_block(v, body);
85 }
86 Stmt::ForIn {
87 collection, body, ..
88 } => {
89 v.visit_expr(collection);
90 walk_block(v, body);
91 }
92 Stmt::While { condition, body } => {
93 v.visit_expr(condition);
94 walk_block(v, body);
95 }
96 Stmt::FunctionDecl { body, .. } | Stmt::MethodDecl { body, .. } => {
97 walk_block(v, body);
98 }
99 Stmt::Break
101 | Stmt::Continue
102 | Stmt::TypeDecl { .. }
103 | Stmt::EnumDecl { .. }
104 | Stmt::Export { .. }
105 | Stmt::Import { .. } => {}
106 }
107}
108
109pub fn walk_expr<V: Visitor + ?Sized>(v: &mut V, expr: &Expr) {
110 match expr {
111 Expr::Binary { left, right, .. } => {
112 v.visit_expr(left);
113 v.visit_expr(right);
114 }
115 Expr::Unary { expr, .. } => {
116 v.visit_expr(expr);
117 }
118 Expr::Call { callee, args, .. } => {
119 v.visit_expr(callee);
120 for arg in args {
121 match arg {
122 Argument::Positional(e) => v.visit_expr(e),
123 Argument::Named { value, .. } => v.visit_expr(value),
124 }
125 }
126 }
127 Expr::Index { expr, index } => {
128 v.visit_expr(expr);
129 v.visit_expr(index);
130 }
131 Expr::MemberAccess { object, .. } => {
132 v.visit_expr(object);
133 }
134 Expr::Ternary {
135 condition,
136 then_expr,
137 else_expr,
138 } => {
139 v.visit_expr(condition);
140 v.visit_expr(then_expr);
141 v.visit_expr(else_expr);
142 }
143 Expr::Function { body, .. } => {
144 walk_block(v, body);
145 }
146 Expr::Array(elements) => {
147 for e in elements {
148 v.visit_expr(e);
149 }
150 }
151 Expr::Switch { value, cases } => {
152 v.visit_expr(value);
153 for (pattern, result) in cases {
154 v.visit_expr(pattern);
155 v.visit_expr(result);
156 }
157 }
158 Expr::IfExpr {
159 condition,
160 then_expr,
161 else_if_branches,
162 else_expr,
163 } => {
164 v.visit_expr(condition);
165 v.visit_expr(then_expr);
166 for (cond, e) in else_if_branches {
167 v.visit_expr(cond);
168 v.visit_expr(e);
169 }
170 if let Some(e) = else_expr {
171 v.visit_expr(e);
172 }
173 }
174 Expr::Literal(_) | Expr::Variable(_) => {}
176 }
177}