This is my first time writing a somewhat non-trivial declarative + recursive macro in Rust. I drew inspiration from this answer.
The goal is to come up with a recursive macro that allows to assemble some kind of node tree. The general interface function is something like parent.add_child(child). The macro should simplify assembling a deeply nested node tree. For instance:
assemble_tree!(
root => {
child_a,
child_b => { subchild_a },
child_c,
child_d => {
sub_child_b => { sub_sub_child_a },
sub_child_c,
}
}
);
Should expand to:
root.add_child(child_a);
root.add_child(child_b);
child_b.add_child(subchild_a);
root.add_child(child_c);
root.add_child(child_d);
child_d.add_child(sub_child_b);
sub_child_b.add_child(sub_sub_child_a);
child_d.add_child(sub_child_c);
My approach is the following:
macro_rules! assemble_tree {
($base:expr => { $($other:tt)* }) => {
assemble_tree!( @recurse, $base, $($other)*);
};
(@recurse, $base:expr, $child:expr $(,)?) => {
$base.add_child($child);
};
(@recurse, $base:expr, $child:expr, $($other:tt)+) => {
$base.add_child($child);
assemble_tree!( @recurse, $base, $($other)*);
};
(@recurse, $base:expr, $child:expr => { $($children:tt)* } $(,)?) => {
$base.add_child($child);
assemble_tree!( $child => { $($children)* });
};
(@recurse, $base:expr, $child:expr => { $($children:tt)* }, $($other:tt)+) => {
$base.add_child($child);
assemble_tree!( $child => { $($children)* });
assemble_tree!( @recurse, $base, $($other)*);
};
}
I'm wondering if there are any gotchas with this approach, or if there is a way to solve this problem more elegantly.
In particular the approach doesn't feel particularly DRY, because each pattern ending with $(,)? has a sibling pattern (the one ending with $($other:tt)+) and the body of these sibling patterns contains some duplication.
cargo expandand the actual code never was in a compiling state (using such undeclared variables), but I could still see the macro expansions and thus the behavior of the macro. \$\endgroup\$