我正在尝试实现一个宏,该宏将扩展brainfuck程序(从一些简单的代码开始,其中我遇到了一个已经提出解决方案的问题:How to parse single tokens in rust macros)。问题在于,在递归匹配的某个时刻,它永远无法匹配结尾:
error: recursion limit reached while expanding the macro `brainfuck`
--> src/lib.rs:119:9
|
119 | brainfuck!(@impl cell; $($all_tokens)*);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
124 | brainfuck!(++++++++++[>+++++++>++++++++++>+++++++++++>+++>+<<<<<-]>++.>>+.---.<---.>>++.<+.++++++++.-------.<+++.>+.>+.>.);
| --------------------------------------------------------------------------------------------------------------------------- in this macro invocation
|
= help: consider adding a `#![recursion_limit="2000"]` attribute to your crate
这是宏代码:
#[macro_export]
macro_rules! brainfuck {
(@impl $var:ident;) => {};
(@impl $var:ident; + $($t:tt)*) => {
$var.inc();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; - $($t:tt)*) => {
$var.dec();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; > $($t:tt)*) => {
$var.next();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; < $($t:tt)*) => {
$var.prev();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; . $($t:tt)*) => {
$var.printVal();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; , $($t:tt)*) => {
$var.getInput();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; [$($t:tt)*] $($ts:tt)*) => {
while $var.getVal() != 0 {
brainfuck!(@impl $var; $($t)*);
}
brainfuck!(@impl $var; $($ts)*);
};
($($all_tokens:tt)*) => {
let mut cell = CellData::new();
brainfuck!(@impl cell; $($all_tokens)*);
};
}
它基于自定义struct
的扩展方法。
完整的代码编译问题可以在此playground
我对这种匹配没有真正的信心:
(@impl $var:ident; [$($t:tt)*] $($ts:tt)*) => {
while $var.getVal() != 0 {
brainfuck!(@impl $var; $($t)*);
}
brainfuck!(@impl $var; $($ts)*);
};
我想到了这个[$($t:tt)*] $($ts:tt)*
来将[]
所包围的部分代码与内部的任何标记匹配,之后再加上任何标记。但是我不确定是否应该工作。
我已经处理了一段时间,我完全陷入困境。 欢迎任何帮助。预先感谢!
答案 0 :(得分:2)
宏中的最后一个模式匹配任何内容,因此,如果您的@impl
大小写与预期输入不匹配,则宏将退回到最后一个模式,并从头开始。 / p>
让它不匹配所有内容以调试问题。我将在模式的开头添加@start
:
#[macro_export]
macro_rules! brainfuck {
// @impl cases elided
(@start $($all_tokens:tt)*) => {
let mut cell = CellData::new();
brainfuck!(@impl cell; $($all_tokens)*);
};
}
fn hello_world() {
brainfuck!(@start ++++++++++[>+++++++>++++++++++>+++++++++++>+++>+<<<<<-]>++.>>+.---.<---.>>++.<+.++++++++.-------.<+++.>+.>+.>.);
}
现在,我们可以清楚地知道出了什么问题:
error: no rules expected the token `<<`
--> src/main.rs:124:71
|
77 | macro_rules! brainfuck {
| ---------------------- when calling this macro
...
124 | brainfuck!(@start ++++++++++[>+++++++>++++++++++>+++++++++++>+++>+<<<<<-]>++.>>+.---.<---.>>++.<+.++++++++.-------.<+++.>+.>+.>.);
| ^^ no rules expected this token in macro call
error: no rules expected the token `>>`
--> src/main.rs:124:82
|
77 | macro_rules! brainfuck {
| ---------------------- when calling this macro
...
124 | brainfuck!(@start ++++++++++[>+++++++>++++++++++>+++++++++++>+++>+<<<<<-]>++.>>+.---.<---.>>++.<+.++++++++.-------.<+++.>+.>+.>.);
| ^^ no rules expected this token in macro call
问题在于序列<<
和>>
是Rust中的单个令牌(至少对于macro_rules!
宏而言)。您可以通过添加以下规则轻松修复宏:
#[macro_export]
macro_rules! brainfuck {
// ...
(@impl $var:ident; >> $($t:tt)*) => {
$var.next();
$var.next();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; << $($t:tt)*) => {
$var.prev();
$var.prev();
brainfuck!(@impl $var; $($t)*);
};
// ...
}
这揭示了另一个有问题的序列:
error: no rules expected the token `<-`
--> src/main.rs:136:75
|
77 | macro_rules! brainfuck {
| ---------------------- when calling this macro
...
109 | brainfuck!(@impl $var; $($t)*);
| - help: missing comma here
...
136 | brainfuck!(@start ++++++++++[>+++++++>++++++++++>+++++++++++>+++>+<<<<<-]>++.>>+.---.<---.>>++.<+.++++++++.-------.<+++.>+.>+.>.);
| ^^ no rules expected this token in macro call
示例中未显示的是->
,它也是一个令牌。同样,这需要其他规则:
#[macro_export]
macro_rules! brainfuck {
// ...
(@impl $var:ident; <- $($t:tt)*) => {
$var.prev();
$var.dec();
brainfuck!(@impl $var; $($t)*);
};
(@impl $var:ident; -> $($t:tt)*) => {
$var.dec();
$var.next();
brainfuck!(@impl $var; $($t)*);
};
// ...
}
Procedural macros不会出现此问题,因为对于每个字符,它们始终会收到一个Punct
作为标点符号。 Punct
知道它是否与下一个令牌联合;这就是宏可以告诉< <
和<<
的原因(因为空格不是令牌)。程序宏也不受递归限制的影响。