use crate::syntax::ast::{Expr, ExprKind, IndexArg};
use crate::syntax::phase::Phase;
pub(crate) trait ExprVisitor<P: Phase> {
type Error;
fn visit_expr(&mut self, expr: &Expr<P>) -> Result<(), Self::Error> {
self.dispatch(expr)
}
fn dispatch(&mut self, expr: &Expr<P>) -> Result<(), Self::Error> {
crate::stack::with_stack_growth(|| self.dispatch_inner(expr))
}
fn dispatch_inner(&mut self, expr: &Expr<P>) -> Result<(), Self::Error> {
match &expr.kind {
ExprKind::Number(_)
| ExprKind::Integer(_)
| ExprKind::Bool(_)
| ExprKind::StringLiteral(_)
| ExprKind::UnitLiteral { .. } => self.visit_leaf(expr),
ExprKind::UnresolvedRef(_) => self.visit_unresolved_ref(expr),
ExprKind::GraphRef(_) => self.visit_graph_ref(expr),
ExprKind::InlineDagRef { args, .. } => self.visit_inline_dag_ref(expr, args),
ExprKind::FnCall { args, .. } => self.visit_fn_call(expr, args),
ExprKind::BinOp { lhs, rhs, .. } => self.visit_bin_op(expr, lhs, rhs),
ExprKind::UnaryOp { operand, .. } => self.visit_unary_op(expr, operand),
ExprKind::If {
condition,
then_branch,
else_branch,
} => self.visit_if(expr, condition, then_branch, else_branch),
ExprKind::Convert { expr: inner, .. }
| ExprKind::DisplayTimezone { expr: inner, .. }
| ExprKind::FieldAccess { expr: inner, .. } => self.visit_single_child(expr, inner),
ExprKind::IndexAccess {
expr: inner, args, ..
} => {
self.visit_single_child(expr, inner)?;
for arg in args {
if let IndexArg::Expr(e) = arg {
self.visit_expr(e)?;
}
}
Ok(())
}
ExprKind::ConstructorCall { fields, .. } => self.visit_constructor_call(expr, fields),
ExprKind::MapLiteral { entries } => self.visit_map_entries(expr, entries),
ExprKind::ForComp { body, .. } => self.visit_expr(body),
ExprKind::Scan {
source, init, body, ..
} => self.visit_scan(expr, source, init, body),
ExprKind::Unfold { init, body, .. } => self.visit_unfold(expr, init, body),
ExprKind::Match { scrutinee, arms } => self.visit_match(expr, scrutinee, arms),
ExprKind::Sugar(_) => self.visit_sugar(expr),
}
}
fn visit_leaf(&mut self, _expr: &Expr<P>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_sugar(&mut self, _expr: &Expr<P>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_graph_ref(&mut self, _expr: &Expr<P>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_unresolved_ref(&mut self, _expr: &Expr<P>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_inline_dag_ref(
&mut self,
_expr: &Expr<P>,
args: &[crate::syntax::ast::ParamBinding<P>],
) -> Result<(), Self::Error> {
for arg in args {
self.visit_expr(&arg.value)?;
}
Ok(())
}
fn visit_fn_call(&mut self, _expr: &Expr<P>, args: &[Expr<P>]) -> Result<(), Self::Error> {
for arg in args {
self.visit_expr(arg)?;
}
Ok(())
}
fn visit_bin_op(
&mut self,
_expr: &Expr<P>,
lhs: &Expr<P>,
rhs: &Expr<P>,
) -> Result<(), Self::Error> {
self.visit_expr(lhs)?;
self.visit_expr(rhs)
}
fn visit_unary_op(&mut self, _expr: &Expr<P>, operand: &Expr<P>) -> Result<(), Self::Error> {
self.visit_expr(operand)
}
fn visit_if(
&mut self,
_expr: &Expr<P>,
condition: &Expr<P>,
then_branch: &Expr<P>,
else_branch: &Expr<P>,
) -> Result<(), Self::Error> {
self.visit_expr(condition)?;
self.visit_expr(then_branch)?;
self.visit_expr(else_branch)
}
fn visit_single_child(&mut self, _expr: &Expr<P>, inner: &Expr<P>) -> Result<(), Self::Error> {
self.visit_expr(inner)
}
fn visit_constructor_call(
&mut self,
_expr: &Expr<P>,
fields: &[crate::syntax::ast::FieldInit<P>],
) -> Result<(), Self::Error> {
for field in fields {
self.visit_expr(&field.value)?;
}
Ok(())
}
fn visit_map_entries(
&mut self,
_expr: &Expr<P>,
entries: &[crate::syntax::ast::MapEntry<P>],
) -> Result<(), Self::Error> {
for entry in entries {
self.visit_expr(&entry.value)?;
}
Ok(())
}
fn visit_scan(
&mut self,
_expr: &Expr<P>,
source: &Expr<P>,
init: &Expr<P>,
body: &Expr<P>,
) -> Result<(), Self::Error> {
self.visit_expr(source)?;
self.visit_expr(init)?;
self.visit_expr(body)
}
fn visit_unfold(
&mut self,
_expr: &Expr<P>,
init: &Expr<P>,
body: &Expr<P>,
) -> Result<(), Self::Error> {
self.visit_expr(init)?;
self.visit_expr(body)
}
fn visit_match(
&mut self,
_expr: &Expr<P>,
scrutinee: &Expr<P>,
arms: &[crate::syntax::ast::MatchArm<P>],
) -> Result<(), Self::Error> {
self.visit_expr(scrutinee)?;
for arm in arms {
self.visit_expr(&arm.body)?;
}
Ok(())
}
}
pub trait ExprVisitorMut<P: Phase> {
type Error;
fn visit_expr_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
self.dispatch_mut(expr)
}
fn dispatch_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
crate::stack::with_stack_growth(|| self.dispatch_mut_inner(expr))
}
fn dispatch_mut_inner(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
match &mut expr.kind {
ExprKind::Number(_)
| ExprKind::Integer(_)
| ExprKind::Bool(_)
| ExprKind::StringLiteral(_)
| ExprKind::UnitLiteral { .. } => Ok(()),
ExprKind::UnresolvedRef(_) => self.visit_unresolved_ref_mut(expr),
ExprKind::GraphRef(_) => self.visit_graph_ref_mut(expr),
ExprKind::InlineDagRef { .. } => self.visit_inline_dag_ref_mut(expr),
ExprKind::FnCall { .. } => self.visit_fn_call_mut(expr),
ExprKind::BinOp { lhs, rhs, .. } => {
self.visit_expr_mut(lhs)?;
self.visit_expr_mut(rhs)
}
ExprKind::UnaryOp { operand, .. } => self.visit_expr_mut(operand),
ExprKind::If {
condition,
then_branch,
else_branch,
} => {
self.visit_expr_mut(condition)?;
self.visit_expr_mut(then_branch)?;
self.visit_expr_mut(else_branch)
}
ExprKind::Convert { expr: inner, .. }
| ExprKind::DisplayTimezone { expr: inner, .. }
| ExprKind::FieldAccess { expr: inner, .. } => self.visit_expr_mut(inner),
ExprKind::IndexAccess { .. } => self.visit_index_access_mut(expr),
ExprKind::ConstructorCall { fields, .. } => {
for field in fields {
self.visit_expr_mut(&mut field.value)?;
}
Ok(())
}
ExprKind::MapLiteral { .. } => self.visit_map_literal_mut(expr),
ExprKind::Sugar(_) => self.visit_sugar_mut(expr),
ExprKind::ForComp { .. } => self.visit_for_comp_mut(expr),
ExprKind::Scan {
source, init, body, ..
} => {
self.visit_expr_mut(source)?;
self.visit_expr_mut(init)?;
self.visit_expr_mut(body)
}
ExprKind::Unfold { init, body, .. } => {
self.visit_expr_mut(init)?;
self.visit_expr_mut(body)
}
ExprKind::Match { .. } => self.visit_match_mut(expr),
}
}
fn visit_graph_ref_mut(&mut self, _expr: &mut Expr<P>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_unresolved_ref_mut(&mut self, _expr: &mut Expr<P>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_fn_call_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
if let ExprKind::FnCall { args, .. } = &mut expr.kind {
for arg in args {
self.visit_expr_mut(arg)?;
}
}
Ok(())
}
fn visit_inline_dag_ref_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
if let ExprKind::InlineDagRef { args, .. } = &mut expr.kind {
for arg in args {
self.visit_expr_mut(&mut arg.value)?;
}
}
Ok(())
}
fn visit_for_comp_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
if let ExprKind::ForComp { body, .. } = &mut expr.kind {
self.visit_expr_mut(body)?;
}
Ok(())
}
fn visit_index_access_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
if let ExprKind::IndexAccess {
expr: inner, args, ..
} = &mut expr.kind
{
self.visit_expr_mut(inner)?;
for arg in args {
if let IndexArg::Expr(e) = arg {
self.visit_expr_mut(e)?;
}
}
}
Ok(())
}
fn visit_map_literal_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
if let ExprKind::MapLiteral { entries } = &mut expr.kind {
for entry in entries {
self.visit_expr_mut(&mut entry.value)?;
}
}
Ok(())
}
fn visit_sugar_mut(&mut self, _expr: &mut Expr<P>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_match_mut(&mut self, expr: &mut Expr<P>) -> Result<(), Self::Error> {
if let ExprKind::Match { scrutinee, arms } = &mut expr.kind {
self.visit_expr_mut(scrutinee)?;
for arm in arms {
self.visit_expr_mut(&mut arm.body)?;
}
}
Ok(())
}
}