diff options
Diffstat (limited to 'vendor/pest/src/macros.rs')
-rw-r--r-- | vendor/pest/src/macros.rs | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/vendor/pest/src/macros.rs b/vendor/pest/src/macros.rs new file mode 100644 index 000000000..01f410610 --- /dev/null +++ b/vendor/pest/src/macros.rs @@ -0,0 +1,455 @@ +// pest. The Elegant Parser +// Copyright (c) 2018 DragoČ™ Tiselice +// +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. All files in the project carrying such notice may not be copied, +// modified, or distributed except according to those terms. + +#[doc(hidden)] +#[macro_export] +macro_rules! consumes_to { + ( $_rules:ident, $tokens:expr, [] ) => (); + ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ) ] ) => { + let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $start); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::Start { rule, pos } => { + let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $start { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + + let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $end); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::End { rule, pos } => { + let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $end { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + }; + ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ), + $( $names:ident $calls:tt ),* $(,)* ] ) => { + + let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $start); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::Start { rule, pos } => { + let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $start { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + + let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $end); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::End { rule, pos } => { + let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $end { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + + consumes_to!($rules, $tokens, [ $( $names $calls ),* ]); + }; + ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr, + [ $( $names:ident $calls:tt ),* $(,)* ] ) ] ) => { + let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $start); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::Start { rule, pos } => { + let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $start { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + + consumes_to!($rules, $tokens, [ $( $names $calls ),* ]); + + let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $end); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::End { rule, pos } => { + let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $end { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + }; + ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr, + [ $( $nested_names:ident $nested_calls:tt ),* + $(,)* ] ), + $( $names:ident $calls:tt ),* ] ) => { + + let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $start); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::Start { rule, pos } => { + let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $start { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + + consumes_to!($rules, $tokens, [ $( $nested_names $nested_calls ),* ]); + + let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}", + $rules::$name, $end); + match $tokens.next().expect(&format!("{} but found nothing", expected)) { + $crate::Token::End { rule, pos } => { + let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}", + expected, rule, pos.pos()); + + if rule != $rules::$name || pos.pos() != $end { + panic!("{}", message); + } + }, + token => panic!("{}", format!("{} but found {:?}", expected, token)) + }; + + consumes_to!($rules, $tokens, [ $( $names $calls ),* ]); + }; +} + +/// Testing tool that compares produced tokens. +/// +/// This macro takes several arguments: +/// +/// * `parser` - name of the data structure implementing `Parser` +/// * `input` - input to be tested against +/// * `rule` - `Rule` which will be run +/// * `tokens` - token pairs of the form `name(start_pos, end_pos, [nested_child_tokens])` +/// +/// *Note:* `start_pos` and `end_pos` are byte positions. +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] +/// # extern crate pest; +/// # use pest::Parser; +/// # use pest::error::Error; +/// # use pest::iterators::Pairs; +/// # fn main() { +/// # #[allow(non_camel_case_types)] +/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +/// # enum Rule { +/// # a, +/// # b, +/// # c +/// # } +/// # +/// # struct AbcParser; +/// # +/// # impl Parser<Rule> for AbcParser { +/// # fn parse<'i>(_: Rule, input: &'i str) -> Result<Pairs<'i, Rule>, Error<Rule>> { +/// # pest::state(input, |state| { +/// # state.rule(Rule::a, |state| { +/// # state.skip(1).unwrap().rule(Rule::b, |state| { +/// # state.skip(1) +/// # }).unwrap().skip(1) +/// # }).and_then(|state| { +/// # state.skip(1).unwrap().rule(Rule::c, |state| { +/// # state.skip(1) +/// # }) +/// # }) +/// # }) +/// # } +/// # } +/// parses_to! { +/// parser: AbcParser, +/// input: "abcde", +/// rule: Rule::a, +/// tokens: [ +/// a(0, 3, [ +/// b(1, 2) +/// ]), +/// c(4, 5) +/// ] +/// }; +/// # } +/// ``` +#[macro_export] +macro_rules! parses_to { + ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt, + tokens: [ $( $names:ident $calls:tt ),* $(,)* ] ) => { + + #[allow(unused_mut)] + { + use $crate::Parser; + + let mut tokens = $parser::parse($rules::$rule, $string).unwrap().tokens(); + + consumes_to!($rules, &mut tokens, [ $( $names $calls ),* ]); + + let rest: Vec<_> = tokens.collect(); + + match rest.len() { + 0 => (), + 2 => { + let (first, second) = (&rest[0], &rest[1]); + + match (first, second) { + ( + &$crate::Token::Start { rule: ref first_rule, .. }, + &$crate::Token::End { rule: ref second_rule, .. } + ) => { + assert!( + format!("{:?}", first_rule) == "EOI", + format!("expected end of input, but found {:?}", rest) + ); + assert!( + format!("{:?}", second_rule) == "EOI", + format!("expected end of input, but found {:?}", rest) + ); + } + _ => panic!("expected end of input, but found {:?}", rest) + } + } + _ => panic!("expected end of input, but found {:?}", rest) + }; + } + }; +} + +/// Testing tool that compares produced errors. +/// +/// This macro takes several arguments: +/// +/// * `parser` - name of the data structure implementing `Parser` +/// * `input` - input to be tested against +/// * `rule` - `Rule` which will be run +/// * `positives` - positive `Rule` attempts that failed +/// * `negative` - negative `Rule` attempts that failed +/// * `pos` - byte position of failure +/// +/// # Examples +/// +/// ``` +/// # #[macro_use] +/// # extern crate pest; +/// # use pest::Parser; +/// # use pest::error::Error; +/// # use pest::iterators::Pairs; +/// # fn main() { +/// # #[allow(non_camel_case_types)] +/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +/// # enum Rule { +/// # a, +/// # b, +/// # c +/// # } +/// # +/// # struct AbcParser; +/// # +/// # impl Parser<Rule> for AbcParser { +/// # fn parse<'i>(_: Rule, input: &'i str) -> Result<Pairs<'i, Rule>, Error<Rule>> { +/// # pest::state(input, |state| { +/// # state.rule(Rule::a, |state| { +/// # state.skip(1).unwrap().rule(Rule::b, |s| { +/// # s.skip(1) +/// # }).unwrap().skip(1) +/// # }).and_then(|state| { +/// # state.skip(1).unwrap().rule(Rule::c, |s| { +/// # s.match_string("e") +/// # }) +/// # }) +/// # }) +/// # } +/// # } +/// fails_with! { +/// parser: AbcParser, +/// input: "abcdf", +/// rule: Rule::a, +/// positives: vec![Rule::c], +/// negatives: vec![], +/// pos: 4 +/// }; +/// # } +/// ``` +#[macro_export] +macro_rules! fails_with { + ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt, + positives: $positives:expr, negatives: $negatives:expr, pos: $pos:expr ) => { + #[allow(unused_mut)] + { + use $crate::Parser; + + let error = $parser::parse($rules::$rule, $string).unwrap_err(); + + match error.variant { + $crate::error::ErrorVariant::ParsingError { + positives, + negatives, + } => { + assert_eq!(positives, $positives); + assert_eq!(negatives, $negatives); + } + _ => unreachable!(), + }; + + match error.location { + $crate::error::InputLocation::Pos(pos) => assert_eq!(pos, $pos), + _ => unreachable!(), + } + } + }; +} + +#[cfg(test)] +pub mod tests { + use super::super::error::Error; + use super::super::iterators::Pairs; + use super::super::{state, Parser}; + + #[allow(non_camel_case_types)] + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub enum Rule { + a, + b, + c, + } + + pub struct AbcParser; + + impl Parser<Rule> for AbcParser { + fn parse<'i>(_: Rule, input: &'i str) -> Result<Pairs<'i, Rule>, Error<Rule>> { + state(input, |state| { + state + .rule(Rule::a, |s| { + s.skip(1) + .unwrap() + .rule(Rule::b, |s| s.skip(1)) + .unwrap() + .skip(1) + }) + .and_then(|s| s.skip(1).unwrap().rule(Rule::c, |s| s.match_string("e"))) + }) + } + } + + #[test] + fn parses_to() { + parses_to! { + parser: AbcParser, + input: "abcde", + rule: Rule::a, + tokens: [ + a(0, 3, [ + b(1, 2), + ]), + c(4, 5) + ] + }; + } + + #[test] + #[should_panic] + fn missing_end() { + parses_to! { + parser: AbcParser, + input: "abcde", + rule: Rule::a, + tokens: [ + a(0, 3, [ + b(1, 2) + ]) + ] + }; + } + + #[test] + #[should_panic] + fn empty() { + parses_to! { + parser: AbcParser, + input: "abcde", + rule: Rule::a, + tokens: [] + }; + } + + #[test] + fn fails_with() { + fails_with! { + parser: AbcParser, + input: "abcdf", + rule: Rule::a, + positives: vec![Rule::c], + negatives: vec![], + pos: 4 + }; + } + + #[test] + #[should_panic] + fn wrong_positives() { + fails_with! { + parser: AbcParser, + input: "abcdf", + rule: Rule::a, + positives: vec![Rule::a], + negatives: vec![], + pos: 4 + }; + } + + #[test] + #[should_panic] + fn wrong_negatives() { + fails_with! { + parser: AbcParser, + input: "abcdf", + rule: Rule::a, + positives: vec![Rule::c], + negatives: vec![Rule::c], + pos: 4 + }; + } + + #[test] + #[should_panic] + fn wrong_pos() { + fails_with! { + parser: AbcParser, + input: "abcdf", + rule: Rule::a, + positives: vec![Rule::c], + negatives: vec![], + pos: 3 + }; + } +} |