use crate::ast_types::*;
pub trait AstVisitor {
fn visit_function(&mut self, _func: &FunctionDefinition, _contract: &str) {}
fn visit_function_call(
&mut self,
_call: &FunctionCall,
_containing_function: &FunctionDefinition,
_contract: &str,
) {
}
fn visit_assignment(
&mut self,
_assign: &Assignment,
_containing_function: &FunctionDefinition,
_contract: &str,
) {
}
fn visit_binary_op(
&mut self,
_op: &BinaryOperation,
_containing_function: &FunctionDefinition,
_contract: &str,
) {
}
fn visit_state_variable(&mut self, _var: &VariableDeclaration, _contract: &str) {}
fn leave_function(
&mut self,
_func: &FunctionDefinition,
_statements: &[AstNode],
_contract: &str,
) {
}
fn visit_member_access(&mut self, _access: &MemberAccess, _contract: &str) {}
fn visit_identifier(&mut self, _id: &Identifier, _contract: &str) {}
}
pub struct AstWalker<'a> {
visitor: &'a mut dyn AstVisitor,
}
impl<'a> AstWalker<'a> {
pub fn new(visitor: &'a mut dyn AstVisitor) -> Self {
Self { visitor }
}
pub fn walk_source_unit(&mut self, unit: &SourceUnit) {
for node in &unit.nodes {
self.walk_node(node, None, "");
}
}
fn walk_node(
&mut self,
node: &AstNode,
current_func: Option<&FunctionDefinition>,
current_contract: &str,
) {
match node {
AstNode::ContractDefinition(contract) => {
self.walk_contract(contract);
}
AstNode::FunctionDefinition(func) => {
self.visitor.visit_function(func, current_contract);
if let Some(body) = &func.body {
for stmt in &body.statements {
self.walk_node(stmt, Some(func), current_contract);
}
self.visitor
.leave_function(func, &body.statements, current_contract);
}
}
AstNode::FunctionCall(call) => {
if let Some(func) = current_func {
self.visitor
.visit_function_call(call, func, current_contract);
}
for arg in &call.arguments {
self.walk_node(arg, current_func, current_contract);
}
self.walk_node(&call.expression, current_func, current_contract);
}
AstNode::Assignment(assign) => {
if let Some(func) = current_func {
self.visitor
.visit_assignment(assign, func, current_contract);
}
self.walk_node(&assign.left_hand_side, current_func, current_contract);
self.walk_node(&assign.right_hand_side, current_func, current_contract);
}
AstNode::BinaryOperation(op) => {
if let Some(func) = current_func {
self.visitor.visit_binary_op(op, func, current_contract);
}
self.walk_node(&op.left_expression, current_func, current_contract);
self.walk_node(&op.right_expression, current_func, current_contract);
}
AstNode::Block(block) => {
for stmt in &block.statements {
self.walk_node(stmt, current_func, current_contract);
}
}
AstNode::ExpressionStatement(expr) => {
if let Some(expression) = &expr.expression {
self.walk_node(expression, current_func, current_contract);
}
}
AstNode::IfStatement(if_stmt) => {
self.walk_node(&if_stmt.condition, current_func, current_contract);
self.walk_node(&if_stmt.true_body, current_func, current_contract);
if let Some(false_body) = &if_stmt.false_body {
self.walk_node(false_body, current_func, current_contract);
}
}
AstNode::StateVariableDeclaration(decl) => {
for var in &decl.variables {
self.visitor.visit_state_variable(var, current_contract);
}
}
AstNode::MemberAccess(access) => {
self.visitor.visit_member_access(access, current_contract);
self.walk_node(&access.expression, current_func, current_contract);
}
AstNode::Identifier(id) => {
self.visitor.visit_identifier(id, current_contract);
}
AstNode::IndexAccess(idx) => {
self.walk_node(&idx.base_expression, current_func, current_contract);
if let Some(index) = &idx.index_expression {
self.walk_node(index, current_func, current_contract);
}
}
AstNode::ForStatement(_) | AstNode::WhileStatement(_) => {
}
_ => {}
}
}
fn walk_contract(&mut self, contract: &ContractDefinition) {
for node in &contract.nodes {
self.walk_node(node, None, &contract.name);
}
}
}