Skip to main content

libxml_rs/xml/xpath/
parser.rs

1//! XPath 1.0 Expression Parser (§25).
2//!
3//! Parses token streams from the lexer into the AST defined in `ast.rs`.
4//! Implements the full XPath 1.0 grammar.
5//!
6//! # UPSTREAM-PARITY
7//!
8//! Grammar (from XPath 1.0 spec §3.7):
9//!
10//! ```text
11//! Expr        ::= OrExpr
12//! OrExpr      ::= AndExpr ('or' AndExpr)*
13//! AndExpr     ::= EqualityExpr ('and' EqualityExpr)*
14//! EqualityExpr ::= RelationalExpr (('=' | '!=') RelationalExpr)*
15//! RelationalExpr ::= AdditiveExpr (('<' | '>' | '<=' | '>=') AdditiveExpr)*
16//! AdditiveExpr ::= MultiplicativeExpr (('+' | '-') MultiplicativeExpr)*
17//! MultiplicativeExpr ::= UnaryExpr (('*' | 'div' | 'mod') UnaryExpr)*
18//! UnaryExpr   ::= '-'* UnionExpr
19//! UnionExpr   ::= PathExpr ('|' PathExpr)*
20//! PathExpr    ::= LocationPath | FilterExpr (('/' | '//') RelativeLocationPath)?
21//! LocationPath ::= AbsoluteLocationPath | RelativeLocationPath
22//! AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
23//! RelativeLocationPath ::= Step (('/' | '//') Step)*
24//! Step        ::= AxisSpecifier NodeTest Predicate*
25//!              |  AbbreviatedStep
26//! AxisSpecifier ::= AxisName '::' | '@'?
27//! AbbreviatedStep ::= '.' | '..'
28//! Predicate   ::= '[' Expr ']'
29//! FilterExpr  ::= PrimaryExpr Predicate*
30//! PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
31//! ```
32//!
33//! # Courts
34//!
35//! XPATH-PARSER-*
36
37use crate::xml::xpath::ast::*;
38use crate::xml::xpath::lexer::Token;
39
40/// Errors that can occur during parsing.
41#[derive(Debug, Clone, PartialEq)]
42pub struct ParseError {
43    /// Human-readable description of what went wrong
44    pub message: String,
45    /// Token index at which the error was detected
46    pub pos: usize,
47}
48
49impl std::fmt::Display for ParseError {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        write!(
52            f,
53            "XPath parse error at position {}: {}",
54            self.pos, self.message
55        )
56    }
57}
58
59// ═══════════════════════════════════════════════════════════════════════════════
60// Parser
61// ═══════════════════════════════════════════════════════════════════════════════
62
63/// Recursive-descent parser for XPath 1.0 expressions.
64///
65/// Consumes the token stream produced by the lexer and builds the
66/// expression AST defined in `crate::xml::xpath::ast`.
67#[derive(Debug)]
68pub struct Parser {
69    tokens: Vec<Token>,
70    pos: usize,
71}
72
73impl Parser {
74    /// Create a parser over a token stream produced by the lexer.
75    pub const fn new(tokens: Vec<Token>) -> Self {
76        Self { tokens, pos: 0 }
77    }
78
79    /// Parse a complete XPath expression.
80    pub fn parse(&mut self) -> Result<Expr, ParseError> {
81        let expr = self.parse_or_expr()?;
82        if !self.is_eof() {
83            Err(self.error(format!("Unexpected token: {}", self.current())))?;
84        }
85        Ok(expr)
86    }
87
88    // ── Current token helpers ────────────────────────────────────────────
89
90    fn current(&self) -> Token {
91        if self.pos < self.tokens.len() {
92            self.tokens[self.pos].clone()
93        } else {
94            Token::Eof
95        }
96    }
97
98    fn peek(&self) -> Token {
99        if self.pos + 1 < self.tokens.len() {
100            self.tokens[self.pos + 1].clone()
101        } else {
102            Token::Eof
103        }
104    }
105
106    const fn advance(&mut self) {
107        if self.pos < self.tokens.len() {
108            self.pos += 1;
109        }
110    }
111
112    fn is_eof(&self) -> bool {
113        matches!(self.current(), Token::Eof)
114    }
115
116    const fn error(&self, msg: String) -> ParseError {
117        ParseError {
118            message: msg,
119            pos: self.pos,
120        }
121    }
122
123    /// Check if the current token matches the given token.
124    fn at(&self, token: &Token) -> bool {
125        std::mem::discriminant(&self.current()) == std::mem::discriminant(token)
126    }
127
128    /// Expect and consume a specific token.
129    fn expect(&mut self, expected: &Token) -> Result<(), ParseError> {
130        if self.at(expected) {
131            self.advance();
132            Ok(())
133        } else {
134            Err(self.error(format!("Expected {}, got {}", expected, self.current())))
135        }
136    }
137
138    // ── Grammar productions ──────────────────────────────────────────────
139
140    /// OrExpr ::= AndExpr ('or' AndExpr)*
141    fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
142        let mut left = self.parse_and_expr()?;
143        while matches!(self.current(), Token::Or) {
144            self.advance();
145            let right = self.parse_and_expr()?;
146            left = Expr::BinaryOp {
147                op: BinaryOp::Or,
148                left: Box::new(left),
149                right: Box::new(right),
150            };
151        }
152        Ok(left)
153    }
154
155    /// AndExpr ::= EqualityExpr ('and' EqualityExpr)*
156    fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
157        let mut left = self.parse_equality_expr()?;
158        while matches!(self.current(), Token::And) {
159            self.advance();
160            let right = self.parse_equality_expr()?;
161            left = Expr::BinaryOp {
162                op: BinaryOp::And,
163                left: Box::new(left),
164                right: Box::new(right),
165            };
166        }
167        Ok(left)
168    }
169
170    /// EqualityExpr ::= RelationalExpr (('=' | '!=') RelationalExpr)*
171    fn parse_equality_expr(&mut self) -> Result<Expr, ParseError> {
172        let mut left = self.parse_relational_expr()?;
173        loop {
174            let op = match self.current() {
175                Token::Eq => BinaryOp::Eq,
176                Token::Ne => BinaryOp::Ne,
177                _ => break,
178            };
179            self.advance();
180            let right = self.parse_relational_expr()?;
181            left = Expr::BinaryOp {
182                op,
183                left: Box::new(left),
184                right: Box::new(right),
185            };
186        }
187        Ok(left)
188    }
189
190    /// RelationalExpr ::= AdditiveExpr (('<' | '>' | '<=' | '>=') AdditiveExpr)*
191    fn parse_relational_expr(&mut self) -> Result<Expr, ParseError> {
192        let mut left = self.parse_additive_expr()?;
193        loop {
194            let op = match self.current() {
195                Token::Lt => BinaryOp::Lt,
196                Token::Gt => BinaryOp::Gt,
197                Token::Le => BinaryOp::Le,
198                Token::Ge => BinaryOp::Ge,
199                _ => break,
200            };
201            self.advance();
202            let right = self.parse_additive_expr()?;
203            left = Expr::BinaryOp {
204                op,
205                left: Box::new(left),
206                right: Box::new(right),
207            };
208        }
209        Ok(left)
210    }
211
212    /// AdditiveExpr ::= MultiplicativeExpr (('+' | '-') MultiplicativeExpr)*
213    fn parse_additive_expr(&mut self) -> Result<Expr, ParseError> {
214        let mut left = self.parse_multiplicative_expr()?;
215        loop {
216            let op = match self.current() {
217                Token::Plus => BinaryOp::Add,
218                Token::Minus => BinaryOp::Sub,
219                _ => break,
220            };
221            self.advance();
222            let right = self.parse_multiplicative_expr()?;
223            left = Expr::BinaryOp {
224                op,
225                left: Box::new(left),
226                right: Box::new(right),
227            };
228        }
229        Ok(left)
230    }
231
232    /// MultiplicativeExpr ::= UnaryExpr (('*' | 'div' | 'mod') UnaryExpr)*
233    fn parse_multiplicative_expr(&mut self) -> Result<Expr, ParseError> {
234        let mut left = self.parse_unary_expr()?;
235        loop {
236            let op = match self.current() {
237                // '*' after an expression is multiply, not wildcard
238                Token::Star => BinaryOp::Mul,
239                Token::Div => BinaryOp::Div,
240                Token::Mod => BinaryOp::Mod,
241                _ => break,
242            };
243            self.advance();
244            let right = self.parse_unary_expr()?;
245            left = Expr::BinaryOp {
246                op,
247                left: Box::new(left),
248                right: Box::new(right),
249            };
250        }
251        Ok(left)
252    }
253
254    /// UnaryExpr ::= '-'* UnionExpr
255    fn parse_unary_expr(&mut self) -> Result<Expr, ParseError> {
256        let mut minus_count = 0;
257        while matches!(self.current(), Token::Minus) {
258            self.advance();
259            minus_count += 1;
260        }
261        let mut expr = self.parse_union_expr()?;
262        if minus_count % 2 == 1 {
263            expr = Expr::UnaryMinus(Box::new(expr));
264        }
265        Ok(expr)
266    }
267
268    /// UnionExpr ::= PathExpr ('|' PathExpr)*
269    fn parse_union_expr(&mut self) -> Result<Expr, ParseError> {
270        let mut left = self.parse_path_expr()?;
271        while matches!(self.current(), Token::Pipe) {
272            self.advance();
273            let right = self.parse_path_expr()?;
274            left = Expr::Union(Box::new(left), Box::new(right));
275        }
276        Ok(left)
277    }
278
279    /// PathExpr ::= LocationPath | FilterExpr (('/' | '//') RelativeLocationPath)?
280    fn parse_path_expr(&mut self) -> Result<Expr, ParseError> {
281        // Check if it starts with a location path
282        if self.is_location_path_start() {
283            return self.parse_location_path();
284        }
285
286        // Otherwise it's a FilterExpr (primary with optional predicates and path)
287        let mut expr = self.parse_filter_expr()?;
288
289        // Optional / or // followed by relative location path
290        loop {
291            match self.current() {
292                Token::Slash => {
293                    self.advance();
294                    let step = self.parse_relative_location_path()?;
295                    expr = Expr::RelativePath(Box::new(expr), Box::new(step));
296                }
297                Token::DoubleSlash => {
298                    self.advance();
299                    let step = self.parse_relative_location_path()?;
300                    // // is shorthand for /descendant-or-self::node()/
301                    let descendant = Expr::Step(Step {
302                        axis: Axis::DescendantOrSelf,
303                        node_test: NodeTest::Node,
304                        predicates: vec![],
305                    });
306                    let path = Expr::RelativePath(Box::new(descendant), Box::new(step));
307                    expr = Expr::RelativePath(Box::new(expr), Box::new(path));
308                }
309                _ => break,
310            }
311        }
312
313        Ok(expr)
314    }
315
316    /// Check if the current position starts a location path.
317    fn is_location_path_start(&self) -> bool {
318        match self.current() {
319            Token::Slash | Token::DoubleSlash => true,
320            Token::Dot | Token::DotDot => true,
321            Token::At => true,
322            Token::Star => true,
323            // Name that could be a step (not followed by '(' which means function call)
324            Token::Name(_) => {
325                // Check if this is followed by :: (axis), /, //, [, or is a simple step
326                let next = self.peek();
327                !matches!(next, Token::LParen)
328            }
329            // Axis keywords
330            Token::Child
331            | Token::Descendant
332            | Token::DescendantOrSelf
333            | Token::Ancestor
334            | Token::AncestorOrSelf
335            | Token::Attribute
336            | Token::Following
337            | Token::FollowingSibling
338            | Token::Namespace
339            | Token::Parent
340            | Token::Preceding
341            | Token::PrecedingSibling
342            | Token::Self_ => true,
343            _ => false,
344        }
345    }
346
347    /// LocationPath ::= AbsoluteLocationPath | RelativeLocationPath
348    fn parse_location_path(&mut self) -> Result<Expr, ParseError> {
349        match self.current() {
350            Token::Slash => {
351                self.advance();
352                if self.is_location_path_start() {
353                    let path = self.parse_relative_location_path()?;
354                    Ok(Expr::AbsolutePath(Box::new(path)))
355                } else {
356                    // Just "/" - root node
357                    Ok(Expr::Step(Step {
358                        axis: Axis::Self_,
359                        node_test: NodeTest::Node,
360                        predicates: vec![],
361                    }))
362                }
363            }
364            Token::DoubleSlash => {
365                self.advance();
366                let path = self.parse_relative_location_path()?;
367                let descendant = Expr::Step(Step {
368                    axis: Axis::DescendantOrSelf,
369                    node_test: NodeTest::Node,
370                    predicates: vec![],
371                });
372                Ok(Expr::AbsolutePath(Box::new(Expr::RelativePath(
373                    Box::new(descendant),
374                    Box::new(path),
375                ))))
376            }
377            _ => self.parse_relative_location_path(),
378        }
379    }
380
381    /// RelativeLocationPath ::= Step (('/' | '//') Step)*
382    fn parse_relative_location_path(&mut self) -> Result<Expr, ParseError> {
383        let mut expr = self.parse_step()?;
384        loop {
385            match self.current() {
386                Token::Slash => {
387                    self.advance();
388                    let step = self.parse_step()?;
389                    expr = Expr::RelativePath(Box::new(expr), Box::new(step));
390                }
391                Token::DoubleSlash => {
392                    self.advance();
393                    let step = self.parse_step()?;
394                    let descendant = Expr::Step(Step {
395                        axis: Axis::DescendantOrSelf,
396                        node_test: NodeTest::Node,
397                        predicates: vec![],
398                    });
399                    let path = Expr::RelativePath(Box::new(descendant), Box::new(step));
400                    expr = Expr::RelativePath(Box::new(expr), Box::new(path));
401                }
402                _ => break,
403            }
404        }
405        Ok(expr)
406    }
407
408    /// Step ::= AxisSpecifier NodeTest Predicate*
409    ///        | AbbreviatedStep
410    fn parse_step(&mut self) -> Result<Expr, ParseError> {
411        // AbbreviatedStep ::= '.' | '..'
412        match self.current() {
413            Token::Dot => {
414                self.advance();
415                return Ok(Expr::Step(Step {
416                    axis: Axis::Self_,
417                    node_test: NodeTest::Node,
418                    predicates: vec![],
419                }));
420            }
421            Token::DotDot => {
422                self.advance();
423                return Ok(Expr::Step(Step {
424                    axis: Axis::Parent,
425                    node_test: NodeTest::Node,
426                    predicates: vec![],
427                }));
428            }
429            _ => {}
430        }
431
432        // Determine axis
433        let axis = self.parse_axis_specifier();
434
435        // Parse node test
436        let node_test = self.parse_node_test()?;
437
438        // Parse predicates
439        let mut predicates = Vec::new();
440        while matches!(self.current(), Token::LBracket) {
441            self.advance(); // consume '['
442            let pred = self.parse_or_expr()?;
443            self.expect(&Token::RBracket)?;
444            predicates.push(pred);
445        }
446
447        Ok(Expr::Step(Step {
448            axis,
449            node_test,
450            predicates,
451        }))
452    }
453
454    /// AxisSpecifier ::= AxisName '::' | '@'?
455    fn parse_axis_specifier(&mut self) -> Axis {
456        // Check for @ (attribute axis shorthand)
457        if matches!(self.current(), Token::At) {
458            self.advance();
459            return Axis::Attribute;
460        }
461
462        // Check for axis keyword followed by ::
463        // We check if the NEXT token is DoubleColon to decide whether this
464        // is an axis specifier or just a name being used as a node test.
465        let is_axis = match self.current() {
466            Token::Ancestor
467            | Token::AncestorOrSelf
468            | Token::Attribute
469            | Token::Child
470            | Token::Descendant
471            | Token::DescendantOrSelf
472            | Token::Following
473            | Token::FollowingSibling
474            | Token::Namespace
475            | Token::Parent
476            | Token::Preceding
477            | Token::PrecedingSibling
478            | Token::Self_ => matches!(self.peek(), Token::DoubleColon),
479            _ => false,
480        };
481
482        if is_axis {
483            let axis = match self.current() {
484                Token::Ancestor => Axis::Ancestor,
485                Token::AncestorOrSelf => Axis::AncestorOrSelf,
486                Token::Attribute => Axis::Attribute,
487                Token::Child => Axis::Child,
488                Token::Descendant => Axis::Descendant,
489                Token::DescendantOrSelf => Axis::DescendantOrSelf,
490                Token::Following => Axis::Following,
491                Token::FollowingSibling => Axis::FollowingSibling,
492                Token::Namespace => Axis::Namespace,
493                Token::Parent => Axis::Parent,
494                Token::Preceding => Axis::Preceding,
495                Token::PrecedingSibling => Axis::PrecedingSibling,
496                Token::Self_ => Axis::Self_,
497                _ => unreachable!(),
498            };
499            self.advance(); // consume axis keyword
500            self.advance(); // consume ::
501            return axis;
502        }
503
504        // Default axis is "child" for everything except attribute
505        Axis::Child
506    }
507
508    /// Convert a keyword token back to its string name for use as a node test.
509    fn token_to_name(&self, token: &Token) -> Option<String> {
510        match token {
511            Token::Name(ref s) => Some(s.clone()),
512            Token::Div => Some("div".to_string()),
513            Token::Mod => Some("mod".to_string()),
514            Token::And => Some("and".to_string()),
515            Token::Or => Some("or".to_string()),
516            Token::Ancestor => Some("ancestor".to_string()),
517            Token::AncestorOrSelf => Some("ancestor-or-self".to_string()),
518            Token::Attribute => Some("attribute".to_string()),
519            Token::Child => Some("child".to_string()),
520            Token::Descendant => Some("descendant".to_string()),
521            Token::DescendantOrSelf => Some("descendant-or-self".to_string()),
522            Token::Following => Some("following".to_string()),
523            Token::FollowingSibling => Some("following-sibling".to_string()),
524            Token::Namespace => Some("namespace".to_string()),
525            Token::Parent => Some("parent".to_string()),
526            Token::Preceding => Some("preceding".to_string()),
527            Token::PrecedingSibling => Some("preceding-sibling".to_string()),
528            Token::Self_ => Some("self".to_string()),
529            _ => None,
530        }
531    }
532
533    /// NodeTest ::= NameTest | 'comment()' | 'text()' | 'processing-instruction()' | 'node()'
534    /// NameTest ::= '*' | NCName ':' '*' | QName
535    fn parse_node_test(&mut self) -> Result<NodeTest, ParseError> {
536        // Try to get the current token as a potential name
537        let name_opt = self.token_to_name(&self.current());
538
539        match self.current() {
540            Token::Star => {
541                self.advance();
542                Ok(NodeTest::NameTest(NameTest::Any))
543            }
544            _ if name_opt.is_some() => {
545                let name = name_opt.unwrap();
546
547                // Check for function-style node tests: node(), text(), comment(), processing-instruction()
548                if matches!(self.peek(), Token::LParen) {
549                    match name.as_str() {
550                        "node" => {
551                            self.advance();
552                            self.advance();
553                            self.advance(); // name, (, )
554                            Ok(NodeTest::Node)
555                        }
556                        "text" => {
557                            self.advance();
558                            self.advance();
559                            self.advance();
560                            Ok(NodeTest::Text)
561                        }
562                        "comment" => {
563                            self.advance();
564                            self.advance();
565                            self.advance();
566                            Ok(NodeTest::Comment)
567                        }
568                        "processing-instruction" => {
569                            self.advance(); // name
570                            self.advance(); // (
571                                            // Check for optional string argument
572                            let target = if matches!(self.current(), Token::StringLiteral(_)) {
573                                if let Token::StringLiteral(s) = self.current() {
574                                    self.advance();
575                                    Some(s)
576                                } else {
577                                    None
578                                }
579                            } else {
580                                None
581                            };
582                            self.expect(&Token::RParen)?;
583                            Ok(NodeTest::ProcessingInstruction(target))
584                        }
585                        _ => {
586                            // Regular function call, not a node test
587                            self.advance(); // function name
588                            self.advance(); // (
589                            let mut args = Vec::new();
590                            if !matches!(self.current(), Token::RParen) {
591                                args.push(self.parse_or_expr()?);
592                                while matches!(self.current(), Token::Comma) {
593                                    self.advance();
594                                    args.push(self.parse_or_expr()?);
595                                }
596                            }
597                            self.expect(&Token::RParen)?;
598                            // Wrap in a step with a name test
599                            Ok(NodeTest::NameTest(NameTest::LocalName(name)))
600                        }
601                    }
602                } else {
603                    self.advance();
604                    // Check for prefix:*
605                    if let Some(rest) = name.strip_suffix(":*") {
606                        Ok(NodeTest::NsWildcard(rest.to_string()))
607                    } else if let Some((prefix, local)) = name.split_once(':') {
608                        Ok(NodeTest::NameTest(NameTest::QName {
609                            prefix: prefix.to_string(),
610                            local: local.to_string(),
611                        }))
612                    } else {
613                        Ok(NodeTest::NameTest(NameTest::LocalName(name)))
614                    }
615                }
616            }
617            _ => Err(self.error(format!("Expected node test, got {}", self.current()))),
618        }
619    }
620
621    /// FilterExpr ::= PrimaryExpr Predicate*
622    fn parse_filter_expr(&mut self) -> Result<Expr, ParseError> {
623        let primary = self.parse_primary_expr()?;
624
625        // Predicates after primary
626        let mut predicates = Vec::new();
627        while matches!(self.current(), Token::LBracket) {
628            self.advance(); // consume '['
629            let pred = self.parse_or_expr()?;
630            self.expect(&Token::RBracket)?;
631            predicates.push(pred);
632        }
633
634        if predicates.is_empty() {
635            Ok(primary)
636        } else {
637            Ok(Expr::Filter(Box::new(primary), predicates))
638        }
639    }
640
641    /// PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
642    fn parse_primary_expr(&mut self) -> Result<Expr, ParseError> {
643        match self.current() {
644            Token::Dollar => {
645                self.advance();
646                if let Token::Name(name) = self.current() {
647                    let name = name.clone();
648                    self.advance();
649                    Ok(Expr::Variable(name))
650                } else {
651                    Err(self.error("Expected variable name after $".to_string()))
652                }
653            }
654            Token::LParen => {
655                self.advance();
656                let expr = self.parse_or_expr()?;
657                self.expect(&Token::RParen)?;
658                Ok(expr)
659            }
660            Token::StringLiteral(ref s) => {
661                let s = s.clone();
662                self.advance();
663                Ok(Expr::StringLiteral(s))
664            }
665            Token::NumberLiteral(n) => {
666                self.advance();
667                Ok(Expr::NumberLiteral(n))
668            }
669            Token::Name(ref name) => {
670                let name = name.clone();
671                if matches!(self.peek(), Token::LParen) {
672                    // Function call
673                    self.advance(); // function name
674                    self.advance(); // (
675                    let mut args = Vec::new();
676                    if !matches!(self.current(), Token::RParen) {
677                        args.push(self.parse_or_expr()?);
678                        while matches!(self.current(), Token::Comma) {
679                            self.advance();
680                            args.push(self.parse_or_expr()?);
681                        }
682                    }
683                    self.expect(&Token::RParen)?;
684                    Ok(Expr::FunctionCall { name, args })
685                } else {
686                    // Standalone name - could be a step or something else
687                    // But at the primary level, this shouldn't happen
688                    Err(self.error(format!("Unexpected name '{}' in primary expression", name)))
689                }
690            }
691            _ => Err(self.error(format!(
692                "Expected primary expression, got {}",
693                self.current()
694            ))),
695        }
696    }
697}
698
699// ═══════════════════════════════════════════════════════════════════════════════
700// Convenience function
701// ═══════════════════════════════════════════════════════════════════════════════
702
703/// Parse an XPath expression string into an AST.
704pub fn parse_xpath(input: &str) -> Result<Expr, ParseError> {
705    let mut lexer = crate::xml::xpath::lexer::Lexer::new(input);
706    let mut tokens = Vec::new();
707    loop {
708        let tok = lexer.next_token();
709        let is_eof = matches!(tok, Token::Eof);
710        tokens.push(tok);
711        if is_eof {
712            break;
713        }
714    }
715    let mut parser = Parser::new(tokens);
716    parser.parse()
717}
718
719// ═══════════════════════════════════════════════════════════════════════════════
720// Tests
721// ═══════════════════════════════════════════════════════════════════════════════
722
723#[cfg(test)]
724mod tests {
725    use super::*;
726
727    #[test]
728    fn test_parse_simple_name() {
729        let expr = parse_xpath("para").unwrap();
730        assert!(matches!(expr, Expr::Step(_)));
731    }
732
733    #[test]
734    fn test_parse_absolute_path() {
735        let expr = parse_xpath("/child::para").unwrap();
736        assert!(matches!(expr, Expr::AbsolutePath(_)));
737    }
738
739    #[test]
740    fn test_parse_attribute() {
741        let expr = parse_xpath("@attr").unwrap();
742        assert!(matches!(expr, Expr::Step(_)));
743    }
744
745    #[test]
746    fn test_parse_predicate() {
747        let expr = parse_xpath("para[1]").unwrap();
748        assert!(matches!(expr, Expr::Step(_)));
749    }
750
751    #[test]
752    fn test_parse_function_call() {
753        let expr = parse_xpath("position()").unwrap();
754        assert!(matches!(expr, Expr::FunctionCall { .. }));
755    }
756
757    #[test]
758    fn test_parse_binary_op() {
759        let expr = parse_xpath("a = b").unwrap();
760        assert!(matches!(
761            expr,
762            Expr::BinaryOp {
763                op: BinaryOp::Eq,
764                ..
765            }
766        ));
767    }
768
769    #[test]
770    fn test_parse_union() {
771        let expr = parse_xpath("a | b").unwrap();
772        assert!(matches!(expr, Expr::Union(_, _)));
773    }
774
775    #[test]
776    fn test_parse_variable() {
777        let expr = parse_xpath("$var").unwrap();
778        assert!(matches!(expr, Expr::Variable(_)));
779    }
780
781    #[test]
782    fn test_parse_string_literal() {
783        let expr = parse_xpath("'hello'").unwrap();
784        assert_eq!(expr, Expr::StringLiteral("hello".to_string()));
785    }
786
787    #[test]
788    fn test_parse_number() {
789        let expr = parse_xpath("42").unwrap();
790        assert_eq!(expr, Expr::NumberLiteral(42.0));
791    }
792
793    #[test]
794    fn test_parse_nested_expression() {
795        let expr = parse_xpath("(1 + 2) * 3").unwrap();
796        assert!(matches!(
797            expr,
798            Expr::BinaryOp {
799                op: BinaryOp::Mul,
800                ..
801            }
802        ));
803    }
804
805    #[test]
806    fn test_parse_chained_path() {
807        let expr = parse_xpath("a/b/c").unwrap();
808        assert!(matches!(expr, Expr::RelativePath(_, _)));
809    }
810
811    #[test]
812    fn test_parse_double_slash() {
813        let expr = parse_xpath("//para").unwrap();
814        assert!(matches!(expr, Expr::AbsolutePath(_)));
815    }
816
817    #[test]
818    fn test_parse_dot() {
819        let expr = parse_xpath(".").unwrap();
820        assert!(matches!(expr, Expr::Step(_)));
821    }
822
823    #[test]
824    fn test_parse_dot_dot() {
825        let expr = parse_xpath("..").unwrap();
826        assert!(matches!(expr, Expr::Step(_)));
827    }
828
829    #[test]
830    fn test_parse_unary_minus() {
831        let expr = parse_xpath("-5").unwrap();
832        assert!(matches!(expr, Expr::UnaryMinus(_)));
833    }
834
835    #[test]
836    fn test_parse_double_unary_minus() {
837        let expr = parse_xpath("--5").unwrap();
838        // Should cancel out
839        assert!(!matches!(expr, Expr::UnaryMinus(_)));
840    }
841
842    #[test]
843    fn test_parse_complex_expression() {
844        let expr = parse_xpath("/html/body//div[@class='main']/p[1]").unwrap();
845        assert!(matches!(expr, Expr::AbsolutePath(_)));
846    }
847
848    #[test]
849    fn test_parse_error() {
850        let result = parse_xpath("(");
851        assert!(result.is_err());
852    }
853
854    #[test]
855    fn test_parse_empty() {
856        let result = parse_xpath("");
857        assert!(result.is_err());
858    }
859
860    #[test]
861    fn test_parse_and_or() {
862        let expr = parse_xpath("a = 1 and b = 2 or c = 3").unwrap();
863        assert!(matches!(expr, Expr::BinaryOp { .. }));
864    }
865
866    #[test]
867    fn test_parse_comparison_chain() {
868        let expr = parse_xpath("a < b <= c > d >= e").unwrap();
869        // Should parse as: ((((a < b) <= c) > d) >= e)
870        assert!(matches!(
871            expr,
872            Expr::BinaryOp {
873                op: BinaryOp::Ge,
874                ..
875            }
876        ));
877    }
878
879    #[test]
880    fn test_parse_arithmetic() {
881        let expr = parse_xpath("1 + 2 * 3").unwrap();
882        // 2 * 3 should bind tighter: 1 + (2 * 3)
883        match expr {
884            Expr::BinaryOp {
885                op: BinaryOp::Add,
886                left,
887                right,
888            } => {
889                assert!(matches!(*left, Expr::NumberLiteral(1.0)));
890                assert!(matches!(
891                    *right,
892                    Expr::BinaryOp {
893                        op: BinaryOp::Mul,
894                        ..
895                    }
896                ));
897            }
898            _ => panic!("Expected Add expression"),
899        }
900    }
901
902    #[test]
903    fn test_parse_filter_path() {
904        let expr = parse_xpath("//div/span").unwrap();
905        assert!(matches!(expr, Expr::AbsolutePath(_)));
906    }
907
908    #[test]
909    fn test_parse_node_test_functions() {
910        let expr = parse_xpath("child::node()").unwrap();
911        assert!(matches!(expr, Expr::Step(_)));
912        if let Expr::Step(step) = expr {
913            assert_eq!(step.node_test, NodeTest::Node);
914        }
915    }
916}