rusty_peg/
macros.rs

1// NOTE: The only macro intentionally exported is `rusty_peg`. All
2// other macros should be marked `#[doc(hidden)]` and considered
3// internal implementation details.
4
5#[macro_export]
6macro_rules! rusty_peg {
7    { parser $name:ident<'input>: $base:ty { $($grammar_defn:tt)+ } } => {
8        rusty_peg_parser! {
9            parser $name: $base { $($grammar_defn)* }
10        }
11    };
12
13    { parser $name:ident<'input> { $($grammar_defn:tt)+ } } => {
14        rusty_peg_parser! {
15            parser $name: () { $($grammar_defn)* }
16        }
17    };
18}
19
20#[macro_export]
21#[doc(hidden)]
22macro_rules! rusty_peg_parser {
23    { parser $name:ident: $base:ty { $($grammar_defn:tt)* } } => {
24        rusty_peg_parse_grammar_definition! {
25            rusty_peg_parser_parsed {
26                (arg ($name) ($base))
27                    (any)
28                    (def)
29                    (map)
30                    (reg)
31                    (fld)
32            }
33            $($grammar_defn)*
34        }
35    }
36}
37
38// This macro is used to parse the user's grammar definition and segregate
39// the nonterminals into various distinct categories. Ultimately, it invokes
40// `rusty_peg_parser_parsed` with a particular form:
41//
42// ```
43// rusty_peg_parser_parsed! {
44//   (arg ...)
45//   (any (<ident>, <ty>)...)
46//   (def (<ident>, <ty>, (<tt>))...)
47//   (map (<ident>, <ty>, (<tt> => <expr>))...)
48//   (reg (<ident>, <ty>, (<expr>))...)
49//   (fld (<ident>, <ty>, (<tt>))...)
50// }
51// ```
52//
53// Here:
54// -  `...` is used to mean "repeated an arbitrary number of times.
55// - `<ident>` is the identifier of some non-terminal.
56// - `<ty>` is the type the user declared for that non-terminal.
57// - `<tt>` refers to a token tree representing the definition of a nonterminal.
58// - `<expr>` refers to a Rust expression.
59//
60// As you can see, `rusty_peg_parser_parsed` is invoked with five
61// lists, each tagged with an identifier. The first list, tagged
62// `any`, contains the names and types of ALL nonterminals. Each other
63// list contains only those entries of a particular kind:
64//
65// - `map`: nonterminals defined like `FOO: u32 = ("a" "b") => { 22
66//   };`.  Here, `<ident>` would be `FOO`, `<ty>` would be `u32`,
67//   `<tt>` would be `("a" "b")`, and `<expr>` would be `{ 22 }`.
68// - `def`: nonterminals defined like `FOO: &'static str =
69//   "bar";`. Here, `<ident>` would be Foo`, `<ty>` would be `&'static
70//   str`, and `<tt>` would be `"bar".
71// - `reg`: nonterminals defined like `FOO: &'input str = regex(r"something");`
72//   or `FOO: &'input str = regex(r"something") - ["a", "b"];`
73//
74// These strings are deliberately constructed to be easy to match and
75// work with in subsequent macro definitions.
76#[macro_export]
77#[doc(hidden)]
78macro_rules! rusty_peg_parse_grammar_definition {
79    // Base case.
80    {
81        $m:ident {
82            (arg $($args:tt)*)
83                (any $(($any_nt:ident, $any_ty:ty))*)
84                (def $(($def_nt:ident, $def_ty:ty, $def_tt:tt))*)
85                (map $(($map_nt:ident, $map_ty:ty, $map_tt:tt))*)
86                (reg $(($reg_nt:ident, $reg_ty:ty, $reg_tt:tt))*)
87                (fld $(($fld_nt:ident, $fld_ty:ty, $fld_tt:tt))*)
88        }
89    } => {
90        rusty_peg_parser_parsed! {
91            (arg $($args)*)
92            (any $(($any_nt, $any_ty))*)
93            (def $(($def_nt, $def_ty, $def_tt))*)
94            (map $(($map_nt, $map_ty, $map_tt))*)
95            (reg $(($reg_nt, $reg_ty, $reg_tt))*)
96            (fld $(($fld_nt, $fld_ty, $fld_tt))*)
97        }
98    };
99
100    // Match a "map" definition:
101    {
102        $m:ident {
103            (arg $($args:tt)*)
104                (any $(($any_nt:ident, $any_ty:ty))*)
105                (def $(($def_nt:ident, $def_ty:ty, $def_tt:tt))*)
106                (map $(($map_nt:ident, $map_ty:ty, $map_tt:tt))*)
107                (reg $(($reg_nt:ident, $reg_ty:ty, $reg_tt:tt))*)
108                (fld $(($fld_nt:ident, $fld_ty:ty, $fld_tt:tt))*)
109        }
110        $nonterminal:ident: $ty:ty = $defn:tt => $body:expr ;
111        $($remainder:tt)*
112    } => {
113        rusty_peg_parse_grammar_definition! {
114            $m {
115                (arg $($args)*)
116                    (any $(($any_nt, $any_ty))* ($nonterminal, $ty))
117                    (def $(($def_nt, $def_ty, $def_tt))*)
118                    (map $(($map_nt, $map_ty, $map_tt))* ($nonterminal, $ty, ($defn => $body)))
119                    (reg $(($reg_nt, $reg_ty, $reg_tt))*)
120                    (fld $(($fld_nt, $fld_ty, $fld_tt))*)
121            }
122            $($remainder)*
123        }
124    };
125
126    // Match a "equate" definition:
127    {
128        $m:ident {
129            (arg $($args:tt)*)
130                (any $(($any_nt:ident, $any_ty:ty))*)
131                (def $(($def_nt:ident, $def_ty:ty, $def_tt:tt))*)
132                (map $(($map_nt:ident, $map_ty:ty, $map_tt:tt))*)
133                (reg $(($reg_nt:ident, $reg_ty:ty, $reg_tt:tt))*)
134                (fld $(($fld_nt:ident, $fld_ty:ty, $fld_tt:tt))*)
135        }
136        $nonterminal:ident: $ty:ty = $defn:tt ;
137        $($remainder:tt)*
138    } => {
139        rusty_peg_parse_grammar_definition! {
140            $m {
141                (arg $($args)*)
142                    (any $(($any_nt, $any_ty))* ($nonterminal, $ty))
143                    (def $(($def_nt, $def_ty, $def_tt))* ($nonterminal, $ty, ($defn)))
144                    (map $(($map_nt, $map_ty, $map_tt))*)
145                    (reg $(($reg_nt, $reg_ty, $reg_tt))*)
146                    (fld $(($fld_nt, $fld_ty, $fld_tt))*)
147            }
148            $($remainder)*
149        }
150    };
151
152    // Match a "regexp" definition with no exceptions:
153    {
154        $m:ident {
155            (arg $($args:tt)*)
156                (any $(($any_nt:ident, $any_ty:ty))*)
157                (def $(($def_nt:ident, $def_ty:ty, $def_tt:tt))*)
158                (map $(($map_nt:ident, $map_ty:ty, $map_tt:tt))*)
159                (reg $(($reg_nt:ident, $reg_ty:ty, $reg_tt:tt))*)
160                (fld $(($fld_nt:ident, $fld_ty:ty, $fld_tt:tt))*)
161        }
162        $nonterminal:ident: $ty:ty = regex($defn:expr);
163        $($remainder:tt)*
164    } => {
165        rusty_peg_parse_grammar_definition! {
166            $m {
167                (arg $($args)*)
168                    (any $(($any_nt, $any_ty))* ($nonterminal, $ty))
169                    (def $(($def_nt, $def_ty, $def_tt))*)
170                    (map $(($map_nt, $map_ty, $map_tt))*)
171                    (reg $(($reg_nt, $reg_ty, $reg_tt))* ($nonterminal, $ty, ($defn, [])))
172                    (fld $(($fld_nt, $fld_ty, $fld_tt))*)
173            }
174            $($remainder)*
175        }
176    };
177
178    // Match a "regexp" definition with exceptions:
179    {
180        $m:ident {
181            (arg $($args:tt)*)
182                (any $(($any_nt:ident, $any_ty:ty))*)
183                (def $(($def_nt:ident, $def_ty:ty, $def_tt:tt))*)
184                (map $(($map_nt:ident, $map_ty:ty, $map_tt:tt))*)
185                (reg $(($reg_nt:ident, $reg_ty:ty, $reg_tt:tt))*)
186                (fld $(($fld_nt:ident, $fld_ty:ty, $fld_tt:tt))*)
187        }
188        $nonterminal:ident: $ty:ty = regex($defn:expr) - [ $($exceptions:expr),* ];
189        $($remainder:tt)*
190    } => {
191        rusty_peg_parse_grammar_definition! {
192            $m {
193                (arg $($args)*)
194                    (any $(($any_nt, $any_ty))* ($nonterminal, $ty))
195                    (def $(($def_nt, $def_ty, $def_tt))*)
196                    (map $(($map_nt, $map_ty, $map_tt))*)
197                    (reg $(($reg_nt, $reg_ty, $reg_tt))*
198                         ($nonterminal, $ty, ($defn, [$($exceptions),*])))
199                    (fld $(($fld_nt, $fld_ty, $fld_tt))*)
200            }
201            $($remainder)*
202        }
203    };
204
205    // Match a "fold" definition:
206    {
207        $m:ident {
208            (arg $($args:tt)*)
209                (any $(($any_nt:ident, $any_ty:ty))*)
210                (def $(($def_nt:ident, $def_ty:ty, $def_tt:tt))*)
211                (map $(($map_nt:ident, $map_ty:ty, $map_tt:tt))*)
212                (reg $(($reg_nt:ident, $reg_ty:ty, $reg_tt:tt))*)
213                (fld $(($fld_nt:ident, $fld_ty:ty, $fld_tt:tt))*)
214        }
215        $nonterminal:ident: $ty:ty = fold(<$lhs_nm:ident:$lhs_defn:tt>,
216                                          $($rhs_defn:tt => $rhs_expr:expr),+);
217        $($remainder:tt)*
218    } => {
219        rusty_peg_parse_grammar_definition! {
220            $m {
221                (arg $($args)*)
222                    (any $(($any_nt, $any_ty))* ($nonterminal, $ty))
223                    (def $(($def_nt, $def_ty, $def_tt))*)
224                    (map $(($map_nt, $map_ty, $map_tt))*)
225                    (reg $(($reg_nt, $reg_ty, $reg_tt))*)
226                    (fld $(($fld_nt, $fld_ty, $fld_tt))*
227                         ($nonterminal, $ty,
228                          (fold($lhs_nm,$lhs_defn,$(($rhs_defn,$rhs_expr)),+))))
229            }
230            $($remainder)*
231        }
232    };
233}
234
235// Invoked by rusty_peg_parse_grammar_definition, actually generates
236// the parser structs and so forth.
237#[macro_export]
238#[doc(hidden)]
239macro_rules! rusty_peg_parser_parsed {
240    {
241        (arg ($name:ident) ($base:ty))
242            (any $(($any_nt:ident, $any_ty:ty))+)
243            (def $(($def_nt:ident, $def_ty:ty, ($def_tt:tt)))*)
244            (map $(($map_nt:ident, $map_ty:ty, ($map_tt:tt => $map_expr:expr)))*)
245            (reg $(($reg_nt:ident, $reg_ty:ty, ($reg_re:expr, [$($reg_exn:expr),*])))*)
246            (fld $(($fld_nt:ident, $fld_ty:ty, $fld_tt:tt))*)
247    } => {
248        pub struct $name<'input> {
249            marker: $crate::util::PhantomData<&'input()>,
250            pub base: $base,
251            caches: Caches<'input>,
252            regexs: Regexs
253        }
254
255        #[allow(non_snake_case)]
256        struct Caches<'input> {
257            __dummy__: (),
258            $($any_nt: $crate::Cache<'input,$any_ty>),*
259        }
260
261        #[allow(non_snake_case)]
262        struct Regexs {
263            __dummy__: (),
264            $($reg_nt: $crate::util::Rc<$crate::util::RegexNt>),*
265        }
266
267        impl<'input> $name<'input> {
268            pub fn new(base: $base) -> $name<'input> {
269                $name {
270                    marker: $crate::util::PhantomData,
271                    base: base,
272                    caches: Caches { __dummy__: (), $($any_nt: $crate::util::HashMap::new()),* },
273                    regexs: Regexs {
274                        __dummy__: (),
275                        $($reg_nt: $crate::util::Rc::new($crate::util::RegexNt::new(
276                            $reg_re,
277                            vec![$($reg_exn),*].into_iter()
278                                               .map(|t: &'static str| t.to_string())
279                                               .collect()))),*
280                    },
281                }
282            }
283        }
284
285        $(rusty_peg_declare_map_nonterminal!{$name, $map_nt, $map_ty, $map_tt, $map_expr})*
286        $(rusty_peg_declare_identity_nonterminal!{$name, $def_nt, $def_ty, $def_tt})*
287        $(rusty_peg_declare_regexp_nonterminal!{$name, $reg_nt, $reg_ty})*
288        $(rusty_peg_declare_fold_nonterminal!{$name, $fld_nt, $fld_ty, $fld_tt})*
289    }
290}
291
292#[macro_export]
293#[doc(hidden)]
294macro_rules! rusty_peg_declare_map_nonterminal {
295    ($grammar:ident, $nonterminal:ident, $ty:ty, $defn:tt, $body:expr) => {
296        #[allow(non_camel_case_types)]
297        #[derive(Copy, Clone, Debug)]
298        pub struct $nonterminal;
299
300        impl<'input> $crate::Symbol<'input,$grammar<'input>> for $nonterminal {
301            type Output = $ty;
302
303            fn pretty_print(&self) -> String {
304                stringify!($nonterminal).to_string()
305            }
306
307            fn parse(&self,
308                     grammar: &mut $grammar<'input>,
309                     start: $crate::Input<'input>)
310                     -> $crate::ParseResult<'input,$ty>
311            {
312                $crate::util::memoize(
313                    grammar,
314                    |g| &mut g.caches.$nonterminal,
315                    start.offset,
316                    |g| {
317                        let parser = rusty_peg_named_item!($defn);
318                        let (end, rusty_peg_named_item_pat!($defn)) =
319                            try!($crate::Symbol::parse(&parser, g, start));
320                        Ok((end,$body))
321                    })
322            }
323        }
324    }
325}
326
327#[macro_export]
328#[doc(hidden)]
329macro_rules! rusty_peg_declare_identity_nonterminal {
330    ($grammar:ident, $nonterminal:ident, $ty:ty, $defn:tt) => {
331        #[allow(non_camel_case_types)]
332        #[derive(Copy, Clone, Debug)]
333        pub struct $nonterminal;
334
335        impl<'input> $crate::Symbol<'input,$grammar<'input>> for $nonterminal {
336            type Output = $ty;
337
338            fn pretty_print(&self) -> String {
339                stringify!($nonterminal).to_string()
340            }
341
342            fn parse(&self,
343                     grammar: &mut $grammar<'input>,
344                     start: $crate::Input<'input>)
345                     -> $crate::ParseResult<'input,$ty>
346            {
347                $crate::util::memoize(
348                    grammar,
349                    |g| &mut g.caches.$nonterminal,
350                    start.offset,
351                    |g| {
352                        let parser = rusty_peg_item!($defn);
353                        $crate::Symbol::parse(&parser, g, start)
354                    })
355            }
356        }
357    }
358}
359
360#[macro_export]
361#[doc(hidden)]
362macro_rules! rusty_peg_declare_regexp_nonterminal {
363    ($grammar:ident, $nonterminal:ident, $ty:ty) => {
364        #[allow(non_camel_case_types)]
365        #[derive(Copy, Clone, Debug)]
366        pub struct $nonterminal;
367
368        impl<'input> $crate::Symbol<'input,$grammar<'input>> for $nonterminal {
369            type Output = $ty;
370
371            fn pretty_print(&self) -> String {
372                stringify!($nonterminal).to_string()
373            }
374
375            fn parse(&self,
376                     grammar: &mut $grammar<'input>,
377                     start: $crate::Input<'input>)
378                     -> $crate::ParseResult<'input,$ty>
379            {
380                let regex = grammar.regexs.$nonterminal.clone();
381                $crate::util::memoize(
382                    grammar,
383                    |grammar| &mut grammar.caches.$nonterminal,
384                    start.offset,
385                    |grammar| {
386                        $crate::Symbol::parse(
387                            &*regex,
388                            grammar,
389                            start)
390                    })
391            }
392        }
393    }
394}
395
396#[macro_export]
397#[doc(hidden)]
398macro_rules! rusty_peg_declare_fold_nonterminal {
399    ($grammar:ident, $nonterminal:ident, $ty:ty,
400     (fold($lhs_nm:ident,$lhs_defn:tt,$(($rhs_defn:tt,$rhs_expr:expr)),+))) => {
401        #[allow(non_camel_case_types)]
402        #[derive(Copy, Clone, Debug)]
403        pub struct $nonterminal;
404
405        impl<'input> $crate::Symbol<'input,$grammar<'input>> for $nonterminal {
406            type Output = $ty;
407
408            fn pretty_print(&self) -> String {
409                stringify!($nonterminal).to_string()
410            }
411
412            fn parse(&self,
413                     grammar: &mut $grammar<'input>,
414                     start: $crate::Input<'input>)
415                     -> $crate::ParseResult<'input,$ty>
416            {
417                $crate::util::memoize(
418                    grammar,
419                    |grammar| &mut grammar.caches.$nonterminal,
420                    start.offset,
421                    |grammar| {
422                        let lhs_parser = rusty_peg_item!($lhs_defn);
423                        let (mut mid, mut $lhs_nm) =
424                            try!($crate::Symbol::parse(&lhs_parser,
425                                                       grammar,
426                                                       start));
427                        loop {
428                            mid = $crate::util::skip_whitespace(mid);
429
430                            $(
431                                let rhs_parser = rusty_peg_named_item!($rhs_defn);
432                                match $crate::Symbol::parse(&rhs_parser, grammar, mid) {
433                                    Ok((end, rusty_peg_named_item_pat!($rhs_defn))) => {
434                                        $lhs_nm = $rhs_expr;
435                                        mid = end;
436                                        continue;
437                                    }
438                                    Err(_) => { }
439                                }
440                                )+
441
442                                break;
443                        }
444
445                        Ok((mid, $lhs_nm))
446                    })
447            }
448        }
449    }
450}
451
452#[macro_export]
453#[doc(hidden)]
454macro_rules! rusty_peg_named_item {
455    ( ( $($a:tt)* ) ) => {
456        rusty_peg_named_items!($($a)*)
457    };
458    ( $a:tt ) => {
459        rusty_peg_item!($a)
460    }
461}
462
463#[macro_export]
464#[doc(hidden)]
465macro_rules! rusty_peg_named_items {
466    ( < $name:ident : $a:tt > , $($bs:tt)* ) => {
467        {
468            let bs = rusty_peg_named_items!($($bs)*);
469            rusty_peg_items!($a, bs)
470        }
471    };
472    ( < $name:ident : $a:tt > ) => {
473        rusty_peg_item!($a)
474    };
475    ( $a:tt, $($bs:tt)* ) => {
476        {
477            let bs = rusty_peg_named_items!($($bs)*);
478            rusty_peg_items!($a, bs)
479        }
480    };
481    ( $a:tt ) => {
482        rusty_peg_item!($a)
483    };
484    ( ) => {
485        Empty
486    };
487}
488
489#[macro_export]
490#[doc(hidden)]
491macro_rules! rusty_peg_named_item_pat {
492    ( ( $($a:tt)* ) ) => {
493        rusty_peg_named_items_pat!($($a)*)
494    };
495    ( $a:tt ) => {
496        _
497    }
498}
499
500#[macro_export]
501#[doc(hidden)]
502macro_rules! rusty_peg_named_items_pat {
503    ( < $name:ident : $a:tt > , $($bs:tt)* ) => {
504        ($name, rusty_peg_named_items_pat!($($bs)*))
505    };
506    ( < $name:ident : $a:tt > ) => {
507        $name
508    };
509    ( $a:tt, $($bs:tt)* ) => {
510        (_, rusty_peg_named_items_pat!($($bs)*))
511    };
512    ( $a:tt ) => {
513        _
514    };
515    ( ) => {
516        ()
517    };
518}
519
520#[macro_export]
521#[doc(hidden)]
522macro_rules! rusty_peg_items {
523    ( $a:tt, $($bs:tt)* ) => {
524        $crate::util::Join { first: rusty_peg_item!($a), second: rusty_peg_items!($($bs)*), }
525    };
526    ( $a:tt / $($bs:tt)* ) => {
527        $crate::util::Or { a: rusty_peg_item!($a), b: rusty_peg_items!($($bs)*) }
528    };
529    ( $a:tt ) => {
530        rusty_peg_item!($a)
531    };
532    ( ) => {
533        Empty
534    }
535}
536
537#[macro_export]
538#[doc(hidden)]
539macro_rules! rusty_peg_item {
540    { ( ) } => {
541        Empty
542    };
543
544    { ( $tt:tt ) } => {
545        rusty_peg_item!($tt)
546    };
547
548    { ( $($tt:tt)* ) } => {
549        rusty_peg_items!($($tt)*)
550    };
551
552    { [ $($tt:tt)* ] } => {
553        $crate::util::Optional { parser: rusty_peg_items!($($tt)*) }
554    };
555
556    { { + $($tt:tt)* } } => {
557        $crate::util::Repeat { parser: rusty_peg_items!($($tt)*), min: 1,
558                               separator: $crate::util::Whitespace }
559    };
560
561    { { * $($tt:tt)* } } => {
562        $crate::util::Repeat { parser: rusty_peg_items!($($tt)*), min: 0,
563                               separator: $crate::util::Whitespace }
564    };
565
566    { { $($tt:tt)* } } => {
567        $crate::util::Repeat { parser: rusty_peg_items!($($tt)*), min: 0,
568                               separator: $crate::util::Whitespace }
569    };
570
571    { $name:expr } => {
572        $name
573    };
574}
575