Reputation: 754
I am hoping to write a Rust macro which forwards its entire argument to a second macro — even when that argument contains exciting parenthesization.
Here is what I have tried so far:
macro_rules! parse {
(done) => (println!("done!"));
(if ($cond:tt) {$then:tt}) => (println!("if! "); parse!($cond); parse($then));
}
macro_rules! forward {
($($e:tt)*) => (parse!($($e)*; done));
}
fn main() {
forward!(if (done) {done} );
}
This doesn't work, and produces the error:
error: no rules expected the token `if`
What am I doing wrong here?
Edit: Beyond simply forwarding the arguments to forward
, I was hoping to "paste" the tokens ; done
on to the end of forward
's arguments. Is there a way to make this work while preserving that behavior?
Upvotes: 2
Views: 1741
Reputation: 59125
The problem is the ; done
in forward
. What's going on here is that the macro expansion code matches literal input tokens against arms one at a time. If an arm doesn't match, it gives up and tries the next one. When it runs out of arms to try, it has to fail and explain why.
But which token in the input was the problem? Answering that when there are potentially multiple arms involved is hard, so instead it just picks the first token and says "this was the problem".
Whenever you see a macro expansion complaining about the first token in the input not matching, it's quite possible it's something later in the input that tripped it up.
Fixing that (and fixing the parse
invocation that's missing its !
) gives:
macro_rules! parse {
(done) => (println!("done!"));
(if ($cond:tt) {$then:tt}) => (println!("if! "); parse!($cond); parse!($then));
}
macro_rules! forward {
($($e:tt)*) => (parse!($($e)*));
}
fn main() {
forward!(if (done) {done} );
}
Upvotes: 4