inside_baseball/script/
visit.rs

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            // Temporarily take exprs, then put it back
173            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            // Temporarily take args, then put it back
214            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}