Created
October 18, 2019 01:02
-
-
Save rust-play/e2781a6d7ac9eeea0c62fa4b6496a3fa to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
input: [i32 i64][ A B ] | |
prev_expanded: [] | |
next_expanded: [] | |
input: [i64][ A B ] | |
prev_expanded: [] | |
next_expanded: [[i32]] | |
input: [][ A B ] | |
prev_expanded: [] | |
next_expanded: [[i32][i64]] | |
input: [ A B ] | |
prev_expanded: [[i32][i64]] | |
next_expanded: [] | |
input: [ B ] | |
prev_expanded: [[i32][i64]] | |
next_expanded: [[i32 A][i64 A]] | |
input: [ ] | |
prev_expanded: [[i32][i64]] | |
next_expanded: [[i32 A][i64 A][i32 B][i64 B]] | |
*/ | |
/// Higher-order macro that iterates over a cartesian product. | |
/// | |
/// Useful for generating impls involving opaque traits of the sort | |
/// sometimes seen used as bounds in public APIs. | |
/// | |
/// It takes a number of groups of token trees and a suitable definition | |
/// for a callback macro, and it calls the macro with one token tree from | |
/// each group in order. | |
macro_rules! cartesian { | |
( | |
$([$($groups:tt)*])* | |
for_each!($($mac_match:tt)*) | |
=> {$($mac_body:tt)*}$(;)* | |
) | |
=> { | |
// (this fixed name gets overwritten on each use. Technically, using a fixed name | |
// makes this macro not "re-entrant", in the sense that you can't call it in a nested | |
// fashion... but there is no reason to do so, because the macro has a monolithic design) | |
macro_rules! __cartesian__user_macro { | |
($($mac_match)*) => {$($mac_body)*}; | |
} | |
cartesian__!{ | |
step: next_group | |
input: ($([$($groups)*])*) | |
expanded: ([]) | |
callback: (__cartesian__user_macro!) | |
} | |
}; | |
} | |
#[doc(hidden)] | |
macro_rules! cartesian__ { | |
// are there any argument groups left? | |
( | |
step: next_group | |
input: ([$($token:tt)+] $($rest:tt)*) | |
expanded: $expanded:tt | |
callback: $cb:tt | |
) => { cartesian__!{ | |
step: next_item | |
input: ( [$($token)+] $($rest)* ) | |
prev_expanded: $expanded | |
next_expanded: [] | |
callback: $cb | |
}}; | |
// base case; direct product of no arguments | |
( | |
step: next_group | |
input: () | |
expanded: ($($expanded:tt)*) | |
callback: ($mac:ident!) | |
) => { | |
// finish | |
$mac!{ $($expanded)* } | |
}; | |
// Each item in each direct product in the invocation incurs a fixed number | |
// of recursions as we replicate previously expanded inputs. First, we must smash anything we want to replicate | |
// into a single tt that can be matched without repetitions. Do this to `rest`. | |
( | |
step: next_item | |
input: ( [$this_item:tt $($more_items:tt)*] $($rest:tt)* ) | |
prev_expanded: ( $([ $($prev_expanded:tt)* ])* ) | |
next_expanded: ( $($next_expanded:tt)* ) | |
callback: $callback:tt | |
) => { cartesian__!{ | |
step: next_item | |
input: ( [$($more_items)*] [$($rest)*] ) | |
prev_expanded: ( $([ $($prev_expanded)* ])* ) | |
next_expanded: ( | |
$($next_expanded)* | |
$([ $($prev_expanded)* $this_item ])* // <-- add new things | |
) | |
callback: $callback | |
}}; | |
( | |
step: next_item | |
input: ([] $($rest:tt)*) | |
prev_expanded: $prev_expanded:tt | |
prev_expanded: $next_expanded:tt | |
callback: $callback:tt | |
) => { cartesian__!{ | |
step: next_group | |
input: ( [$($rest)*] ) | |
expanded: $next_expanded | |
callback: $callback | |
}}; | |
} | |
mod examples { | |
trait Trait { } | |
// NOTE: Braces around the alternatives are not strictly necessary | |
// (cartesian simply iterates over token trees), but they tend | |
// to help group tokens and resolve any would-be ambiguities in | |
// the callback's match pattern. | |
cartesian!{ | |
[{i32} {u32}] | |
[{0} {1} {2} {3}] | |
for_each!($([ | |
{$T:ty} {$n:expr}] | |
)*) | |
=> { | |
$( | |
impl Trait for [$T; $n] { } | |
)* | |
} | |
} | |
#[test] | |
fn example_works() { | |
fn assert_trait<T:Trait>() {} | |
assert_trait::<[u32; 0]>(); | |
assert_trait::<[i32; 2]>(); | |
} | |
#[derive(Debug, PartialEq, Eq)] | |
struct Euro; | |
impl Euro { | |
fn parse(_: &str) -> Result<(&str, Euro), ()> { Ok(("", Euro)) } | |
fn from(x: u32) -> Self { Euro } | |
} | |
#[test] | |
fn parse_euro() { | |
cartesian!{ | |
[{"{}€"} {"{} Euro"} {"€{}"}] | |
[{1} {32} {1823} {0} {39} {99999999}] | |
for_each!({$f:literal} {$n:expr}) | |
=> { | |
assert_eq!( | |
Euro::parse(&format!($f, $n)).unwrap().1, | |
Euro::from($n), | |
); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment