我正在尝试提出一个我想调用的宏
create_states!(S0, S1, final S2, final S3);
它将创建一个枚举来表示状态机状态,其中一些将是最终(接受)状态-S2
,S3
。产生的枚举及其impl
应该如下所示:
enum State {
S0,
S1,
S2,
S3,
}
impl State {
fn is_final(&self) -> bool {
match self {
Self::S2 => true,
Self::S3 => true,
_ => false,
}
}
}
我天真的尝试:
macro_rules! create_states {
($($r:ident),+, $(final $f:ident),*) => {
#[derive(Copy, Clone)]
enum State {
$($s),*
$($f),*
}
impl State {
fn is_final(&self) -> bool {
match self {
$(Self::$f => true,)*
_ => false,
}
}
}
}
}
最终出现以下错误:
error: local ambiguity: multiple parsing options: built-in NTs ident ('r') or 1 other option.
--> src/lib.rs:20:24
|
20 | create_states!(S0, S1, final S2, final S3);
| ^^^^^
尝试删除第二行中的模式之间的逗号:
($($r:ident),+ $(final $f:ident),*) => { ...
正在生产另一个:
error: no rules expected the token `S2`
--> src/lib.rs:20:30
|
1 | macro_rules! create_states {
| -------------------------- when calling this macro
...
20 | create_states!(S0, S1, final S2, final S3);
| ^^ no rules expected this token in macro call
我认为我理解是什么导致了这些错误-它认为final
是与r
匹配的另一个标识符。但是编写这样的宏的正确方法是什么(如果可能的话,又不会过于复杂)?
我对宏调用具有完全的灵活性,因为这是我的个人学习练习。主要目标是学习正确的做事方式。如果可能的话,最好让此宏在任何位置接受final
。
答案 0 :(得分:3)
这可以通过TT muncher,push-down accumulation和处理trailing separators来实现。
macro_rules! create_states {
// User entry points.
(final $name:ident $($tt:tt)*) => {
create_states!(@ {[] [$name]} $($tt)*);
};
($name:ident $($tt:tt)*) => {
create_states!(@ {[$name] []} $($tt)*);
};
// Internal rules to categorize each value
(@ {[$($n:ident)*] [$($t:ident)*]} $(,)? final $name:ident $($tt:tt)*) => {
create_states!(@ {[$($n)*] [$($t)* $name]} $($tt)*);
};
(@ {[$($n:ident)*] [$($t:ident)*]} $(,)? $name:ident $($tt:tt)*) => {
create_states!(@ {[$($n)* $name] [$($t)*]} $($tt)*);
};
// Final internal rule that generates the enum from the categorized input
(@ {[$($n:ident)*] [$($t:ident)*]} $(,)?) => {
#[derive(Copy, Clone)]
enum State {
$($n,)*
$($t,)*
}
impl State {
fn is_final(&self) -> bool {
match self {
$(Self::$t => true,)*
_ => false,
}
}
}
};
}
另请参阅:
答案 1 :(得分:1)
Shepmaster的回答较为笼统,但是在您的特定情况下,由于“在宏调用方面具有充分的灵活性”,您可以将final
替换为@final
,并且天真的尝试可以避免轻微的错别字:
macro_rules! create_states {
($($r:ident),+, $(@final $f:ident),*) => {
#[derive(Copy, Clone)]
enum State {
$($r,)*
$($f),*
}
impl State {
fn is_final(&self) -> bool {
match self {
$(Self::$f => true,)*
_ => false,
}
}
}
}
}
create_states!(S0, S1, @final S2, @final S3);