Skip to main content

dtor/macros/
perform.rs

1//! A set of macros to perform chained macro invocations.
2
3/// The top-level macro that invokes one sub-macro and renders that output.
4#[macro_export]
5#[doc(hidden)]
6macro_rules! __perform {
7    ( $input:tt, $macro:path $( [ $($args:tt)* ] )? ) => {
8        $macro ! ( @entry next=$crate::__perform[[@complete]], input=$input $(, args=[$($args)*])? );
9    };
10    ( [@complete], ($($input:tt)*) ) => {
11        $($input)*
12    };
13    ( $($input:tt)* ) => {
14        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
15    };
16}
17
18/// Pass-through macro that stringifies the input for debugging.
19#[macro_export]
20#[doc(hidden)]
21macro_rules! __debug {
22    ( @entry next=$next:path[$next_args:tt], input=$input:tt ) => {
23        const _: () = { stringify! $input ; };
24        $next ! ( $next_args, $input );
25    };
26
27    ( $($input:tt)* ) => {
28        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
29    };
30}
31
32/// Stringifies the input.
33#[macro_export]
34#[doc(hidden)]
35macro_rules! __stringify {
36    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=[$(prefix=[$($prefix:tt)*])? $(suffix=[$($suffix:tt)*])?] ) => {
37        $next ! ( $next_args, ($($($prefix)*)? stringify! $input $($($suffix)*)? ) );
38    };
39
40    ( $($input:tt)* ) => {
41        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
42    };
43}
44
45/// Emits the arguments, ignoring the inputs.
46#[macro_export]
47#[doc(hidden)]
48macro_rules! __emit {
49    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=[$($args:tt)*] ) => {
50        $next ! ( $next_args, ($($args)*) );
51    };
52
53    ( $($input:tt)* ) => {
54        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
55    };
56}
57
58/// Surrounds the input with the arguments.
59#[macro_export]
60#[doc(hidden)]
61macro_rules! __surround {
62    ( @entry next=$next:path[$next_args:tt], input=($($input:tt)*),
63        args=[ $( prefix=[$($prefix:tt)*] )? $( suffix=[$($suffix:tt)*] )? ] ) => {
64        $next ! ( $next_args, ( $($($prefix)*)? $($input)* $($($suffix)*)? ) );
65    };
66
67    ( $($input:tt)* ) => {
68        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
69    };
70}
71
72/// Surrounds the input with the arguments.
73#[macro_export]
74#[doc(hidden)]
75macro_rules! __brace {
76    ( @entry next=$next:path[$next_args:tt], input=($($input:tt)*), args=[ () ] ) => {
77        $next ! ( $next_args, ( ( $($input)* ) ) );
78    };
79
80    ( @entry next=$next:path[$next_args:tt], input=($($input:tt)*), args=[ [] ] ) => {
81        $next ! ( $next_args, ( [ $($input)* ] ) );
82    };
83
84    ( @entry next=$next:path[$next_args:tt], input=($($input:tt)*), args=[ {} ] ) => {
85        $next ! ( $next_args, ( { $($input)* } ) );
86    };
87
88    ( $($input:tt)* ) => {
89        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
90    };
91}
92
93/// Removes surrounding braces from the input.
94#[macro_export]
95#[doc(hidden)]
96macro_rules! __unbrace {
97    ( @entry next=$next:path[$next_args:tt], input=(($($input:tt)*))) => {
98        $next ! ( $next_args, ( $($input)* ) );
99    };
100
101    ( @entry next=$next:path[$next_args:tt], input=([$($input:tt)*])) => {
102        $next ! ( $next_args, [ $($input)* ] );
103    };
104
105    ( @entry next=$next:path[$next_args:tt], input=({$($input:tt)*})) => {
106        $next ! ( $next_args, { $($input)* } );
107    };
108
109    ( $($input:tt)* ) => {
110        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
111    };
112}
113
114/// Passes the input through unchanged.
115#[macro_export]
116#[doc(hidden)]
117macro_rules! __identity {
118    ( @entry next=$next:path[$next_args:tt], input=$input:tt ) => {
119        $next!($next_args, $input);
120    };
121}
122
123/// Sinks the input, ignoring any chained output
124#[macro_export]
125#[doc(hidden)]
126macro_rules! __sink {
127    ( @entry next=$next:path[$next_args:tt], input=$input:tt ) => {};
128}
129
130/// Splits the input and processes it down multiple paths, in order. The next
131/// macro will be called for each path.
132#[macro_export]
133#[doc(hidden)]
134macro_rules! __split {
135    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=[
136        $($macro:path $([$($args:tt)*])?),*
137        $(,)?
138    ] ) => {
139        $(
140            $macro ! ( @entry next=$next[$next_args], input=$input $(, args=[$($args)*])? );
141        )*
142    };
143
144    ( $($input:tt)* ) => {
145        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
146    };
147}
148
149/// Runs the same input through multiple macros and passes the output of each to
150/// the next macro concatenated.
151#[macro_export]
152#[doc(hidden)]
153macro_rules! __parallel {
154    // Entry point, start with empty accumulator
155    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=$args:tt ) => {
156        $crate::__parallel!( @process $input, $args, (), [$next[$next_args]] );
157    };
158
159    // Exit point, all parallel is done, emit accumulator to next macro
160    ( @process $input:tt, [], $accum:tt, [$next:path[$next_args:tt]] ) => {
161        $next ! ( $next_args, $accum );
162    };
163
164    // Continue point, call the next macro in the parallel chain
165    ( @process $input:tt, [$next:path $([$($args:tt)*])?, $($stack:tt)*], $accum:tt, $final:tt ) => {
166        $next!(
167            @entry next=$crate::__parallel[[@continue [$($stack)*] $input $accum $final]],
168            input=$input $(, args=[$($args)*])?
169        );
170    };
171
172    ( [@continue [$($stack:tt)*] $input:tt ($( $accum:tt )*) $final:tt], ($($output:tt)*) ) => {
173        $crate::__parallel!( @process $input, [$($stack)*], ($($accum)* $($output)*), $final);
174    };
175
176    ( $($input:tt)* ) => {
177        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
178    };
179}
180
181/// Processes input through a chain of macros, emitting the final output to the
182/// next macro.
183#[macro_export]
184#[doc(hidden)]
185macro_rules! __chain {
186    // Entry point
187    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=[$($args:tt)*] ) => {
188        $crate::__chain!( @process $input, [$($args)*], [$next[$next_args]] );
189    };
190    // Exit point, all chain is done
191    ( @process $input:tt, [], [$next:path[$next_args:tt]] ) => {
192        $next ! ( $next_args, $input );
193    };
194
195    // Continue point, call the next macro in the chain
196    ( @process $input:tt, [$next:path $([$($args:tt)*])?, $($stack:tt)*], $final:tt ) => {
197        $next!( @entry next=$crate::__chain[[@continue [$($stack)*] $final]], input=$input $(, args=[$($args)*])?);
198    };
199
200    // Return from macro call
201    ( [@continue [$($stack:tt)*] $final:tt], $input:tt ) => {
202        $crate::__chain!( @process $input, [$($stack)*], $final);
203    };
204
205    ( $($input:tt)* ) => {
206        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
207    };
208}
209
210/// Separates the token trees of the input into multiple paths and runs them in parallel.
211#[macro_export]
212#[doc(hidden)]
213macro_rules! __separate {
214    // Entry point, start with empty accumulator
215    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=$args:tt ) => {
216        $crate::__separate!( @process $input, $args, (), [$next[$next_args]] );
217    };
218
219    // Exit point, all parallel is done, emit accumulator to next macro
220    ( @process $input:tt, [], $accum:tt, [$next:path[$next_args:tt]] ) => {
221        $next ! ( $next_args, $accum );
222    };
223
224    // Continue point, call the next macro in the parallel chain
225    ( @process ($input:tt $($input_rest:tt)*), [$next:path $([$($args:tt)*])?, $($stack:tt)*], $accum:tt, $final:tt ) => {
226        $next!(
227            @entry next=$crate::__separate[[@continue [$($stack)*] ($($input_rest)*) $accum $final]],
228            input=$input $(, args=[$($args)*])?
229        );
230    };
231
232    ( [@continue [$($stack:tt)*] $input:tt ($( $accum:tt )*) $final:tt], ($($output:tt)*) ) => {
233        $crate::__separate!( @process $input, [$($stack)*], ($($accum)* $($output)*), $final);
234    };
235
236    ( $($input:tt)* ) => {
237        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
238    };
239}
240
241/// Zip up multiple arrays into a single array of tuples. The arrays must all be the same length.
242#[macro_export]
243#[doc(hidden)]
244macro_rules! __zip {
245    ( @entry next=$next:path[$next_args:tt], input=$input:tt ) => {
246        $crate::__zip!( @process accum=(), input=$input, next=[$next[$next_args]] );
247    };
248
249    ( @process accum=$accum:tt, input=($(())*), next=[$next:path[$next_args:tt]] ) => {
250        $next ! ( $next_args, $accum );
251    };
252
253    ( @process accum=($($accum:tt)*), input=(
254        $( ($first:tt $($inner:tt)*) )*
255    ), next=$next:tt ) => {
256        $crate::__zip!( @process accum=($($accum)* ($($first)*)), input=($(($($inner)*))*), next=$next );
257    };
258
259    ( $($input:tt)* ) => {
260        const _: () = { compile_error!(concat!("Unexpected input: ", stringify!($($input)*))); };
261    };
262}
263
264/// Re-arrange items from the input into a new order.
265#[macro_export]
266#[doc(hidden)]
267macro_rules! __pick {
268    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=$args:tt ) => {
269        $crate::__pick!( @process $input, $input, (), $args, [$next[$next_args]] );
270    };
271
272    ( @process $input:tt, $input_:tt, $accum:tt, [], [$next:path[$next_args:tt]] ) => {
273        $next ! ( $next_args, $accum );
274    };
275
276    ( @process ($i0:tt $($rest:tt)*), $input:tt, ($($accum:tt)*), [0 $($args:tt)*], $next:tt) => {
277        $crate::__pick!( @process $input, $input, ($($accum)* $i0), [$($args)*], $next );
278    };
279    ( @process ($i0:tt $i1:tt $($rest:tt)*), $input:tt, ($($accum:tt)*), [1 $($args:tt)*], $next:tt) => {
280        $crate::__pick!( @process $input, $input, ($($accum)* $i1), [$($args)*], $next );
281    };
282    ( @process ($i0:tt $i1:tt $i2:tt $($rest:tt)*), $input:tt, ($($accum:tt)*), [2 $($args:tt)*], $next:tt) => {
283        $crate::__pick!( @process $input, $input, ($($accum)* $i2), [$($args)*], $next );
284    };
285    ( @process ($i0:tt $i1:tt $i2:tt $i3:tt $($rest:tt)*), $input:tt, ($($accum:tt)*), [3 $($args:tt)*], $next:tt) => {
286        $crate::__pick!( @process $input, $input, ($($accum)* $i3), [$($args)*], $next );
287    };
288    ( @process ($i0:tt $i1:tt $i2:tt $i3:tt $i4:tt $($rest:tt)*), $input:tt, ($($accum:tt)*), [4 $($args:tt)*], $next:tt) => {
289        $crate::__pick!( @process $input, $input, ($($accum)* $i4), [$($args)*], $next );
290    };
291    ( @process ($i0:tt $i1:tt $i2:tt $i3:tt $i4:tt $i5:tt $($rest:tt)*), $input:tt, ($($accum:tt)*), [5 $($args:tt)*], $next:tt) => {
292        $crate::__pick!( @process $input, $input, ($($accum)* $i5), [$($args)*], $next );
293    };
294}
295
296/// Calls a macro for each tokentree in the input and accumulates the results.
297#[macro_export]
298#[doc(hidden)]
299macro_rules! __for_each {
300    ( @entry next=$next:path[$next_args:tt], input=$input:tt, args=[$macro_name:path $([$($args:tt)*])?] ) => {
301        $crate::__for_each!( @process accum=(), input=$input, args=[$macro_name $([$($args)*])?], next=[$next[$next_args]] );
302    };
303    ( @process accum=$accum:tt, input=(), args=$args:tt, next=[$next:path [$next_args:tt]] ) => {
304        $next ! ( $next_args, $accum );
305    };
306    ( @process accum=$accum:tt, input=($input:tt $($rest:tt)*), args=[$macro_name:path $( [$($macro_args:tt)*] )?], next=$next:tt) => {
307        $macro_name ! ( @entry next=$crate::__for_each[
308            [@continue $accum, ($($rest)*), [$macro_name $( [$($macro_args)*] )?], $next]
309        ], input=($input) $(, args=[$($macro_args)*])? );
310    };
311    ( [@continue ($($accum:tt)*), $input:tt, $args:tt, $next:tt], ($($output:tt)*) ) => {
312        $crate::__for_each!( @process accum=($($accum)* $($output)*), input=$input, args=$args, next=$next );
313    };
314    ( $($input:tt)* ) => {
315        const _: () = { compile_error!(concat!("Unexpected input for __for_each: ", stringify!($($input)*))); };
316    };
317}
318
319/// Calls a dynamic macro with the given input and arguments.
320#[macro_export]
321#[doc(hidden)]
322macro_rules! __call {
323    ( @entry next=$next:path[$next_args:tt], input=($($input:tt)*), args=[$macro_name:path $([$($args:tt)*])?] ) => {
324        $macro_name ! ( @entry next=$next[$next_args], input=($($input)*) $(, args=[$($args)*])? );
325    };
326}