I want to emulate capture rules in cpp using a macro as a learning exercise, and I realized that rustfmt doesn't always play nice. I also am wondering if there is a way of simplifying or using particular techniques to reduce the complexity of macro rules. Like if there is a standard form for macro_rules.
Here is the macro in question:
macro_rules! capture {
([$($tail:tt)*] $expression:expr) => (
{
capture![$($tail)*];
$expression
}
);
[mut $value:ident, $($tail:tt)*] => {
let mut $value = $value.clone();
capture![$($tail)*];
};
[mut $value:ident] => {
let mut $value = $value.clone();
};
[$value:ident, $($tail:tt)*] => {
let $value = $value.clone();
capture![$($tail)*];
};
[$value:ident] => {
let $value = $value.clone();
};
[$name:tt = $value:expr, $($tail:tt)*] => {
let $name = $value;
capture![$($tail)*];
};
[$name:tt = $value:expr] => {
let $name = $value;
};
[mut $name:tt = $value:expr, $($tail:tt)*] => {
let mut $name = $value;
capture![$($tail)*];
};
[mut $name:tt = $value:expr] => {
let mut $name = $value;
};
}
which may be used like the following:
#[test]
fn test_capture() {
let a = String::from("hello");
let b = String::from("world");
let x = capture!([mut a, b, c = 42] move || a.clone() + &b + &c.to_string() );
let _ = a;
let _ = b;
let _ = x();
let _ = x();
println!("{:?}", x());
}
I tried to reduce the macro rules by creating a comma separated list, similar to the vec
macro, but the vec macro takes a homogenous list of expr I believe which makes the problem simplier. In contrast my problem wants to be able to accept different kinds of token patterns, but it causes a lot of boilerplate.
One thing you can do is avoid duplicating your tailed and non-tailed rules via a zero-or-one repetition: matching on $(...)?
and expanding with $(...)*
. That at least reduces your rules into only the major forms of your DSL:
macro_rules! capture {
([$($tail:tt)*] $expression:expr) => (
{
capture![$($tail)*];
$expression
}
);
[mut $value:ident $(, $($tail:tt)*)?] => {
let mut $value = $value.clone();
$(capture![$($tail)*];)*
};
[$value:ident $(, $($tail:tt)*)?] => {
let $value = $value.clone();
$(capture![$($tail)*];)*
};
[$name:tt = $value:expr $(, $($tail:tt)*)?] => {
let $name = $value;
$(capture![$($tail)*];)*
};
[mut $name:tt = $value:expr $(, $($tail:tt)*)?] => {
let mut $name = $value;
$(capture![$($tail)*];)*
};
}
As for something more "systematic"... eh no, not really. Not that I can think of. You can check out the tracing::event
macro to see it has many very similar arms as well.