fuel_pest/
macros.rs

1// pest. The Elegant Parser
2// Copyright (c) 2018 DragoČ™ Tiselice
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10#[doc(hidden)]
11#[macro_export]
12macro_rules! consumes_to {
13    ( $_rules:ident, $tokens:expr, [] ) => ();
14    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ) ] ) => {
15        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
16                               $rules::$name, $start);
17        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
18            $crate::Token::Start { rule, pos } => {
19                if rule != $rules::$name || pos.pos() != $start {
20                    panic!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
21                                      expected, rule, pos.pos());
22                }
23            },
24            token => panic!("{} but found {:?}", expected, token)
25        };
26
27        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
28                               $rules::$name, $end);
29        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
30            $crate::Token::End { rule, pos } => {
31                if rule != $rules::$name || pos.pos() != $end {
32                    panic!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
33                                      expected, rule, pos.pos());
34                }
35            },
36            token => panic!("{} but found {:?}", expected, token)
37        };
38    };
39    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ),
40                                    $( $names:ident $calls:tt ),* $(,)* ] ) => {
41
42        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
43                               $rules::$name, $start);
44        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
45            $crate::Token::Start { rule, pos } => {
46                if rule != $rules::$name || pos.pos() != $start {
47                    panic!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
48                                      expected, rule, pos.pos());
49                }
50            },
51            token => panic!("{} but found {:?}", expected, token)
52        };
53
54        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
55                               $rules::$name, $end);
56        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
57            $crate::Token::End { rule, pos } => {
58                if rule != $rules::$name || pos.pos() != $end {
59                    panic!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
60                                      expected, rule, pos.pos());
61                }
62            },
63            token => panic!("{} but found {:?}", expected, token)
64        };
65
66        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
67    };
68    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
69                                                  [ $( $names:ident $calls:tt ),* $(,)* ] ) ] ) => {
70        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
71                               $rules::$name, $start);
72        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
73            $crate::Token::Start { rule, pos } => {
74                if rule != $rules::$name || pos.pos() != $start {
75                    panic!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
76                                      expected, rule, pos.pos());
77                }
78            },
79            token => panic!("{} but found {:?}", expected, token)
80        };
81
82        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
83
84        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
85                               $rules::$name, $end);
86        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
87            $crate::Token::End { rule, pos } => {
88                if rule != $rules::$name || pos.pos() != $end {
89                    panic!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
90                                      expected, rule, pos.pos());
91                }
92            },
93            token => panic!("{} but found {:?}", expected, token)
94        };
95    };
96    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
97                                                  [ $( $nested_names:ident $nested_calls:tt ),*
98                                                  $(,)* ] ),
99                                    $( $names:ident $calls:tt ),* ] ) => {
100
101        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
102                               $rules::$name, $start);
103        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
104            $crate::Token::Start { rule, pos } => {
105                if rule != $rules::$name || pos.pos() != $start {
106                    panic!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
107                                      expected, rule, pos.pos());
108                }
109            },
110            token => panic!("{} but found {:?}", expected, token)
111        };
112
113        consumes_to!($rules, $tokens, [ $( $nested_names $nested_calls ),* ]);
114
115        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
116                               $rules::$name, $end);
117        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
118            $crate::Token::End { rule, pos } => {
119                if rule != $rules::$name || pos.pos() != $end {
120                    panic!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
121                                      expected, rule, pos.pos());
122                }
123            },
124            token => panic!("{} but found {:?}", expected, token)
125        };
126
127        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
128    };
129}
130
131/// Testing tool that compares produced tokens.
132///
133/// This macro takes several arguments:
134///
135/// * `parser` - name of the data structure implementing `Parser`
136/// * `input` - input to be tested against
137/// * `rule` - `Rule` which will be run
138/// * `tokens` - token pairs of the form `name(start_pos, end_pos, [nested_child_tokens])`
139///
140/// *Note:* `start_pos` and `end_pos` are byte positions.
141///
142/// # Examples
143///
144/// ```
145/// # #[macro_use]
146/// # extern crate pest;
147/// # use pest::Parser;
148/// # use pest::error::Error;
149/// # use pest::iterators::Pairs;
150/// # use std::sync::Arc;
151/// # fn main() {
152/// # #[allow(non_camel_case_types)]
153/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
154/// # enum Rule {
155/// #     a,
156/// #     b,
157/// #     c
158/// # }
159/// #
160/// # struct AbcParser;
161/// #
162/// # impl Parser<Rule> for AbcParser {
163/// #     fn parse(_: Rule, input: Arc<str>) -> Result<Pairs<Rule>, Error<Rule>> {
164/// #         pest::state(input, |state| {
165/// #             state.rule(Rule::a, |state| {
166/// #                 state.skip(1).unwrap().rule(Rule::b, |state| {
167/// #                     state.skip(1)
168/// #                 }).unwrap().skip(1)
169/// #             }).and_then(|state| {
170/// #                 state.skip(1).unwrap().rule(Rule::c, |state| {
171/// #                     state.skip(1)
172/// #                 })
173/// #             })
174/// #         })
175/// #     }
176/// # }
177/// parses_to! {
178///     parser: AbcParser,
179///     input:  Arc::from("abcde"),
180///     rule:   Rule::a,
181///     tokens: [
182///         a(0, 3, [
183///             b(1, 2)
184///         ]),
185///         c(4, 5)
186///     ]
187/// };
188/// # }
189/// ```
190#[macro_export]
191macro_rules! parses_to {
192    ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt,
193      tokens: [ $( $names:ident $calls:tt ),* $(,)* ] ) => {
194
195        #[allow(unused_mut)]
196        {
197            use $crate::Parser;
198
199            let mut tokens = $parser::parse($rules::$rule, $string).unwrap().tokens();
200
201            consumes_to!($rules, &mut tokens, [ $( $names $calls ),* ]);
202
203            let rest: Vec<_> = tokens.collect();
204
205            match rest.len() {
206                0 => (),
207                2 => {
208                    let (first, second) = (&rest[0], &rest[1]);
209
210                    match (first, second) {
211                        (
212                            &$crate::Token::Start { rule: ref first_rule, .. },
213                            &$crate::Token::End { rule: ref second_rule, .. }
214                        ) => {
215                            assert!(
216                                format!("{:?}", first_rule) == "EOI",
217                                "expected end of input, but found {:?}", rest
218                            );
219                            assert!(
220                                format!("{:?}", second_rule) == "EOI",
221                                "expected end of input, but found {:?}", rest
222                            );
223                        }
224                        _ => panic!("expected end of input, but found {:?}", rest)
225                    }
226                }
227                _ => panic!("expected end of input, but found {:?}", rest)
228            };
229        }
230    };
231}
232
233/// Testing tool that compares produced errors.
234///
235/// This macro takes several arguments:
236///
237/// * `parser` - name of the data structure implementing `Parser`
238/// * `input` - input to be tested against
239/// * `rule` - `Rule` which will be run
240/// * `positives` - positive `Rule` attempts that failed
241/// * `negative` - negative `Rule` attempts that failed
242/// * `pos` - byte position of failure
243///
244/// # Examples
245///
246/// ```
247/// # #[macro_use]
248/// # extern crate pest;
249/// # use pest::Parser;
250/// # use pest::error::Error;
251/// # use pest::iterators::Pairs;
252/// # use std::sync::Arc;
253/// # fn main() {
254/// # #[allow(non_camel_case_types)]
255/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
256/// # enum Rule {
257/// #     a,
258/// #     b,
259/// #     c
260/// # }
261/// #
262/// # struct AbcParser;
263/// #
264/// # impl Parser<Rule> for AbcParser {
265/// #     fn parse(_: Rule, input: Arc<str>) -> Result<Pairs<Rule>, Error<Rule>> {
266/// #         pest::state(input, |state| {
267/// #             state.rule(Rule::a, |state| {
268/// #                 state.skip(1).unwrap().rule(Rule::b, |s| {
269/// #                     s.skip(1)
270/// #                 }).unwrap().skip(1)
271/// #             }).and_then(|state| {
272/// #                 state.skip(1).unwrap().rule(Rule::c, |s| {
273/// #                     s.match_string("e")
274/// #                 })
275/// #             })
276/// #         })
277/// #     }
278/// # }
279/// fails_with! {
280///     parser: AbcParser,
281///     input: Arc::from("abcdf"),
282///     rule: Rule::a,
283///     positives: vec![Rule::c],
284///     negatives: vec![],
285///     pos: 4
286/// };
287/// # }
288/// ```
289#[macro_export]
290macro_rules! fails_with {
291    ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt,
292      positives: $positives:expr, negatives: $negatives:expr, pos: $pos:expr ) => {
293        #[allow(unused_mut)]
294        {
295            use $crate::Parser;
296
297            let error = $parser::parse($rules::$rule, $string).unwrap_err();
298
299            match error.variant {
300                $crate::error::ErrorVariant::ParsingError {
301                    positives,
302                    negatives,
303                } => {
304                    assert_eq!(positives, $positives);
305                    assert_eq!(negatives, $negatives);
306                }
307                _ => unreachable!(),
308            };
309
310            match error.location {
311                $crate::error::InputLocation::Pos(pos) => assert_eq!(pos, $pos),
312                _ => unreachable!(),
313            }
314        }
315    };
316}
317
318#[cfg(test)]
319pub mod tests {
320    use super::super::error::Error;
321    use super::super::iterators::Pairs;
322    use super::super::{state, Parser};
323    use alloc::format;
324    use alloc::vec;
325    use alloc::vec::Vec;
326    use std::sync::Arc;
327
328    #[allow(non_camel_case_types)]
329    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
330    pub enum Rule {
331        a,
332        b,
333        c,
334    }
335
336    pub struct AbcParser;
337
338    impl Parser<Rule> for AbcParser {
339        fn parse(_: Rule, input: Arc<str>) -> Result<Pairs<Rule>, Error<Rule>> {
340            state(input, |state| {
341                state
342                    .rule(Rule::a, |s| {
343                        s.skip(1)
344                            .unwrap()
345                            .rule(Rule::b, |s| s.skip(1))
346                            .unwrap()
347                            .skip(1)
348                    })
349                    .and_then(|s| s.skip(1).unwrap().rule(Rule::c, |s| s.match_string("e")))
350            })
351        }
352    }
353
354    #[test]
355    fn parses_to() {
356        parses_to! {
357            parser: AbcParser,
358            input: Arc::from("abcde"),
359            rule: Rule::a,
360            tokens: [
361                a(0, 3, [
362                    b(1, 2),
363                ]),
364                c(4, 5)
365            ]
366        };
367    }
368
369    #[test]
370    #[should_panic]
371    fn missing_end() {
372        parses_to! {
373            parser: AbcParser,
374            input: Arc::from("abcde"),
375            rule: Rule::a,
376            tokens: [
377                a(0, 3, [
378                    b(1, 2)
379                ])
380            ]
381        };
382    }
383
384    #[test]
385    #[should_panic]
386    fn empty() {
387        parses_to! {
388            parser: AbcParser,
389            input: Arc::from("abcde"),
390            rule: Rule::a,
391            tokens: []
392        };
393    }
394
395    #[test]
396    fn fails_with() {
397        fails_with! {
398            parser: AbcParser,
399            input: Arc::from("abcdf"),
400            rule: Rule::a,
401            positives: vec![Rule::c],
402            negatives: vec![],
403            pos: 4
404        };
405    }
406
407    #[test]
408    #[should_panic]
409    fn wrong_positives() {
410        fails_with! {
411            parser: AbcParser,
412            input: Arc::from("abcdf"),
413            rule: Rule::a,
414            positives: vec![Rule::a],
415            negatives: vec![],
416            pos: 4
417        };
418    }
419
420    #[test]
421    #[should_panic]
422    fn wrong_negatives() {
423        fails_with! {
424            parser: AbcParser,
425            input: Arc::from("abcdf"),
426            rule: Rule::a,
427            positives: vec![Rule::c],
428            negatives: vec![Rule::c],
429            pos: 4
430        };
431    }
432
433    #[test]
434    #[should_panic]
435    fn wrong_pos() {
436        fails_with! {
437            parser: AbcParser,
438            input: Arc::from("abcdf"),
439            rule: Rule::a,
440            positives: vec![Rule::c],
441            negatives: vec![],
442            pos: 3
443        };
444    }
445}