use crate::hir::common::{
HirBlock, HirCallExpr, HirDecisionExpr, HirExpr, HirLValue, HirProto, HirStmt,
HirTableConstructor,
};
use super::traverse::{
traverse_hir_call_children, traverse_hir_decision_children, traverse_hir_expr_children,
traverse_hir_lvalue_children, traverse_hir_stmt_children,
traverse_hir_table_constructor_children,
};
pub(super) trait HirRewritePass {
fn rewrite_block(&mut self, _block: &mut HirBlock) -> bool {
false
}
fn rewrite_stmt(&mut self, _stmt: &mut HirStmt) -> bool {
false
}
fn rewrite_expr(&mut self, _expr: &mut HirExpr) -> bool {
false
}
fn rewrite_lvalue(&mut self, _lvalue: &mut HirLValue) -> bool {
false
}
fn rewrite_call(&mut self, _call: &mut HirCallExpr) -> bool {
false
}
fn rewrite_condition_expr(&mut self, expr: &mut HirExpr) -> bool {
self.rewrite_expr(expr)
}
}
pub(super) fn rewrite_proto(proto: &mut HirProto, pass: &mut impl HirRewritePass) -> bool {
rewrite_block(&mut proto.body, pass)
}
pub(super) fn rewrite_stmts(stmts: &mut [HirStmt], pass: &mut impl HirRewritePass) -> bool {
stmts.iter_mut().fold(false, |changed, stmt| {
rewrite_stmt(stmt, pass) || changed
})
}
pub(super) trait ExprRewritePass {
fn rewrite_expr(&mut self, expr: &mut HirExpr) -> bool;
fn rewrite_condition_expr(&mut self, expr: &mut HirExpr) -> bool {
self.rewrite_expr(expr)
}
}
pub(super) fn rewrite_proto_exprs(proto: &mut HirProto, pass: &mut impl ExprRewritePass) -> bool {
let mut adapter = ExprRewritePassAdapter { pass };
rewrite_proto(proto, &mut adapter)
}
pub(super) fn for_each_nested_block_mut(stmt: &mut HirStmt, visit: &mut impl FnMut(&mut HirBlock)) {
match stmt {
HirStmt::If(if_stmt) => {
visit(&mut if_stmt.then_block);
if let Some(else_block) = &mut if_stmt.else_block {
visit(else_block);
}
}
HirStmt::While(while_stmt) => visit(&mut while_stmt.body),
HirStmt::Repeat(repeat_stmt) => visit(&mut repeat_stmt.body),
HirStmt::NumericFor(numeric_for) => visit(&mut numeric_for.body),
HirStmt::GenericFor(generic_for) => visit(&mut generic_for.body),
HirStmt::Block(block) => visit(block),
HirStmt::Unstructured(unstructured) => visit(&mut unstructured.body),
HirStmt::LocalDecl(_)
| HirStmt::Assign(_)
| HirStmt::TableSetList(_)
| HirStmt::ErrNil(_)
| HirStmt::ToBeClosed(_)
| HirStmt::Close(_)
| HirStmt::CallStmt(_)
| HirStmt::Return(_)
| HirStmt::Break
| HirStmt::Continue
| HirStmt::Goto(_)
| HirStmt::Label(_) => {}
}
}
pub(super) fn rewrite_nested_blocks_in_stmt(
stmt: &mut HirStmt,
rewrite_block: &mut impl FnMut(&mut HirBlock) -> bool,
) -> bool {
let mut changed = false;
for_each_nested_block_mut(stmt, &mut |block| {
changed |= rewrite_block(block);
});
changed
}
struct ExprRewritePassAdapter<'a, P> {
pass: &'a mut P,
}
impl<P: ExprRewritePass> HirRewritePass for ExprRewritePassAdapter<'_, P> {
fn rewrite_expr(&mut self, expr: &mut HirExpr) -> bool {
self.pass.rewrite_expr(expr)
}
fn rewrite_condition_expr(&mut self, expr: &mut HirExpr) -> bool {
self.pass.rewrite_condition_expr(expr)
}
}
fn rewrite_block(block: &mut HirBlock, pass: &mut impl HirRewritePass) -> bool {
let nested_changed = block
.stmts
.iter_mut()
.fold(false, |changed, stmt| rewrite_stmt(stmt, pass) || changed);
let block_changed = pass.rewrite_block(block);
block_changed || nested_changed
}
fn rewrite_stmt(stmt: &mut HirStmt, pass: &mut impl HirRewritePass) -> bool {
let mut nested_changed = false;
traverse_hir_stmt_children!(
stmt,
iter = iter_mut,
opt = as_mut,
borrow = [&mut],
expr(expr) => {
nested_changed |= rewrite_expr(expr, pass);
},
lvalue(lvalue) => {
nested_changed |= rewrite_lvalue(lvalue, pass);
},
block(block) => {
nested_changed |= rewrite_block(block, pass);
},
call(call) => {
nested_changed |= rewrite_call_expr(call, pass);
},
condition(cond) => {
nested_changed |= rewrite_condition_expr(cond, pass);
}
);
let stmt_changed = pass.rewrite_stmt(stmt);
stmt_changed || nested_changed
}
fn rewrite_lvalue(lvalue: &mut HirLValue, pass: &mut impl HirRewritePass) -> bool {
let mut nested_changed = false;
traverse_hir_lvalue_children!(lvalue, borrow = [&mut], expr(expr) => {
nested_changed |= rewrite_expr(expr, pass);
});
let lvalue_changed = pass.rewrite_lvalue(lvalue);
lvalue_changed || nested_changed
}
fn rewrite_call_expr(call: &mut HirCallExpr, pass: &mut impl HirRewritePass) -> bool {
let mut nested_changed = false;
traverse_hir_call_children!(call, iter = iter_mut, borrow = [&mut], expr(expr) => {
nested_changed |= rewrite_expr(expr, pass);
});
let call_changed = pass.rewrite_call(call);
call_changed || nested_changed
}
fn rewrite_expr(expr: &mut HirExpr, pass: &mut impl HirRewritePass) -> bool {
let mut nested_changed = false;
traverse_hir_expr_children!(
expr,
iter = iter_mut,
borrow = [&mut],
expr(e) => {
nested_changed |= rewrite_expr(e, pass);
},
call(c) => {
nested_changed |= rewrite_call_expr(c, pass);
},
decision(d) => {
nested_changed |= rewrite_decision_expr(d, pass);
},
table_constructor(t) => {
nested_changed |= rewrite_table_constructor(t, pass);
}
);
let expr_changed = pass.rewrite_expr(expr);
expr_changed || nested_changed
}
fn rewrite_decision_expr(
decision: &mut HirDecisionExpr,
pass: &mut impl HirRewritePass,
) -> bool {
let mut changed = false;
traverse_hir_decision_children!(
decision,
iter = iter_mut,
borrow = [&mut],
expr(e) => {
changed |= rewrite_expr(e, pass);
},
condition(cond) => {
changed |= rewrite_condition_expr(cond, pass);
}
);
changed
}
fn rewrite_table_constructor(
table: &mut HirTableConstructor,
pass: &mut impl HirRewritePass,
) -> bool {
let mut changed = false;
traverse_hir_table_constructor_children!(
table,
iter = iter_mut,
opt = as_mut,
borrow = [&mut],
expr(e) => {
changed |= rewrite_expr(e, pass);
}
);
changed
}
fn rewrite_condition_expr(expr: &mut HirExpr, pass: &mut impl HirRewritePass) -> bool {
let nested_changed = rewrite_expr(expr, pass);
pass.rewrite_condition_expr(expr) || nested_changed
}