tt_call/
replace.rs

1/// Replace each token that matches a given predicate by a given replacement
2/// sequence of tokens.
3/// <sup>**[tt-call]**</sup>
4///
5/// # Input
6///
7///   - `condition = [{` name of predicate macro `}]`
8///   - `replace_with = [{` arbitrary tokens inserted when the predicate is true `}]`
9///   - `input = [{` arbitrary input tokens `}]`
10///
11/// The predicate macro must accept a single input value named `input`. It is
12/// expected to return a single output value which may have any name but must
13/// hold the tokens `true` or `false`. For example the built-in `tt_is_ident!`
14/// predicate expands to `is_ident = [{ true }]` or `is_ident = [{ false }]`.
15///
16/// # Output
17///
18///   - `tokens = [{` tokens after replacement `}]`
19///
20/// # Example
21///
22/// ```
23/// use tt_call::{tt_call, tt_replace, tt_return};
24///
25/// macro_rules! is_lowercase_self {
26///     {
27///         $caller:tt
28///         input = [{ self }]
29///     } => {
30///         tt_return! {
31///             $caller
32///             is = [{ true }]
33///         }
34///     };
35///
36///     {
37///         $caller:tt
38///         input = [{ $other:tt }]
39///     } => {
40///         tt_return! {
41///             $caller
42///             is = [{ false }]
43///         }
44///     };
45/// }
46///
47/// macro_rules! closure {
48///     ($($expr:tt)+) => {
49///         |__value| tt_call! {
50///             macro = [{ tt_replace }]
51///             condition = [{ is_lowercase_self }]
52///             replace_with = [{ __value }]
53///             input = [{ $($expr)+ }]
54///         }
55///     };
56/// }
57///
58/// fn main() {
59///     let add_one = closure!(self + 1);
60///     println!("{}", add_one(1));
61/// }
62/// ```
63#[macro_export]
64macro_rules! tt_replace {
65    {
66        $caller:tt
67        condition = [{ $($condition:ident)::* }]
68        replace_with = [{ $($with:tt)* }]
69        input = [{ $($input:tt)* }]
70    } => {
71        $crate::private_replace! {
72            $caller
73            condition = [{ $($condition)::* }]
74            replace_with = [{ $($with)* }]
75            tokens = [{ }]
76            rest = [{ $($input)* }]
77        }
78    };
79}
80
81#[doc(hidden)]
82#[macro_export]
83macro_rules! private_replace {
84    // Arrived at end of input. Return to caller.
85    {
86        $caller:tt
87        condition = [{ $($condition:ident)::* }]
88        replace_with = [{ $($with:tt)* }]
89        tokens = [{ $($tokens:tt)* }]
90        rest = [{ }]
91    } => {
92        $crate::tt_return! {
93            $caller
94            tokens = [{ $($tokens)* }]
95        }
96    };
97
98    // Next token tree is a parenthesized group. Recurse to replace contents.
99    {
100        $caller:tt
101        condition = [{ $($condition:ident)::* }]
102        replace_with = [{ $($with:tt)* }]
103        tokens = [{ $($tokens:tt)* }]
104        rest = [{ ( $($group:tt)* ) $($rest:tt)* }]
105    } => {
106        $crate::tt_call! {
107            macro = [{ $crate::private_replace }]
108            condition = [{ $($condition)::* }]
109            replace_with = [{ $($with)* }]
110            tokens = [{ }]
111            rest = [{ $($group)* }]
112            ~~> $crate::private_replace! {
113                $caller
114                condition = [{ $($condition)::* }]
115                replace_with = [{ $($with)* }]
116                tokens = [{ $($tokens)* }]
117                after_paren = [{ $($rest)* }]
118            }
119        }
120    };
121
122    // Return from replacing contents of parenthesized group.
123    {
124        $caller:tt
125        condition = [{ $($condition:ident)::* }]
126        replace_with = [{ $($with:tt)* }]
127        tokens = [{ $($tokens:tt)* }]
128        after_paren = [{ $($after:tt)* }]
129        tokens = [{ $($inside:tt)* }]
130    } => {
131        $crate::private_replace! {
132            $caller
133            condition = [{ $($condition)::* }]
134            replace_with = [{ $($with)* }]
135            tokens = [{ $($tokens)* ( $($inside)* ) }]
136            rest = [{ $($after)* }]
137        }
138    };
139
140    // Next token tree is a square bracketed group. Recurse to replace contents.
141    {
142        $caller:tt
143        condition = [{ $($condition:ident)::* }]
144        replace_with = [{ $($with:tt)* }]
145        tokens = [{ $($tokens:tt)* }]
146        rest = [{ [ $($group:tt)* ] $($rest:tt)* }]
147    } => {
148        $crate::tt_call! {
149            macro = [{ $crate::private_replace }]
150            condition = [{ $($condition)::* }]
151            replace_with = [{ $($with)* }]
152            tokens = [{ }]
153            rest = [{ $($group)* }]
154            ~~> $crate::private_replace! {
155                $caller
156                condition = [{ $($condition)::* }]
157                replace_with = [{ $($with)* }]
158                tokens = [{ $($tokens)* }]
159                after_bracket = [{ $($rest)* }]
160            }
161        }
162    };
163
164    // Return from replacing contents of square bracketed group.
165    {
166        $caller:tt
167        condition = [{ $($condition:ident)::* }]
168        replace_with = [{ $($with:tt)* }]
169        tokens = [{ $($tokens:tt)* }]
170        after_bracket = [{ $($after:tt)* }]
171        tokens = [{ $($inside:tt)* }]
172    } => {
173        $crate::private_replace! {
174            $caller
175            condition = [{ $($condition)::* }]
176            replace_with = [{ $($with)* }]
177            tokens = [{ $($tokens)* [ $($inside)* ] }]
178            rest = [{ $($after)* }]
179        }
180    };
181
182    // Next token tree is a curly braced group. Recurse to replace contents.
183    {
184        $caller:tt
185        condition = [{ $($condition:ident)::* }]
186        replace_with = [{ $($with:tt)* }]
187        tokens = [{ $($tokens:tt)* }]
188        rest = [{ { $($group:tt)* } $($rest:tt)* }]
189    } => {
190        $crate::tt_call! {
191            macro = [{ $crate::private_replace }]
192            condition = [{ $($condition)::* }]
193            replace_with = [{ $($with)* }]
194            tokens = [{ }]
195            rest = [{ $($group)* }]
196            ~~> $crate::private_replace! {
197                $caller
198                condition = [{ $($condition)::* }]
199                replace_with = [{ $($with)* }]
200                tokens = [{ $($tokens)* }]
201                after_brace = [{ $($rest)* }]
202            }
203        }
204    };
205
206    // Return from replacing contents of curly braced group.
207    {
208        $caller:tt
209        condition = [{ $($condition:ident)::* }]
210        replace_with = [{ $($with:tt)* }]
211        tokens = [{ $($tokens:tt)* }]
212        after_brace = [{ $($after:tt)* }]
213        tokens = [{ $($inside:tt)* }]
214    } => {
215        $crate::private_replace! {
216            $caller
217            condition = [{ $($condition)::* }]
218            replace_with = [{ $($with)* }]
219            tokens = [{ $($tokens)* { $($inside)* } }]
220            rest = [{ $($after)* }]
221        }
222    };
223
224    // Next token is not a group, invoke condition and continue.
225    {
226        $caller:tt
227        condition = [{ $($condition:ident)::* }]
228        replace_with = [{ $($with:tt)* }]
229        tokens = [{ $($tokens:tt)* }]
230        rest = [{ $first:tt $($rest:tt)* }]
231    } => {
232        $crate::tt_if! {
233            condition = [{ $($condition)::* }]
234            input = [{ $first }]
235            true = [{
236                $crate::private_replace! {
237                    $caller
238                    condition = [{ $($condition)::* }]
239                    replace_with = [{ $($with)* }]
240                    tokens = [{ $($tokens)* $($with)* }]
241                    rest = [{ $($rest)* }]
242                }
243            }]
244            false = [{
245                $crate::private_replace! {
246                    $caller
247                    condition = [{ $($condition)::* }]
248                    replace_with = [{ $($with)* }]
249                    tokens = [{ $($tokens)* $first }]
250                    rest = [{ $($rest)* }]
251                }
252            }]
253        }
254    };
255}