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