Mutable visitor

This commit is contained in:
Laurenz 2021-06-29 10:54:34 +02:00
parent 8ea05739af
commit 6d0911d7a8
2 changed files with 132 additions and 95 deletions

View File

@ -1,7 +1,7 @@
use std::rc::Rc; use std::rc::Rc;
use super::{Scope, Scopes, Value}; use super::{Scope, Scopes, Value};
use crate::syntax::visit::{visit_expr, Visit}; use crate::syntax::visit::{immutable::visit_expr, Visit};
use crate::syntax::{Expr, Ident}; use crate::syntax::{Expr, Ident};
/// A visitor that captures variable slots. /// A visitor that captures variable slots.

View File

@ -1,54 +1,91 @@
//! Syntax tree traversal. //! Mutable and immutable syntax tree traversal.
use super::*; use crate::syntax::*;
macro_rules! visit { /// Implement the immutable and the mutable visitor version.
($(fn $name:ident($v:ident $(, $node:ident: &$ty:ty)?) $body:block)*) => { macro_rules! impl_visitors {
/// Traverses the syntax tree. ($($name:ident($($tts:tt)*) $body:block)*) => {
pub trait Visit<'ast> { macro_rules! r {
$(fn $name(&mut self $(, $node: &'ast $ty)?) { (rc: $x:expr) => { $x.as_ref() };
$name(self, $($node)?); ($x:expr) => { &$x };
})* }
impl_visitor! {
/// Walk syntax trees immutably.
Visit,
/// Immutable visitor functions.
immutable,
[$(($name($($tts)*) $body))*]
}
macro_rules! r {
(rc: $x:expr) => { std::rc::Rc::make_mut(&mut $x) };
($x:expr) => { &mut $x };
}
impl_visitor! {
/// Walk syntax trees mutably.
VisitMut,
/// Mutable visitor functions.
mutable,
[$(($name($($tts)*) $body mut))*] mut
}
};
}
/// Implement an immutable or mutable visitor.
macro_rules! impl_visitor {
(
#[doc = $visit_doc:expr] $visit:ident,
#[doc = $module_doc:expr] $module:ident,
[$((
$name:ident($v:ident, $node:ident: $ty:ty)
$body:block
$($fmut:tt)?
))*]
$($mut:tt)?
) => {
#[doc = $visit_doc]
pub trait $visit<'ast> {
/// Visit a definition of a binding. /// Visit a definition of a binding.
/// ///
/// Bindings are, for example, left-hand side of let expressions, /// Bindings are, for example, left-hand side of let expressions,
/// and key/value patterns in for loops. /// and key/value patterns in for loops.
fn visit_binding(&mut self, _: &'ast Ident) {} fn visit_binding(&mut self, _: &'ast $($mut)? Ident) {}
/// Visit the entry into a scope. /// Visit the entry into a scope.
fn visit_enter(&mut self) {} fn visit_enter(&mut self) {}
/// Visit the exit from a scope. /// Visit the exit from a scope.
fn visit_exit(&mut self) {} fn visit_exit(&mut self) {}
$(fn $name(&mut self, $node: &'ast $($fmut)? $ty) {
$module::$name(self, $node);
})*
} }
$(visit! { #[doc = $module_doc]
@$(concat!("Walk a node of type [`", stringify!($ty), "`]."), )? pub mod $module {
pub fn $name<'ast, V>( use super::*;
#[allow(unused)] $v: &mut V $(
$(, #[allow(unused)] $node: &'ast $ty)? #[allow(unused_variables)]
) pub fn $name<'ast, V>($v: &mut V, $node: &'ast $($fmut)? $ty)
where where
V: Visit<'ast> + ?Sized V: $visit<'ast> + ?Sized
$body $body
})* )*
}; }
(@$doc:expr, $($tts:tt)*) => {
#[doc = $doc]
$($tts)*
}; };
} }
visit! { impl_visitors! {
fn visit_tree(v, node: &Tree) { visit_tree(v, tree: Tree) {
for node in node { for node in tree {
v.visit_node(&node); v.visit_node(node);
} }
} }
fn visit_node(v, node: &Node) { visit_node(v, node: Node) {
match node { match node {
Node::Text(_) => {} Node::Text(_) => {}
Node::Space => {} Node::Space => {}
@ -64,20 +101,20 @@ visit! {
} }
} }
fn visit_heading(v, node: &HeadingNode) { visit_heading(v, heading: HeadingNode) {
v.visit_tree(&node.body); v.visit_tree(r!(rc: heading.body));
} }
fn visit_list(v, node: &ListItem) { visit_list(v, item: ListItem) {
v.visit_tree(&node.body); v.visit_tree(r!(item.body));
} }
fn visit_enum(v, node: &EnumItem) { visit_enum(v, item: EnumItem) {
v.visit_tree(&node.body); v.visit_tree(r!(item.body));
} }
fn visit_expr(v, node: &Expr) { visit_expr(v, expr: Expr) {
match node { match expr {
Expr::None(_) => {} Expr::None(_) => {}
Expr::Auto(_) => {} Expr::Auto(_) => {}
Expr::Bool(_, _) => {} Expr::Bool(_, _) => {}
@ -109,121 +146,121 @@ visit! {
} }
} }
fn visit_array(v, node: &ArrayExpr) { visit_array(v, array: ArrayExpr) {
for expr in &node.items { for expr in r!(array.items) {
v.visit_expr(&expr); v.visit_expr(expr);
} }
} }
fn visit_dict(v, node: &DictExpr) { visit_dict(v, dict: DictExpr) {
for named in &node.items { for named in r!(dict.items) {
v.visit_expr(&named.expr); v.visit_expr(r!(named.expr));
} }
} }
fn visit_template(v, node: &TemplateExpr) { visit_template(v, template: TemplateExpr) {
v.visit_enter(); v.visit_enter();
v.visit_tree(&node.tree); v.visit_tree(r!(rc: template.tree));
v.visit_exit(); v.visit_exit();
} }
fn visit_group(v, node: &GroupExpr) { visit_group(v, group: GroupExpr) {
v.visit_expr(&node.expr); v.visit_expr(r!(group.expr));
} }
fn visit_block(v, node: &BlockExpr) { visit_block(v, block: BlockExpr) {
if node.scoping { if block.scoping {
v.visit_enter(); v.visit_enter();
} }
for expr in &node.exprs { for expr in r!(block.exprs) {
v.visit_expr(&expr); v.visit_expr(expr);
} }
if node.scoping { if block.scoping {
v.visit_exit(); v.visit_exit();
} }
} }
fn visit_binary(v, node: &BinaryExpr) { visit_binary(v, binary: BinaryExpr) {
v.visit_expr(&node.lhs); v.visit_expr(r!(binary.lhs));
v.visit_expr(&node.rhs); v.visit_expr(r!(binary.rhs));
} }
fn visit_unary(v, node: &UnaryExpr) { visit_unary(v, unary: UnaryExpr) {
v.visit_expr(&node.expr); v.visit_expr(r!(unary.expr));
} }
fn visit_call(v, node: &CallExpr) { visit_call(v, call: CallExpr) {
v.visit_expr(&node.callee); v.visit_expr(r!(call.callee));
v.visit_args(&node.args); v.visit_args(r!(call.args));
} }
fn visit_closure(v, node: &ClosureExpr) { visit_closure(v, closure: ClosureExpr) {
for param in node.params.iter() { for param in r!(rc: closure.params) {
v.visit_binding(param); v.visit_binding(param);
} }
v.visit_expr(&node.body); v.visit_expr(r!(rc: closure.body));
} }
fn visit_args(v, node: &CallArgs) { visit_args(v, args: CallArgs) {
for arg in &node.items { for arg in r!(args.items) {
v.visit_arg(arg); v.visit_arg(arg);
} }
} }
fn visit_arg(v, node: &CallArg) { visit_arg(v, arg: CallArg) {
match node { match arg {
CallArg::Pos(expr) => v.visit_expr(&expr), CallArg::Pos(expr) => v.visit_expr(expr),
CallArg::Named(named) => v.visit_expr(&named.expr), CallArg::Named(named) => v.visit_expr(r!(named.expr)),
} }
} }
fn visit_with(v, node: &WithExpr) { visit_with(v, with_expr: WithExpr) {
v.visit_expr(&node.callee); v.visit_expr(r!(with_expr.callee));
v.visit_args(&node.args); v.visit_args(r!(with_expr.args));
} }
fn visit_let(v, node: &LetExpr) { visit_let(v, let_expr: LetExpr) {
if let Some(init) = &node.init { if let Some(init) = r!(let_expr.init) {
v.visit_expr(&init); v.visit_expr(init);
} }
v.visit_binding(&node.binding); v.visit_binding(r!(let_expr.binding));
} }
fn visit_if(v, node: &IfExpr) { visit_if(v, if_expr: IfExpr) {
v.visit_expr(&node.condition); v.visit_expr(r!(if_expr.condition));
v.visit_expr(&node.if_body); v.visit_expr(r!(if_expr.if_body));
if let Some(body) = &node.else_body { if let Some(body) = r!(if_expr.else_body) {
v.visit_expr(&body); v.visit_expr(body);
} }
} }
fn visit_while(v, node: &WhileExpr) { visit_while(v, while_expr: WhileExpr) {
v.visit_expr(&node.condition); v.visit_expr(r!(while_expr.condition));
v.visit_expr(&node.body); v.visit_expr(r!(while_expr.body));
} }
fn visit_for(v, node: &ForExpr) { visit_for(v, for_expr: ForExpr) {
v.visit_expr(&node.iter); v.visit_expr(r!(for_expr.iter));
match &node.pattern { match r!(for_expr.pattern) {
ForPattern::Value(value) => v.visit_binding(value), ForPattern::Value(value) => v.visit_binding(value),
ForPattern::KeyValue(key, value) => { ForPattern::KeyValue(key, value) => {
v.visit_binding(key); v.visit_binding(key);
v.visit_binding(value); v.visit_binding(value);
} }
} }
v.visit_expr(&node.body); v.visit_expr(r!(for_expr.body));
} }
fn visit_import(v, node: &ImportExpr) { visit_import(v, import_expr: ImportExpr) {
v.visit_expr(&node.path); v.visit_expr(r!(import_expr.path));
if let Imports::Idents(idents) = &node.imports { if let Imports::Idents(idents) = r!(import_expr.imports) {
for ident in idents { for ident in idents {
v.visit_binding(ident); v.visit_binding(ident);
} }
} }
} }
fn visit_include(v, node: &IncludeExpr) { visit_include(v, include_expr: IncludeExpr) {
v.visit_expr(&node.path); v.visit_expr(r!(include_expr.path));
} }
} }