更新时间: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));
}