0

我正在尝试提出一个我称之为宏的宏

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在任何位置接受。

4

2 回答 2

4

这可以通过TT 咀嚼器、下推式堆积器和处理尾随分离器来完成。

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,
                }
            }
        }
    };
}

也可以看看:

于 2019-09-24T18:10:40.250 回答
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);

操场

于 2019-09-25T06:43:05.747 回答