Skip to main content

xrust/
pattern.rs

1/*! # Support for XPath patterns.
2
3This module provides both a parser to compile a [Pattern], and an interpreter to determine if an item matches a compiled pattern.
4
5Patterns are defined in XSLT 3.0 5.5.2.
6
7A string can be compiled as [Pattern] by using the ```try_from``` associated function.
8
9```rust
10# use xrust::item::Node;
11use xrust::pattern::Pattern;
12
13# fn compile<N: Node>() {
14let p: Pattern<N> = Pattern::try_from("child::foobar")
15        .expect("unable to compile pattern");
16# ()
17# }
18```
19
20An [Item] can then be tested to see if it matches the [Pattern]. To do that, it is necessary to have a transformation [Context].
21
22```rust
23# use std::rc::Rc;
24# use xrust::ErrorKind;
25# use xrust::xdmerror::Error;
26# use xrust::item::{Item, NodeType};
27# use xrust::pattern::Pattern;
28# use xrust::transform::context::{Context, StaticContext, StaticContextBuilder};
29# use xrust::Node;
30# use xrust::trees::smite::RNode;
31# type F = Box<dyn FnMut(&str) -> Result<(), Error>>;
32let p = Pattern::try_from("/").expect("unable to compile pattern");
33let n = Item::Node(RNode::new_document());
34
35// Create a static context
36let mut static_context = StaticContextBuilder::new()
37    .message(|_| Ok(()))
38    .fetcher(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
39    .parser(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
40   .build();
41
42// This pattern matches the root node
43assert_eq!(p.matches(&Context::new(), &mut static_context, &n), true)
44```
45
46```rust
47# use std::rc::Rc;
48# use xrust::xdmerror::{Error, ErrorKind};
49# use xrust::item::{Item, NodeType};
50# use xrust::pattern::Pattern;
51# use xrust::transform::context::{Context, StaticContext, StaticContextBuilder};
52# use xrust::Node;
53# use xrust::trees::smite::RNode;
54# type F = Box<dyn FnMut(&str) -> Result<(), Error>>;
55let p = Pattern::try_from("child::foobar").expect("unable to compile pattern");
56let n = Item::Node(RNode::new_document());
57// Create a static context
58# let mut static_context = StaticContextBuilder::new()
59#    .message(|_| Ok(()))
60#    .fetcher(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
61#    .parser(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
62#   .build();
63// This pattern will not match because "n" is not an element named "foobar"
64assert_eq!(p.matches(&Context::new(), &mut static_context, &n), false)
65```
66
67*/
68
69use std::convert::TryFrom;
70use std::fmt;
71use std::fmt::{Debug, Formatter};
72use std::rc::Rc;
73use url::Url;
74
75use crate::item::{Item, Node, NodeType, Sequence, SequenceTrait};
76use crate::parser::combinators::whitespace::xpwhitespace;
77use crate::parser::xpath::literals::literal;
78use crate::parser::xpath::nodetests::{nodetest, qualname_test};
79use crate::parser::xpath::predicates::predicate_list;
80use crate::parser::xpath::variables::variable_reference;
81use crate::transform::context::{Context, ContextBuilder, StaticContext};
82use crate::transform::{Axis, KindTest, NameTest, NodeTest, Transform};
83use crate::value::Value;
84use crate::xdmerror::{Error, ErrorKind};
85
86use crate::parser::combinators::alt::{alt2, alt4, alt6};
87//use crate::parser::combinators::debug::inspect;
88use crate::parser::combinators::list::{separated_list0, separated_list1};
89use crate::parser::combinators::many::many0;
90use crate::parser::combinators::map::map;
91use crate::parser::combinators::opt::opt;
92use crate::parser::combinators::pair::pair;
93use crate::parser::combinators::tag::tag;
94use crate::parser::combinators::tuple::{tuple2, tuple3};
95use crate::parser::{
96    ParseError, ParseInput, ParserState, ParserStateBuilder, StaticState, StaticStateBuilder,
97};
98use qualname::{NamespacePrefix, NamespaceUri, NcName, QName};
99
100/// An XPath pattern. A pattern most frequently appears as the value of a match attribute.
101/// A pattern is either a predicate pattern or a selection pattern.
102///
103/// A predicate pattern matches the current item if all of the predicates evaluate to true.
104///
105/// A selection pattern is subset of XPath path expressions.
106#[derive(Clone)]
107pub enum Pattern<N: Node> {
108    Predicate(Transform<N>),
109    Selection(Path),
110    Error(Error),
111}
112
113impl<N: Node> Pattern<N> {
114    /// Returns whether the Pattern is of type error.
115    pub fn is_err(&self) -> bool {
116        if let Pattern::Selection(s) = self {
117            s.is_err()
118        } else {
119            matches!(self, Pattern::Error(_))
120        }
121    }
122    pub fn get_err(&self) -> Option<Error> {
123        if let Pattern::Selection(s) = self {
124            s.get_err()
125        } else if let Pattern::Error(e) = self {
126            Some(e.clone())
127        } else {
128            None
129        }
130    }
131    /// Returns whether the given item matches the pattern.
132    /// TODO: return dynamic errors
133    pub fn matches<
134        F: FnMut(&str) -> Result<(), Error>,
135        G: FnMut(&str) -> Result<N, Error>,
136        H: FnMut(&Url) -> Result<String, Error>,
137    >(
138        &self,
139        ctxt: &Context<N>,
140        stctxt: &mut StaticContext<N, F, G, H>,
141        i: &Item<N>,
142    ) -> bool {
143        match self {
144            Pattern::Predicate(t) => ContextBuilder::from(ctxt)
145                .context(vec![i.clone()])
146                .build()
147                .dispatch(stctxt, t)
148                .unwrap_or(vec![Item::Value(Rc::new(Value::from(false)))])
149                .to_bool(),
150            Pattern::Selection(p) => path_match(p, i),
151            _ => false, // not yet implemented
152        }
153    }
154    /// Find the NodeTest for the terminal step
155    pub fn terminal_node_test(&self) -> (Axis, Axis, NodeTest) {
156        if let Pattern::Selection(sel) = self {
157            branch_terminal_node_test(sel)
158        } else {
159            (
160                Axis::SelfDocument,
161                Axis::SelfDocument,
162                NodeTest::Kind(KindTest::Document),
163            )
164        }
165    }
166}
167
168fn branch_terminal_node_test(b: &Branch) -> (Axis, Axis, NodeTest) {
169    match b {
170        Branch::SingleStep(t) => (t.terminal, t.non_terminal, t.nt.clone()),
171        Branch::RelPath(r) => branch_terminal_node_test(&r[0]),
172        Branch::Union(u) => branch_terminal_node_test(&u[0]), // TODO: should be all of the alternatives
173        Branch::Error(_) => (
174            Axis::SelfDocument,
175            Axis::SelfDocument,
176            NodeTest::Kind(KindTest::Document),
177        ),
178    }
179}
180
181// Entry point for matching a Pattern.
182// The given Item is the initial context.
183fn path_match<N: Node>(p: &Path, i: &Item<N>) -> bool {
184    !branch_match(p, vec![i.clone()]).is_empty()
185}
186// Match a branch in the Pattern. Each Item in the Sequence is tested.
187// This results in a new context.
188fn branch_match<N: Node>(p: &Path, s: Sequence<N>) -> Sequence<N> {
189    // First step is the terminal case,
190    // next steps are non-terminal
191    match p {
192        Branch::SingleStep(t) => s
193            .iter()
194            .filter(|i| is_match(&t.terminal, &t.nt, i))
195            .flat_map(|i| find_seq(&t.non_terminal, i))
196            .collect(),
197        Branch::RelPath(r) => {
198            // A series of steps
199            // Each step selects a new context for the next step
200            r.iter().fold(s, |ctxt, b| {
201                let new_ctxt = ctxt
202                    .iter()
203                    .cloned()
204                    .flat_map(|i| branch_match(b, vec![i]))
205                    .collect();
206                new_ctxt
207            })
208        }
209        Branch::Union(u) => {
210            // If any match, then the whole matches
211            u.iter()
212                .flat_map(|b| {
213                    s.iter()
214                        .cloned()
215                        .flat_map(|i| branch_match(b, vec![i]))
216                        .collect::<Sequence<N>>()
217                })
218                .collect()
219        }
220        Branch::Error(_) => vec![],
221    }
222}
223
224fn find_seq<N: Node>(a: &Axis, i: &Item<N>) -> Sequence<N> {
225    match a {
226        Axis::SelfDocument => match i {
227            Item::Node(n) => {
228                if n.node_type() == NodeType::Document {
229                    vec![i.clone()]
230                } else {
231                    vec![]
232                }
233            }
234            _ => vec![],
235        },
236        Axis::SelfAxis => vec![i.clone()],
237        Axis::Parent => match i {
238            Item::Node(n) => n.parent().map_or(vec![], |p| vec![Item::Node(p)]),
239            _ => vec![],
240        },
241        _ => vec![], // todo
242    }
243}
244
245fn is_match<N: Node>(a: &Axis, nt: &NodeTest, i: &Item<N>) -> bool {
246    match a {
247        Axis::SelfDocument => {
248            // Select item only if it is a document-type node
249            match i {
250                Item::Node(n) => {
251                    if n.node_type() == NodeType::Document {
252                        nt.matches_item(i)
253                    } else {
254                        false
255                    }
256                }
257                _ => false,
258            }
259        }
260        Axis::SelfAxis => {
261            // Select item if it is an element-type node
262            nt.matches_item(i)
263            /*            match i {
264                Item::Node(n) => {
265                    if n.node_type() == NodeType::Element {
266                        nt.matches(i)
267                    } else {
268                        false
269                    }
270                }
271                _ => false,
272            }*/
273        }
274        Axis::Parent => {
275            // Select the parent node
276            match i {
277                Item::Node(n) => n.parent().is_some_and(|p| nt.matches(&p)),
278                _ => false,
279            }
280        }
281        Axis::SelfAttribute => match i {
282            Item::Node(n) => {
283                if n.node_type() == NodeType::Attribute {
284                    nt.matches(n)
285                } else {
286                    false
287                }
288            }
289            _ => false,
290        },
291        _ => false, // todo
292    }
293}
294
295impl<N: Node> Debug for Pattern<N> {
296    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
297        match self {
298            Pattern::Predicate(t) => write!(f, "Pattern::Predicate t==\"{:?}\"", t),
299            Pattern::Selection(p) => write!(f, "Pattern::Selection path=\"{:?}\"", p),
300            Pattern::Error(e) => write!(f, "Pattern::Error error=\"{:?}\"", e),
301        }
302    }
303}
304
305// A Path is a tree structure, but does not need to be mutable.
306// It also does not need to be fully navigable.
307
308// A Path is a Branch.
309// A Branch::Union is caused by a union ("|") operator -
310// if any of the union branches match then the Path matches.
311// If the vector is empty then there is no match.
312// A Rel(ative)Path is caused by the "/" character.
313// The terminal case is a single Step.
314pub type Path = Branch;
315#[derive(Clone, Debug)]
316pub enum Branch {
317    SingleStep(Step),
318    RelPath(Vec<Branch>),
319    Union(Vec<Branch>),
320    Error(Error),
321}
322
323impl Branch {
324    pub fn terminal_node_test(&self) -> (Axis, Axis, NodeTest) {
325        branch_terminal_node_test(self)
326    }
327    /// Check whether the Branch is an error or contains an error
328    pub fn is_err(&self) -> bool {
329        match self {
330            Branch::Error(_) => true,
331            Branch::SingleStep(_) => false,
332            Branch::RelPath(r) => r.iter().any(|f| f.is_err()),
333            Branch::Union(u) => u.iter().any(|f| f.is_err()),
334        }
335    }
336    /// Get any error in the Branch
337    pub fn get_err(&self) -> Option<Error> {
338        match self {
339            Branch::Error(e) => Some(e.clone()),
340            Branch::SingleStep(_) => None,
341            Branch::RelPath(r) => r.iter().fold(None, |v, f| v.or_else(|| f.get_err())),
342            Branch::Union(u) => u.iter().fold(None, |v, f| v.or_else(|| f.get_err())),
343        }
344    }
345}
346
347// * == Branch::SingleStep(*)
348// *|node() == Branch::Union(vec![Branch::SingleStep(*), Branch::SingleStep(node())])
349// a/b == Branch::RelPath(vec![Branch::SingleStep(a), Branch::SingleStep(b)])
350// a/b|c/d == Branch::Union(vec![
351//   Branch::RelPath(vec![Branch::SingleStep(a),Branch::SingleStep(b)]),
352//   Branch::RelPath(vec![Branch::SingleStep(c),Branch::SingleStep(d)]),
353// ])
354// a/(b|c)/d (matches a/b/d or a/c/d) == Branch::RelPath(vec![
355//   Branch::SingleStep(a),
356//   Branch::Union(vec![Branch::SingleStep(b),Branch::SingleStep(c)]),
357//   Branch::SingleStep(d)
358// ]
359// a/ (b/c | (d/e|f/g)) / (h|i) |j == Branch::Union(vec![
360//   Branch::RelPath(vec![
361//     Branch::SingleStep(a),
362//     Branch::RelPath(vec![
363//       Branch::Union(vec![
364//         Branch::RelPath(vec![Branch::SingleStep(b), Branch::SingleStep(c)]),
365//         Branch::Union(vec![
366//           Branch::RelPath(vec![Branch::SingleStep(d), Branch::SingleStep(e)]),
367//           Branch::RelPath(vec![Branch::SingleStep(f), Branch::SingleStep(g)]),
368//         ]),
369//       ]),
370//       Branch::Union(vec![
371//         Branch::SingleStep(h),
372//         Branch::SingleStep(i),
373//       ])
374//     ]),
375//   ]),
376//   Branch::SingleStep(j),
377// ]
378
379// A step in the Path consists of (terminal, non-terminal) axes and a NodeTest
380// If this is the last step, then the terminal axis is used.
381// Otherwise the non-terminal axis applies.
382#[derive(Clone, Debug)]
383pub struct Step {
384    terminal: Axis,
385    non_terminal: Axis,
386    nt: NodeTest,
387}
388
389impl Step {
390    pub fn new(terminal: Axis, non_terminal: Axis, nt: NodeTest) -> Self {
391        Step {
392            terminal,
393            non_terminal,
394            nt,
395        }
396    }
397    pub fn get_ref(&self) -> (&Axis, &Axis, &NodeTest) {
398        (&self.terminal, &self.non_terminal, &self.nt)
399    }
400}
401
402/// Compile an XPath pattern.
403impl<N: Node> TryFrom<&str> for Pattern<N> {
404    type Error = Error;
405    fn try_from(e: &str) -> Result<Self, <crate::pattern::Pattern<N> as TryFrom<&str>>::Error> {
406        if e.is_empty() {
407            Err(Error::new(
408                ErrorKind::TypeError,
409                String::from("empty string is not allowed as an XPath pattern"),
410            ))
411        } else {
412            pattern_driver(e, None)
413            /*let state = ParserState::new();
414            let mut static_state = StaticState::new();
415            match pattern::<N, L>((e, state), &mut static_state) {
416                Ok(((rem, _), f)) => {
417                    if rem.is_empty() {
418                        Ok(f)
419                    } else {
420                        Err(Error::new(
421                            ErrorKind::Unknown,
422                            format!("extra characters found: \"{:?}\"", rem),
423                        ))
424                    }
425                }
426                Err(err) => Err(Error::new(ErrorKind::Unknown, format!("{:?}", err))),
427            }*/
428        }
429    }
430}
431
432/// Compile an XPath pattern. Uses the supplied [Node] to resolve in-scope XML Namespaces.
433impl<N: Node> TryFrom<(&str, N)> for Pattern<N> {
434    type Error = Error;
435    fn try_from(
436        e: (&str, N),
437    ) -> Result<Self, <crate::pattern::Pattern<N> as TryFrom<&str>>::Error> {
438        if e.0.is_empty() {
439            Err(Error::new(
440                ErrorKind::TypeError,
441                String::from("empty string is not allowed as an XPath pattern"),
442            ))
443        } else {
444            pattern_driver(e.0, Some(e.1))
445            /*let state = ParserStateBuilder::new().doc(e.1).build();
446            // TODO: use closure that uses node's in-scope namespaces
447            let mut static_state = StaticStateBuilder::new()
448                .namespace(|_| {
449                    NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
450                })
451                .build();
452            match pattern::<N>((e.0, state), &mut static_state) {
453                Ok(((rem, _), f)) => {
454                    if rem.is_empty() {
455                        Ok(f)
456                    } else {
457                        Err(Error::new(
458                            ErrorKind::Unknown,
459                            format!("extra characters found: \"{:?}\"", rem),
460                        ))
461                    }
462                }
463                Err(err) => Err(Error::new(ErrorKind::Unknown, format!("{:?}", err))),
464            }*/
465        }
466    }
467}
468
469impl<'a, N: Node> TryFrom<String> for Pattern<N> {
470    type Error = Error;
471    fn try_from(e: String) -> Result<Self, <Pattern<N> as TryFrom<&'a str>>::Error> {
472        Pattern::try_from(e.as_str())
473    }
474}
475impl<'a, N: Node> TryFrom<(String, N)> for Pattern<N> {
476    type Error = Error;
477    fn try_from(e: (String, N)) -> Result<Self, <Pattern<N> as TryFrom<(&'a str, N)>>::Error> {
478        Pattern::try_from((e.0.as_str(), e.1))
479    }
480}
481
482fn pattern_driver<N: Node>(e: &str, n: Option<N>) -> Result<Pattern<N>, Error> {
483    let state = n.clone().map_or_else(
484        || ParserState::new(),
485        |m| ParserStateBuilder::new().doc(m).build(),
486    );
487    // TODO: use closure that uses node's in-scope namespaces
488    let mut static_state = StaticStateBuilder::new()
489        .namespace(|nsp| {
490            n.as_ref().map_or_else(
491                || Err(ParseError::MissingNameSpace),
492                |m| {
493                    m.to_namespace_uri(&Some(nsp.clone()))
494                        .map_err(|_| ParseError::MissingNameSpace)
495                },
496            )
497        })
498        .build();
499    match pattern((e, state), &mut static_state) {
500        Ok(((rem, _), f)) => {
501            if rem.is_empty() {
502                Ok(f)
503            } else {
504                Err(Error::new(
505                    ErrorKind::Unknown,
506                    format!("extra characters found: \"{:?}\"", rem),
507                ))
508            }
509        }
510        Err(err) => Err(Error::new(ErrorKind::Unknown, format!("{:?}", err))),
511    }
512}
513
514// Pattern30 ::= PredicatePattern | UnionExprP ;
515fn pattern<'a, N: Node + 'a, L>(
516    input: ParseInput<'a, N>,
517    static_state: &mut StaticState<L>,
518) -> Result<(ParseInput<'a, N>, Pattern<N>), ParseError>
519where
520    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
521{
522    alt2(predicate_pattern::<N, L>(), union_expr_pattern())(input, static_state)
523}
524
525// PredicatePattern ::= "." PredicateList
526// Context must match all predicates
527fn predicate_pattern<'a, N: Node + 'a, L>() -> Box<
528    dyn Fn(
529            ParseInput<'a, N>,
530            &mut StaticState<L>,
531        ) -> Result<(ParseInput<'a, N>, Pattern<N>), ParseError>
532        + 'a,
533>
534where
535    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
536{
537    Box::new(map(
538        pair(
539            map(tuple3(xpwhitespace(), tag("."), xpwhitespace()), |_| ()),
540            predicate_list::<N, L>(),
541        ),
542        |(_, p)| Pattern::Predicate(p),
543    ))
544}
545
546// UnionExprP ::= IntersectExceptExprP (("union" | "|") IntersectExceptExprP)*
547// A union expression matches if any of its components is a match. This creates a branching structure in the compilation of the Pattern<N>.
548fn union_expr_pattern<'a, N: Node + 'a, L>() -> Box<
549    dyn Fn(
550            ParseInput<'a, N>,
551            &mut StaticState<L>,
552        ) -> Result<(ParseInput<'a, N>, Pattern<N>), ParseError>
553        + 'a,
554>
555where
556    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
557{
558    Box::new(map(
559        separated_list1(
560            map(
561                tuple3(xpwhitespace(), alt2(tag("union"), tag("|")), xpwhitespace()),
562                |_| (),
563            ),
564            intersect_except_expr_pattern::<N, L>(),
565        ),
566        |v| {
567            Pattern::Selection(Branch::Union(v))
568            //            if v.len() == 1 {
569            //                v.pop().unwrap()
570            //            } else {
571            //                Pattern::Selection(vec![v])
572            //            }
573        },
574    ))
575}
576
577// NB. Rust *really* doesn't like recursive types, so we must force it to lazily evaluate arguments to avoid stack overflow.
578fn union_expr_wrapper<'a, N: Node + 'a, L>(
579    b: bool,
580) -> Box<
581    dyn Fn(
582        ParseInput<'a, N>,
583        &mut StaticState<L>,
584    ) -> Result<(ParseInput<'a, N>, Pattern<N>), ParseError>,
585>
586where
587    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
588{
589    Box::new(move |input, ss| {
590        if b {
591            union_expr_pattern::<N, L>()(input, ss)
592        } else {
593            noop()(input, ss)
594        }
595    })
596}
597
598fn noop<'a, N: Node, L>() -> Box<
599    dyn Fn(
600        ParseInput<'a, N>,
601        &mut StaticState<L>,
602    ) -> Result<(ParseInput<'a, N>, Pattern<N>), ParseError>,
603>
604where
605    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
606{
607    Box::new(move |_, _| Err(ParseError::Combinator(String::from("noop - pattern"))))
608}
609
610// IntersectExceptExprP ::= PathExprP (("intersect" | "except") PathExprP)*
611// intersect and except not yet supported
612fn intersect_except_expr_pattern<'a, N: Node + 'a, L>() -> Box<
613    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
614        + 'a,
615>
616where
617    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
618{
619    Box::new(map(
620        separated_list1(
621            map(
622                tuple3(
623                    xpwhitespace(),
624                    alt2(tag("intersect"), tag("except")),
625                    xpwhitespace(),
626                ),
627                |_| (),
628            ),
629            path_expr_pattern::<N, L>(),
630        ),
631        |mut v| {
632            if v.len() == 1 {
633                v.pop().unwrap()
634            } else {
635                // intersect/except not implemented
636                Branch::Error(Error::new(
637                    ErrorKind::NotImplemented,
638                    String::from("intersect or except in a pattern has not been implemented"),
639                ))
640            }
641        },
642    ))
643}
644
645// PathExprP ::= RootedPath | ("/" RelativePathExprP) | ("//" RelativePathExprP) | RelativePathExprP
646fn path_expr_pattern<'a, N: Node + 'a, L>() -> Box<
647    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
648        + 'a,
649>
650where
651    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
652{
653    Box::new(alt4(
654        rooted_path_pattern::<N, L>(),
655        absolutedescendant_expr_pattern(),
656        absolutepath_expr_pattern(),
657        relativepath_expr_pattern::<N, L>(),
658    ))
659}
660
661// RootedPath ::= (VarRef | FunctionCallP) PredicateList (("/" | "//") RelativePathExprP)?
662fn rooted_path_pattern<'a, N: Node + 'a, L>() -> Box<
663    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
664        + 'a,
665>
666where
667    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
668{
669    Box::new(map(
670        tuple3(
671            alt2(
672                variable_reference_pattern::<N, L>(),
673                function_call_pattern(),
674            ),
675            predicate_list::<N, L>(),
676            alt2(
677                absolutedescendant_expr_pattern::<N, L>(),
678                absolutepath_expr_pattern(),
679            ),
680        ),
681        |(_a, _b, _c)| {
682            Branch::Error(Error::new(
683                ErrorKind::NotImplemented,
684                String::from("rooted path in a pattern has not been implemented"),
685            ))
686        },
687    ))
688}
689
690// Variable Reference
691fn variable_reference_pattern<'a, N: Node + 'a, L>() -> Box<
692    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
693        + 'a,
694>
695where
696    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
697{
698    Box::new(map(variable_reference::<N, L>(), |_| {
699        Branch::Error(Error::new(
700            ErrorKind::NotImplemented,
701            "variable reference not yet supported",
702        ))
703    }))
704}
705
706// ('//' RelativePathExpr?)
707fn absolutedescendant_expr_pattern<'a, N: Node + 'a, L>() -> Box<
708    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
709        + 'a,
710>
711where
712    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
713{
714    Box::new(map(
715        pair(tag("//"), relativepath_expr_pattern::<N, L>()),
716        |(_, _r)| {
717            Branch::Error(Error::new(
718                ErrorKind::NotImplemented,
719                String::from("absolute descendant path in a pattern has not been implemented"),
720            ))
721        },
722    ))
723}
724
725// ('/' RelativePathExpr?)
726fn absolutepath_expr_pattern<'a, N: Node + 'a, L>() -> Box<
727    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
728        + 'a,
729>
730where
731    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
732{
733    Box::new(map(
734        pair(
735            map(tag("/"), |_| "/"),
736            opt(relativepath_expr_pattern::<N, L>()),
737        ),
738        |(d, r)| match (d, r.clone()) {
739            ("/", None) => {
740                // Matches the root node
741                Branch::SingleStep(Step::new(
742                    Axis::SelfDocument,
743                    Axis::SelfDocument,
744                    NodeTest::Kind(KindTest::Document),
745                ))
746            }
747            ("/", Some(Branch::SingleStep(s))) => Branch::RelPath(vec![
748                Branch::SingleStep(s),
749                Branch::SingleStep(Step::new(
750                    Axis::SelfDocument,
751                    Axis::SelfDocument,
752                    NodeTest::Kind(KindTest::Document),
753                )),
754            ]),
755            ("/", Some(Branch::RelPath(mut a))) => {
756                /*a.insert(
757                    0,
758                    Branch::SingleStep(Step::new(
759                        Axis::SelfDocument,
760                        Axis::SelfDocument,
761                        NodeTest::Kind(KindTest::Document),
762                    )),
763                );*/
764                a.push(Branch::SingleStep(Step::new(
765                    Axis::SelfDocument,
766                    Axis::SelfDocument,
767                    NodeTest::Kind(KindTest::Document),
768                )));
769                Branch::RelPath(a)
770            }
771            ("/", Some(Branch::Union(u))) => Branch::RelPath(vec![
772                Branch::Union(u),
773                Branch::SingleStep(Step::new(
774                    Axis::SelfDocument,
775                    Axis::SelfDocument,
776                    NodeTest::Kind(KindTest::Document),
777                )),
778            ]),
779            _ => Branch::Error(Error::new(
780                ErrorKind::Unknown,
781                String::from("unable to parse pattern"),
782            )),
783        },
784    ))
785}
786
787// RelativePathExpr ::= StepExpr (('/' | '//') StepExpr)*
788fn relativepath_expr_pattern<'a, N: Node + 'a, L>() -> Box<
789    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
790        + 'a,
791>
792where
793    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
794{
795    Box::new(map(
796        pair(
797            step_expr_pattern::<N, L>(),
798            many0(tuple2(
799                alt2(
800                    map(tuple3(xpwhitespace(), tag("//"), xpwhitespace()), |_| "//"),
801                    map(tuple3(xpwhitespace(), tag("/"), xpwhitespace()), |_| "/"),
802                ),
803                step_expr_pattern::<N, L>(),
804            )),
805        ),
806        |(a, b)| {
807            if b.is_empty() {
808                // this is the terminal step
809                a
810            } else {
811                // TODO: handle "//" separator
812                let mut result = vec![a];
813                for (_c, d) in b {
814                    result.insert(0, d);
815                }
816                Branch::RelPath(result)
817            }
818        },
819    ))
820}
821
822// StepExprP ::= PostfixExprExpr | AxisStepP
823fn step_expr_pattern<'a, N: Node + 'a, L>() -> Box<
824    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
825        + 'a,
826>
827where
828    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
829{
830    Box::new(alt2(
831        postfix_expr_pattern::<N, L>(),
832        axis_step_pattern::<N, L>(),
833    ))
834}
835
836// PostfixExprP ::= ParenthesizedExprP PredicateList
837// TODO: predicates
838fn postfix_expr_pattern<'a, N: Node + 'a, L>() -> Box<
839    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
840        + 'a,
841>
842where
843    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
844{
845    Box::new(map(
846        tuple2(paren_expr_pattern(), predicate_list::<N, L>()),
847        |(p, _)| p,
848    ))
849}
850
851// ParenthesizedExprP ::= "(" UnionExprP ")"
852fn paren_expr_pattern<'a, N: Node + 'a, L>() -> Box<
853    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
854        + 'a,
855>
856where
857    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
858{
859    Box::new(map(
860        tuple3(
861            tuple3(xpwhitespace(), tag("("), xpwhitespace()),
862            union_expr_wrapper(true),
863            tuple3(xpwhitespace(), tag(")"), xpwhitespace()),
864        ),
865        |(_, u, _)| {
866            if let Pattern::Selection(sel) = u {
867                sel
868            } else {
869                Branch::Error(Error::new(
870                    ErrorKind::TypeError,
871                    "expression must be a selection",
872                ))
873            }
874        },
875    ))
876}
877
878// AxisStepP ::= ForwardStepP PredicateList
879// TODO: predicate
880fn axis_step_pattern<'a, N: Node + 'a, L>() -> Box<
881    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
882        + 'a,
883>
884where
885    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
886{
887    Box::new(map(
888        tuple2(forward_step_pattern(), predicate_list::<N, L>()),
889        |(f, _p)| f, // TODO: pass predicate back to caller
890    ))
891}
892
893// ForwardStepP ::= (ForwardAxisP NodeTest) | AbbrevForwardStep
894// Returns the node test, the terminal axis and the non-terminal axis
895fn forward_step_pattern<'a, N: Node + 'a, L>() -> Box<
896    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
897        + 'a,
898>
899where
900    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
901{
902    Box::new(map(
903        alt2(
904            tuple2(forward_axis_pattern(), nodetest()),
905            abbrev_forward_step(),
906        ),
907        |((a, c), nt)| Branch::SingleStep(Step::new(a, c, nt)),
908    ))
909}
910
911// AbbrevForwardStep ::= "@"? NodeTest
912fn abbrev_forward_step<'a, N: Node + 'a, L>() -> Box<
913    dyn Fn(
914            ParseInput<'a, N>,
915            &mut StaticState<L>,
916        ) -> Result<(ParseInput<'a, N>, ((Axis, Axis), NodeTest)), ParseError>
917        + 'a,
918>
919where
920    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
921{
922    Box::new(map(tuple2(opt(tag("@")), nodetest()), |(a, nt)| {
923        a.map_or_else(
924            || {
925                // not an attribute
926                ((Axis::SelfAxis, Axis::Parent), nt.clone())
927            },
928            |_| {
929                // attribute
930                ((Axis::SelfAttribute, Axis::Parent), nt.clone())
931            },
932        )
933    }))
934}
935
936// ForwardAxisP ::= ("child" | "descendant" | "attribute" | "self" | "descendant-or-self" | "namespace" ) "::"
937// Returns a pair: the axis to match this step, and the axis for the previous step
938fn forward_axis_pattern<'a, N: Node + 'a, L>() -> Box<
939    dyn Fn(
940            ParseInput<'a, N>,
941            &mut StaticState<L>,
942        ) -> Result<(ParseInput<'a, N>, (Axis, Axis)), ParseError>
943        + 'a,
944>
945where
946    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
947{
948    Box::new(map(
949        tuple2(
950            alt6(
951                map(tag("child"), |_| (Axis::SelfAxis, Axis::Parent)),
952                map(tag("descendant"), |_| (Axis::SelfAxis, Axis::Ancestor)),
953                map(tag("attribute"), |_| (Axis::SelfAttribute, Axis::Parent)),
954                map(tag("self"), |_| (Axis::SelfAxis, Axis::SelfAxis)),
955                map(tag("descendant-or-self"), |_| {
956                    (Axis::SelfAxis, Axis::Ancestor)
957                }),
958                map(tag("namespace"), |_| (Axis::SelfNamespace, Axis::Parent)),
959            ),
960            tag("::"),
961        ),
962        |(a, _)| a,
963    ))
964}
965
966// FunctionCallP ::= OuterFunctionName ArgumentListP
967fn function_call_pattern<'a, N: Node + 'a, L>() -> Box<
968    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
969        + 'a,
970>
971where
972    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
973{
974    Box::new(map(
975        tuple2(outer_function_name(), argument_list_pattern::<N, L>()),
976        |(_n, _a)| {
977            // TODO
978            Branch::Error(Error::new(
979                ErrorKind::NotImplemented,
980                String::from("function call in a pattern has not been implemented"),
981            ))
982        },
983    ))
984}
985
986// ArgumentListP ::= "(" (ArgumentP ("," ArgumentP)*)? ")"
987fn argument_list_pattern<'a, N: Node + 'a, L>() -> Box<
988    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
989        + 'a,
990>
991where
992    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
993{
994    Box::new(map(
995        tuple3(
996            map(tuple3(xpwhitespace(), tag("("), xpwhitespace()), |_| ()),
997            separated_list0(
998                map(tuple3(xpwhitespace(), tag(","), xpwhitespace()), |_| ()),
999                argument_pattern::<N, L>(),
1000            ),
1001            map(tuple3(xpwhitespace(), tag(")"), xpwhitespace()), |_| ()),
1002        ),
1003        |(_, _a, _)| {
1004            // TODO
1005            Branch::Error(Error::new(
1006                ErrorKind::NotImplemented,
1007                String::from("argument list in a pattern has not been implemented"),
1008            ))
1009        },
1010    ))
1011}
1012
1013// ArgumentP ::= VarRef | Literal
1014fn argument_pattern<'a, N: Node + 'a, L>() -> Box<
1015    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
1016        + 'a,
1017>
1018where
1019    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
1020{
1021    Box::new(alt2(
1022        variable_reference_pattern::<N, L>(),
1023        literal_pattern::<N, L>(),
1024    ))
1025}
1026
1027// literal
1028fn literal_pattern<'a, N: Node + 'a, L>() -> Box<
1029    dyn Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, Path), ParseError>
1030        + 'a,
1031>
1032where
1033    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
1034{
1035    Box::new(map(literal::<N, L>(), |_| {
1036        Branch::Error(Error::new(ErrorKind::NotImplemented, "not yet implemented"))
1037    }))
1038}
1039
1040// OuterFunctionName ::= "doc" | "id" | "element-with-id" | "key" | "root" | URIQualifiedName
1041fn outer_function_name<'a, N: Node + 'a, L>() -> Box<
1042    dyn Fn(
1043            ParseInput<'a, N>,
1044            &mut StaticState<L>,
1045        ) -> Result<(ParseInput<'a, N>, NodeTest), ParseError>
1046        + 'a,
1047>
1048where
1049    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError> + 'a,
1050{
1051    Box::new(alt6(
1052        map(tag("doc"), |_| {
1053            // TODO: Don't Panic
1054            NodeTest::Name(NameTest::Name(QName::from_local_name(
1055                NcName::try_from("doc").unwrap(),
1056            )))
1057        }),
1058        map(tag("id"), |_| {
1059            NodeTest::Name(NameTest::Name(QName::from_local_name(
1060                NcName::try_from("id").unwrap(),
1061            )))
1062        }),
1063        map(tag("element-with-id"), |_| {
1064            NodeTest::Name(NameTest::Name(QName::from_local_name(
1065                NcName::try_from("element-with-id").unwrap(),
1066            )))
1067        }),
1068        map(tag("key"), |_| {
1069            NodeTest::Name(NameTest::Name(QName::from_local_name(
1070                NcName::try_from("key").unwrap(),
1071            )))
1072        }),
1073        map(tag("root"), |_| {
1074            NodeTest::Name(NameTest::Name(QName::from_local_name(
1075                NcName::try_from("root").unwrap(),
1076            )))
1077        }),
1078        map(qualname_test(), |q| q),
1079    ))
1080}