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