我正在尝试实现一个宏来扩展一个Brainfuck程序(在开始使用一些更简单的代码之后,我已经提出了一个解决方案的问题:如何解析 rust 宏中的单个标记)。问题在于,在递归匹配的某个时刻,它永远无法匹配结尾:
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)*
为了匹配包含[]
在里面的任何标记的代码部分,然后是任何标记。但我不确定它是否应该起作用。
我处理这个问题已经有一段时间了,我完全被困住了。欢迎任何形式的帮助。提前致谢!
宏中的最后一个模式匹配任何内容,因此如果您的@impl
案例未能匹配预期的输入,宏将回退到最后一个模式并基本上重新开始。
让我们让它不匹配所有来调试问题。我将@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)*);
};
// ...
}
程序宏没有这个问题,因为它们总是Punct
为每个字符接收一个标点符号。APunct
知道它是否与下一个令牌联合;这是怎样一个宏可以告诉< <
从开<<
(因为空间不是记号)。程序宏也不受递归限制的影响。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句