1use crate::types::{ItemType, NameTest as RuntimeNameTest, SequenceType, XmlTypeCode};
20use crate::xpath::arena::{AstArena, AstNodeId};
21use crate::xpath::ast::{
22 AstNode, Axis, BinaryOpKind, FilterExprNode, ForBinding, ForNode, ItemTypeNode, KindTest,
23 NodeTest as AstNodeTest, OccurrenceIndicator, PathExprNode, PathStepNode, QuantifiedNode,
24 QuantifierKind, TypeExprKind, TypeExprNode, ValueNode,
25};
26use crate::xpath::axis_iterators::{
27 AncestorAxis, AttributeAxis, ChildAxis, DescendantNodeIterator, FollowingNodeIterator,
28 FollowingSiblingAxis, NamespaceAxis, ParentAxis, PrecedingNodeIterator, PrecedingSiblingAxis,
29 SelfAxis, SequentialAxisNodeIterator,
30};
31use crate::xpath::cast::{cast_to, castable, occurrence_allows_count, resolved_type_to_type_code};
32use crate::xpath::context::{DynamicContext, XPathContext};
33use crate::xpath::error::XPathError;
34use crate::xpath::functions::{
35 atomize_to_single_opt, effective_boolean_value, effective_boolean_value_10, XPathValue,
36};
37use crate::xpath::iterator::{
38 DocumentOrderNodeIterator, VecNodeIterator, XmlItem, XmlNodeIterator,
39};
40use crate::xpath::node_ops::{following_node, get_root, preceding_node, same_node};
41use crate::xpath::node_test::{matches_item_type_node, NodeTest};
42use crate::xpath::operators::cast_to_qname_with_context;
43use crate::xpath::operators::{
44 eval_binary, eval_numeric_binary_10, eval_range, eval_unary, general_eq_iter,
45 general_eq_iter_10, general_ge_iter, general_ge_iter_10, general_gt_iter, general_gt_iter_10,
46 general_le_iter, general_le_iter_10, general_lt_iter, general_lt_iter_10, general_ne_iter,
47 general_ne_iter_10,
48};
49use crate::xpath::sequence_ops::{except_nodes, intersect_nodes, union_nodes};
50use crate::xpath::{DomNavigator, XPathMode};
51
52pub fn eval_node<N: DomNavigator>(
72 arena: &AstArena,
73 id: AstNodeId,
74 ctx: &mut DynamicContext<'_, N>,
75) -> Result<XPathValue<N>, XPathError> {
76 let node = arena.get(id);
77
78 match node {
79 AstNode::Expr(expr) => {
80 if expr.items.is_empty() {
82 return Ok(XPathValue::empty());
83 }
84
85 if expr.items.len() == 1 {
86 return eval_node(arena, expr.items[0], ctx);
88 }
89
90 if ctx.static_context.mode() == XPathMode::XPath10 {
93 return Err(XPathError::XPST0003 {
94 message: "Sequence expressions (comma operator) are not available in XPath 1.0"
95 .to_string(),
96 });
97 }
98
99 let mut results: Vec<XmlItem<N>> = Vec::new();
101 for item_id in &expr.items {
102 let value = eval_node(arena, *item_id, ctx)?;
103 results.extend(value.into_vec());
104 }
105 Ok(XPathValue::from_sequence(results))
106 }
107
108 AstNode::Value(value_node) => {
109 if ctx.static_context.mode() == XPathMode::XPath10 {
111 if matches!(value_node, ValueNode::Empty) {
112 return Err(XPathError::XPST0003 {
113 message: "Empty sequence () is not available in XPath 1.0".to_string(),
114 });
115 }
116 if matches!(value_node, ValueNode::Double(_)) {
117 return Err(XPathError::XPST0003 {
118 message: "Double literals (e.g. 1e10) are not available in XPath 1.0"
119 .to_string(),
120 });
121 }
122 }
123 eval_value(value_node, ctx.static_context.mode())
125 }
126
127 AstNode::ContextItem(_) => {
128 match &ctx.context_item {
130 Some(item) => Ok(XPathValue::from_item(item.clone())),
131 None => Err(XPathError::XPDY0002 {
132 message: "Context item is undefined".to_string(),
133 }),
134 }
135 }
136
137 AstNode::VarRef(var_ref) => {
138 let slot = var_ref
140 .slot
141 .ok_or_else(|| XPathError::Internal("Variable reference not bound".to_string()))?;
142
143 ctx.get_variable(slot)
144 .cloned()
145 .ok_or_else(|| XPathError::XPDY0002 {
146 message: format!("Variable ${} is not set", var_ref.local_name),
147 })
148 }
149
150 AstNode::If(if_node) => {
151 let test_value = eval_node(arena, if_node.test, ctx)?;
153 let condition = effective_boolean_value(&test_value)?;
154
155 if condition {
156 eval_node(arena, if_node.then_branch, ctx)
157 } else {
158 eval_node(arena, if_node.else_branch, ctx)
159 }
160 }
161
162 AstNode::FunctionCall(func_call) => {
163 let handle = func_call
165 .function_handle
166 .ok_or_else(|| XPathError::Internal("Function call not bound".to_string()))?;
167
168 let mut args: Vec<XPathValue<N>> = Vec::with_capacity(func_call.args.len());
170 for arg_id in &func_call.args {
171 args.push(eval_node(arena, *arg_id, ctx)?);
172 }
173
174 ctx.eval_function(handle, args)
176 }
177
178 AstNode::For(for_node) => eval_for_expression(arena, for_node, ctx),
179
180 AstNode::Quantified(quant_node) => eval_quantified_expression(arena, quant_node, ctx),
181
182 AstNode::PathExpr(path_expr) => eval_path_expr(arena, path_expr, ctx),
183
184 AstNode::FilterExpr(filter_expr) => eval_filter_expr(arena, filter_expr, ctx),
185
186 AstNode::Range(range) => {
187 let start_val = eval_node(arena, range.start, ctx)?;
188 let end_val = eval_node(arena, range.end, ctx)?;
189
190 let start_opt = atomize_to_single_opt(start_val)?;
191 let end_opt = atomize_to_single_opt(end_val)?;
192
193 match (start_opt, end_opt) {
194 (None, _) | (_, None) => Ok(XPathValue::empty()),
195 (Some(start), Some(end)) => {
196 let values = eval_range(&start, &end)?;
197 let items: Vec<XmlItem<N>> = values.into_iter().map(XmlItem::Atomic).collect();
198 Ok(XPathValue::from_sequence(items))
199 }
200 }
201 }
202
203 AstNode::UnaryOp(unary_op) => {
204 let operand_val = eval_node(arena, unary_op.operand, ctx)?;
205 let opt = atomize_to_single_opt(operand_val)?;
206
207 match opt {
208 None => Ok(XPathValue::empty()),
209 Some(operand) => {
210 let result = eval_unary(unary_op.kind, &operand)?;
211 Ok(XPathValue::from_atomic(result))
212 }
213 }
214 }
215
216 AstNode::BinaryOp(bin_op) => {
217 match bin_op.kind {
218 BinaryOpKind::And => {
220 let left_val = eval_node(arena, bin_op.left, ctx)?;
221 let left_bool = if ctx.static_context.mode() == XPathMode::XPath10 {
222 effective_boolean_value_10(&left_val)?
223 } else {
224 effective_boolean_value(&left_val)?
225 };
226 if !left_bool {
227 return Ok(XPathValue::boolean(false));
228 }
229 let right_val = eval_node(arena, bin_op.right, ctx)?;
230 let right_bool = if ctx.static_context.mode() == XPathMode::XPath10 {
231 effective_boolean_value_10(&right_val)?
232 } else {
233 effective_boolean_value(&right_val)?
234 };
235 Ok(XPathValue::boolean(right_bool))
236 }
237 BinaryOpKind::Or => {
238 let left_val = eval_node(arena, bin_op.left, ctx)?;
239 let left_bool = if ctx.static_context.mode() == XPathMode::XPath10 {
240 effective_boolean_value_10(&left_val)?
241 } else {
242 effective_boolean_value(&left_val)?
243 };
244 if left_bool {
245 return Ok(XPathValue::boolean(true));
246 }
247 let right_val = eval_node(arena, bin_op.right, ctx)?;
248 let right_bool = if ctx.static_context.mode() == XPathMode::XPath10 {
249 effective_boolean_value_10(&right_val)?
250 } else {
251 effective_boolean_value(&right_val)?
252 };
253 Ok(XPathValue::boolean(right_bool))
254 }
255
256 BinaryOpKind::Add
258 | BinaryOpKind::Sub
259 | BinaryOpKind::Mul
260 | BinaryOpKind::Div
261 | BinaryOpKind::IDiv
262 | BinaryOpKind::Mod
263 | BinaryOpKind::ValueEq
264 | BinaryOpKind::ValueNe
265 | BinaryOpKind::ValueLt
266 | BinaryOpKind::ValueLe
267 | BinaryOpKind::ValueGt
268 | BinaryOpKind::ValueGe => {
269 let left_val = eval_node(arena, bin_op.left, ctx)?;
270 let right_val = eval_node(arena, bin_op.right, ctx)?;
271
272 let left_opt = atomize_to_single_opt(left_val)?;
273 let right_opt = atomize_to_single_opt(right_val)?;
274
275 match (left_opt, right_opt) {
276 (None, _) | (_, None) => Ok(XPathValue::empty()),
277 (Some(left), Some(right)) => {
278 let is_arithmetic = matches!(
279 bin_op.kind,
280 BinaryOpKind::Add
281 | BinaryOpKind::Sub
282 | BinaryOpKind::Mul
283 | BinaryOpKind::Div
284 | BinaryOpKind::Mod
285 );
286 let result = if is_arithmetic
287 && ctx.static_context.mode() == XPathMode::XPath10
288 {
289 eval_numeric_binary_10(bin_op.kind, &left, &right)?
290 } else {
291 eval_binary(bin_op.kind, &left, &right)?
292 };
293 Ok(XPathValue::from_atomic(result))
294 }
295 }
296 }
297
298 BinaryOpKind::GeneralEq
300 | BinaryOpKind::GeneralNe
301 | BinaryOpKind::GeneralLt
302 | BinaryOpKind::GeneralLe
303 | BinaryOpKind::GeneralGt
304 | BinaryOpKind::GeneralGe => {
305 let left_val = eval_node(arena, bin_op.left, ctx)?;
306 let right_val = eval_node(arena, bin_op.right, ctx)?;
307
308 if ctx.static_context.mode() == XPathMode::XPath10 {
310 let left_is_bool = is_boolean_value(&left_val);
311 let right_is_bool = is_boolean_value(&right_val);
312 let left_has_nodes = has_nodes_or_empty(&left_val);
313 let right_has_nodes = has_nodes_or_empty(&right_val);
314
315 if (left_is_bool && right_has_nodes) || (right_is_bool && left_has_nodes) {
316 let l = effective_boolean_value_10(&left_val)?;
317 let r = effective_boolean_value_10(&right_val)?;
318 let result = match bin_op.kind {
319 BinaryOpKind::GeneralEq => l == r,
320 BinaryOpKind::GeneralNe => l != r,
321 BinaryOpKind::GeneralLt
322 | BinaryOpKind::GeneralLe
323 | BinaryOpKind::GeneralGt
324 | BinaryOpKind::GeneralGe => {
325 let ln = if l { 1.0_f64 } else { 0.0 };
326 let rn = if r { 1.0_f64 } else { 0.0 };
327 match bin_op.kind {
328 BinaryOpKind::GeneralLt => ln < rn,
329 BinaryOpKind::GeneralLe => ln <= rn,
330 BinaryOpKind::GeneralGt => ln > rn,
331 BinaryOpKind::GeneralGe => ln >= rn,
332 _ => unreachable!(),
333 }
334 }
335 _ => unreachable!(),
336 };
337 return Ok(XPathValue::boolean(result));
338 }
339 }
340
341 let left_iter = VecNodeIterator::new(left_val.into_vec());
342 let right_iter = VecNodeIterator::new(right_val.into_vec());
343
344 let result = if ctx.static_context.mode() == XPathMode::XPath10 {
345 match bin_op.kind {
346 BinaryOpKind::GeneralEq => general_eq_iter_10(&left_iter, &right_iter)?,
347 BinaryOpKind::GeneralNe => general_ne_iter_10(&left_iter, &right_iter)?,
348 BinaryOpKind::GeneralLt => general_lt_iter_10(&left_iter, &right_iter)?,
349 BinaryOpKind::GeneralLe => general_le_iter_10(&left_iter, &right_iter)?,
350 BinaryOpKind::GeneralGt => general_gt_iter_10(&left_iter, &right_iter)?,
351 BinaryOpKind::GeneralGe => general_ge_iter_10(&left_iter, &right_iter)?,
352 _ => unreachable!(),
353 }
354 } else {
355 match bin_op.kind {
356 BinaryOpKind::GeneralEq => {
357 general_eq_iter(ctx.static_context, &left_iter, &right_iter)?
358 }
359 BinaryOpKind::GeneralNe => {
360 general_ne_iter(ctx.static_context, &left_iter, &right_iter)?
361 }
362 BinaryOpKind::GeneralLt => {
363 general_lt_iter(ctx.static_context, &left_iter, &right_iter)?
364 }
365 BinaryOpKind::GeneralLe => {
366 general_le_iter(ctx.static_context, &left_iter, &right_iter)?
367 }
368 BinaryOpKind::GeneralGt => {
369 general_gt_iter(ctx.static_context, &left_iter, &right_iter)?
370 }
371 BinaryOpKind::GeneralGe => {
372 general_ge_iter(ctx.static_context, &left_iter, &right_iter)?
373 }
374 _ => unreachable!(),
375 }
376 };
377 Ok(XPathValue::boolean(result))
378 }
379
380 BinaryOpKind::Is | BinaryOpKind::Before | BinaryOpKind::After => {
382 let left_val = eval_node(arena, bin_op.left, ctx)?;
383 let right_val = eval_node(arena, bin_op.right, ctx)?;
384
385 let left_node = extract_single_node(left_val)?;
386 let right_node = extract_single_node(right_val)?;
387
388 match (left_node, right_node) {
390 (Some(left), Some(right)) => {
391 let result = match bin_op.kind {
392 BinaryOpKind::Is => same_node(&left, &right),
393 BinaryOpKind::Before => preceding_node(&left, &right),
394 BinaryOpKind::After => following_node(&left, &right),
395 _ => unreachable!(),
396 };
397 Ok(XPathValue::boolean(result))
398 }
399 _ => Ok(XPathValue::empty()),
400 }
401 }
402
403 BinaryOpKind::Union | BinaryOpKind::Intersect | BinaryOpKind::Except => {
405 let left_val = eval_node(arena, bin_op.left, ctx)?;
406 let right_val = eval_node(arena, bin_op.right, ctx)?;
407
408 let left_vec = left_val.into_vec();
409 let right_vec = right_val.into_vec();
410
411 let result = match bin_op.kind {
412 BinaryOpKind::Union => union_nodes(left_vec, right_vec)?,
413 BinaryOpKind::Intersect => intersect_nodes(left_vec, right_vec)?,
414 BinaryOpKind::Except => except_nodes(left_vec, right_vec)?,
415 _ => unreachable!(),
416 };
417 Ok(XPathValue::from_sequence(result))
418 }
419 }
420 }
421
422 AstNode::PathStep(_) => {
423 Err(XPathError::Internal(
425 "PathStep should not be evaluated directly".to_string(),
426 ))
427 }
428
429 AstNode::TypeExpr(type_expr) => eval_type_expr(arena, type_expr, ctx),
430 }
431}
432
433fn eval_type_expr<N: DomNavigator>(
439 arena: &AstArena,
440 type_expr: &TypeExprNode,
441 ctx: &mut DynamicContext<'_, N>,
442) -> Result<XPathValue<N>, XPathError> {
443 let operand = eval_node(arena, type_expr.operand, ctx)?;
445
446 match type_expr.kind {
447 TypeExprKind::InstanceOf => eval_instance_of(operand, type_expr, ctx),
448 TypeExprKind::TreatAs => eval_treat_as(operand, type_expr, ctx),
449 TypeExprKind::CastAs => eval_cast_as(operand, type_expr, ctx),
450 TypeExprKind::CastableAs => eval_castable_as(operand, type_expr, ctx),
451 }
452}
453
454fn eval_instance_of<N: DomNavigator>(
458 operand: XPathValue<N>,
459 type_expr: &TypeExprNode,
460 ctx: &DynamicContext<'_, N>,
461) -> Result<XPathValue<N>, XPathError> {
462 let items = operand.into_vec();
463 let count = items.len();
464
465 if type_expr.target_type.item_type.is_none() {
467 return Ok(XPathValue::boolean(count == 0));
468 }
469
470 if !occurrence_allows_count(type_expr.target_type.occurrence, count) {
472 return Ok(XPathValue::boolean(false));
473 }
474
475 let item_type = type_expr.target_type.item_type.as_ref().unwrap();
477
478 for item in &items {
480 if !matches_item_type_node(
481 item,
482 item_type,
483 type_expr.resolved_atomic_type.as_ref(),
484 ctx.static_context,
485 ) {
486 return Ok(XPathValue::boolean(false));
487 }
488 }
489
490 Ok(XPathValue::boolean(true))
491}
492
493fn eval_treat_as<N: DomNavigator>(
497 operand: XPathValue<N>,
498 type_expr: &TypeExprNode,
499 ctx: &DynamicContext<'_, N>,
500) -> Result<XPathValue<N>, XPathError> {
501 let items = operand.into_vec();
502 let count = items.len();
503
504 if !occurrence_allows_count(type_expr.target_type.occurrence, count) {
506 return Err(XPathError::XPTY0004 {
507 expected: format_sequence_type(&type_expr.target_type, ctx),
508 found: format!("sequence of {} items", count),
509 });
510 }
511
512 let item_type = match &type_expr.target_type.item_type {
514 None => {
515 if count == 0 {
517 return Ok(XPathValue::empty());
518 } else {
519 return Err(XPathError::XPTY0004 {
520 expected: "empty-sequence()".to_string(),
521 found: format!("sequence of {} items", count),
522 });
523 }
524 }
525 Some(it) => it,
526 };
527
528 for item in &items {
530 if !matches_item_type_node(
531 item,
532 item_type,
533 type_expr.resolved_atomic_type.as_ref(),
534 ctx.static_context,
535 ) {
536 return Err(XPathError::XPTY0004 {
537 expected: format_sequence_type(&type_expr.target_type, ctx),
538 found: format_item_type(item),
539 });
540 }
541 }
542
543 Ok(XPathValue::from_sequence(items))
545}
546
547fn eval_cast_as<N: DomNavigator>(
551 operand: XPathValue<N>,
552 type_expr: &TypeExprNode,
553 ctx: &DynamicContext<'_, N>,
554) -> Result<XPathValue<N>, XPathError> {
555 let item_type =
557 type_expr
558 .target_type
559 .item_type
560 .as_ref()
561 .ok_or_else(|| XPathError::XPTY0004 {
562 expected: "atomic type".to_string(),
563 found: "empty-sequence()".to_string(),
564 })?;
565
566 if !matches!(item_type, ItemTypeNode::Atomic(_)) {
568 return Err(XPathError::XPTY0004 {
569 expected: "atomic type".to_string(),
570 found: "non-atomic type".to_string(),
571 });
572 }
573
574 let atomic_opt = atomize_to_single_opt(operand)?;
576
577 let allows_empty = matches!(
579 type_expr.target_type.occurrence,
580 OccurrenceIndicator::ZeroOrOne | OccurrenceIndicator::ZeroOrMore
581 );
582
583 match atomic_opt {
584 None => {
585 if allows_empty {
586 Ok(XPathValue::empty())
587 } else {
588 Err(XPathError::XPTY0004 {
589 expected: format_sequence_type(&type_expr.target_type, ctx),
590 found: "empty-sequence()".to_string(),
591 })
592 }
593 }
594 Some(value) => {
595 let qname = type_expr
597 .resolved_atomic_type
598 .as_ref()
599 .ok_or_else(|| XPathError::Internal("Cast target type not resolved".to_string()))?;
600 let target_type = resolved_type_to_type_code(qname, ctx.static_context.names)?;
601
602 let result = if matches!(target_type, XmlTypeCode::QName | XmlTypeCode::Notation) {
604 cast_to_qname_with_context(ctx.static_context, &value, target_type)?
605 } else {
606 cast_to(&value, target_type)?
607 };
608 Ok(XPathValue::from_atomic(result))
609 }
610 }
611}
612
613fn eval_castable_as<N: DomNavigator>(
617 operand: XPathValue<N>,
618 type_expr: &TypeExprNode,
619 ctx: &DynamicContext<'_, N>,
620) -> Result<XPathValue<N>, XPathError> {
621 let item_type = type_expr.target_type.item_type.as_ref();
623 if !matches!(item_type, Some(ItemTypeNode::Atomic(_))) {
624 return Ok(XPathValue::boolean(false));
625 }
626
627 let atomic_opt = match atomize_to_single_opt(operand) {
629 Ok(opt) => opt,
630 Err(_) => return Ok(XPathValue::boolean(false)), };
632
633 let allows_empty = matches!(
635 type_expr.target_type.occurrence,
636 OccurrenceIndicator::ZeroOrOne | OccurrenceIndicator::ZeroOrMore
637 );
638
639 match atomic_opt {
640 None => {
641 Ok(XPathValue::boolean(allows_empty))
643 }
644 Some(value) => {
645 let qname = match type_expr.resolved_atomic_type.as_ref() {
647 Some(q) => q,
648 None => return Ok(XPathValue::boolean(false)),
649 };
650 let target_type = match resolved_type_to_type_code(qname, ctx.static_context.names) {
651 Ok(tc) => tc,
652 Err(_) => return Ok(XPathValue::boolean(false)),
653 };
654
655 let is_castable = if matches!(target_type, XmlTypeCode::QName | XmlTypeCode::Notation) {
657 cast_to_qname_with_context(ctx.static_context, &value, target_type).is_ok()
658 } else {
659 castable(&value, target_type)
660 };
661 Ok(XPathValue::boolean(is_castable))
662 }
663 }
664}
665
666fn format_sequence_type<N: DomNavigator>(
668 seq_type: &crate::xpath::ast::SequenceTypeNode,
669 _ctx: &DynamicContext<'_, N>,
670) -> String {
671 let item_str = match &seq_type.item_type {
672 None => "empty-sequence()".to_string(),
673 Some(ItemTypeNode::Item) => "item()".to_string(),
674 Some(ItemTypeNode::Atomic(qname)) => {
675 if qname.prefix.is_empty() {
676 qname.local.clone()
677 } else {
678 format!("{}:{}", qname.prefix, qname.local)
679 }
680 }
681 Some(ItemTypeNode::Kind(kind)) => format_kind_test(kind),
682 };
683
684 let occ_str = match seq_type.occurrence {
685 OccurrenceIndicator::One => "",
686 OccurrenceIndicator::ZeroOrOne => "?",
687 OccurrenceIndicator::ZeroOrMore => "*",
688 OccurrenceIndicator::OneOrMore => "+",
689 };
690
691 format!("{}{}", item_str, occ_str)
692}
693
694fn format_kind_test(kind: &crate::xpath::ast::KindTest) -> String {
696 use crate::xpath::ast::KindTest;
697 match kind {
698 KindTest::AnyKind => "node()".to_string(),
699 KindTest::Text => "text()".to_string(),
700 KindTest::Comment => "comment()".to_string(),
701 KindTest::ProcessingInstruction(None) => "processing-instruction()".to_string(),
702 KindTest::ProcessingInstruction(Some(name)) => {
703 format!("processing-instruction('{}')", name)
704 }
705 KindTest::Document(None) => "document-node()".to_string(),
706 KindTest::Document(Some(inner)) => {
707 format!("document-node({})", format_kind_test(inner))
708 }
709 KindTest::Element(test) => {
710 if let Some(ref qname) = test.name {
711 if qname.prefix.is_empty() {
712 format!("element({})", qname.local)
713 } else {
714 format!("element({}:{})", qname.prefix, qname.local)
715 }
716 } else {
717 "element()".to_string()
718 }
719 }
720 KindTest::Attribute(test) => {
721 if let Some(ref qname) = test.name {
722 if qname.prefix.is_empty() {
723 format!("attribute({})", qname.local)
724 } else {
725 format!("attribute({}:{})", qname.prefix, qname.local)
726 }
727 } else {
728 "attribute()".to_string()
729 }
730 }
731 KindTest::SchemaElement(name) => format!("schema-element({})", name),
732 KindTest::SchemaAttribute(name) => format!("schema-attribute({})", name),
733 }
734}
735
736fn format_item_type<N: DomNavigator>(item: &XmlItem<N>) -> String {
738 match item {
739 XmlItem::Node(nav) => {
740 use crate::xpath::DomNodeType;
741 match nav.node_type() {
742 DomNodeType::Root => "document-node()".to_string(),
743 DomNodeType::Element => format!("element({})", nav.local_name()),
744 DomNodeType::Attribute => format!("attribute({})", nav.local_name()),
745 DomNodeType::Text
746 | DomNodeType::Whitespace
747 | DomNodeType::SignificantWhitespace => "text()".to_string(),
748 DomNodeType::Comment => "comment()".to_string(),
749 DomNodeType::ProcessingInstruction => "processing-instruction()".to_string(),
750 DomNodeType::Namespace => "namespace-node()".to_string(),
751 DomNodeType::All => "node()".to_string(),
752 }
753 }
754 XmlItem::Atomic(value) => {
755 format!("{:?}", value.type_code)
756 }
757 }
758}
759
760fn eval_path_expr<N: DomNavigator>(
772 arena: &AstArena,
773 path_expr: &PathExprNode,
774 ctx: &mut DynamicContext<'_, N>,
775) -> Result<XPathValue<N>, XPathError> {
776 if path_expr.is_absolute && path_expr.steps.is_empty() {
778 let context_node = ctx.require_context_node()?;
779 let root = get_root(context_node);
780 return Ok(XPathValue::from_node(root));
781 }
782
783 let first_is_primary = path_expr.steps.first().is_some_and(|&step_id| {
791 matches!(
792 arena.get(step_id),
793 AstNode::FilterExpr(_)
794 | AstNode::FunctionCall(_)
795 | AstNode::Value(_)
796 | AstNode::Expr(_)
797 | AstNode::VarRef(_)
798 | AstNode::TypeExpr(_)
799 | AstNode::ContextItem(_)
800 )
801 });
802
803 let starting_nodes: Vec<N> = if path_expr.is_absolute {
805 let context_node = ctx.require_context_node()?;
807 vec![get_root(context_node)]
808 } else if first_is_primary {
809 Vec::new()
811 } else {
812 let context_node = ctx.require_context_node()?;
814 vec![context_node.clone()]
815 };
816
817 let needs_doc_order = path_needs_document_order(arena, path_expr);
820
821 let mut current_nodes: Vec<XmlItem<N>> =
823 starting_nodes.into_iter().map(XmlItem::Node).collect();
824
825 for (step_idx, &step_id) in path_expr.steps.iter().enumerate() {
826 let step_node = arena.get(step_id);
827
828 current_nodes = match step_node {
829 AstNode::PathStep(path_step) => {
830 eval_path_step(arena, path_step, current_nodes, ctx, step_idx == 0)?
831 }
832 AstNode::FilterExpr(filter_expr) => {
833 if step_idx == 0 {
835 let result = eval_filter_expr(arena, filter_expr, ctx)?;
837 result.into_vec()
838 } else {
839 let mut results = Vec::new();
841 for item in current_nodes {
842 let saved_context = ctx.context_item.take();
844 let saved_pos = ctx.context_position;
845 let saved_size = ctx.context_size;
846
847 ctx.context_item = Some(item);
848 ctx.context_position = 1;
849 ctx.context_size = 1;
850
851 let step_result = eval_filter_expr(arena, filter_expr, ctx)?;
852 results.extend(step_result.into_vec());
853
854 ctx.context_item = saved_context;
855 ctx.context_position = saved_pos;
856 ctx.context_size = saved_size;
857 }
858 results
859 }
860 }
861 _ => {
862 if step_idx == 0 && current_nodes.is_empty() {
864 let result = eval_node(arena, step_id, ctx)?;
867 result.into_vec()
868 } else {
869 let mut results = Vec::new();
871 for item in current_nodes {
872 let saved_context = ctx.context_item.take();
873 let saved_pos = ctx.context_position;
874 let saved_size = ctx.context_size;
875
876 ctx.context_item = Some(item);
877 ctx.context_position = 1;
878 ctx.context_size = 1;
879
880 let step_result = eval_node(arena, step_id, ctx)?;
881 results.extend(step_result.into_vec());
882
883 ctx.context_item = saved_context;
884 ctx.context_position = saved_pos;
885 ctx.context_size = saved_size;
886 }
887 results
888 }
889 }
890 };
891
892 if current_nodes.is_empty() {
894 return Ok(XPathValue::empty());
895 }
896 }
897
898 if needs_doc_order && !current_nodes.is_empty() {
900 let iter = VecNodeIterator::new(current_nodes);
901 let doc_order_iter = DocumentOrderNodeIterator::new(iter)?;
902 let mut doc_order_iter = doc_order_iter;
903 current_nodes = collect_iterator(&mut doc_order_iter)?;
904 }
905
906 Ok(XPathValue::from_sequence(current_nodes))
907}
908
909fn path_needs_document_order(arena: &AstArena, path_expr: &PathExprNode) -> bool {
917 let len = path_expr.steps.len();
918 for (idx, &step_id) in path_expr.steps.iter().enumerate() {
919 match arena.get(step_id) {
920 AstNode::PathStep(step) => {
921 if step.axis.is_reverse() {
923 return true;
924 }
925 if matches!(
928 step.axis,
929 Axis::Descendant | Axis::DescendantOrSelf | Axis::Following
930 ) {
931 for s in (idx + 1)..len {
932 if let AstNode::PathStep(next_step) = arena.get(path_expr.steps[s]) {
933 if !matches!(next_step.axis, Axis::Attribute | Axis::Namespace) {
934 return true;
935 }
936 }
937 }
938 }
939 }
940 AstNode::FilterExpr(_) if idx > 0 => {
941 return true;
942 }
943 _ => {}
944 }
945 }
946 false
947}
948
949fn eval_path_step<N: DomNavigator>(
951 arena: &AstArena,
952 step: &PathStepNode,
953 input_nodes: Vec<XmlItem<N>>,
954 ctx: &mut DynamicContext<'_, N>,
955 _is_first_step: bool,
956) -> Result<Vec<XmlItem<N>>, XPathError> {
957 let nodes: Vec<N> = input_nodes
959 .into_iter()
960 .map(|item| match item {
961 XmlItem::Node(n) => Ok(n),
962 XmlItem::Atomic(_) => Err(XPathError::XPTY0019),
963 })
964 .collect::<Result<Vec<_>, _>>()?;
965
966 if nodes.is_empty() {
967 return Ok(Vec::new());
968 }
969
970 let node_test = step_to_node_test(step, ctx.static_context);
972
973 let base_iter = VecNodeIterator::new(nodes.into_iter().map(XmlItem::Node).collect());
975
976 let xpath_ctx = ctx.static_context.clone();
978 let stepped_items = apply_axis_iterator(step.axis, node_test, xpath_ctx, base_iter)?;
979
980 if step.predicates.is_empty() {
982 Ok(stepped_items)
983 } else {
984 eval_predicates(arena, &step.predicates, ctx, stepped_items)
985 }
986}
987
988fn step_to_node_test(step: &PathStepNode, ctx: &XPathContext<'_>) -> Option<NodeTest> {
990 if let Some(ref resolved) = step.resolved_test {
992 return Some(NodeTest::Name(resolved.clone()));
993 }
994
995 match &step.test {
997 AstNodeTest::Name(name_test) => {
998 match (&name_test.prefix, &name_test.local_name) {
1000 (None, None) => {
1001 Some(NodeTest::Name(RuntimeNameTest::Wildcard))
1003 }
1004 (None, Some(local)) => {
1005 let local_id = ctx.names.add(local);
1007 Some(NodeTest::Name(RuntimeNameTest::NamespaceWildcard(local_id)))
1008 }
1009 (Some(prefix), None) => {
1010 if let Some(ns_uri) = ctx.resolve_prefix(prefix) {
1012 let ns_id = ctx.names.add(&ns_uri);
1013 Some(NodeTest::Name(RuntimeNameTest::LocalWildcard(ns_id)))
1014 } else {
1015 None }
1017 }
1018 (Some(prefix), Some(local)) => {
1019 let local_id = ctx.names.add(local);
1021 let ns_uri = if prefix.is_empty() {
1022 ctx.default_element_ns
1023 } else {
1024 ctx.resolve_prefix(prefix).map(|s| ctx.names.add(&s))
1025 };
1026 let qname = crate::namespace::qname::QualifiedName::new(ns_uri, local_id, None);
1027 Some(NodeTest::Name(RuntimeNameTest::QName(qname)))
1028 }
1029 }
1030 }
1031 AstNodeTest::Kind(kind_test) => {
1032 let seq_type = kind_test_to_sequence_type(kind_test);
1034 Some(NodeTest::Type(seq_type))
1035 }
1036 }
1037}
1038
1039fn kind_test_to_sequence_type(kind: &KindTest) -> SequenceType {
1041 match kind {
1042 KindTest::AnyKind => SequenceType::node(),
1043 KindTest::Text => SequenceType::one(ItemType::Text),
1044 KindTest::Comment => SequenceType::one(ItemType::Comment),
1045 KindTest::ProcessingInstruction(target) => {
1046 SequenceType::one(ItemType::ProcessingInstruction(target.clone()))
1047 }
1048 KindTest::Document(inner) => {
1049 let inner_type = inner.as_ref().map(|k| Box::new(kind_test_to_item_type(k)));
1050 SequenceType::one(ItemType::Document(inner_type))
1051 }
1052 KindTest::Element(_) => {
1053 SequenceType::one(ItemType::Element(None, None))
1056 }
1057 KindTest::Attribute(_) => {
1058 SequenceType::one(ItemType::Attribute(None, None))
1060 }
1061 KindTest::SchemaElement(_) | KindTest::SchemaAttribute(_) => {
1062 SequenceType::node()
1064 }
1065 }
1066}
1067
1068fn kind_test_to_item_type(kind: &KindTest) -> ItemType {
1070 match kind {
1071 KindTest::AnyKind => ItemType::AnyNode,
1072 KindTest::Text => ItemType::Text,
1073 KindTest::Comment => ItemType::Comment,
1074 KindTest::ProcessingInstruction(target) => ItemType::ProcessingInstruction(target.clone()),
1075 KindTest::Document(inner) => {
1076 let inner_type = inner.as_ref().map(|k| Box::new(kind_test_to_item_type(k)));
1077 ItemType::Document(inner_type)
1078 }
1079 KindTest::Element(_) => ItemType::Element(None, None),
1080 KindTest::Attribute(_) => ItemType::Attribute(None, None),
1081 KindTest::SchemaElement(_) | KindTest::SchemaAttribute(_) => ItemType::AnyNode,
1082 }
1083}
1084
1085fn apply_axis_iterator<N: DomNavigator>(
1087 axis: Axis,
1088 node_test: Option<NodeTest>,
1089 ctx: XPathContext<'_>,
1090 base_iter: VecNodeIterator<N>,
1091) -> Result<Vec<XmlItem<N>>, XPathError> {
1092 match axis {
1093 Axis::Child => {
1094 let mut iter =
1095 SequentialAxisNodeIterator::new(ctx, node_test, false, base_iter, ChildAxis);
1096 collect_iterator(&mut iter)
1097 }
1098 Axis::Descendant => {
1099 let mut iter = DescendantNodeIterator::new(ctx, node_test, false, base_iter);
1100 collect_iterator(&mut iter)
1101 }
1102 Axis::DescendantOrSelf => {
1103 let mut iter = DescendantNodeIterator::new(ctx, node_test, true, base_iter);
1104 collect_iterator(&mut iter)
1105 }
1106 Axis::Attribute => {
1107 let mut iter =
1108 SequentialAxisNodeIterator::new(ctx, node_test, false, base_iter, AttributeAxis);
1109 collect_iterator(&mut iter)
1110 }
1111 Axis::SelfAxis => {
1112 let mut iter =
1114 SequentialAxisNodeIterator::new(ctx, node_test, false, base_iter, SelfAxis);
1115 collect_iterator(&mut iter)
1116 }
1117 Axis::Parent => {
1118 let mut iter =
1119 SequentialAxisNodeIterator::new(ctx, node_test, false, base_iter, ParentAxis);
1120 collect_iterator(&mut iter)
1121 }
1122 Axis::Ancestor => {
1123 let mut iter =
1124 SequentialAxisNodeIterator::new(ctx, node_test, false, base_iter, AncestorAxis);
1125 collect_iterator(&mut iter)
1126 }
1127 Axis::AncestorOrSelf => {
1128 let mut iter =
1129 SequentialAxisNodeIterator::new(ctx, node_test, true, base_iter, AncestorAxis);
1130 collect_iterator(&mut iter)
1131 }
1132 Axis::FollowingSibling => {
1133 let mut iter = SequentialAxisNodeIterator::new(
1134 ctx,
1135 node_test,
1136 false,
1137 base_iter,
1138 FollowingSiblingAxis,
1139 );
1140 collect_iterator(&mut iter)
1141 }
1142 Axis::PrecedingSibling => {
1143 let mut iter = SequentialAxisNodeIterator::new(
1144 ctx,
1145 node_test,
1146 false,
1147 base_iter,
1148 PrecedingSiblingAxis,
1149 );
1150 collect_iterator(&mut iter)
1151 }
1152 Axis::Following => {
1153 let mut iter = FollowingNodeIterator::new(ctx, node_test, base_iter);
1154 collect_iterator(&mut iter)
1155 }
1156 Axis::Preceding => {
1157 let mut iter = PrecedingNodeIterator::new(ctx, node_test, base_iter);
1158 collect_iterator(&mut iter)
1159 }
1160 Axis::Namespace => {
1161 let mut iter = SequentialAxisNodeIterator::new(
1162 ctx,
1163 node_test,
1164 false,
1165 base_iter,
1166 NamespaceAxis::default(),
1167 );
1168 collect_iterator(&mut iter)
1169 }
1170 }
1171}
1172
1173fn collect_iterator<I: XmlNodeIterator>(
1175 iter: &mut I,
1176) -> Result<Vec<XmlItem<I::Navigator>>, XPathError> {
1177 let mut results = Vec::new();
1178 while iter.move_next()? {
1179 if let Some(item_ref) = iter.current() {
1180 let item = match item_ref {
1181 crate::xpath::iterator::XmlItemRef::Node(n) => XmlItem::Node(n.clone()),
1182 crate::xpath::iterator::XmlItemRef::Atomic(v) => XmlItem::Atomic(v.clone()),
1183 };
1184 results.push(item);
1185 }
1186 }
1187 Ok(results)
1188}
1189
1190fn eval_filter_expr<N: DomNavigator>(
1196 arena: &AstArena,
1197 filter_expr: &FilterExprNode,
1198 ctx: &mut DynamicContext<'_, N>,
1199) -> Result<XPathValue<N>, XPathError> {
1200 let base_value = eval_node(arena, filter_expr.base, ctx)?;
1202
1203 if filter_expr.predicates.is_empty() {
1205 return Ok(base_value);
1206 }
1207
1208 let items = base_value.into_vec();
1210 let filtered = eval_predicates(arena, &filter_expr.predicates, ctx, items)?;
1211 Ok(XPathValue::from_sequence(filtered))
1212}
1213
1214fn eval_predicates<N: DomNavigator>(
1220 arena: &AstArena,
1221 predicates: &[AstNodeId],
1222 ctx: &mut DynamicContext<'_, N>,
1223 mut items: Vec<XmlItem<N>>,
1224) -> Result<Vec<XmlItem<N>>, XPathError> {
1225 for &pred_id in predicates {
1226 if items.is_empty() {
1227 break;
1228 }
1229
1230 let size = items.len();
1231 let mut filtered = Vec::new();
1232
1233 for (idx, item) in items.into_iter().enumerate() {
1234 let position = idx + 1; let saved_item = ctx.context_item.take();
1238 let saved_pos = ctx.context_position;
1239 let saved_size = ctx.context_size;
1240
1241 ctx.context_item = Some(item.clone());
1243 ctx.context_position = position;
1244 ctx.context_size = size;
1245
1246 let pred_result = eval_node(arena, pred_id, ctx)?;
1248
1249 ctx.context_item = saved_item;
1251 ctx.context_position = saved_pos;
1252 ctx.context_size = saved_size;
1253
1254 let is_10 = ctx.static_context.mode() == XPathMode::XPath10;
1256 let include = match &pred_result {
1257 XPathValue::Item(XmlItem::Atomic(value)) if value.type_code.is_numeric() => {
1258 let num = crate::xpath::atomize::to_number(value);
1260 if num.is_nan() {
1261 false
1262 } else {
1263 (position as f64) == num
1264 }
1265 }
1266 _ => {
1267 if is_10 {
1268 effective_boolean_value_10(&pred_result)?
1269 } else {
1270 effective_boolean_value(&pred_result)?
1271 }
1272 }
1273 };
1274
1275 if include {
1276 filtered.push(item);
1277 }
1278 }
1279
1280 items = filtered;
1281 }
1282
1283 Ok(items)
1284}
1285
1286use std::ops::ControlFlow;
1287
1288fn eval_for_expression<N: DomNavigator>(
1300 arena: &AstArena,
1301 for_node: &ForNode,
1302 ctx: &mut DynamicContext<'_, N>,
1303) -> Result<XPathValue<N>, XPathError> {
1304 let mut results: Vec<XmlItem<N>> = Vec::new();
1306
1307 match eval_for_bindings(
1310 arena,
1311 &for_node.bindings,
1312 0,
1313 for_node.return_expr,
1314 ctx,
1315 &mut |result| match result {
1316 Ok(value) => {
1317 results.extend(value.into_vec());
1318 ControlFlow::Continue(())
1319 }
1320 Err(e) => ControlFlow::Break(e),
1321 },
1322 ) {
1323 ControlFlow::Continue(()) => {}
1324 ControlFlow::Break(e) => return Err(e),
1325 }
1326
1327 Ok(XPathValue::from_sequence(results))
1328}
1329
1330fn eval_for_bindings<N: DomNavigator, B>(
1346 arena: &AstArena,
1347 bindings: &[ForBinding],
1348 binding_index: usize,
1349 body_id: AstNodeId,
1350 ctx: &mut DynamicContext<'_, N>,
1351 collector: &mut impl FnMut(Result<XPathValue<N>, XPathError>) -> ControlFlow<B>,
1352) -> ControlFlow<B> {
1353 if binding_index >= bindings.len() {
1354 let result = eval_node(arena, body_id, ctx);
1356 return collector(result);
1357 }
1358
1359 let binding = &bindings[binding_index];
1360 let slot = match binding.slot {
1361 Some(s) => s,
1362 None => {
1363 return collector(Err(XPathError::Internal(
1364 "For binding slot not assigned".to_string(),
1365 )))
1366 }
1367 };
1368
1369 let seq_value = match eval_node(arena, binding.in_expr, ctx) {
1371 Ok(v) => v,
1372 Err(e) => return collector(Err(e)),
1373 };
1374 let items = seq_value.into_vec();
1375
1376 if items.is_empty() {
1378 return ControlFlow::Continue(());
1379 }
1380
1381 for item in items {
1383 ctx.set_variable(slot, XPathValue::from_item(item));
1385
1386 if let cf @ ControlFlow::Break(_) =
1388 eval_for_bindings(arena, bindings, binding_index + 1, body_id, ctx, collector)
1389 {
1390 return cf;
1391 }
1392 }
1393
1394 ControlFlow::Continue(())
1395}
1396
1397enum QuantifiedExit {
1406 ShortCircuit,
1408 Error(XPathError),
1410}
1411
1412fn eval_quantified_expression<N: DomNavigator>(
1422 arena: &AstArena,
1423 quant_node: &QuantifiedNode,
1424 ctx: &mut DynamicContext<'_, N>,
1425) -> Result<XPathValue<N>, XPathError> {
1426 let mut had_any_iteration = false;
1428 let mut found_some = false;
1429 let mut all_satisfied = true;
1430
1431 match eval_for_bindings(
1434 arena,
1435 &quant_node.bindings,
1436 0,
1437 quant_node.satisfies,
1438 ctx,
1439 &mut |result| {
1440 had_any_iteration = true;
1441 match result {
1442 Ok(value) => match effective_boolean_value(&value) {
1443 Ok(satisfied) => {
1444 match quant_node.kind {
1445 QuantifierKind::Some => {
1446 if satisfied {
1447 found_some = true;
1448 return ControlFlow::Break(QuantifiedExit::ShortCircuit);
1449 }
1450 }
1451 QuantifierKind::Every => {
1452 if !satisfied {
1453 all_satisfied = false;
1454 return ControlFlow::Break(QuantifiedExit::ShortCircuit);
1455 }
1456 }
1457 }
1458 ControlFlow::Continue(())
1459 }
1460 Err(e) => ControlFlow::Break(QuantifiedExit::Error(e)),
1461 },
1462 Err(e) => ControlFlow::Break(QuantifiedExit::Error(e)),
1463 }
1464 },
1465 ) {
1466 ControlFlow::Continue(()) => {} ControlFlow::Break(QuantifiedExit::ShortCircuit) => {} ControlFlow::Break(QuantifiedExit::Error(e)) => return Err(e),
1469 }
1470
1471 if !had_any_iteration {
1474 return match quant_node.kind {
1475 QuantifierKind::Some => Ok(XPathValue::boolean(false)),
1476 QuantifierKind::Every => Ok(XPathValue::boolean(true)),
1477 };
1478 }
1479
1480 match quant_node.kind {
1481 QuantifierKind::Some => Ok(XPathValue::boolean(found_some)),
1482 QuantifierKind::Every => Ok(XPathValue::boolean(all_satisfied)),
1483 }
1484}
1485
1486fn eval_value<N: DomNavigator>(
1488 value: &ValueNode,
1489 mode: XPathMode,
1490) -> Result<XPathValue<N>, XPathError> {
1491 match value {
1492 ValueNode::Empty => Ok(XPathValue::empty()),
1493
1494 ValueNode::String(s) => Ok(XPathValue::string(s.clone())),
1495
1496 ValueNode::Boolean(b) => Ok(XPathValue::boolean(*b)),
1497
1498 ValueNode::Integer(s) => {
1499 let i: num_bigint::BigInt = s.parse().map_err(|_| XPathError::FORG0001 {
1501 value: s.clone(),
1502 target_type: "xs:integer".to_string(),
1503 })?;
1504 Ok(XPathValue::integer(i))
1505 }
1506
1507 ValueNode::Decimal(s) => {
1508 if mode == XPathMode::XPath10 {
1509 let d: f64 = s.parse().unwrap_or(f64::NAN);
1511 Ok(XPathValue::double(d))
1512 } else {
1513 let d: rust_decimal::Decimal = s.parse().map_err(|_| XPathError::FORG0001 {
1514 value: s.clone(),
1515 target_type: "xs:decimal".to_string(),
1516 })?;
1517 Ok(XPathValue::decimal(d))
1518 }
1519 }
1520
1521 ValueNode::Double(s) => {
1522 let d: f64 = s.parse().unwrap_or(f64::NAN);
1523 Ok(XPathValue::double(d))
1524 }
1525
1526 ValueNode::Typed(xml_value) => Ok(XPathValue::from_atomic(xml_value.clone())),
1527 }
1528}
1529
1530fn extract_single_node<N: DomNavigator>(value: XPathValue<N>) -> Result<Option<N>, XPathError> {
1534 match value {
1535 XPathValue::Empty => Ok(None),
1536 XPathValue::Item(XmlItem::Node(node)) => Ok(Some(node)),
1537 XPathValue::Item(XmlItem::Atomic(_)) => Err(XPathError::XPTY0004 {
1538 expected: "node()".to_string(),
1539 found: "atomic value".to_string(),
1540 }),
1541 XPathValue::Sequence(items) => {
1542 if items.len() == 1 {
1543 match items.into_iter().next().unwrap() {
1544 XmlItem::Node(node) => Ok(Some(node)),
1545 XmlItem::Atomic(_) => Err(XPathError::XPTY0004 {
1546 expected: "node()".to_string(),
1547 found: "atomic value".to_string(),
1548 }),
1549 }
1550 } else if items.is_empty() {
1551 Ok(None)
1552 } else {
1553 Err(XPathError::more_than_one_item())
1554 }
1555 }
1556 }
1557}
1558
1559fn is_boolean_value<N: DomNavigator>(val: &XPathValue<N>) -> bool {
1561 matches!(val, XPathValue::Item(XmlItem::Atomic(v)) if v.type_code == crate::types::XmlTypeCode::Boolean)
1562}
1563
1564fn has_nodes_or_empty<N: DomNavigator>(val: &XPathValue<N>) -> bool {
1569 match val {
1570 XPathValue::Empty => true,
1571 XPathValue::Item(XmlItem::Node(_)) => true,
1572 XPathValue::Sequence(items) => items.iter().any(|i| matches!(i, XmlItem::Node(_))),
1573 _ => false,
1574 }
1575}
1576
1577#[cfg(test)]
1578#[path = "eval_tests.rs"]
1579mod eval_tests;