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