//! Syntax tree traversal. use super::*; macro_rules! visit { ($(fn $name:ident($v:ident, $item:ident: &$ty:ty) $body:block)*) => { /// Traverses the syntax tree. pub trait Visit<'ast> { $(fn $name(&mut self, $item: &'ast $ty) { $name(self, $item); })* } $(visit! { @concat!("Walk a node of type [`", stringify!($ty), "`]."), pub fn $name<'ast, V>($v: &mut V, $item: &'ast $ty) where V: Visit<'ast> + ?Sized $body })* }; (@$doc:expr, $($tts:tt)*) => { #[doc = $doc] $($tts)* } } visit! { fn visit_tree(v, item: &Tree) { for node in item { v.visit_node(&node.v); } } fn visit_node(v, item: &Node) { match item { Node::Strong => {} Node::Emph => {} Node::Space => {} Node::Linebreak => {} Node::Parbreak => {} Node::Text(_) => {} Node::Heading(n) => v.visit_tree(&n.contents), Node::Raw(_) => {} Node::Expr(expr) => v.visit_expr(expr), } } fn visit_expr(v, item: &Expr) { match item { Expr::None => {} Expr::Ident(_) => {} Expr::Bool(_) => {} Expr::Int(_) => {} Expr::Float(_) => {} Expr::Length(_, _) => {} Expr::Angle(_, _) => {} Expr::Percent(_) => {} Expr::Color(_) => {} Expr::Str(_) => {} Expr::Array(e) => v.visit_array(e), Expr::Dict(e) => v.visit_dict(e), Expr::Template(e) => v.visit_template(e), Expr::Group(e) => v.visit_group(e), Expr::Block(e) => v.visit_block(e), Expr::Unary(e) => v.visit_unary(e), Expr::Binary(e) => v.visit_binary(e), Expr::Call(e) => v.visit_call(e), Expr::Let(e) => v.visit_let(e), Expr::If(e) => v.visit_if(e), Expr::For(e) => v.visit_for(e), } } fn visit_array(v, item: &ExprArray) { for expr in item { v.visit_expr(&expr.v); } } fn visit_dict(v, item: &ExprDict) { for named in item { v.visit_expr(&named.expr.v); } } fn visit_template(v, item: &ExprTemplate) { v.visit_tree(item); } fn visit_group(v, item: &ExprGroup) { v.visit_expr(&item.v); } fn visit_block(v, item: &ExprBlock) { for expr in &item.exprs { v.visit_expr(&expr.v); } } fn visit_binary(v, item: &ExprBinary) { v.visit_expr(&item.lhs.v); v.visit_expr(&item.rhs.v); } fn visit_unary(v, item: &ExprUnary) { v.visit_expr(&item.expr.v); } fn visit_call(v, item: &ExprCall) { v.visit_expr(&item.callee.v); v.visit_args(&item.args.v); } fn visit_args(v, item: &ExprArgs) { for arg in item { v.visit_arg(arg); } } fn visit_arg(v, item: &Argument) { match item { Argument::Pos(expr) => v.visit_expr(&expr.v), Argument::Named(named) => v.visit_expr(&named.expr.v), } } fn visit_let(v, item: &ExprLet) { if let Some(init) = &item.init { v.visit_expr(&init.v); } } fn visit_if(v, item: &ExprIf) { v.visit_expr(&item.condition.v); v.visit_expr(&item.if_body.v); if let Some(body) = &item.else_body { v.visit_expr(&body.v); } } fn visit_for(v, item: &ExprFor) { v.visit_expr(&item.iter.v); v.visit_expr(&item.body.v); } }