1use crate::abi::structs::_xmlNode;
16use crate::xml::xpath::ast::{Axis, BinaryOp, Expr, NameTest, NodeTest, Step};
17use crate::xml::xpath::axes;
18use crate::xml::xpath::context::XPathContext;
19use crate::xml::xpath::functions;
20use crate::xml::xpath::types::{node_string_value, string_to_number, NodeSet, XPathValue};
21use std::collections::HashMap;
22
23pub fn eval(ctx: &mut XPathContext, expr: &Expr) -> Result<XPathValue, String> {
29 ctx.push_recursion()?;
30
31 let result = match expr {
32 Expr::Step(step) => eval_step(ctx, step),
33 Expr::AbsolutePath(expr) => eval_absolute_path(ctx, expr),
34 Expr::RelativePath(left, right) => eval_relative_path(ctx, left, right),
35 Expr::Filter(expr, predicates) => eval_filter(ctx, expr, predicates),
36 Expr::Variable(name) => eval_variable(ctx, name),
37 Expr::StringLiteral(s) => Ok(XPathValue::String(s.clone())),
38 Expr::NumberLiteral(n) => Ok(XPathValue::Number(*n)),
39 Expr::BooleanLiteral(b) => Ok(XPathValue::Boolean(*b)),
40 Expr::FunctionCall { name, args } => eval_function_call(ctx, name, args),
41 Expr::BinaryOp { op, left, right } => eval_binary_op(ctx, op, left, right),
42 Expr::UnaryMinus(expr) => {
43 let val = eval(ctx, expr)?;
44 Ok(XPathValue::Number(-val.as_number()))
45 }
46 Expr::Union(left, right) => eval_union(ctx, left, right),
47 };
48
49 ctx.pop_recursion();
50 result
51}
52
53fn eval_absolute_path(ctx: &mut XPathContext, expr: &Expr) -> Result<XPathValue, String> {
59 let doc = ctx.document;
61 if doc.is_null() {
62 return Ok(XPathValue::NodeSet(NodeSet::new()));
63 }
64
65 unsafe {
66 let root = (*doc).children;
67 let mut doc_node = root;
69 while !doc_node.is_null() && (*doc_node).type_ != 9 {
70 doc_node = (*doc_node).next;
71 }
72 if doc_node.is_null() {
73 doc_node = root;
74 }
75
76 let saved_node = ctx.context_node;
78 let saved_list = ctx.context_list.clone();
79 ctx.context_node = doc_node;
80 ctx.set_context_list(vec![doc_node]);
81
82 let result = eval(ctx, expr);
83
84 ctx.context_node = saved_node;
85 ctx.context_list = saved_list;
86
87 result
88 }
89}
90
91fn eval_relative_path(
93 ctx: &mut XPathContext,
94 left: &Expr,
95 right: &Expr,
96) -> Result<XPathValue, String> {
97 let left_val = eval(ctx, left)?;
99 let left_ns = left_val.as_node_set().clone();
100
101 let mut result = NodeSet::new();
102
103 for node in left_ns.iter() {
104 let saved_node = ctx.context_node;
106 let saved_list = ctx.context_list.clone();
107 ctx.context_node = node;
108 ctx.set_context_list(left_ns.iter().collect());
109
110 match eval(ctx, right) {
111 Ok(val) => {
112 if let XPathValue::NodeSet(ns) = val {
113 for n in ns.iter() {
114 result.push(n);
115 }
116 }
117 }
118 Err(e) => {
119 ctx.context_node = saved_node;
120 ctx.context_list = saved_list;
121 return Err(e);
122 }
123 }
124
125 ctx.context_node = saved_node;
126 ctx.context_list = saved_list;
127 }
128
129 Ok(XPathValue::NodeSet(result))
130}
131
132fn eval_step(ctx: &mut XPathContext, step: &Step) -> Result<XPathValue, String> {
134 let context_node = ctx.context_node;
135 if context_node.is_null() {
136 return Ok(XPathValue::NodeSet(NodeSet::new()));
137 }
138
139 let mut result = unsafe {
141 axes::traverse_axis(
142 context_node,
143 step.axis,
144 &step.node_test,
145 true, false, )
148 };
149
150 for predicate in &step.predicates {
152 let mut filtered = NodeSet::new();
153
154 for (i, node) in result.iter().enumerate() {
155 let saved_node = ctx.context_node;
157 let saved_pos = ctx.context_position;
158 let saved_size = ctx.context_size;
159 let saved_list = ctx.context_list.clone();
160
161 ctx.context_node = node;
162 ctx.context_position = (i + 1) as i32;
163 ctx.context_size = result.len() as i32;
164
165 let pred_val = eval(ctx, predicate)?;
167
168 let matches = match pred_val {
173 XPathValue::Number(n) => {
174 (n - (i as f64 + 1.0)).abs() < f64::EPSILON
176 || (n.round() as i32) == (i + 1) as i32
177 }
178 _ => pred_val.as_boolean(),
179 };
180
181 if matches {
182 filtered.push(node);
183 }
184
185 ctx.context_node = saved_node;
186 ctx.context_position = saved_pos;
187 ctx.context_size = saved_size;
188 ctx.context_list = saved_list;
189 }
190
191 result = filtered;
192 }
193
194 Ok(XPathValue::NodeSet(result))
195}
196
197fn eval_filter(
199 ctx: &mut XPathContext,
200 expr: &Expr,
201 predicates: &[Expr],
202) -> Result<XPathValue, String> {
203 let mut result = eval(ctx, expr)?;
205
206 let ns = match &mut result {
208 XPathValue::NodeSet(ns) => ns,
209 _ => return Ok(result), };
211
212 for predicate in predicates {
213 let mut filtered = NodeSet::new();
214 let nodes: Vec<_> = ns.iter().collect();
215
216 for (i, node) in nodes.iter().enumerate() {
217 let saved_node = ctx.context_node;
218 let saved_pos = ctx.context_position;
219 let saved_size = ctx.context_size;
220 let saved_list = ctx.context_list.clone();
221
222 ctx.context_node = *node;
223 ctx.context_position = (i + 1) as i32;
224 ctx.context_size = nodes.len() as i32;
225
226 let pred_val = eval(ctx, predicate)?;
227
228 let matches = match pred_val {
229 XPathValue::Number(n) => {
230 (n - (i as f64 + 1.0)).abs() < f64::EPSILON
231 || (n.round() as i32) == (i + 1) as i32
232 }
233 _ => pred_val.as_boolean(),
234 };
235
236 if matches {
237 filtered.push(*node);
238 }
239
240 ctx.context_node = saved_node;
241 ctx.context_position = saved_pos;
242 ctx.context_size = saved_size;
243 ctx.context_list = saved_list;
244 }
245
246 *ns = filtered;
247 }
248
249 Ok(result)
250}
251
252fn eval_variable(ctx: &mut XPathContext, name: &str) -> Result<XPathValue, String> {
258 ctx.resolve_variable(name)
259 .ok_or_else(|| format!("Undefined variable: ${}", name))
260}
261
262fn eval_function_call(
264 ctx: &mut XPathContext,
265 name: &str,
266 args: &[Expr],
267) -> Result<XPathValue, String> {
268 let mut evaluated_args = Vec::new();
270 for arg in args {
271 evaluated_args.push(eval(ctx, arg)?);
272 }
273
274 match ctx.lookup_function(name) {
276 Some(func) => func(ctx, &evaluated_args),
277 None => Err(format!("Unknown XPath function: {}", name)),
278 }
279}
280
281fn eval_binary_op(
287 ctx: &mut XPathContext,
288 op: &BinaryOp,
289 left: &Expr,
290 right: &Expr,
291) -> Result<XPathValue, String> {
292 match op {
293 BinaryOp::Or => {
294 let left_val = eval(ctx, left)?;
296 if left_val.as_boolean() {
297 return Ok(XPathValue::Boolean(true));
298 }
299 let right_val = eval(ctx, right)?;
300 Ok(XPathValue::Boolean(right_val.as_boolean()))
301 }
302 BinaryOp::And => {
303 let left_val = eval(ctx, left)?;
305 if !left_val.as_boolean() {
306 return Ok(XPathValue::Boolean(false));
307 }
308 let right_val = eval(ctx, right)?;
309 Ok(XPathValue::Boolean(right_val.as_boolean()))
310 }
311 BinaryOp::Eq | BinaryOp::Ne => {
312 let left_val = eval(ctx, left)?;
313 let right_val = eval(ctx, right)?;
314 let eq = compare_equal(ctx, &left_val, &right_val);
315 Ok(match op {
316 BinaryOp::Eq => XPathValue::Boolean(eq),
317 BinaryOp::Ne => XPathValue::Boolean(!eq),
318 _ => unreachable!(),
319 })
320 }
321 BinaryOp::Lt | BinaryOp::Gt | BinaryOp::Le | BinaryOp::Ge => {
322 let left_val = eval(ctx, left)?;
323 let right_val = eval(ctx, right)?;
324 let cmp = compare_ordered(ctx, &left_val, &right_val);
325 let result = match op {
326 BinaryOp::Lt => cmp == std::cmp::Ordering::Less,
327 BinaryOp::Gt => cmp == std::cmp::Ordering::Greater,
328 BinaryOp::Le => cmp != std::cmp::Ordering::Greater,
329 BinaryOp::Ge => cmp != std::cmp::Ordering::Less,
330 _ => unreachable!(),
331 };
332 Ok(XPathValue::Boolean(result))
333 }
334 BinaryOp::Add => {
335 let left_val = eval(ctx, left)?;
336 let right_val = eval(ctx, right)?;
337 Ok(XPathValue::Number(
338 left_val.as_number() + right_val.as_number(),
339 ))
340 }
341 BinaryOp::Sub => {
342 let left_val = eval(ctx, left)?;
343 let right_val = eval(ctx, right)?;
344 Ok(XPathValue::Number(
345 left_val.as_number() - right_val.as_number(),
346 ))
347 }
348 BinaryOp::Mul => {
349 let left_val = eval(ctx, left)?;
350 let right_val = eval(ctx, right)?;
351 Ok(XPathValue::Number(
352 left_val.as_number() * right_val.as_number(),
353 ))
354 }
355 BinaryOp::Div => {
356 let left_val = eval(ctx, left)?;
357 let right_val = eval(ctx, right)?;
358 Ok(XPathValue::Number(
359 left_val.as_number() / right_val.as_number(),
360 ))
361 }
362 BinaryOp::Mod => {
363 let left_val = eval(ctx, left)?;
364 let right_val = eval(ctx, right)?;
365 Ok(XPathValue::Number(
366 left_val.as_number() % right_val.as_number(),
367 ))
368 }
369 BinaryOp::Union => {
370 unreachable!("Union operator should be handled by Expr::Union")
372 }
373 }
374}
375
376fn eval_union(ctx: &mut XPathContext, left: &Expr, right: &Expr) -> Result<XPathValue, String> {
378 let left_val = eval(ctx, left)?;
379 let right_val = eval(ctx, right)?;
380
381 let mut result = left_val.as_node_set().clone();
382 result.extend(right_val.as_node_set());
383 result.sort();
384
385 Ok(XPathValue::NodeSet(result))
386}
387
388fn compare_equal(ctx: &mut XPathContext, a: &XPathValue, b: &XPathValue) -> bool {
394 match (a, b) {
395 (XPathValue::NodeSet(ns_a), XPathValue::NodeSet(ns_b)) => {
397 for node_a in ns_a.iter() {
398 let val_a = node_string_value(node_a);
399 for node_b in ns_b.iter() {
400 let val_b = node_string_value(node_b);
401 if val_a == val_b {
402 return true;
403 }
404 }
405 }
406 false
407 }
408 (XPathValue::NodeSet(ns), other) | (other, XPathValue::NodeSet(ns)) => {
410 for node in ns.iter() {
411 let node_str = node_string_value(node);
412 let other_val = match other {
413 XPathValue::Boolean(_) => {
414 return (ns.len() > 0) == other.as_boolean();
416 }
417 XPathValue::Number(_) => {
418 let node_num = string_to_number(&node_str);
419 if (node_num - other.as_number()).abs() < f64::EPSILON {
420 return true;
421 }
422 continue;
423 }
424 XPathValue::String(_) => {
425 if node_str == other.as_string() {
426 return true;
427 }
428 continue;
429 }
430 _ => continue,
431 };
432 }
433 false
434 }
435 _ => match (a, b) {
437 (XPathValue::Boolean(_), _) | (_, XPathValue::Boolean(_)) => {
438 a.as_boolean() == b.as_boolean()
439 }
440 (XPathValue::Number(_), _) | (_, XPathValue::Number(_)) => {
441 let na = a.as_number();
442 let nb = b.as_number();
443 if na.is_nan() || nb.is_nan() {
444 false
445 } else {
446 (na - nb).abs() < f64::EPSILON || na == nb
447 }
448 }
449 _ => a.as_string() == b.as_string(),
450 },
451 }
452}
453
454fn compare_ordered(ctx: &mut XPathContext, a: &XPathValue, b: &XPathValue) -> std::cmp::Ordering {
456 match (a, b) {
457 (XPathValue::NodeSet(ns_a), XPathValue::NodeSet(ns_b)) => {
459 for node_a in ns_a.iter() {
460 let num_a = string_to_number(&node_string_value(node_a));
461 for node_b in ns_b.iter() {
462 let num_b = string_to_number(&node_string_value(node_b));
463 if num_a < num_b {
464 return std::cmp::Ordering::Less;
465 }
466 if num_a > num_b {
467 return std::cmp::Ordering::Greater;
468 }
469 }
470 }
471 std::cmp::Ordering::Equal
472 }
473 (XPathValue::NodeSet(ns), other) | (other, XPathValue::NodeSet(ns)) => {
475 let other_num = other.as_number();
476 for node in ns.iter() {
477 let node_num = string_to_number(&node_string_value(node));
478 if node_num < other_num {
479 return std::cmp::Ordering::Less;
480 }
481 if node_num > other_num {
482 return std::cmp::Ordering::Greater;
483 }
484 }
485 std::cmp::Ordering::Equal
486 }
487 _ => {
489 let na = a.as_number();
490 let nb = b.as_number();
491 if na.is_nan() || nb.is_nan() {
492 std::cmp::Ordering::Equal } else if na < nb {
494 std::cmp::Ordering::Less
495 } else if na > nb {
496 std::cmp::Ordering::Greater
497 } else {
498 std::cmp::Ordering::Equal
499 }
500 }
501 }
502}
503
504pub fn eval_xpath(ctx: &mut XPathContext, expression: &str) -> Result<XPathValue, String> {
512 let expr = crate::xml::xpath::parser::parse_xpath(expression).map_err(|e| e.message)?;
513 eval(ctx, &expr)
514}
515
516#[cfg(test)]
521mod tests {
522 use super::*;
523 use crate::xml::xpath::context::XPathContext;
524
525 fn setup_context() -> XPathContext {
526 let mut ctx = XPathContext::new(std::ptr::null_mut());
527 let funcs = functions::core_functions();
529 for (name, func) in funcs {
530 ctx.register_function(&name, func);
531 }
532 ctx
533 }
534
535 #[test]
536 fn test_eval_string_literal() {
537 let mut ctx = setup_context();
538 let result = eval_xpath(&mut ctx, "'hello'").unwrap();
539 assert_eq!(result.as_string(), "hello");
540 }
541
542 #[test]
543 fn test_eval_number_literal() {
544 let mut ctx = setup_context();
545 let result = eval_xpath(&mut ctx, "42").unwrap();
546 assert_eq!(result.as_number(), 42.0);
547 }
548
549 #[test]
550 fn test_eval_addition() {
551 let mut ctx = setup_context();
552 let result = eval_xpath(&mut ctx, "1 + 2").unwrap();
553 assert_eq!(result.as_number(), 3.0);
554 }
555
556 #[test]
557 fn test_eval_subtraction() {
558 let mut ctx = setup_context();
559 let result = eval_xpath(&mut ctx, "5 - 3").unwrap();
560 assert_eq!(result.as_number(), 2.0);
561 }
562
563 #[test]
564 fn test_eval_multiplication() {
565 let mut ctx = setup_context();
566 let result = eval_xpath(&mut ctx, "3 * 4").unwrap();
567 assert_eq!(result.as_number(), 12.0);
568 }
569
570 #[test]
571 fn test_eval_division() {
572 let mut ctx = setup_context();
573 let result = eval_xpath(&mut ctx, "10 div 3").unwrap();
574 assert!((result.as_number() - 3.3333333333333335).abs() < 1e-10);
575 }
576
577 #[test]
578 fn test_eval_modulo() {
579 let mut ctx = setup_context();
580 let result = eval_xpath(&mut ctx, "10 mod 3").unwrap();
581 assert_eq!(result.as_number(), 1.0);
582 }
583
584 #[test]
585 fn test_eval_equality() {
586 let mut ctx = setup_context();
587 assert_eq!(eval_xpath(&mut ctx, "1 = 1").unwrap().as_boolean(), true);
588 assert_eq!(eval_xpath(&mut ctx, "1 = 2").unwrap().as_boolean(), false);
589 assert_eq!(eval_xpath(&mut ctx, "1 != 2").unwrap().as_boolean(), true);
590 }
591
592 #[test]
593 fn test_eval_comparison() {
594 let mut ctx = setup_context();
595 assert_eq!(eval_xpath(&mut ctx, "1 < 2").unwrap().as_boolean(), true);
596 assert_eq!(eval_xpath(&mut ctx, "2 > 1").unwrap().as_boolean(), true);
597 assert_eq!(eval_xpath(&mut ctx, "1 <= 1").unwrap().as_boolean(), true);
598 assert_eq!(eval_xpath(&mut ctx, "2 >= 2").unwrap().as_boolean(), true);
599 }
600
601 #[test]
602 fn test_eval_and_or() {
603 let mut ctx = setup_context();
604 assert_eq!(
605 eval_xpath(&mut ctx, "true() and true()")
606 .unwrap()
607 .as_boolean(),
608 true
609 );
610 assert_eq!(
611 eval_xpath(&mut ctx, "true() and false()")
612 .unwrap()
613 .as_boolean(),
614 false
615 );
616 assert_eq!(
617 eval_xpath(&mut ctx, "true() or false()")
618 .unwrap()
619 .as_boolean(),
620 true
621 );
622 assert_eq!(
623 eval_xpath(&mut ctx, "false() or false()")
624 .unwrap()
625 .as_boolean(),
626 false
627 );
628 }
629
630 #[test]
631 fn test_eval_not() {
632 let mut ctx = setup_context();
633 assert_eq!(
634 eval_xpath(&mut ctx, "not(true())").unwrap().as_boolean(),
635 false
636 );
637 assert_eq!(
638 eval_xpath(&mut ctx, "not(false())").unwrap().as_boolean(),
639 true
640 );
641 }
642
643 #[test]
644 fn test_eval_boolean() {
645 let mut ctx = setup_context();
646 assert_eq!(
647 eval_xpath(&mut ctx, "boolean('hello')")
648 .unwrap()
649 .as_boolean(),
650 true
651 );
652 assert_eq!(
653 eval_xpath(&mut ctx, "boolean('')").unwrap().as_boolean(),
654 false
655 );
656 assert_eq!(
657 eval_xpath(&mut ctx, "boolean(0)").unwrap().as_boolean(),
658 false
659 );
660 assert_eq!(
661 eval_xpath(&mut ctx, "boolean(1)").unwrap().as_boolean(),
662 true
663 );
664 }
665
666 #[test]
667 fn test_eval_number() {
668 let mut ctx = setup_context();
669 assert_eq!(
670 eval_xpath(&mut ctx, "number('42')").unwrap().as_number(),
671 42.0
672 );
673 }
674
675 #[test]
676 fn test_eval_string() {
677 let mut ctx = setup_context();
678 assert_eq!(
679 eval_xpath(&mut ctx, "string(42)").unwrap().as_string(),
680 "42"
681 );
682 }
683
684 #[test]
685 fn test_eval_concat() {
686 let mut ctx = setup_context();
687 assert_eq!(
688 eval_xpath(&mut ctx, "concat('a', 'b', 'c')")
689 .unwrap()
690 .as_string(),
691 "abc"
692 );
693 }
694
695 #[test]
696 fn test_eval_starts_with() {
697 let mut ctx = setup_context();
698 assert_eq!(
699 eval_xpath(&mut ctx, "starts-with('hello', 'he')")
700 .unwrap()
701 .as_boolean(),
702 true
703 );
704 }
705
706 #[test]
707 fn test_eval_contains() {
708 let mut ctx = setup_context();
709 assert_eq!(
710 eval_xpath(&mut ctx, "contains('hello', 'ell')")
711 .unwrap()
712 .as_boolean(),
713 true
714 );
715 }
716
717 #[test]
718 fn test_eval_substring() {
719 let mut ctx = setup_context();
720 assert_eq!(
721 eval_xpath(&mut ctx, "substring('12345', 1, 3)")
722 .unwrap()
723 .as_string(),
724 "123"
725 );
726 assert_eq!(
727 eval_xpath(&mut ctx, "substring('12345', 2)")
728 .unwrap()
729 .as_string(),
730 "2345"
731 );
732 }
733
734 #[test]
735 fn test_eval_string_length() {
736 let mut ctx = setup_context();
737 assert_eq!(
738 eval_xpath(&mut ctx, "string-length('hello')")
739 .unwrap()
740 .as_number(),
741 5.0
742 );
743 }
744
745 #[test]
746 fn test_eval_normalize_space() {
747 let mut ctx = setup_context();
748 assert_eq!(
749 eval_xpath(&mut ctx, "normalize-space(' hello world ')")
750 .unwrap()
751 .as_string(),
752 "hello world"
753 );
754 }
755
756 #[test]
757 fn test_eval_floor_ceiling_round() {
758 let mut ctx = setup_context();
759 assert_eq!(eval_xpath(&mut ctx, "floor(3.7)").unwrap().as_number(), 3.0);
760 assert_eq!(
761 eval_xpath(&mut ctx, "ceiling(3.2)").unwrap().as_number(),
762 4.0
763 );
764 assert_eq!(eval_xpath(&mut ctx, "round(3.5)").unwrap().as_number(), 4.0);
765 }
766
767 #[test]
768 fn test_eval_sum() {
769 let mut ctx = setup_context();
771 ctx.document = std::ptr::null_mut();
772 }
775
776 #[test]
777 fn test_eval_variable_not_found() {
778 let mut ctx = setup_context();
779 let result = eval_xpath(&mut ctx, "$undefined_var");
780 assert!(result.is_err());
781 }
782
783 #[test]
784 fn test_eval_variable_found() {
785 let mut ctx = setup_context();
786 ctx.register_variable("x", XPathValue::Number(42.0));
787 let result = eval_xpath(&mut ctx, "$x").unwrap();
788 assert_eq!(result.as_number(), 42.0);
789 }
790
791 #[test]
792 fn test_eval_union() {
793 }
798
799 #[test]
800 fn test_eval_operator_precedence() {
801 let mut ctx = setup_context();
802 let result = eval_xpath(&mut ctx, "1 + 2 * 3").unwrap();
804 assert_eq!(result.as_number(), 7.0);
805
806 let result = eval_xpath(&mut ctx, "(1 + 2) * 3").unwrap();
808 assert_eq!(result.as_number(), 9.0);
809 }
810
811 #[test]
812 fn test_eval_unary_minus() {
813 let mut ctx = setup_context();
814 let result = eval_xpath(&mut ctx, "-5").unwrap();
815 assert_eq!(result.as_number(), -5.0);
816
817 let result = eval_xpath(&mut ctx, "--5").unwrap();
818 assert_eq!(result.as_number(), 5.0);
819 }
820
821 #[test]
822 fn test_eval_true_false() {
823 let mut ctx = setup_context();
824 assert_eq!(eval_xpath(&mut ctx, "true()").unwrap().as_boolean(), true);
825 assert_eq!(eval_xpath(&mut ctx, "false()").unwrap().as_boolean(), false);
826 }
827
828 #[test]
829 fn test_eval_translate() {
830 let mut ctx = setup_context();
831 assert_eq!(
832 eval_xpath(
833 &mut ctx,
834 "translate('hello', 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"
835 )
836 .unwrap()
837 .as_string(),
838 "HELLO"
839 );
840 }
841
842 #[test]
843 fn test_eval_empty_expression() {
844 let mut ctx = setup_context();
845 let result = eval_xpath(&mut ctx, "");
846 assert!(result.is_err());
847 }
848}