宏将非元组参数转换为元组

时间:2016-06-01 18:35:01

标签: macros rust

是否可以创建一个宏来将非元组参数转换为元组?我想要这样的东西:

assert_eq!(tuplify!(1, (2, 3), 4), ((1,), (2, 3), (4,)));

我试图创建这样的宏但无法这样做。我遇到的问题是每个参数可以有两种形式中的一种,我无法弄清楚如何指定它。

1 个答案:

答案 0 :(得分:5)

如果宏的每个参数都是单个标记树,那么这将执行:

macro_rules! tuplify {
    (@inner ($($args:expr),*)) => (($($args,)*));
    (@inner $arg:expr) => (($arg,));
    ($($arg:tt),*) => (($(tuplify!(@inner $arg),)*));
}

如果参数可以包含多个标记树,该怎么办?例如:

assert_eq!(tuplify!(1 + 6, (2, 3), 4), ((7,), (2, 3), (4,)));

然后我们只需要接受一系列令牌树,对吧?

macro_rules! tuplify {
    (@inner ($($args:expr),*)) => (($($args,)*));
    (@inner $arg:expr) => (($arg,));
    ($($($arg_tt:tt)+),*) => (($(tuplify!(@inner $($arg)+),)*));
}
不,那太容易了:

<anon>:12:30: 12:31 error: local ambiguity: multiple parsing options: built-in NTs tt ('arg_tt') or 1 other option.
<anon>:12     assert_eq!(tuplify!(1 + 6, (2, 3), 4), ((7,), (2, 3), (4,)));

这是不明确的,因为,也可以解析为令牌树。

为了解决这个问题,我认为我们需要写一个&#34; TT muncher&#34;。

macro_rules! tuplify {
    (@as_expr $e:expr) => { $e };

    // No more tokens
    (@parse { } -> { $($current:tt)* } -> { $($output:tt)* }) => {
        tuplify!(@as_expr ( $($output)* ($($current)*,), ))
    };

    // Comma
    (@parse { , $($ts:tt)* } -> { $($current:tt)* } -> { $($output:tt)* }) => {
        tuplify!(@parse { $($ts)* } -> { } -> { $($output)* ($($current)*,), })
    };

    // Tuple followed by a comma, nothing in the current argument yet
    (@parse { ($($tuple_item:expr),*) , $($ts:tt)* } -> { } -> { $($output:tt)* }) => {
        tuplify!(@parse { $($ts)* } -> { } -> { $($output)* ($($tuple_item,)*), })
    };

    // Tuple followed by nothing else, nothing in the current argument yet
    (@parse { ($($tuple_item:expr),*) } -> { } -> { $($output:tt)* }) => {
        tuplify!(@parse { } -> { } -> { $($output)* ($($tuple_item,)*), })
    };

    // Base case
    (@parse { $t:tt $($ts:tt)* } -> { $($current:tt)* } -> { $($output:tt)* }) => {
        tuplify!(@parse { $($ts)* } -> { $t $($current)* } -> { $($output)* })
    };

    // Entry point
    ($($tokens:tt)*) => (tuplify!(@parse { $($tokens)* } -> { } -> { }));
}

fn main() {
    assert_eq!(tuplify!(1 + 6, (2, 3), 4), ((7,), (2, 3), (4,)));
}