2016-06-01 7 views
3

非タプル引数をタプルに変換するマクロを作成できますか?非タプル引数をタプルに変換するマクロ

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

このようなマクロを作成しようとしましたが、作成できませんでした。私が直面した問題は、それぞれの議論が2つの形式のうちの1つを持つことができ、それを特定する方法を理解できなかったことでした。

答えて

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

,もトークンツリーとして解析することができますので、それがあいまいです。

これに取り組むために、私は "TT muncher"と書く必要があると思います。

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,))); 
} 
+0

二度目に '@internal'と' @parse'を数日で見ると、私は狩りに行く必要があります。 –

+1

私は[convention](https://danielkeep.github.io/tlborm/book/pat-internal-rules.html)にちょうど従っています。 :) –

+0

リンクありがとう!それは巧妙なトリックです。 –

関連する問題