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 //! Types for the pest's abstract syntax tree. 11 12 /// A grammar rule 13 #[derive(Clone, Debug, Eq, PartialEq)] 14 pub struct Rule { 15 /// The name of the rule 16 pub name: String, 17 /// The rule's type (silent, atomic, ...) 18 pub ty: RuleType, 19 /// The rule's expression 20 pub expr: Expr, 21 } 22 23 /// All possible rule types 24 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 25 pub enum RuleType { 26 /// The normal rule type 27 Normal, 28 /// Silent rules are just like normal rules 29 /// — when run, they function the same way — 30 /// except they do not produce pairs or tokens. 31 /// If a rule is silent, it will never appear in a parse result. 32 /// (their syntax is `_{ ... }`) 33 Silent, 34 /// atomic rule prevent implicit whitespace: inside an atomic rule, 35 /// the tilde ~ means "immediately followed by", 36 /// and repetition operators (asterisk * and plus sign +) 37 /// have no implicit separation. In addition, all other rules 38 /// called from an atomic rule are also treated as atomic. 39 /// In an atomic rule, interior matching rules are silent. 40 /// (their syntax is `@{ ... }`) 41 Atomic, 42 /// Compound atomic rules are similar to atomic rules, 43 /// but they produce inner tokens as normal. 44 /// (their syntax is `${ ... }`) 45 CompoundAtomic, 46 /// Non-atomic rules cancel the effect of atomic rules. 47 /// (their syntax is `!{ ... }`) 48 NonAtomic, 49 } 50 51 /// All possible rule expressions 52 /// 53 /// # Warning: Semantic Versioning 54 /// There may be non-breaking changes to the meta-grammar 55 /// between minor versions. Those non-breaking changes, however, 56 /// may translate into semver-breaking changes due to the additional variants 57 /// propaged from the `Rule` enum. This is a known issue and will be fixed in the 58 /// future (e.g. by increasing MSRV and non_exhaustive annotations). 59 #[derive(Clone, Debug, Eq, PartialEq)] 60 pub enum Expr { 61 /// Matches an exact string, e.g. `"a"` 62 Str(String), 63 /// Matches an exact string, case insensitively (ASCII only), e.g. `^"a"` 64 Insens(String), 65 /// Matches one character in the range, e.g. `'a'..'z'` 66 Range(String, String), 67 /// Matches the rule with the given name, e.g. `a` 68 Ident(String), 69 /// Matches a custom part of the stack, e.g. `PEEK[..]` 70 PeekSlice(i32, Option<i32>), 71 /// Positive lookahead; matches expression without making progress, e.g. `&e` 72 PosPred(Box<Expr>), 73 /// Negative lookahead; matches if expression doesn't match, without making progress, e.g. `!e` 74 NegPred(Box<Expr>), 75 /// Matches a sequence of two expressions, e.g. `e1 ~ e2` 76 Seq(Box<Expr>, Box<Expr>), 77 /// Matches either of two expressions, e.g. `e1 | e2` 78 Choice(Box<Expr>, Box<Expr>), 79 /// Optionally matches an expression, e.g. `e?` 80 Opt(Box<Expr>), 81 /// Matches an expression zero or more times, e.g. `e*` 82 Rep(Box<Expr>), 83 /// Matches an expression one or more times, e.g. `e+` 84 RepOnce(Box<Expr>), 85 /// Matches an expression an exact number of times, e.g. `e{n}` 86 RepExact(Box<Expr>, u32), 87 /// Matches an expression at least a number of times, e.g. `e{n,}` 88 RepMin(Box<Expr>, u32), 89 /// Matches an expression at most a number of times, e.g. `e{,n}` 90 RepMax(Box<Expr>, u32), 91 /// Matches an expression a number of times within a range, e.g. `e{m, n}` 92 RepMinMax(Box<Expr>, u32, u32), 93 /// Continues to match expressions until one of the strings in the `Vec` is found 94 Skip(Vec<String>), 95 /// Matches an expression and pushes it to the stack, e.g. `push(e)` 96 Push(Box<Expr>), 97 /// Matches an expression and assigns a label to it, e.g. #label = exp 98 #[cfg(feature = "grammar-extras")] 99 NodeTag(Box<Expr>, String), 100 } 101 102 impl Expr { 103 /// Returns the iterator that steps the expression from top to bottom. iter_top_down(&self) -> ExprTopDownIterator104 pub fn iter_top_down(&self) -> ExprTopDownIterator { 105 ExprTopDownIterator::new(self) 106 } 107 108 /// Applies `f` to the expression and all its children (top to bottom). map_top_down<F>(self, mut f: F) -> Expr where F: FnMut(Expr) -> Expr,109 pub fn map_top_down<F>(self, mut f: F) -> Expr 110 where 111 F: FnMut(Expr) -> Expr, 112 { 113 fn map_internal<F>(expr: Expr, f: &mut F) -> Expr 114 where 115 F: FnMut(Expr) -> Expr, 116 { 117 let expr = f(expr); 118 119 match expr { 120 Expr::PosPred(expr) => { 121 let mapped = Box::new(map_internal(*expr, f)); 122 Expr::PosPred(mapped) 123 } 124 Expr::NegPred(expr) => { 125 let mapped = Box::new(map_internal(*expr, f)); 126 Expr::NegPred(mapped) 127 } 128 Expr::Seq(lhs, rhs) => { 129 let mapped_lhs = Box::new(map_internal(*lhs, f)); 130 let mapped_rhs = Box::new(map_internal(*rhs, f)); 131 Expr::Seq(mapped_lhs, mapped_rhs) 132 } 133 Expr::Choice(lhs, rhs) => { 134 let mapped_lhs = Box::new(map_internal(*lhs, f)); 135 let mapped_rhs = Box::new(map_internal(*rhs, f)); 136 Expr::Choice(mapped_lhs, mapped_rhs) 137 } 138 Expr::Rep(expr) => { 139 let mapped = Box::new(map_internal(*expr, f)); 140 Expr::Rep(mapped) 141 } 142 Expr::RepOnce(expr) => { 143 let mapped = Box::new(map_internal(*expr, f)); 144 Expr::RepOnce(mapped) 145 } 146 Expr::RepExact(expr, max) => { 147 let mapped = Box::new(map_internal(*expr, f)); 148 Expr::RepExact(mapped, max) 149 } 150 Expr::RepMin(expr, num) => { 151 let mapped = Box::new(map_internal(*expr, f)); 152 Expr::RepMin(mapped, num) 153 } 154 Expr::RepMax(expr, num) => { 155 let mapped = Box::new(map_internal(*expr, f)); 156 Expr::RepMax(mapped, num) 157 } 158 Expr::RepMinMax(expr, min, max) => { 159 let mapped = Box::new(map_internal(*expr, f)); 160 Expr::RepMinMax(mapped, min, max) 161 } 162 Expr::Opt(expr) => { 163 let mapped = Box::new(map_internal(*expr, f)); 164 Expr::Opt(mapped) 165 } 166 Expr::Push(expr) => { 167 let mapped = Box::new(map_internal(*expr, f)); 168 Expr::Push(mapped) 169 } 170 #[cfg(feature = "grammar-extras")] 171 Expr::NodeTag(expr, tag) => { 172 let mapped = Box::new(map_internal(*expr, f)); 173 Expr::NodeTag(mapped, tag) 174 } 175 expr => expr, 176 } 177 } 178 179 map_internal(self, &mut f) 180 } 181 182 /// Applies `f` to the expression and all its children (bottom up). map_bottom_up<F>(self, mut f: F) -> Expr where F: FnMut(Expr) -> Expr,183 pub fn map_bottom_up<F>(self, mut f: F) -> Expr 184 where 185 F: FnMut(Expr) -> Expr, 186 { 187 fn map_internal<F>(expr: Expr, f: &mut F) -> Expr 188 where 189 F: FnMut(Expr) -> Expr, 190 { 191 let mapped = match expr { 192 Expr::PosPred(expr) => { 193 let mapped = Box::new(map_internal(*expr, f)); 194 Expr::PosPred(mapped) 195 } 196 Expr::NegPred(expr) => { 197 let mapped = Box::new(map_internal(*expr, f)); 198 Expr::NegPred(mapped) 199 } 200 Expr::Seq(lhs, rhs) => { 201 let mapped_lhs = Box::new(map_internal(*lhs, f)); 202 let mapped_rhs = Box::new(map_internal(*rhs, f)); 203 Expr::Seq(mapped_lhs, mapped_rhs) 204 } 205 Expr::Choice(lhs, rhs) => { 206 let mapped_lhs = Box::new(map_internal(*lhs, f)); 207 let mapped_rhs = Box::new(map_internal(*rhs, f)); 208 Expr::Choice(mapped_lhs, mapped_rhs) 209 } 210 Expr::Rep(expr) => { 211 let mapped = Box::new(map_internal(*expr, f)); 212 Expr::Rep(mapped) 213 } 214 Expr::RepOnce(expr) => { 215 let mapped = Box::new(map_internal(*expr, f)); 216 Expr::RepOnce(mapped) 217 } 218 Expr::RepExact(expr, num) => { 219 let mapped = Box::new(map_internal(*expr, f)); 220 Expr::RepExact(mapped, num) 221 } 222 Expr::RepMin(expr, max) => { 223 let mapped = Box::new(map_internal(*expr, f)); 224 Expr::RepMin(mapped, max) 225 } 226 Expr::RepMax(expr, max) => { 227 let mapped = Box::new(map_internal(*expr, f)); 228 Expr::RepMax(mapped, max) 229 } 230 Expr::RepMinMax(expr, min, max) => { 231 let mapped = Box::new(map_internal(*expr, f)); 232 Expr::RepMinMax(mapped, min, max) 233 } 234 Expr::Opt(expr) => { 235 let mapped = Box::new(map_internal(*expr, f)); 236 Expr::Opt(mapped) 237 } 238 Expr::Push(expr) => { 239 let mapped = Box::new(map_internal(*expr, f)); 240 Expr::Push(mapped) 241 } 242 #[cfg(feature = "grammar-extras")] 243 Expr::NodeTag(expr, tag) => { 244 let mapped = Box::new(map_internal(*expr, f)); 245 Expr::NodeTag(mapped, tag) 246 } 247 expr => expr, 248 }; 249 250 f(mapped) 251 } 252 253 map_internal(self, &mut f) 254 } 255 } 256 257 impl core::fmt::Display for Expr { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result258 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 259 match self { 260 Expr::Str(s) => write!(f, "{:?}", s), 261 Expr::Insens(s) => write!(f, "^{:?}", s), 262 Expr::Range(start, end) => { 263 let start = start.chars().next().expect("Empty range start."); 264 let end = end.chars().next().expect("Empty range end."); 265 write!(f, "({:?}..{:?})", start, end) 266 } 267 Expr::Ident(id) => write!(f, "{}", id), 268 Expr::PeekSlice(start, end) => match end { 269 Some(end) => write!(f, "PEEK[{}..{}]", start, end), 270 None => write!(f, "PEEK[{}..]", start), 271 }, 272 Expr::PosPred(expr) => write!(f, "&{}", expr.as_ref()), 273 Expr::NegPred(expr) => write!(f, "!{}", expr.as_ref()), 274 Expr::Seq(lhs, rhs) => { 275 let mut nodes = Vec::new(); 276 nodes.push(lhs); 277 let mut current = rhs; 278 while let Expr::Seq(lhs, rhs) = current.as_ref() { 279 nodes.push(lhs); 280 current = rhs; 281 } 282 nodes.push(current); 283 let sequence = nodes 284 .iter() 285 .map(|node| format!("{}", node)) 286 .collect::<Vec<_>>() 287 .join(" ~ "); 288 write!(f, "({})", sequence) 289 } 290 Expr::Choice(lhs, rhs) => { 291 let mut nodes = Vec::new(); 292 nodes.push(lhs); 293 let mut current = rhs; 294 while let Expr::Choice(lhs, rhs) = current.as_ref() { 295 nodes.push(lhs); 296 current = rhs; 297 } 298 nodes.push(current); 299 let sequence = nodes 300 .iter() 301 .map(|node| format!("{}", node)) 302 .collect::<Vec<_>>() 303 .join(" | "); 304 write!(f, "({})", sequence) 305 } 306 Expr::Opt(expr) => write!(f, "{}?", expr), 307 Expr::Rep(expr) => write!(f, "{}*", expr), 308 Expr::RepOnce(expr) => write!(f, "{}+", expr), 309 Expr::RepExact(expr, n) => write!(f, "{}{{{}}}", expr, n), 310 Expr::RepMin(expr, min) => write!(f, "{}{{{},}}", expr, min), 311 Expr::RepMax(expr, max) => write!(f, "{}{{,{}}}", expr, max), 312 Expr::RepMinMax(expr, min, max) => write!(f, "{}{{{}, {}}}", expr, min, max), 313 Expr::Skip(strings) => { 314 let strings = strings 315 .iter() 316 .map(|s| format!("{:?}", s)) 317 .collect::<Vec<_>>() 318 .join(" | "); 319 write!(f, "(!({}) ~ ANY)*", strings) 320 } 321 Expr::Push(expr) => write!(f, "PUSH({})", expr), 322 #[cfg(feature = "grammar-extras")] 323 Expr::NodeTag(expr, tag) => { 324 write!(f, "(#{} = {})", tag, expr) 325 } 326 } 327 } 328 } 329 330 /// The top down iterator for an expression. 331 pub struct ExprTopDownIterator { 332 current: Option<Expr>, 333 next: Option<Expr>, 334 right_branches: Vec<Expr>, 335 } 336 337 impl ExprTopDownIterator { 338 /// Constructs a top-down iterator from the expression. new(expr: &Expr) -> Self339 pub fn new(expr: &Expr) -> Self { 340 let mut iter = ExprTopDownIterator { 341 current: None, 342 next: None, 343 right_branches: vec![], 344 }; 345 iter.iterate_expr(expr.clone()); 346 iter 347 } 348 iterate_expr(&mut self, expr: Expr)349 fn iterate_expr(&mut self, expr: Expr) { 350 self.current = Some(expr.clone()); 351 match expr { 352 Expr::Seq(lhs, rhs) => { 353 self.right_branches.push(*rhs); 354 self.next = Some(*lhs); 355 } 356 Expr::Choice(lhs, rhs) => { 357 self.right_branches.push(*rhs); 358 self.next = Some(*lhs); 359 } 360 Expr::PosPred(expr) 361 | Expr::NegPred(expr) 362 | Expr::Rep(expr) 363 | Expr::RepOnce(expr) 364 | Expr::RepExact(expr, _) 365 | Expr::RepMin(expr, _) 366 | Expr::RepMax(expr, _) 367 | Expr::RepMinMax(expr, ..) 368 | Expr::Opt(expr) 369 | Expr::Push(expr) => { 370 self.next = Some(*expr); 371 } 372 #[cfg(feature = "grammar-extras")] 373 Expr::NodeTag(expr, _) => { 374 self.next = Some(*expr); 375 } 376 _ => { 377 self.next = None; 378 } 379 } 380 } 381 } 382 383 impl Iterator for ExprTopDownIterator { 384 type Item = Expr; 385 next(&mut self) -> Option<Self::Item>386 fn next(&mut self) -> Option<Self::Item> { 387 let result = self.current.take(); 388 389 if let Some(expr) = self.next.take() { 390 self.iterate_expr(expr); 391 } else if let Some(expr) = self.right_branches.pop() { 392 self.iterate_expr(expr); 393 } 394 395 result 396 } 397 } 398 399 #[cfg(test)] 400 mod tests { 401 use super::*; 402 403 #[test] top_down_iterator()404 fn top_down_iterator() { 405 let expr = Expr::Choice( 406 Box::new(Expr::Str(String::from("a"))), 407 Box::new(Expr::Str(String::from("b"))), 408 ); 409 let mut top_down = expr.iter_top_down(); 410 assert_eq!(top_down.next(), Some(expr)); 411 assert_eq!(top_down.next(), Some(Expr::Str(String::from("a")))); 412 assert_eq!(top_down.next(), Some(Expr::Str(String::from("b")))); 413 assert_eq!(top_down.next(), None); 414 } 415 416 #[test] identity()417 fn identity() { 418 let expr = Expr::Choice( 419 Box::new(Expr::Seq( 420 Box::new(Expr::Ident("a".to_owned())), 421 Box::new(Expr::Str("b".to_owned())), 422 )), 423 Box::new(Expr::PosPred(Box::new(Expr::NegPred(Box::new(Expr::Rep( 424 Box::new(Expr::RepOnce(Box::new(Expr::Opt(Box::new(Expr::Choice( 425 Box::new(Expr::Insens("c".to_owned())), 426 Box::new(Expr::Push(Box::new(Expr::Range( 427 "'d'".to_owned(), 428 "'e'".to_owned(), 429 )))), 430 )))))), 431 )))))), 432 ); 433 434 assert_eq!( 435 expr.clone() 436 .map_bottom_up(|expr| expr) 437 .map_top_down(|expr| expr), 438 expr, 439 ); 440 } 441 442 mod display { 443 use super::super::*; 444 445 #[test] string()446 fn string() { 447 assert_eq!(Expr::Str("a".to_owned()).to_string(), r#""a""#); 448 } 449 450 #[test] insens()451 fn insens() { 452 assert_eq!(Expr::Insens("a".to_owned()).to_string(), r#"^"a""#); 453 } 454 455 #[test] range()456 fn range() { 457 assert_eq!( 458 Expr::Range("a".to_owned(), "z".to_owned()).to_string(), 459 r#"('a'..'z')"#, 460 ); 461 } 462 463 #[test] ident()464 fn ident() { 465 assert_eq!(Expr::Ident("a".to_owned()).to_string(), "a"); 466 } 467 468 #[test] peek_slice()469 fn peek_slice() { 470 assert_eq!(Expr::PeekSlice(0, None).to_string(), "PEEK[0..]"); 471 assert_eq!(Expr::PeekSlice(0, Some(-1)).to_string(), "PEEK[0..-1]"); 472 } 473 474 #[test] pos_pred()475 fn pos_pred() { 476 assert_eq!( 477 Expr::PosPred(Box::new(Expr::Ident("e".to_owned()))).to_string(), 478 "&e", 479 ); 480 } 481 482 #[test] neg_pred()483 fn neg_pred() { 484 assert_eq!( 485 Expr::NegPred(Box::new(Expr::Ident("e".to_owned()))).to_string(), 486 "!e", 487 ); 488 } 489 490 #[test] seq()491 fn seq() { 492 assert_eq!( 493 Expr::Seq( 494 Box::new(Expr::Ident("e1".to_owned())), 495 Box::new(Expr::Ident("e2".to_owned())), 496 ) 497 .to_string(), 498 "(e1 ~ e2)", 499 ); 500 assert_eq!( 501 Expr::Seq( 502 Box::new(Expr::Ident("e1".to_owned())), 503 Box::new(Expr::Seq( 504 Box::new(Expr::Ident("e2".to_owned())), 505 Box::new(Expr::Ident("e3".to_owned())), 506 )), 507 ) 508 .to_string(), 509 "(e1 ~ e2 ~ e3)", 510 ); 511 assert_eq!( 512 Expr::Seq( 513 Box::new(Expr::Ident("e1".to_owned())), 514 Box::new(Expr::Seq( 515 Box::new(Expr::Ident("e2".to_owned())), 516 Box::new(Expr::Seq( 517 Box::new(Expr::Ident("e3".to_owned())), 518 Box::new(Expr::Ident("e4".to_owned())), 519 )), 520 )), 521 ) 522 .to_string(), 523 "(e1 ~ e2 ~ e3 ~ e4)", 524 ); 525 assert_eq!( 526 Expr::Seq( 527 Box::new(Expr::Ident("e1".to_owned())), 528 Box::new(Expr::Choice( 529 Box::new(Expr::Ident("e2".to_owned())), 530 Box::new(Expr::Seq( 531 Box::new(Expr::Ident("e3".to_owned())), 532 Box::new(Expr::Ident("e4".to_owned())), 533 )), 534 )), 535 ) 536 .to_string(), 537 "(e1 ~ (e2 | (e3 ~ e4)))", 538 ); 539 assert_eq!( 540 Expr::Seq( 541 Box::new(Expr::Ident("e1".to_owned())), 542 Box::new(Expr::Seq( 543 Box::new(Expr::Ident("e2".to_owned())), 544 Box::new(Expr::Choice( 545 Box::new(Expr::Ident("e3".to_owned())), 546 Box::new(Expr::Ident("e4".to_owned())), 547 )), 548 )), 549 ) 550 .to_string(), 551 "(e1 ~ e2 ~ (e3 | e4))", 552 ); 553 } 554 555 #[test] choice()556 fn choice() { 557 assert_eq!( 558 Expr::Choice( 559 Box::new(Expr::Ident("e1".to_owned())), 560 Box::new(Expr::Ident("e2".to_owned())), 561 ) 562 .to_string(), 563 "(e1 | e2)", 564 ); 565 assert_eq!( 566 Expr::Choice( 567 Box::new(Expr::Ident("e1".to_owned())), 568 Box::new(Expr::Choice( 569 Box::new(Expr::Ident("e2".to_owned())), 570 Box::new(Expr::Ident("e3".to_owned())), 571 )), 572 ) 573 .to_string(), 574 "(e1 | e2 | e3)", 575 ); 576 assert_eq!( 577 Expr::Choice( 578 Box::new(Expr::Ident("e1".to_owned())), 579 Box::new(Expr::Choice( 580 Box::new(Expr::Ident("e2".to_owned())), 581 Box::new(Expr::Choice( 582 Box::new(Expr::Ident("e3".to_owned())), 583 Box::new(Expr::Ident("e4".to_owned())), 584 )), 585 )), 586 ) 587 .to_string(), 588 "(e1 | e2 | e3 | e4)", 589 ); 590 assert_eq!( 591 Expr::Choice( 592 Box::new(Expr::Ident("e1".to_owned())), 593 Box::new(Expr::Seq( 594 Box::new(Expr::Ident("e2".to_owned())), 595 Box::new(Expr::Choice( 596 Box::new(Expr::Ident("e3".to_owned())), 597 Box::new(Expr::Ident("e4".to_owned())), 598 )), 599 )), 600 ) 601 .to_string(), 602 "(e1 | (e2 ~ (e3 | e4)))", 603 ); 604 } 605 606 #[test] opt()607 fn opt() { 608 assert_eq!( 609 Expr::Opt(Box::new(Expr::Ident("e".to_owned()))).to_string(), 610 "e?", 611 ); 612 } 613 614 #[test] rep()615 fn rep() { 616 assert_eq!( 617 Expr::Rep(Box::new(Expr::Ident("e".to_owned()))).to_string(), 618 "e*", 619 ); 620 } 621 622 #[test] rep_once()623 fn rep_once() { 624 assert_eq!( 625 Expr::RepOnce(Box::new(Expr::Ident("e".to_owned()))).to_string(), 626 "e+", 627 ); 628 } 629 630 #[test] rep_exact()631 fn rep_exact() { 632 assert_eq!( 633 Expr::RepExact(Box::new(Expr::Ident("e".to_owned())), 1).to_string(), 634 "e{1}", 635 ); 636 } 637 638 #[test] rep_min()639 fn rep_min() { 640 assert_eq!( 641 Expr::RepMin(Box::new(Expr::Ident("e".to_owned())), 1).to_string(), 642 "e{1,}", 643 ); 644 } 645 646 #[test] rep_max()647 fn rep_max() { 648 assert_eq!( 649 Expr::RepMax(Box::new(Expr::Ident("e".to_owned())), 1).to_string(), 650 "e{,1}", 651 ); 652 } 653 654 #[test] rep_min_max()655 fn rep_min_max() { 656 assert_eq!( 657 Expr::RepMinMax(Box::new(Expr::Ident("e".to_owned())), 1, 2).to_string(), 658 "e{1, 2}", 659 ); 660 } 661 662 #[test] skip()663 fn skip() { 664 assert_eq!( 665 Expr::Skip( 666 ["a", "bc"] 667 .into_iter() 668 .map(|s| s.to_owned()) 669 .collect::<Vec<_>>(), 670 ) 671 .to_string(), 672 r#"(!("a" | "bc") ~ ANY)*"#, 673 ); 674 } 675 676 #[test] push()677 fn push() { 678 assert_eq!( 679 Expr::Push(Box::new(Expr::Ident("e".to_owned()))).to_string(), 680 "PUSH(e)", 681 ); 682 } 683 684 #[test] 685 #[cfg(feature = "grammar-extras")] node_tag()686 fn node_tag() { 687 assert_eq!( 688 Expr::NodeTag(Box::new(Expr::Ident("expr".to_owned())), "label".to_owned()) 689 .to_string(), 690 "(#label = expr)", 691 ); 692 } 693 } 694 } 695