use crate::{Argument, Expr, Program, Stmt};
pub trait Visitor {
fn visit_program(&mut self, program: &Program) {
walk_program(self, program);
}
fn visit_stmt(&mut self, stmt: &Stmt) {
walk_stmt(self, stmt);
}
fn visit_expr(&mut self, expr: &Expr) {
walk_expr(self, expr);
}
}
pub fn walk_program<V: Visitor + ?Sized>(v: &mut V, program: &Program) {
for stmt in &program.statements {
v.visit_stmt(stmt);
}
}
pub fn walk_block<V: Visitor + ?Sized>(v: &mut V, body: &[Stmt]) {
for stmt in body {
v.visit_stmt(stmt);
}
}
pub fn walk_stmt<V: Visitor + ?Sized>(v: &mut V, stmt: &Stmt) {
match stmt {
Stmt::VarDecl { initializer, .. } => {
if let Some(init) = initializer {
v.visit_expr(init);
}
}
Stmt::Assignment { target, value } => {
v.visit_expr(target);
v.visit_expr(value);
}
Stmt::TupleAssignment { value, .. } => {
v.visit_expr(value);
}
Stmt::Expression(expr) => {
v.visit_expr(expr);
}
Stmt::If {
condition,
then_branch,
else_if_branches,
else_branch,
} => {
v.visit_expr(condition);
walk_block(v, then_branch);
for (cond, body) in else_if_branches {
v.visit_expr(cond);
walk_block(v, body);
}
if let Some(body) = else_branch {
walk_block(v, body);
}
}
Stmt::For { from, to, body, .. } => {
v.visit_expr(from);
v.visit_expr(to);
walk_block(v, body);
}
Stmt::ForIn {
collection, body, ..
} => {
v.visit_expr(collection);
walk_block(v, body);
}
Stmt::While { condition, body } => {
v.visit_expr(condition);
walk_block(v, body);
}
Stmt::FunctionDecl { body, .. } | Stmt::MethodDecl { body, .. } => {
walk_block(v, body);
}
Stmt::Break
| Stmt::Continue
| Stmt::TypeDecl { .. }
| Stmt::EnumDecl { .. }
| Stmt::Export { .. }
| Stmt::Import { .. } => {}
}
}
pub fn walk_expr<V: Visitor + ?Sized>(v: &mut V, expr: &Expr) {
match expr {
Expr::Binary { left, right, .. } => {
v.visit_expr(left);
v.visit_expr(right);
}
Expr::Unary { expr, .. } => {
v.visit_expr(expr);
}
Expr::Call { callee, args, .. } => {
v.visit_expr(callee);
for arg in args {
match arg {
Argument::Positional(e) => v.visit_expr(e),
Argument::Named { value, .. } => v.visit_expr(value),
}
}
}
Expr::Index { expr, index } => {
v.visit_expr(expr);
v.visit_expr(index);
}
Expr::MemberAccess { object, .. } => {
v.visit_expr(object);
}
Expr::Ternary {
condition,
then_expr,
else_expr,
} => {
v.visit_expr(condition);
v.visit_expr(then_expr);
v.visit_expr(else_expr);
}
Expr::Function { body, .. } => {
walk_block(v, body);
}
Expr::Array(elements) => {
for e in elements {
v.visit_expr(e);
}
}
Expr::Switch { value, cases } => {
v.visit_expr(value);
for (pattern, result) in cases {
v.visit_expr(pattern);
v.visit_expr(result);
}
}
Expr::IfExpr {
condition,
then_expr,
else_if_branches,
else_expr,
} => {
v.visit_expr(condition);
v.visit_expr(then_expr);
for (cond, e) in else_if_branches {
v.visit_expr(cond);
v.visit_expr(e);
}
if let Some(e) = else_expr {
v.visit_expr(e);
}
}
Expr::Literal(_) | Expr::Variable(_) => {}
}
}