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 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] 191 macro_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] 289 macro_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)] 318 pub 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 { parse(_: Rule, input: &str) -> Result<Pairs<'_, Rule>, Error<Rule>>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] parses_to()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] missing_end()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] empty()386 fn empty() { 387 parses_to! { 388 parser: AbcParser, 389 input: "abcde", 390 rule: Rule::a, 391 tokens: [] 392 }; 393 } 394 395 #[test] fails_with()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] wrong_positives()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] wrong_negatives()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] wrong_pos()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