somen_language/
macros.rs

1/// Generate enums represents tokens or syntax trees.
2///
3/// In this macro, each variants must be formed `Self::Variant` or `Self::Variant(Type)` and if a
4/// parameter exists, the parser output type should be `Type`.
5#[macro_export]
6macro_rules! token {
7    ($(#[$attrs:meta])* $vis:vis enum $name:ident $(<
8        $($lt:lifetime $(: $ltltbound:lifetime)?),* $(,)?
9        $($T:ident $(: $bound:path)? $(: ?$sized:path)? $(: $ltbound:lifetime)? $(= $default:ty)?),* $(,)?
10    >)? : $src:ty
11    $(where $($U:ty $(: $whbound:path)? $(: ?$whsized:path)? $(: $whltbound:lifetime)?),* $(,)?)?
12     {
13        $(
14            $(#[$f_attrs:meta])*
15            $(@[$($key:ident = $value:ident),* $(,)?])?
16            $var:ident $(($field:ty))? = $token:expr
17        ),+ $(,)?
18    }) => {
19        $(#[$attrs])*
20        $vis enum $name $(<
21            $($lt $(: $ltltbound)?,)*
22            $($T $(: $bound)? $(: ?$sized)? $(: $ltbound)? $(= $default)?),*
23        >)?
24        $(where $($U $(: $whbound)? $(: ?$whsized)? $(: $whltbound)?,)*)?
25        {
26            $($(#[$f_attrs])* $var $(($field))?,)+
27        }
28
29        impl $(<
30            $($lt $(: $ltltbound)?,)*
31            $($T $(: $bound)? $(: ?$sized)? $(: $ltbound)? $(= $default)?),*
32        >)? $name $(<$($lt,)* $($T),*>)?
33        $(where $($U $(: $whbound)? $(: ?$whsized)? $(: $whltbound)?,)*)?
34        {
35            #[allow(dead_code)]
36            pub fn parser<'__parser, __Input>() -> impl somen::parser::Parser<
37                __Input,
38                Output = $name $(<$($lt,)* $($T),*>)?
39            > + '__parser
40            where
41                __Input: Input<Ok=$src> + ?Sized + '__parser,
42                $(
43                    $($lt: '__parser,)*
44                    $($T: '__parser,)*
45                )?
46            {
47                somen::parser::choice((
48                    $(
49                        somen::parser::wrapper::Map::new(
50                            $token,
51                            $crate::__token_inner!{@closure [$name] [$var]; $($field)? },
52                        ),
53                    )+
54                ))
55            }
56
57            $crate::__token_inner! {@expand [$name] [$src]  $([$([$lt])* | $([$T])*])?;
58                $([$var] [$($field)?] [$token] [|] $($([$key = $value])*)?;)+
59            }
60        }
61    };
62}
63
64#[macro_export]
65#[doc(hidden)]
66macro_rules! __token_inner {
67    (@expand [$name:ident] [$src:ty]; $(
68            [$var:ident] [$($field:ty)?] [$token:expr] [|] $([$key:ident = $value:ident])*;
69    )*) => {
70        $(
71            $crate::__token_inner! {
72                @method [$name] [$src] [$var] [$($field)?] [$token];
73                $([$key = $value])*
74            }
75        )*
76    };
77    (@expand [$name:ident] [$src:ty] [|]; $(
78            [$var:ident] [$($field:ty)?] [$token:expr] [$([$lt:lifetime])* | $([$T:ident])*]
79            $([$key:ident = $value:ident])*;
80    )*) => {
81        $(
82            $crate::__token_inner! {
83                @method [$name] [$src] [$var] [$($field)?] [$token] [$($lt),*|$($T),*];
84                $([$key = $value])*
85            }
86        )*
87    };
88    (@expand [$name:ident] [$src:ty] [|[$T:ident]$([$rest:ident])*]; $(
89            [$var:ident] [$($field:ty)?] [$token:expr] [$([$lt:lifetime])* | $([$U:ident])*]
90            $([$key:ident = $value:ident])*;
91    )*) => {
92        $crate::__token_inner! { @expand [$name] [$src] [|$([$rest])*]; $(
93            [$var] [$($field)?] [$token] [$([$lt])*|$([$U])*[$T]] $([$key = $value])*;
94        )* }
95    };
96    (@expand [$name:ident] [$src:ty] [[$lt:lifetime]$([$rest:lifetime])*|$([$T:ident])*]; $(
97            [$var:ident] [$($field:ty)?] [$token:expr] [$([$lt2:lifetime])*|]
98            $([$key:ident = $value:ident])*;
99    )*) => {
100        $crate::__token_inner! { @expand [$name] [$src] [$([$rest])*|$([$T])*]; $(
101            [$var] [$($field)?] [$token] [$([$lt2])*[$lt]|] $([$key = $value])*;
102        )* }
103    };
104    (@method [$name:ident] [$src:ty] [$var:ident] [$($field:ty)?] [$token:expr]
105        $([$($lt:lifetime),*|$($T:ident),*])?;) => {};
106    (@method [$name:ident] [$src:ty] [$var:ident] [$($field:ty)?] [$token:expr]
107        $([$($lt:lifetime),*|$($T:ident),*])?; [match = $fname:ident]$([$k:ident = $v:ident])*) => {
108        #[allow(dead_code)]
109        #[inline]
110        pub fn $fname<'__parser, __Input>() -> impl somen::parser::Parser<
111            __Input, Output = ($($field)?)
112        > + '__parser $($(+ $lt)*)?
113        where
114           __Input: Positioned<Ok = Self> + ?Sized + '__parser $($(+ $lt)*)?,
115            $(
116                $($lt: '__parser,)*
117                $($T: '__parser,)*
118            )?
119        {
120            somen::parser::wrapper::Expect::new(
121                somen::parser::is_some(|c|
122                    $crate::__token_inner! { @match c [$name] [$var]; $($field)? }
123                ),
124                stringify!($fname).into(),
125            )
126        }
127
128        $crate::__token_inner! { @method [$name] [$src] [$var] [$($field)?] [$token] $([$($lt),*|$($T),*])?; $([$k = $v])* }
129    };
130    (@method [$name:ident] [$src:ty] [$var:ident] [$field:ty] [$token:expr]
131        $([$($lt:lifetime),*|$($T:ident),*])?; [match_arg = $fname:ident]$([$k:ident = $v:ident])*) => {
132        #[allow(dead_code)]
133        #[inline]
134        pub fn $fname<'__parser, __Input, __Value>(inner: __Value) -> impl somen::parser::Parser<
135            __Input, Output = $field
136        > + '__parser $($(+ $lt)*)?
137        where
138            __Input: Positioned<Ok = Self> + ?Sized + '__parser $($(+ $lt)*)?,
139            __Value: PartialEq<$field> + '__parser $($(+ $lt)*)?,
140            $(
141                $($lt: '__parser,)*
142                $($T: '__parser,)*
143            )?
144        {
145            somen::parser::wrapper::Expect::new(
146                somen::parser::is_some(move |c| match c {
147                    $name::$var(val) if inner == val => Some(val),
148                    _ => None,
149                }),
150                stringify!($fname).into(),
151            )
152        }
153
154        $crate::__token_inner! { @method [$name] [$src] [$var] [$field] [$token] $([$($lt),*|$($T),*])?; $([$k = $v])* }
155    };
156    (@method [$name:ident] [$src:ty] [$var:ident] [] [$token:expr]
157        $([$($lt:lifetime),*|$($T:ident),*])?; [match_arg = $fname:ident]$([$k:ident = $v:ident])*) => {
158        compile_error!("`match_arg` is not supported for variants without fields.");
159
160        $crate::__token_inner! { @method [$name] [$src] [$var] [] [$token] $([$($lt),*|$($T),*])?; $([$k = $v])* }
161    };
162    (@method [$name:ident] [$src:ty] [$var:ident] [$($field:ty)?] [$token:expr]
163        $([$($lt:lifetime),*|$($T:ident),*])?; [single = $fname:ident]$([$k:ident = $v:ident])*) => {
164        #[allow(dead_code)]
165        #[inline]
166        pub fn $fname<'__parser, __Input>() -> impl somen::parser::Parser<
167            __Input,
168            Output = $name $(<$($lt,)* $($T),*>)?
169        > + '__parser
170        where
171            __Input: Input<Ok=$src> + ?Sized + '__parser,
172            $(
173                $($lt: '__parser,)*
174                $($T: '__parser,)*
175            )?
176        {
177            somen::parser::wrapper::Map::new(
178                $token,
179                $crate::__token_inner! { @closure [$name] [$var]; $($field)? },
180            )
181        }
182
183        $crate::__token_inner! { @method [$name] [$src] [$var] [$($field)?] [$token] $([$($lt),*|$($T),*])?; $([$k = $v])* }
184    };
185    (@match $c:ident [$name:ident] [$var:ident];) => {
186        match $c {
187            $name::$var => Some(()),
188            _ => None,
189        }
190    };
191    (@match $c:ident [$name:ident] [$var:ident]; $field:ty) => {
192        match $c {
193            $name::$var(inner) => Some(inner),
194            _ => None,
195        }
196    };
197    (@closure [$name:ident] [$var:ident];) => {
198        |_| $name::$var
199    };
200    (@closure [$name:ident] [$var:ident]; $field:ty) => {
201        $name::$var
202    };
203}
204
205/// Automatically generate a parser for infix expressions, using precedence climbing.
206#[cfg(feature = "alloc")]
207#[cfg_attr(feature = "nightly", doc(cfg(feature = "alloc")))]
208#[macro_export]
209macro_rules! infix {
210    (
211        $name:ident: $output:ty;
212        $($atom_val:ident : $atom:expr => $atom_ex:expr;)+
213        $(
214            @[$type:ident $(($($vars:tt)*))?]
215            $($op:expr => $ex:expr;)+
216        )*
217    ) => {{
218        let $name = || somen::parser::choice(($(
219            somen::parser::wrapper::Map::new(
220                $atom,
221                |$atom_val| -> $output { $atom_ex },
222            ),
223        )+));
224
225        $(
226            let $name = move || $crate::__infix_inner! {
227                [$type $(($($vars)*))?] $name, $output;
228                $($op => $ex;)+
229            };
230         )*
231        $name()
232    }};
233}
234
235#[macro_export]
236#[doc(hidden)]
237macro_rules! __infix_inner {
238    ([prefix($val:ident)] $name:ident, $output:ty;
239     $($op:expr => $ex:expr;)+) => {{
240        extern crate alloc;
241
242        somen::parser::wrapper::Map::new(
243            (
244                somen::parser::iterable::combinator::Collect::<_, alloc::vec::Vec<_>>::new(
245                    somen::parser::iterable::generator::Repeat::new(
246                        somen::parser::one_of([$($op),+]),
247                        ..,
248                    )
249                ),
250                $name(),
251            ),
252            |(collect, init): (alloc::vec::Vec<_>, $output)| -> $output {
253                collect
254                    .into_iter()
255                    .rev()
256                    .fold(init, |$val: $output, op| match op {
257                        $(
258                            $op => $ex,
259                         )+
260                        _ => unreachable!(),
261                    })
262            },
263        )
264    }};
265    ([prefix_once($val:ident)] $name:ident, $output:ty; $($op:expr => $ex:expr;)+) => {
266        somen::parser::wrapper::Map::new(
267            (somen::parser::combinator::Opt::new(somen::parser::one_of([$($op),+])), $name()),
268            |(op, $val): (core::option::Option<_>, $output)| -> $output {
269                match op {
270                    $(
271                        Some($op) => $ex,
272                     )+
273                    Some(_) => unreachable!(),
274                    None => $val,
275                }
276            },
277        )
278    };
279    ([postfix($val:ident)] $name:ident, $output:ty; $($op:expr => $ex:expr;)+) => {
280        somen::parser::iterable::combinator::Fold::new(
281            somen::parser::iterable::generator::Repeat::new(somen::parser::one_of([$($op),+]), ..),
282            $name(),
283            |$val: $output, op: _| match op {
284                $(
285                    $op => $ex,
286                 )+
287                _ => unreachable!(),
288            },
289        )
290    };
291    ([postfix_once($val:ident)] $name:ident, $output:ty; $($op:expr => $ex:expr;)+) => {
292        somen::parser::wrapper::Map::new(
293            ($name(), somen::parser::combinator::Opt::new(somen::parser::one_of([$($op),+]))),
294            |($val, op): ($output, core::option::Option<_>)| -> $output {
295                match op {
296                    $(
297                        Some($op) => $ex,
298                     )+
299                    Some(_) => unreachable!(),
300                    None => $val,
301                }
302            },
303        )
304    };
305    ([binary($rhs:ident $(: $rt:ty)? $(= $rp:expr)?, $lhs:ident $(: $lt:ty)? $(= $lp:expr)?)]
306     $name:ident, $output:ty; $($op:expr => $ex:expr;)+) => {
307        somen::parser::wrapper::Map::new(
308            (
309                $crate::__infix_inner!(@opt $name; $($rp)?),
310                somen::parser::combinator::Opt::new((
311                    somen::parser::one_of([$($op),+]),
312                    $crate::__infix_inner!(@opt $name; $($lp)?),
313                ))
314            ),
315            |($rhs, op): (
316                $crate::__infix_inner!(@opt_ty $output; $($rt)? $(= $rp)?),
317                core::option::Option<(_, $crate::__infix_inner!(@opt_ty $output; $($lt)? $(= $lp)?))>
318            )| -> $output {
319                match op {
320                    $(
321                        Some(($op, $lhs)) => $ex,
322                     )+
323                    Some(_) => unreachable!(),
324                    None => $rhs,
325                }
326            },
327        )
328    };
329    ([left($rhs:ident, $lhs:ident $(: $lt:ty)? $(= $lp:expr)?)] $name:ident, $output:ty;
330     $($op:expr => $ex:expr;)+) => {
331        somen::parser::iterable::combinator::Fold::new(
332            somen::parser::iterable::generator::Repeat::new(
333                (somen::parser::one_of([$($op),+]), $crate::__infix_inner!(@opt $name; $($lp)?)),
334                ..
335            ),
336            $name(),
337            |$rhs: $output, (op, $lhs): (_, $crate::__infix_inner!(@opt_ty $output; $($lt)? $(= $lp)?))|
338                -> $output {
339                match op {
340                    $(
341                        $op => $ex,
342                     )+
343                    _ => unreachable!(),
344                }
345            },
346        )
347    };
348    ([right($rhs:ident $(: $rt:ty)? $(= $rp:expr)?, $lhs:ident)] $name:ident, $output:ty;
349     $($op:expr => $ex:expr;)+) => {{
350        extern crate alloc;
351
352        somen::parser::wrapper::Map::new(
353            (
354                somen::parser::iterable::combinator::Collect::<_, alloc::vec::Vec<_>>::new(
355                    somen::parser::iterable::generator::Repeat::new(
356                        (
357                            $crate::__infix_inner!(@opt $name; $($rp)?),
358                            somen::parser::one_of([$($op),+]),
359                        ),
360                        ..,
361                    )
362                ),
363                $name(),
364            ),
365            |(collect, init): (
366                alloc::vec::Vec<($crate::__infix_inner!(@opt_ty $output; $($rt)? $(= $rp)?), _)>,
367                $output
368            )| -> $output {
369                collect
370                    .into_iter()
371                    .rev()
372                    .fold(init, |$lhs, ($rhs, op)| match op {
373                        $(
374                            $op => $ex,
375                         )+
376                        _ => unreachable!(),
377                    })
378            },
379        )
380    }};
381    (@opt $name:ident;) => { $name() };
382    (@opt $name:ident; $parser:expr) => { $parser };
383    (@opt_ty $output:ty;) => { $output };
384    (@opt_ty $output:ty; = $parser:expr) => { _ };
385    (@opt_ty $output:ty; $type:ty $(= $parser:expr)?) => { $type };
386}