1use crate::script::{
2 ast::{CaseCond, Expr, ExprId, Scripto, Stmt, StmtBlock},
3 ins::Variable,
4};
5use std::mem;
6
7pub trait Visitor: AsVisitor {
8 fn block(&mut self, script: &mut Scripto, block: &mut StmtBlock) {
9 default_visit_block(script, block, self.as_visit_mut());
10 }
11
12 fn stmt(&mut self, script: &mut Scripto, stmt: &mut Stmt) {
13 default_visit_stmt(script, stmt, self.as_visit_mut());
14 }
15
16 fn expr(&mut self, script: &mut Scripto, id: ExprId) {
17 default_visit_expr(script, id, self.as_visit_mut());
18 }
19
20 #[allow(unused_variables)]
21 fn var(&mut self, var: Variable) {}
22}
23
24pub trait AsVisitor {
25 fn as_visit_mut(&mut self) -> &mut dyn Visitor;
26}
27
28impl<V: Visitor> AsVisitor for V {
29 fn as_visit_mut(&mut self) -> &mut dyn Visitor {
30 self
31 }
32}
33
34fn default_visit_block(script: &mut Scripto, block: &mut StmtBlock, visit: &mut dyn Visitor) {
35 for s in &mut block.stmts {
36 visit.stmt(script, s);
37 }
38}
39
40#[allow(clippy::too_many_lines)]
41pub fn default_visit_stmt(script: &mut Scripto, stmt: &mut Stmt, visit: &mut dyn Visitor) {
42 #[allow(clippy::match_same_arms)]
43 match *stmt {
44 Stmt::DimArray {
45 var,
46 item_size: _,
47 min_y,
48 max_y,
49 min_x,
50 max_x,
51 swap,
52 } => {
53 visit.var(var);
54 visit.expr(script, min_y);
55 visit.expr(script, max_y);
56 visit.expr(script, min_x);
57 visit.expr(script, max_x);
58 visit.expr(script, swap);
59 }
60 Stmt::RedimArray {
61 var,
62 item_size: _,
63 min_y,
64 max_y,
65 min_x,
66 max_x,
67 } => {
68 visit.var(var);
69 visit.expr(script, min_y);
70 visit.expr(script, max_y);
71 visit.expr(script, min_x);
72 visit.expr(script, max_x);
73 }
74 Stmt::Assign(var, expr) => {
75 visit.var(var);
76 visit.expr(script, expr);
77 }
78 Stmt::SetArrayItem(var, x, value) => {
79 visit.var(var);
80 visit.expr(script, x);
81 visit.expr(script, value);
82 }
83 Stmt::SetArrayItem2D(var, y, x, value) => {
84 visit.var(var);
85 visit.expr(script, y);
86 visit.expr(script, x);
87 visit.expr(script, value);
88 }
89 Stmt::Inc(var) | Stmt::Dec(var) => {
90 visit.var(var);
91 }
92 Stmt::Label | Stmt::Goto(_) => {}
93 Stmt::If {
94 condition,
95 ref mut true_,
96 ref mut false_,
97 } => {
98 visit.expr(script, condition);
99 visit.block(script, true_);
100 visit.block(script, false_);
101 }
102 Stmt::While {
103 condition,
104 ref mut body,
105 } => {
106 visit.expr(script, condition);
107 visit.block(script, body);
108 }
109 Stmt::Do {
110 ref mut body,
111 condition,
112 ..
113 } => {
114 visit.block(script, body);
115 if let Some(condition) = condition {
116 visit.expr(script, condition);
117 }
118 }
119 Stmt::Case {
120 value,
121 ref mut cases,
122 } => {
123 visit.expr(script, value);
124 for case in cases {
125 match case.cond {
126 CaseCond::Eq(x) | CaseCond::In(x) => visit.expr(script, x),
127 CaseCond::Else => {}
128 }
129 visit.block(script, &mut case.body);
130 }
131 }
132 Stmt::For {
133 var,
134 start,
135 end,
136 step: _,
137 ref mut body,
138 } => {
139 visit.var(var);
140 visit.expr(script, start);
141 visit.expr(script, end);
142 visit.block(script, body);
143 }
144 Stmt::ForList {
145 var,
146 list,
147 ref mut body,
148 } => {
149 visit.var(var);
150 visit.expr(script, list);
151 visit.block(script, body);
152 }
153 Stmt::Generic {
154 bytecode: _,
155 ins: _,
156 ref args,
157 } => {
158 for &e in args {
159 visit.expr(script, e);
160 }
161 }
162 Stmt::DecompileError(_, _) => {}
163 }
164}
165
166pub fn default_visit_expr(script: &mut Scripto, id: ExprId, visit: &mut dyn Visitor) {
167 match script.exprs[id] {
168 Expr::Variable(var) => {
169 visit.var(var);
170 }
171 Expr::List(ref mut exprs) => {
172 let exprs = mem::take(exprs);
174 for &expr in &exprs {
175 visit.expr(script, expr);
176 }
177 match script.exprs[id] {
178 Expr::List(ref mut exprs_mut) => *exprs_mut = exprs,
179 _ => unreachable!(),
180 }
181 }
182 Expr::ArrayIndex(var, x) => {
183 visit.var(var);
184 visit.expr(script, x);
185 }
186 Expr::ArrayIndex2D(var, y, x) => {
187 visit.var(var);
188 visit.expr(script, y);
189 visit.expr(script, x);
190 }
191 Expr::StackDup(expr) | Expr::Not(expr) => {
192 visit.expr(script, expr);
193 }
194 Expr::Equal(lhs, rhs)
195 | Expr::NotEqual(lhs, rhs)
196 | Expr::Greater(lhs, rhs)
197 | Expr::Less(lhs, rhs)
198 | Expr::LessOrEqual(lhs, rhs)
199 | Expr::GreaterOrEqual(lhs, rhs)
200 | Expr::Add(lhs, rhs)
201 | Expr::Sub(lhs, rhs)
202 | Expr::Mul(lhs, rhs)
203 | Expr::Div(lhs, rhs)
204 | Expr::LogicalAnd(lhs, rhs)
205 | Expr::LogicalOr(lhs, rhs)
206 | Expr::BitwiseAnd(lhs, rhs)
207 | Expr::BitwiseOr(lhs, rhs)
208 | Expr::In(lhs, rhs) => {
209 visit.expr(script, lhs);
210 visit.expr(script, rhs);
211 }
212 Expr::Call(_, _, ref mut args) => {
213 let args = mem::take(args);
215 for &arg in &args {
216 visit.expr(script, arg);
217 }
218 match script.exprs[id] {
219 Expr::Call(_, _, ref mut args_mut) => *args_mut = args,
220 _ => unreachable!(),
221 }
222 }
223 Expr::Number(_)
224 | Expr::Char(_)
225 | Expr::String(_)
226 | Expr::Script(_)
227 | Expr::StackUnderflow
228 | Expr::EnumConst(_, _)
229 | Expr::DecompileError(_, _) => {}
230 }
231}