“This is the 21st day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”
Now, any word is processed by the corresponding branch, and we only need to deal with the final case, where the stack contains one item and there are no more words.
macro_rules! rpn {
// ...
([ $result:expr ]) => {
$result
};
}
Copy the code
At this point, if you call the macro with an empty stack and an RPN expression, it will already produce the correct result.
Playground
println!("{}", rpn! ([]2 3 + 4 *)); / / 20
Copy the code
However, our stack is an implementation detail, and we really don’t want every consumer passing in an empty stack, so let’s add a continuation branch at the end as an entry point, and add [] automatically.
Playground
macro_rules! rpn {
// ...($($tokens:tt)*) => { rpn! ([] $($tokens)*) }; }println!("{}", rpn! (2 3 + 4 *)); / / 20
Copy the code
Our macros work even with more complex expressions.
println!("{}", rpn! (15 7 1 1 + - / 3 * 2 1 1 + + -)); / / 5
Copy the code
So what if something goes wrong? Now, everything seems to be working fine for a proper RPN expression, but for a production-ready macro, we need to make sure it also handles invalid input and has reasonable error messages.
First, let’s try inserting another number in the middle and see what happens:
println!("{}", rpn! (2 3 7 + 4 *));
Copy the code
Output:
error[E0277]: the trait bound `[{integer}; 2]: std::fmt::Display` is not satisfied
--> src/main.rs:36:20
|
36 | println!("{}", rpn! (2 3 7 + 4 *));
| ^^^^^^^^^^^^^^^^^ `[{integer}; 2]` cannot be formatted with the default formatter; tryusing `:? ` insteadif you are using a format string
|
= help: the trait `std::fmt::Display` is not implemented for `[{integer}; 2]`
= note: required by `std::fmt::Display::fmt`
Copy the code
Well, this output doesn’t seem helpful because it doesn’t provide any information about the actual error in the expression.
To figure out what’s going on, we need to debug our macros. To do this, we’ll use the trace_Macros feature (as with other optional compiler features, you’ll need a Rust nightly version). We don’t want to track println! Call, so we’ll separate our RPN calculation into a variable.