static_cond/lib.rs
1#![no_std]
2
3/// Evaluates a conditional during macro expansion as expression.
4///
5/// Currently limited to equality comparison. Can compare any two token trees. Can be nested.
6///
7/// This macro only works in expression-position, see [static_cond_item] for
8/// a macro that works in item-position.
9///
10/// Examples
11/// =======
12///
13/// ```
14/// # #[macro_use] extern crate static_cond;
15/// # fn main() {
16/// let x = static_cond!(if (+ 1 [2 3]) == (+ 1 [2 3]) {
17/// static_cond!(if black != white {
18/// "ok"
19/// } else {
20/// the compiler will never even try to interpret this
21/// })
22/// } else {
23/// blah blah blah blah blah unreachable
24/// });
25/// assert_eq!(x, "ok");
26/// # }
27/// ```
28///
29/// The actual conditional and the code provided for the branches not followed is eliminated after
30/// macro expansion (check `rustc --pretty=expanded`).
31#[macro_export]
32macro_rules! static_cond {
33 // private rule to define and call the local macro
34 // we evaluate a conditional by generating a new macro (in an inner scope, so name shadowing is
35 // not a big concern) and calling it
36 (@expr $lhs:tt $rhs:tt $arm1:tt $arm2:tt) => {{
37 // note that the inner macro has no captures (it can't, because there's no way to escape `$`)
38 macro_rules! __static_cond {
39 ($lhs $lhs) => $arm1;
40 ($lhs $rhs) => $arm2;
41 }
42
43 __static_cond!($lhs $rhs)
44 }};
45 (@item $lhs:tt $rhs:tt $arm1:tt $arm2:tt) => {
46 // note that the inner macro has no captures (it can't, because there's no way to escape `$`)
47 macro_rules! __static_cond {
48 ($lhs $lhs) => $arm1;
49 ($lhs $rhs) => $arm2;
50 }
51
52 __static_cond! { $lhs $rhs }
53 };
54
55 // no else condition provided: fall through with empty else
56 (if $lhs:tt == $rhs:tt $then:tt) => {
57 $crate::static_cond!(if $lhs == $rhs $then else { })
58 };
59 (if $lhs:tt != $rhs:tt $then:tt) => {
60 $crate::static_cond!(if $lhs != $rhs $then else { })
61 };
62
63 // main entry point with then and else arms
64 (if $lhs:tt == $rhs:tt $then:tt else $els:tt) => {
65 $crate::static_cond!(@expr $lhs $rhs $then $els)
66 };
67 (if $lhs:tt != $rhs:tt $then:tt else $els:tt) => {
68 $crate::static_cond!(@expr $lhs $rhs $els $then)
69 };
70}
71
72/// Evaluates a conditional during macro expansion as item.
73///
74/// Currently limited to equality comparison. Can compare any two token trees. Can be nested.
75///
76/// This macro only works in item-position, see [static_cond] for
77/// a macro that works in expression-position.
78///
79/// Examples
80/// =======
81///
82/// ```
83/// # #[macro_use] extern crate static_cond;
84/// # fn main() {
85/// static_cond_item!{
86/// if (+ 1 [2 3]) == (+ 1 [2 3]) {
87/// static_cond_item!{if black != white {
88/// fn foo() -> &'static str {"ok"}
89/// } else {
90/// the compiler will never even try to interpret this
91/// }}
92/// } else {
93/// blah blah blah blah blah unreachable
94/// }
95/// }
96/// assert_eq!(foo(), "ok");
97/// # }
98/// ```
99///
100/// The actual conditional and the code provided for the branches not followed is eliminated after
101/// macro expansion (check `rustc --pretty=expanded`).
102#[macro_export]
103macro_rules! static_cond_item {
104 // no else condition provided: fall through with empty else
105 (if $lhs:tt == $rhs:tt $then:tt) => {
106 $crate::static_cond_item! { if $lhs == $rhs $then else { } }
107 };
108 (if $lhs:tt != $rhs:tt $then:tt) => {
109 $crate::static_cond_item! { if $lhs != $rhs $then else { } }
110 };
111
112 // main entry point with then and else arms
113 (if $lhs:tt == $rhs:tt $then:tt else $els:tt) => {
114 $crate::static_cond! { @item $lhs $rhs $then $els }
115 };
116 (if $lhs:tt != $rhs:tt $then:tt else $els:tt) => {
117 $crate::static_cond! { @item $lhs $rhs $els $then }
118 };
119}