“This is the 21st day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”


But we still lack operator support. How do we match operators?

If our RPN is a series of tokens and we want to treat it exactly the same way, we can simply use a list like $($Token :tt)*. Unfortunately, this does not allow us to push an operand or apply an operator per tag through the list.

The book says that “the macro system does not deal with analytic ambiguity at all”, which is true for a single macro branch — we cannot match an operator after a sequence of numbers, such as $($num:tt)*+, because + is also a valid tag that can be matched by tt groups, but that is why recursive macros exist.

If you have different branches in your macro definition, Rust will try them one by one, so we can avoid any conflicts by putting our operator branch before the number branch:

macro_rules! rpn {
  ([ $($stack:expr),* ] + $($rest:tt)*) => {
    // TODO
  };

  ([ $($stack:expr),* ] - $($rest:tt)*) => {
    // TODO
  };

  ([ $($stack:expr),* ] * $($rest:tt)*) => {
    // TODO
  };

  ([ $($stack:expr),* ] / $($rest:tt)*) => {
    // TODO}; ([ $($stack:expr),* ] $num:tt $($rest:tt)*) => { rpn! ([ $num $(, $stack)* ] $($rest)*) }; }Copy the code

As I said earlier, the operator is applied to the last two numbers on the stack, so we need to match them separately, “compute” the result (build a regular Infix expression) and put it back.

macro_rules!rpn { ([ $b:expr, $a:expr $(, $stack:expr)* ] + $($rest:tt)*) => { rpn! ([ $a + $b $(, $stack)* ] $($rest)*) }; ([ $b:expr, $a:expr $(, $stack:expr)* ] - $($rest:tt)*) => { rpn! ([ $a - $b $(, $stack)* ] $($rest)*) }; ([ $b:expr, $a:expr $(, $stack:expr)* ] * $($rest:tt)*) => { rpn! ([ $a * $b $(,$stack)* ] $($rest)*) }; ([ $b:expr, $a:expr $(, $stack:expr)* ] / $($rest:tt)*) => { rpn! ([ $a / $b $(,$stack)* ] $($rest)*) }; ([ $($stack:expr),* ] $num:tt $($rest:tt)*) => { rpn! ([ $num $(, $stack)* ] $($rest)*) }; }Copy the code

I’m not a big fan of the obvious repetition, but just as literally, there’s no special tag type to match the operator.

What we can do, however, is add a helper function that takes care of the computation and delegate any explicit operator branches to it.

In macros, you can’t really use external helper functions, but the only thing you can be sure of is that your macro is already in scope, so the usual trick is to “mark” a branch in the same macro with some unique sequence of tags, and then call it recursively as we would in a regular branch.

Let’s use @op as such a tag and accept any operator through tt inside it (TT is explicit in this case, because we will only pass the operator to this helper).

Also, the stack no longer needs to be expanded in each separate branch — since we wrapped it in [] parentheses earlier, it can be matched as another tag tree (TT) and then passed to our helper function.

macro_rules!rpn { (@op [ $b:expr, $a:expr $(, $stack:expr)* ] $op:tt $($rest:tt)*) => { rpn! ([ $a $op $b $(, $stack)* ] $($rest)*) }; ($stack:tt + $($rest:tt)*) => { rpn! (@op $stack + $($rest)*) }; ($stack:tt - $($rest:tt)*) => { rpn! (@op $stack - $($rest)*) }; ($stack:tt * $($rest:tt)*) => { rpn! (@op $stack * $($rest)*) }; ($stack:tt / $($rest:tt)*) => { rpn! (@op $stack / $($rest)*) }; ([ $($stack:expr),* ] $num:tt $($rest:tt)*) => { rpn! ([ $num $(, $stack)* ] $($rest)*) }; }Copy the code