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 nom::branch::alt;
6use nom::bytes::complete::{tag, take_while1};
7use nom::character::complete::{char, digit1, multispace0};
8use nom::combinator::{map, opt, recognize, value};
9use nom::error::{Error as NomError, ErrorKind as NomErrorKind, ParseError as NomParseError};
10use nom::multi::{many0, separated_list0};
11use nom::sequence::{delimited, pair, preceded};
12use nom::{AsChar, Finish, IResult, Input, Parser};
13
14use crate::ast::{
15    Axis, BinaryOperator, CoreFunction, Expression, FilterExpression, KindTest, Literal,
16    LocationStepExpression, NodeTest, PathExpression, PredicateListExpression, QName,
17};
18use crate::{is_valid_continuation, is_valid_start};
19
20pub(crate) fn parse(input: &str) -> Result<Expression, OwnedParserError> {
21    let (_, ast) = expr(input).finish().map_err(OwnedParserError::from)?;
22    Ok(ast)
23}
24
25#[derive(Clone, Debug, PartialEq)]
26pub struct OwnedParserError {
27    pub input: String,
28    pub kind: NomErrorKind,
29}
30
31impl<'a> From<NomError<&'a str>> for OwnedParserError {
32    fn from(err: NomError<&'a str>) -> Self {
33        OwnedParserError {
34            input: err.input.to_string(),
35            kind: err.code,
36        }
37    }
38}
39
40impl std::fmt::Display for OwnedParserError {
41    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
42        write!(f, "error {:?} at: {}", self.kind, self.input)
43    }
44}
45
46impl std::error::Error for OwnedParserError {}
47
48/// Top-level parser
49fn expr(input: &str) -> IResult<&str, Expression> {
50    expr_single(input)
51}
52
53/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Expr>
54fn expr_single(input: &str) -> IResult<&str, Expression> {
55    or_expr(input)
56}
57
58/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-OrExpr>
59fn or_expr(input: &str) -> IResult<&str, Expression> {
60    let (input, first) = and_expr(input)?;
61    let (input, rest) = many0(preceded(ws(tag("or")), and_expr)).parse(input)?;
62
63    Ok((
64        input,
65        rest.into_iter().fold(first, |acc, expr| {
66            Expression::Binary(Box::new(acc), BinaryOperator::Or, Box::new(expr))
67        }),
68    ))
69}
70
71/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AndExpr>
72fn and_expr(input: &str) -> IResult<&str, Expression> {
73    let (input, first) = equality_expr(input)?;
74    let (input, rest) = many0(preceded(ws(tag("and")), equality_expr)).parse(input)?;
75
76    Ok((
77        input,
78        rest.into_iter().fold(first, |acc, expr| {
79            Expression::Binary(Box::new(acc), BinaryOperator::And, Box::new(expr))
80        }),
81    ))
82}
83
84/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-EqualityExpr>
85fn equality_expr(input: &str) -> IResult<&str, Expression> {
86    let (input, first) = relational_expr(input)?;
87    let (input, rest) = many0((
88        ws(alt((
89            map(tag("="), |_| BinaryOperator::Equal),
90            map(tag("!="), |_| BinaryOperator::NotEqual),
91        ))),
92        relational_expr,
93    ))
94    .parse(input)?;
95
96    Ok((
97        input,
98        rest.into_iter().fold(first, |acc, (op, expr)| {
99            Expression::Binary(Box::new(acc), op, Box::new(expr))
100        }),
101    ))
102}
103
104/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-RelationalExpr>
105fn relational_expr(input: &str) -> IResult<&str, Expression> {
106    let (input, first) = additive_expr(input)?;
107    let (input, rest) = many0((
108        ws(alt((
109            map(tag("<="), |_| BinaryOperator::LessThanOrEqual),
110            map(tag(">="), |_| BinaryOperator::GreaterThanOrEqual),
111            map(tag("<"), |_| BinaryOperator::LessThan),
112            map(tag(">"), |_| BinaryOperator::GreaterThan),
113        ))),
114        additive_expr,
115    ))
116    .parse(input)?;
117
118    Ok((
119        input,
120        rest.into_iter().fold(first, |acc, (op, expr)| {
121            Expression::Binary(Box::new(acc), op, Box::new(expr))
122        }),
123    ))
124}
125
126/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-AdditiveExpr>
127fn additive_expr(input: &str) -> IResult<&str, Expression> {
128    let (input, first) = multiplicative_expr(input)?;
129    let (input, rest) = many0((
130        ws(alt((
131            map(tag("+"), |_| BinaryOperator::Add),
132            map(tag("-"), |_| BinaryOperator::Subtract),
133        ))),
134        multiplicative_expr,
135    ))
136    .parse(input)?;
137
138    Ok((
139        input,
140        rest.into_iter().fold(first, |acc, (op, expr)| {
141            Expression::Binary(Box::new(acc), op, Box::new(expr))
142        }),
143    ))
144}
145
146/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-MultiplicativeExpr>
147fn multiplicative_expr(input: &str) -> IResult<&str, Expression> {
148    let (input, first) = unary_expr(input)?;
149    let (input, rest) = many0((
150        ws(alt((
151            map(tag("*"), |_| BinaryOperator::Multiply),
152            map(tag("div"), |_| BinaryOperator::Divide),
153            map(tag("mod"), |_| BinaryOperator::Modulo),
154        ))),
155        unary_expr,
156    ))
157    .parse(input)?;
158
159    Ok((
160        input,
161        rest.into_iter().fold(first, |acc, (op, expr)| {
162            Expression::Binary(Box::new(acc), op, Box::new(expr))
163        }),
164    ))
165}
166
167/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-UnaryExpr>
168fn unary_expr(input: &str) -> IResult<&str, Expression> {
169    let (input, minus_count) = many0(ws(char('-'))).parse(input)?;
170    let (input, expr) = union_expr(input)?;
171
172    Ok((
173        input,
174        (0..minus_count.len()).fold(expr, |acc, _| Expression::Negate(Box::new(acc))),
175    ))
176}
177
178/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-UnionExpr>
179fn union_expr(input: &str) -> IResult<&str, Expression> {
180    let (input, first) = path_expr(input)?;
181    let (input, rest) = many0(preceded(ws(char('|')), path_expr)).parse(input)?;
182
183    Ok((
184        input,
185        rest.into_iter().fold(first, |acc, expr| {
186            Expression::Binary(Box::new(acc), BinaryOperator::Union, Box::new(expr))
187        }),
188    ))
189}
190
191/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-PathExpr>
192fn path_expr(input: &str) -> IResult<&str, Expression> {
193    ws(alt((
194        // "//" RelativePathExpr
195        map(
196            pair(tag("//"), move |i| relative_path_expr(true, i)),
197            |(_, relative_path)| {
198                Expression::Path(PathExpression {
199                    is_absolute: true,
200                    has_implicit_descendant_or_self_step: true,
201                    steps: relative_path.steps,
202                })
203            },
204        ),
205        // "/" RelativePathExpr?
206        map(
207            pair(char('/'), opt(move |i| relative_path_expr(false, i))),
208            |(_, relative_path)| {
209                Expression::Path(PathExpression {
210                    is_absolute: true,
211                    has_implicit_descendant_or_self_step: false,
212                    steps: relative_path.map(|path| path.steps).unwrap_or_default(),
213                })
214            },
215        ),
216        // RelativePathExpr
217        map(
218            move |i| relative_path_expr(false, i),
219            |mut relative_path_expression| {
220                if relative_path_expression.steps.len() == 1 {
221                    relative_path_expression.steps.pop().unwrap()
222                } else {
223                    Expression::Path(relative_path_expression)
224                }
225            },
226        ),
227    )))
228    .parse(input)
229}
230
231fn relative_path_expr(is_descendant: bool, input: &str) -> IResult<&str, PathExpression> {
232    let (input, first) = step_expr(is_descendant, input)?;
233    let (input, steps) = many0(pair(
234        ws(alt((value(true, tag("//")), value(false, char('/'))))),
235        ws(move |i| step_expr(false, i)),
236    ))
237    .parse(input)?;
238
239    let mut all_steps = vec![first];
240    for (implicit_descendant_or_self, step) in steps {
241        if implicit_descendant_or_self {
242            // Insert an implicit descendant-or-self::node() step
243            all_steps.push(Expression::LocationStep(LocationStepExpression {
244                axis: Axis::DescendantOrSelf,
245                node_test: NodeTest::Kind(KindTest::Node),
246                predicate_list: PredicateListExpression { predicates: vec![] },
247            }));
248        }
249        all_steps.push(step);
250    }
251
252    Ok((
253        input,
254        PathExpression {
255            is_absolute: false,
256            has_implicit_descendant_or_self_step: false,
257            steps: all_steps,
258        },
259    ))
260}
261
262fn step_expr(is_descendant: bool, input: &str) -> IResult<&str, Expression> {
263    alt((filter_expr, |i| axis_step(is_descendant, i))).parse(input)
264}
265
266fn axis_step(is_descendant: bool, input: &str) -> IResult<&str, Expression> {
267    let (input, (step, predicates)) = pair(
268        alt((move |i| forward_step(is_descendant, i), reverse_step)),
269        predicate_list,
270    )
271    .parse(input)?;
272
273    let (axis, node_test) = step;
274    Ok((
275        input,
276        Expression::LocationStep(LocationStepExpression {
277            axis,
278            node_test,
279            predicate_list: predicates,
280        }),
281    ))
282}
283
284fn forward_step(is_descendant: bool, input: &str) -> IResult<&str, (Axis, NodeTest)> {
285    alt((pair(forward_axis, node_test), move |i| {
286        abbrev_forward_step(is_descendant, i)
287    }))
288    .parse(input)
289}
290
291fn forward_axis(input: &str) -> IResult<&str, Axis> {
292    let (input, axis) = alt((
293        value(Axis::Child, tag("child::")),
294        value(Axis::Descendant, tag("descendant::")),
295        value(Axis::Attribute, tag("attribute::")),
296        value(Axis::Self_, tag("self::")),
297        value(Axis::DescendantOrSelf, tag("descendant-or-self::")),
298        value(Axis::FollowingSibling, tag("following-sibling::")),
299        value(Axis::Following, tag("following::")),
300        value(Axis::Namespace, tag("namespace::")),
301    ))
302    .parse(input)?;
303
304    Ok((input, axis))
305}
306
307// <https://www.w3.org/TR/1999/REC-xpath-19991116/#path-abbrev>
308fn abbrev_forward_step(is_descendant: bool, input: &str) -> IResult<&str, (Axis, NodeTest)> {
309    let (input, attr) = opt(char('@')).parse(input)?;
310    let (input, test) = node_test(input)?;
311
312    let axis = if attr.is_some() {
313        Axis::Attribute
314    } else if is_descendant {
315        Axis::DescendantOrSelf
316    } else {
317        Axis::Child
318    };
319    Ok((input, (axis, test)))
320}
321
322fn reverse_step(input: &str) -> IResult<&str, (Axis, NodeTest)> {
323    alt((
324        // ReverseAxis NodeTest
325        pair(reverse_axis, node_test),
326        // AbbrevReverseStep
327        abbrev_reverse_step,
328    ))
329    .parse(input)
330}
331
332fn reverse_axis(input: &str) -> IResult<&str, Axis> {
333    alt((
334        value(Axis::Parent, tag("parent::")),
335        value(Axis::Ancestor, tag("ancestor::")),
336        value(Axis::PrecedingSibling, tag("preceding-sibling::")),
337        value(Axis::Preceding, tag("preceding::")),
338        value(Axis::AncestorOrSelf, tag("ancestor-or-self::")),
339    ))
340    .parse(input)
341}
342
343fn abbrev_reverse_step(input: &str) -> IResult<&str, (Axis, NodeTest)> {
344    map(tag(".."), |_| {
345        (Axis::Parent, NodeTest::Kind(KindTest::Node))
346    })
347    .parse(input)
348}
349
350/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NodeTest>
351fn node_test(input: &str) -> IResult<&str, NodeTest> {
352    alt((
353        map(kind_test, NodeTest::Kind),
354        map(name_test, |name| match name {
355            NameTest::Wildcard => NodeTest::Wildcard,
356            NameTest::QName(qname) => NodeTest::Name(qname),
357        }),
358    ))
359    .parse(input)
360}
361
362#[derive(Clone, Debug, PartialEq)]
363enum NameTest {
364    QName(QName),
365    Wildcard,
366}
367
368/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-NameTest>
369fn name_test(input: &str) -> IResult<&str, NameTest> {
370    alt((
371        // NCName ":" "*"
372        map((ncname, char(':'), char('*')), |(prefix, _, _)| {
373            NameTest::QName(QName {
374                prefix: Some(prefix.to_string()),
375                local_part: "*".to_string(),
376            })
377        }),
378        // "*"
379        value(NameTest::Wildcard, char('*')),
380        // QName
381        map(qname, NameTest::QName),
382    ))
383    .parse(input)
384}
385
386/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-FilterExpr>
387fn filter_expr(input: &str) -> IResult<&str, Expression> {
388    let (input, primary) = primary_expr(input)?;
389    let (input, predicate_list) = predicate_list(input)?;
390
391    if predicate_list.predicates.is_empty() {
392        return Ok((input, primary));
393    }
394
395    Ok((
396        input,
397        Expression::Filter(FilterExpression {
398            expression: Box::new(primary),
399            predicates: predicate_list,
400        }),
401    ))
402}
403
404fn predicate_list(input: &str) -> IResult<&str, PredicateListExpression> {
405    let (input, predicates) = many0(predicate).parse(input)?;
406
407    Ok((input, PredicateListExpression { predicates }))
408}
409
410/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Predicate>
411fn predicate(input: &str) -> IResult<&str, Expression> {
412    let (input, expr) = delimited(ws(char('[')), expr, ws(char(']'))).parse(input)?;
413    Ok((input, expr))
414}
415
416/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-PrimaryExpr>
417fn primary_expr(input: &str) -> IResult<&str, Expression> {
418    alt((
419        literal,
420        var_ref,
421        parenthesized_expr,
422        context_item_expr,
423        function_call,
424    ))
425    .parse(input)
426}
427
428/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Literal>
429fn literal(input: &str) -> IResult<&str, Expression> {
430    map(alt((numeric_literal, string_literal)), |lit| {
431        Expression::Literal(lit)
432    })
433    .parse(input)
434}
435
436/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-Number>
437fn numeric_literal(input: &str) -> IResult<&str, Literal> {
438    alt((decimal_literal, integer_literal)).parse(input)
439}
440
441/// <https://www.w3.org/TR/1999/REC-xpath-19991116/#NT-VariableReference>
442fn var_ref(input: &str) -> IResult<&str, Expression> {
443    let (input, _) = char('$').parse(input)?;
444    let (input, name) = qname(input)?;
445    Ok((input, Expression::Variable(name)))
446}
447
448fn parenthesized_expr(input: &str) -> IResult<&str, Expression> {
449    delimited(ws(char('(')), expr, ws(char(')'))).parse(input)
450}
451
452fn context_item_expr(input: &str) -> IResult<&str, Expression> {
453    map(char('.'), |_| Expression::ContextItem).parse(input)
454}
455
456fn function_call(input: &str) -> IResult<&str, Expression> {
457    let (input, name) = qname(input)?;
458    let (input, args) = delimited(
459        ws(char('(')),
460        separated_list0(ws(char(',')), expr_single),
461        ws(char(')')),
462    )
463    .parse(input)?;
464
465    // Helper to create error
466    let arity_error = || nom::Err::Error(NomError::new(input, NomErrorKind::Verify));
467
468    let core_fn = match name.local_part.as_str() {
469        // Node Set Functions
470        "last" => CoreFunction::Last,
471        "position" => CoreFunction::Position,
472        "count" => CoreFunction::Count(Box::new(args.into_iter().next().ok_or_else(arity_error)?)),
473        "id" => CoreFunction::Id(Box::new(args.into_iter().next().ok_or_else(arity_error)?)),
474        "local-name" => CoreFunction::LocalName(args.into_iter().next().map(Box::new)),
475        "namespace-uri" => CoreFunction::NamespaceUri(args.into_iter().next().map(Box::new)),
476        "name" => CoreFunction::Name(args.into_iter().next().map(Box::new)),
477
478        // String Functions
479        "string" => CoreFunction::String(args.into_iter().next().map(Box::new)),
480        "concat" => CoreFunction::Concat(args.into_iter().collect()),
481        "starts-with" => {
482            let mut args = args.into_iter();
483            CoreFunction::StartsWith(
484                Box::new(args.next().ok_or_else(arity_error)?),
485                Box::new(args.next().ok_or_else(arity_error)?),
486            )
487        },
488        "contains" => {
489            let mut args = args.into_iter();
490            CoreFunction::Contains(
491                Box::new(args.next().ok_or_else(arity_error)?),
492                Box::new(args.next().ok_or_else(arity_error)?),
493            )
494        },
495        "substring-before" => {
496            let mut args = args.into_iter();
497            CoreFunction::SubstringBefore(
498                Box::new(args.next().ok_or_else(arity_error)?),
499                Box::new(args.next().ok_or_else(arity_error)?),
500            )
501        },
502        "substring-after" => {
503            let mut args = args.into_iter();
504            CoreFunction::SubstringAfter(
505                Box::new(args.next().ok_or_else(arity_error)?),
506                Box::new(args.next().ok_or_else(arity_error)?),
507            )
508        },
509        "substring" => {
510            let mut args = args.into_iter();
511            CoreFunction::Substring(
512                Box::new(args.next().ok_or_else(arity_error)?),
513                Box::new(args.next().ok_or_else(arity_error)?),
514                args.next().map(Box::new),
515            )
516        },
517        "string-length" => CoreFunction::StringLength(args.into_iter().next().map(Box::new)),
518        "normalize-space" => CoreFunction::NormalizeSpace(args.into_iter().next().map(Box::new)),
519        "translate" => {
520            let mut args = args.into_iter();
521            CoreFunction::Translate(
522                Box::new(args.next().ok_or_else(arity_error)?),
523                Box::new(args.next().ok_or_else(arity_error)?),
524                Box::new(args.next().ok_or_else(arity_error)?),
525            )
526        },
527
528        // Number Functions
529        "number" => CoreFunction::Number(args.into_iter().next().map(Box::new)),
530        "sum" => CoreFunction::Sum(Box::new(args.into_iter().next().ok_or_else(arity_error)?)),
531        "floor" => CoreFunction::Floor(Box::new(args.into_iter().next().ok_or_else(arity_error)?)),
532        "ceiling" => {
533            CoreFunction::Ceiling(Box::new(args.into_iter().next().ok_or_else(arity_error)?))
534        },
535        "round" => CoreFunction::Round(Box::new(args.into_iter().next().ok_or_else(arity_error)?)),
536
537        // Boolean Functions
538        "boolean" => {
539            CoreFunction::Boolean(Box::new(args.into_iter().next().ok_or_else(arity_error)?))
540        },
541        "not" => CoreFunction::Not(Box::new(args.into_iter().next().ok_or_else(arity_error)?)),
542        "true" => CoreFunction::True,
543        "false" => CoreFunction::False,
544        "lang" => CoreFunction::Lang(Box::new(args.into_iter().next().ok_or_else(arity_error)?)),
545
546        // Unknown function
547        _ => return Err(nom::Err::Error(NomError::new(input, NomErrorKind::Verify))),
548    };
549
550    Ok((input, Expression::Function(core_fn)))
551}
552
553fn kind_test(input: &str) -> IResult<&str, KindTest> {
554    alt((pi_test, comment_test, text_test, any_kind_test)).parse(input)
555}
556
557fn any_kind_test(input: &str) -> IResult<&str, KindTest> {
558    map((tag("node"), ws(char('(')), ws(char(')'))), |_| {
559        KindTest::Node
560    })
561    .parse(input)
562}
563
564fn text_test(input: &str) -> IResult<&str, KindTest> {
565    map((tag("text"), ws(char('(')), ws(char(')'))), |_| {
566        KindTest::Text
567    })
568    .parse(input)
569}
570
571fn comment_test(input: &str) -> IResult<&str, KindTest> {
572    map((tag("comment"), ws(char('(')), ws(char(')'))), |_| {
573        KindTest::Comment
574    })
575    .parse(input)
576}
577
578fn pi_test(input: &str) -> IResult<&str, KindTest> {
579    map(
580        (
581            tag("processing-instruction"),
582            ws(char('(')),
583            opt(ws(string_literal)),
584            ws(char(')')),
585        ),
586        |(_, _, literal, _)| {
587            KindTest::PI(literal.map(|l| match l {
588                Literal::String(s) => s,
589                _ => unreachable!(),
590            }))
591        },
592    )
593    .parse(input)
594}
595
596fn ws<'a, F, O, E>(inner: F) -> impl Parser<&'a str, Output = O, Error = E>
597where
598    E: NomParseError<&'a str>,
599    F: Parser<&'a str, Output = O, Error = E>,
600{
601    delimited(multispace0, inner, multispace0)
602}
603
604fn integer_literal(input: &str) -> IResult<&str, Literal> {
605    map(recognize((opt(char('-')), digit1)), |s: &str| {
606        Literal::Integer(s.parse().unwrap())
607    })
608    .parse(input)
609}
610
611fn decimal_literal(input: &str) -> IResult<&str, Literal> {
612    map(
613        recognize((opt(char('-')), opt(digit1), char('.'), digit1)),
614        |s: &str| Literal::Decimal(s.parse().unwrap()),
615    )
616    .parse(input)
617}
618
619fn string_literal(input: &str) -> IResult<&str, Literal> {
620    alt((
621        delimited(
622            char('"'),
623            map(take_while1(|c| c != '"'), |s: &str| {
624                Literal::String(s.to_string())
625            }),
626            char('"'),
627        ),
628        delimited(
629            char('\''),
630            map(take_while1(|c| c != '\''), |s: &str| {
631                Literal::String(s.to_string())
632            }),
633            char('\''),
634        ),
635    ))
636    .parse(input)
637}
638
639/// <https://www.w3.org/TR/REC-xml-names/#NT-QName>
640fn qname(input: &str) -> IResult<&str, QName> {
641    let (input, prefix) = opt((ncname, char(':'))).parse(input)?;
642    let (input, local) = ncname(input)?;
643
644    Ok((
645        input,
646        QName {
647            prefix: prefix.map(|(p, _)| p.to_string()),
648            local_part: local.to_string(),
649        },
650    ))
651}
652
653/// <https://www.w3.org/TR/REC-xml-names/#NT-NCName>
654fn ncname(input: &str) -> IResult<&str, &str> {
655    fn name_start_character<T, E: NomParseError<T>>(input: T) -> IResult<T, T, E>
656    where
657        T: Input,
658        <T as Input>::Item: AsChar,
659    {
660        input.split_at_position1_complete(
661            |character| !is_valid_start(character.as_char()) || character.as_char() == ':',
662            NomErrorKind::OneOf,
663        )
664    }
665
666    fn name_character<T, E: NomParseError<T>>(input: T) -> IResult<T, T, E>
667    where
668        T: Input,
669        <T as Input>::Item: AsChar,
670    {
671        input.split_at_position1_complete(
672            |character| !is_valid_continuation(character.as_char()) || character.as_char() == ':',
673            NomErrorKind::OneOf,
674        )
675    }
676
677    recognize(pair(name_start_character, many0(name_character))).parse(input)
678}
679
680// Test functions to verify the parsers:
681#[cfg(test)]
682mod tests {
683    use super::*;
684
685    #[test]
686    fn test_node_tests() {
687        let cases = vec![
688            ("node()", NodeTest::Kind(KindTest::Node)),
689            ("text()", NodeTest::Kind(KindTest::Text)),
690            ("comment()", NodeTest::Kind(KindTest::Comment)),
691            (
692                "processing-instruction()",
693                NodeTest::Kind(KindTest::PI(None)),
694            ),
695            (
696                "processing-instruction('test')",
697                NodeTest::Kind(KindTest::PI(Some("test".to_string()))),
698            ),
699            ("*", NodeTest::Wildcard),
700            (
701                "prefix:*",
702                NodeTest::Name(QName {
703                    prefix: Some("prefix".to_string()),
704                    local_part: "*".to_string(),
705                }),
706            ),
707            (
708                "div",
709                NodeTest::Name(QName {
710                    prefix: None,
711                    local_part: "div".to_string(),
712                }),
713            ),
714            (
715                "ns:div",
716                NodeTest::Name(QName {
717                    prefix: Some("ns".to_string()),
718                    local_part: "div".to_string(),
719                }),
720            ),
721        ];
722
723        for (input, expected) in cases {
724            match node_test(input) {
725                Ok((remaining, result)) => {
726                    assert!(remaining.is_empty(), "Parser didn't consume all input");
727                    assert_eq!(result, expected, "{:?} was parsed incorrectly", input);
728                },
729                Err(e) => panic!("Failed to parse '{}': {:?}", input, e),
730            }
731        }
732    }
733
734    #[test]
735    fn test_filter_expr() {
736        let cases = vec![
737            (
738                "processing-instruction('test')[2]",
739                Expression::LocationStep(LocationStepExpression {
740                    axis: Axis::Child,
741                    node_test: NodeTest::Kind(KindTest::PI(Some("test".to_string()))),
742                    predicate_list: PredicateListExpression {
743                        predicates: vec![Expression::Literal(Literal::Integer(2))],
744                    },
745                }),
746            ),
747            (
748                "concat('hello', ' ', 'world')",
749                Expression::Function(CoreFunction::Concat(vec![
750                    Expression::Literal(Literal::String("hello".to_string())),
751                    Expression::Literal(Literal::String(" ".to_string())),
752                    Expression::Literal(Literal::String("world".to_string())),
753                ])),
754            ),
755        ];
756
757        for (input, expected) in cases {
758            match parse(input) {
759                Ok(result) => {
760                    assert_eq!(result, expected, "{:?} was parsed incorrectly", input);
761                },
762                Err(e) => panic!("Failed to parse '{}': {:?}", input, e),
763            }
764        }
765    }
766
767    #[test]
768    fn test_complex_paths() {
769        let cases = vec![
770            (
771                "//*[contains(@class, 'test')]",
772                Expression::Path(PathExpression {
773                    is_absolute: true,
774                    has_implicit_descendant_or_self_step: true,
775                    steps: vec![Expression::LocationStep(LocationStepExpression {
776                        axis: Axis::DescendantOrSelf,
777                        node_test: NodeTest::Wildcard,
778                        predicate_list: PredicateListExpression {
779                            predicates: vec![Expression::Function(CoreFunction::Contains(
780                                Box::new(Expression::LocationStep(LocationStepExpression {
781                                    axis: Axis::Attribute,
782                                    node_test: NodeTest::Name(QName {
783                                        prefix: None,
784                                        local_part: "class".to_owned(),
785                                    }),
786                                    predicate_list: PredicateListExpression { predicates: vec![] },
787                                })),
788                                Box::new(Expression::Literal(Literal::String("test".to_owned()))),
789                            ))],
790                        },
791                    })],
792                }),
793            ),
794            (
795                "//div[position() > 1]/*[last()]",
796                Expression::Path(PathExpression {
797                    is_absolute: true,
798                    has_implicit_descendant_or_self_step: true,
799                    steps: vec![
800                        Expression::LocationStep(LocationStepExpression {
801                            axis: Axis::DescendantOrSelf,
802                            node_test: NodeTest::Name(QName {
803                                prefix: None,
804                                local_part: "div".to_owned(),
805                            }),
806                            predicate_list: PredicateListExpression {
807                                predicates: vec![Expression::Binary(
808                                    Box::new(Expression::Function(CoreFunction::Position)),
809                                    BinaryOperator::GreaterThan,
810                                    Box::new(Expression::Literal(Literal::Integer(1))),
811                                )],
812                            },
813                        }),
814                        Expression::LocationStep(LocationStepExpression {
815                            axis: Axis::Child,
816                            node_test: NodeTest::Wildcard,
817                            predicate_list: PredicateListExpression {
818                                predicates: vec![Expression::Function(CoreFunction::Last)],
819                            },
820                        }),
821                    ],
822                }),
823            ),
824            (
825                "//mu[@xml:id=\"id1\"]//rho[@title][@xml:lang=\"en-GB\"]",
826                Expression::Path(PathExpression {
827                    is_absolute: true,
828                    has_implicit_descendant_or_self_step: true,
829                    steps: vec![
830                        Expression::LocationStep(LocationStepExpression {
831                            axis: Axis::DescendantOrSelf,
832                            node_test: NodeTest::Name(QName {
833                                prefix: None,
834                                local_part: "mu".to_owned(),
835                            }),
836                            predicate_list: PredicateListExpression {
837                                predicates: vec![Expression::Binary(
838                                    Box::new(Expression::LocationStep(LocationStepExpression {
839                                        axis: Axis::Attribute,
840                                        node_test: NodeTest::Name(QName {
841                                            prefix: Some("xml".to_owned()),
842                                            local_part: "id".to_owned(),
843                                        }),
844                                        predicate_list: PredicateListExpression {
845                                            predicates: vec![],
846                                        },
847                                    })),
848                                    BinaryOperator::Equal,
849                                    Box::new(Expression::Literal(Literal::String(
850                                        "id1".to_owned(),
851                                    ))),
852                                )],
853                            },
854                        }),
855                        Expression::LocationStep(LocationStepExpression {
856                            axis: Axis::DescendantOrSelf,
857                            node_test: NodeTest::Kind(KindTest::Node),
858                            predicate_list: PredicateListExpression { predicates: vec![] },
859                        }),
860                        Expression::LocationStep(LocationStepExpression {
861                            axis: Axis::Child,
862                            node_test: NodeTest::Name(QName {
863                                prefix: None,
864                                local_part: "rho".to_owned(),
865                            }),
866                            predicate_list: PredicateListExpression {
867                                predicates: vec![
868                                    Expression::LocationStep(LocationStepExpression {
869                                        axis: Axis::Attribute,
870                                        node_test: NodeTest::Name(QName {
871                                            prefix: None,
872                                            local_part: "title".to_owned(),
873                                        }),
874                                        predicate_list: PredicateListExpression {
875                                            predicates: vec![],
876                                        },
877                                    }),
878                                    Expression::Binary(
879                                        Box::new(Expression::LocationStep(
880                                            LocationStepExpression {
881                                                axis: Axis::Attribute,
882                                                node_test: NodeTest::Name(QName {
883                                                    prefix: Some("xml".to_owned()),
884                                                    local_part: "lang".to_owned(),
885                                                }),
886                                                predicate_list: PredicateListExpression {
887                                                    predicates: vec![],
888                                                },
889                                            },
890                                        )),
891                                        BinaryOperator::Equal,
892                                        Box::new(Expression::Literal(Literal::String(
893                                            "en-GB".to_owned(),
894                                        ))),
895                                    ),
896                                ],
897                            },
898                        }),
899                    ],
900                }),
901            ),
902        ];
903
904        for (input, expected) in cases {
905            match parse(input) {
906                Ok(result) => {
907                    assert_eq!(result, expected, "{:?} was parsed incorrectly", input);
908                },
909                Err(e) => panic!("Failed to parse '{}': {:?}", input, e),
910            }
911        }
912    }
913}