0

私は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)*);
    };
}

custom からメソッドを展開することに基づいていますstruct完全なコード コンパイルの問題は、このプレイグラウンドで再現できます

私はこのマッチングにあまり自信がありません:

    (@impl $var:ident; [$($t:tt)*] $($ts:tt)*) => {
        while $var.getVal() != 0 {
            brainfuck!(@impl $var; $($t)*);
        }
        brainfuck!(@impl $var; $($ts)*);
    };

[$($t:tt)*] $($ts:tt)*これは、 で囲まれたコードの部分を、[]トークンが内部にあるもので、その後にトークンが続くものと一致させると考えました。しかし、それが機能するかどうかはわかりません。

私はしばらくこれを扱ってきましたが、完全に立ち往生しています。どんな種類の助けも大歓迎です。前もって感謝します!

4

1 に答える 1

2

マクロの最後のパターンはにでも一致するため、@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は、句読点を各文字に 1 つとして常に受け取るため、この問題はありません。APunctは次のトークンと結合しているかどうかを知っています。これが、マクロが区別できる方法< <です<<(スペースはトークンではないため)。手続き型マクロも再帰制限の影響を受けません。

于 2019-05-05T01:30:12.950 に答える