|  | //@ run-pass | 
|  |  | 
|  | #![feature(macro_metavar_expr)] | 
|  |  | 
|  | /// Count the number of idents in a macro repetition. | 
|  | macro_rules! count_idents { | 
|  | ( $( $i:ident ),* ) => { | 
|  | ${count($i)} | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// Count the number of idents in a 2-dimensional macro repetition. | 
|  | macro_rules! count_idents_2 { | 
|  | ( $( [ $( $i:ident ),* ] ),* ) => { | 
|  | ${count($i)} | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// Mostly counts the number of OUTER-MOST repetitions | 
|  | macro_rules! count_depth_limits { | 
|  | ( $( { $( [ $( $outer:ident : ( $( $inner:ident )* ) )* ] )* } )* ) => { | 
|  | ( | 
|  | ( | 
|  | ${count($inner)}, | 
|  | ${count($inner, 0)}, | 
|  | ${count($inner, 1)}, | 
|  | ${count($inner, 2)}, | 
|  | ${count($inner, 3)}, | 
|  | ), | 
|  | ( | 
|  | ${count($outer)}, | 
|  | ${count($outer, 0)}, | 
|  | ${count($outer, 1)}, | 
|  | ${count($outer, 2)}, | 
|  | ), | 
|  | ) | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// Produce (index, len) pairs for literals in a macro repetition. | 
|  | /// The literal is not included in the output, so this macro uses the | 
|  | /// `ignore` meta-variable expression to create a non-expanding | 
|  | /// repetition binding. | 
|  | macro_rules! enumerate_literals { | 
|  | ( $( ($l:stmt) ),* ) => { | 
|  | [$( ${ignore($l)} (${index()}, ${len()}) ),*] | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// Produce index and len tuples for literals in a 2-dimensional | 
|  | /// macro repetition. | 
|  | macro_rules! enumerate_literals_2 { | 
|  | ( $( [ $( ($l:literal) ),* ] ),* ) => { | 
|  | [ | 
|  | $( | 
|  | $( | 
|  | ( | 
|  | ${index(1)}, | 
|  | ${len(1)}, | 
|  | ${index(0)}, | 
|  | ${len(0)}, | 
|  | $l | 
|  | ), | 
|  | )* | 
|  | )* | 
|  | ] | 
|  | }; | 
|  | } | 
|  |  | 
|  | /// Generate macros that count idents and then add a constant number | 
|  | /// to the count. | 
|  | /// | 
|  | /// This macro uses dollar escaping to make it unambiguous as to which | 
|  | /// macro the repetition belongs to. | 
|  | macro_rules! make_count_adders { | 
|  | ( $( $i:ident, $b:literal );* ) => { | 
|  | $( | 
|  | macro_rules! $i { | 
|  | ( $$( $$j:ident ),* ) => { | 
|  | $b + $${count($j)} | 
|  | }; | 
|  | } | 
|  | )* | 
|  | }; | 
|  | } | 
|  |  | 
|  | make_count_adders! { plus_one, 1; plus_five, 5 } | 
|  |  | 
|  | /// Generate a macro that allows selection of a particular literal | 
|  | /// from a sequence of inputs by their identifier. | 
|  | /// | 
|  | /// This macro uses dollar escaping to make it unambiguous as to which | 
|  | /// macro the repetition belongs to, and to allow expansion of an | 
|  | /// identifier the name of which is not known in the definition | 
|  | /// of `make_picker`. | 
|  | macro_rules! make_picker { | 
|  | ( $m:ident => $( $i:ident ),* ; $p:ident ) => { | 
|  | macro_rules! $m { | 
|  | ( $( $$ $i:literal ),* ) => { | 
|  | $$ $p | 
|  | }; | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | make_picker!(first => a, b; a); | 
|  |  | 
|  | make_picker!(second => a, b; b); | 
|  |  | 
|  | fn main() { | 
|  | assert_eq!(count_idents!(a, b, c), 3); | 
|  | assert_eq!(count_idents_2!([a, b, c], [d, e], [f]), 6); | 
|  | assert_eq!( | 
|  | count_depth_limits! { | 
|  | { | 
|  | [ A: (a b c) D: (d e f) ] | 
|  | [ G: (g h) I: (i j k l m) ] | 
|  | [ N: (n) ] | 
|  | } | 
|  | { | 
|  | [ O: (o) P: (p q) R: (r s) ] | 
|  | [ T: (t u v w x y z) ] | 
|  | } | 
|  | }, | 
|  | ((26, 26, 9, 5, 2), (9, 9, 5, 2)) | 
|  | ); | 
|  | assert_eq!(enumerate_literals![("foo"), ("bar")], [(0, 2), (1, 2)]); | 
|  | assert_eq!( | 
|  | enumerate_literals_2![ | 
|  | [("foo"), ("bar"), ("baz")], | 
|  | [("qux"), ("quux"), ("quuz"), ("xyzzy")] | 
|  | ], | 
|  | [ | 
|  | (0, 2, 0, 3, "foo"), | 
|  | (0, 2, 1, 3, "bar"), | 
|  | (0, 2, 2, 3, "baz"), | 
|  | (1, 2, 0, 4, "qux"), | 
|  | (1, 2, 1, 4, "quux"), | 
|  | (1, 2, 2, 4, "quuz"), | 
|  | (1, 2, 3, 4, "xyzzy"), | 
|  | ] | 
|  | ); | 
|  | assert_eq!(plus_one!(a, b, c), 4); | 
|  | assert_eq!(plus_five!(a, b), 7); | 
|  | assert_eq!(first!(1, 2), 1); | 
|  | assert_eq!(second!(1, 2), 2); | 
|  | } |