宏匹配令牌递归扩展

网波

我正在尝试实现一个宏来扩展一个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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章