使用不同 "kinds" 元素构建枚举的宏
Macro to build enum with different "kinds" of elements
我正在尝试想出一个我会称之为的宏
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
的标识符。但是编写这样一个宏的正确方法是什么(如果可能而不会过于复杂)?
我对宏调用有充分的灵活性,因为这是我个人的学习练习。主要objective是学习正确的做事方法。如果可能的话,最好让这个宏在任何位置接受 final
。
这可以通过 TT muncher, push-down accumulation, and handling the 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,
}
}
}
};
}
另请参阅:
Shepmaster 的回答更为笼统,但在您的具体情况下,由于您 "have full flexibility in the macro invocation",您可以将 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);
我正在尝试想出一个我会称之为的宏
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
的标识符。但是编写这样一个宏的正确方法是什么(如果可能而不会过于复杂)?
我对宏调用有充分的灵活性,因为这是我个人的学习练习。主要objective是学习正确的做事方法。如果可能的话,最好让这个宏在任何位置接受 final
。
这可以通过 TT muncher, push-down accumulation, and handling the 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,
}
}
}
};
}
另请参阅:
Shepmaster 的回答更为笼统,但在您的具体情况下,由于您 "have full flexibility in the macro invocation",您可以将 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);