且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

递归宏以在Rust中解析匹配武器

更新时间:2023-02-21 23:03:55

当您需要基于重复内的不同匹配进行分支时,您需要执行

When you need to branch based on different matches inside repetitions like this, you need to do incremental parsing.

所以

macro_rules! parse {

这是宏的入口点.它设置最外层,并将输入馈入一般的解析规则.我们向下传递chars,以便更深的层可以找到它.

This is the entry point for the macro. It sets up the outer-most layer, and feeds the input into a general parsing rule. We pass down chars so the deeper layers can find it.

    ($buf:expr, {$($body:tt)*}) => {
        {
            let mut chars = $buf.chars().peekable();
            parse! { @parse chars, {}, $($body)* }
        }
    };

终止规则:一旦我们用完了输入(以逗号为模),就将累积的匹配臂代码片段转储到match表达式中,并附加最后的全部捕获臂.

Termination rule: once we run out of input (modulo some commas), dump the accumulated match arm code fragments into a match expression, and append the final catch-all arm.

    (@parse $chars:expr, {$($arms:tt)*}, $(,)*) => {
        match $chars.peek() {
            $($arms)*
            _ => None
        }
    };

或者,如果指定了包罗万象的手臂,请使用它.

Alternately, if the catch-all arm is specified, use that.

    (@parse $chars:expr, {$($arms:tt)*}, _ => $e:expr $(,)*) => {
        match $chars.peek() {
            $($arms)*
            _ => Some($e)
        }
    };

这处理递归.如果看到一个块,则前进$chars并使用空的代码累加器解析该块的内容.所有这些的结果是附加到当前累加器( ie $($arms)).

This handles the recursion. If we see a block, we advance $chars and parse the contents of the block with an empty code accumulator. The result of all this is appended to the current accumulator (i.e. $($arms)).

    (@parse $chars:expr, {$($arms:tt)*}, $p:pat => { $($block:tt)* }, $($tail:tt)*) => {
        parse! {
            @parse
            $chars,
            {
                $($arms)*
                Some(&$p) => {
                    $chars.next().unwrap();
                    parse!(@parse $chars, {}, $($block)*)
                },
            },
            $($tail)*
        }
    };

非递归情况.

    (@parse $chars:expr, {$($arms:tt)*}, $p:pat => $e:expr, $($tail:tt)*) => {
        parse! {
            @parse
            $chars,
            {
                $($arms)*
                Some(&$p) => Some($e),
            },
            $($tail)*
        }
    };
}

为完整起见,其余的测试代码.请注意,我必须更改test1,因为它不是有效的测试.

And, for completeness, the rest of the test code. Note that I had to change test1, as it wasn't a valid test.

#[derive(Debug, PartialEq)]
enum Digit { One, Two, Three, Ten, Eleven }

#[test]
fn test1() {
    let buf = "111";
    let token = parse!(buf, {
        '1' => Digit::One,
        '2' => Digit::Two,
        '3' => Digit::Three,
    });
    assert_eq!(token, Some(Digit::One));
}

#[test]
fn test2() {
    let buf = "111";
    let token = parse!(buf, {
        '1' => {
            '0' => Digit::Ten,
            '1' => Digit::Eleven,
            _ => Digit::One,
        },
        '2' => Digit::Two,
        '3' => Digit::Three,
    });
    assert_eq!(token, Some(Digit::Eleven));
}