script/xpath/
eval.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 std::fmt;
6
7use html5ever::{LocalName, Namespace, Prefix, QualName, local_name, namespace_prefix, ns};
8
9use super::parser::{
10    AdditiveOp, Axis, EqualityOp, Expr, FilterExpr, KindTest, Literal, MultiplicativeOp, NodeTest,
11    NumericLiteral, PathExpr, PredicateExpr, PredicateListExpr, PrimaryExpr,
12    QName as ParserQualName, RelationalOp, StepExpr, UnaryOp,
13};
14use super::{EvaluationCtx, Value};
15use crate::dom::attr::Attr;
16use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
17use crate::dom::bindings::domname::namespace_from_domstring;
18use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::bindings::str::DOMString;
21use crate::dom::bindings::xmlname;
22use crate::dom::element::Element;
23use crate::dom::node::{Node, ShadowIncluding};
24use crate::dom::processinginstruction::ProcessingInstruction;
25use crate::xpath::context::PredicateCtx;
26
27#[derive(Clone, Debug, PartialEq)]
28pub(crate) enum Error {
29    NotANodeset,
30    InvalidPath,
31    UnknownFunction { name: QualName },
32    UnknownVariable { name: QualName },
33    UnknownNamespace { prefix: String },
34    InvalidQName { qname: ParserQualName },
35    FunctionEvaluation { fname: String },
36    Internal { msg: String },
37}
38
39impl std::fmt::Display for Error {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        match self {
42            Error::NotANodeset => write!(f, "expression did not evaluate to a nodeset"),
43            Error::InvalidPath => write!(f, "invalid path expression"),
44            Error::UnknownFunction { name } => write!(f, "unknown function {:?}", name),
45            Error::UnknownVariable { name } => write!(f, "unknown variable {:?}", name),
46            Error::UnknownNamespace { prefix } => {
47                write!(f, "unknown namespace prefix {:?}", prefix)
48            },
49            Error::InvalidQName { qname } => {
50                write!(f, "invalid QName {:?}", qname)
51            },
52            Error::FunctionEvaluation { fname } => {
53                write!(f, "error while evaluating function: {}", fname)
54            },
55            Error::Internal { msg } => {
56                write!(f, "internal error: {}", msg)
57            },
58        }
59    }
60}
61
62impl std::error::Error for Error {}
63
64pub(crate) fn try_extract_nodeset(v: Value) -> Result<Vec<DomRoot<Node>>, Error> {
65    match v {
66        Value::Nodeset(ns) => Ok(ns),
67        _ => Err(Error::NotANodeset),
68    }
69}
70
71pub(crate) trait Evaluatable: fmt::Debug {
72    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error>;
73    /// Returns true if this expression evaluates to a primitive value, without needing to touch the DOM
74    fn is_primitive(&self) -> bool;
75}
76
77impl<T: ?Sized> Evaluatable for Box<T>
78where
79    T: Evaluatable,
80{
81    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
82        (**self).evaluate(context)
83    }
84
85    fn is_primitive(&self) -> bool {
86        (**self).is_primitive()
87    }
88}
89
90impl<T> Evaluatable for Option<T>
91where
92    T: Evaluatable,
93{
94    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
95        match self {
96            Some(expr) => expr.evaluate(context),
97            None => Ok(Value::Nodeset(vec![])),
98        }
99    }
100
101    fn is_primitive(&self) -> bool {
102        self.as_ref().is_some_and(|t| T::is_primitive(t))
103    }
104}
105
106impl Evaluatable for Expr {
107    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
108        match self {
109            Expr::And(left, right) => {
110                let left_bool = left.evaluate(context)?.boolean();
111                let v = left_bool && right.evaluate(context)?.boolean();
112                Ok(Value::Boolean(v))
113            },
114            Expr::Or(left, right) => {
115                let left_bool = left.evaluate(context)?.boolean();
116                let v = left_bool || right.evaluate(context)?.boolean();
117                Ok(Value::Boolean(v))
118            },
119            Expr::Equality(left, equality_op, right) => {
120                let left_val = left.evaluate(context)?;
121                let right_val = right.evaluate(context)?;
122
123                let v = match equality_op {
124                    EqualityOp::Eq => left_val == right_val,
125                    EqualityOp::NotEq => left_val != right_val,
126                };
127
128                Ok(Value::Boolean(v))
129            },
130            Expr::Relational(left, relational_op, right) => {
131                let left_val = left.evaluate(context)?.number();
132                let right_val = right.evaluate(context)?.number();
133
134                let v = match relational_op {
135                    RelationalOp::Lt => left_val < right_val,
136                    RelationalOp::Gt => left_val > right_val,
137                    RelationalOp::LtEq => left_val <= right_val,
138                    RelationalOp::GtEq => left_val >= right_val,
139                };
140                Ok(Value::Boolean(v))
141            },
142            Expr::Additive(left, additive_op, right) => {
143                let left_val = left.evaluate(context)?.number();
144                let right_val = right.evaluate(context)?.number();
145
146                let v = match additive_op {
147                    AdditiveOp::Add => left_val + right_val,
148                    AdditiveOp::Sub => left_val - right_val,
149                };
150                Ok(Value::Number(v))
151            },
152            Expr::Multiplicative(left, multiplicative_op, right) => {
153                let left_val = left.evaluate(context)?.number();
154                let right_val = right.evaluate(context)?.number();
155
156                let v = match multiplicative_op {
157                    MultiplicativeOp::Mul => left_val * right_val,
158                    MultiplicativeOp::Div => left_val / right_val,
159                    MultiplicativeOp::Mod => left_val % right_val,
160                };
161                Ok(Value::Number(v))
162            },
163            Expr::Unary(unary_op, expr) => {
164                let v = expr.evaluate(context)?.number();
165
166                match unary_op {
167                    UnaryOp::Minus => Ok(Value::Number(-v)),
168                }
169            },
170            Expr::Union(left, right) => {
171                let as_nodes = |e: &Expr| e.evaluate(context).and_then(try_extract_nodeset);
172
173                let mut left_nodes = as_nodes(left)?;
174                let right_nodes = as_nodes(right)?;
175
176                left_nodes.extend(right_nodes);
177                Ok(Value::Nodeset(left_nodes))
178            },
179            Expr::Path(path_expr) => path_expr.evaluate(context),
180        }
181    }
182
183    fn is_primitive(&self) -> bool {
184        match self {
185            Expr::Or(left, right) => left.is_primitive() && right.is_primitive(),
186            Expr::And(left, right) => left.is_primitive() && right.is_primitive(),
187            Expr::Equality(left, _, right) => left.is_primitive() && right.is_primitive(),
188            Expr::Relational(left, _, right) => left.is_primitive() && right.is_primitive(),
189            Expr::Additive(left, _, right) => left.is_primitive() && right.is_primitive(),
190            Expr::Multiplicative(left, _, right) => left.is_primitive() && right.is_primitive(),
191            Expr::Unary(_, expr) => expr.is_primitive(),
192            Expr::Union(_, _) => false,
193            Expr::Path(path_expr) => path_expr.is_primitive(),
194        }
195    }
196}
197
198impl Evaluatable for PathExpr {
199    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
200        // Use starting_node for absolute/descendant paths, context_node otherwise
201        let mut current_nodes = if self.is_absolute || self.is_descendant {
202            vec![context.starting_node.clone()]
203        } else {
204            vec![context.context_node.clone()]
205        };
206
207        // If path starts with '//', add an implicit descendant-or-self::node() step
208        if self.is_descendant {
209            current_nodes = current_nodes
210                .iter()
211                .flat_map(|n| n.traverse_preorder(ShadowIncluding::No))
212                .collect();
213        }
214
215        trace!("[PathExpr] Evaluating path expr: {:?}", self);
216
217        let have_multiple_steps = self.steps.len() > 1;
218
219        for step in &self.steps {
220            let mut next_nodes = Vec::new();
221            for node in current_nodes {
222                let step_context = context.subcontext_for_node(&node);
223                let step_result = step.evaluate(&step_context)?;
224                match (have_multiple_steps, step_result) {
225                    (_, Value::Nodeset(mut nodes)) => {
226                        // as long as we evaluate to nodesets, keep going
227                        next_nodes.append(&mut nodes);
228                    },
229                    (false, value) => {
230                        trace!("[PathExpr] Got single primitive value: {:?}", value);
231                        return Ok(value);
232                    },
233                    (true, value) => {
234                        error!(
235                            "Expected nodeset from step evaluation, got: {:?} node: {:?}, step: {:?}",
236                            value, node, step
237                        );
238                        return Ok(value);
239                    },
240                }
241            }
242            current_nodes = next_nodes;
243        }
244
245        trace!("[PathExpr] Got nodes: {:?}", current_nodes);
246
247        Ok(Value::Nodeset(current_nodes))
248    }
249
250    fn is_primitive(&self) -> bool {
251        !self.is_absolute &&
252            !self.is_descendant &&
253            self.steps.len() == 1 &&
254            self.steps[0].is_primitive()
255    }
256}
257
258/// Error types for validate and extract a qualified name following
259/// the XML naming rules.
260#[derive(Debug)]
261enum ValidationError {
262    InvalidCharacter,
263    Namespace,
264}
265
266/// Validate a qualified name following the XML naming rules.
267///
268/// On success, this returns a tuple `(prefix, local name)`.
269fn validate_and_extract_qualified_name(
270    qualified_name: &str,
271) -> Result<(Option<&str>, &str), ValidationError> {
272    if qualified_name.is_empty() {
273        // Qualified names must not be empty
274        return Err(ValidationError::InvalidCharacter);
275    }
276    let mut colon_offset = None;
277    let mut at_start_of_name = true;
278
279    for (byte_position, c) in qualified_name.char_indices() {
280        if c == ':' {
281            if colon_offset.is_some() {
282                // Qualified names must not contain more than one colon
283                return Err(ValidationError::InvalidCharacter);
284            }
285            colon_offset = Some(byte_position);
286            at_start_of_name = true;
287            continue;
288        }
289
290        if at_start_of_name {
291            if !xmlname::is_valid_start(c) {
292                // Name segments must begin with a valid start character
293                return Err(ValidationError::InvalidCharacter);
294            }
295            at_start_of_name = false;
296        } else if !xmlname::is_valid_continuation(c) {
297            // Name segments must consist of valid characters
298            return Err(ValidationError::InvalidCharacter);
299        }
300    }
301
302    let Some(colon_offset) = colon_offset else {
303        // Simple case: there is no prefix
304        return Ok((None, qualified_name));
305    };
306
307    let (prefix, local_name) = qualified_name.split_at(colon_offset);
308    let local_name = &local_name[1..]; // Remove the colon
309
310    if prefix.is_empty() || local_name.is_empty() {
311        // Neither prefix nor local name can be empty
312        return Err(ValidationError::InvalidCharacter);
313    }
314
315    Ok((Some(prefix), local_name))
316}
317
318/// Validate a namespace and qualified name following the XML naming rules
319/// and extract their parts.
320fn validate_and_extract(
321    namespace: Option<DOMString>,
322    qualified_name: &str,
323) -> Result<(Namespace, Option<Prefix>, LocalName), ValidationError> {
324    // Step 1. If namespace is the empty string, then set it to null.
325    let namespace = namespace_from_domstring(namespace);
326
327    // Step 2. Validate qualifiedName.
328    // Step 3. Let prefix be null.
329    // Step 4. Let localName be qualifiedName.
330    // Step 5. If qualifiedName contains a U+003A (:):
331    // NOTE: validate_and_extract_qualified_name does all of these things for us, because
332    // it's easier to do them together
333    let (prefix, local_name) = validate_and_extract_qualified_name(qualified_name)?;
334    debug_assert!(!local_name.contains(':'));
335
336    match (namespace, prefix) {
337        (ns!(), Some(_)) => {
338            // Step 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException.
339            Err(ValidationError::Namespace)
340        },
341        (ref ns, Some("xml")) if ns != &ns!(xml) => {
342            // Step 7. If prefix is "xml" and namespace is not the XML namespace,
343            // then throw a "NamespaceError" DOMException.
344            Err(ValidationError::Namespace)
345        },
346        (ref ns, p) if ns != &ns!(xmlns) && (qualified_name == "xmlns" || p == Some("xmlns")) => {
347            // Step 8. If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace,
348            // then throw a "NamespaceError" DOMException.
349            Err(ValidationError::Namespace)
350        },
351        (ns!(xmlns), p) if qualified_name != "xmlns" && p != Some("xmlns") => {
352            // Step 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns",
353            // then throw a "NamespaceError" DOMException.
354            Err(ValidationError::Namespace)
355        },
356        (ns, p) => {
357            // Step 10. Return namespace, prefix, and localName.
358            Ok((ns, p.map(Prefix::from), LocalName::from(local_name)))
359        },
360    }
361}
362
363pub(crate) struct QualNameConverter<'a> {
364    qname: &'a ParserQualName,
365    context: &'a EvaluationCtx,
366}
367
368impl<'a> TryFrom<QualNameConverter<'a>> for QualName {
369    type Error = Error;
370
371    fn try_from(converter: QualNameConverter<'a>) -> Result<Self, Self::Error> {
372        let qname_as_str = converter.qname.to_string();
373        let namespace = converter
374            .context
375            .resolve_namespace(converter.qname.prefix.as_deref());
376
377        if let Ok((ns, prefix, local)) = validate_and_extract(namespace, &qname_as_str) {
378            Ok(QualName { prefix, ns, local })
379        } else {
380            Err(Error::InvalidQName {
381                qname: converter.qname.clone(),
382            })
383        }
384    }
385}
386
387#[derive(Debug)]
388pub(crate) enum NameTestComparisonMode {
389    /// Namespaces must match exactly
390    XHtml,
391    /// Missing namespace information is treated as the HTML namespace
392    Html,
393}
394
395pub(crate) fn element_name_test(
396    expected_name: QualName,
397    element_qualname: QualName,
398    comparison_mode: NameTestComparisonMode,
399) -> bool {
400    let is_wildcard = expected_name.local == local_name!("*");
401
402    let test_prefix = expected_name
403        .prefix
404        .clone()
405        .unwrap_or(namespace_prefix!(""));
406    let test_ns_uri = match test_prefix {
407        namespace_prefix!("*") => ns!(*),
408        namespace_prefix!("html") => ns!(html),
409        namespace_prefix!("xml") => ns!(xml),
410        namespace_prefix!("xlink") => ns!(xlink),
411        namespace_prefix!("svg") => ns!(svg),
412        namespace_prefix!("mathml") => ns!(mathml),
413        namespace_prefix!("") => {
414            if matches!(comparison_mode, NameTestComparisonMode::XHtml) {
415                ns!()
416            } else {
417                ns!(html)
418            }
419        },
420        _ => {
421            // We don't support custom namespaces, use fallback or panic depending on strictness
422            if matches!(comparison_mode, NameTestComparisonMode::XHtml) {
423                panic!("Unrecognized namespace prefix: {}", test_prefix)
424            } else {
425                ns!(html)
426            }
427        },
428    };
429
430    if is_wildcard {
431        test_ns_uri == element_qualname.ns
432    } else {
433        test_ns_uri == element_qualname.ns && expected_name.local == element_qualname.local
434    }
435}
436
437fn apply_node_test(context: &EvaluationCtx, test: &NodeTest, node: &Node) -> Result<bool, Error> {
438    let result = match test {
439        NodeTest::Name(qname) => {
440            // Convert the unvalidated "parser QualName" into the proper QualName structure
441            let wanted_name: QualName = QualNameConverter { qname, context }.try_into()?;
442            match node.type_id() {
443                NodeTypeId::Element(_) => {
444                    let element = node.downcast::<Element>().unwrap();
445                    let comparison_mode = if node.owner_doc().is_html_document() {
446                        NameTestComparisonMode::Html
447                    } else {
448                        NameTestComparisonMode::XHtml
449                    };
450                    let element_qualname = QualName::new(
451                        element.prefix().as_ref().cloned(),
452                        element.namespace().clone(),
453                        element.local_name().clone(),
454                    );
455                    element_name_test(wanted_name, element_qualname, comparison_mode)
456                },
457                NodeTypeId::Attr => {
458                    let attr = node.downcast::<Attr>().unwrap();
459                    let attr_qualname = QualName::new(
460                        attr.prefix().cloned(),
461                        attr.namespace().clone(),
462                        attr.local_name().clone(),
463                    );
464                    // attributes are always compared with strict namespace matching
465                    let comparison_mode = NameTestComparisonMode::XHtml;
466                    element_name_test(wanted_name, attr_qualname, comparison_mode)
467                },
468                _ => false,
469            }
470        },
471        NodeTest::Wildcard => matches!(node.type_id(), NodeTypeId::Element(_)),
472        NodeTest::Kind(kind) => match kind {
473            KindTest::PI(target) => {
474                if NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) ==
475                    node.type_id()
476                {
477                    let pi = node.downcast::<ProcessingInstruction>().unwrap();
478                    match (target, pi.target()) {
479                        (Some(target_name), node_target_name)
480                            if target_name == &node_target_name.to_string() =>
481                        {
482                            true
483                        },
484                        (Some(_), _) => false,
485                        (None, _) => true,
486                    }
487                } else {
488                    false
489                }
490            },
491            KindTest::Comment => matches!(
492                node.type_id(),
493                NodeTypeId::CharacterData(CharacterDataTypeId::Comment)
494            ),
495            KindTest::Text => matches!(
496                node.type_id(),
497                NodeTypeId::CharacterData(CharacterDataTypeId::Text(_))
498            ),
499            KindTest::Node => true,
500        },
501    };
502    Ok(result)
503}
504
505impl Evaluatable for StepExpr {
506    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
507        match self {
508            StepExpr::Filter(filter_expr) => filter_expr.evaluate(context),
509            StepExpr::Axis(axis_step) => {
510                let nodes: Vec<DomRoot<Node>> = match axis_step.axis {
511                    Axis::Child => context.context_node.children().collect(),
512                    Axis::Descendant => context
513                        .context_node
514                        .traverse_preorder(ShadowIncluding::No)
515                        .skip(1)
516                        .collect(),
517                    Axis::Parent => vec![context.context_node.GetParentNode()]
518                        .into_iter()
519                        .flatten()
520                        .collect(),
521                    Axis::Ancestor => context.context_node.ancestors().collect(),
522                    Axis::Following => context
523                        .context_node
524                        .following_nodes(&context.context_node)
525                        .skip(1)
526                        .collect(),
527                    Axis::Preceding => context
528                        .context_node
529                        .preceding_nodes(&context.context_node)
530                        .skip(1)
531                        .collect(),
532                    Axis::FollowingSibling => context.context_node.following_siblings().collect(),
533                    Axis::PrecedingSibling => context.context_node.preceding_siblings().collect(),
534                    Axis::Attribute => {
535                        if matches!(Node::type_id(&context.context_node), NodeTypeId::Element(_)) {
536                            let element = context.context_node.downcast::<Element>().unwrap();
537                            element
538                                .attrs()
539                                .iter()
540                                .map(|attr| attr.upcast::<Node>())
541                                .map(DomRoot::from_ref)
542                                .collect()
543                        } else {
544                            vec![]
545                        }
546                    },
547                    Axis::Self_ => vec![context.context_node.clone()],
548                    Axis::DescendantOrSelf => context
549                        .context_node
550                        .traverse_preorder(ShadowIncluding::No)
551                        .collect(),
552                    Axis::AncestorOrSelf => context
553                        .context_node
554                        .inclusive_ancestors(ShadowIncluding::No)
555                        .collect(),
556                    Axis::Namespace => Vec::new(), // Namespace axis is not commonly implemented
557                };
558
559                trace!("[StepExpr] Axis {:?} got nodes {:?}", axis_step.axis, nodes);
560
561                // Filter nodes according to the step's node_test. Will error out if any NodeTest
562                // application errors out.
563                let filtered_nodes: Vec<DomRoot<Node>> = nodes
564                    .into_iter()
565                    .map(|node| {
566                        apply_node_test(context, &axis_step.node_test, &node)
567                            .map(|matches| matches.then_some(node))
568                    })
569                    .collect::<Result<Vec<_>, _>>()?
570                    .into_iter()
571                    .flatten()
572                    .collect();
573
574                trace!("[StepExpr] Filtering got nodes {:?}", filtered_nodes);
575
576                if axis_step.predicates.predicates.is_empty() {
577                    trace!(
578                        "[StepExpr] No predicates, returning nodes {:?}",
579                        filtered_nodes
580                    );
581                    Ok(Value::Nodeset(filtered_nodes))
582                } else {
583                    // Apply predicates
584                    let predicate_list_subcontext = context
585                        .update_predicate_nodes(filtered_nodes.iter().map(|n| &**n).collect());
586                    axis_step.predicates.evaluate(&predicate_list_subcontext)
587                }
588            },
589        }
590    }
591
592    fn is_primitive(&self) -> bool {
593        match self {
594            StepExpr::Filter(filter_expr) => filter_expr.is_primitive(),
595            StepExpr::Axis(_) => false,
596        }
597    }
598}
599
600impl Evaluatable for PredicateListExpr {
601    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
602        if let Some(ref predicate_nodes) = context.predicate_nodes {
603            let mut matched_nodes: Vec<DomRoot<Node>> = predicate_nodes.clone();
604
605            for predicate_expr in &self.predicates {
606                let size = matched_nodes.len();
607                let mut new_matched = Vec::new();
608
609                for (i, node) in matched_nodes.iter().enumerate() {
610                    // 1-based position, per XPath spec
611                    let predicate_ctx = EvaluationCtx {
612                        starting_node: context.starting_node.clone(),
613                        context_node: node.clone(),
614                        predicate_nodes: context.predicate_nodes.clone(),
615                        predicate_ctx: Some(PredicateCtx { index: i + 1, size }),
616                    };
617
618                    let eval_result = predicate_expr.expr.evaluate(&predicate_ctx);
619
620                    let keep = match eval_result {
621                        Ok(Value::Number(n)) => (i + 1) as f64 == n,
622                        Ok(Value::Boolean(b)) => b,
623                        Ok(v) => v.boolean(),
624                        Err(_) => false,
625                    };
626
627                    if keep {
628                        new_matched.push(node.clone());
629                    }
630                }
631
632                matched_nodes = new_matched;
633                trace!(
634                    "[PredicateListExpr] Predicate {:?} matched nodes {:?}",
635                    predicate_expr, matched_nodes
636                );
637            }
638            Ok(Value::Nodeset(matched_nodes))
639        } else {
640            Err(Error::Internal {
641                msg: "[PredicateListExpr] No nodes on stack for predicate to operate on"
642                    .to_string(),
643            })
644        }
645    }
646
647    fn is_primitive(&self) -> bool {
648        self.predicates.len() == 1 && self.predicates[0].is_primitive()
649    }
650}
651
652impl Evaluatable for PredicateExpr {
653    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
654        let narrowed_nodes: Result<Vec<DomRoot<Node>>, Error> = context
655            .subcontext_iter_for_nodes()
656            .filter_map(|ctx| {
657                if let Some(predicate_ctx) = ctx.predicate_ctx {
658                    let eval_result = self.expr.evaluate(&ctx);
659
660                    let v = match eval_result {
661                        Ok(Value::Number(v)) => Ok(predicate_ctx.index == v as usize),
662                        Ok(Value::Boolean(v)) => Ok(v),
663                        Ok(v) => Ok(v.boolean()),
664                        Err(e) => Err(e),
665                    };
666
667                    match v {
668                        Ok(true) => Some(Ok(ctx.context_node)),
669                        Ok(false) => None,
670                        Err(e) => Some(Err(e)),
671                    }
672                } else {
673                    Some(Err(Error::Internal {
674                        msg: "[PredicateExpr] No predicate context set".to_string(),
675                    }))
676                }
677            })
678            .collect();
679
680        Ok(Value::Nodeset(narrowed_nodes?))
681    }
682
683    fn is_primitive(&self) -> bool {
684        self.expr.is_primitive()
685    }
686}
687
688impl Evaluatable for FilterExpr {
689    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
690        let primary_result = self.primary.evaluate(context)?;
691        let have_predicates = !self.predicates.predicates.is_empty();
692
693        match (have_predicates, &primary_result) {
694            (false, _) => {
695                trace!(
696                    "[FilterExpr] No predicates, returning primary result: {:?}",
697                    primary_result
698                );
699                Ok(primary_result)
700            },
701            (true, Value::Nodeset(vec)) => {
702                let predicate_list_subcontext =
703                    context.update_predicate_nodes(vec.iter().map(|n| &**n).collect());
704                let result_filtered_by_predicates =
705                    self.predicates.evaluate(&predicate_list_subcontext);
706                trace!(
707                    "[FilterExpr] Result filtered by predicates: {:?}",
708                    result_filtered_by_predicates
709                );
710                result_filtered_by_predicates
711            },
712            // You can't use filtering expressions `[]` on other than node-sets
713            (true, _) => Err(Error::NotANodeset),
714        }
715    }
716
717    fn is_primitive(&self) -> bool {
718        self.predicates.predicates.is_empty() && self.primary.is_primitive()
719    }
720}
721
722impl Evaluatable for PrimaryExpr {
723    fn evaluate(&self, context: &EvaluationCtx) -> Result<Value, Error> {
724        match self {
725            PrimaryExpr::Literal(literal) => literal.evaluate(context),
726            PrimaryExpr::Variable(_qname) => todo!(),
727            PrimaryExpr::Parenthesized(expr) => expr.evaluate(context),
728            PrimaryExpr::ContextItem => Ok(Value::Nodeset(vec![context.context_node.clone()])),
729            PrimaryExpr::Function(core_function) => core_function.evaluate(context),
730        }
731    }
732
733    fn is_primitive(&self) -> bool {
734        match self {
735            PrimaryExpr::Literal(_) => true,
736            PrimaryExpr::Variable(_qname) => false,
737            PrimaryExpr::Parenthesized(expr) => expr.is_primitive(),
738            PrimaryExpr::ContextItem => false,
739            PrimaryExpr::Function(_) => false,
740        }
741    }
742}
743
744impl Evaluatable for Literal {
745    fn evaluate(&self, _context: &EvaluationCtx) -> Result<Value, Error> {
746        match self {
747            Literal::Numeric(numeric_literal) => match numeric_literal {
748                // We currently make no difference between ints and floats
749                NumericLiteral::Integer(v) => Ok(Value::Number(*v as f64)),
750                NumericLiteral::Decimal(v) => Ok(Value::Number(*v)),
751            },
752            Literal::String(s) => Ok(Value::String(s.into())),
753        }
754    }
755
756    fn is_primitive(&self) -> bool {
757        true
758    }
759}