1use crate::xml::xpath::ast::*;
88use crate::xml::xpath::lexer::Token;
89
90#[derive(Debug, Clone, PartialEq)]
92pub struct ParseError {
93 pub message: String,
95 pub pos: usize,
97}
98
99impl std::fmt::Display for ParseError {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(
102 f,
103 "XPath parse error at position {}: {}",
104 self.pos, self.message
105 )
106 }
107}
108
109#[cfg(fuzzing)]
125const XPATH_MAX_RECURSION_DEPTH: u32 = 500;
126#[cfg(not(fuzzing))]
127const XPATH_MAX_RECURSION_DEPTH: u32 = 5000;
128
129#[derive(Debug)]
134pub struct Parser {
135 tokens: Vec<Token>,
136 pos: usize,
137 depth: u32,
140}
141
142impl Parser {
143 pub const fn new(tokens: Vec<Token>) -> Self {
145 Self {
146 tokens,
147 pos: 0,
148 depth: 0,
149 }
150 }
151
152 pub fn parse(&mut self) -> Result<Expr, ParseError> {
154 let expr = self.parse_or_expr()?;
155 if !self.is_eof() {
156 Err(self.error(format!("Unexpected token: {}", self.current())))?;
157 }
158 Ok(expr)
159 }
160
161 fn current(&self) -> Token {
164 if self.pos < self.tokens.len() {
165 self.tokens[self.pos].clone()
166 } else {
167 Token::Eof
168 }
169 }
170
171 fn peek(&self) -> Token {
172 if self.pos + 1 < self.tokens.len() {
173 self.tokens[self.pos + 1].clone()
174 } else {
175 Token::Eof
176 }
177 }
178
179 const fn advance(&mut self) {
180 if self.pos < self.tokens.len() {
181 self.pos += 1;
182 }
183 }
184
185 fn is_eof(&self) -> bool {
186 matches!(self.current(), Token::Eof)
187 }
188
189 const fn error(&self, msg: String) -> ParseError {
190 ParseError {
191 message: msg,
192 pos: self.pos,
193 }
194 }
195
196 fn at(&self, token: &Token) -> bool {
198 std::mem::discriminant(&self.current()) == std::mem::discriminant(token)
199 }
200
201 fn expect(&mut self, expected: &Token) -> Result<(), ParseError> {
203 if self.at(expected) {
204 self.advance();
205 Ok(())
206 } else {
207 Err(self.error(format!("Expected {}, got {}", expected, self.current())))
208 }
209 }
210
211 fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
215 if self.depth >= XPATH_MAX_RECURSION_DEPTH {
221 return Err(self.error("Recursion limit exceeded".to_string()));
222 }
223 self.depth += 10;
224 let result = self.parse_or_expr_body();
225 self.depth -= 10;
226 result
227 }
228
229 fn parse_or_expr_body(&mut self) -> Result<Expr, ParseError> {
230 let mut left = self.parse_and_expr()?;
231 while matches!(self.current(), Token::Or) {
232 self.advance();
233 let right = self.parse_and_expr()?;
234 left = Expr::BinaryOp {
235 op: BinaryOp::Or,
236 left: Box::new(left),
237 right: Box::new(right),
238 };
239 }
240 Ok(left)
241 }
242
243 fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
245 let mut left = self.parse_equality_expr()?;
246 while matches!(self.current(), Token::And) {
247 self.advance();
248 let right = self.parse_equality_expr()?;
249 left = Expr::BinaryOp {
250 op: BinaryOp::And,
251 left: Box::new(left),
252 right: Box::new(right),
253 };
254 }
255 Ok(left)
256 }
257
258 fn parse_equality_expr(&mut self) -> Result<Expr, ParseError> {
260 let mut left = self.parse_relational_expr()?;
261 loop {
262 let op = match self.current() {
263 Token::Eq => BinaryOp::Eq,
264 Token::Ne => BinaryOp::Ne,
265 _ => break,
266 };
267 self.advance();
268 let right = self.parse_relational_expr()?;
269 left = Expr::BinaryOp {
270 op,
271 left: Box::new(left),
272 right: Box::new(right),
273 };
274 }
275 Ok(left)
276 }
277
278 fn parse_relational_expr(&mut self) -> Result<Expr, ParseError> {
280 let mut left = self.parse_additive_expr()?;
281 loop {
282 let op = match self.current() {
283 Token::Lt => BinaryOp::Lt,
284 Token::Gt => BinaryOp::Gt,
285 Token::Le => BinaryOp::Le,
286 Token::Ge => BinaryOp::Ge,
287 _ => break,
288 };
289 self.advance();
290 let right = self.parse_additive_expr()?;
291 left = Expr::BinaryOp {
292 op,
293 left: Box::new(left),
294 right: Box::new(right),
295 };
296 }
297 Ok(left)
298 }
299
300 fn parse_additive_expr(&mut self) -> Result<Expr, ParseError> {
302 let mut left = self.parse_multiplicative_expr()?;
303 loop {
304 let op = match self.current() {
305 Token::Plus => BinaryOp::Add,
306 Token::Minus => BinaryOp::Sub,
307 _ => break,
308 };
309 self.advance();
310 let right = self.parse_multiplicative_expr()?;
311 left = Expr::BinaryOp {
312 op,
313 left: Box::new(left),
314 right: Box::new(right),
315 };
316 }
317 Ok(left)
318 }
319
320 fn parse_multiplicative_expr(&mut self) -> Result<Expr, ParseError> {
322 let mut left = self.parse_unary_expr()?;
323 loop {
324 let op = match self.current() {
325 Token::Star => BinaryOp::Mul,
327 Token::Div => BinaryOp::Div,
328 Token::Mod => BinaryOp::Mod,
329 _ => break,
330 };
331 self.advance();
332 let right = self.parse_unary_expr()?;
333 left = Expr::BinaryOp {
334 op,
335 left: Box::new(left),
336 right: Box::new(right),
337 };
338 }
339 Ok(left)
340 }
341
342 fn parse_unary_expr(&mut self) -> Result<Expr, ParseError> {
344 let mut minus_count = 0;
345 while matches!(self.current(), Token::Minus) {
346 self.advance();
347 minus_count += 1;
348 }
349 let mut expr = self.parse_union_expr()?;
350 if minus_count % 2 == 1 {
351 expr = Expr::UnaryMinus(Box::new(expr));
352 }
353 Ok(expr)
354 }
355
356 fn parse_union_expr(&mut self) -> Result<Expr, ParseError> {
358 let mut left = self.parse_path_expr()?;
359 while matches!(self.current(), Token::Pipe) {
360 self.advance();
361 let right = self.parse_path_expr()?;
362 left = Expr::Union(Box::new(left), Box::new(right));
363 }
364 Ok(left)
365 }
366
367 fn parse_path_expr(&mut self) -> Result<Expr, ParseError> {
369 if self.is_location_path_start() {
371 return self.parse_location_path();
372 }
373
374 let mut expr = self.parse_filter_expr()?;
376
377 loop {
379 match self.current() {
380 Token::Slash => {
381 self.advance();
382 let step = self.parse_relative_location_path()?;
383 expr = Expr::RelativePath(Box::new(expr), Box::new(step));
384 }
385 Token::DoubleSlash => {
386 self.advance();
387 let step = self.parse_relative_location_path()?;
388 let descendant = Expr::Step(Step {
390 axis: Axis::DescendantOrSelf,
391 node_test: NodeTest::Node,
392 predicates: vec![],
393 });
394 let path = Expr::RelativePath(Box::new(descendant), Box::new(step));
395 expr = Expr::RelativePath(Box::new(expr), Box::new(path));
396 }
397 _ => break,
398 }
399 }
400
401 Ok(expr)
402 }
403
404 fn is_location_path_start(&self) -> bool {
406 match self.current() {
407 Token::Slash | Token::DoubleSlash => true,
408 Token::Dot | Token::DotDot => true,
409 Token::At => true,
410 Token::Star => true,
411 Token::Name(ref n) => {
413 let next = self.peek();
415 if matches!(next, Token::LParen) {
416 !n.contains(':')
425 && matches!(
426 n.as_str(),
427 "node" | "text" | "comment" | "processing-instruction"
428 )
429 } else {
430 true
431 }
432 }
433 Token::Child
435 | Token::Descendant
436 | Token::DescendantOrSelf
437 | Token::Ancestor
438 | Token::AncestorOrSelf
439 | Token::Attribute
440 | Token::Following
441 | Token::FollowingSibling
442 | Token::Namespace
443 | Token::Parent
444 | Token::Preceding
445 | Token::PrecedingSibling
446 | Token::Self_ => true,
447 _ => false,
448 }
449 }
450
451 fn parse_location_path(&mut self) -> Result<Expr, ParseError> {
453 match self.current() {
454 Token::Slash => {
455 self.advance();
456 if self.is_location_path_start() {
457 let path = self.parse_relative_location_path()?;
458 Ok(Expr::AbsolutePath(Box::new(path)))
459 } else {
460 Ok(Expr::Step(Step {
462 axis: Axis::Self_,
463 node_test: NodeTest::Node,
464 predicates: vec![],
465 }))
466 }
467 }
468 Token::DoubleSlash => {
469 self.advance();
470 let path = self.parse_relative_location_path()?;
471 let descendant = Expr::Step(Step {
472 axis: Axis::DescendantOrSelf,
473 node_test: NodeTest::Node,
474 predicates: vec![],
475 });
476 Ok(Expr::AbsolutePath(Box::new(Expr::RelativePath(
477 Box::new(descendant),
478 Box::new(path),
479 ))))
480 }
481 _ => self.parse_relative_location_path(),
482 }
483 }
484
485 fn parse_relative_location_path(&mut self) -> Result<Expr, ParseError> {
487 let mut expr = self.parse_step()?;
488 loop {
489 match self.current() {
490 Token::Slash => {
491 self.advance();
492 let step = self.parse_step()?;
493 expr = Expr::RelativePath(Box::new(expr), Box::new(step));
494 }
495 Token::DoubleSlash => {
496 self.advance();
497 let step = self.parse_step()?;
498 let descendant = Expr::Step(Step {
499 axis: Axis::DescendantOrSelf,
500 node_test: NodeTest::Node,
501 predicates: vec![],
502 });
503 let path = Expr::RelativePath(Box::new(descendant), Box::new(step));
504 expr = Expr::RelativePath(Box::new(expr), Box::new(path));
505 }
506 _ => break,
507 }
508 }
509 Ok(expr)
510 }
511
512 fn parse_step(&mut self) -> Result<Expr, ParseError> {
515 match self.current() {
517 Token::Dot => {
518 self.advance();
519 return Ok(Expr::Step(Step {
520 axis: Axis::Self_,
521 node_test: NodeTest::Node,
522 predicates: vec![],
523 }));
524 }
525 Token::DotDot => {
526 self.advance();
527 return Ok(Expr::Step(Step {
528 axis: Axis::Parent,
529 node_test: NodeTest::Node,
530 predicates: vec![],
531 }));
532 }
533 _ => {}
534 }
535
536 let axis = self.parse_axis_specifier();
538
539 let node_test = self.parse_node_test()?;
541
542 let mut predicates = Vec::new();
544 while matches!(self.current(), Token::LBracket) {
545 self.advance(); let pred = self.parse_or_expr()?;
547 self.expect(&Token::RBracket)?;
548 predicates.push(pred);
549 }
550
551 Ok(Expr::Step(Step {
552 axis,
553 node_test,
554 predicates,
555 }))
556 }
557
558 fn parse_axis_specifier(&mut self) -> Axis {
560 if matches!(self.current(), Token::At) {
562 self.advance();
563 return Axis::Attribute;
564 }
565
566 let is_axis = match self.current() {
570 Token::Ancestor
571 | Token::AncestorOrSelf
572 | Token::Attribute
573 | Token::Child
574 | Token::Descendant
575 | Token::DescendantOrSelf
576 | Token::Following
577 | Token::FollowingSibling
578 | Token::Namespace
579 | Token::Parent
580 | Token::Preceding
581 | Token::PrecedingSibling
582 | Token::Self_ => matches!(self.peek(), Token::DoubleColon),
583 _ => false,
584 };
585
586 if is_axis {
587 let axis = match self.current() {
588 Token::Ancestor => Axis::Ancestor,
589 Token::AncestorOrSelf => Axis::AncestorOrSelf,
590 Token::Attribute => Axis::Attribute,
591 Token::Child => Axis::Child,
592 Token::Descendant => Axis::Descendant,
593 Token::DescendantOrSelf => Axis::DescendantOrSelf,
594 Token::Following => Axis::Following,
595 Token::FollowingSibling => Axis::FollowingSibling,
596 Token::Namespace => Axis::Namespace,
597 Token::Parent => Axis::Parent,
598 Token::Preceding => Axis::Preceding,
599 Token::PrecedingSibling => Axis::PrecedingSibling,
600 Token::Self_ => Axis::Self_,
601 _ => unreachable!(),
602 };
603 self.advance(); self.advance(); return axis;
606 }
607
608 Axis::Child
610 }
611
612 fn token_to_name(&self, token: &Token) -> Option<String> {
614 match token {
615 Token::Name(ref s) => Some(s.clone()),
616 Token::Div => Some("div".to_string()),
617 Token::Mod => Some("mod".to_string()),
618 Token::And => Some("and".to_string()),
619 Token::Or => Some("or".to_string()),
620 Token::Ancestor => Some("ancestor".to_string()),
621 Token::AncestorOrSelf => Some("ancestor-or-self".to_string()),
622 Token::Attribute => Some("attribute".to_string()),
623 Token::Child => Some("child".to_string()),
624 Token::Descendant => Some("descendant".to_string()),
625 Token::DescendantOrSelf => Some("descendant-or-self".to_string()),
626 Token::Following => Some("following".to_string()),
627 Token::FollowingSibling => Some("following-sibling".to_string()),
628 Token::Namespace => Some("namespace".to_string()),
629 Token::Parent => Some("parent".to_string()),
630 Token::Preceding => Some("preceding".to_string()),
631 Token::PrecedingSibling => Some("preceding-sibling".to_string()),
632 Token::Self_ => Some("self".to_string()),
633 _ => None,
634 }
635 }
636
637 fn parse_node_test(&mut self) -> Result<NodeTest, ParseError> {
640 let name_opt = self.token_to_name(&self.current());
642
643 match self.current() {
644 Token::Star => {
645 self.advance();
646 Ok(NodeTest::NameTest(NameTest::Any))
647 }
648 _ if name_opt.is_some() => {
649 let name = name_opt.unwrap();
650
651 if matches!(self.peek(), Token::LParen) {
653 match name.as_str() {
654 "node" => {
655 self.advance();
656 self.advance();
657 self.advance(); Ok(NodeTest::Node)
659 }
660 "text" => {
661 self.advance();
662 self.advance();
663 self.advance();
664 Ok(NodeTest::Text)
665 }
666 "comment" => {
667 self.advance();
668 self.advance();
669 self.advance();
670 Ok(NodeTest::Comment)
671 }
672 "processing-instruction" => {
673 self.advance(); self.advance(); let target = if matches!(self.current(), Token::StringLiteral(_)) {
677 if let Token::StringLiteral(s) = self.current() {
678 self.advance();
679 Some(s)
680 } else {
681 None
682 }
683 } else {
684 None
685 };
686 self.expect(&Token::RParen)?;
687 Ok(NodeTest::ProcessingInstruction(target))
688 }
689 _ => {
690 self.advance(); self.advance(); let mut args = Vec::new();
694 if !matches!(self.current(), Token::RParen) {
695 args.push(self.parse_or_expr()?);
696 while matches!(self.current(), Token::Comma) {
697 self.advance();
698 args.push(self.parse_or_expr()?);
699 }
700 }
701 self.expect(&Token::RParen)?;
702 Ok(NodeTest::NameTest(NameTest::LocalName(name)))
704 }
705 }
706 } else {
707 self.advance();
708 if let Some(rest) = name.strip_suffix(":*") {
710 Ok(NodeTest::NsWildcard(rest.to_string()))
711 } else if let Some((prefix, local)) = name.split_once(':') {
712 Ok(NodeTest::NameTest(NameTest::QName {
713 prefix: prefix.to_string(),
714 local: local.to_string(),
715 }))
716 } else {
717 Ok(NodeTest::NameTest(NameTest::LocalName(name)))
718 }
719 }
720 }
721 _ => Err(self.error(format!("Expected node test, got {}", self.current()))),
722 }
723 }
724
725 fn parse_filter_expr(&mut self) -> Result<Expr, ParseError> {
727 let primary = self.parse_primary_expr()?;
728
729 let mut predicates = Vec::new();
731 while matches!(self.current(), Token::LBracket) {
732 self.advance(); let pred = self.parse_or_expr()?;
734 self.expect(&Token::RBracket)?;
735 predicates.push(pred);
736 }
737
738 if predicates.is_empty() {
739 Ok(primary)
740 } else {
741 Ok(Expr::Filter(Box::new(primary), predicates))
742 }
743 }
744
745 fn parse_primary_expr(&mut self) -> Result<Expr, ParseError> {
747 match self.current() {
748 Token::Dollar => {
749 self.advance();
750 if let Token::Name(name) = self.current() {
751 let name = name.clone();
752 self.advance();
753 Ok(Expr::Variable(name))
754 } else {
755 Err(self.error("Expected variable name after $".to_string()))
756 }
757 }
758 Token::LParen => {
759 self.advance();
760 let expr = self.parse_or_expr()?;
761 self.expect(&Token::RParen)?;
762 Ok(expr)
763 }
764 Token::StringLiteral(ref s) => {
765 let s = s.clone();
766 self.advance();
767 Ok(Expr::StringLiteral(s))
768 }
769 Token::NumberLiteral(n) => {
770 self.advance();
771 Ok(Expr::NumberLiteral(n))
772 }
773 Token::Name(ref name) => {
774 let name = name.clone();
775 if matches!(self.peek(), Token::LParen) {
776 self.advance(); self.advance(); let mut args = Vec::new();
780 if !matches!(self.current(), Token::RParen) {
781 args.push(self.parse_or_expr()?);
782 while matches!(self.current(), Token::Comma) {
783 self.advance();
784 args.push(self.parse_or_expr()?);
785 }
786 }
787 self.expect(&Token::RParen)?;
788 Ok(Expr::FunctionCall { name, args })
789 } else {
790 Err(self.error(format!("Unexpected name '{}' in primary expression", name)))
793 }
794 }
795 _ => Err(self.error(format!(
796 "Expected primary expression, got {}",
797 self.current()
798 ))),
799 }
800 }
801}
802
803pub fn parse_xpath(input: &str) -> Result<Expr, ParseError> {
809 let mut lexer = crate::xml::xpath::lexer::Lexer::new(input);
810 let mut tokens = Vec::new();
811 loop {
812 let tok = lexer.next_token();
813 let is_eof = matches!(tok, Token::Eof);
814 tokens.push(tok);
815 if is_eof {
816 break;
817 }
818 }
819 let starts = lexer.token_starts();
820 let mut parser = Parser::new(tokens);
821 parser.parse().map_err(|e| {
822 let byte_off = starts
828 .get(e.pos)
829 .copied()
830 .unwrap_or(input.len())
831 .min(input.len());
832 ParseError {
833 message: e.message,
834 pos: byte_off,
835 }
836 })
837}
838
839#[cfg(test)]
844mod tests {
845 use super::*;
846
847 #[test]
848 fn test_parse_simple_name() {
849 let expr = parse_xpath("para").unwrap();
850 assert!(matches!(expr, Expr::Step(_)));
851 }
852
853 #[test]
854 fn test_parse_absolute_path() {
855 let expr = parse_xpath("/child::para").unwrap();
856 assert!(matches!(expr, Expr::AbsolutePath(_)));
857 }
858
859 #[test]
860 fn test_parse_attribute() {
861 let expr = parse_xpath("@attr").unwrap();
862 assert!(matches!(expr, Expr::Step(_)));
863 }
864
865 #[test]
866 fn test_parse_predicate() {
867 let expr = parse_xpath("para[1]").unwrap();
868 assert!(matches!(expr, Expr::Step(_)));
869 }
870
871 #[test]
872 fn test_parse_function_call() {
873 let expr = parse_xpath("position()").unwrap();
874 assert!(matches!(expr, Expr::FunctionCall { .. }));
875 }
876
877 #[test]
878 fn test_parse_binary_op() {
879 let expr = parse_xpath("a = b").unwrap();
880 assert!(matches!(
881 expr,
882 Expr::BinaryOp {
883 op: BinaryOp::Eq,
884 ..
885 }
886 ));
887 }
888
889 #[test]
890 fn test_parse_union() {
891 let expr = parse_xpath("a | b").unwrap();
892 assert!(matches!(expr, Expr::Union(_, _)));
893 }
894
895 #[test]
896 fn test_parse_variable() {
897 let expr = parse_xpath("$var").unwrap();
898 assert!(matches!(expr, Expr::Variable(_)));
899 }
900
901 #[test]
902 fn test_parse_string_literal() {
903 let expr = parse_xpath("'hello'").unwrap();
904 assert_eq!(expr, Expr::StringLiteral("hello".to_string()));
905 }
906
907 #[test]
908 fn test_parse_number() {
909 let expr = parse_xpath("42").unwrap();
910 assert_eq!(expr, Expr::NumberLiteral(42.0));
911 }
912
913 #[test]
914 fn test_parse_nested_expression() {
915 let expr = parse_xpath("(1 + 2) * 3").unwrap();
916 assert!(matches!(
917 expr,
918 Expr::BinaryOp {
919 op: BinaryOp::Mul,
920 ..
921 }
922 ));
923 }
924
925 #[test]
926 fn test_parse_chained_path() {
927 let expr = parse_xpath("a/b/c").unwrap();
928 assert!(matches!(expr, Expr::RelativePath(_, _)));
929 }
930
931 #[test]
932 fn test_parse_double_slash() {
933 let expr = parse_xpath("//para").unwrap();
934 assert!(matches!(expr, Expr::AbsolutePath(_)));
935 }
936
937 #[test]
938 fn test_parse_dot() {
939 let expr = parse_xpath(".").unwrap();
940 assert!(matches!(expr, Expr::Step(_)));
941 }
942
943 #[test]
944 fn test_parse_dot_dot() {
945 let expr = parse_xpath("..").unwrap();
946 assert!(matches!(expr, Expr::Step(_)));
947 }
948
949 #[test]
950 fn test_parse_unary_minus() {
951 let expr = parse_xpath("-5").unwrap();
952 assert!(matches!(expr, Expr::UnaryMinus(_)));
953 }
954
955 #[test]
956 fn test_parse_double_unary_minus() {
957 let expr = parse_xpath("--5").unwrap();
958 assert!(!matches!(expr, Expr::UnaryMinus(_)));
960 }
961
962 #[test]
963 fn test_parse_complex_expression() {
964 let expr = parse_xpath("/html/body//div[@class='main']/p[1]").unwrap();
965 assert!(matches!(expr, Expr::AbsolutePath(_)));
966 }
967
968 #[test]
969 fn test_parse_error() {
970 let result = parse_xpath("(");
971 assert!(result.is_err());
972 }
973
974 #[test]
975 fn test_parse_empty() {
976 let result = parse_xpath("");
977 assert!(result.is_err());
978 }
979
980 #[test]
981 fn test_parse_and_or() {
982 let expr = parse_xpath("a = 1 and b = 2 or c = 3").unwrap();
983 assert!(matches!(expr, Expr::BinaryOp { .. }));
984 }
985
986 #[test]
987 fn test_parse_comparison_chain() {
988 let expr = parse_xpath("a < b <= c > d >= e").unwrap();
989 assert!(matches!(
991 expr,
992 Expr::BinaryOp {
993 op: BinaryOp::Ge,
994 ..
995 }
996 ));
997 }
998
999 #[test]
1000 fn test_parse_arithmetic() {
1001 let expr = parse_xpath("1 + 2 * 3").unwrap();
1002 match expr {
1004 Expr::BinaryOp {
1005 op: BinaryOp::Add,
1006 left,
1007 right,
1008 } => {
1009 assert!(matches!(*left, Expr::NumberLiteral(1.0)));
1010 assert!(matches!(
1011 *right,
1012 Expr::BinaryOp {
1013 op: BinaryOp::Mul,
1014 ..
1015 }
1016 ));
1017 }
1018 _ => panic!("Expected Add expression"),
1019 }
1020 }
1021
1022 #[test]
1023 fn test_parse_filter_path() {
1024 let expr = parse_xpath("//div/span").unwrap();
1025 assert!(matches!(expr, Expr::AbsolutePath(_)));
1026 }
1027
1028 #[test]
1029 fn test_parse_node_test_functions() {
1030 let expr = parse_xpath("child::node()").unwrap();
1031 assert!(matches!(expr, Expr::Step(_)));
1032 if let Expr::Step(step) = expr {
1033 assert_eq!(step.node_test, NodeTest::Node);
1034 }
1035 }
1036}