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 assert!(
20 rule == $rules::$name && pos.pos() == $start,
21 "{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
22 expected, rule, pos.pos(),
23 )
24 },
25 token => panic!("{} but found {:?}", expected, token)
26 };
27
28 let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
29 $rules::$name, $end);
30 match $tokens.next().expect(&format!("{} but found nothing", expected)) {
31 $crate::Token::End { rule, pos } => {
32 assert!(rule == $rules::$name && pos.pos() == $end,
33 "{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
34 expected, rule, pos.pos(),
35 );
36 },
37 token => panic!("{} but found {:?}", expected, token)
38 };
39 };
40 ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ),
41 $( $names:ident $calls:tt ),* $(,)* ] ) => {
42
43 let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
44 $rules::$name, $start);
45 match $tokens.next().expect(&format!("{} but found nothing", expected)) {
46 $crate::Token::Start { rule, pos } => {
47 assert!(rule == $rules::$name && pos.pos() == $start,
48 "{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
49 expected, rule, pos.pos(),
50 );
51 },
52 token => panic!("{} but found {:?}", expected, token)
53 };
54
55 let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
56 $rules::$name, $end);
57 match $tokens.next().expect(&format!("{} but found nothing", expected)) {
58 $crate::Token::End { rule, pos } => {
59 assert!(rule == $rules::$name && pos.pos() == $end,
60 "{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
61 expected, rule, pos.pos(),
62 );
63 },
64 token => panic!("{} but found {:?}", expected, token)
65 };
66
67 consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
68 };
69 ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
70 [ $( $names:ident $calls:tt ),* $(,)* ] ) ] ) => {
71 let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
72 $rules::$name, $start);
73 match $tokens.next().expect(&format!("{} but found nothing", expected)) {
74 $crate::Token::Start { rule, pos } => {
75 assert!(rule == $rules::$name && pos.pos() == $start,
76 "{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
77 expected, rule, pos.pos(),
78 );
79 },
80 token => panic!("{} but found {:?}", expected, token)
81 };
82
83 consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
84
85 let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
86 $rules::$name, $end);
87 match $tokens.next().expect(&format!("{} but found nothing", expected)) {
88 $crate::Token::End { rule, pos } => {
89 assert!(rule == $rules::$name && pos.pos() == $end,
90 "{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
91 expected, rule, pos.pos(),
92 );
93 },
94 token => panic!("{} but found {:?}", expected, token)
95 };
96 };
97 ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
98 [ $( $nested_names:ident $nested_calls:tt ),*
99 $(,)* ] ),
100 $( $names:ident $calls:tt ),* ] ) => {
101
102 let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
103 $rules::$name, $start);
104 match $tokens.next().expect(&format!("{} but found nothing", expected)) {
105 $crate::Token::Start { rule, pos } => {
106 assert!(rule == $rules::$name && pos.pos() == $start,
107 "{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
108 expected, rule, pos.pos(),
109 );
110 },
111 token => panic!("{} but found {:?}", expected, token)
112 };
113
114 consumes_to!($rules, $tokens, [ $( $nested_names $nested_calls ),* ]);
115
116 let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
117 $rules::$name, $end);
118 match $tokens.next().expect(&format!("{} but found nothing", expected)) {
119 $crate::Token::End { rule, pos } => {
120 assert!(rule == $rules::$name && pos.pos() == $end,
121 "{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
122 expected, rule, pos.pos(),
123 );
124 },
125 token => panic!("{} but found {:?}", expected, token)
126 };
127
128 consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
129 };
130}
131
132/// Testing tool that compares produced tokens.
133///
134/// This macro takes several arguments:
135///
136/// * `parser` - name of the data structure implementing `Parser`
137/// * `input` - input to be tested against
138/// * `rule` - `Rule` which will be run
139/// * `tokens` - token pairs of the form `name(start_pos, end_pos, [nested_child_tokens])`
140///
141/// *Note:* `start_pos` and `end_pos` are byte positions.
142///
143/// # Examples
144///
145/// ```
146/// # #[macro_use]
147/// # extern crate pest;
148/// # use pest::Parser;
149/// # use pest::error::Error;
150/// # use pest::iterators::Pairs;
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<'i>(_: Rule, input: &'i str) -> Result<Pairs<'i, 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: "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/// * `negatives` - 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/// # fn main() {
253/// # #[allow(non_camel_case_types)]
254/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
255/// # enum Rule {
256/// # a,
257/// # b,
258/// # c
259/// # }
260/// #
261/// # struct AbcParser;
262/// #
263/// # impl Parser<Rule> for AbcParser {
264/// # fn parse<'i>(_: Rule, input: &'i str) -> Result<Pairs<'i, Rule>, Error<Rule>> {
265/// # pest::state(input, |state| {
266/// # state.rule(Rule::a, |state| {
267/// # state.skip(1).unwrap().rule(Rule::b, |s| {
268/// # s.skip(1)
269/// # }).unwrap().skip(1)
270/// # }).and_then(|state| {
271/// # state.skip(1).unwrap().rule(Rule::c, |s| {
272/// # s.match_string("e")
273/// # })
274/// # })
275/// # })
276/// # }
277/// # }
278/// fails_with! {
279/// parser: AbcParser,
280/// input: "abcdf",
281/// rule: Rule::a,
282/// positives: vec![Rule::c],
283/// negatives: vec![],
284/// pos: 4
285/// };
286/// # }
287/// ```
288#[macro_export]
289macro_rules! fails_with {
290 ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt,
291 positives: $positives:expr, negatives: $negatives:expr, pos: $pos:expr ) => {
292 #[allow(unused_mut)]
293 {
294 use $crate::Parser;
295
296 let error = $parser::parse($rules::$rule, $string).unwrap_err();
297
298 match error.variant {
299 $crate::error::ErrorVariant::ParsingError {
300 positives,
301 negatives,
302 } => {
303 assert_eq!(positives, $positives, "positives");
304 assert_eq!(negatives, $negatives, "negatives");
305 }
306 _ => unreachable!(),
307 };
308
309 match error.location {
310 $crate::error::InputLocation::Pos(pos) => assert_eq!(pos, $pos, "pos"),
311 _ => unreachable!(),
312 }
313 }
314 };
315}
316
317#[cfg(test)]
318pub mod tests {
319 use super::super::error::Error;
320 use super::super::iterators::Pairs;
321 use super::super::{state, Parser};
322 use alloc::format;
323 use alloc::vec;
324 use alloc::vec::Vec;
325
326 #[allow(non_camel_case_types)]
327 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
328 pub enum Rule {
329 a,
330 b,
331 c,
332 d,
333 }
334
335 pub struct AbcParser;
336
337 impl Parser<Rule> for AbcParser {
338 fn parse(_: Rule, input: &str) -> Result<Pairs<'_, Rule>, Error<Rule>> {
339 state(input, |state| {
340 state
341 .rule(Rule::a, |s| {
342 s.skip(1)
343 .unwrap()
344 .rule(Rule::b, |s| s.skip(1))
345 .unwrap()
346 .skip(1)
347 })
348 .and_then(|s| s.skip(1).unwrap().rule(Rule::c, |s| s.match_string("e")))
349 .and_then(|s| s.optional(|s| s.rule(Rule::d, |s| s.match_string("fgh"))))
350 })
351 }
352 }
353
354 #[test]
355 fn parses_to() {
356 parses_to! {
357 parser: AbcParser,
358 input: "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: "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: "abcde",
390 rule: Rule::a,
391 tokens: []
392 };
393 }
394
395 #[test]
396 fn fails_with() {
397 fails_with! {
398 parser: AbcParser,
399 input: "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: "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: "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: "abcdf",
439 rule: Rule::a,
440 positives: vec![Rule::c],
441 negatives: vec![],
442 pos: 3
443 };
444 }
445}
446