【问题标题】:Adding an ident to multiple repetition results in "local ambiguity: multiple parsing options"向多个重复添加 ident 会导致“局部歧义:多个解析选项”
【发布时间】:2017-03-19 10:47:32
【问题描述】:

我正在尝试构建一个可以使用函数调用列表 (test2()) 或标识符 (test3) 调用的宏:

invoke!( 
    ["str1", "str2"]: i32 => test1(), test2(), test3, test4; 
    ["str3", "str1"]: i32 => test1(), test3; 
)

目前只支持函数调用:

invoke!( 
    ["str1", "str2"]: i32 => test1(), test2(); 
    ["str3", "str1"]: i32 => test1(); 
)

原宏代码:

macro_rules! invoke {
    ( $([$($field:expr),*]: $vtype:ty => $($func:ident$args:tt),*; )+ ) => {
        $(
            let x = vec![$($field),*];
            let y = vec![$($func$args),*];
            println!("{:#?}: {:#?}", x, y)
        )+
    };
}

当我尝试将其更改为支持两种形式时:

macro_rules! invoke {
    ( $([$($field:expr),*]: $vtype:ty => $($func:ident$args:tt),* $($newfunc:ident),*; )+ ) => {
        $(
            let x = vec![$($field),*];
            let y = vec![$($func$args),*];
            let z: Vec<$vtype> = vec![$newfunc()];
            println!("{:#?}: {:#?} [{:#?}]", x, y, z)
        )+
    };
}

我收到一个错误:

error: local ambiguity: multiple parsing options: built-in NTs ident ('func') or ident ('newfunc').
  --> src/main.rs:15:34
   |
15 |         ["str1", "str2"]: i32 => test1(), test2(), test3, test4; 
   |                                  ^^^^^

我知道可以通过:tt 构建这个宏,但我找不到方法。

Working code sample

Expected and not working code sample

【问题讨论】:

  • 你能改变invoke!的调用方式吗?
  • @kennytm 它在代码中明确显示:invoke!( ["str1", "str2"]: i32 =&gt; test1() test2(); ["str3", "str1"]: i32 =&gt; test1(); )
  • 我在你的宏中发现了错误——即使你说的那个是有效的。另外,您能否展示一下您希望将您的第一个示例转换成什么?
  • @PeterHall 我不知道你怎么会出错。我添加了问题的链接,它是有效的代码示例。随着时间的推移,我添加了预期的样本。
  • @mrLSD 我的意思是,不是invoke!(["str1"]: i32 =&gt; test1(), test2(), test3, test4),你能把语法改成invoke!(["str1"]: i32 =&gt; {test1() =&gt; test3, test2() =&gt; test4})吗?

标签: macros rust


【解决方案1】:

这个宏模式没有意义:

$($func:ident$args:tt),* $($newfunc:ident),*;

这表示解析零个或多个标识符 (func),后跟零个或多个标识符 (newfunc)。如果输入是标识符foo,那么解析器将无法决定选择哪个语法路径,因为前瞻的数量有限(可能是一个标记)。

正如 cmets 中所指出的,最简单的解决方案是引入新的语法来消除案例的歧义。

macro_rules! invoke {
    ( $([$($field:expr),*]: $vtype:ty => $($func:ident$args:tt),* [$($newfunc:ident),*]; )+ ) => {{
        $(
            let x = vec![$($field),*];
            let y = vec![$($func$args),*];
            let z: Vec<$vtype> = vec![$($newfunc()),*];
            println!("{:#?}: {:#?} [{:#?}]", x, y, z);
        )+
    }};
}

fn main() {
    invoke!( 
        ["str1", "str2"]: i32 => test1(), test2() [test3, test4]; 
        ["str3", "str1"]: i32 => test1() [test3]; 
    );
}

fn test1() -> i32 { 42 }
fn test2() -> i32 { 42 }
fn test3() -> i32 { 42 }
fn test4() -> i32 { 42 }

我是否正确理解不可能将$args:tt 设置为可选,即testtest()

据我所知,宏中没有 optional 修饰符。通常,这是通过多个并行模式来处理的:

macro_rules! thing {
    ($name:ident$args:tt) => { println!("a") };
    ($name:ident) => { println!("b") };
}

fn main() {
    thing!(foo);   // b
    thing!(foo()); // a
}

这似乎对您的情况没有帮助,因为您希望收集一堆 A,然后是一堆 B。这将允许一堆 A-or-B。在语法上,它类似于

(A|B)* # This form
A*B*   # What you want

【讨论】:

  • 感谢您的精彩回答。我理解$args:tt不可能设置为可选,即test vs test()是否正确?
  • 刚刚阅读这个答案,它应该被更新。现在有一个可选的宏修饰符$($x:ident)?
猜你喜欢
  • 2020-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-14
  • 2018-08-14
  • 2019-02-01
  • 2013-07-06
相关资源
最近更新 更多