wtflip/
lib.rs

1#[macro_export]
2macro_rules! wtflip {
3    /* Mutable declaration. */
4    (@as_statement mut $ident:ident := $($expr:tt)+) => {
5        let mut $ident = wtflip!(@as_expr $($expr)+);
6    };
7
8    /* Assignment or immutable declaration. */
9    (@as_statement $ident:ident $(: $(@$($_:tt)* $declaration:tt)?)?= $($expr:tt)+) => {
10        $($($declaration)? let)? $ident = wtflip!(@as_expr $($expr)+);
11    };
12
13    /* A compatibility layer. Allows for writing Rust in the wtflip invocation. */
14    (@as_statement @{ $($compat:tt)* }) => ($($compat)*);
15
16    /* Macro invocation. The macro invocation can't be used in callback parsers because it itself expands a macro. */
17    (@as_expr @ $(:: $(@$($_:tt)* $prefixed:tt)?)? $ident:ident $(:: $path:ident)* $(($($args:tt)*))?) => (
18        $crate::args_splitter!(
19            [$($($prefixed)? ::)? $ident $(:: $path)*!]
20            [$crate::wtflip!]
21            []
22            []
23            [$($($args)*)?]
24        );
25    );
26
27    /* Callback helper that turns @as_expr into a proper @callback invocation. */
28    (@as_expr $($tokens:tt)*) => (wtflip!(@callback [] [] @as_expr $($tokens)*));
29
30    /* @as_expr $literal callback. */
31    (@callback [$($cb:tt)*] [$($args:tt)*]
32        @as_expr $lit:literal $($tail:tt)*
33    ) => (wtflip!(@callback [$($cb)*] [$($args)* [$lit]] $($tail)*));
34
35    /* The compat layer but in expression position. */
36    (@callback [$($cb:tt)*] [$($args:tt)*]
37        @as_expr @{ $($tokens:tt)* } $($tail:tt)*
38    ) => (wtflip!(@callback [$($cb)*] [$($args)* [{ $($tokens)* }]]));
39
40    /* Convert empty callback arguments stack to an invocation of the callback with the now processed arguments. */
41    (@callback [$($cb:tt)*] [$([$($arg:tt)*])*]) => ($($cb)*($($($arg)*),*));
42
43    /* The end of a statement has been reached, process the buffer as a statement. */
44    (@statement_accumulator [$($buffer:tt)*] ; $($tail:tt)*) => {
45        wtflip!(@as_statement $($buffer)*);
46        wtflip!($($tail)*);
47    };
48
49    /* Append all non semicolon tokens to the buffer which will be processed as a statement later. */
50    (@statement_accumulator [$($buffer:tt)*] $token:tt $($tail:tt)*) => (wtflip!(
51        @statement_accumulator [$($buffer)* $token] $($tail)*
52    ));
53
54    /* Finished munching the token tree */
55    () => ();
56
57    /* Just passes a non-empty token tree into a statement accumulator. */
58    ($($tokens:tt)+) => (wtflip!(
59        @statement_accumulator [] $($tokens)*
60    ));
61}
62
63#[macro_export]
64macro_rules! args_splitter {
65    (
66        [$($wrap:tt)*]
67        [$($processor:tt)*]
68        []
69        [$([ $($out:tt)* ])*]
70        []
71    ) => ($($processor)*(@callback [$($wrap)*] [] $(@as_expr $($out)*)*));
72    (
73        [$($wrap:tt)*]
74        [$($processor:tt)*]
75        [$($current:tt)*]
76        [$($out:tt)*]
77        [$(, $($rest:tt)*)?]
78    ) => ($crate::args_splitter!(
79        [$($wrap)*]
80        [$($processor)*]
81        []
82        [$($out)* [$($current)*]]
83        [$($($rest)*)?]
84    ));
85    (
86        [$($wrap:tt)*]
87        [$($processor:tt)*]
88        [$($current:tt)*]
89        $out:tt
90        [$no_comma:tt $($rest:tt)*]
91    ) => ($crate::args_splitter!(
92        [$($wrap)*]
93        [$($processor)*]
94        [$($current)* $no_comma]
95        $out
96        [$($rest)*]
97    ));
98}