「这是我参与11月更文挑战的第 21 天,活动详情查看:2021最后一次更文挑战」
不过我们仍然缺少对操作员的支持。我们如何匹配运算符?
如果我们的RPN是一连串的标记,我们希望以完全相同的方式来处理,我们可以简单地使用$($token:tt)*
这样的列表。不幸的是,这不能让我们通过列表,根据每个标记推送一个操作数或应用一个运算符。
书中说 “宏系统完全不处理解析的模糊性”,这对单个宏分支来说是正确的 —— 我们不能匹配一个数字序列后面的运算符,如 $($num:tt)*+
,因为 + 也是一个有效的标记,可以被tt组匹配,但这又是递归宏存在的原因。
如果你的宏定义中有不同的分支,Rust会逐一进行尝试,所以我们可以把我们的操作符分支放在数字分支之前,这样就可以避免任何冲突:
1 | rust复制代码macro_rules! rpn { |
正如我前面所说,运算符被应用于堆栈上的最后两个数字,所以我们需要分别匹配它们,”计算” 结果(构建一个正则的infix表达式)并将其放回。
1 | rust复制代码macro_rules! rpn { |
不过我不是很喜欢这种明显的重复,但是就像字面意思一样,没有特殊的标记类型来匹配运算符。
然而,我们可以做的是,添加一个负责计算的辅助函数,并将任何显式运算符分支委托给它。
在宏中,你不能真正使用外部辅助函数,但你唯一能确定的是你的宏已经在范围内了,所以通常的技巧是在同一个宏中用一些独特的标记序列 “标记” 一个分支,然后像我们在常规分支中那样递归地调用它。
让我们使用 @op
作为这样的标记,并通过它里面的 tt 接受任何运算符(tt 在这种情况下是明确的,因为我们将只向这个助手传递运算符)。
而且,栈不再需要在每个单独的分支中展开 —— 因为我们在前面将其包裹在[]括号中,它可以作为另一个标记树(tt)进行匹配,然后传递到我们的辅助函数。
1 | rust复制代码macro_rules! rpn { |
本文转载自: 掘金