ast: further macro cleanup

This commit is contained in:
Peter Ammon
2025-05-03 19:05:22 -07:00
parent e9036774cb
commit 1f79d48a48

View File

@@ -39,6 +39,11 @@ pub trait NodeVisitor<'a> {
fn visit(&mut self, node: &'a dyn Node);
}
/**
* Acceptor is implemented on Nodes which can be visited by a NodeVisitor.
*
* It generally invokes the visitor's visit() method on each of its children.
*/
pub trait Acceptor {
fn accept<'a>(&'a self, visitor: &mut dyn NodeVisitor<'a>);
}
@@ -58,6 +63,11 @@ pub struct MissingEndError {
pub type VisitResult = ControlFlow<MissingEndError>;
/**
* Similar to NodeVisitor, but for mutable nodes.
*
* Note that this breaks out various node types into their own functions.
*/
trait NodeVisitorMut {
/// will_visit (did_visit) is called before (after) a node's fields are visited.
fn will_visit_fields_of(&mut self, node: &mut dyn NodeMut);
@@ -79,6 +89,11 @@ fn visit_decorated_statement_decorator(
fn visit_token_background(&mut self, _node: &mut Option<TokenBackground>);
}
/**
* A trait implemented on Nodes which can be visited by a NodeVisitorMut.
*
* It generally invokes the right visit() method on each of its children.
*/
trait AcceptorMut {
fn accept_mut(&mut self, visitor: &mut dyn NodeVisitorMut);
}
@@ -580,47 +595,47 @@ fn accept_mut(&mut self, visitor: &mut dyn NodeVisitorMut) {
/// Visit the given fields in order, returning whether the visitation succeeded.
macro_rules! visitor_accept_field {
// Visit fields using NodeVisitor. This does not use ControlFlow.
(
$accept:ident,
$visit:ident,
visit,
$self:ident,
$visitor:ident,
$( $field_name:ident: $field_type:tt ),* $(,)?
) => {
loop {
{
$(
visit_1_field!($visit, ($self.$field_name), $field_type, $visitor);
visit_1_field!(visit, ($self.$field_name), $field_type, $visitor);
)*
break VisitResult::Continue(());
}
};
}
/// Visit the given field, breaking on failure.
macro_rules! visit_1_field {
(
visit,
$field:expr,
$field_type:tt,
$visitor:ident
) => {
visit_1_field_impl!(visit, $field, $field_type, $visitor);
};
// Visit fields using NodeVisitorMut. This uses ControlFlow.
(
$accept:ident,
visit_mut,
$field:expr,
$field_type:tt,
$visitor:ident
$self:ident,
$visitor:ident,
$( $field_name:ident: $field_type:tt ),* $(,)?
) => {
let result = visit_1_field_impl!(visit_mut, $field, $field_type, $visitor);
if result.is_break() {
break result;
// Visit fields using NodeVisitorMut.
// This uses control flow.
{
loop {
$(
let result = visit_1_field!(visit_mut, ($self.$field_name), $field_type, $visitor);
if result.is_break() {
break result;
}
)*
break VisitResult::Continue(());
}
}
};
}
/// Visit the given field.
macro_rules! visit_1_field_impl {
macro_rules! visit_1_field {
(
$visit:ident,
$field:expr,
@@ -657,7 +672,7 @@ macro_rules! visit_optional_field {
) => {
match &$field {
Some(value) => $visitor.visit(&*value),
None => visit_result!(visit),
None => (),
}
};
(
@@ -692,15 +707,6 @@ macro_rules! visit_optional_field_mut {
};
}
macro_rules! visit_result {
( visit) => {
()
};
( visit_mut ) => {
VisitResult::Continue(())
};
}
/// A redirection has an operator like > or 2>, and a target like /dev/null or &1.
/// Note that pipes are not redirections.
#[derive(Default, Debug)]