Skip to main content

servo_xpath/
parser.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use markup5ever::{LocalName, Namespace, Prefix, QualName};
6
7use crate::NamespaceResolver;
8use crate::ast::{
9    Axis, BinaryOperator, CoreFunction, Expression, FilterExpression, KindTest, Literal,
10    LocationStepExpression, NodeTest, PathExpression, PredicateListExpression,
11};
12use crate::tokenizer::{Error as TokenizerError, LiteralToken, OperatorToken, Token, tokenize};
13
14#[derive(Clone, Debug)]
15pub enum Error {
16    Tokenization(TokenizerError),
17    UnknownFunction,
18    ExpectedSeperatorBetweenFunctionArguments,
19    TooFewFunctionArguments,
20    TooManyFunctionArguments,
21    ExpectedClosingParenthesis,
22    ExpectedClosingBracket,
23    CannotUseVariables,
24    UnknownAxis,
25    TrailingInput,
26    UnknownNodeTest,
27    ExpectedNodeTest,
28    UnexpectedEndOfInput,
29    FailedToResolveNamespacePrefix,
30}
31
32impl From<TokenizerError> for Error {
33    fn from(value: TokenizerError) -> Self {
34        Self::Tokenization(value)
35    }
36}
37
38/// Parse an XPath expression from a string.
39pub fn parse<N>(
40    cx: &mut N::Context,
41    input: &str,
42    namespace_resolver: Option<N>,
43    is_in_html_document: bool,
44) -> Result<Expression, Error>
45where
46    N: NamespaceResolver,
47{
48    let mut parser = Parser::new(input, namespace_resolver, is_in_html_document)?;
49    let root_expression = parser.parse_expression(cx)?;
50    if !parser.remaining().is_empty() {
51        log::debug!(
52            "Found trailing tokens after expression: {:?}",
53            parser.remaining()
54        );
55        return Err(Error::TrailingInput);
56    }
57
58    log::debug!("Parsed XPath expression: {root_expression:?}");
59    Ok(root_expression)
60}
61
62pub(crate) struct Parser<'a, N>
63where
64    N: NamespaceResolver,
65{
66    tokens: Vec<Token<'a>>,
67    position: usize,
68    namespace_resolver: Option<N>,
69    is_in_html_document: bool,
70}
71
72impl<'a, N> Parser<'a, N>
73where
74    N: NamespaceResolver,
75{
76    pub(crate) fn new(
77        input: &'a str,
78        namespace_resolver: Option<N>,
79        is_in_html_document: bool,
80    ) -> Result<Self, TokenizerError> {
81        let parser = Self {
82            tokens: tokenize(input)?,
83            position: 0,
84            namespace_resolver,
85            is_in_html_document,
86        };
87        Ok(parser)
88    }
89
90    fn expect_current_token(&self) -> Result<Token<'a>, Error> {
91        self.tokens
92            .get(self.position)
93            .copied()
94            .ok_or(Error::UnexpectedEndOfInput)
95    }
96
97    fn peek(&self, n: usize) -> Option<Token<'a>> {
98        self.tokens.get(self.position + n).copied()
99    }
100
101    fn advance(&mut self, advance_by: usize) {
102        self.position += advance_by;
103    }
104
105    pub(crate) fn remaining(&self) -> &[Token<'a>] {
106        &self.tokens[self.position..]
107    }
108
109    fn resolve_qualified_name(
110        &self,
111        cx: &mut N::Context,
112        prefix: &str,
113    ) -> Result<Namespace, Error> {
114        let Some(namespace_resolver) = self.namespace_resolver.as_ref() else {
115            return Err(Error::FailedToResolveNamespacePrefix);
116        };
117
118        log::debug!("Resolving namespace prefix: {:?}", prefix);
119        namespace_resolver
120            .resolve_namespace_prefix(cx, prefix)
121            .map(Namespace::from)
122            .ok_or(Error::FailedToResolveNamespacePrefix)
123    }
124
125    fn advance_if_current_token_equals(&mut self, wanted: Token<'a>) -> bool {
126        if self.peek(0).is_some_and(|token| token == wanted) {
127            self.position += 1;
128            true
129        } else {
130            false
131        }
132    }
133
134    pub(crate) fn parse_expression(&mut self, cx: &mut N::Context) -> Result<Expression, Error> {
135        let mut result;
136
137        let mut expression_stack: Vec<(Expression, OperatorToken)> = vec![];
138        loop {
139            let mut negations = 0;
140            while self.advance_if_current_token_equals(Token::Operator(OperatorToken::Subtract)) {
141                negations += 1;
142            }
143
144            result = self.parse_union_expression(cx)?;
145
146            if negations > 1 {
147                if negations % 2 == 0 {
148                    result = Expression::Function(CoreFunction::Number(Some(Box::new(result))))
149                } else {
150                    result = Expression::Negate(Box::new(result))
151                }
152            }
153
154            // If the next token is not an operator then the expression ends here.
155            let Some(next_token) = self.peek(0) else {
156                break;
157            };
158            let Token::Operator(current_operator) = next_token else {
159                break;
160            };
161            self.advance(1);
162
163            // Finish all ongoing expressions that have higher precedence
164            while let Some((lhs, operator)) = expression_stack
165                .pop_if(|(_, operator)| current_operator.precedence() <= operator.precedence())
166            {
167                result = create_binary_expression(Box::new(lhs), operator, Box::new(result));
168            }
169
170            expression_stack.push((result, current_operator));
171        }
172
173        // Close any expressions that are still open
174        for (lhs, operator) in expression_stack.into_iter().rev() {
175            result = create_binary_expression(Box::new(lhs), operator, Box::new(result))
176        }
177
178        Ok(result)
179    }
180
181    /// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-UnionExpr>
182    fn parse_union_expression(&mut self, cx: &mut N::Context) -> Result<Expression, Error> {
183        let mut result = self.parse_path_expression(cx)?;
184
185        while self.advance_if_current_token_equals(Token::Union) {
186            let rhs = self.parse_path_expression(cx)?;
187            result = Expression::Binary(Box::new(result), BinaryOperator::Union, Box::new(rhs));
188        }
189
190        Ok(result)
191    }
192
193    /// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-PathExpr>
194    fn parse_path_expression(&mut self, cx: &mut N::Context) -> Result<Expression, Error> {
195        let current_token = self.expect_current_token()?;
196
197        let is_absolute = matches!(current_token, Token::Parent | Token::Ancestor);
198        let has_implicit_descendant_or_self_step = current_token == Token::Ancestor;
199
200        if is_absolute {
201            self.advance(1);
202
203            if !self
204                .peek(0)
205                .is_some_and(|token| token.is_start_of_location_step())
206            {
207                return Ok(Expression::Path(PathExpression {
208                    is_absolute,
209                    has_implicit_descendant_or_self_step,
210                    steps: vec![],
211                }));
212            }
213        }
214
215        let first_expression = if !is_absolute {
216            let expression = self.parse_filter_or_step_expression(cx)?;
217
218            // If there are no further steps in this path expression then return it as-is.
219            if !self
220                .peek(0)
221                .is_some_and(|token| matches!(token, Token::Parent | Token::Ancestor))
222            {
223                return Ok(expression);
224            }
225
226            expression
227        } else {
228            self.parse_step_expression(cx)?
229        };
230
231        let mut path_expression = PathExpression {
232            is_absolute,
233            has_implicit_descendant_or_self_step,
234            steps: vec![first_expression],
235        };
236
237        while let Some(current_token) = self.peek(0) {
238            match current_token {
239                Token::Ancestor => {
240                    self.advance(1);
241
242                    // Insert implicit "descendant-or-self" step
243                    path_expression
244                        .steps
245                        .push(Expression::LocationStep(LocationStepExpression {
246                            axis: Axis::DescendantOrSelf,
247                            node_test: NodeTest::Kind(KindTest::Node),
248                            predicate_list: PredicateListExpression { predicates: vec![] },
249                        }));
250                    true
251                },
252                Token::Parent => {
253                    self.advance(1);
254                    false
255                },
256                _ => {
257                    // The path expression ends here.
258                    return Ok(Expression::Path(path_expression));
259                },
260            };
261
262            let step_expression = self.parse_step_expression(cx)?;
263            path_expression.steps.push(step_expression);
264        }
265
266        Ok(Expression::Path(path_expression))
267    }
268
269    /// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-FilterExpr>
270    ///
271    /// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Step>
272    fn parse_filter_or_step_expression(
273        &mut self,
274        cx: &mut N::Context,
275    ) -> Result<Expression, Error> {
276        let mut expression = match self.expect_current_token()? {
277            Token::FunctionCall(name) => {
278                self.advance(1);
279                self.parse_function_call(cx, name)?
280            },
281            Token::OpeningParenthesis => {
282                self.advance(1);
283                let expression = self.parse_expression(cx)?;
284                if !self.advance_if_current_token_equals(Token::ClosingParenthesis) {
285                    log::debug!("{:?}", self.expect_current_token()?);
286                    return Err(Error::ExpectedClosingParenthesis);
287                }
288                expression
289            },
290            Token::Literal(literal) => {
291                self.advance(1);
292                Expression::Literal(literal.into())
293            },
294            Token::VariableReference(_) => {
295                // TODO: Gecko does *something* here. Is it observable?
296                // https://searchfox.org/firefox-main/rev/054e2b072785984455b3b59acad9444ba1eeffb4/dom/xslt/xpath/txExprParser.cpp#349
297                return Err(Error::CannotUseVariables);
298            },
299            _ => self.parse_step_expression(cx)?,
300        };
301
302        // Parse a potential list of predicates
303        let predicate_list = self.parse_predicates(cx)?;
304        if !predicate_list.predicates.is_empty() {
305            expression = Expression::Filter(FilterExpression {
306                expression: Box::new(expression),
307                predicates: predicate_list,
308            });
309        }
310
311        Ok(expression)
312    }
313
314    /// <https://www.w3.org/TR/1999/REC-xpath-19991116/#section-Location-Steps>
315    fn parse_step_expression(&mut self, cx: &mut N::Context) -> Result<Expression, Error> {
316        let axis;
317        let mut node_test = None;
318
319        match self.expect_current_token()? {
320            Token::AxisIdentifier(axis_name) => {
321                self.advance(1);
322                axis = match axis_name {
323                    "ancestor" => Axis::Ancestor,
324                    "ancestor-or-self" => Axis::AncestorOrSelf,
325                    "attribute" => Axis::Attribute,
326                    "child" => Axis::Child,
327                    "descendant" => Axis::Descendant,
328                    "descendant-or-self" => Axis::DescendantOrSelf,
329                    "following" => Axis::Following,
330                    "following-sibling" => Axis::FollowingSibling,
331                    "namespace" => Axis::Namespace,
332                    "parent" => Axis::Parent,
333                    "preceding" => Axis::Preceding,
334                    "preceding-sibling" => Axis::PrecedingSibling,
335                    "self" => Axis::Self_,
336                    _ => {
337                        log::debug!("Unknown XPath axis name: {axis_name:?}");
338                        return Err(Error::UnknownAxis);
339                    },
340                };
341            },
342            Token::AtSign => {
343                // This is a shorthand for the attribute axis
344                self.advance(1);
345                axis = Axis::Attribute;
346            },
347            Token::ParentNode => {
348                self.advance(1);
349                axis = Axis::Parent;
350                node_test = Some(NodeTest::Kind(KindTest::Node));
351            },
352            Token::SelfNode => {
353                self.advance(1);
354                axis = Axis::Self_;
355                node_test = Some(NodeTest::Kind(KindTest::Node));
356            },
357            _ => {
358                axis = Axis::Child;
359            },
360        }
361
362        let node_test = if let Some(node_test) = node_test {
363            node_test
364        } else if let Token::CName(name_token) = self.expect_current_token()? {
365            self.advance(1);
366
367            if name_token.local_name == "*" {
368                NodeTest::Wildcard
369            } else {
370                let namespace = name_token
371                    .prefix
372                    .map(|prefix| self.resolve_qualified_name(cx, prefix))
373                    .transpose()?;
374
375                let local_name = if self.is_in_html_document && name_token.prefix.is_none() {
376                    LocalName::from(name_token.local_name.to_ascii_lowercase().as_str())
377                } else {
378                    LocalName::from(name_token.local_name)
379                };
380
381                let qualified_name = QualName {
382                    prefix: name_token.prefix.map(Prefix::from),
383                    ns: namespace.unwrap_or_default(),
384                    local: local_name,
385                };
386
387                NodeTest::Name(qualified_name)
388            }
389        } else {
390            self.parse_node_test()?
391        };
392
393        let predicate_list = self.parse_predicates(cx)?;
394        Ok(Expression::LocationStep(LocationStepExpression {
395            axis,
396            node_test,
397            predicate_list,
398        }))
399    }
400
401    fn parse_node_test(&mut self) -> Result<NodeTest, Error> {
402        let kind_test = match self.expect_current_token()? {
403            Token::CommentTest => {
404                self.advance(1);
405                KindTest::Comment
406            },
407            Token::NodeTest => {
408                self.advance(1);
409                KindTest::Node
410            },
411            Token::ProcessingInstructionTest => {
412                self.advance(1);
413                let name = if let Token::Literal(LiteralToken::String(name)) =
414                    self.expect_current_token()?
415                {
416                    self.advance(1);
417                    Some(name)
418                } else {
419                    None
420                };
421                KindTest::PI(name.map(String::from))
422            },
423            Token::TextTest => {
424                self.advance(1);
425                KindTest::Text
426            },
427            _ => {
428                return Err(Error::ExpectedNodeTest);
429            },
430        };
431
432        if !self.advance_if_current_token_equals(Token::ClosingParenthesis) {
433            return Err(Error::TooManyFunctionArguments);
434        }
435
436        Ok(NodeTest::Kind(kind_test))
437    }
438
439    /// <https://www.w3.org/TR/1999/REC-xpath-19991116/#predicates>
440    fn parse_predicates(&mut self, cx: &mut N::Context) -> Result<PredicateListExpression, Error> {
441        let mut predicates = vec![];
442        while self.advance_if_current_token_equals(Token::OpeningBracket) {
443            let expression = self.parse_expression(cx)?;
444            predicates.push(expression);
445            if !self.advance_if_current_token_equals(Token::ClosingBracket) {
446                return Err(Error::ExpectedClosingBracket);
447            }
448        }
449        Ok(PredicateListExpression { predicates })
450    }
451
452    fn parse_function_call(
453        &mut self,
454        cx: &mut N::Context,
455        function_name: &str,
456    ) -> Result<Expression, Error> {
457        struct ArgumentIterator<'a, 'b, N>
458        where
459            N: NamespaceResolver,
460        {
461            parser: &'b mut Parser<'a, N>,
462            done: bool,
463        }
464
465        impl<'a, 'b, N> ArgumentIterator<'a, 'b, N>
466        where
467            N: NamespaceResolver,
468        {
469            fn maybe_next(&mut self, cx: &mut N::Context) -> Result<Option<Expression>, Error> {
470                if self.done {
471                    return Ok(None);
472                }
473                let expression = self.parser.parse_expression(cx)?;
474                if self
475                    .parser
476                    .advance_if_current_token_equals(Token::ClosingParenthesis)
477                {
478                    self.done = true;
479                } else if !self.parser.advance_if_current_token_equals(Token::Comma) {
480                    log::debug!("{:?}", self.parser.peek(0));
481                    return Err(Error::ExpectedSeperatorBetweenFunctionArguments);
482                }
483
484                Ok(Some(expression))
485            }
486
487            fn next(&mut self, cx: &mut N::Context) -> Result<Expression, Error> {
488                self.maybe_next(cx)
489                    .and_then(|maybe_argument| maybe_argument.ok_or(Error::TooFewFunctionArguments))
490            }
491        }
492
493        let mut arguments = ArgumentIterator {
494            done: self.advance_if_current_token_equals(Token::ClosingParenthesis),
495            parser: self,
496        };
497
498        let core_fn = match function_name {
499            // Node Set Functions
500            "last" => CoreFunction::Last,
501            "position" => CoreFunction::Position,
502            "count" => CoreFunction::Count(Box::new(arguments.next(cx)?)),
503            "id" => CoreFunction::Id(Box::new(arguments.next(cx)?)),
504            "local-name" => CoreFunction::LocalName(arguments.maybe_next(cx)?.map(Box::new)),
505            "namespace-uri" => CoreFunction::NamespaceUri(arguments.maybe_next(cx)?.map(Box::new)),
506            "name" => CoreFunction::Name(arguments.maybe_next(cx)?.map(Box::new)),
507
508            // String Functions
509            "string" => CoreFunction::String(arguments.maybe_next(cx)?.map(Box::new)),
510            "concat" => {
511                let mut args = vec![];
512                while let Some(argument) = arguments.maybe_next(cx)? {
513                    args.push(argument);
514                }
515                CoreFunction::Concat(args)
516            },
517            "starts-with" => CoreFunction::StartsWith(
518                Box::new(arguments.next(cx)?),
519                Box::new(arguments.next(cx)?),
520            ),
521            "contains" => {
522                CoreFunction::Contains(Box::new(arguments.next(cx)?), Box::new(arguments.next(cx)?))
523            },
524            "substring-before" => CoreFunction::SubstringBefore(
525                Box::new(arguments.next(cx)?),
526                Box::new(arguments.next(cx)?),
527            ),
528            "substring-after" => CoreFunction::SubstringAfter(
529                Box::new(arguments.next(cx)?),
530                Box::new(arguments.next(cx)?),
531            ),
532            "substring" => CoreFunction::Substring(
533                Box::new(arguments.next(cx)?),
534                Box::new(arguments.next(cx)?),
535                arguments.maybe_next(cx)?.map(Box::new),
536            ),
537            "string-length" => CoreFunction::StringLength(arguments.maybe_next(cx)?.map(Box::new)),
538            "normalize-space" => {
539                CoreFunction::NormalizeSpace(arguments.maybe_next(cx)?.map(Box::new))
540            },
541            "translate" => CoreFunction::Translate(
542                Box::new(arguments.next(cx)?),
543                Box::new(arguments.next(cx)?),
544                Box::new(arguments.next(cx)?),
545            ),
546
547            // Number Functions
548            "number" => CoreFunction::Number(arguments.maybe_next(cx)?.map(Box::new)),
549            "sum" => CoreFunction::Sum(Box::new(arguments.next(cx)?)),
550            "floor" => CoreFunction::Floor(Box::new(arguments.next(cx)?)),
551            "ceiling" => CoreFunction::Ceiling(Box::new(arguments.next(cx)?)),
552            "round" => CoreFunction::Round(Box::new(arguments.next(cx)?)),
553
554            // Boolean Functions
555            "boolean" => CoreFunction::Boolean(Box::new(arguments.next(cx)?)),
556            "not" => CoreFunction::Not(Box::new(arguments.next(cx)?)),
557            "true" => CoreFunction::True,
558            "false" => CoreFunction::False,
559            "lang" => CoreFunction::Lang(Box::new(arguments.next(cx)?)),
560
561            // Unknown function
562            _ => return Err(Error::UnknownFunction),
563        };
564
565        // Ensure that there are no more arguments left
566        if !arguments.done {
567            return Err(Error::TooManyFunctionArguments);
568        }
569
570        Ok(Expression::Function(core_fn))
571    }
572}
573
574fn create_binary_expression(
575    lhs: Box<Expression>,
576    operator: OperatorToken,
577    rhs: Box<Expression>,
578) -> Expression {
579    let binary_operator = match operator {
580        OperatorToken::And => BinaryOperator::And,
581        OperatorToken::Or => BinaryOperator::Or,
582        OperatorToken::Multiply => BinaryOperator::Multiply,
583        OperatorToken::Divide => BinaryOperator::Divide,
584        OperatorToken::Modulo => BinaryOperator::Modulo,
585        OperatorToken::Add => BinaryOperator::Add,
586        OperatorToken::Subtract => BinaryOperator::Subtract,
587        OperatorToken::Equal => BinaryOperator::Equal,
588        OperatorToken::NotEqual => BinaryOperator::NotEqual,
589        OperatorToken::GreaterThan => BinaryOperator::GreaterThan,
590        OperatorToken::GreaterThanOrEqual => BinaryOperator::GreaterThanOrEqual,
591        OperatorToken::LessThan => BinaryOperator::LessThan,
592        OperatorToken::LessThanOrEqual => BinaryOperator::LessThanOrEqual,
593    };
594
595    Expression::Binary(lhs, binary_operator, rhs)
596}
597
598impl<'a> From<LiteralToken<'a>> for Literal {
599    fn from(value: LiteralToken<'a>) -> Self {
600        match value {
601            LiteralToken::Integer(integer) => Self::Integer(integer),
602            LiteralToken::Decimal(float) => Self::Decimal(float),
603            LiteralToken::String(string) => Self::String(string.to_owned()),
604        }
605    }
606}
607
608// Test functions to verify the parsers:
609#[cfg(test)]
610mod tests {
611    use markup5ever::{LocalName, QualName, local_name, namespace_prefix, ns};
612
613    use super::*;
614    use crate::NamespaceResolver;
615
616    #[derive(Clone)]
617    struct DummyNamespaceResolver;
618
619    impl NamespaceResolver for DummyNamespaceResolver {
620        type Context = ();
621
622        fn resolve_namespace_prefix(&self, _: &mut (), _: &str) -> Option<String> {
623            Some("http://www.w3.org/1999/xhtml".to_owned())
624        }
625    }
626
627    #[test]
628    fn test_filter_expr() {
629        let cases = vec![
630            (
631                "processing-instruction('test')[2]",
632                Expression::LocationStep(LocationStepExpression {
633                    axis: Axis::Child,
634                    node_test: NodeTest::Kind(KindTest::PI(Some("test".to_string()))),
635                    predicate_list: PredicateListExpression {
636                        predicates: vec![Expression::Literal(Literal::Integer(2))],
637                    },
638                }),
639            ),
640            (
641                "concat('hello', ' ', 'world')",
642                Expression::Function(CoreFunction::Concat(vec![
643                    Expression::Literal(Literal::String("hello".to_string())),
644                    Expression::Literal(Literal::String(" ".to_string())),
645                    Expression::Literal(Literal::String("world".to_string())),
646                ])),
647            ),
648        ];
649
650        for (input, expected) in cases {
651            match parse(&mut (), input, Some(DummyNamespaceResolver), true) {
652                Ok(result) => {
653                    assert_eq!(result, expected, "{:?} was parsed incorrectly", input);
654                },
655                Err(e) => panic!("Failed to parse '{}': {:?}", input, e),
656            }
657        }
658    }
659
660    #[test]
661    fn test_complex_paths() {
662        let cases = vec![
663            (
664                "//*[contains(@class, 'test')]",
665                Expression::Path(PathExpression {
666                    is_absolute: true,
667                    has_implicit_descendant_or_self_step: true,
668                    steps: vec![Expression::LocationStep(LocationStepExpression {
669                        axis: Axis::Child,
670                        node_test: NodeTest::Wildcard,
671                        predicate_list: PredicateListExpression {
672                            predicates: vec![Expression::Function(CoreFunction::Contains(
673                                Box::new(Expression::LocationStep(LocationStepExpression {
674                                    axis: Axis::Attribute,
675                                    node_test: NodeTest::Name(QualName {
676                                        prefix: None,
677                                        ns: ns!(),
678                                        local: local_name!("class"),
679                                    }),
680                                    predicate_list: PredicateListExpression { predicates: vec![] },
681                                })),
682                                Box::new(Expression::Literal(Literal::String("test".to_owned()))),
683                            ))],
684                        },
685                    })],
686                }),
687            ),
688            (
689                "//div[position() > 1]/*[last()]",
690                Expression::Path(PathExpression {
691                    is_absolute: true,
692                    has_implicit_descendant_or_self_step: true,
693                    steps: vec![
694                        Expression::LocationStep(LocationStepExpression {
695                            axis: Axis::Child,
696                            node_test: NodeTest::Name(QualName {
697                                prefix: None,
698                                ns: ns!(),
699                                local: local_name!("div"),
700                            }),
701                            predicate_list: PredicateListExpression {
702                                predicates: vec![Expression::Binary(
703                                    Box::new(Expression::Function(CoreFunction::Position)),
704                                    BinaryOperator::GreaterThan,
705                                    Box::new(Expression::Literal(Literal::Integer(1))),
706                                )],
707                            },
708                        }),
709                        Expression::LocationStep(LocationStepExpression {
710                            axis: Axis::Child,
711                            node_test: NodeTest::Wildcard,
712                            predicate_list: PredicateListExpression {
713                                predicates: vec![Expression::Function(CoreFunction::Last)],
714                            },
715                        }),
716                    ],
717                }),
718            ),
719            (
720                "//mu[@xml:id=\"id1\"]//rho[@title][@xml:lang=\"en-GB\"]",
721                Expression::Path(PathExpression {
722                    is_absolute: true,
723                    has_implicit_descendant_or_self_step: true,
724                    steps: vec![
725                        Expression::LocationStep(LocationStepExpression {
726                            axis: Axis::Child,
727                            node_test: NodeTest::Name(QualName {
728                                prefix: None,
729                                ns: ns!(),
730                                local: LocalName::from("mu"),
731                            }),
732                            predicate_list: PredicateListExpression {
733                                predicates: vec![Expression::Binary(
734                                    Box::new(Expression::LocationStep(LocationStepExpression {
735                                        axis: Axis::Attribute,
736                                        node_test: NodeTest::Name(QualName {
737                                            prefix: Some(namespace_prefix!("xml")),
738                                            ns: ns!(html),
739                                            local: local_name!("id"),
740                                        }),
741                                        predicate_list: PredicateListExpression {
742                                            predicates: vec![],
743                                        },
744                                    })),
745                                    BinaryOperator::Equal,
746                                    Box::new(Expression::Literal(Literal::String(
747                                        "id1".to_owned(),
748                                    ))),
749                                )],
750                            },
751                        }),
752                        Expression::LocationStep(LocationStepExpression {
753                            axis: Axis::DescendantOrSelf,
754                            node_test: NodeTest::Kind(KindTest::Node),
755                            predicate_list: PredicateListExpression { predicates: vec![] },
756                        }),
757                        Expression::LocationStep(LocationStepExpression {
758                            axis: Axis::Child,
759                            node_test: NodeTest::Name(QualName {
760                                prefix: None,
761                                ns: ns!(),
762                                local: LocalName::from("rho"),
763                            }),
764                            predicate_list: PredicateListExpression {
765                                predicates: vec![
766                                    Expression::LocationStep(LocationStepExpression {
767                                        axis: Axis::Attribute,
768                                        node_test: NodeTest::Name(QualName {
769                                            prefix: None,
770                                            ns: ns!(),
771                                            local: local_name!("title"),
772                                        }),
773                                        predicate_list: PredicateListExpression {
774                                            predicates: vec![],
775                                        },
776                                    }),
777                                    Expression::Binary(
778                                        Box::new(Expression::LocationStep(
779                                            LocationStepExpression {
780                                                axis: Axis::Attribute,
781                                                node_test: NodeTest::Name(QualName {
782                                                    prefix: Some(namespace_prefix!("xml")),
783                                                    ns: ns!(html),
784                                                    local: local_name!("lang"),
785                                                }),
786                                                predicate_list: PredicateListExpression {
787                                                    predicates: vec![],
788                                                },
789                                            },
790                                        )),
791                                        BinaryOperator::Equal,
792                                        Box::new(Expression::Literal(Literal::String(
793                                            "en-GB".to_owned(),
794                                        ))),
795                                    ),
796                                ],
797                            },
798                        }),
799                    ],
800                }),
801            ),
802        ];
803
804        for (input, expected) in cases {
805            match parse(&mut (), input, Some(DummyNamespaceResolver), true) {
806                Ok(result) => {
807                    assert_eq!(result, expected, "{:?} was parsed incorrectly", input);
808                },
809                Err(e) => panic!("Failed to parse '{}': {:?}", input, e),
810            }
811        }
812    }
813
814    #[test]
815    fn parse_expression_in_parenthesis() {
816        let test_case = "(./span)";
817        let expected = Expression::Path(PathExpression {
818            is_absolute: false,
819            has_implicit_descendant_or_self_step: false,
820            steps: vec![
821                Expression::LocationStep(LocationStepExpression {
822                    axis: Axis::Self_,
823                    node_test: NodeTest::Kind(KindTest::Node),
824                    predicate_list: PredicateListExpression { predicates: vec![] },
825                }),
826                Expression::LocationStep(LocationStepExpression {
827                    axis: Axis::Child,
828                    node_test: NodeTest::Name(QualName {
829                        prefix: None,
830                        ns: ns!(),
831                        local: local_name!("span"),
832                    }),
833                    predicate_list: PredicateListExpression { predicates: vec![] },
834                }),
835            ],
836        });
837        match parse(&mut (), test_case, Some(DummyNamespaceResolver), true) {
838            Ok(result) => {
839                assert_eq!(result, expected, "{:?} was parsed incorrectly", test_case);
840            },
841            Err(e) => panic!("Failed to parse '{}': {:?}", test_case, e),
842        }
843    }
844}