use crate::ast::common::{
AstBlock, AstCallKind, AstExpr, AstFunctionExpr, AstLValue, AstModule, AstStmt,
};
use super::traverse::BlockKind;
use crate::ast::traverse::{
traverse_call_children, traverse_expr_children, traverse_lvalue_children,
traverse_stmt_children,
};
pub(super) trait AstVisitor {
fn visit_block(&mut self, _block: &AstBlock, _kind: BlockKind) {}
fn visit_stmt(&mut self, _stmt: &AstStmt) {}
fn visit_expr(&mut self, _expr: &AstExpr) {}
fn visit_lvalue(&mut self, _lvalue: &AstLValue) {}
fn visit_call(&mut self, _call: &AstCallKind) {}
fn visit_function_expr(&mut self, _function: &AstFunctionExpr) -> bool {
true
}
fn leave_function_expr(&mut self, _function: &AstFunctionExpr) {}
fn visit_condition_expr(&mut self, expr: &AstExpr) {
self.visit_expr(expr);
}
}
pub(super) fn visit_module(module: &AstModule, visitor: &mut impl AstVisitor) {
visit_block_with_kind(&module.body, BlockKind::ModuleBody, visitor);
}
pub(super) fn visit_block(block: &AstBlock, visitor: &mut impl AstVisitor) {
visit_block_with_kind(block, BlockKind::Regular, visitor);
}
pub(super) fn visit_stmt(stmt: &AstStmt, visitor: &mut impl AstVisitor) {
visit_stmt_impl(stmt, visitor);
}
fn visit_block_with_kind(block: &AstBlock, kind: BlockKind, visitor: &mut impl AstVisitor) {
visitor.visit_block(block, kind);
for stmt in &block.stmts {
visit_stmt_impl(stmt, visitor);
}
}
fn visit_stmt_impl(stmt: &AstStmt, visitor: &mut impl AstVisitor) {
visitor.visit_stmt(stmt);
traverse_stmt_children!(
stmt,
iter = iter,
opt = as_ref,
borrow = [&],
expr(expr) => {
visit_expr(expr, visitor);
},
lvalue(lvalue) => {
visit_lvalue(lvalue, visitor);
},
block(block) => {
visit_block_with_kind(block, BlockKind::Regular, visitor);
},
function(function) => {
visit_function_expr(function, BlockKind::FunctionBody, visitor);
},
condition(condition) => {
visit_condition_expr(condition, visitor);
},
call(call) => {
visit_call(call, visitor);
}
);
}
fn visit_call(call: &AstCallKind, visitor: &mut impl AstVisitor) {
visitor.visit_call(call);
traverse_call_children!(call, iter = iter, borrow = [&], expr(expr) => {
visit_expr(expr, visitor);
});
}
fn visit_lvalue(lvalue: &AstLValue, visitor: &mut impl AstVisitor) {
visitor.visit_lvalue(lvalue);
traverse_lvalue_children!(lvalue, borrow = [&], expr(expr) => {
visit_expr(expr, visitor);
});
}
fn visit_expr(expr: &AstExpr, visitor: &mut impl AstVisitor) {
visitor.visit_expr(expr);
traverse_expr_children!(
expr,
iter = iter,
borrow = [&],
expr(expr) => {
visit_expr(expr, visitor);
},
function(function) => {
visit_function_expr(function, BlockKind::FunctionBody, visitor);
}
);
}
fn visit_condition_expr(expr: &AstExpr, visitor: &mut impl AstVisitor) {
visitor.visit_condition_expr(expr);
visit_expr(expr, visitor);
}
fn visit_function_expr(function: &AstFunctionExpr, kind: BlockKind, visitor: &mut impl AstVisitor) {
if visitor.visit_function_expr(function) {
visit_block_with_kind(&function.body, kind, visitor);
}
visitor.leave_function_expr(function);
}