「用 macro! 实现逆波兰表达式」验证结果

695 阅读2分钟

「这是我参与11月更文挑战的第 21 天,活动详情查看:2021最后一次更文挑战


现在,任何词都被相应的分支处理,我们只需要处理最后的情况,即栈包含一个项目,并且没有更多的词了。

macro_rules! rpn {
  // ...

  ([ $result:expr ]) => {
    $result
  };
}

此时,如果你用一个空的栈和RPN表达式来调用这个宏,它已经会产生一个正确的结果。

Playground

println!("{}", rpn!([] 2 3 + 4 *)); // 20

然而,我们的栈是一个实现细节,我们真的不希望每个消费者都传入一个空的堆栈,所以让我们在最后再添加一个接续分支,作为一个入口点,并自动添加[]。

Playground

macro_rules! rpn {
  // ...

  ($($tokens:tt)*) => {
    rpn!([] $($tokens)*)
  };
}

println!("{}", rpn!(2 3 + 4 *)); // 20

我们的宏甚至适用于更复杂的表达式。

println!("{}", rpn!(15 7 1 1 + - / 3 * 2 1 1 + + -)); // 5

那如果出错了怎么办?现在,对于正确的RPN表达式来说,一切似乎都工作得很顺利,但对于一个可以投入生产的宏来说,我们需要确保它也能处理无效的输入,并且有合理的错误信息。

首先,让我们试着在中间插入另一个数字,看看会发生什么:

println!("{}", rpn!(2 3 7 + 4 *));

输出:

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; try using `:?` instead if 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`

好吧,这个输出看起来没有帮助,因为它没有提供任何与表达式中的实际错误相关的信息。

为了弄清发生了什么,我们需要对我们的宏进行调试。为此,我们将使用 trace_macros 功能(和其他可选的编译器功能一样,你需要一个Rust nightly版本)。我们不想跟踪 println! 调用,所以我们将把我们的RPN计算分离到一个变量中。