1#![allow(clippy::cloned_ref_to_slice_refs)]
5#![allow(clippy::explicit_counter_loop)]
6#![allow(clippy::too_many_arguments)]
7#![allow(clippy::manual_strip)]
8
9use std::cmp::Ordering;
10use std::collections::{HashMap, HashSet};
11
12use crate::ast::{AstNode, BinaryOp, PathStep, Stage};
13use crate::parser;
14use crate::value::JValue;
15use indexmap::IndexMap;
16use std::rc::Rc;
17use thiserror::Error;
18
19struct SpecializedSortComparator {
26 field: String,
27 descending: bool,
28}
29
30enum SortKey {
32 Num(f64),
33 Str(Rc<str>),
34 None,
35}
36
37fn compare_sort_keys(a: &SortKey, b: &SortKey, descending: bool) -> Ordering {
38 let ord = match (a, b) {
39 (SortKey::Num(x), SortKey::Num(y)) => x.partial_cmp(y).unwrap_or(Ordering::Equal),
40 (SortKey::Str(x), SortKey::Str(y)) => (**x).cmp(&**y),
41 (SortKey::None, SortKey::None) => Ordering::Equal,
42 (SortKey::None, _) => Ordering::Greater,
43 (_, SortKey::None) => Ordering::Less,
44 _ => Ordering::Equal,
46 };
47 if descending {
48 ord.reverse()
49 } else {
50 ord
51 }
52}
53
54fn try_specialize_sort_comparator(
57 body: &AstNode,
58 left_param: &str,
59 right_param: &str,
60) -> Option<SpecializedSortComparator> {
61 let AstNode::Binary { op, lhs, rhs } = body else {
62 return None;
63 };
64
65 let is_ascending = |op: &BinaryOp| -> Option<bool> {
67 match op {
68 BinaryOp::GreaterThan | BinaryOp::GreaterThanOrEqual => Some(true),
69 BinaryOp::LessThan | BinaryOp::LessThanOrEqual => Some(false),
70 _ => None,
71 }
72 };
73
74 let extract_var_field = |node: &AstNode, param: &str| -> Option<String> {
76 let AstNode::Path { steps } = node else {
77 return None;
78 };
79 if steps.len() != 2 {
80 return None;
81 }
82 let AstNode::Variable(var) = &steps[0].node else {
83 return None;
84 };
85 if var != param {
86 return None;
87 }
88 let AstNode::Name(field) = &steps[1].node else {
89 return None;
90 };
91 if !steps[0].stages.is_empty() || !steps[1].stages.is_empty() {
92 return None;
93 }
94 Some(field.clone())
95 };
96
97 for flipped in [false, true] {
99 let (lhs_param, rhs_param) = if flipped {
100 (right_param, left_param)
101 } else {
102 (left_param, right_param)
103 };
104 if let (Some(lhs_field), Some(rhs_field)) = (
105 extract_var_field(lhs, lhs_param),
106 extract_var_field(rhs, rhs_param),
107 ) {
108 if lhs_field == rhs_field {
109 let descending = match op {
110 BinaryOp::Subtract => flipped,
113 _ => {
115 let ascending = is_ascending(op)?;
116 if flipped {
117 ascending
118 } else {
119 !ascending
120 }
121 }
122 };
123 return Some(SpecializedSortComparator {
124 field: lhs_field,
125 descending,
126 });
127 }
128 }
129 }
130 None
131}
132
133type ShapeCache = HashMap<String, usize>;
147
148fn build_shape_cache(first_element: &JValue) -> Option<ShapeCache> {
151 match first_element {
152 JValue::Object(obj) => {
153 let mut cache = HashMap::with_capacity(obj.len());
154 for (idx, (key, _)) in obj.iter().enumerate() {
155 cache.insert(key.clone(), idx);
156 }
157 Some(cache)
158 }
159 _ => None,
160 }
161}
162
163#[derive(Debug, Clone, Copy)]
165pub(crate) enum CompiledCmp {
166 Eq,
167 Ne,
168 Lt,
169 Le,
170 Gt,
171 Ge,
172}
173
174#[derive(Debug, Clone, Copy)]
176pub(crate) enum CompiledArithOp {
177 Add,
178 Sub,
179 Mul,
180 Div,
181 Mod,
182}
183
184#[derive(Clone, Debug)]
190pub(crate) enum CompiledExpr {
191 Literal(JValue),
194 ExplicitNull,
198 FieldLookup(String),
200 NestedFieldLookup(String, String),
202 VariableLookup(String),
205
206 Compare {
208 op: CompiledCmp,
209 lhs: Box<CompiledExpr>,
210 rhs: Box<CompiledExpr>,
211 },
212
213 Arithmetic {
215 op: CompiledArithOp,
216 lhs: Box<CompiledExpr>,
217 rhs: Box<CompiledExpr>,
218 },
219
220 Concat(Box<CompiledExpr>, Box<CompiledExpr>),
222
223 And(Box<CompiledExpr>, Box<CompiledExpr>),
225 Or(Box<CompiledExpr>, Box<CompiledExpr>),
226 Not(Box<CompiledExpr>),
227 Negate(Box<CompiledExpr>),
229
230 Conditional {
232 condition: Box<CompiledExpr>,
233 then_expr: Box<CompiledExpr>,
234 else_expr: Option<Box<CompiledExpr>>,
235 },
236
237 ObjectConstruct(Vec<(String, CompiledExpr)>),
240 ArrayConstruct(Vec<(CompiledExpr, bool)>),
248
249 #[allow(dead_code)]
255 ContextVar(String),
256
257 FieldPath(Vec<CompiledStep>),
260
261 BuiltinCall {
264 name: &'static str,
265 args: Vec<CompiledExpr>,
266 },
267
268 Block(Vec<CompiledExpr>),
270
271 Coalesce(Box<CompiledExpr>, Box<CompiledExpr>),
273
274 MapCall {
279 array: Box<CompiledExpr>,
280 params: Vec<String>,
281 body: Box<CompiledExpr>,
282 },
283 FilterCall {
286 array: Box<CompiledExpr>,
287 params: Vec<String>,
288 body: Box<CompiledExpr>,
289 },
290 ReduceCall {
293 array: Box<CompiledExpr>,
294 params: Vec<String>,
295 body: Box<CompiledExpr>,
296 initial: Option<Box<CompiledExpr>>,
297 },
298}
299
300#[derive(Clone, Debug)]
302pub(crate) struct CompiledStep {
303 pub field: String,
305 pub filter: Option<CompiledExpr>,
307}
308
309pub(crate) fn try_compile_expr(node: &AstNode) -> Option<CompiledExpr> {
313 try_compile_expr_inner(node, None)
314}
315
316pub(crate) fn try_compile_expr_with_allowed_vars(
320 node: &AstNode,
321 allowed_vars: &[&str],
322) -> Option<CompiledExpr> {
323 try_compile_expr_inner(node, Some(allowed_vars))
324}
325
326fn try_compile_expr_inner(node: &AstNode, allowed_vars: Option<&[&str]>) -> Option<CompiledExpr> {
327 match node {
328 AstNode::String(s) => Some(CompiledExpr::Literal(JValue::string(s.clone()))),
330 AstNode::Number(n) => Some(CompiledExpr::Literal(JValue::Number(*n))),
331 AstNode::Boolean(b) => Some(CompiledExpr::Literal(JValue::Bool(*b))),
332 AstNode::Null => Some(CompiledExpr::ExplicitNull),
333
334 AstNode::Name(field) => Some(CompiledExpr::FieldLookup(field.clone())),
336
337 AstNode::Variable(var) if var.is_empty() => Some(CompiledExpr::VariableLookup(var.clone())),
344 AstNode::Variable(var) => {
345 if let Some(allowed) = allowed_vars {
346 if allowed.contains(&var.as_str()) {
348 return Some(CompiledExpr::VariableLookup(var.clone()));
349 }
350 }
351 None
355 }
356
357 AstNode::Path { steps } => try_compile_path(steps, allowed_vars),
359
360 AstNode::Binary { op, lhs, rhs } => {
362 let compiled_lhs = try_compile_expr_inner(lhs, allowed_vars)?;
363 let compiled_rhs = try_compile_expr_inner(rhs, allowed_vars)?;
364 match op {
365 BinaryOp::Equal => Some(CompiledExpr::Compare {
367 op: CompiledCmp::Eq,
368 lhs: Box::new(compiled_lhs),
369 rhs: Box::new(compiled_rhs),
370 }),
371 BinaryOp::NotEqual => Some(CompiledExpr::Compare {
372 op: CompiledCmp::Ne,
373 lhs: Box::new(compiled_lhs),
374 rhs: Box::new(compiled_rhs),
375 }),
376 BinaryOp::LessThan => Some(CompiledExpr::Compare {
377 op: CompiledCmp::Lt,
378 lhs: Box::new(compiled_lhs),
379 rhs: Box::new(compiled_rhs),
380 }),
381 BinaryOp::LessThanOrEqual => Some(CompiledExpr::Compare {
382 op: CompiledCmp::Le,
383 lhs: Box::new(compiled_lhs),
384 rhs: Box::new(compiled_rhs),
385 }),
386 BinaryOp::GreaterThan => Some(CompiledExpr::Compare {
387 op: CompiledCmp::Gt,
388 lhs: Box::new(compiled_lhs),
389 rhs: Box::new(compiled_rhs),
390 }),
391 BinaryOp::GreaterThanOrEqual => Some(CompiledExpr::Compare {
392 op: CompiledCmp::Ge,
393 lhs: Box::new(compiled_lhs),
394 rhs: Box::new(compiled_rhs),
395 }),
396 BinaryOp::Add => Some(CompiledExpr::Arithmetic {
398 op: CompiledArithOp::Add,
399 lhs: Box::new(compiled_lhs),
400 rhs: Box::new(compiled_rhs),
401 }),
402 BinaryOp::Subtract => Some(CompiledExpr::Arithmetic {
403 op: CompiledArithOp::Sub,
404 lhs: Box::new(compiled_lhs),
405 rhs: Box::new(compiled_rhs),
406 }),
407 BinaryOp::Multiply => Some(CompiledExpr::Arithmetic {
408 op: CompiledArithOp::Mul,
409 lhs: Box::new(compiled_lhs),
410 rhs: Box::new(compiled_rhs),
411 }),
412 BinaryOp::Divide => Some(CompiledExpr::Arithmetic {
413 op: CompiledArithOp::Div,
414 lhs: Box::new(compiled_lhs),
415 rhs: Box::new(compiled_rhs),
416 }),
417 BinaryOp::Modulo => Some(CompiledExpr::Arithmetic {
418 op: CompiledArithOp::Mod,
419 lhs: Box::new(compiled_lhs),
420 rhs: Box::new(compiled_rhs),
421 }),
422 BinaryOp::And => Some(CompiledExpr::And(
424 Box::new(compiled_lhs),
425 Box::new(compiled_rhs),
426 )),
427 BinaryOp::Or => Some(CompiledExpr::Or(
428 Box::new(compiled_lhs),
429 Box::new(compiled_rhs),
430 )),
431 BinaryOp::Concatenate => Some(CompiledExpr::Concat(
433 Box::new(compiled_lhs),
434 Box::new(compiled_rhs),
435 )),
436 BinaryOp::Coalesce => Some(CompiledExpr::Coalesce(
438 Box::new(compiled_lhs),
439 Box::new(compiled_rhs),
440 )),
441 _ => None,
443 }
444 }
445
446 AstNode::Unary { op, operand } => {
448 let compiled = try_compile_expr_inner(operand, allowed_vars)?;
449 match op {
450 crate::ast::UnaryOp::Not => Some(CompiledExpr::Not(Box::new(compiled))),
451 crate::ast::UnaryOp::Negate => Some(CompiledExpr::Negate(Box::new(compiled))),
452 }
453 }
454
455 AstNode::Conditional {
457 condition,
458 then_branch,
459 else_branch,
460 } => {
461 let cond = try_compile_expr_inner(condition, allowed_vars)?;
462 let then_e = try_compile_expr_inner(then_branch, allowed_vars)?;
463 let else_e = match else_branch {
464 Some(e) => Some(Box::new(try_compile_expr_inner(e, allowed_vars)?)),
465 None => None,
466 };
467 Some(CompiledExpr::Conditional {
468 condition: Box::new(cond),
469 then_expr: Box::new(then_e),
470 else_expr: else_e,
471 })
472 }
473
474 AstNode::Object(pairs) => {
476 let mut fields = Vec::with_capacity(pairs.len());
477 for (key_node, val_node) in pairs {
478 let key = match key_node {
480 AstNode::String(s) => s.clone(),
481 _ => return None,
482 };
483 let val = try_compile_expr_inner(val_node, allowed_vars)?;
484 fields.push((key, val));
485 }
486 Some(CompiledExpr::ObjectConstruct(fields))
487 }
488
489 AstNode::Array(elems) => {
491 let mut compiled = Vec::with_capacity(elems.len());
492 for elem in elems {
493 let is_nested = matches!(elem, AstNode::Array(_));
496 compiled.push((try_compile_expr_inner(elem, allowed_vars)?, is_nested));
497 }
498 Some(CompiledExpr::ArrayConstruct(compiled))
499 }
500
501 AstNode::Block(exprs) if !exprs.is_empty() => {
503 let compiled: Option<Vec<CompiledExpr>> = exprs
504 .iter()
505 .map(|e| try_compile_expr_inner(e, allowed_vars))
506 .collect();
507 compiled.map(CompiledExpr::Block)
508 }
509
510 AstNode::Function {
512 name,
513 args,
514 is_builtin: true,
515 } => {
516 if is_compilable_builtin(name) {
517 if let Some(max) = compilable_builtin_max_args(name) {
520 if args.len() > max {
521 return None;
522 }
523 }
524 let compiled_args: Option<Vec<CompiledExpr>> = args
525 .iter()
526 .map(|a| try_compile_expr_inner(a, allowed_vars))
527 .collect();
528 compiled_args.map(|cargs| CompiledExpr::BuiltinCall {
529 name: static_builtin_name(name),
530 args: cargs,
531 })
532 } else {
533 try_compile_hof_expr(name, args, allowed_vars)
534 }
535 }
536
537 _ => None,
539 }
540}
541
542fn extract_inline_lambda(node: &AstNode) -> Option<(&Vec<String>, &AstNode)> {
545 match node {
546 AstNode::Lambda {
547 params,
548 body,
549 signature: None,
550 thunk: false,
551 } => Some((params, body)),
552 _ => None,
553 }
554}
555
556fn compile_hof_array_and_body(
560 array_node: &AstNode,
561 params: &[String],
562 body: &AstNode,
563 allowed_vars: Option<&[&str]>,
564) -> Option<(Box<CompiledExpr>, Box<CompiledExpr>)> {
565 let array = try_compile_expr_inner(array_node, allowed_vars)?;
566 let param_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
567 let compiled_body = try_compile_expr_inner(body, Some(¶m_refs))?;
568 Some((Box::new(array), Box::new(compiled_body)))
569}
570
571fn try_compile_hof_expr(
581 name: &str,
582 args: &[AstNode],
583 allowed_vars: Option<&[&str]>,
584) -> Option<CompiledExpr> {
585 match name {
586 "map" | "filter" => {
587 if args.len() != 2 {
588 return None;
589 }
590 let (params, body) = extract_inline_lambda(&args[1])?;
591 if params.is_empty() || params.len() > 2 {
592 return None;
593 }
594 let (array, compiled_body) =
595 compile_hof_array_and_body(&args[0], params, body, allowed_vars)?;
596 if name == "map" {
597 Some(CompiledExpr::MapCall {
598 array,
599 params: params.clone(),
600 body: compiled_body,
601 })
602 } else {
603 Some(CompiledExpr::FilterCall {
604 array,
605 params: params.clone(),
606 body: compiled_body,
607 })
608 }
609 }
610 "reduce" => {
611 if args.len() < 2 || args.len() > 3 {
612 return None;
613 }
614 let (params, body) = extract_inline_lambda(&args[1])?;
615 if params.len() != 2 {
616 return None;
617 }
618 let (array, compiled_body) =
619 compile_hof_array_and_body(&args[0], params, body, allowed_vars)?;
620 let initial = if args.len() == 3 {
621 Some(Box::new(try_compile_expr_inner(&args[2], allowed_vars)?))
622 } else {
623 None
624 };
625 Some(CompiledExpr::ReduceCall {
626 array,
627 params: params.clone(),
628 body: compiled_body,
629 initial,
630 })
631 }
632 _ => None,
633 }
634}
635
636fn is_compilable_builtin(name: &str) -> bool {
639 matches!(
640 name,
641 "string"
642 | "length"
643 | "substring"
644 | "substringBefore"
645 | "substringAfter"
646 | "uppercase"
647 | "lowercase"
648 | "trim"
649 | "contains"
650 | "split"
651 | "join"
652 | "number"
653 | "floor"
654 | "ceil"
655 | "round"
656 | "abs"
657 | "sqrt"
658 | "sum"
659 | "max"
660 | "min"
661 | "average"
662 | "count"
663 | "boolean"
664 | "not"
665 | "keys"
666 | "append"
667 | "reverse"
668 | "distinct"
669 | "merge"
670 )
671}
672
673fn compilable_builtin_max_args(name: &str) -> Option<usize> {
678 match name {
679 "string" => Some(2),
680 "length" | "uppercase" | "lowercase" | "trim" => Some(1),
681 "substring" | "split" => Some(3),
682 "substringBefore" | "substringAfter" | "contains" | "join" | "append" | "round" => Some(2),
683 "number" | "floor" | "ceil" | "abs" | "sqrt" => Some(1),
684 "sum" | "max" | "min" | "average" | "count" => Some(1),
685 "boolean" | "not" | "keys" | "reverse" | "distinct" => Some(1),
686 "merge" => None, _ => None,
688 }
689}
690
691fn static_builtin_name(name: &str) -> &'static str {
694 match name {
695 "string" => "string",
696 "length" => "length",
697 "substring" => "substring",
698 "substringBefore" => "substringBefore",
699 "substringAfter" => "substringAfter",
700 "uppercase" => "uppercase",
701 "lowercase" => "lowercase",
702 "trim" => "trim",
703 "contains" => "contains",
704 "split" => "split",
705 "join" => "join",
706 "number" => "number",
707 "floor" => "floor",
708 "ceil" => "ceil",
709 "round" => "round",
710 "abs" => "abs",
711 "sqrt" => "sqrt",
712 "sum" => "sum",
713 "max" => "max",
714 "min" => "min",
715 "average" => "average",
716 "count" => "count",
717 "boolean" => "boolean",
718 "not" => "not",
719 "keys" => "keys",
720 "append" => "append",
721 "reverse" => "reverse",
722 "distinct" => "distinct",
723 "merge" => "merge",
724 _ => unreachable!("Not a compilable builtin: {}", name),
725 }
726}
727
728#[inline(always)]
736pub(crate) fn eval_compiled(
737 expr: &CompiledExpr,
738 data: &JValue,
739 vars: Option<&HashMap<&str, &JValue>>,
740) -> Result<JValue, EvaluatorError> {
741 eval_compiled_inner(expr, data, vars, None, None)
742}
743
744#[inline(always)]
748fn eval_compiled_shaped(
749 expr: &CompiledExpr,
750 data: &JValue,
751 vars: Option<&HashMap<&str, &JValue>>,
752 shape: &ShapeCache,
753) -> Result<JValue, EvaluatorError> {
754 eval_compiled_inner(expr, data, vars, None, Some(shape))
755}
756
757#[inline]
761fn clone_outer_vars<'a>(
762 vars: Option<&HashMap<&'a str, &'a JValue>>,
763 capacity: usize,
764) -> HashMap<&'a str, &'a JValue> {
765 vars.map(|v| v.iter().map(|(&k, v)| (k, *v)).collect())
766 .unwrap_or_else(|| HashMap::with_capacity(capacity))
767}
768
769fn eval_compiled_inner(
770 expr: &CompiledExpr,
771 data: &JValue,
772 vars: Option<&HashMap<&str, &JValue>>,
773 ctx: Option<&Context>,
774 shape: Option<&ShapeCache>,
775) -> Result<JValue, EvaluatorError> {
776 match expr {
777 CompiledExpr::Literal(v) => Ok(v.clone()),
779
780 CompiledExpr::ExplicitNull => Ok(JValue::Null),
783
784 CompiledExpr::FieldLookup(field) => match data {
785 JValue::Object(obj) => {
786 if let Some(shape) = shape {
788 if let Some(&idx) = shape.get(field.as_str()) {
789 return Ok(obj
790 .get_index(idx)
791 .map(|(_, v)| v.clone())
792 .unwrap_or(JValue::Undefined));
793 }
794 }
795 Ok(obj
796 .get(field.as_str())
797 .cloned()
798 .unwrap_or(JValue::Undefined))
799 }
800 _ => Ok(JValue::Undefined),
801 },
802
803 CompiledExpr::NestedFieldLookup(outer, inner) => match data {
804 JValue::Object(obj) => {
805 let outer_val = if let Some(shape) = shape {
807 if let Some(&idx) = shape.get(outer.as_str()) {
808 obj.get_index(idx).map(|(_, v)| v)
809 } else {
810 obj.get(outer.as_str())
811 }
812 } else {
813 obj.get(outer.as_str())
814 };
815 Ok(outer_val
816 .and_then(|v| match v {
817 JValue::Object(nested) => nested.get(inner.as_str()).cloned(),
818 _ => None,
819 })
820 .unwrap_or(JValue::Undefined))
821 }
822 _ => Ok(JValue::Undefined),
823 },
824
825 CompiledExpr::VariableLookup(var) => {
826 if let Some(vars) = vars {
827 if let Some(val) = vars.get(var.as_str()) {
828 return Ok((*val).clone());
829 }
830 }
831 if var.is_empty() {
833 return Ok(data.clone());
834 }
835 Ok(JValue::Undefined)
836 }
837
838 CompiledExpr::Compare { op, lhs, rhs } => {
840 let lhs_explicit_null = is_compiled_explicit_null(lhs);
841 let rhs_explicit_null = is_compiled_explicit_null(rhs);
842 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
843 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
844 match op {
845 CompiledCmp::Eq => Ok(JValue::Bool(crate::functions::array::values_equal(
846 &left, &right,
847 ))),
848 CompiledCmp::Ne => Ok(JValue::Bool(!crate::functions::array::values_equal(
849 &left, &right,
850 ))),
851 CompiledCmp::Lt => compiled_ordered_cmp(
852 &left,
853 &right,
854 lhs_explicit_null,
855 rhs_explicit_null,
856 |a, b| a < b,
857 |a, b| a < b,
858 ),
859 CompiledCmp::Le => compiled_ordered_cmp(
860 &left,
861 &right,
862 lhs_explicit_null,
863 rhs_explicit_null,
864 |a, b| a <= b,
865 |a, b| a <= b,
866 ),
867 CompiledCmp::Gt => compiled_ordered_cmp(
868 &left,
869 &right,
870 lhs_explicit_null,
871 rhs_explicit_null,
872 |a, b| a > b,
873 |a, b| a > b,
874 ),
875 CompiledCmp::Ge => compiled_ordered_cmp(
876 &left,
877 &right,
878 lhs_explicit_null,
879 rhs_explicit_null,
880 |a, b| a >= b,
881 |a, b| a >= b,
882 ),
883 }
884 }
885
886 CompiledExpr::Arithmetic { op, lhs, rhs } => {
888 let lhs_explicit_null = is_compiled_explicit_null(lhs);
889 let rhs_explicit_null = is_compiled_explicit_null(rhs);
890 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
891 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
892 compiled_arithmetic(*op, &left, &right, lhs_explicit_null, rhs_explicit_null)
893 }
894
895 CompiledExpr::Concat(lhs, rhs) => {
897 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
898 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
899 let ls = compiled_to_concat_string(&left)?;
900 let rs = compiled_to_concat_string(&right)?;
901 Ok(JValue::string(format!("{}{}", ls, rs)))
902 }
903
904 CompiledExpr::And(lhs, rhs) => {
906 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
907 if !compiled_is_truthy(&left) {
908 return Ok(JValue::Bool(false));
909 }
910 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
911 Ok(JValue::Bool(compiled_is_truthy(&right)))
912 }
913 CompiledExpr::Or(lhs, rhs) => {
914 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
915 if compiled_is_truthy(&left) {
916 return Ok(JValue::Bool(true));
917 }
918 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
919 Ok(JValue::Bool(compiled_is_truthy(&right)))
920 }
921 CompiledExpr::Not(inner) => {
922 let val = eval_compiled_inner(inner, data, vars, ctx, shape)?;
923 Ok(JValue::Bool(!compiled_is_truthy(&val)))
924 }
925 CompiledExpr::Negate(inner) => {
926 let val = eval_compiled_inner(inner, data, vars, ctx, shape)?;
927 match val {
928 JValue::Number(n) => Ok(JValue::Number(-n)),
929 JValue::Null => Ok(JValue::Null),
930 v if v.is_undefined() => Ok(JValue::Undefined),
932 _ => Err(EvaluatorError::TypeError(
933 "D1002: Cannot negate non-number value".to_string(),
934 )),
935 }
936 }
937
938 CompiledExpr::Conditional {
940 condition,
941 then_expr,
942 else_expr,
943 } => {
944 let cond = eval_compiled_inner(condition, data, vars, ctx, shape)?;
945 if compiled_is_truthy(&cond) {
946 eval_compiled_inner(then_expr, data, vars, ctx, shape)
947 } else if let Some(else_e) = else_expr {
948 eval_compiled_inner(else_e, data, vars, ctx, shape)
949 } else {
950 Ok(JValue::Undefined)
951 }
952 }
953
954 CompiledExpr::ObjectConstruct(fields) => {
956 let mut result = IndexMap::with_capacity(fields.len());
957 for (key, expr) in fields {
958 let value = eval_compiled_inner(expr, data, vars, ctx, shape)?;
959 if !value.is_undefined() {
960 result.insert(key.clone(), value);
961 }
962 }
963 Ok(JValue::object(result))
964 }
965
966 CompiledExpr::ArrayConstruct(elems) => {
968 let mut result = Vec::new();
969 for (elem_expr, is_nested) in elems {
970 let value = eval_compiled_inner(elem_expr, data, vars, ctx, shape)?;
971 if value.is_undefined() {
973 continue;
974 }
975 if *is_nested {
976 result.push(value);
978 } else if let JValue::Array(arr) = value {
979 result.extend(arr.iter().cloned());
981 } else {
982 result.push(value);
983 }
984 }
985 Ok(JValue::array(result))
986 }
987
988 CompiledExpr::ContextVar(name) => {
995 if let Some(vars) = vars {
997 if let Some(val) = vars.get(name.as_str()) {
998 return Ok((*val).clone());
999 }
1000 }
1001 if let Some(ctx) = ctx {
1003 if let Some(val) = ctx.lookup(name) {
1004 return Ok(val.clone());
1005 }
1006 }
1007 Ok(JValue::Undefined)
1008 }
1009
1010 CompiledExpr::FieldPath(steps) => compiled_eval_field_path(steps, data, vars, ctx, shape),
1012
1013 CompiledExpr::BuiltinCall { name, args } => {
1015 let mut evaled_args = Vec::with_capacity(args.len());
1016 for arg in args.iter() {
1017 evaled_args.push(eval_compiled_inner(arg, data, vars, ctx, shape)?);
1018 }
1019 call_pure_builtin(name, &evaled_args, data)
1020 }
1021
1022 CompiledExpr::Block(exprs) => {
1024 let mut result = JValue::Undefined;
1025 for expr in exprs.iter() {
1026 result = eval_compiled_inner(expr, data, vars, ctx, shape)?;
1027 }
1028 Ok(result)
1029 }
1030
1031 CompiledExpr::Coalesce(lhs, rhs) => {
1034 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
1035 if left.is_undefined() {
1036 eval_compiled_inner(rhs, data, vars, ctx, shape)
1037 } else {
1038 Ok(left)
1039 }
1040 }
1041
1042 CompiledExpr::MapCall {
1049 array,
1050 params,
1051 body,
1052 } => {
1053 let arr_val = eval_compiled_inner(array, data, vars, ctx, shape)?;
1054 let single_holder;
1055 let items: &[JValue] = match &arr_val {
1056 JValue::Array(a) => a.as_slice(),
1057 JValue::Undefined => return Ok(JValue::Undefined),
1058 other => {
1059 single_holder = [other.clone()];
1060 &single_holder[..]
1061 }
1062 };
1063 let mut result = Vec::with_capacity(items.len());
1064 let p0 = params.first().map(|s| s.as_str());
1065
1066 if let Some(p1) = params.get(1).map(|s| s.as_str()) {
1067 for (idx, item) in items.iter().enumerate() {
1070 let idx_val = JValue::Number(idx as f64);
1071 let mut call_vars = clone_outer_vars(vars, 2);
1072 if let Some(p) = p0 {
1073 call_vars.insert(p, item);
1074 }
1075 call_vars.insert(p1, &idx_val);
1076 let mapped = eval_compiled_inner(body, data, Some(&call_vars), ctx, shape)?;
1077 if !mapped.is_undefined() {
1078 result.push(mapped);
1079 }
1080 }
1081 } else if let Some(p0) = p0 {
1082 let mut call_vars = clone_outer_vars(vars, 1);
1084 for item in items.iter() {
1085 call_vars.insert(p0, item);
1086 let mapped = eval_compiled_inner(body, data, Some(&call_vars), ctx, shape)?;
1087 if !mapped.is_undefined() {
1088 result.push(mapped);
1089 }
1090 }
1091 }
1092 Ok(if result.is_empty() {
1093 JValue::Undefined
1094 } else {
1095 JValue::array(result)
1096 })
1097 }
1098
1099 CompiledExpr::FilterCall {
1100 array,
1101 params,
1102 body,
1103 } => {
1104 let arr_val = eval_compiled_inner(array, data, vars, ctx, shape)?;
1105 if arr_val.is_undefined() || arr_val.is_null() {
1106 return Ok(JValue::Undefined);
1107 }
1108 let single_holder;
1109 let (items, was_single) = match &arr_val {
1110 JValue::Array(a) => (a.as_slice(), false),
1111 other => {
1112 single_holder = [other.clone()];
1113 (&single_holder[..], true)
1114 }
1115 };
1116 let mut result = Vec::with_capacity(items.len() / 2);
1117 let p0 = params.first().map(|s| s.as_str());
1118
1119 if let Some(p1) = params.get(1).map(|s| s.as_str()) {
1120 for (idx, item) in items.iter().enumerate() {
1121 let idx_val = JValue::Number(idx as f64);
1122 let mut call_vars = clone_outer_vars(vars, 2);
1123 if let Some(p) = p0 {
1124 call_vars.insert(p, item);
1125 }
1126 call_vars.insert(p1, &idx_val);
1127 let pred = eval_compiled_inner(body, data, Some(&call_vars), ctx, shape)?;
1128 if compiled_is_truthy(&pred) {
1129 result.push(item.clone());
1130 }
1131 }
1132 } else if let Some(p0) = p0 {
1133 let mut call_vars = clone_outer_vars(vars, 1);
1134 for item in items.iter() {
1135 call_vars.insert(p0, item);
1136 let pred = eval_compiled_inner(body, data, Some(&call_vars), ctx, shape)?;
1137 if compiled_is_truthy(&pred) {
1138 result.push(item.clone());
1139 }
1140 }
1141 }
1142 if was_single {
1143 Ok(match result.len() {
1144 0 => JValue::Undefined,
1145 1 => result.remove(0),
1146 _ => JValue::array(result),
1147 })
1148 } else {
1149 Ok(JValue::array(result))
1150 }
1151 }
1152
1153 CompiledExpr::ReduceCall {
1154 array,
1155 params,
1156 body,
1157 initial,
1158 } => {
1159 let arr_val = eval_compiled_inner(array, data, vars, ctx, shape)?;
1160 let single_holder;
1161 let items: &[JValue] = match &arr_val {
1162 JValue::Array(a) => a.as_slice(),
1163 JValue::Null => return Ok(JValue::Null),
1164 JValue::Undefined => return Ok(JValue::Undefined),
1165 other => {
1166 single_holder = [other.clone()];
1167 &single_holder[..]
1168 }
1169 };
1170 let (start_idx, mut accumulator) = if let Some(init_expr) = initial {
1171 let init_val = eval_compiled_inner(init_expr, data, vars, ctx, shape)?;
1172 if items.is_empty() {
1173 return Ok(init_val);
1174 }
1175 (0usize, init_val)
1176 } else {
1177 if items.is_empty() {
1178 return Ok(JValue::Null);
1179 }
1180 (1, items[0].clone())
1181 };
1182 let acc_param = params[0].as_str();
1183 let item_param = params[1].as_str();
1184 for item in items[start_idx..].iter() {
1185 let mut call_vars = clone_outer_vars(vars, 2);
1188 call_vars.insert(acc_param, &accumulator);
1189 call_vars.insert(item_param, item);
1190 let new_acc = eval_compiled_inner(body, data, Some(&call_vars), ctx, shape)?;
1191 drop(call_vars);
1192 accumulator = new_acc;
1193 }
1194 Ok(accumulator)
1195 }
1196 }
1197}
1198
1199#[inline]
1201pub(crate) fn compiled_is_truthy(value: &JValue) -> bool {
1202 match value {
1203 JValue::Null | JValue::Undefined => false,
1204 JValue::Bool(b) => *b,
1205 JValue::Number(n) => *n != 0.0,
1206 JValue::String(s) => !s.is_empty(),
1207 JValue::Array(a) => !a.is_empty(),
1208 JValue::Object(o) => !o.is_empty(),
1209 _ => false,
1210 }
1211}
1212
1213#[inline]
1216fn is_compiled_explicit_null(expr: &CompiledExpr) -> bool {
1217 matches!(expr, CompiledExpr::ExplicitNull)
1218}
1219
1220#[inline]
1223pub(crate) fn compiled_ordered_cmp(
1224 left: &JValue,
1225 right: &JValue,
1226 left_is_explicit_null: bool,
1227 right_is_explicit_null: bool,
1228 cmp_num: fn(f64, f64) -> bool,
1229 cmp_str: fn(&str, &str) -> bool,
1230) -> Result<JValue, EvaluatorError> {
1231 match (left, right) {
1232 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Bool(cmp_num(*a, *b))),
1233 (JValue::String(a), JValue::String(b)) => Ok(JValue::Bool(cmp_str(a, b))),
1234 (JValue::Null, JValue::Null) | (JValue::Undefined, JValue::Undefined) => Ok(JValue::Null),
1236 (JValue::Undefined, JValue::Null) | (JValue::Null, JValue::Undefined) => Ok(JValue::Null),
1237 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::EvaluationError(
1239 "T2010: Type mismatch in comparison".to_string(),
1240 )),
1241 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::EvaluationError(
1242 "T2010: Type mismatch in comparison".to_string(),
1243 )),
1244 (JValue::Bool(_), JValue::Null | JValue::Undefined)
1246 | (JValue::Null | JValue::Undefined, JValue::Bool(_)) => Err(
1247 EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()),
1248 ),
1249 (JValue::Number(_) | JValue::String(_), JValue::Null | JValue::Undefined)
1251 | (JValue::Null | JValue::Undefined, JValue::Number(_) | JValue::String(_)) => {
1252 Ok(JValue::Null)
1253 }
1254 (JValue::String(_), JValue::Number(_)) | (JValue::Number(_), JValue::String(_)) => {
1256 Err(EvaluatorError::EvaluationError(
1257 "T2009: The expressions on either side of operator must be of the same data type"
1258 .to_string(),
1259 ))
1260 }
1261 _ => Err(EvaluatorError::EvaluationError(
1262 "T2010: Type mismatch in comparison".to_string(),
1263 )),
1264 }
1265}
1266
1267#[inline]
1270pub(crate) fn compiled_arithmetic(
1271 op: CompiledArithOp,
1272 left: &JValue,
1273 right: &JValue,
1274 left_is_explicit_null: bool,
1275 right_is_explicit_null: bool,
1276) -> Result<JValue, EvaluatorError> {
1277 let op_sym = match op {
1278 CompiledArithOp::Add => "+",
1279 CompiledArithOp::Sub => "-",
1280 CompiledArithOp::Mul => "*",
1281 CompiledArithOp::Div => "/",
1282 CompiledArithOp::Mod => "%",
1283 };
1284 match (left, right) {
1285 (JValue::Number(a), JValue::Number(b)) => {
1286 let result = match op {
1287 CompiledArithOp::Add => *a + *b,
1288 CompiledArithOp::Sub => *a - *b,
1289 CompiledArithOp::Mul => {
1290 let r = *a * *b;
1291 if r.is_infinite() {
1292 return Err(EvaluatorError::EvaluationError(
1293 "D1001: Number out of range".to_string(),
1294 ));
1295 }
1296 r
1297 }
1298 CompiledArithOp::Div => {
1299 if *b == 0.0 {
1300 return Err(EvaluatorError::EvaluationError(
1301 "Division by zero".to_string(),
1302 ));
1303 }
1304 *a / *b
1305 }
1306 CompiledArithOp::Mod => {
1307 if *b == 0.0 {
1308 return Err(EvaluatorError::EvaluationError(
1309 "Division by zero".to_string(),
1310 ));
1311 }
1312 *a % *b
1313 }
1314 };
1315 Ok(JValue::Number(result))
1316 }
1317 (JValue::Null | JValue::Undefined, _) if left_is_explicit_null => {
1319 Err(EvaluatorError::TypeError(format!(
1320 "T2002: The left side of the {} operator must evaluate to a number",
1321 op_sym
1322 )))
1323 }
1324 (_, JValue::Null | JValue::Undefined) if right_is_explicit_null => {
1325 Err(EvaluatorError::TypeError(format!(
1326 "T2002: The right side of the {} operator must evaluate to a number",
1327 op_sym
1328 )))
1329 }
1330 (JValue::Null | JValue::Undefined, _) | (_, JValue::Null | JValue::Undefined) => {
1332 Ok(JValue::Null)
1333 }
1334 _ => Err(EvaluatorError::TypeError(format!(
1335 "Cannot apply {} to {:?} and {:?}",
1336 op_sym, left, right
1337 ))),
1338 }
1339}
1340
1341#[inline]
1343pub(crate) fn compiled_to_concat_string(value: &JValue) -> Result<String, EvaluatorError> {
1344 match value {
1345 JValue::String(s) => Ok(s.to_string()),
1346 JValue::Null | JValue::Undefined => Ok(String::new()),
1347 JValue::Number(_) | JValue::Bool(_) | JValue::Array(_) | JValue::Object(_) => {
1348 match crate::functions::string::string(value, None) {
1349 Ok(JValue::String(s)) => Ok(s.to_string()),
1350 Ok(JValue::Null) => Ok(String::new()),
1351 _ => Err(EvaluatorError::TypeError(
1352 "Cannot concatenate complex types".to_string(),
1353 )),
1354 }
1355 }
1356 _ => Ok(String::new()),
1357 }
1358}
1359
1360#[inline]
1362pub(crate) fn compiled_equal(lhs: &JValue, rhs: &JValue) -> JValue {
1363 JValue::Bool(crate::functions::array::values_equal(lhs, rhs))
1364}
1365
1366#[inline]
1368pub(crate) fn compiled_concat(lhs: JValue, rhs: JValue) -> Result<JValue, EvaluatorError> {
1369 let l = compiled_to_concat_string(&lhs)?;
1370 let r = compiled_to_concat_string(&rhs)?;
1371 Ok(JValue::string(l + &r))
1372}
1373
1374#[inline]
1376pub(crate) fn call_pure_builtin_by_name(
1377 name: &str,
1378 args: &[JValue],
1379 data: &JValue,
1380) -> Result<JValue, EvaluatorError> {
1381 call_pure_builtin(name, args, data)
1382}
1383
1384fn try_compile_path(
1393 steps: &[crate::ast::PathStep],
1394 allowed_vars: Option<&[&str]>,
1395) -> Option<CompiledExpr> {
1396 use crate::ast::{AstNode, Stage};
1397
1398 if steps.is_empty() {
1399 return None;
1400 }
1401
1402 let field_steps: &[crate::ast::PathStep] = match &steps[0].node {
1407 AstNode::Variable(var) if var.is_empty() && steps[0].stages.is_empty() => &steps[1..],
1408 AstNode::Variable(_) => return None,
1409 AstNode::Name(_) => steps,
1410 _ => return None,
1411 };
1412
1413 let compile_filter = |node: &AstNode| -> Option<CompiledExpr> {
1416 if matches!(node, AstNode::Number(_)) {
1417 return None;
1418 }
1419 try_compile_expr_inner(node, allowed_vars)
1420 };
1421
1422 let mut compiled_steps = Vec::with_capacity(field_steps.len());
1428 for step in field_steps {
1429 match &step.node {
1430 AstNode::Name(name) => {
1431 let filter = match step.stages.as_slice() {
1432 [] => None,
1433 [Stage::Filter(filter_node)] => Some(compile_filter(filter_node)?),
1434 _ => return None,
1435 };
1436 compiled_steps.push(CompiledStep {
1437 field: name.clone(),
1438 filter,
1439 });
1440 }
1441 AstNode::Predicate(filter_node) => {
1442 if !step.stages.is_empty() {
1444 return None;
1445 }
1446 let last = compiled_steps.last_mut()?;
1447 if last.filter.is_some() {
1448 return None;
1449 }
1450 last.filter = Some(compile_filter(filter_node)?);
1451 }
1452 _ => return None,
1453 }
1454 }
1455
1456 if compiled_steps.is_empty() {
1457 return Some(CompiledExpr::VariableLookup(String::new()));
1459 }
1460
1461 if allowed_vars.is_some() {
1466 if compiled_steps.len() == 1 && compiled_steps[0].filter.is_none() {
1467 return Some(CompiledExpr::FieldLookup(compiled_steps.remove(0).field));
1468 }
1469 if compiled_steps.len() == 2
1470 && compiled_steps[0].filter.is_none()
1471 && compiled_steps[1].filter.is_none()
1472 {
1473 let outer = compiled_steps.remove(0).field;
1474 let inner = compiled_steps.remove(0).field;
1475 return Some(CompiledExpr::NestedFieldLookup(outer, inner));
1476 }
1477 }
1478
1479 Some(CompiledExpr::FieldPath(compiled_steps))
1480}
1481
1482fn compiled_eval_field_path(
1491 steps: &[CompiledStep],
1492 data: &JValue,
1493 vars: Option<&HashMap<&str, &JValue>>,
1494 ctx: Option<&Context>,
1495 shape: Option<&ShapeCache>,
1496) -> Result<JValue, EvaluatorError> {
1497 let mut current = data.clone();
1498 let mut did_array_mapping = false;
1501 for step in steps {
1502 let is_array = matches!(current, JValue::Array(_));
1504 current = compiled_field_step(&step.field, ¤t);
1506 if is_array {
1507 did_array_mapping = true;
1508 } else {
1509 did_array_mapping = false;
1511 }
1512 if let Some(filter) = &step.filter {
1514 current = compiled_apply_filter(filter, ¤t, vars, ctx, shape)?;
1515 did_array_mapping = true;
1517 }
1518 }
1519 if did_array_mapping {
1521 Ok(match current {
1522 JValue::Array(ref arr) if arr.len() == 1 => arr[0].clone(),
1523 other => other,
1524 })
1525 } else {
1526 Ok(current)
1527 }
1528}
1529
1530fn compiled_field_step(field: &str, value: &JValue) -> JValue {
1537 match value {
1538 JValue::Object(obj) => {
1539 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
1541 if let Some(JValue::Object(inner)) = obj.get("@") {
1542 return inner.get(field).cloned().unwrap_or(JValue::Undefined);
1543 }
1544 return JValue::Undefined;
1545 }
1546 obj.get(field).cloned().unwrap_or(JValue::Undefined)
1547 }
1548 JValue::Array(arr) => {
1549 let shape: Option<ShapeCache> = arr.iter().find_map(|v| {
1551 if let JValue::Object(obj) = v {
1552 if obj.get("__tuple__") != Some(&JValue::Bool(true)) {
1553 return build_shape_cache(v);
1554 }
1555 }
1556 None
1557 });
1558 let mut result = Vec::new();
1559 for item in arr.iter() {
1560 let extracted = if let (Some(ref sh), JValue::Object(obj)) = (&shape, item) {
1561 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
1563 compiled_field_step(field, item)
1564 } else if let Some(&pos) = sh.get(field) {
1565 match obj.get_index(pos) {
1569 Some((k, v)) if k.as_str() == field => v.clone(),
1570 _ => obj.get(field).cloned().unwrap_or(JValue::Undefined),
1571 }
1572 } else {
1573 obj.get(field).cloned().unwrap_or(JValue::Undefined)
1576 }
1577 } else {
1578 compiled_field_step(field, item)
1579 };
1580 match extracted {
1581 JValue::Undefined => {}
1582 JValue::Array(inner) => result.extend(inner.iter().cloned()),
1583 other => result.push(other),
1584 }
1585 }
1586 if result.is_empty() {
1587 JValue::Undefined
1588 } else {
1589 JValue::array(result)
1590 }
1591 }
1592 _ => JValue::Undefined,
1593 }
1594}
1595
1596fn compiled_apply_filter(
1602 filter: &CompiledExpr,
1603 value: &JValue,
1604 vars: Option<&HashMap<&str, &JValue>>,
1605 ctx: Option<&Context>,
1606 shape: Option<&ShapeCache>,
1607) -> Result<JValue, EvaluatorError> {
1608 match value {
1609 JValue::Array(arr) => {
1610 let mut result = Vec::new();
1611 let local_shape: Option<ShapeCache> = if shape.is_none() {
1614 arr.first().and_then(build_shape_cache)
1615 } else {
1616 None
1617 };
1618 let effective_shape = shape.or(local_shape.as_ref());
1619 for item in arr.iter() {
1620 let pred = eval_compiled_inner(filter, item, vars, ctx, effective_shape)?;
1621 if compiled_is_truthy(&pred) {
1622 result.push(item.clone());
1623 }
1624 }
1625 if result.is_empty() {
1626 Ok(JValue::Undefined)
1627 } else if result.len() == 1 {
1628 Ok(result.remove(0))
1629 } else {
1630 Ok(JValue::array(result))
1631 }
1632 }
1633 JValue::Undefined => Ok(JValue::Undefined),
1634 _ => {
1635 let pred = eval_compiled_inner(filter, value, vars, ctx, shape)?;
1636 if compiled_is_truthy(&pred) {
1637 Ok(value.clone())
1638 } else {
1639 Ok(JValue::Undefined)
1640 }
1641 }
1642 }
1643}
1644
1645fn call_pure_builtin(name: &str, args: &[JValue], data: &JValue) -> Result<JValue, EvaluatorError> {
1651 use crate::functions;
1652
1653 let args_storage: Vec<JValue>;
1655 let effective_args: &[JValue] = if args.is_empty() {
1656 match name {
1657 "string" => {
1658 if data.is_undefined() || data.is_null() {
1661 return Ok(JValue::Undefined);
1662 }
1663 args_storage = vec![data.clone()];
1664 &args_storage
1665 }
1666 "number" | "boolean" | "uppercase" | "lowercase" => {
1667 args_storage = vec![data.clone()];
1668 &args_storage
1669 }
1670 _ => args,
1671 }
1672 } else if args.len() == 1 {
1673 match name {
1674 "substringBefore" | "substringAfter" | "contains" | "split" => {
1675 if matches!(data, JValue::String(_)) {
1676 args_storage = std::iter::once(data.clone())
1677 .chain(args.iter().cloned())
1678 .collect();
1679 &args_storage
1680 } else {
1681 args
1682 }
1683 }
1684 _ => args,
1685 }
1686 } else {
1687 args
1688 };
1689
1690 if effective_args.first().is_some_and(JValue::is_undefined) && propagates_undefined(name) {
1694 return Ok(JValue::Undefined);
1695 }
1696
1697 match name {
1698 "string" => {
1700 let prettify = match effective_args.get(1) {
1702 None => None,
1703 Some(JValue::Bool(b)) => Some(*b),
1704 Some(_) => {
1705 return Err(EvaluatorError::TypeError(
1706 "string() prettify parameter must be a boolean".to_string(),
1707 ))
1708 }
1709 };
1710 let arg = effective_args.first().unwrap_or(&JValue::Null);
1711 Ok(functions::string::string(arg, prettify)?)
1712 }
1713 "length" => match effective_args.first() {
1714 Some(JValue::String(s)) => Ok(functions::string::length(s)?),
1715 Some(JValue::Undefined) => Ok(JValue::Undefined),
1717 None => Err(EvaluatorError::EvaluationError(
1720 "length() requires exactly 1 argument".to_string(),
1721 )),
1722 _ => Err(EvaluatorError::TypeError(
1724 "T0410: Argument 1 of function length does not match function signature"
1725 .to_string(),
1726 )),
1727 },
1728 "uppercase" => match effective_args.first() {
1729 Some(JValue::String(s)) => Ok(functions::string::uppercase(s)?),
1730 Some(JValue::Undefined) | None => Ok(JValue::Undefined),
1731 _ => Err(EvaluatorError::TypeError(
1732 "T0410: Argument 1 of function uppercase does not match function signature"
1733 .to_string(),
1734 )),
1735 },
1736 "lowercase" => match effective_args.first() {
1737 Some(JValue::String(s)) => Ok(functions::string::lowercase(s)?),
1738 Some(JValue::Undefined) | None => Ok(JValue::Undefined),
1739 _ => Err(EvaluatorError::TypeError(
1740 "T0410: Argument 1 of function lowercase does not match function signature"
1741 .to_string(),
1742 )),
1743 },
1744 "trim" => match effective_args.first() {
1745 None | Some(JValue::Null | JValue::Undefined) => Ok(JValue::Null),
1746 Some(JValue::String(s)) => Ok(functions::string::trim(s)?),
1747 _ => Err(EvaluatorError::TypeError(
1748 "trim() requires a string argument".to_string(),
1749 )),
1750 },
1751 "substring" => {
1752 if effective_args.len() < 2 {
1753 return Err(EvaluatorError::EvaluationError(
1754 "substring() requires at least 2 arguments".to_string(),
1755 ));
1756 }
1757 match (&effective_args[0], &effective_args[1]) {
1758 (JValue::String(s), JValue::Number(start)) => {
1759 let length = match effective_args.get(2) {
1761 None => None,
1762 Some(JValue::Number(l)) => Some(*l as i64),
1763 Some(_) => {
1764 return Err(EvaluatorError::TypeError(
1765 "T0410: Argument 3 of function substring does not match function signature"
1766 .to_string(),
1767 ))
1768 }
1769 };
1770 Ok(functions::string::substring(s, *start as i64, length)?)
1771 }
1772 _ => Err(EvaluatorError::TypeError(
1773 "T0410: Argument 1 of function substring does not match function signature"
1774 .to_string(),
1775 )),
1776 }
1777 }
1778 "substringBefore" => {
1779 if effective_args.len() != 2 {
1780 return Err(EvaluatorError::TypeError(
1781 "T0411: Context value is not a compatible type with argument 2 of function substringBefore".to_string(),
1782 ));
1783 }
1784 match (&effective_args[0], &effective_args[1]) {
1785 (JValue::String(s), JValue::String(sep)) => {
1786 Ok(functions::string::substring_before(s, sep)?)
1787 }
1788 (JValue::Undefined, _) => Ok(JValue::Undefined),
1790 _ => Err(EvaluatorError::TypeError(
1791 "T0410: Argument 1 of function substringBefore does not match function signature".to_string(),
1792 )),
1793 }
1794 }
1795 "substringAfter" => {
1796 if effective_args.len() != 2 {
1797 return Err(EvaluatorError::TypeError(
1798 "T0411: Context value is not a compatible type with argument 2 of function substringAfter".to_string(),
1799 ));
1800 }
1801 match (&effective_args[0], &effective_args[1]) {
1802 (JValue::String(s), JValue::String(sep)) => {
1803 Ok(functions::string::substring_after(s, sep)?)
1804 }
1805 (JValue::Undefined, _) => Ok(JValue::Undefined),
1807 _ => Err(EvaluatorError::TypeError(
1808 "T0410: Argument 1 of function substringAfter does not match function signature".to_string(),
1809 )),
1810 }
1811 }
1812 "contains" => {
1813 if effective_args.len() != 2 {
1814 return Err(EvaluatorError::EvaluationError(
1815 "contains() requires exactly 2 arguments".to_string(),
1816 ));
1817 }
1818 match &effective_args[0] {
1819 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1820 JValue::String(s) => Ok(functions::string::contains(s, &effective_args[1])?),
1821 _ => Err(EvaluatorError::TypeError(
1822 "contains() requires a string as the first argument".to_string(),
1823 )),
1824 }
1825 }
1826 "split" => {
1827 if effective_args.len() < 2 {
1828 return Err(EvaluatorError::EvaluationError(
1829 "split() requires at least 2 arguments".to_string(),
1830 ));
1831 }
1832 match &effective_args[0] {
1833 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1834 JValue::String(s) => {
1835 let limit = match effective_args.get(2) {
1837 None => None,
1838 Some(JValue::Number(n)) => {
1839 if *n < 0.0 {
1840 return Err(EvaluatorError::EvaluationError(
1841 "D3020: Third argument of split function must be a positive number"
1842 .to_string(),
1843 ));
1844 }
1845 Some(n.floor() as usize)
1846 }
1847 Some(_) => {
1848 return Err(EvaluatorError::TypeError(
1849 "split() limit must be a number".to_string(),
1850 ))
1851 }
1852 };
1853 Ok(functions::string::split(s, &effective_args[1], limit)?)
1854 }
1855 _ => Err(EvaluatorError::TypeError(
1856 "split() requires a string as the first argument".to_string(),
1857 )),
1858 }
1859 }
1860 "join" => {
1861 if effective_args.is_empty() {
1862 return Err(EvaluatorError::TypeError(
1863 "T0410: Argument 1 of function $join does not match function signature"
1864 .to_string(),
1865 ));
1866 }
1867 match &effective_args[0] {
1868 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1869 JValue::Bool(_) | JValue::Number(_) | JValue::Object(_) => {
1871 Err(EvaluatorError::TypeError(
1872 "T0412: Argument 1 of function $join must be an array of String"
1873 .to_string(),
1874 ))
1875 }
1876 JValue::Array(arr) => {
1877 for item in arr.iter() {
1879 if !matches!(item, JValue::String(_)) {
1880 return Err(EvaluatorError::TypeError(
1881 "T0412: Argument 1 of function $join must be an array of String"
1882 .to_string(),
1883 ));
1884 }
1885 }
1886 let separator = match effective_args.get(1) {
1888 None | Some(JValue::Undefined) => None,
1889 Some(JValue::String(s)) => Some(&**s),
1890 Some(_) => {
1891 return Err(EvaluatorError::TypeError(
1892 "T0410: Argument 2 of function $join does not match function signature (expected String)"
1893 .to_string(),
1894 ))
1895 }
1896 };
1897 Ok(functions::string::join(arr, separator)?)
1898 }
1899 JValue::String(s) => Ok(JValue::String(s.clone())),
1900 _ => Err(EvaluatorError::TypeError(
1901 "T0412: Argument 1 of function $join must be an array of String".to_string(),
1902 )),
1903 }
1904 }
1905
1906 "number" => match effective_args.first() {
1908 Some(v) => Ok(functions::numeric::number(v)?),
1909 None => Err(EvaluatorError::EvaluationError(
1910 "number() requires at least 1 argument".to_string(),
1911 )),
1912 },
1913 "floor" => match effective_args.first() {
1914 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1915 Some(JValue::Number(n)) => Ok(functions::numeric::floor(*n)?),
1916 _ => Err(EvaluatorError::TypeError(
1917 "floor() requires a number argument".to_string(),
1918 )),
1919 },
1920 "ceil" => match effective_args.first() {
1921 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1922 Some(JValue::Number(n)) => Ok(functions::numeric::ceil(*n)?),
1923 _ => Err(EvaluatorError::TypeError(
1924 "ceil() requires a number argument".to_string(),
1925 )),
1926 },
1927 "round" => match effective_args.first() {
1928 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1929 Some(JValue::Number(n)) => {
1930 let precision = effective_args.get(1).and_then(|v| {
1931 if let JValue::Number(p) = v {
1932 Some(*p as i32)
1933 } else {
1934 None
1935 }
1936 });
1937 Ok(functions::numeric::round(*n, precision)?)
1938 }
1939 _ => Err(EvaluatorError::TypeError(
1940 "round() requires a number argument".to_string(),
1941 )),
1942 },
1943 "abs" => match effective_args.first() {
1944 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1945 Some(JValue::Number(n)) => Ok(functions::numeric::abs(*n)?),
1946 _ => Err(EvaluatorError::TypeError(
1947 "abs() requires a number argument".to_string(),
1948 )),
1949 },
1950 "sqrt" => match effective_args.first() {
1951 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1952 Some(JValue::Number(n)) => Ok(functions::numeric::sqrt(*n)?),
1953 _ => Err(EvaluatorError::TypeError(
1954 "sqrt() requires a number argument".to_string(),
1955 )),
1956 },
1957
1958 "sum" => match effective_args.first() {
1960 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1961 None => Err(EvaluatorError::EvaluationError(
1962 "sum() requires exactly 1 argument".to_string(),
1963 )),
1964 Some(JValue::Null) => Ok(JValue::Null),
1965 Some(JValue::Array(arr)) => Ok(aggregation::sum(arr)?),
1966 Some(JValue::Number(n)) => Ok(JValue::Number(*n)),
1967 Some(other) => Ok(functions::numeric::sum(&[other.clone()])?),
1968 },
1969 "max" => match effective_args.first() {
1970 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1971 Some(JValue::Null) | None => Ok(JValue::Null),
1972 Some(JValue::Array(arr)) => Ok(aggregation::max(arr)?),
1973 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1974 _ => Err(EvaluatorError::TypeError(
1975 "max() requires an array or number argument".to_string(),
1976 )),
1977 },
1978 "min" => match effective_args.first() {
1979 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1980 Some(JValue::Null) | None => Ok(JValue::Null),
1981 Some(JValue::Array(arr)) => Ok(aggregation::min(arr)?),
1982 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1983 _ => Err(EvaluatorError::TypeError(
1984 "min() requires an array or number argument".to_string(),
1985 )),
1986 },
1987 "average" => match effective_args.first() {
1988 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1989 Some(JValue::Null) | None => Ok(JValue::Null),
1990 Some(JValue::Array(arr)) => Ok(aggregation::average(arr)?),
1991 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1992 _ => Err(EvaluatorError::TypeError(
1993 "average() requires an array or number argument".to_string(),
1994 )),
1995 },
1996 "count" => match effective_args.first() {
1997 Some(v) if v.is_undefined() => Ok(JValue::from(0i64)),
1998 Some(JValue::Null) | None => Ok(JValue::from(0i64)),
1999 Some(JValue::Array(arr)) => Ok(functions::array::count(arr)?),
2000 _ => Ok(JValue::from(1i64)),
2001 },
2002
2003 "boolean" => match effective_args.first() {
2005 Some(v) => Ok(functions::boolean::boolean(v)?),
2006 None => Err(EvaluatorError::EvaluationError(
2007 "boolean() requires 1 argument".to_string(),
2008 )),
2009 },
2010 "not" => match effective_args.first() {
2011 Some(v) => Ok(JValue::Bool(!compiled_is_truthy(v))),
2012 None => Err(EvaluatorError::EvaluationError(
2013 "not() requires 1 argument".to_string(),
2014 )),
2015 },
2016
2017 "append" => {
2019 if effective_args.len() != 2 {
2020 return Err(EvaluatorError::EvaluationError(
2021 "append() requires exactly 2 arguments".to_string(),
2022 ));
2023 }
2024 let first = &effective_args[0];
2025 let second = &effective_args[1];
2026 if matches!(second, JValue::Null | JValue::Undefined) {
2027 return Ok(first.clone());
2028 }
2029 if matches!(first, JValue::Null | JValue::Undefined) {
2030 return Ok(second.clone());
2031 }
2032 let arr = match first {
2033 JValue::Array(a) => a.to_vec(),
2034 other => vec![other.clone()],
2035 };
2036 Ok(functions::array::append(&arr, second)?)
2037 }
2038 "reverse" => match effective_args.first() {
2039 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
2040 Some(JValue::Array(arr)) => Ok(functions::array::reverse(arr)?),
2041 _ => Err(EvaluatorError::TypeError(
2042 "reverse() requires an array argument".to_string(),
2043 )),
2044 },
2045 "distinct" => match effective_args.first() {
2046 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
2047 Some(JValue::Array(arr)) => Ok(functions::array::distinct(arr)?),
2048 _ => Err(EvaluatorError::TypeError(
2049 "distinct() requires an array argument".to_string(),
2050 )),
2051 },
2052
2053 "keys" => match effective_args.first() {
2055 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
2056 Some(JValue::Lambda { .. } | JValue::Builtin { .. }) => Ok(JValue::Null),
2057 Some(JValue::Object(obj)) => {
2058 if obj.is_empty() {
2059 Ok(JValue::Null)
2060 } else {
2061 let keys: Vec<JValue> = obj.keys().map(|k| JValue::string(k.clone())).collect();
2062 if keys.len() == 1 {
2063 Ok(keys.into_iter().next().unwrap())
2064 } else {
2065 Ok(JValue::array(keys))
2066 }
2067 }
2068 }
2069 Some(JValue::Array(arr)) => {
2070 let mut all_keys: Vec<JValue> = Vec::new();
2071 for item in arr.iter() {
2072 if let JValue::Object(obj) = item {
2073 for key in obj.keys() {
2074 let k = JValue::string(key.clone());
2075 if !all_keys.contains(&k) {
2076 all_keys.push(k);
2077 }
2078 }
2079 }
2080 }
2081 if all_keys.is_empty() {
2082 Ok(JValue::Null)
2083 } else if all_keys.len() == 1 {
2084 Ok(all_keys.into_iter().next().unwrap())
2085 } else {
2086 Ok(JValue::array(all_keys))
2087 }
2088 }
2089 _ => Ok(JValue::Null),
2090 },
2091 "merge" => match effective_args.len() {
2092 0 => Err(EvaluatorError::EvaluationError(
2093 "merge() requires at least 1 argument".to_string(),
2094 )),
2095 1 => match &effective_args[0] {
2096 JValue::Array(arr) => Ok(functions::object::merge(arr)?),
2097 JValue::Null | JValue::Undefined => Ok(JValue::Null),
2098 JValue::Object(_) => Ok(effective_args[0].clone()),
2099 _ => Err(EvaluatorError::TypeError(
2100 "merge() requires objects or an array of objects".to_string(),
2101 )),
2102 },
2103 _ => Ok(functions::object::merge(effective_args)?),
2104 },
2105
2106 _ => unreachable!(
2107 "call_pure_builtin called with non-compilable builtin: {}",
2108 name
2109 ),
2110 }
2111}
2112
2113const UNDEFINED_PROPAGATING_FUNCTIONS: &[&str] = &[
2121 "not",
2122 "boolean",
2123 "length",
2124 "number",
2125 "uppercase",
2126 "lowercase",
2127 "substring",
2128 "substringBefore",
2129 "substringAfter",
2130 "string",
2131];
2132
2133fn propagates_undefined(name: &str) -> bool {
2135 UNDEFINED_PROPAGATING_FUNCTIONS.contains(&name)
2136}
2137
2138mod aggregation {
2141 use super::*;
2142
2143 fn for_each_numeric(
2146 arr: &[JValue],
2147 func_name: &str,
2148 mut f: impl FnMut(f64),
2149 ) -> Result<(), EvaluatorError> {
2150 fn recurse(
2151 arr: &[JValue],
2152 func_name: &str,
2153 f: &mut dyn FnMut(f64),
2154 ) -> Result<(), EvaluatorError> {
2155 for value in arr.iter() {
2156 match value {
2157 JValue::Array(inner) => recurse(inner, func_name, f)?,
2158 JValue::Number(n) => {
2159 f(*n);
2160 }
2161 _ => {
2162 return Err(EvaluatorError::TypeError(format!(
2163 "{}() requires all array elements to be numbers",
2164 func_name
2165 )));
2166 }
2167 }
2168 }
2169 Ok(())
2170 }
2171 recurse(arr, func_name, &mut f)
2172 }
2173
2174 fn count_numeric(arr: &[JValue], func_name: &str) -> Result<usize, EvaluatorError> {
2176 let mut count = 0usize;
2177 for_each_numeric(arr, func_name, |_| count += 1)?;
2178 Ok(count)
2179 }
2180
2181 pub fn sum(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
2182 if arr.is_empty() {
2183 return Ok(JValue::from(0i64));
2184 }
2185 let mut total = 0.0f64;
2186 for_each_numeric(arr, "sum", |n| total += n)?;
2187 Ok(JValue::Number(total))
2188 }
2189
2190 pub fn max(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
2191 if arr.is_empty() {
2192 return Ok(JValue::Null);
2193 }
2194 let mut max_val = f64::NEG_INFINITY;
2195 for_each_numeric(arr, "max", |n| {
2196 if n > max_val {
2197 max_val = n;
2198 }
2199 })?;
2200 Ok(JValue::Number(max_val))
2201 }
2202
2203 pub fn min(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
2204 if arr.is_empty() {
2205 return Ok(JValue::Null);
2206 }
2207 let mut min_val = f64::INFINITY;
2208 for_each_numeric(arr, "min", |n| {
2209 if n < min_val {
2210 min_val = n;
2211 }
2212 })?;
2213 Ok(JValue::Number(min_val))
2214 }
2215
2216 pub fn average(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
2217 if arr.is_empty() {
2218 return Ok(JValue::Null);
2219 }
2220 let mut total = 0.0f64;
2221 let count = count_numeric(arr, "average")?;
2222 for_each_numeric(arr, "average", |n| total += n)?;
2223 Ok(JValue::Number(total / count as f64))
2224 }
2225}
2226
2227#[derive(Error, Debug)]
2229pub enum EvaluatorError {
2230 #[error("Type error: {0}")]
2231 TypeError(String),
2232
2233 #[error("Reference error: {0}")]
2234 ReferenceError(String),
2235
2236 #[error("Evaluation error: {0}")]
2237 EvaluationError(String),
2238}
2239
2240impl From<crate::functions::FunctionError> for EvaluatorError {
2241 fn from(e: crate::functions::FunctionError) -> Self {
2242 EvaluatorError::EvaluationError(e.to_string())
2243 }
2244}
2245
2246impl From<crate::datetime::DateTimeError> for EvaluatorError {
2247 fn from(e: crate::datetime::DateTimeError) -> Self {
2248 EvaluatorError::EvaluationError(e.to_string())
2249 }
2250}
2251
2252enum LambdaResult {
2255 JValue(JValue),
2257 TailCall {
2259 lambda: Box<StoredLambda>,
2261 args: Vec<JValue>,
2263 data: JValue,
2265 },
2266}
2267
2268#[derive(Clone, Debug)]
2272pub struct StoredLambda {
2273 pub params: Vec<String>,
2274 pub body: AstNode,
2275 pub(crate) compiled_body: Option<CompiledExpr>,
2278 pub signature: Option<String>,
2279 pub captured_env: HashMap<String, JValue>,
2281 pub captured_data: Option<JValue>,
2283 pub thunk: bool,
2285}
2286
2287struct Scope {
2289 bindings: HashMap<String, JValue>,
2290 lambdas: HashMap<String, StoredLambda>,
2291}
2292
2293impl Scope {
2294 fn new() -> Self {
2295 Scope {
2296 bindings: HashMap::new(),
2297 lambdas: HashMap::new(),
2298 }
2299 }
2300}
2301
2302pub struct Context {
2307 scope_stack: Vec<Scope>,
2308 parent_data: Option<JValue>,
2309}
2310
2311impl Context {
2312 pub fn new() -> Self {
2313 Context {
2314 scope_stack: vec![Scope::new()],
2315 parent_data: None,
2316 }
2317 }
2318
2319 fn push_scope(&mut self) {
2321 self.scope_stack.push(Scope::new());
2322 }
2323
2324 fn pop_scope(&mut self) {
2326 if self.scope_stack.len() > 1 {
2327 self.scope_stack.pop();
2328 }
2329 }
2330
2331 fn pop_scope_preserving_lambdas(&mut self, lambda_ids: &[String]) {
2333 if self.scope_stack.len() > 1 {
2334 let popped = self.scope_stack.pop().unwrap();
2335 if !lambda_ids.is_empty() {
2336 let top = self.scope_stack.last_mut().unwrap();
2337 for id in lambda_ids {
2338 if let Some(stored) = popped.lambdas.get(id) {
2339 top.lambdas.insert(id.clone(), stored.clone());
2340 }
2341 }
2342 }
2343 }
2344 }
2345
2346 fn clear_current_scope(&mut self) {
2348 let top = self.scope_stack.last_mut().unwrap();
2349 top.bindings.clear();
2350 top.lambdas.clear();
2351 }
2352
2353 pub fn bind(&mut self, name: String, value: JValue) {
2354 self.scope_stack
2355 .last_mut()
2356 .unwrap()
2357 .bindings
2358 .insert(name, value);
2359 }
2360
2361 pub fn bind_lambda(&mut self, name: String, lambda: StoredLambda) {
2362 self.scope_stack
2363 .last_mut()
2364 .unwrap()
2365 .lambdas
2366 .insert(name, lambda);
2367 }
2368
2369 pub fn unbind(&mut self, name: &str) {
2370 let top = self.scope_stack.last_mut().unwrap();
2372 top.bindings.remove(name);
2373 top.lambdas.remove(name);
2374 }
2375
2376 pub fn lookup(&self, name: &str) -> Option<&JValue> {
2377 for scope in self.scope_stack.iter().rev() {
2379 if let Some(value) = scope.bindings.get(name) {
2380 return Some(value);
2381 }
2382 }
2383 None
2384 }
2385
2386 pub fn lookup_lambda(&self, name: &str) -> Option<&StoredLambda> {
2387 for scope in self.scope_stack.iter().rev() {
2389 if let Some(lambda) = scope.lambdas.get(name) {
2390 return Some(lambda);
2391 }
2392 }
2393 None
2394 }
2395
2396 pub fn set_parent(&mut self, data: JValue) {
2397 self.parent_data = Some(data);
2398 }
2399
2400 pub fn get_parent(&self) -> Option<&JValue> {
2401 self.parent_data.as_ref()
2402 }
2403
2404 fn all_bindings(&self) -> HashMap<String, JValue> {
2407 let mut result = HashMap::new();
2408 for scope in &self.scope_stack {
2409 for (k, v) in &scope.bindings {
2410 result.insert(k.clone(), v.clone());
2411 }
2412 }
2413 result
2414 }
2415}
2416
2417impl Default for Context {
2418 fn default() -> Self {
2419 Self::new()
2420 }
2421}
2422
2423pub struct Evaluator {
2425 context: Context,
2426 recursion_depth: usize,
2427 max_recursion_depth: usize,
2428}
2429
2430impl Evaluator {
2431 pub fn new() -> Self {
2432 Evaluator {
2433 context: Context::new(),
2434 recursion_depth: 0,
2435 max_recursion_depth: 302,
2438 }
2439 }
2440
2441 pub fn with_context(context: Context) -> Self {
2442 Evaluator {
2443 context,
2444 recursion_depth: 0,
2445 max_recursion_depth: 302,
2446 }
2447 }
2448
2449 fn invoke_stored_lambda(
2453 &mut self,
2454 stored: &StoredLambda,
2455 args: &[JValue],
2456 data: &JValue,
2457 ) -> Result<JValue, EvaluatorError> {
2458 if let Some(ref ce) = stored.compiled_body {
2462 if stored.signature.is_none()
2463 && !stored.thunk
2464 && !stored
2465 .captured_env
2466 .values()
2467 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
2468 {
2469 let call_data = stored.captured_data.as_ref().unwrap_or(data);
2470 let vars: HashMap<&str, &JValue> = stored
2471 .params
2472 .iter()
2473 .zip(args.iter())
2474 .map(|(p, v)| (p.as_str(), v))
2475 .chain(stored.captured_env.iter().map(|(k, v)| (k.as_str(), v)))
2476 .collect();
2477 return eval_compiled(ce, call_data, Some(&vars));
2478 }
2479 }
2480
2481 let captured_env = if stored.captured_env.is_empty() {
2482 None
2483 } else {
2484 Some(&stored.captured_env)
2485 };
2486 let captured_data = stored.captured_data.as_ref();
2487 self.invoke_lambda_with_env(
2488 &stored.params,
2489 &stored.body,
2490 stored.signature.as_ref(),
2491 args,
2492 data,
2493 captured_env,
2494 captured_data,
2495 stored.thunk,
2496 )
2497 }
2498
2499 fn lookup_lambda_from_value(&self, value: &JValue) -> Option<StoredLambda> {
2503 if let JValue::Lambda { lambda_id, .. } = value {
2504 return self.context.lookup_lambda(lambda_id).cloned();
2505 }
2506 None
2507 }
2508
2509 fn get_callback_param_count(&self, func_node: &AstNode) -> usize {
2513 match func_node {
2514 AstNode::Lambda { params, .. } => params.len(),
2515 AstNode::Variable(var_name) => {
2516 if let Some(stored_lambda) = self.context.lookup_lambda(var_name) {
2518 return stored_lambda.params.len();
2519 }
2520 if let Some(value) = self.context.lookup(var_name) {
2522 if let Some(stored_lambda) = self.lookup_lambda_from_value(value) {
2523 return stored_lambda.params.len();
2524 }
2525 }
2526 usize::MAX
2528 }
2529 AstNode::Function { .. } => {
2530 usize::MAX
2533 }
2534 _ => usize::MAX,
2535 }
2536 }
2537
2538 fn merge_sort_specialized(arr: &mut [JValue], spec: &SpecializedSortComparator) {
2542 if arr.len() <= 1 {
2543 return;
2544 }
2545
2546 let keys: Vec<SortKey> = arr
2548 .iter()
2549 .map(|item| match item {
2550 JValue::Object(obj) => match obj.get(&spec.field) {
2551 Some(JValue::Number(n)) => SortKey::Num(*n),
2552 Some(JValue::String(s)) => SortKey::Str(s.clone()),
2553 _ => SortKey::None,
2554 },
2555 _ => SortKey::None,
2556 })
2557 .collect();
2558
2559 let mut perm: Vec<usize> = (0..arr.len()).collect();
2561 perm.sort_by(|&a, &b| compare_sort_keys(&keys[a], &keys[b], spec.descending));
2562
2563 let mut placed = vec![false; arr.len()];
2565 for i in 0..arr.len() {
2566 if placed[i] || perm[i] == i {
2567 continue;
2568 }
2569 let mut j = i;
2570 loop {
2571 let target = perm[j];
2572 placed[j] = true;
2573 if target == i {
2574 break;
2575 }
2576 arr.swap(j, target);
2577 j = target;
2578 }
2579 }
2580 }
2581
2582 fn merge_sort_with_comparator(
2586 &mut self,
2587 arr: &mut [JValue],
2588 comparator: &AstNode,
2589 data: &JValue,
2590 ) -> Result<(), EvaluatorError> {
2591 if arr.len() <= 1 {
2592 return Ok(());
2593 }
2594
2595 if let AstNode::Lambda { params, body, .. } = comparator {
2598 if params.len() >= 2 {
2599 if let Some(spec) = try_specialize_sort_comparator(body, ¶ms[0], ¶ms[1]) {
2600 Self::merge_sort_specialized(arr, &spec);
2601 return Ok(());
2602 }
2603 }
2604 }
2605
2606 let mid = arr.len() / 2;
2607
2608 self.merge_sort_with_comparator(&mut arr[..mid], comparator, data)?;
2610
2611 self.merge_sort_with_comparator(&mut arr[mid..], comparator, data)?;
2613
2614 let mut temp = Vec::with_capacity(arr.len());
2616 let (left, right) = arr.split_at(mid);
2617
2618 let mut i = 0;
2619 let mut j = 0;
2620
2621 if let AstNode::Lambda { params, body, .. } = comparator {
2624 if params.len() >= 2 {
2625 let param0 = params[0].clone();
2627 let param1 = params[1].clone();
2628 self.context.push_scope();
2629 while i < left.len() && j < right.len() {
2630 self.context.clear_current_scope();
2632 self.context.bind(param0.clone(), left[i].clone());
2633 self.context.bind(param1.clone(), right[j].clone());
2634
2635 let cmp_result = self.evaluate_internal(body, data)?;
2636
2637 if self.is_truthy(&cmp_result) {
2638 temp.push(right[j].clone());
2639 j += 1;
2640 } else {
2641 temp.push(left[i].clone());
2642 i += 1;
2643 }
2644 }
2645 self.context.pop_scope();
2646 } else {
2647 while i < left.len() && j < right.len() {
2649 let cmp_result = self.apply_function(
2650 comparator,
2651 &[left[i].clone(), right[j].clone()],
2652 data,
2653 )?;
2654 if self.is_truthy(&cmp_result) {
2655 temp.push(right[j].clone());
2656 j += 1;
2657 } else {
2658 temp.push(left[i].clone());
2659 i += 1;
2660 }
2661 }
2662 }
2663 } else {
2664 while i < left.len() && j < right.len() {
2666 let cmp_result =
2667 self.apply_function(comparator, &[left[i].clone(), right[j].clone()], data)?;
2668 if self.is_truthy(&cmp_result) {
2669 temp.push(right[j].clone());
2670 j += 1;
2671 } else {
2672 temp.push(left[i].clone());
2673 i += 1;
2674 }
2675 }
2676 }
2677
2678 temp.extend_from_slice(&left[i..]);
2680 temp.extend_from_slice(&right[j..]);
2681
2682 for (i, val) in temp.into_iter().enumerate() {
2684 arr[i] = val;
2685 }
2686
2687 Ok(())
2688 }
2689
2690 pub fn evaluate(&mut self, node: &AstNode, data: &JValue) -> Result<JValue, EvaluatorError> {
2695 if self.context.get_parent().is_none() {
2697 self.context.set_parent(data.clone());
2698 }
2699
2700 self.evaluate_internal(node, data)
2701 }
2702
2703 #[inline(always)]
2707 fn evaluate_leaf(
2708 &mut self,
2709 node: &AstNode,
2710 data: &JValue,
2711 ) -> Option<Result<JValue, EvaluatorError>> {
2712 match node {
2713 AstNode::String(s) => Some(Ok(JValue::string(s.clone()))),
2714 AstNode::Number(n) => {
2715 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2716 Some(Ok(JValue::from(*n as i64)))
2717 } else {
2718 Some(Ok(JValue::Number(*n)))
2719 }
2720 }
2721 AstNode::Boolean(b) => Some(Ok(JValue::Bool(*b))),
2722 AstNode::Null => Some(Ok(JValue::Null)),
2723 AstNode::Undefined => Some(Ok(JValue::Undefined)),
2724 AstNode::Name(field_name) => match data {
2725 JValue::Object(obj) => {
2727 Some(Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null)))
2728 }
2729 _ => None,
2730 },
2731 AstNode::Variable(name) if !name.is_empty() => {
2732 if let JValue::Object(obj) = data {
2734 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2735 return None; }
2737 }
2738 self.context.lookup(name).map(|value| Ok(value.clone()))
2740 }
2741 _ => None,
2742 }
2743 }
2744
2745 fn evaluate_internal(
2747 &mut self,
2748 node: &AstNode,
2749 data: &JValue,
2750 ) -> Result<JValue, EvaluatorError> {
2751 if let Some(result) = self.evaluate_leaf(node, data) {
2753 return result;
2754 }
2755
2756 self.recursion_depth += 1;
2758 if self.recursion_depth > self.max_recursion_depth {
2759 self.recursion_depth -= 1;
2760 return Err(EvaluatorError::EvaluationError(format!(
2761 "U1001: Stack overflow - maximum recursion depth ({}) exceeded",
2762 self.max_recursion_depth
2763 )));
2764 }
2765
2766 let result = self.evaluate_internal_impl(node, data);
2767
2768 self.recursion_depth -= 1;
2769 result
2770 }
2771
2772 fn evaluate_internal_impl(
2774 &mut self,
2775 node: &AstNode,
2776 data: &JValue,
2777 ) -> Result<JValue, EvaluatorError> {
2778 match node {
2779 AstNode::String(s) => Ok(JValue::string(s.clone())),
2780
2781 AstNode::Name(field_name) => {
2783 match data {
2784 JValue::Object(obj) => Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null)),
2785 JValue::Array(arr) => {
2786 let mut result = Vec::new();
2788 for item in arr.iter() {
2789 if let JValue::Object(obj) = item {
2790 if let Some(val) = obj.get(field_name) {
2791 result.push(val.clone());
2792 }
2793 }
2794 }
2795 if result.is_empty() {
2796 Ok(JValue::Null)
2797 } else if result.len() == 1 {
2798 Ok(result.into_iter().next().unwrap())
2799 } else {
2800 Ok(JValue::array(result))
2801 }
2802 }
2803 _ => Ok(JValue::Null),
2804 }
2805 }
2806
2807 AstNode::Number(n) => {
2808 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2810 Ok(JValue::from(*n as i64))
2812 } else {
2813 Ok(JValue::Number(*n))
2814 }
2815 }
2816 AstNode::Boolean(b) => Ok(JValue::Bool(*b)),
2817 AstNode::Null => Ok(JValue::Null),
2818 AstNode::Undefined => Ok(JValue::Undefined),
2819 AstNode::Placeholder => {
2820 Err(EvaluatorError::EvaluationError(
2823 "Placeholder '?' can only be used as a function argument".to_string(),
2824 ))
2825 }
2826 AstNode::Regex { pattern, flags } => {
2827 Ok(JValue::regex(pattern.as_str(), flags.as_str()))
2830 }
2831
2832 AstNode::Variable(name) => {
2833 if name.is_empty() {
2837 if let Some(value) = self.context.lookup("$") {
2838 return Ok(value.clone());
2839 }
2840 if let JValue::Object(obj) = data {
2842 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2843 if let Some(inner) = obj.get("@") {
2844 return Ok(inner.clone());
2845 }
2846 }
2847 }
2848 return Ok(data.clone());
2849 }
2850
2851 if let Some(value) = self.context.lookup(name) {
2855 return Ok(value.clone());
2856 }
2857
2858 if let JValue::Object(obj) = data {
2861 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2862 let binding_key = format!("${}", name);
2864 if let Some(binding_value) = obj.get(&binding_key) {
2865 return Ok(binding_value.clone());
2866 }
2867 }
2868 }
2869
2870 if let Some(stored_lambda) = self.context.lookup_lambda(name) {
2872 let lambda_repr = JValue::lambda(
2876 name.as_str(),
2877 stored_lambda.params.clone(),
2878 Some(name.to_string()),
2879 stored_lambda.signature.clone(),
2880 );
2881 return Ok(lambda_repr);
2882 }
2883
2884 if self.is_builtin_function(name) {
2886 let builtin_repr = JValue::builtin(name.as_str());
2889 return Ok(builtin_repr);
2890 }
2891
2892 Ok(JValue::Null)
2896 }
2897
2898 AstNode::ParentVariable(name) => {
2899 if name.is_empty() {
2901 return self.context.get_parent().cloned().ok_or_else(|| {
2902 EvaluatorError::ReferenceError("Parent context not available".to_string())
2903 });
2904 }
2905
2906 let parent_data = self.context.get_parent().ok_or_else(|| {
2909 EvaluatorError::ReferenceError("Parent context not available".to_string())
2910 })?;
2911
2912 match parent_data {
2914 JValue::Object(obj) => Ok(obj.get(name).cloned().unwrap_or(JValue::Null)),
2915 _ => Ok(JValue::Null),
2916 }
2917 }
2918
2919 AstNode::Path { steps } => self.evaluate_path(steps, data),
2920
2921 AstNode::Binary { op, lhs, rhs } => self.evaluate_binary_op(*op, lhs, rhs, data),
2922
2923 AstNode::Unary { op, operand } => self.evaluate_unary_op(*op, operand, data),
2924
2925 AstNode::Array(elements) => {
2927 let mut result = Vec::with_capacity(elements.len());
2931 for element in elements {
2932 let is_array_constructor = matches!(element, AstNode::Array(_));
2934
2935 let value = self.evaluate_internal(element, data)?;
2936
2937 if value.is_undefined() {
2940 continue;
2941 }
2942
2943 if is_array_constructor {
2944 result.push(value);
2946 } else if let JValue::Array(arr) = value {
2947 result.extend(arr.iter().cloned());
2949 } else {
2950 result.push(value);
2952 }
2953 }
2954 Ok(JValue::array(result))
2955 }
2956
2957 AstNode::Object(pairs) => {
2958 let mut result = IndexMap::with_capacity(pairs.len());
2959
2960 let all_literal_keys = pairs.iter().all(|(k, _)| matches!(k, AstNode::String(_)));
2962
2963 if all_literal_keys {
2964 for (key_node, value_node) in pairs.iter() {
2966 let key = match key_node {
2967 AstNode::String(s) => s,
2968 _ => unreachable!(),
2969 };
2970 let value = self.evaluate_internal(value_node, data)?;
2971 if value.is_undefined() {
2972 continue;
2973 }
2974 result.insert(key.clone(), value);
2975 }
2976 } else {
2977 let mut key_sources: HashMap<String, usize> = HashMap::new();
2978 for (pair_index, (key_node, value_node)) in pairs.iter().enumerate() {
2979 let key = match self.evaluate_internal(key_node, data)? {
2980 JValue::String(s) => s,
2981 JValue::Null => continue,
2982 other => {
2983 if other.is_undefined() {
2984 continue;
2985 }
2986 return Err(EvaluatorError::TypeError(format!(
2987 "Object key must be a string, got: {:?}",
2988 other
2989 )));
2990 }
2991 };
2992
2993 if let Some(&existing_idx) = key_sources.get(&*key) {
2994 if existing_idx != pair_index {
2995 return Err(EvaluatorError::EvaluationError(format!(
2996 "D1009: Multiple key expressions evaluate to same key: {}",
2997 key
2998 )));
2999 }
3000 }
3001 key_sources.insert(key.to_string(), pair_index);
3002
3003 let value = self.evaluate_internal(value_node, data)?;
3004 if value.is_undefined() {
3005 continue;
3006 }
3007 result.insert(key.to_string(), value);
3008 }
3009 }
3010 Ok(JValue::object(result))
3011 }
3012
3013 AstNode::ObjectTransform { input, pattern } => {
3015 let input_value = self.evaluate_internal(input, data)?;
3017
3018 if input_value.is_undefined() {
3020 return Ok(JValue::Undefined);
3021 }
3022
3023 let items: Vec<JValue> = match input_value {
3025 JValue::Array(ref arr) => (**arr).clone(),
3026 JValue::Null => return Ok(JValue::Null),
3027 other => vec![other],
3028 };
3029
3030 let items = if items.is_empty() {
3032 vec![JValue::Undefined]
3033 } else {
3034 items
3035 };
3036
3037 let mut groups: HashMap<String, (Vec<JValue>, usize)> = HashMap::new();
3041
3042 let saved_dollar = self.context.lookup("$").cloned();
3044
3045 for item in &items {
3046 self.context.bind("$".to_string(), item.clone());
3048
3049 for (pair_index, (key_node, _value_node)) in pattern.iter().enumerate() {
3050 let key = match self.evaluate_internal(key_node, item)? {
3052 JValue::String(s) => s,
3053 JValue::Null => continue, other => {
3055 if other.is_undefined() {
3057 continue;
3058 }
3059 return Err(EvaluatorError::TypeError(format!(
3060 "T1003: Object key must be a string, got: {:?}",
3061 other
3062 )));
3063 }
3064 };
3065
3066 if let Some((existing_data, existing_idx)) = groups.get_mut(&*key) {
3068 if *existing_idx != pair_index {
3070 return Err(EvaluatorError::EvaluationError(format!(
3072 "D1009: Multiple key expressions evaluate to same key: {}",
3073 key
3074 )));
3075 }
3076 existing_data.push(item.clone());
3078 } else {
3079 groups.insert(key.to_string(), (vec![item.clone()], pair_index));
3081 }
3082 }
3083 }
3084
3085 let mut result = IndexMap::new();
3087
3088 for (key, (grouped_data, expr_index)) in groups {
3089 let (_key_node, value_node) = &pattern[expr_index];
3091
3092 let context = if grouped_data.len() == 1 {
3096 grouped_data.into_iter().next().unwrap()
3097 } else {
3098 JValue::array(grouped_data)
3099 };
3100
3101 self.context.bind("$".to_string(), context.clone());
3103
3104 let value = self.evaluate_internal(value_node, &context)?;
3106
3107 if !value.is_undefined() {
3109 result.insert(key, value);
3110 }
3111 }
3112
3113 if let Some(saved) = saved_dollar {
3115 self.context.bind("$".to_string(), saved);
3116 } else {
3117 self.context.unbind("$");
3118 }
3119
3120 Ok(JValue::object(result))
3121 }
3122
3123 AstNode::Function {
3124 name,
3125 args,
3126 is_builtin,
3127 } => self.evaluate_function_call(name, args, *is_builtin, data),
3128
3129 AstNode::Call { procedure, args } => {
3132 let callable = self.evaluate_internal(procedure, data)?;
3134
3135 if let Some(stored_lambda) = self.lookup_lambda_from_value(&callable) {
3137 let mut evaluated_args = Vec::with_capacity(args.len());
3138 for arg in args.iter() {
3139 evaluated_args.push(self.evaluate_internal(arg, data)?);
3140 }
3141 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
3142 }
3143
3144 Err(EvaluatorError::TypeError(format!(
3146 "Cannot call non-function value: {:?}",
3147 callable
3148 )))
3149 }
3150
3151 AstNode::Conditional {
3152 condition,
3153 then_branch,
3154 else_branch,
3155 } => {
3156 let condition_value = self.evaluate_internal(condition, data)?;
3157 if self.is_truthy(&condition_value) {
3158 self.evaluate_internal(then_branch, data)
3159 } else if let Some(else_branch) = else_branch {
3160 self.evaluate_internal(else_branch, data)
3161 } else {
3162 Ok(JValue::Undefined)
3165 }
3166 }
3167
3168 AstNode::Block(expressions) => {
3169 self.context.push_scope();
3171
3172 let mut result = JValue::Null;
3173 for expr in expressions {
3174 result = self.evaluate_internal(expr, data)?;
3175 }
3176
3177 let lambdas_to_keep = self.extract_lambda_ids(&result);
3180 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
3181
3182 Ok(result)
3183 }
3184
3185 AstNode::Lambda {
3187 params,
3188 body,
3189 signature,
3190 thunk,
3191 } => {
3192 let lambda_id = format!("__lambda_{}_{:p}", params.len(), body.as_ref());
3193
3194 let compiled_body = if !thunk {
3195 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
3196 try_compile_expr_with_allowed_vars(body, &var_refs)
3197 } else {
3198 None
3199 };
3200 let stored_lambda = StoredLambda {
3201 params: params.clone(),
3202 body: (**body).clone(),
3203 compiled_body,
3204 signature: signature.clone(),
3205 captured_env: self.capture_environment_for(body, params),
3206 captured_data: Some(data.clone()),
3207 thunk: *thunk,
3208 };
3209 self.context.bind_lambda(lambda_id.clone(), stored_lambda);
3210
3211 let lambda_obj = JValue::lambda(
3212 lambda_id.as_str(),
3213 params.clone(),
3214 None::<String>,
3215 signature.clone(),
3216 );
3217
3218 Ok(lambda_obj)
3219 }
3220
3221 AstNode::Wildcard => {
3223 match data {
3224 JValue::Object(obj) => {
3225 let mut result = Vec::new();
3226 for value in obj.values() {
3227 match value {
3229 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3230 _ => result.push(value.clone()),
3231 }
3232 }
3233 Ok(JValue::array(result))
3234 }
3235 JValue::Array(arr) => {
3236 Ok(JValue::Array(arr.clone()))
3238 }
3239 _ => Ok(JValue::Null),
3240 }
3241 }
3242
3243 AstNode::Descendant => {
3245 let descendants = self.collect_descendants(data);
3246 if descendants.is_empty() {
3247 Ok(JValue::Null) } else {
3249 Ok(JValue::array(descendants))
3250 }
3251 }
3252
3253 AstNode::Predicate(_) => Err(EvaluatorError::EvaluationError(
3254 "Predicate can only be used in path expressions".to_string(),
3255 )),
3256
3257 AstNode::ArrayGroup(elements) => {
3259 let mut result = Vec::new();
3260 for element in elements {
3261 let value = self.evaluate_internal(element, data)?;
3262 result.push(value);
3263 }
3264 Ok(JValue::array(result))
3265 }
3266
3267 AstNode::FunctionApplication(_) => Err(EvaluatorError::EvaluationError(
3268 "Function application can only be used in path expressions".to_string(),
3269 )),
3270
3271 AstNode::Sort { input, terms } => {
3272 let value = self.evaluate_internal(input, data)?;
3273 self.evaluate_sort(&value, terms)
3274 }
3275
3276 AstNode::IndexBind { input, variable } => {
3278 let value = self.evaluate_internal(input, data)?;
3279
3280 match value {
3283 JValue::Array(arr) => {
3284 let mut result = Vec::new();
3286 for (idx, item) in arr.iter().enumerate() {
3287 let mut wrapper = IndexMap::new();
3289 wrapper.insert("@".to_string(), item.clone());
3290 wrapper.insert(format!("${}", variable), JValue::Number(idx as f64));
3291 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
3292 result.push(JValue::object(wrapper));
3293 }
3294 Ok(JValue::array(result))
3295 }
3296 other => {
3298 let mut wrapper = IndexMap::new();
3299 wrapper.insert("@".to_string(), other);
3300 wrapper.insert(format!("${}", variable), JValue::from(0i64));
3301 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
3302 Ok(JValue::object(wrapper))
3303 }
3304 }
3305 }
3306
3307 AstNode::Transform {
3309 location,
3310 update,
3311 delete,
3312 } => {
3313 if self.context.lookup("$").is_some() {
3315 self.execute_transform(location, update, delete.as_deref(), data)
3317 } else {
3318 let transform_lambda = StoredLambda {
3321 params: vec!["$".to_string()],
3322 body: AstNode::Transform {
3323 location: location.clone(),
3324 update: update.clone(),
3325 delete: delete.clone(),
3326 },
3327 compiled_body: None, signature: None,
3329 captured_env: HashMap::new(),
3330 captured_data: None, thunk: false,
3332 };
3333
3334 let lambda_name = format!("__transform_{:p}", location);
3336 self.context.bind_lambda(lambda_name, transform_lambda);
3337
3338 Ok(JValue::string("<lambda>"))
3340 }
3341 }
3342 }
3343 }
3344
3345 fn apply_stages(&mut self, value: JValue, stages: &[Stage]) -> Result<JValue, EvaluatorError> {
3349 let mut result = match value {
3351 JValue::Null => return Ok(JValue::Null), JValue::Array(_) => value,
3353 other => JValue::array(vec![other]),
3354 };
3355
3356 for stage in stages {
3357 match stage {
3358 Stage::Filter(predicate_expr) => {
3359 result = self.evaluate_predicate_as_stage(&result, predicate_expr)?;
3361 }
3362 }
3363 }
3364 Ok(result)
3365 }
3366
3367 fn is_filter_predicate(predicate: &AstNode) -> bool {
3370 match predicate {
3371 AstNode::Binary { op, .. } => matches!(
3372 op,
3373 BinaryOp::GreaterThan
3374 | BinaryOp::GreaterThanOrEqual
3375 | BinaryOp::LessThan
3376 | BinaryOp::LessThanOrEqual
3377 | BinaryOp::Equal
3378 | BinaryOp::NotEqual
3379 | BinaryOp::And
3380 | BinaryOp::Or
3381 | BinaryOp::In
3382 ),
3383 AstNode::Unary {
3384 op: crate::ast::UnaryOp::Not,
3385 ..
3386 } => true,
3387 _ => false,
3388 }
3389 }
3390
3391 fn evaluate_predicate_as_stage(
3395 &mut self,
3396 current: &JValue,
3397 predicate: &AstNode,
3398 ) -> Result<JValue, EvaluatorError> {
3399 if matches!(predicate, AstNode::Boolean(true)) {
3401 return match current {
3402 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
3403 JValue::Null => Ok(JValue::Null),
3404 other => Ok(JValue::array(vec![other.clone()])),
3405 };
3406 }
3407
3408 match current {
3409 JValue::Array(arr) => {
3410 if let AstNode::Number(n) = predicate {
3415 let is_array_of_arrays =
3417 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3418
3419 if !is_array_of_arrays {
3420 return self.array_index(current, &JValue::Number(*n));
3422 }
3423
3424 let mut result = Vec::new();
3426 for item in arr.iter() {
3427 match item {
3428 JValue::Array(_) => {
3429 let indexed = self.array_index(item, &JValue::Number(*n))?;
3430 if !indexed.is_null() {
3431 result.push(indexed);
3432 }
3433 }
3434 _ => {
3435 if *n == 0.0 {
3436 result.push(item.clone());
3437 }
3438 }
3439 }
3440 }
3441 return Ok(JValue::array(result));
3442 }
3443
3444 if Self::is_filter_predicate(predicate) {
3447 if let Some(compiled) = try_compile_expr(predicate) {
3449 let shape = arr.first().and_then(build_shape_cache);
3450 let mut filtered = Vec::with_capacity(arr.len());
3451 for item in arr.iter() {
3452 let result = if let Some(ref s) = shape {
3453 eval_compiled_shaped(&compiled, item, None, s)?
3454 } else {
3455 eval_compiled(&compiled, item, None)?
3456 };
3457 if compiled_is_truthy(&result) {
3458 filtered.push(item.clone());
3459 }
3460 }
3461 return Ok(JValue::array(filtered));
3462 }
3463 let mut filtered = Vec::new();
3465 for item in arr.iter() {
3466 let item_result = self.evaluate_internal(predicate, item)?;
3467 if self.is_truthy(&item_result) {
3468 filtered.push(item.clone());
3469 }
3470 }
3471 return Ok(JValue::array(filtered));
3472 }
3473
3474 match self.evaluate_internal(predicate, current) {
3479 Ok(JValue::Number(n)) => {
3480 let n_val = n;
3481 let is_array_of_arrays =
3482 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3483
3484 if !is_array_of_arrays {
3485 let pred_result = JValue::Number(n_val);
3486 return self.array_index(current, &pred_result);
3487 }
3488
3489 let mut result = Vec::new();
3491 let pred_result = JValue::Number(n_val);
3492 for item in arr.iter() {
3493 match item {
3494 JValue::Array(_) => {
3495 let indexed = self.array_index(item, &pred_result)?;
3496 if !indexed.is_null() {
3497 result.push(indexed);
3498 }
3499 }
3500 _ => {
3501 if n_val == 0.0 {
3502 result.push(item.clone());
3503 }
3504 }
3505 }
3506 }
3507 return Ok(JValue::array(result));
3508 }
3509 Ok(JValue::Array(indices)) => {
3510 let has_non_numeric =
3513 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
3514
3515 if has_non_numeric {
3516 } else {
3518 let arr_len = arr.len() as i64;
3520 let mut resolved_indices: Vec<i64> = indices
3521 .iter()
3522 .filter_map(|v| {
3523 if let JValue::Number(n) = v {
3524 let idx = *n as i64;
3525 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
3527 if actual_idx >= 0 && actual_idx < arr_len {
3529 Some(actual_idx)
3530 } else {
3531 None
3532 }
3533 } else {
3534 None
3535 }
3536 })
3537 .collect();
3538
3539 resolved_indices.sort();
3541 resolved_indices.dedup();
3542
3543 let result: Vec<JValue> = resolved_indices
3545 .iter()
3546 .map(|&idx| arr[idx as usize].clone())
3547 .collect();
3548
3549 return Ok(JValue::array(result));
3550 }
3551 }
3552 Ok(_) => {
3553 }
3556 Err(_) => {
3557 }
3560 }
3561
3562 let mut filtered = Vec::new();
3564 for item in arr.iter() {
3565 let item_result = self.evaluate_internal(predicate, item)?;
3566 if self.is_truthy(&item_result) {
3567 filtered.push(item.clone());
3568 }
3569 }
3570 Ok(JValue::array(filtered))
3571 }
3572 JValue::Null => {
3573 Ok(JValue::Null)
3575 }
3576 other => {
3577 if let AstNode::Number(n) = predicate {
3583 if *n == 0.0 {
3585 return Ok(other.clone());
3586 } else {
3587 return Ok(JValue::Null);
3588 }
3589 }
3590
3591 match self.evaluate_internal(predicate, other) {
3593 Ok(JValue::Number(n)) => {
3594 if n == 0.0 {
3596 Ok(other.clone())
3597 } else {
3598 Ok(JValue::Null)
3599 }
3600 }
3601 Ok(pred_result) => {
3602 if self.is_truthy(&pred_result) {
3604 Ok(other.clone())
3605 } else {
3606 Ok(JValue::Null)
3607 }
3608 }
3609 Err(e) => Err(e),
3610 }
3611 }
3612 }
3613 }
3614
3615 fn evaluate_path(
3617 &mut self,
3618 steps: &[PathStep],
3619 data: &JValue,
3620 ) -> Result<JValue, EvaluatorError> {
3621 if steps.is_empty() {
3623 return Ok(data.clone());
3624 }
3625
3626 if steps.len() == 1 {
3629 if let AstNode::Name(field_name) = &steps[0].node {
3630 return match data {
3631 JValue::Object(obj) => {
3632 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
3634 if let Some(JValue::Object(inner)) = obj.get("@") {
3635 Ok(inner.get(field_name).cloned().unwrap_or(JValue::Null))
3636 } else {
3637 Ok(JValue::Null)
3638 }
3639 } else {
3640 Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null))
3641 }
3642 }
3643 JValue::Array(arr) => {
3644 let has_tuples = arr.first().is_some_and(|item| {
3648 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
3649 });
3650
3651 if !has_tuples {
3652 let mut result = Vec::with_capacity(arr.len());
3654 for item in arr.iter() {
3655 if let JValue::Object(obj) = item {
3656 if let Some(val) = obj.get(field_name) {
3657 if !val.is_null() {
3658 match val {
3659 JValue::Array(arr_val) => {
3660 result.extend(arr_val.iter().cloned());
3661 }
3662 other => result.push(other.clone()),
3663 }
3664 }
3665 }
3666 } else if let JValue::Array(inner_arr) = item {
3667 let nested_result = self.evaluate_path(
3668 &[PathStep::new(AstNode::Name(field_name.clone()))],
3669 &JValue::Array(inner_arr.clone()),
3670 )?;
3671 match nested_result {
3672 JValue::Array(nested) => {
3673 result.extend(nested.iter().cloned());
3674 }
3675 JValue::Null => {}
3676 other => result.push(other),
3677 }
3678 }
3679 }
3680
3681 if result.is_empty() {
3682 Ok(JValue::Null)
3683 } else if result.len() == 1 {
3684 Ok(result.into_iter().next().unwrap())
3685 } else {
3686 Ok(JValue::array(result))
3687 }
3688 } else {
3689 let mut result = Vec::new();
3691 for item in arr.iter() {
3692 match item {
3693 JValue::Object(obj) => {
3694 let is_tuple =
3695 obj.get("__tuple__") == Some(&JValue::Bool(true));
3696
3697 if is_tuple {
3698 let inner = match obj.get("@") {
3699 Some(JValue::Object(inner)) => inner,
3700 _ => continue,
3701 };
3702
3703 if let Some(val) = inner.get(field_name) {
3704 if !val.is_null() {
3705 let wrap = |v: JValue| -> JValue {
3707 let mut wrapper = IndexMap::new();
3708 wrapper.insert("@".to_string(), v);
3709 wrapper.insert(
3710 "__tuple__".to_string(),
3711 JValue::Bool(true),
3712 );
3713 for (k, v) in obj.iter() {
3714 if k.starts_with('$') {
3715 wrapper
3716 .insert(k.clone(), v.clone());
3717 }
3718 }
3719 JValue::object(wrapper)
3720 };
3721
3722 match val {
3723 JValue::Array(arr_val) => {
3724 for item in arr_val.iter() {
3725 result.push(wrap(item.clone()));
3726 }
3727 }
3728 other => result.push(wrap(other.clone())),
3729 }
3730 }
3731 }
3732 } else {
3733 if let Some(val) = obj.get(field_name) {
3735 if !val.is_null() {
3736 match val {
3737 JValue::Array(arr_val) => {
3738 for item in arr_val.iter() {
3739 result.push(item.clone());
3740 }
3741 }
3742 other => result.push(other.clone()),
3743 }
3744 }
3745 }
3746 }
3747 }
3748 JValue::Array(inner_arr) => {
3749 let nested_result = self.evaluate_path(
3751 &[PathStep::new(AstNode::Name(field_name.clone()))],
3752 &JValue::Array(inner_arr.clone()),
3753 )?;
3754 match nested_result {
3756 JValue::Array(nested) => {
3757 result.extend(nested.iter().cloned());
3759 }
3760 JValue::Null => {} other => result.push(other),
3762 }
3763 }
3764 _ => {} }
3766 }
3767
3768 if result.is_empty() {
3772 Ok(JValue::Null)
3773 } else if result.len() == 1 {
3774 Ok(result.into_iter().next().unwrap())
3775 } else {
3776 Ok(JValue::array(result))
3777 }
3778 } }
3780 _ => Ok(JValue::Null),
3781 };
3782 }
3783 }
3784
3785 if steps.len() == 2 && steps[0].stages.is_empty() && steps[1].stages.is_empty() {
3788 if let (AstNode::Variable(var_name), AstNode::Name(field_name)) =
3789 (&steps[0].node, &steps[1].node)
3790 {
3791 if !var_name.is_empty() {
3792 if let Some(value) = self.context.lookup(var_name) {
3793 match value {
3794 JValue::Object(obj) => {
3795 return Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null));
3796 }
3797 JValue::Array(arr) => {
3798 let mut result = Vec::with_capacity(arr.len());
3800 for item in arr.iter() {
3801 if let JValue::Object(obj) = item {
3802 if let Some(val) = obj.get(field_name) {
3803 if !val.is_null() {
3804 match val {
3805 JValue::Array(inner) => {
3806 result.extend(inner.iter().cloned());
3807 }
3808 other => result.push(other.clone()),
3809 }
3810 }
3811 }
3812 }
3813 }
3814 return match result.len() {
3815 0 => Ok(JValue::Null),
3816 1 => Ok(result.pop().unwrap()),
3817 _ => Ok(JValue::array(result)),
3818 };
3819 }
3820 _ => {} }
3822 }
3823 }
3824 }
3825 }
3826
3827 let mut did_array_mapping = false;
3829
3830 let mut current: JValue = match &steps[0].node {
3832 AstNode::Wildcard => {
3833 match data {
3835 JValue::Object(obj) => {
3836 let mut result = Vec::new();
3837 for value in obj.values() {
3838 match value {
3840 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3841 _ => result.push(value.clone()),
3842 }
3843 }
3844 JValue::array(result)
3845 }
3846 JValue::Array(arr) => JValue::Array(arr.clone()),
3847 _ => JValue::Null,
3848 }
3849 }
3850 AstNode::Descendant => {
3851 let descendants = self.collect_descendants(data);
3853 JValue::array(descendants)
3854 }
3855 AstNode::ParentVariable(name) => {
3856 let parent_data = self.context.get_parent().ok_or_else(|| {
3858 EvaluatorError::ReferenceError("Parent context not available".to_string())
3859 })?;
3860
3861 if name.is_empty() {
3862 parent_data.clone()
3864 } else {
3865 match parent_data {
3867 JValue::Object(obj) => obj.get(name).cloned().unwrap_or(JValue::Null),
3868 _ => JValue::Null,
3869 }
3870 }
3871 }
3872 AstNode::Name(field_name) => {
3873 let stages = &steps[0].stages;
3875
3876 match data {
3877 JValue::Object(obj) => {
3878 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3879 if !stages.is_empty() {
3881 self.apply_stages(val, stages)?
3882 } else {
3883 val
3884 }
3885 }
3886 JValue::Array(arr) => {
3887 let mut result = Vec::new();
3889 for item in arr.iter() {
3890 match item {
3891 JValue::Object(obj) => {
3892 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3893 if !val.is_null() {
3894 if !stages.is_empty() {
3895 let processed_val = self.apply_stages(val, stages)?;
3897 match processed_val {
3899 JValue::Array(arr) => {
3900 result.extend(arr.iter().cloned())
3901 }
3902 JValue::Null => {} other => result.push(other), }
3905 } else {
3906 match val {
3908 JValue::Array(arr) => {
3909 result.extend(arr.iter().cloned())
3910 }
3911 other => result.push(other),
3912 }
3913 }
3914 }
3915 }
3916 JValue::Array(inner_arr) => {
3917 let nested_result = self.evaluate_path(
3919 &[steps[0].clone()],
3920 &JValue::Array(inner_arr.clone()),
3921 )?;
3922 match nested_result {
3923 JValue::Array(nested) => {
3924 result.extend(nested.iter().cloned())
3925 }
3926 JValue::Null => {} other => result.push(other),
3928 }
3929 }
3930 _ => {} }
3932 }
3933 JValue::array(result)
3934 }
3935 JValue::Null => JValue::Null,
3936 _ => JValue::Undefined,
3938 }
3939 }
3940 AstNode::String(string_literal) => {
3941 let stages = &steps[0].stages;
3944 let val = JValue::string(string_literal.clone());
3945
3946 if !stages.is_empty() {
3947 let result = self.apply_stages(val, stages)?;
3949 match result {
3952 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
3953 JValue::Array(arr) if arr.is_empty() => JValue::Null,
3954 other => other,
3955 }
3956 } else {
3957 val
3958 }
3959 }
3960 AstNode::Predicate(pred_expr) => {
3961 self.evaluate_predicate(data, pred_expr)?
3963 }
3964 AstNode::IndexBind { .. } => {
3965 self.evaluate_internal(&steps[0].node, data)?
3967 }
3968 _ => {
3969 self.evaluate_path_step(&steps[0].node, data, data)?
3971 }
3972 };
3973
3974 for step in steps[1..].iter() {
3976 if current.is_null() || current.is_undefined() {
3979 return Ok(JValue::Null);
3980 }
3981
3982 let is_tuple_array = if let JValue::Array(arr) = ¤t {
3985 arr.first().is_some_and(|first| {
3986 if let JValue::Object(obj) = first {
3987 obj.get("__tuple__") == Some(&JValue::Bool(true))
3988 } else {
3989 false
3990 }
3991 })
3992 } else {
3993 false
3994 };
3995
3996 let needs_tuple_context_binding = is_tuple_array
4006 && matches!(
4007 &step.node,
4008 AstNode::Object(_) | AstNode::FunctionApplication(_) | AstNode::Variable(_)
4009 );
4010
4011 if needs_tuple_context_binding {
4012 if let JValue::Array(arr) = ¤t {
4013 let mut results = Vec::new();
4014
4015 for tuple in arr.iter() {
4016 if let JValue::Object(tuple_obj) = tuple {
4017 let bindings: Vec<(String, JValue)> = tuple_obj
4019 .iter()
4020 .filter(|(k, _)| k.starts_with('$') && k.len() > 1) .map(|(k, v)| (k[1..].to_string(), v.clone())) .collect();
4023
4024 let saved_bindings: Vec<(String, Option<JValue>)> = bindings
4026 .iter()
4027 .map(|(name, _)| (name.clone(), self.context.lookup(name).cloned()))
4028 .collect();
4029
4030 for (name, value) in &bindings {
4032 self.context.bind(name.clone(), value.clone());
4033 }
4034
4035 let actual_data = tuple_obj.get("@").cloned().unwrap_or(JValue::Null);
4037
4038 let step_result = match &step.node {
4040 AstNode::Variable(_) => {
4041 self.evaluate_internal(&step.node, tuple)?
4043 }
4044 AstNode::Object(_) | AstNode::FunctionApplication(_) => {
4045 self.evaluate_internal(&step.node, &actual_data)?
4047 }
4048 _ => unreachable!(), };
4050
4051 for (name, saved_value) in &saved_bindings {
4053 if let Some(value) = saved_value {
4054 self.context.bind(name.clone(), value.clone());
4055 } else {
4056 self.context.unbind(name);
4057 }
4058 }
4059
4060 if !step_result.is_null() && !step_result.is_undefined() {
4062 if matches!(&step.node, AstNode::Object(_)) {
4065 results.push(step_result);
4066 } else if matches!(step_result, JValue::Array(_)) {
4067 if let JValue::Array(arr) = step_result {
4068 results.extend(arr.iter().cloned());
4069 }
4070 } else {
4071 results.push(step_result);
4072 }
4073 }
4074 }
4075 }
4076
4077 current = JValue::array(results);
4078 continue; }
4080 }
4081
4082 current = match &step.node {
4083 AstNode::Wildcard => {
4084 let stages = &step.stages;
4086 let wildcard_result = match ¤t {
4087 JValue::Object(obj) => {
4088 let mut result = Vec::new();
4089 for value in obj.values() {
4090 match value {
4092 JValue::Array(arr) => result.extend(arr.iter().cloned()),
4093 _ => result.push(value.clone()),
4094 }
4095 }
4096 JValue::array(result)
4097 }
4098 JValue::Array(arr) => {
4099 let mut all_values = Vec::new();
4101 for item in arr.iter() {
4102 match item {
4103 JValue::Object(obj) => {
4104 for value in obj.values() {
4105 match value {
4107 JValue::Array(arr) => {
4108 all_values.extend(arr.iter().cloned())
4109 }
4110 _ => all_values.push(value.clone()),
4111 }
4112 }
4113 }
4114 JValue::Array(inner) => {
4115 all_values.extend(inner.iter().cloned());
4116 }
4117 _ => {}
4118 }
4119 }
4120 JValue::array(all_values)
4121 }
4122 _ => JValue::Null,
4123 };
4124
4125 if !stages.is_empty() {
4127 self.apply_stages(wildcard_result, stages)?
4128 } else {
4129 wildcard_result
4130 }
4131 }
4132 AstNode::Descendant => {
4133 match ¤t {
4135 JValue::Array(arr) => {
4136 let mut all_descendants = Vec::new();
4138 for item in arr.iter() {
4139 all_descendants.extend(self.collect_descendants(item));
4140 }
4141 JValue::array(all_descendants)
4142 }
4143 _ => {
4144 let descendants = self.collect_descendants(¤t);
4146 JValue::array(descendants)
4147 }
4148 }
4149 }
4150 AstNode::Name(field_name) => {
4151 let stages = &step.stages;
4153
4154 match ¤t {
4155 JValue::Object(obj) => {
4156 did_array_mapping = false;
4161 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
4162 if !stages.is_empty() {
4164 self.apply_stages(val, stages)?
4165 } else {
4166 val
4167 }
4168 }
4169 JValue::Array(arr) => {
4170 did_array_mapping = true; let has_tuples = arr.first().is_some_and(|item| {
4178 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
4179 });
4180
4181 if !has_tuples && stages.is_empty() {
4182 let mut result = Vec::with_capacity(arr.len());
4183 for item in arr.iter() {
4184 match item {
4185 JValue::Object(obj) => {
4186 if let Some(val) = obj.get(field_name) {
4187 if !val.is_null() {
4188 match val {
4189 JValue::Array(arr_val) => {
4190 result.extend(arr_val.iter().cloned())
4191 }
4192 other => result.push(other.clone()),
4193 }
4194 }
4195 }
4196 }
4197 JValue::Array(_) => {
4198 let nested_result =
4199 self.evaluate_path(&[step.clone()], item)?;
4200 match nested_result {
4201 JValue::Array(nested) => {
4202 result.extend(nested.iter().cloned())
4203 }
4204 JValue::Null => {}
4205 other => result.push(other),
4206 }
4207 }
4208 _ => {}
4209 }
4210 }
4211 JValue::array(result)
4212 } else {
4213 let mut result = Vec::new();
4215
4216 for item in arr.iter() {
4217 match item {
4218 JValue::Object(obj) => {
4219 let (actual_obj, tuple_bindings) = if obj
4221 .get("__tuple__")
4222 == Some(&JValue::Bool(true))
4223 {
4224 if let Some(JValue::Object(inner)) = obj.get("@") {
4226 let bindings: Vec<(String, JValue)> = obj
4228 .iter()
4229 .filter(|(k, _)| k.starts_with('$'))
4230 .map(|(k, v)| (k.clone(), v.clone()))
4231 .collect();
4232 (inner.clone(), Some(bindings))
4233 } else {
4234 continue; }
4236 } else {
4237 (obj.clone(), None)
4238 };
4239
4240 let val = actual_obj
4241 .get(field_name)
4242 .cloned()
4243 .unwrap_or(JValue::Null);
4244
4245 if !val.is_null() {
4246 let wrap_in_tuple = |v: JValue, bindings: &Option<Vec<(String, JValue)>>| -> JValue {
4248 if let Some(b) = bindings {
4249 let mut wrapper = IndexMap::new();
4250 wrapper.insert("@".to_string(), v);
4251 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4252 for (k, val) in b {
4253 wrapper.insert(k.clone(), val.clone());
4254 }
4255 JValue::object(wrapper)
4256 } else {
4257 v
4258 }
4259 };
4260
4261 if !stages.is_empty() {
4262 let processed_val =
4264 self.apply_stages(val, stages)?;
4265 match processed_val {
4267 JValue::Array(arr) => {
4268 for item in arr.iter() {
4269 result.push(wrap_in_tuple(
4270 item.clone(),
4271 &tuple_bindings,
4272 ));
4273 }
4274 }
4275 JValue::Null => {} other => result.push(wrap_in_tuple(
4277 other,
4278 &tuple_bindings,
4279 )),
4280 }
4281 } else {
4282 match val {
4285 JValue::Array(arr) => {
4286 for item in arr.iter() {
4287 result.push(wrap_in_tuple(
4288 item.clone(),
4289 &tuple_bindings,
4290 ));
4291 }
4292 }
4293 other => result.push(wrap_in_tuple(
4294 other,
4295 &tuple_bindings,
4296 )),
4297 }
4298 }
4299 }
4300 }
4301 JValue::Array(_) => {
4302 let nested_result =
4304 self.evaluate_path(&[step.clone()], item)?;
4305 match nested_result {
4306 JValue::Array(nested) => {
4307 result.extend(nested.iter().cloned())
4308 }
4309 JValue::Null => {}
4310 other => result.push(other),
4311 }
4312 }
4313 _ => {}
4314 }
4315 }
4316
4317 JValue::array(result)
4318 }
4319 }
4320 JValue::Null => JValue::Null,
4321 _ => JValue::Undefined,
4323 }
4324 }
4325 AstNode::String(string_literal) => {
4326 let stages = &step.stages;
4328 let val = JValue::string(string_literal.clone());
4329
4330 if !stages.is_empty() {
4331 let result = self.apply_stages(val, stages)?;
4333 match result {
4335 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
4336 JValue::Array(arr) if arr.is_empty() => JValue::Null,
4337 other => other,
4338 }
4339 } else {
4340 val
4341 }
4342 }
4343 AstNode::Predicate(pred_expr) => {
4344 self.evaluate_predicate(¤t, pred_expr)?
4346 }
4347 AstNode::ArrayGroup(elements) => {
4348 match ¤t {
4351 JValue::Array(arr) => {
4352 let mut result = Vec::new();
4353 for item in arr.iter() {
4354 let mut group_values = Vec::new();
4356 for element in elements {
4357 let value = self.evaluate_internal(element, item)?;
4358 let should_preserve_array = matches!(
4361 element,
4362 AstNode::Array(_) | AstNode::ArrayGroup(_)
4363 );
4364
4365 if should_preserve_array {
4366 group_values.push(value);
4368 } else {
4369 match value {
4371 JValue::Array(arr) => {
4372 group_values.extend(arr.iter().cloned())
4373 }
4374 other => group_values.push(other),
4375 }
4376 }
4377 }
4378 result.push(JValue::array(group_values));
4380 }
4381 JValue::array(result)
4382 }
4383 _ => {
4384 let mut result = Vec::new();
4386 for element in elements {
4387 let value = self.evaluate_internal(element, ¤t)?;
4388 result.push(value);
4389 }
4390 JValue::array(result)
4391 }
4392 }
4393 }
4394 AstNode::FunctionApplication(expr) => {
4395 match ¤t {
4399 JValue::Array(arr) => {
4400 let mapped: Vec<JValue> = if let Some(compiled) = try_compile_expr(expr)
4404 {
4405 let shape = arr.first().and_then(build_shape_cache);
4406 let mut result = Vec::with_capacity(arr.len());
4407 for item in arr.iter() {
4408 let value = if let Some(ref s) = shape {
4409 eval_compiled_shaped(&compiled, item, None, s)?
4410 } else {
4411 eval_compiled(&compiled, item, None)?
4412 };
4413 if !value.is_null() && !value.is_undefined() {
4414 result.push(value);
4415 }
4416 }
4417 result
4418 } else {
4419 let mut result = Vec::new();
4420 for item in arr.iter() {
4421 let saved_dollar = self.context.lookup("$").cloned();
4423
4424 self.context.bind("$".to_string(), item.clone());
4426
4427 let value = self.evaluate_internal(expr, item)?;
4429
4430 if let Some(saved) = saved_dollar {
4432 self.context.bind("$".to_string(), saved);
4433 } else {
4434 self.context.unbind("$");
4435 }
4436
4437 if !value.is_null() && !value.is_undefined() {
4439 result.push(value);
4440 }
4441 }
4442 result
4443 };
4444 JValue::array(mapped)
4447 }
4448 _ => {
4449 let saved_dollar = self.context.lookup("$").cloned();
4451 self.context.bind("$".to_string(), current.clone());
4452
4453 let value = self.evaluate_internal(expr, ¤t)?;
4454
4455 if let Some(saved) = saved_dollar {
4456 self.context.bind("$".to_string(), saved);
4457 } else {
4458 self.context.unbind("$");
4459 }
4460
4461 value
4462 }
4463 }
4464 }
4465 AstNode::Sort { terms, .. } => {
4466 self.evaluate_sort(¤t, terms)?
4468 }
4469 AstNode::IndexBind { variable, .. } => {
4470 match ¤t {
4473 JValue::Array(arr) => {
4474 let mut result = Vec::new();
4475 for (idx, item) in arr.iter().enumerate() {
4476 let mut wrapper = IndexMap::new();
4477 wrapper.insert("@".to_string(), item.clone());
4478 wrapper
4479 .insert(format!("${}", variable), JValue::Number(idx as f64));
4480 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4481 result.push(JValue::object(wrapper));
4482 }
4483 JValue::array(result)
4484 }
4485 other => {
4486 let mut wrapper = IndexMap::new();
4488 wrapper.insert("@".to_string(), other.clone());
4489 wrapper.insert(format!("${}", variable), JValue::from(0i64));
4490 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4491 JValue::object(wrapper)
4492 }
4493 }
4494 }
4495 _ => self.evaluate_path_step(&step.node, ¤t, data)?,
4497 };
4498 }
4499
4500 let has_explicit_array_keep = steps.iter().any(|step| {
4508 if let AstNode::Predicate(pred) = &step.node {
4510 if matches!(**pred, AstNode::Boolean(true)) {
4511 return true;
4512 }
4513 }
4514 step.stages.iter().any(|stage| {
4516 let crate::ast::Stage::Filter(pred) = stage;
4517 matches!(**pred, AstNode::Boolean(true))
4518 })
4519 });
4520
4521 let should_unwrap = !has_explicit_array_keep
4531 && (steps.iter().any(|step| !step.stages.is_empty()) || did_array_mapping);
4532
4533 let result = match ¤t {
4534 JValue::Array(arr) if arr.is_empty() => JValue::Null,
4536 JValue::Array(arr) if arr.len() == 1 && should_unwrap => arr[0].clone(),
4538 _ => current,
4540 };
4541
4542 Ok(result)
4543 }
4544
4545 fn evaluate_path_step(
4547 &mut self,
4548 step: &AstNode,
4549 current: &JValue,
4550 original_data: &JValue,
4551 ) -> Result<JValue, EvaluatorError> {
4552 if matches!(current, JValue::Array(_)) && matches!(step, AstNode::Object(_)) {
4555 match (current, step) {
4556 (JValue::Array(arr), AstNode::Object(pairs)) => {
4557 if let Some(compiled) = try_compile_expr(&AstNode::Object(pairs.clone())) {
4559 let shape = arr.first().and_then(build_shape_cache);
4560 let mut mapped = Vec::with_capacity(arr.len());
4561 for item in arr.iter() {
4562 let result = if let Some(ref s) = shape {
4563 eval_compiled_shaped(&compiled, item, None, s)?
4564 } else {
4565 eval_compiled(&compiled, item, None)?
4566 };
4567 if !result.is_undefined() {
4568 mapped.push(result);
4569 }
4570 }
4571 return Ok(JValue::array(mapped));
4572 }
4573 let mapped: Result<Vec<JValue>, EvaluatorError> = arr
4575 .iter()
4576 .map(|item| self.evaluate_internal(step, item))
4577 .collect();
4578 Ok(JValue::array(mapped?))
4579 }
4580 _ => unreachable!(),
4581 }
4582 } else {
4583 if let AstNode::Variable(name) = step {
4586 if name.is_empty() {
4587 if let JValue::Array(arr) = current {
4589 return Ok(JValue::Array(arr.clone()));
4591 } else {
4592 return Ok(current.clone());
4594 }
4595 }
4596 }
4597
4598 if matches!(step, AstNode::Variable(_)) {
4602 if let JValue::Array(arr) = current {
4603 let is_tuple_array = arr.first().is_some_and(|first| {
4605 if let JValue::Object(obj) = first {
4606 obj.get("__tuple__") == Some(&JValue::Bool(true))
4607 } else {
4608 false
4609 }
4610 });
4611
4612 if is_tuple_array {
4613 let mut results = Vec::new();
4615 for tuple in arr.iter() {
4616 let val = self.evaluate_internal(step, tuple)?;
4619 if !val.is_null() && !val.is_undefined() {
4620 results.push(val);
4621 }
4622 }
4623 return Ok(JValue::array(results));
4624 }
4625 }
4626 }
4627
4628 if matches!(
4637 step,
4638 AstNode::Binary { .. }
4639 | AstNode::Function { .. }
4640 | AstNode::Variable(_)
4641 | AstNode::ParentVariable(_)
4642 | AstNode::Array(_)
4643 | AstNode::Object(_)
4644 | AstNode::Sort { .. }
4645 | AstNode::Block(_)
4646 ) {
4647 return self.evaluate_internal(step, original_data);
4649 }
4650
4651 let step_value = self.evaluate_internal(step, original_data)?;
4653 Ok(match (current, &step_value) {
4654 (JValue::Object(obj), JValue::String(key)) => {
4655 obj.get(&**key).cloned().unwrap_or(JValue::Null)
4656 }
4657 (JValue::Array(arr), JValue::Number(n)) => {
4658 let index = *n as i64;
4659 let len = arr.len() as i64;
4660
4661 let actual_idx = if index < 0 { len + index } else { index };
4663
4664 if actual_idx < 0 || actual_idx >= len {
4665 JValue::Null
4666 } else {
4667 arr[actual_idx as usize].clone()
4668 }
4669 }
4670 _ => JValue::Null,
4671 })
4672 }
4673 }
4674
4675 fn evaluate_binary_op(
4677 &mut self,
4678 op: crate::ast::BinaryOp,
4679 lhs: &AstNode,
4680 rhs: &AstNode,
4681 data: &JValue,
4682 ) -> Result<JValue, EvaluatorError> {
4683 use crate::ast::BinaryOp;
4684
4685 if op == BinaryOp::Coalesce {
4689 return match self.evaluate_internal(lhs, data) {
4691 Ok(value) => {
4692 if matches!(lhs, AstNode::Null) {
4695 Ok(value)
4696 }
4697 else if value.is_null()
4699 && (matches!(lhs, AstNode::Path { .. })
4700 || matches!(lhs, AstNode::String(_))
4701 || matches!(lhs, AstNode::Variable(_)))
4702 {
4703 self.evaluate_internal(rhs, data)
4704 } else {
4705 Ok(value)
4706 }
4707 }
4708 Err(_) => {
4709 self.evaluate_internal(rhs, data)
4711 }
4712 };
4713 }
4714
4715 if op == BinaryOp::Default {
4718 let left = self.evaluate_internal(lhs, data)?;
4719 if self.is_truthy_for_default(&left) {
4720 return Ok(left);
4721 }
4722 return self.evaluate_internal(rhs, data);
4723 }
4724
4725 if op == BinaryOp::ChainPipe {
4729 if let AstNode::Regex { pattern, flags } = rhs {
4731 let lhs_value = self.evaluate_internal(lhs, data)?;
4733 return match lhs_value {
4735 JValue::String(s) => {
4736 let case_insensitive = flags.contains('i');
4738 let regex_pattern = if case_insensitive {
4739 format!("(?i){}", pattern)
4740 } else {
4741 pattern.clone()
4742 };
4743 match regex::Regex::new(®ex_pattern) {
4744 Ok(re) => {
4745 if let Some(m) = re.find(&s) {
4746 let mut result = IndexMap::new();
4748 result.insert(
4749 "match".to_string(),
4750 JValue::string(m.as_str().to_string()),
4751 );
4752 result.insert(
4753 "start".to_string(),
4754 JValue::Number(m.start() as f64),
4755 );
4756 result
4757 .insert("end".to_string(), JValue::Number(m.end() as f64));
4758
4759 let mut groups = Vec::new();
4761 for cap in re.captures_iter(&s).take(1) {
4762 for i in 1..cap.len() {
4763 if let Some(c) = cap.get(i) {
4764 groups.push(JValue::string(c.as_str().to_string()));
4765 }
4766 }
4767 }
4768 if !groups.is_empty() {
4769 result.insert("groups".to_string(), JValue::array(groups));
4770 }
4771
4772 Ok(JValue::object(result))
4773 } else {
4774 Ok(JValue::Null)
4775 }
4776 }
4777 Err(e) => Err(EvaluatorError::EvaluationError(format!(
4778 "Invalid regex: {}",
4779 e
4780 ))),
4781 }
4782 }
4783 JValue::Null => Ok(JValue::Null),
4784 _ => Err(EvaluatorError::TypeError(
4785 "Left side of ~> /regex/ must be a string".to_string(),
4786 )),
4787 };
4788 }
4789
4790 let lhs_value_for_check = self.evaluate_internal(lhs, data)?;
4793 if lhs_value_for_check.is_undefined() || lhs_value_for_check.is_null() {
4794 return Ok(JValue::Undefined);
4795 }
4796
4797 match rhs {
4799 AstNode::Function {
4800 name,
4801 args,
4802 is_builtin,
4803 } => {
4804 let has_placeholder =
4807 args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
4808
4809 if has_placeholder {
4810 let lhs_value = self.evaluate_internal(lhs, data)?;
4812 let mut filled_args = Vec::new();
4813 let mut lhs_used = false;
4814
4815 for arg in args.iter() {
4816 if matches!(arg, AstNode::Placeholder) && !lhs_used {
4817 let temp_name = format!("__pipe_arg_{}", filled_args.len());
4820 self.context.bind(temp_name.clone(), lhs_value.clone());
4821 filled_args.push(AstNode::Variable(temp_name));
4822 lhs_used = true;
4823 } else {
4824 filled_args.push(arg.clone());
4825 }
4826 }
4827
4828 let result =
4830 self.evaluate_function_call(name, &filled_args, *is_builtin, data);
4831
4832 for (i, arg) in args.iter().enumerate() {
4834 if matches!(arg, AstNode::Placeholder) {
4835 self.context.unbind(&format!("__pipe_arg_{}", i));
4836 }
4837 }
4838
4839 return result.map(|v| self.unwrap_singleton(v));
4841 } else {
4842 let mut all_args = vec![lhs.clone()];
4844 all_args.extend_from_slice(args);
4845 return self
4847 .evaluate_function_call(name, &all_args, *is_builtin, data)
4848 .map(|v| self.unwrap_singleton(v));
4849 }
4850 }
4851 AstNode::Variable(var_name) => {
4852 let all_args = vec![lhs.clone()];
4855 return self
4857 .evaluate_function_call(var_name, &all_args, true, data)
4858 .map(|v| self.unwrap_singleton(v));
4859 }
4860 AstNode::Binary {
4861 op: BinaryOp::ChainPipe,
4862 ..
4863 } => {
4864 let lhs_value = self.evaluate_internal(lhs, data)?;
4867 return self.evaluate_internal(rhs, &lhs_value);
4868 }
4869 AstNode::Transform { .. } => {
4870 let lhs_value = self.evaluate_internal(lhs, data)?;
4873
4874 let saved_binding = self.context.lookup("$").cloned();
4876 self.context.bind("$".to_string(), lhs_value.clone());
4877
4878 let result = self.evaluate_internal(rhs, data);
4879
4880 if let Some(saved) = saved_binding {
4882 self.context.bind("$".to_string(), saved);
4883 } else {
4884 self.context.unbind("$");
4885 }
4886
4887 return result.map(|v| self.unwrap_singleton(v));
4889 }
4890 AstNode::Lambda {
4891 params,
4892 body,
4893 signature,
4894 thunk,
4895 } => {
4896 let lhs_value = self.evaluate_internal(lhs, data)?;
4898 return self
4900 .invoke_lambda(params, body, signature.as_ref(), &[lhs_value], data, *thunk)
4901 .map(|v| self.unwrap_singleton(v));
4902 }
4903 AstNode::Path { steps } => {
4904 if let Some(first_step) = steps.first() {
4907 match &first_step.node {
4908 AstNode::Function {
4909 name,
4910 args,
4911 is_builtin,
4912 } => {
4913 let mut all_args = vec![lhs.clone()];
4915 all_args.extend_from_slice(args);
4916
4917 let mut result = self.evaluate_function_call(
4919 name,
4920 &all_args,
4921 *is_builtin,
4922 data,
4923 )?;
4924
4925 for stage in &first_step.stages {
4927 match stage {
4928 Stage::Filter(filter_expr) => {
4929 result = self.evaluate_predicate_as_stage(
4930 &result,
4931 filter_expr,
4932 )?;
4933 }
4934 }
4935 }
4936
4937 if steps.len() > 1 {
4939 let remaining_path = AstNode::Path {
4940 steps: steps[1..].to_vec(),
4941 };
4942 result = self.evaluate_internal(&remaining_path, &result)?;
4943 }
4944
4945 if !first_step.stages.is_empty() || steps.len() > 1 {
4948 return Ok(result);
4949 } else {
4950 return Ok(self.unwrap_singleton(result));
4951 }
4952 }
4953 AstNode::Variable(var_name) => {
4954 let all_args = vec![lhs.clone()];
4956 let mut result =
4957 self.evaluate_function_call(var_name, &all_args, true, data)?;
4958
4959 for stage in &first_step.stages {
4961 match stage {
4962 Stage::Filter(filter_expr) => {
4963 result = self.evaluate_predicate_as_stage(
4964 &result,
4965 filter_expr,
4966 )?;
4967 }
4968 }
4969 }
4970
4971 if steps.len() > 1 {
4973 let remaining_path = AstNode::Path {
4974 steps: steps[1..].to_vec(),
4975 };
4976 result = self.evaluate_internal(&remaining_path, &result)?;
4977 }
4978
4979 if !first_step.stages.is_empty() || steps.len() > 1 {
4982 return Ok(result);
4983 } else {
4984 return Ok(self.unwrap_singleton(result));
4985 }
4986 }
4987 _ => {
4988 let lhs_value = self.evaluate_internal(lhs, data)?;
4990 return self
4991 .evaluate_internal(rhs, &lhs_value)
4992 .map(|v| self.unwrap_singleton(v));
4993 }
4994 }
4995 }
4996
4997 let lhs_value = self.evaluate_internal(lhs, data)?;
4999 return self
5000 .evaluate_internal(rhs, &lhs_value)
5001 .map(|v| self.unwrap_singleton(v));
5002 }
5003 _ => {
5004 return Err(EvaluatorError::TypeError(
5005 "Right side of ~> must be a function call or function reference"
5006 .to_string(),
5007 ));
5008 }
5009 }
5010 }
5011
5012 if op == BinaryOp::ColonEqual {
5014 let var_name = match lhs {
5016 AstNode::Variable(name) => name.clone(),
5017 _ => {
5018 return Err(EvaluatorError::TypeError(
5019 "Left side of := must be a variable".to_string(),
5020 ))
5021 }
5022 };
5023
5024 if let AstNode::Lambda {
5026 params,
5027 body,
5028 signature,
5029 thunk,
5030 } = rhs
5031 {
5032 let captured_env = self.capture_environment_for(body, params);
5035 let compiled_body = if !thunk {
5036 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
5037 try_compile_expr_with_allowed_vars(body, &var_refs)
5038 } else {
5039 None
5040 };
5041 let stored_lambda = StoredLambda {
5042 params: params.clone(),
5043 body: (**body).clone(),
5044 compiled_body,
5045 signature: signature.clone(),
5046 captured_env,
5047 captured_data: Some(data.clone()),
5048 thunk: *thunk,
5049 };
5050 let lambda_params = stored_lambda.params.clone();
5051 let lambda_sig = stored_lambda.signature.clone();
5052 self.context.bind_lambda(var_name.clone(), stored_lambda);
5053
5054 let lambda_repr = JValue::lambda(
5056 var_name.as_str(),
5057 lambda_params,
5058 Some(var_name.clone()),
5059 lambda_sig,
5060 );
5061 return Ok(lambda_repr);
5062 }
5063
5064 if let AstNode::Binary {
5070 op: BinaryOp::ChainPipe,
5071 lhs: chain_lhs,
5072 rhs: chain_rhs,
5073 } = rhs
5074 {
5075 let is_function_composition = match chain_lhs.as_ref() {
5078 AstNode::Variable(name)
5080 if self.is_builtin_function(name)
5081 || self.context.lookup_lambda(name).is_some() =>
5082 {
5083 true
5084 }
5085 AstNode::Binary {
5087 op: BinaryOp::ChainPipe,
5088 ..
5089 } => true,
5090 AstNode::Function { args, .. }
5093 if args.iter().any(|a| matches!(a, AstNode::Placeholder)) =>
5094 {
5095 true
5096 }
5097 _ => false,
5099 };
5100
5101 if is_function_composition {
5102 let param_name = "$".to_string();
5106
5107 let first_pipe = AstNode::Binary {
5109 op: BinaryOp::ChainPipe,
5110 lhs: Box::new(AstNode::Variable(param_name.clone())),
5111 rhs: chain_lhs.clone(),
5112 };
5113
5114 let composed_body = AstNode::Binary {
5116 op: BinaryOp::ChainPipe,
5117 lhs: Box::new(first_pipe),
5118 rhs: chain_rhs.clone(),
5119 };
5120
5121 let stored_lambda = StoredLambda {
5122 params: vec![param_name],
5123 body: composed_body,
5124 compiled_body: None, signature: None,
5126 captured_env: self.capture_current_environment(),
5127 captured_data: Some(data.clone()),
5128 thunk: false,
5129 };
5130 self.context.bind_lambda(var_name.clone(), stored_lambda);
5131
5132 let lambda_repr = JValue::lambda(
5134 var_name.as_str(),
5135 vec!["$".to_string()],
5136 Some(var_name.clone()),
5137 None::<String>,
5138 );
5139 return Ok(lambda_repr);
5140 }
5141 }
5143
5144 let value = self.evaluate_internal(rhs, data)?;
5146
5147 if let Some(stored) = self.lookup_lambda_from_value(&value) {
5149 self.context.bind_lambda(var_name.clone(), stored);
5150 }
5151
5152 self.context.bind(var_name, value.clone());
5154 return Ok(value);
5155 }
5156
5157 if op == BinaryOp::In {
5160 let left = self.evaluate_internal(lhs, data)?;
5161
5162 if matches!(left, JValue::Array(_)) {
5164 let right_result = self.evaluate_internal(rhs, data);
5166
5167 if let Ok(JValue::Number(_)) = right_result {
5168 return self.array_index(&left, &right_result.unwrap());
5170 } else {
5171 return self.array_filter(lhs, rhs, &left, data);
5174 }
5175 }
5176 }
5177
5178 if op == BinaryOp::And {
5180 let left = self.evaluate_internal(lhs, data)?;
5181 if !self.is_truthy(&left) {
5182 return Ok(JValue::Bool(false));
5184 }
5185 let right = self.evaluate_internal(rhs, data)?;
5186 return Ok(JValue::Bool(self.is_truthy(&right)));
5187 }
5188
5189 if op == BinaryOp::Or {
5190 let left = self.evaluate_internal(lhs, data)?;
5191 if self.is_truthy(&left) {
5192 return Ok(JValue::Bool(true));
5194 }
5195 let right = self.evaluate_internal(rhs, data)?;
5196 return Ok(JValue::Bool(self.is_truthy(&right)));
5197 }
5198
5199 let left_is_explicit_null = matches!(lhs, AstNode::Null);
5201 let right_is_explicit_null = matches!(rhs, AstNode::Null);
5202
5203 let left = self.evaluate_internal(lhs, data)?;
5205 let right = self.evaluate_internal(rhs, data)?;
5206
5207 match op {
5208 BinaryOp::Add => self.add(&left, &right, left_is_explicit_null, right_is_explicit_null),
5209 BinaryOp::Subtract => {
5210 self.subtract(&left, &right, left_is_explicit_null, right_is_explicit_null)
5211 }
5212 BinaryOp::Multiply => {
5213 self.multiply(&left, &right, left_is_explicit_null, right_is_explicit_null)
5214 }
5215 BinaryOp::Divide => {
5216 self.divide(&left, &right, left_is_explicit_null, right_is_explicit_null)
5217 }
5218 BinaryOp::Modulo => {
5219 self.modulo(&left, &right, left_is_explicit_null, right_is_explicit_null)
5220 }
5221
5222 BinaryOp::Equal => Ok(JValue::Bool(self.equals(&left, &right))),
5223 BinaryOp::NotEqual => Ok(JValue::Bool(!self.equals(&left, &right))),
5224 BinaryOp::LessThan => {
5225 self.less_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
5226 }
5227 BinaryOp::LessThanOrEqual => self.less_than_or_equal(
5228 &left,
5229 &right,
5230 left_is_explicit_null,
5231 right_is_explicit_null,
5232 ),
5233 BinaryOp::GreaterThan => {
5234 self.greater_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
5235 }
5236 BinaryOp::GreaterThanOrEqual => self.greater_than_or_equal(
5237 &left,
5238 &right,
5239 left_is_explicit_null,
5240 right_is_explicit_null,
5241 ),
5242
5243 BinaryOp::And | BinaryOp::Or => unreachable!(),
5245
5246 BinaryOp::Concatenate => self.concatenate(&left, &right),
5247 BinaryOp::Range => self.range(&left, &right),
5248 BinaryOp::In => self.in_operator(&left, &right),
5249
5250 BinaryOp::ColonEqual | BinaryOp::Coalesce | BinaryOp::Default | BinaryOp::ChainPipe => {
5252 unreachable!()
5253 }
5254 }
5255 }
5256
5257 fn evaluate_unary_op(
5259 &mut self,
5260 op: crate::ast::UnaryOp,
5261 operand: &AstNode,
5262 data: &JValue,
5263 ) -> Result<JValue, EvaluatorError> {
5264 use crate::ast::UnaryOp;
5265
5266 let value = self.evaluate_internal(operand, data)?;
5267
5268 match op {
5269 UnaryOp::Negate => match value {
5270 JValue::Null => Ok(JValue::Null),
5272 JValue::Number(n) => Ok(JValue::Number(-n)),
5273 _ => Err(EvaluatorError::TypeError(
5274 "D1002: Cannot negate non-number value".to_string(),
5275 )),
5276 },
5277 UnaryOp::Not => Ok(JValue::Bool(!self.is_truthy(&value))),
5278 }
5279 }
5280
5281 fn try_fused_aggregate(
5288 &mut self,
5289 name: &str,
5290 arg: &AstNode,
5291 data: &JValue,
5292 ) -> Result<Option<JValue>, EvaluatorError> {
5293 if !matches!(name, "sum" | "max" | "min" | "average") {
5295 return Ok(None);
5296 }
5297
5298 let AstNode::Path { steps } = arg else {
5300 return Ok(None);
5301 };
5302
5303 if steps.len() != 2 {
5306 return Ok(None);
5307 }
5308
5309 let field_step = &steps[1];
5311 if !field_step.stages.is_empty() {
5312 return Ok(None);
5313 }
5314 let AstNode::Name(extract_field) = &field_step.node else {
5315 return Ok(None);
5316 };
5317
5318 let arr_step = &steps[0];
5320 let AstNode::Name(arr_name) = &arr_step.node else {
5321 return Ok(None);
5322 };
5323
5324 let arr = match data {
5326 JValue::Object(obj) => match obj.get(arr_name) {
5327 Some(JValue::Array(arr)) => arr,
5328 _ => return Ok(None),
5329 },
5330 _ => return Ok(None),
5331 };
5332
5333 let filter_compiled = match arr_step.stages.as_slice() {
5335 [] => None,
5336 [Stage::Filter(pred)] => try_compile_expr(pred),
5337 _ => return Ok(None),
5338 };
5339 if !arr_step.stages.is_empty() && filter_compiled.is_none() {
5341 return Ok(None);
5342 }
5343
5344 let shape = arr.first().and_then(build_shape_cache);
5346
5347 let mut total = 0.0f64;
5349 let mut count = 0usize;
5350 let mut max_val = f64::NEG_INFINITY;
5351 let mut min_val = f64::INFINITY;
5352 let mut has_any = false;
5353
5354 for item in arr.iter() {
5355 if let Some(ref compiled) = filter_compiled {
5357 let result = if let Some(ref s) = shape {
5358 eval_compiled_shaped(compiled, item, None, s)?
5359 } else {
5360 eval_compiled(compiled, item, None)?
5361 };
5362 if !compiled_is_truthy(&result) {
5363 continue;
5364 }
5365 }
5366
5367 let val = match item {
5369 JValue::Object(obj) => match obj.get(extract_field) {
5370 Some(JValue::Number(n)) => *n,
5371 Some(_) | None => continue, },
5373 _ => continue,
5374 };
5375
5376 has_any = true;
5377 match name {
5378 "sum" => total += val,
5379 "max" => max_val = max_val.max(val),
5380 "min" => min_val = min_val.min(val),
5381 "average" => {
5382 total += val;
5383 count += 1;
5384 }
5385 _ => unreachable!(),
5386 }
5387 }
5388
5389 if !has_any {
5390 return Ok(Some(match name {
5391 "sum" => JValue::from(0i64),
5392 "average" | "max" | "min" => JValue::Null,
5393 _ => unreachable!(),
5394 }));
5395 }
5396
5397 Ok(Some(match name {
5398 "sum" => JValue::Number(total),
5399 "max" => JValue::Number(max_val),
5400 "min" => JValue::Number(min_val),
5401 "average" => JValue::Number(total / count as f64),
5402 _ => unreachable!(),
5403 }))
5404 }
5405
5406 fn evaluate_function_call(
5408 &mut self,
5409 name: &str,
5410 args: &[AstNode],
5411 is_builtin: bool,
5412 data: &JValue,
5413 ) -> Result<JValue, EvaluatorError> {
5414 use crate::functions;
5415
5416 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
5418 if has_placeholder {
5419 return self.create_partial_application(name, args, is_builtin, data);
5420 }
5421
5422 if let Some(value) = self.context.lookup(name).cloned() {
5429 if let Some(stored_lambda) = self.lookup_lambda_from_value(&value) {
5430 let mut evaluated_args = Vec::with_capacity(args.len());
5431 for arg in args {
5432 evaluated_args.push(self.evaluate_internal(arg, data)?);
5433 }
5434 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5435 }
5436 if let JValue::Builtin { name: builtin_name } = &value {
5437 let mut evaluated_args = Vec::with_capacity(args.len());
5439 for arg in args {
5440 evaluated_args.push(self.evaluate_internal(arg, data)?);
5441 }
5442 return self.call_builtin_with_values(builtin_name, &evaluated_args);
5443 }
5444 }
5445
5446 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
5449 let mut evaluated_args = Vec::with_capacity(args.len());
5450 for arg in args {
5451 evaluated_args.push(self.evaluate_internal(arg, data)?);
5452 }
5453 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5454 }
5455
5456 if !is_builtin && name != "__lambda__" {
5459 return Err(EvaluatorError::ReferenceError(format!(
5460 "Unknown function: {}",
5461 name
5462 )));
5463 }
5464
5465 if name == "exists" && args.len() == 1 {
5468 let arg = &args[0];
5469
5470 if matches!(arg, AstNode::Null) {
5472 return Ok(JValue::Bool(true)); }
5474
5475 if let AstNode::Variable(var_name) = arg {
5477 if self.is_builtin_function(var_name) {
5478 return Ok(JValue::Bool(true)); }
5480
5481 if self.context.lookup_lambda(var_name).is_some() {
5483 return Ok(JValue::Bool(true)); }
5485
5486 if let Some(val) = self.context.lookup(var_name) {
5488 if val.is_undefined() {
5490 return Ok(JValue::Bool(false));
5491 }
5492 return Ok(JValue::Bool(true)); } else {
5494 return Ok(JValue::Bool(false)); }
5496 }
5497
5498 let value = self.evaluate_internal(arg, data)?;
5500 return Ok(JValue::Bool(!value.is_null() && !value.is_undefined()));
5501 }
5502
5503 for arg in args {
5506 if let AstNode::Variable(var_name) = arg {
5508 if !var_name.is_empty()
5510 && !self.is_builtin_function(var_name)
5511 && self.context.lookup(var_name).is_none()
5512 {
5513 if propagates_undefined(name) {
5515 return Ok(JValue::Null); }
5517 }
5518 }
5519 if let AstNode::Name(field_name) = arg {
5521 let field_exists =
5522 matches!(data, JValue::Object(obj) if obj.contains_key(field_name));
5523 if !field_exists && propagates_undefined(name) {
5524 return Ok(JValue::Null);
5525 }
5526 }
5527 if let AstNode::Path { steps } = arg {
5532 if let Ok(JValue::Null) = self.evaluate_internal(arg, data) {
5539 if steps.len() == 1 {
5542 let field_name = match &steps[0].node {
5544 AstNode::Name(n) => Some(n.as_str()),
5545 AstNode::String(s) => Some(s.as_str()),
5546 _ => None,
5547 };
5548 if let Some(field) = field_name {
5549 match data {
5550 JValue::Object(obj) => {
5551 if !obj.contains_key(field) {
5552 if propagates_undefined(name) {
5554 return Ok(JValue::Null);
5555 }
5556 }
5557 }
5559 JValue::Null => {
5560 if propagates_undefined(name) {
5562 return Ok(JValue::Null);
5563 }
5564 }
5565 _ => {}
5566 }
5567 }
5568 }
5569 else if steps.len() > 1 {
5571 let mut current = data;
5573 let mut failed_due_to_missing_field = false;
5574
5575 for (i, step) in steps.iter().enumerate() {
5576 if let AstNode::Name(field_name) = &step.node {
5577 match current {
5578 JValue::Object(obj) => {
5579 if let Some(val) = obj.get(field_name) {
5580 current = val;
5581 } else {
5582 failed_due_to_missing_field = true;
5584 break;
5585 }
5586 }
5587 JValue::Array(_) => {
5588 break;
5590 }
5591 JValue::Null => {
5592 if i > 0 {
5594 failed_due_to_missing_field = false;
5596 }
5597 break;
5598 }
5599 _ => break,
5600 }
5601 }
5602 }
5603
5604 if failed_due_to_missing_field && propagates_undefined(name) {
5605 return Ok(JValue::Null);
5606 }
5607 }
5608 }
5609 }
5610 }
5611
5612 if args.len() == 1 {
5615 if let Some(result) = self.try_fused_aggregate(name, &args[0], data)? {
5616 return Ok(result);
5617 }
5618 }
5619
5620 let mut evaluated_args = Vec::with_capacity(args.len());
5621 for arg in args {
5622 evaluated_args.push(self.evaluate_internal(arg, data)?);
5623 }
5624
5625 let context_functions_zero_arg = ["string", "number", "boolean", "uppercase", "lowercase"];
5630 let context_functions_missing_first = [
5631 "substringBefore",
5632 "substringAfter",
5633 "contains",
5634 "split",
5635 "replace",
5636 ];
5637
5638 if evaluated_args.is_empty() && context_functions_zero_arg.contains(&name) {
5639 evaluated_args.push(data.clone());
5641 } else if evaluated_args.len() == 1 && context_functions_missing_first.contains(&name) {
5642 if matches!(data, JValue::String(_)) {
5646 evaluated_args.insert(0, data.clone());
5647 }
5648 }
5649
5650 if name == "string"
5653 && args.is_empty()
5654 && !evaluated_args.is_empty()
5655 && evaluated_args[0].is_null()
5656 {
5657 return Ok(JValue::Null);
5659 }
5660
5661 match name {
5662 "string" => {
5663 if evaluated_args.len() > 2 {
5664 return Err(EvaluatorError::EvaluationError(
5665 "string() takes at most 2 arguments".to_string(),
5666 ));
5667 }
5668
5669 let prettify = if evaluated_args.len() == 2 {
5670 match &evaluated_args[1] {
5671 JValue::Bool(b) => Some(*b),
5672 _ => {
5673 return Err(EvaluatorError::TypeError(
5674 "string() prettify parameter must be a boolean".to_string(),
5675 ))
5676 }
5677 }
5678 } else {
5679 None
5680 };
5681
5682 Ok(functions::string::string(&evaluated_args[0], prettify)?)
5683 }
5684 "length" => {
5685 if evaluated_args.len() != 1 {
5686 return Err(EvaluatorError::EvaluationError(
5687 "length() requires exactly 1 argument".to_string(),
5688 ));
5689 }
5690 match &evaluated_args[0] {
5691 JValue::String(s) => Ok(functions::string::length(s)?),
5692 _ => Err(EvaluatorError::TypeError(
5693 "T0410: Argument 1 of function length does not match function signature"
5694 .to_string(),
5695 )),
5696 }
5697 }
5698 "uppercase" => {
5699 if evaluated_args.len() != 1 {
5700 return Err(EvaluatorError::EvaluationError(
5701 "uppercase() requires exactly 1 argument".to_string(),
5702 ));
5703 }
5704 match &evaluated_args[0] {
5705 JValue::String(s) => Ok(functions::string::uppercase(s)?),
5706 _ => Err(EvaluatorError::TypeError(
5707 "T0410: Argument 1 of function uppercase does not match function signature"
5708 .to_string(),
5709 )),
5710 }
5711 }
5712 "lowercase" => {
5713 if evaluated_args.len() != 1 {
5714 return Err(EvaluatorError::EvaluationError(
5715 "lowercase() requires exactly 1 argument".to_string(),
5716 ));
5717 }
5718 match &evaluated_args[0] {
5719 JValue::String(s) => Ok(functions::string::lowercase(s)?),
5720 _ => Err(EvaluatorError::TypeError(
5721 "T0410: Argument 1 of function lowercase does not match function signature"
5722 .to_string(),
5723 )),
5724 }
5725 }
5726 "number" => {
5727 if evaluated_args.is_empty() {
5728 return Err(EvaluatorError::EvaluationError(
5729 "number() requires at least 1 argument".to_string(),
5730 ));
5731 }
5732 if evaluated_args.len() > 1 {
5733 return Err(EvaluatorError::TypeError(
5734 "T0410: Argument 2 of function number does not match function signature"
5735 .to_string(),
5736 ));
5737 }
5738 Ok(functions::numeric::number(&evaluated_args[0])?)
5739 }
5740 "sum" => {
5741 if evaluated_args.len() != 1 {
5742 return Err(EvaluatorError::EvaluationError(
5743 "sum() requires exactly 1 argument".to_string(),
5744 ));
5745 }
5746 if evaluated_args[0].is_undefined() {
5748 return Ok(JValue::Undefined);
5749 }
5750 match &evaluated_args[0] {
5751 JValue::Null => Ok(JValue::Null),
5752 JValue::Array(arr) => {
5753 Ok(aggregation::sum(arr)?)
5755 }
5756 JValue::Number(n) => Ok(JValue::Number(*n)),
5758 other => Ok(functions::numeric::sum(&[other.clone()])?),
5759 }
5760 }
5761 "count" => {
5762 if evaluated_args.len() != 1 {
5763 return Err(EvaluatorError::EvaluationError(
5764 "count() requires exactly 1 argument".to_string(),
5765 ));
5766 }
5767 if evaluated_args[0].is_undefined() {
5769 return Ok(JValue::from(0i64));
5770 }
5771 match &evaluated_args[0] {
5772 JValue::Null => Ok(JValue::from(0i64)), JValue::Array(arr) => Ok(functions::array::count(arr)?),
5774 _ => Ok(JValue::from(1i64)), }
5776 }
5777 "substring" => {
5778 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5779 return Err(EvaluatorError::EvaluationError(
5780 "substring() requires 2 or 3 arguments".to_string(),
5781 ));
5782 }
5783 match (&evaluated_args[0], &evaluated_args[1]) {
5784 (JValue::String(s), JValue::Number(start)) => {
5785 let length = if evaluated_args.len() == 3 {
5786 match &evaluated_args[2] {
5787 JValue::Number(l) => Some(*l as i64),
5788 _ => return Err(EvaluatorError::TypeError(
5789 "T0410: Argument 3 of function substring does not match function signature".to_string(),
5790 )),
5791 }
5792 } else {
5793 None
5794 };
5795 Ok(functions::string::substring(s, *start as i64, length)?)
5796 }
5797 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5798 "T0410: Argument 2 of function substring does not match function signature"
5799 .to_string(),
5800 )),
5801 _ => Err(EvaluatorError::TypeError(
5802 "T0410: Argument 1 of function substring does not match function signature"
5803 .to_string(),
5804 )),
5805 }
5806 }
5807 "substringBefore" => {
5808 if evaluated_args.len() != 2 {
5809 return Err(EvaluatorError::TypeError(
5810 "T0411: Context value is not a compatible type with argument 2 of function substringBefore".to_string(),
5811 ));
5812 }
5813 match (&evaluated_args[0], &evaluated_args[1]) {
5814 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_before(s, sep)?),
5815 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5816 "T0410: Argument 2 of function substringBefore does not match function signature".to_string(),
5817 )),
5818 _ => Err(EvaluatorError::TypeError(
5819 "T0410: Argument 1 of function substringBefore does not match function signature".to_string(),
5820 )),
5821 }
5822 }
5823 "substringAfter" => {
5824 if evaluated_args.len() != 2 {
5825 return Err(EvaluatorError::TypeError(
5826 "T0411: Context value is not a compatible type with argument 2 of function substringAfter".to_string(),
5827 ));
5828 }
5829 match (&evaluated_args[0], &evaluated_args[1]) {
5830 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_after(s, sep)?),
5831 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5832 "T0410: Argument 2 of function substringAfter does not match function signature".to_string(),
5833 )),
5834 _ => Err(EvaluatorError::TypeError(
5835 "T0410: Argument 1 of function substringAfter does not match function signature".to_string(),
5836 )),
5837 }
5838 }
5839 "pad" => {
5840 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
5841 return Err(EvaluatorError::EvaluationError(
5842 "pad() requires 2 or 3 arguments".to_string(),
5843 ));
5844 }
5845
5846 let string = match &evaluated_args[0] {
5848 JValue::String(s) => s.clone(),
5849 JValue::Null => return Ok(JValue::Null),
5850 _ => {
5851 return Err(EvaluatorError::TypeError(
5852 "pad() first argument must be a string".to_string(),
5853 ))
5854 }
5855 };
5856
5857 let width = match &evaluated_args.get(1) {
5859 Some(JValue::Number(n)) => *n as i32,
5860 _ => {
5861 return Err(EvaluatorError::TypeError(
5862 "pad() second argument must be a number".to_string(),
5863 ))
5864 }
5865 };
5866
5867 let pad_string = match evaluated_args.get(2) {
5869 Some(JValue::String(s)) if !s.is_empty() => s.clone(),
5870 _ => Rc::from(" "),
5871 };
5872
5873 let abs_width = width.unsigned_abs() as usize;
5874 let char_count = string.chars().count();
5876
5877 if char_count >= abs_width {
5878 return Ok(JValue::string(string));
5880 }
5881
5882 let padding_needed = abs_width - char_count;
5883
5884 let pad_chars: Vec<char> = pad_string.chars().collect();
5885 let mut padding = String::with_capacity(padding_needed);
5886 for i in 0..padding_needed {
5887 padding.push(pad_chars[i % pad_chars.len()]);
5888 }
5889
5890 let result = if width < 0 {
5891 format!("{}{}", padding, string)
5893 } else {
5894 format!("{}{}", string, padding)
5896 };
5897
5898 Ok(JValue::string(result))
5899 }
5900
5901 "trim" => {
5902 if evaluated_args.is_empty() {
5903 return Ok(JValue::Null); }
5905 if evaluated_args.len() != 1 {
5906 return Err(EvaluatorError::EvaluationError(
5907 "trim() requires at most 1 argument".to_string(),
5908 ));
5909 }
5910 match &evaluated_args[0] {
5911 JValue::Null => Ok(JValue::Null),
5912 JValue::String(s) => Ok(functions::string::trim(s)?),
5913 _ => Err(EvaluatorError::TypeError(
5914 "trim() requires a string argument".to_string(),
5915 )),
5916 }
5917 }
5918 "contains" => {
5919 if evaluated_args.len() != 2 {
5920 return Err(EvaluatorError::EvaluationError(
5921 "contains() requires exactly 2 arguments".to_string(),
5922 ));
5923 }
5924 if evaluated_args[0].is_null() {
5925 return Ok(JValue::Null);
5926 }
5927 match &evaluated_args[0] {
5928 JValue::String(s) => Ok(functions::string::contains(s, &evaluated_args[1])?),
5929 _ => Err(EvaluatorError::TypeError(
5930 "contains() requires a string as the first argument".to_string(),
5931 )),
5932 }
5933 }
5934 "split" => {
5935 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5936 return Err(EvaluatorError::EvaluationError(
5937 "split() requires 2 or 3 arguments".to_string(),
5938 ));
5939 }
5940 if evaluated_args[0].is_null() {
5941 return Ok(JValue::Null);
5942 }
5943 match &evaluated_args[0] {
5944 JValue::String(s) => {
5945 let limit = if evaluated_args.len() == 3 {
5946 match &evaluated_args[2] {
5947 JValue::Number(n) => {
5948 let f = *n;
5949 if f < 0.0 {
5951 return Err(EvaluatorError::EvaluationError(
5952 "D3020: Third argument of split function must be a positive number".to_string(),
5953 ));
5954 }
5955 Some(f.floor() as usize)
5957 }
5958 _ => {
5959 return Err(EvaluatorError::TypeError(
5960 "split() limit must be a number".to_string(),
5961 ))
5962 }
5963 }
5964 } else {
5965 None
5966 };
5967 Ok(functions::string::split(s, &evaluated_args[1], limit)?)
5968 }
5969 _ => Err(EvaluatorError::TypeError(
5970 "split() requires a string as the first argument".to_string(),
5971 )),
5972 }
5973 }
5974 "join" => {
5975 if evaluated_args.is_empty() {
5978 return Err(EvaluatorError::TypeError(
5979 "T0410: Argument 1 of function $join does not match function signature"
5980 .to_string(),
5981 ));
5982 }
5983 if evaluated_args[0].is_null() {
5984 return Ok(JValue::Null);
5985 }
5986
5987 use crate::signature::Signature;
5990
5991 let signature = Signature::parse("<a<s>s?:s>").map_err(|e| {
5992 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
5993 })?;
5994
5995 let coerced_args = match signature.validate_and_coerce(&evaluated_args) {
5996 Ok(args) => args,
5997 Err(crate::signature::SignatureError::UndefinedArgument) => {
5998 let sig_first_arg = Signature::parse("<a<s>:a<s>>").map_err(|e| {
6001 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
6002 })?;
6003
6004 match sig_first_arg.validate_and_coerce(&evaluated_args[0..1]) {
6005 Ok(args) => args,
6006 Err(crate::signature::SignatureError::ArrayTypeMismatch {
6007 index,
6008 expected,
6009 }) => {
6010 return Err(EvaluatorError::TypeError(format!(
6011 "T0412: Argument {} of function $join must be an array of {}",
6012 index, expected
6013 )));
6014 }
6015 Err(e) => {
6016 return Err(EvaluatorError::TypeError(format!(
6017 "Signature validation failed: {}",
6018 e
6019 )));
6020 }
6021 }
6022 }
6023 Err(crate::signature::SignatureError::ArgumentTypeMismatch {
6024 index,
6025 expected,
6026 }) => {
6027 return Err(EvaluatorError::TypeError(
6028 format!("T0410: Argument {} of function $join does not match function signature (expected {})", index, expected)
6029 ));
6030 }
6031 Err(crate::signature::SignatureError::ArrayTypeMismatch {
6032 index,
6033 expected,
6034 }) => {
6035 return Err(EvaluatorError::TypeError(format!(
6036 "T0412: Argument {} of function $join must be an array of {}",
6037 index, expected
6038 )));
6039 }
6040 Err(e) => {
6041 return Err(EvaluatorError::TypeError(format!(
6042 "Signature validation failed: {}",
6043 e
6044 )));
6045 }
6046 };
6047
6048 match &coerced_args[0] {
6050 JValue::Array(arr) => {
6051 let separator = if coerced_args.len() == 2 {
6052 match &coerced_args[1] {
6053 JValue::String(s) => Some(&**s),
6054 JValue::Null => None, _ => None, }
6057 } else {
6058 None };
6060 Ok(functions::string::join(arr, separator)?)
6061 }
6062 JValue::Null => Ok(JValue::Null),
6063 _ => unreachable!("Signature validation should ensure array type"),
6064 }
6065 }
6066 "replace" => {
6067 if evaluated_args.len() < 3 || evaluated_args.len() > 4 {
6068 return Err(EvaluatorError::EvaluationError(
6069 "replace() requires 3 or 4 arguments".to_string(),
6070 ));
6071 }
6072 if evaluated_args[0].is_null() {
6073 return Ok(JValue::Null);
6074 }
6075
6076 let replacement_is_lambda = matches!(
6078 evaluated_args[2],
6079 JValue::Lambda { .. } | JValue::Builtin { .. }
6080 );
6081
6082 if replacement_is_lambda {
6083 return self.replace_with_lambda(
6085 &evaluated_args[0],
6086 &evaluated_args[1],
6087 &evaluated_args[2],
6088 if evaluated_args.len() == 4 {
6089 Some(&evaluated_args[3])
6090 } else {
6091 None
6092 },
6093 data,
6094 );
6095 }
6096
6097 match (&evaluated_args[0], &evaluated_args[2]) {
6099 (JValue::String(s), JValue::String(replacement)) => {
6100 let limit = if evaluated_args.len() == 4 {
6101 match &evaluated_args[3] {
6102 JValue::Number(n) => {
6103 let lim_f64 = *n;
6104 if lim_f64 < 0.0 {
6105 return Err(EvaluatorError::EvaluationError(format!(
6106 "D3011: Limit must be non-negative, got {}",
6107 lim_f64
6108 )));
6109 }
6110 Some(lim_f64 as usize)
6111 }
6112 _ => {
6113 return Err(EvaluatorError::TypeError(
6114 "replace() limit must be a number".to_string(),
6115 ))
6116 }
6117 }
6118 } else {
6119 None
6120 };
6121 Ok(functions::string::replace(
6122 s,
6123 &evaluated_args[1],
6124 replacement,
6125 limit,
6126 )?)
6127 }
6128 _ => Err(EvaluatorError::TypeError(
6129 "replace() requires string arguments".to_string(),
6130 )),
6131 }
6132 }
6133 "match" => {
6134 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
6137 return Err(EvaluatorError::EvaluationError(
6138 "match() requires 1 to 3 arguments".to_string(),
6139 ));
6140 }
6141 if evaluated_args[0].is_null() {
6142 return Ok(JValue::Null);
6143 }
6144
6145 let s = match &evaluated_args[0] {
6146 JValue::String(s) => s.clone(),
6147 _ => {
6148 return Err(EvaluatorError::TypeError(
6149 "match() first argument must be a string".to_string(),
6150 ))
6151 }
6152 };
6153
6154 let limit = if evaluated_args.len() == 3 {
6156 match &evaluated_args[2] {
6157 JValue::Number(n) => Some(*n as usize),
6158 JValue::Null => None,
6159 _ => {
6160 return Err(EvaluatorError::TypeError(
6161 "match() limit must be a number".to_string(),
6162 ))
6163 }
6164 }
6165 } else {
6166 None
6167 };
6168
6169 let pattern_value = evaluated_args.get(1);
6171 let is_custom_matcher = pattern_value.is_some_and(|val| {
6172 matches!(val, JValue::Lambda { .. } | JValue::Builtin { .. })
6173 });
6174
6175 if is_custom_matcher {
6176 return self.match_with_custom_matcher(&s, &args[1], limit, data);
6179 }
6180
6181 let (pattern, flags) = match pattern_value {
6183 Some(val) => crate::functions::string::extract_regex(val).ok_or_else(|| {
6184 EvaluatorError::TypeError(
6185 "match() second argument must be a regex pattern or matcher function"
6186 .to_string(),
6187 )
6188 })?,
6189 None => (".*".to_string(), "".to_string()),
6190 };
6191
6192 let is_global = flags.contains('g');
6194 let regex_pattern = if flags.contains('i') {
6195 format!("(?i){}", pattern)
6196 } else {
6197 pattern.clone()
6198 };
6199
6200 let re = regex::Regex::new(®ex_pattern).map_err(|e| {
6201 EvaluatorError::EvaluationError(format!("Invalid regex pattern: {}", e))
6202 })?;
6203
6204 let mut results = Vec::new();
6205 let mut count = 0;
6206
6207 for caps in re.captures_iter(&s) {
6208 if let Some(lim) = limit {
6209 if count >= lim {
6210 break;
6211 }
6212 }
6213
6214 let full_match = caps.get(0).unwrap();
6215 let mut match_obj = IndexMap::new();
6216 match_obj.insert(
6217 "match".to_string(),
6218 JValue::string(full_match.as_str().to_string()),
6219 );
6220 match_obj.insert(
6221 "index".to_string(),
6222 JValue::Number(full_match.start() as f64),
6223 );
6224
6225 let mut groups: Vec<JValue> = Vec::new();
6227 for i in 1..caps.len() {
6228 if let Some(group) = caps.get(i) {
6229 groups.push(JValue::string(group.as_str().to_string()));
6230 } else {
6231 groups.push(JValue::Null);
6232 }
6233 }
6234 if !groups.is_empty() {
6235 match_obj.insert("groups".to_string(), JValue::array(groups));
6236 }
6237
6238 results.push(JValue::object(match_obj));
6239 count += 1;
6240
6241 if !is_global {
6243 break;
6244 }
6245 }
6246
6247 if results.is_empty() {
6248 Ok(JValue::Null)
6249 } else if results.len() == 1 && !is_global {
6250 Ok(results.into_iter().next().unwrap())
6252 } else {
6253 Ok(JValue::array(results))
6254 }
6255 }
6256 "max" => {
6257 if evaluated_args.len() != 1 {
6258 return Err(EvaluatorError::EvaluationError(
6259 "max() requires exactly 1 argument".to_string(),
6260 ));
6261 }
6262 if evaluated_args[0].is_undefined() {
6264 return Ok(JValue::Undefined);
6265 }
6266 match &evaluated_args[0] {
6267 JValue::Null => Ok(JValue::Null),
6268 JValue::Array(arr) => {
6269 Ok(aggregation::max(arr)?)
6271 }
6272 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
6274 "max() requires an array or number argument".to_string(),
6275 )),
6276 }
6277 }
6278 "min" => {
6279 if evaluated_args.len() != 1 {
6280 return Err(EvaluatorError::EvaluationError(
6281 "min() requires exactly 1 argument".to_string(),
6282 ));
6283 }
6284 if evaluated_args[0].is_undefined() {
6286 return Ok(JValue::Undefined);
6287 }
6288 match &evaluated_args[0] {
6289 JValue::Null => Ok(JValue::Null),
6290 JValue::Array(arr) => {
6291 Ok(aggregation::min(arr)?)
6293 }
6294 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
6296 "min() requires an array or number argument".to_string(),
6297 )),
6298 }
6299 }
6300 "average" => {
6301 if evaluated_args.len() != 1 {
6302 return Err(EvaluatorError::EvaluationError(
6303 "average() requires exactly 1 argument".to_string(),
6304 ));
6305 }
6306 if evaluated_args[0].is_undefined() {
6308 return Ok(JValue::Undefined);
6309 }
6310 match &evaluated_args[0] {
6311 JValue::Null => Ok(JValue::Null),
6312 JValue::Array(arr) => {
6313 Ok(aggregation::average(arr)?)
6315 }
6316 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
6318 "average() requires an array or number argument".to_string(),
6319 )),
6320 }
6321 }
6322 "abs" => {
6323 if evaluated_args.len() != 1 {
6324 return Err(EvaluatorError::EvaluationError(
6325 "abs() requires exactly 1 argument".to_string(),
6326 ));
6327 }
6328 match &evaluated_args[0] {
6329 JValue::Null => Ok(JValue::Null),
6330 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
6331 _ => Err(EvaluatorError::TypeError(
6332 "abs() requires a number argument".to_string(),
6333 )),
6334 }
6335 }
6336 "floor" => {
6337 if evaluated_args.len() != 1 {
6338 return Err(EvaluatorError::EvaluationError(
6339 "floor() requires exactly 1 argument".to_string(),
6340 ));
6341 }
6342 match &evaluated_args[0] {
6343 JValue::Null => Ok(JValue::Null),
6344 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
6345 _ => Err(EvaluatorError::TypeError(
6346 "floor() requires a number argument".to_string(),
6347 )),
6348 }
6349 }
6350 "ceil" => {
6351 if evaluated_args.len() != 1 {
6352 return Err(EvaluatorError::EvaluationError(
6353 "ceil() requires exactly 1 argument".to_string(),
6354 ));
6355 }
6356 match &evaluated_args[0] {
6357 JValue::Null => Ok(JValue::Null),
6358 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
6359 _ => Err(EvaluatorError::TypeError(
6360 "ceil() requires a number argument".to_string(),
6361 )),
6362 }
6363 }
6364 "round" => {
6365 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6366 return Err(EvaluatorError::EvaluationError(
6367 "round() requires 1 or 2 arguments".to_string(),
6368 ));
6369 }
6370 match &evaluated_args[0] {
6371 JValue::Null => Ok(JValue::Null),
6372 JValue::Number(n) => {
6373 let precision = if evaluated_args.len() == 2 {
6374 match &evaluated_args[1] {
6375 JValue::Number(p) => Some(*p as i32),
6376 _ => {
6377 return Err(EvaluatorError::TypeError(
6378 "round() precision must be a number".to_string(),
6379 ))
6380 }
6381 }
6382 } else {
6383 None
6384 };
6385 Ok(functions::numeric::round(*n, precision)?)
6386 }
6387 _ => Err(EvaluatorError::TypeError(
6388 "round() requires a number argument".to_string(),
6389 )),
6390 }
6391 }
6392 "sqrt" => {
6393 if evaluated_args.len() != 1 {
6394 return Err(EvaluatorError::EvaluationError(
6395 "sqrt() requires exactly 1 argument".to_string(),
6396 ));
6397 }
6398 match &evaluated_args[0] {
6399 JValue::Null => Ok(JValue::Null),
6400 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
6401 _ => Err(EvaluatorError::TypeError(
6402 "sqrt() requires a number argument".to_string(),
6403 )),
6404 }
6405 }
6406 "power" => {
6407 if evaluated_args.len() != 2 {
6408 return Err(EvaluatorError::EvaluationError(
6409 "power() requires exactly 2 arguments".to_string(),
6410 ));
6411 }
6412 if evaluated_args[0].is_null() {
6413 return Ok(JValue::Null);
6414 }
6415 match (&evaluated_args[0], &evaluated_args[1]) {
6416 (JValue::Number(base), JValue::Number(exp)) => {
6417 Ok(functions::numeric::power(*base, *exp)?)
6418 }
6419 _ => Err(EvaluatorError::TypeError(
6420 "power() requires number arguments".to_string(),
6421 )),
6422 }
6423 }
6424 "formatNumber" => {
6425 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
6426 return Err(EvaluatorError::EvaluationError(
6427 "formatNumber() requires 2 or 3 arguments".to_string(),
6428 ));
6429 }
6430 if evaluated_args[0].is_null() {
6431 return Ok(JValue::Null);
6432 }
6433 match (&evaluated_args[0], &evaluated_args[1]) {
6434 (JValue::Number(num), JValue::String(picture)) => {
6435 let options = if evaluated_args.len() == 3 {
6436 Some(&evaluated_args[2])
6437 } else {
6438 None
6439 };
6440 Ok(functions::numeric::format_number(*num, picture, options)?)
6441 }
6442 _ => Err(EvaluatorError::TypeError(
6443 "formatNumber() requires a number and a string".to_string(),
6444 )),
6445 }
6446 }
6447 "formatBase" => {
6448 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6449 return Err(EvaluatorError::EvaluationError(
6450 "formatBase() requires 1 or 2 arguments".to_string(),
6451 ));
6452 }
6453 if evaluated_args[0].is_null() {
6455 return Ok(JValue::Null);
6456 }
6457 match &evaluated_args[0] {
6458 JValue::Number(num) => {
6459 let radix = if evaluated_args.len() == 2 {
6460 match &evaluated_args[1] {
6461 JValue::Number(r) => Some(r.trunc() as i64),
6462 _ => {
6463 return Err(EvaluatorError::TypeError(
6464 "formatBase() radix must be a number".to_string(),
6465 ))
6466 }
6467 }
6468 } else {
6469 None
6470 };
6471 Ok(functions::numeric::format_base(*num, radix)?)
6472 }
6473 _ => Err(EvaluatorError::TypeError(
6474 "formatBase() requires a number".to_string(),
6475 )),
6476 }
6477 }
6478 "append" => {
6479 if evaluated_args.len() != 2 {
6480 return Err(EvaluatorError::EvaluationError(
6481 "append() requires exactly 2 arguments".to_string(),
6482 ));
6483 }
6484 let first = &evaluated_args[0];
6486 let second = &evaluated_args[1];
6487
6488 if second.is_null() {
6490 return Ok(first.clone());
6491 }
6492
6493 if first.is_null() {
6495 return Ok(second.clone());
6496 }
6497
6498 let arr = match first {
6500 JValue::Array(a) => a.to_vec(),
6501 other => vec![other.clone()], };
6503
6504 Ok(functions::array::append(&arr, second)?)
6505 }
6506 "reverse" => {
6507 if evaluated_args.len() != 1 {
6508 return Err(EvaluatorError::EvaluationError(
6509 "reverse() requires exactly 1 argument".to_string(),
6510 ));
6511 }
6512 match &evaluated_args[0] {
6513 JValue::Null => Ok(JValue::Null), JValue::Array(arr) => Ok(functions::array::reverse(arr)?),
6515 _ => Err(EvaluatorError::TypeError(
6516 "reverse() requires an array argument".to_string(),
6517 )),
6518 }
6519 }
6520 "shuffle" => {
6521 if evaluated_args.len() != 1 {
6522 return Err(EvaluatorError::EvaluationError(
6523 "shuffle() requires exactly 1 argument".to_string(),
6524 ));
6525 }
6526 if evaluated_args[0].is_null() {
6527 return Ok(JValue::Null);
6528 }
6529 match &evaluated_args[0] {
6530 JValue::Array(arr) => Ok(functions::array::shuffle(arr)?),
6531 _ => Err(EvaluatorError::TypeError(
6532 "shuffle() requires an array argument".to_string(),
6533 )),
6534 }
6535 }
6536
6537 "sift" => {
6538 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6540 return Err(EvaluatorError::EvaluationError(
6541 "sift() requires 1 or 2 arguments".to_string(),
6542 ));
6543 }
6544
6545 let func_arg = if evaluated_args.len() == 1 {
6547 &args[0]
6548 } else {
6549 &args[1]
6550 };
6551
6552 let param_count = self.get_callback_param_count(func_arg);
6554
6555 let sift_object = |evaluator: &mut Self,
6557 obj: &IndexMap<String, JValue>,
6558 func_node: &AstNode,
6559 context_data: &JValue,
6560 param_count: usize|
6561 -> Result<JValue, EvaluatorError> {
6562 let obj_value = if param_count >= 3 {
6564 Some(JValue::object(obj.clone()))
6565 } else {
6566 None
6567 };
6568
6569 let mut result = IndexMap::new();
6570 for (key, value) in obj.iter() {
6571 let call_args = match param_count {
6573 1 => vec![value.clone()],
6574 2 => vec![value.clone(), JValue::string(key.clone())],
6575 _ => vec![
6576 value.clone(),
6577 JValue::string(key.clone()),
6578 obj_value.as_ref().unwrap().clone(),
6579 ],
6580 };
6581
6582 let pred_result =
6583 evaluator.apply_function(func_node, &call_args, context_data)?;
6584 if evaluator.is_truthy(&pred_result) {
6585 result.insert(key.clone(), value.clone());
6586 }
6587 }
6588 if result.is_empty() {
6590 Ok(JValue::Undefined)
6591 } else {
6592 Ok(JValue::object(result))
6593 }
6594 };
6595
6596 if evaluated_args.len() == 1 {
6598 match data {
6600 JValue::Object(o) => sift_object(self, o, &args[0], data, param_count),
6601 JValue::Array(arr) => {
6602 let mut results = Vec::new();
6604 for item in arr.iter() {
6605 if let JValue::Object(o) = item {
6606 let sifted = sift_object(self, o, &args[0], item, param_count)?;
6607 if !sifted.is_undefined() {
6609 results.push(sifted);
6610 }
6611 }
6612 }
6613 Ok(JValue::array(results))
6614 }
6615 JValue::Null => Ok(JValue::Null),
6616 _ => Ok(JValue::Undefined),
6617 }
6618 } else {
6619 match &evaluated_args[0] {
6621 JValue::Object(o) => sift_object(self, o, &args[1], data, param_count),
6622 JValue::Null => Ok(JValue::Null),
6623 _ => Err(EvaluatorError::TypeError(
6624 "sift() first argument must be an object".to_string(),
6625 )),
6626 }
6627 }
6628 }
6629
6630 "zip" => {
6631 if evaluated_args.is_empty() {
6632 return Err(EvaluatorError::EvaluationError(
6633 "zip() requires at least 1 argument".to_string(),
6634 ));
6635 }
6636
6637 let mut arrays: Vec<Vec<JValue>> = Vec::with_capacity(evaluated_args.len());
6640 for arg in &evaluated_args {
6641 match arg {
6642 JValue::Array(arr) => {
6643 if arr.is_empty() {
6644 return Ok(JValue::array(vec![]));
6646 }
6647 arrays.push(arr.to_vec());
6648 }
6649 JValue::Null => {
6650 return Ok(JValue::array(vec![]));
6652 }
6653 other => {
6654 arrays.push(vec![other.clone()]);
6656 }
6657 }
6658 }
6659
6660 if arrays.is_empty() {
6661 return Ok(JValue::array(vec![]));
6662 }
6663
6664 let min_len = arrays.iter().map(|a| a.len()).min().unwrap_or(0);
6666
6667 let mut result = Vec::with_capacity(min_len);
6669 for i in 0..min_len {
6670 let mut tuple = Vec::with_capacity(arrays.len());
6671 for array in &arrays {
6672 tuple.push(array[i].clone());
6673 }
6674 result.push(JValue::array(tuple));
6675 }
6676
6677 Ok(JValue::array(result))
6678 }
6679
6680 "sort" => {
6681 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6682 return Err(EvaluatorError::EvaluationError(
6683 "sort() requires 1 or 2 arguments".to_string(),
6684 ));
6685 }
6686
6687 let array_value = &evaluated_args[0];
6689
6690 if array_value.is_null() {
6692 return Ok(JValue::Null);
6693 }
6694
6695 let mut arr = match array_value {
6696 JValue::Array(arr) => arr.to_vec(),
6697 other => vec![other.clone()],
6698 };
6699
6700 if args.len() == 2 {
6701 self.merge_sort_with_comparator(&mut arr, &args[1], data)?;
6704 Ok(JValue::array(arr))
6705 } else {
6706 Ok(functions::array::sort(&arr)?)
6708 }
6709 }
6710 "distinct" => {
6711 if evaluated_args.len() != 1 {
6712 return Err(EvaluatorError::EvaluationError(
6713 "distinct() requires exactly 1 argument".to_string(),
6714 ));
6715 }
6716 match &evaluated_args[0] {
6717 JValue::Array(arr) => Ok(functions::array::distinct(arr)?),
6718 _ => Err(EvaluatorError::TypeError(
6719 "distinct() requires an array argument".to_string(),
6720 )),
6721 }
6722 }
6723 "exists" => {
6724 if evaluated_args.len() != 1 {
6725 return Err(EvaluatorError::EvaluationError(
6726 "exists() requires exactly 1 argument".to_string(),
6727 ));
6728 }
6729 Ok(functions::array::exists(&evaluated_args[0])?)
6730 }
6731 "keys" => {
6732 if evaluated_args.len() != 1 {
6733 return Err(EvaluatorError::EvaluationError(
6734 "keys() requires exactly 1 argument".to_string(),
6735 ));
6736 }
6737
6738 let unwrap_single = |keys: Vec<JValue>| -> JValue {
6740 if keys.len() == 1 {
6741 keys.into_iter().next().unwrap()
6742 } else {
6743 JValue::array(keys)
6744 }
6745 };
6746
6747 match &evaluated_args[0] {
6748 JValue::Null => Ok(JValue::Null),
6749 JValue::Lambda { .. } | JValue::Builtin { .. } => Ok(JValue::Null),
6750 JValue::Object(obj) => {
6751 if obj.is_empty() {
6753 Ok(JValue::Null)
6754 } else {
6755 let keys: Vec<JValue> =
6756 obj.keys().map(|k| JValue::string(k.clone())).collect();
6757 Ok(unwrap_single(keys))
6758 }
6759 }
6760 JValue::Array(arr) => {
6761 let mut all_keys = Vec::new();
6763 for item in arr.iter() {
6764 if matches!(item, JValue::Lambda { .. } | JValue::Builtin { .. }) {
6766 continue;
6767 }
6768 if let JValue::Object(obj) = item {
6769 for key in obj.keys() {
6770 if !all_keys.contains(&JValue::string(key.clone())) {
6771 all_keys.push(JValue::string(key.clone()));
6772 }
6773 }
6774 }
6775 }
6776 if all_keys.is_empty() {
6777 Ok(JValue::Null)
6778 } else {
6779 Ok(unwrap_single(all_keys))
6780 }
6781 }
6782 _ => Ok(JValue::Null),
6784 }
6785 }
6786 "lookup" => {
6787 if evaluated_args.len() != 2 {
6788 return Err(EvaluatorError::EvaluationError(
6789 "lookup() requires exactly 2 arguments".to_string(),
6790 ));
6791 }
6792 if evaluated_args[0].is_null() {
6793 return Ok(JValue::Null);
6794 }
6795
6796 let key = match &evaluated_args[1] {
6797 JValue::String(k) => &**k,
6798 _ => {
6799 return Err(EvaluatorError::TypeError(
6800 "lookup() requires a string key".to_string(),
6801 ))
6802 }
6803 };
6804
6805 fn lookup_recursive(val: &JValue, key: &str) -> Vec<JValue> {
6807 match val {
6808 JValue::Array(arr) => {
6809 let mut results = Vec::new();
6810 for item in arr.iter() {
6811 let nested = lookup_recursive(item, key);
6812 results.extend(nested.iter().cloned());
6813 }
6814 results
6815 }
6816 JValue::Object(obj) => {
6817 if let Some(v) = obj.get(key) {
6818 vec![v.clone()]
6819 } else {
6820 vec![]
6821 }
6822 }
6823 _ => vec![],
6824 }
6825 }
6826
6827 let results = lookup_recursive(&evaluated_args[0], key);
6828 if results.is_empty() {
6829 Ok(JValue::Null)
6830 } else if results.len() == 1 {
6831 Ok(results[0].clone())
6832 } else {
6833 Ok(JValue::array(results))
6834 }
6835 }
6836 "spread" => {
6837 if evaluated_args.len() != 1 {
6838 return Err(EvaluatorError::EvaluationError(
6839 "spread() requires exactly 1 argument".to_string(),
6840 ));
6841 }
6842 match &evaluated_args[0] {
6843 JValue::Null => Ok(JValue::Null),
6844 JValue::Lambda { .. } | JValue::Builtin { .. } => Ok(JValue::Undefined),
6845 JValue::Object(obj) => Ok(functions::object::spread(obj)?),
6846 JValue::Array(arr) => {
6847 let mut result = Vec::new();
6849 for item in arr.iter() {
6850 match item {
6851 JValue::Lambda { .. } | JValue::Builtin { .. } => {
6852 continue;
6854 }
6855 JValue::Object(obj) => {
6856 let spread_result = functions::object::spread(obj)?;
6857 if let JValue::Array(spread_items) = spread_result {
6858 result.extend(spread_items.iter().cloned());
6859 } else {
6860 result.push(spread_result);
6861 }
6862 }
6863 other => result.push(other.clone()),
6865 }
6866 }
6867 Ok(JValue::array(result))
6868 }
6869 other => Ok(other.clone()),
6871 }
6872 }
6873 "merge" => {
6874 if evaluated_args.is_empty() {
6875 return Err(EvaluatorError::EvaluationError(
6876 "merge() requires at least 1 argument".to_string(),
6877 ));
6878 }
6879 if evaluated_args.len() == 1 {
6882 match &evaluated_args[0] {
6883 JValue::Array(arr) => Ok(functions::object::merge(arr)?),
6884 JValue::Null => Ok(JValue::Null), JValue::Object(_) => {
6886 Ok(evaluated_args[0].clone())
6888 }
6889 _ => Err(EvaluatorError::TypeError(
6890 "merge() requires objects or an array of objects".to_string(),
6891 )),
6892 }
6893 } else {
6894 Ok(functions::object::merge(&evaluated_args)?)
6895 }
6896 }
6897
6898 "map" => {
6899 if args.len() != 2 {
6900 return Err(EvaluatorError::EvaluationError(
6901 "map() requires exactly 2 arguments".to_string(),
6902 ));
6903 }
6904
6905 let array = self.evaluate_internal(&args[0], data)?;
6907
6908 match array {
6909 JValue::Array(arr) => {
6910 let param_count = self.get_callback_param_count(&args[1]);
6912
6913 if param_count == 1 {
6915 if let AstNode::Lambda {
6916 params,
6917 body,
6918 signature: None,
6919 thunk: false,
6920 } = &args[1]
6921 {
6922 let var_refs: Vec<&str> =
6923 params.iter().map(|s| s.as_str()).collect();
6924 if let Some(compiled) =
6925 try_compile_expr_with_allowed_vars(body, &var_refs)
6926 {
6927 let param_name = params[0].as_str();
6928 let mut result = Vec::with_capacity(arr.len());
6929 let mut vars = HashMap::new();
6930 for item in arr.iter() {
6931 vars.insert(param_name, item);
6932 let mapped = eval_compiled(&compiled, data, Some(&vars))?;
6933 if !mapped.is_undefined() {
6934 result.push(mapped);
6935 }
6936 }
6937 return Ok(JValue::array(result));
6938 }
6939 }
6940 if let AstNode::Variable(var_name) = &args[1] {
6942 if let Some(stored) = self.context.lookup_lambda(var_name) {
6943 if let Some(ref ce) = stored.compiled_body.clone() {
6944 let param_name = stored.params[0].clone();
6945 let captured_data = stored.captured_data.clone();
6946 let captured_env_clone = stored.captured_env.clone();
6947 let ce_clone = ce.clone();
6948 if !captured_env_clone.values().any(|v| {
6949 matches!(
6950 v,
6951 JValue::Lambda { .. } | JValue::Builtin { .. }
6952 )
6953 }) {
6954 let call_data = captured_data.as_ref().unwrap_or(data);
6955 let mut result = Vec::with_capacity(arr.len());
6956 let mut vars: HashMap<&str, &JValue> =
6957 captured_env_clone
6958 .iter()
6959 .map(|(k, v)| (k.as_str(), v))
6960 .collect();
6961 for item in arr.iter() {
6962 vars.insert(param_name.as_str(), item);
6963 let mapped = eval_compiled(
6964 &ce_clone,
6965 call_data,
6966 Some(&vars),
6967 )?;
6968 if !mapped.is_undefined() {
6969 result.push(mapped);
6970 }
6971 }
6972 return Ok(JValue::array(result));
6973 }
6974 }
6975 }
6976 }
6977 }
6978
6979 let arr_value = if param_count >= 3 {
6981 Some(JValue::Array(arr.clone()))
6982 } else {
6983 None
6984 };
6985
6986 let mut result = Vec::with_capacity(arr.len());
6987 for (index, item) in arr.iter().enumerate() {
6988 let call_args = match param_count {
6990 1 => vec![item.clone()],
6991 2 => vec![item.clone(), JValue::Number(index as f64)],
6992 _ => vec![
6993 item.clone(),
6994 JValue::Number(index as f64),
6995 arr_value.as_ref().unwrap().clone(),
6996 ],
6997 };
6998
6999 let mapped = self.apply_function(&args[1], &call_args, data)?;
7000 if !mapped.is_undefined() {
7003 result.push(mapped);
7004 }
7005 }
7006 Ok(JValue::array(result))
7007 }
7008 JValue::Null => Ok(JValue::Null),
7009 _ => Err(EvaluatorError::TypeError(
7010 "map() first argument must be an array".to_string(),
7011 )),
7012 }
7013 }
7014
7015 "filter" => {
7016 if args.len() != 2 {
7017 return Err(EvaluatorError::EvaluationError(
7018 "filter() requires exactly 2 arguments".to_string(),
7019 ));
7020 }
7021
7022 let array = self.evaluate_internal(&args[0], data)?;
7024
7025 if array.is_undefined() {
7027 return Ok(JValue::Undefined);
7028 }
7029
7030 if array.is_null() {
7032 return Ok(JValue::Undefined);
7033 }
7034
7035 let single_holder;
7039 let (items, was_single_value): (&[JValue], bool) = match &array {
7040 JValue::Array(arr) => (arr.as_slice(), false),
7041 _ => {
7042 single_holder = [array];
7043 (&single_holder[..], true)
7044 }
7045 };
7046
7047 let param_count = self.get_callback_param_count(&args[1]);
7049
7050 if param_count == 1 {
7052 if let AstNode::Lambda {
7053 params,
7054 body,
7055 signature: None,
7056 thunk: false,
7057 } = &args[1]
7058 {
7059 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
7060 if let Some(compiled) = try_compile_expr_with_allowed_vars(body, &var_refs)
7061 {
7062 let param_name = params[0].as_str();
7063 let mut result = Vec::with_capacity(items.len() / 2);
7064 let mut vars = HashMap::new();
7065 for item in items.iter() {
7066 vars.insert(param_name, item);
7067 let pred_result = eval_compiled(&compiled, data, Some(&vars))?;
7068 if compiled_is_truthy(&pred_result) {
7069 result.push(item.clone());
7070 }
7071 }
7072 if was_single_value {
7073 if result.len() == 1 {
7074 return Ok(result.remove(0));
7075 } else if result.is_empty() {
7076 return Ok(JValue::Undefined);
7077 }
7078 }
7079 return Ok(JValue::array(result));
7080 }
7081 }
7082 if let AstNode::Variable(var_name) = &args[1] {
7084 if let Some(stored) = self.context.lookup_lambda(var_name) {
7085 if let Some(ref ce) = stored.compiled_body.clone() {
7086 let param_name = stored.params[0].clone();
7087 let captured_data = stored.captured_data.clone();
7088 let captured_env_clone = stored.captured_env.clone();
7089 let ce_clone = ce.clone();
7090 if !captured_env_clone.values().any(|v| {
7091 matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. })
7092 }) {
7093 let call_data = captured_data.as_ref().unwrap_or(data);
7094 let mut result = Vec::with_capacity(items.len() / 2);
7095 let mut vars: HashMap<&str, &JValue> = captured_env_clone
7096 .iter()
7097 .map(|(k, v)| (k.as_str(), v))
7098 .collect();
7099 for item in items.iter() {
7100 vars.insert(param_name.as_str(), item);
7101 let pred_result =
7102 eval_compiled(&ce_clone, call_data, Some(&vars))?;
7103 if compiled_is_truthy(&pred_result) {
7104 result.push(item.clone());
7105 }
7106 }
7107 if was_single_value {
7108 if result.len() == 1 {
7109 return Ok(result.remove(0));
7110 } else if result.is_empty() {
7111 return Ok(JValue::Undefined);
7112 }
7113 }
7114 return Ok(JValue::array(result));
7115 }
7116 }
7117 }
7118 }
7119 }
7120
7121 let arr_value = if param_count >= 3 {
7123 Some(JValue::array(items.to_vec()))
7124 } else {
7125 None
7126 };
7127
7128 let mut result = Vec::with_capacity(items.len() / 2);
7129
7130 for (index, item) in items.iter().enumerate() {
7131 let call_args = match param_count {
7133 1 => vec![item.clone()],
7134 2 => vec![item.clone(), JValue::Number(index as f64)],
7135 _ => vec![
7136 item.clone(),
7137 JValue::Number(index as f64),
7138 arr_value.as_ref().unwrap().clone(),
7139 ],
7140 };
7141
7142 let predicate_result = self.apply_function(&args[1], &call_args, data)?;
7143 if self.is_truthy(&predicate_result) {
7144 result.push(item.clone());
7145 }
7146 }
7147
7148 if was_single_value {
7151 if result.len() == 1 {
7152 return Ok(result.remove(0));
7153 } else if result.is_empty() {
7154 return Ok(JValue::Undefined);
7155 }
7156 }
7157
7158 Ok(JValue::array(result))
7159 }
7160
7161 "reduce" => {
7162 if args.len() < 2 || args.len() > 3 {
7163 return Err(EvaluatorError::EvaluationError(
7164 "reduce() requires 2 or 3 arguments".to_string(),
7165 ));
7166 }
7167
7168 if let AstNode::Lambda { params, .. } = &args[1] {
7170 if params.len() < 2 {
7171 return Err(EvaluatorError::EvaluationError(
7172 "D3050: The second argument of reduce must be a function with at least two arguments".to_string(),
7173 ));
7174 }
7175 } else if let AstNode::Function { name, .. } = &args[1] {
7176 let _ = name; }
7180
7181 let array = self.evaluate_internal(&args[0], data)?;
7183
7184 let single_holder;
7187 let items: &[JValue] = match &array {
7188 JValue::Array(arr) => arr.as_slice(),
7189 JValue::Null => return Ok(JValue::Null),
7190 _ => {
7191 single_holder = [array];
7192 &single_holder[..]
7193 }
7194 };
7195
7196 if items.is_empty() {
7197 return if args.len() == 3 {
7199 self.evaluate_internal(&args[2], data)
7200 } else {
7201 Ok(JValue::Null)
7202 };
7203 }
7204
7205 let mut accumulator = if args.len() == 3 {
7207 self.evaluate_internal(&args[2], data)?
7208 } else {
7209 items[0].clone()
7210 };
7211
7212 let start_idx = if args.len() == 3 { 0 } else { 1 };
7213
7214 let param_count = self.get_callback_param_count(&args[1]);
7216
7217 if param_count == 2 {
7219 if let AstNode::Lambda {
7220 params,
7221 body,
7222 signature: None,
7223 thunk: false,
7224 } = &args[1]
7225 {
7226 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
7227 if let Some(compiled) = try_compile_expr_with_allowed_vars(body, &var_refs)
7228 {
7229 let acc_name = params[0].as_str();
7230 let item_name = params[1].as_str();
7231 for item in items[start_idx..].iter() {
7232 let vars: HashMap<&str, &JValue> =
7233 HashMap::from([(acc_name, &accumulator), (item_name, item)]);
7234 accumulator = eval_compiled(&compiled, data, Some(&vars))?;
7235 }
7236 return Ok(accumulator);
7237 }
7238 }
7239 if let AstNode::Variable(var_name) = &args[1] {
7241 if let Some(stored) = self.context.lookup_lambda(var_name) {
7242 if stored.params.len() == 2 {
7243 if let Some(ref ce) = stored.compiled_body.clone() {
7244 let acc_param = stored.params[0].clone();
7245 let item_param = stored.params[1].clone();
7246 let captured_data = stored.captured_data.clone();
7247 let captured_env_clone = stored.captured_env.clone();
7248 let ce_clone = ce.clone();
7249 if !captured_env_clone.values().any(|v| {
7250 matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. })
7251 }) {
7252 let call_data = captured_data.as_ref().unwrap_or(data);
7253 for item in items[start_idx..].iter() {
7254 let mut vars: HashMap<&str, &JValue> =
7255 captured_env_clone
7256 .iter()
7257 .map(|(k, v)| (k.as_str(), v))
7258 .collect();
7259 vars.insert(acc_param.as_str(), &accumulator);
7260 vars.insert(item_param.as_str(), item);
7261 let new_acc =
7264 eval_compiled(&ce_clone, call_data, Some(&vars))?;
7265 drop(vars);
7266 accumulator = new_acc;
7267 }
7268 return Ok(accumulator);
7269 }
7270 }
7271 }
7272 }
7273 }
7274 }
7275
7276 let arr_value = if param_count >= 4 {
7278 Some(JValue::array(items.to_vec()))
7279 } else {
7280 None
7281 };
7282
7283 for (idx, item) in items[start_idx..].iter().enumerate() {
7285 let actual_idx = start_idx + idx;
7288
7289 let call_args = match param_count {
7291 2 => vec![accumulator.clone(), item.clone()],
7292 3 => vec![
7293 accumulator.clone(),
7294 item.clone(),
7295 JValue::Number(actual_idx as f64),
7296 ],
7297 _ => vec![
7298 accumulator.clone(),
7299 item.clone(),
7300 JValue::Number(actual_idx as f64),
7301 arr_value.as_ref().unwrap().clone(),
7302 ],
7303 };
7304
7305 accumulator = self.apply_function(&args[1], &call_args, data)?;
7306 }
7307
7308 Ok(accumulator)
7309 }
7310
7311 "single" => {
7312 if args.is_empty() || args.len() > 2 {
7313 return Err(EvaluatorError::EvaluationError(
7314 "single() requires 1 or 2 arguments".to_string(),
7315 ));
7316 }
7317
7318 let array = self.evaluate_internal(&args[0], data)?;
7320
7321 let arr = match array {
7323 JValue::Array(arr) => arr.to_vec(),
7324 JValue::Null => return Ok(JValue::Null),
7325 other => vec![other],
7326 };
7327
7328 if args.len() == 1 {
7329 match arr.len() {
7331 0 => Err(EvaluatorError::EvaluationError(
7332 "single() argument is empty".to_string(),
7333 )),
7334 1 => Ok(arr.into_iter().next().unwrap()),
7335 count => Err(EvaluatorError::EvaluationError(format!(
7336 "single() argument has {} values (expected exactly 1)",
7337 count
7338 ))),
7339 }
7340 } else {
7341 let arr_value = JValue::array(arr.clone());
7343 let mut matches = Vec::new();
7344 for (index, item) in arr.into_iter().enumerate() {
7345 let predicate_result = self.apply_function(
7347 &args[1],
7348 &[
7349 item.clone(),
7350 JValue::Number(index as f64),
7351 arr_value.clone(),
7352 ],
7353 data,
7354 )?;
7355 if self.is_truthy(&predicate_result) {
7356 matches.push(item);
7357 }
7358 }
7359
7360 match matches.len() {
7361 0 => Err(EvaluatorError::EvaluationError(
7362 "single() predicate matches no values".to_string(),
7363 )),
7364 1 => Ok(matches.into_iter().next().unwrap()),
7365 count => Err(EvaluatorError::EvaluationError(format!(
7366 "single() predicate matches {} values (expected exactly 1)",
7367 count
7368 ))),
7369 }
7370 }
7371 }
7372
7373 "each" => {
7374 if args.is_empty() || args.len() > 2 {
7377 return Err(EvaluatorError::EvaluationError(
7378 "each() requires 1 or 2 arguments".to_string(),
7379 ));
7380 }
7381
7382 let (obj_value, func_arg) = if args.len() == 1 {
7384 (data.clone(), &args[0])
7386 } else {
7387 (self.evaluate_internal(&args[0], data)?, &args[1])
7389 };
7390
7391 let param_count = self.get_callback_param_count(func_arg);
7393
7394 match obj_value {
7395 JValue::Object(obj) => {
7396 let mut result = Vec::new();
7397 for (key, value) in obj.iter() {
7398 let call_args = match param_count {
7401 1 => vec![value.clone()],
7402 _ => vec![value.clone(), JValue::string(key.clone())],
7403 };
7404
7405 let fn_result = self.apply_function(func_arg, &call_args, data)?;
7406 if !fn_result.is_null() && !fn_result.is_undefined() {
7408 result.push(fn_result);
7409 }
7410 }
7411 Ok(JValue::array(result))
7412 }
7413 JValue::Null => Ok(JValue::Null),
7414 _ => Err(EvaluatorError::TypeError(
7415 "each() first argument must be an object".to_string(),
7416 )),
7417 }
7418 }
7419
7420 "not" => {
7421 if evaluated_args.len() != 1 {
7422 return Err(EvaluatorError::EvaluationError(
7423 "not() requires exactly 1 argument".to_string(),
7424 ));
7425 }
7426 Ok(JValue::Bool(!self.is_truthy(&evaluated_args[0])))
7429 }
7430 "boolean" => {
7431 if evaluated_args.len() != 1 {
7432 return Err(EvaluatorError::EvaluationError(
7433 "boolean() requires exactly 1 argument".to_string(),
7434 ));
7435 }
7436 Ok(functions::boolean::boolean(&evaluated_args[0])?)
7437 }
7438 "type" => {
7439 if evaluated_args.len() != 1 {
7440 return Err(EvaluatorError::EvaluationError(
7441 "type() requires exactly 1 argument".to_string(),
7442 ));
7443 }
7444 match &evaluated_args[0] {
7448 JValue::Null => Ok(JValue::string("null")),
7449 JValue::Bool(_) => Ok(JValue::string("boolean")),
7450 JValue::Number(_) => Ok(JValue::string("number")),
7451 JValue::String(_) => Ok(JValue::string("string")),
7452 JValue::Array(_) => Ok(JValue::string("array")),
7453 JValue::Object(_) => Ok(JValue::string("object")),
7454 JValue::Undefined => Ok(JValue::Undefined),
7455 JValue::Lambda { .. } | JValue::Builtin { .. } => {
7456 Ok(JValue::string("function"))
7457 }
7458 JValue::Regex { .. } => Ok(JValue::string("regex")),
7459 }
7460 }
7461
7462 "base64encode" => {
7463 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7464 return Ok(JValue::Null);
7465 }
7466 if evaluated_args.len() != 1 {
7467 return Err(EvaluatorError::EvaluationError(
7468 "base64encode() requires exactly 1 argument".to_string(),
7469 ));
7470 }
7471 match &evaluated_args[0] {
7472 JValue::String(s) => Ok(functions::encoding::base64encode(s)?),
7473 _ => Err(EvaluatorError::TypeError(
7474 "base64encode() requires a string argument".to_string(),
7475 )),
7476 }
7477 }
7478 "base64decode" => {
7479 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7480 return Ok(JValue::Null);
7481 }
7482 if evaluated_args.len() != 1 {
7483 return Err(EvaluatorError::EvaluationError(
7484 "base64decode() requires exactly 1 argument".to_string(),
7485 ));
7486 }
7487 match &evaluated_args[0] {
7488 JValue::String(s) => Ok(functions::encoding::base64decode(s)?),
7489 _ => Err(EvaluatorError::TypeError(
7490 "base64decode() requires a string argument".to_string(),
7491 )),
7492 }
7493 }
7494 "encodeUrlComponent" => {
7495 if evaluated_args.len() != 1 {
7496 return Err(EvaluatorError::EvaluationError(
7497 "encodeUrlComponent() requires exactly 1 argument".to_string(),
7498 ));
7499 }
7500 if evaluated_args[0].is_null() {
7501 return Ok(JValue::Null);
7502 }
7503 match &evaluated_args[0] {
7504 JValue::String(s) => Ok(functions::encoding::encode_url_component(s)?),
7505 _ => Err(EvaluatorError::TypeError(
7506 "encodeUrlComponent() requires a string argument".to_string(),
7507 )),
7508 }
7509 }
7510 "decodeUrlComponent" => {
7511 if evaluated_args.len() != 1 {
7512 return Err(EvaluatorError::EvaluationError(
7513 "decodeUrlComponent() requires exactly 1 argument".to_string(),
7514 ));
7515 }
7516 if evaluated_args[0].is_null() {
7517 return Ok(JValue::Null);
7518 }
7519 match &evaluated_args[0] {
7520 JValue::String(s) => Ok(functions::encoding::decode_url_component(s)?),
7521 _ => Err(EvaluatorError::TypeError(
7522 "decodeUrlComponent() requires a string argument".to_string(),
7523 )),
7524 }
7525 }
7526 "encodeUrl" => {
7527 if evaluated_args.len() != 1 {
7528 return Err(EvaluatorError::EvaluationError(
7529 "encodeUrl() requires exactly 1 argument".to_string(),
7530 ));
7531 }
7532 if evaluated_args[0].is_null() {
7533 return Ok(JValue::Null);
7534 }
7535 match &evaluated_args[0] {
7536 JValue::String(s) => Ok(functions::encoding::encode_url(s)?),
7537 _ => Err(EvaluatorError::TypeError(
7538 "encodeUrl() requires a string argument".to_string(),
7539 )),
7540 }
7541 }
7542 "decodeUrl" => {
7543 if evaluated_args.len() != 1 {
7544 return Err(EvaluatorError::EvaluationError(
7545 "decodeUrl() requires exactly 1 argument".to_string(),
7546 ));
7547 }
7548 if evaluated_args[0].is_null() {
7549 return Ok(JValue::Null);
7550 }
7551 match &evaluated_args[0] {
7552 JValue::String(s) => Ok(functions::encoding::decode_url(s)?),
7553 _ => Err(EvaluatorError::TypeError(
7554 "decodeUrl() requires a string argument".to_string(),
7555 )),
7556 }
7557 }
7558
7559 "error" => {
7560 if evaluated_args.is_empty() {
7562 return Err(EvaluatorError::EvaluationError(
7564 "D3137: $error() function evaluated".to_string(),
7565 ));
7566 }
7567
7568 match &evaluated_args[0] {
7569 JValue::String(s) => {
7570 Err(EvaluatorError::EvaluationError(format!("D3137: {}", s)))
7571 }
7572 _ => Err(EvaluatorError::TypeError(
7573 "T0410: Argument 1 of function error does not match function signature"
7574 .to_string(),
7575 )),
7576 }
7577 }
7578 "assert" => {
7579 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7581 return Err(EvaluatorError::EvaluationError(
7582 "assert() requires 1 or 2 arguments".to_string(),
7583 ));
7584 }
7585
7586 let condition = match &evaluated_args[0] {
7588 JValue::Bool(b) => *b,
7589 _ => {
7590 return Err(EvaluatorError::TypeError(
7591 "T0410: Argument 1 of function $assert does not match function signature".to_string(),
7592 ));
7593 }
7594 };
7595
7596 if !condition {
7597 let message = if evaluated_args.len() == 2 {
7598 match &evaluated_args[1] {
7599 JValue::String(s) => s.clone(),
7600 _ => Rc::from("$assert() statement failed"),
7601 }
7602 } else {
7603 Rc::from("$assert() statement failed")
7604 };
7605 return Err(EvaluatorError::EvaluationError(format!(
7606 "D3141: {}",
7607 message
7608 )));
7609 }
7610
7611 Ok(JValue::Null)
7612 }
7613
7614 "eval" => {
7615 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7617 return Err(EvaluatorError::EvaluationError(
7618 "T0410: Argument 1 of function $eval must be a string".to_string(),
7619 ));
7620 }
7621
7622 if evaluated_args[0].is_null() {
7624 return Ok(JValue::Null);
7625 }
7626
7627 let expr_str = match &evaluated_args[0] {
7629 JValue::String(s) => &**s,
7630 _ => {
7631 return Err(EvaluatorError::EvaluationError(
7632 "T0410: Argument 1 of function $eval must be a string".to_string(),
7633 ));
7634 }
7635 };
7636
7637 let parsed_ast = match parser::parse(expr_str) {
7639 Ok(ast) => ast,
7640 Err(e) => {
7641 return Err(EvaluatorError::EvaluationError(format!(
7643 "D3120: The expression passed to $eval cannot be parsed: {}",
7644 e
7645 )));
7646 }
7647 };
7648
7649 let eval_context = if evaluated_args.len() == 2 {
7651 &evaluated_args[1]
7652 } else {
7653 data
7654 };
7655
7656 match self.evaluate_internal(&parsed_ast, eval_context) {
7658 Ok(result) => Ok(result),
7659 Err(e) => {
7660 let err_msg = e.to_string();
7662 if err_msg.starts_with("D3121") || err_msg.contains("Unknown function") {
7663 Err(EvaluatorError::EvaluationError(format!(
7664 "D3121: {}",
7665 err_msg
7666 )))
7667 } else {
7668 Err(e)
7669 }
7670 }
7671 }
7672 }
7673
7674 "now" => {
7675 if !evaluated_args.is_empty() {
7676 return Err(EvaluatorError::EvaluationError(
7677 "now() takes no arguments".to_string(),
7678 ));
7679 }
7680 Ok(crate::datetime::now())
7681 }
7682
7683 "millis" => {
7684 if !evaluated_args.is_empty() {
7685 return Err(EvaluatorError::EvaluationError(
7686 "millis() takes no arguments".to_string(),
7687 ));
7688 }
7689 Ok(crate::datetime::millis())
7690 }
7691
7692 "toMillis" => {
7693 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7694 return Err(EvaluatorError::EvaluationError(
7695 "toMillis() requires 1 or 2 arguments".to_string(),
7696 ));
7697 }
7698
7699 match &evaluated_args[0] {
7700 JValue::String(s) => {
7701 if evaluated_args.len() == 2 {
7703 match &evaluated_args[1] {
7704 JValue::String(picture) => {
7705 Ok(crate::datetime::to_millis_with_picture(s, picture)?)
7707 }
7708 JValue::Null => Ok(JValue::Null),
7709 _ => Err(EvaluatorError::TypeError(
7710 "toMillis() second argument must be a string".to_string(),
7711 )),
7712 }
7713 } else {
7714 Ok(crate::datetime::to_millis(s)?)
7716 }
7717 }
7718 JValue::Null => Ok(JValue::Null),
7719 _ => Err(EvaluatorError::TypeError(
7720 "toMillis() requires a string argument".to_string(),
7721 )),
7722 }
7723 }
7724
7725 "fromMillis" => {
7726 if evaluated_args.len() != 1 {
7727 return Err(EvaluatorError::EvaluationError(
7728 "fromMillis() requires exactly 1 argument".to_string(),
7729 ));
7730 }
7731
7732 match &evaluated_args[0] {
7733 JValue::Number(n) => {
7734 let millis = (if n.fract() == 0.0 {
7735 Ok(*n as i64)
7736 } else {
7737 Err(())
7738 })
7739 .map_err(|_| {
7740 EvaluatorError::TypeError(
7741 "fromMillis() requires an integer".to_string(),
7742 )
7743 })?;
7744 Ok(crate::datetime::from_millis(millis)?)
7745 }
7746 JValue::Null => Ok(JValue::Null),
7747 _ => Err(EvaluatorError::TypeError(
7748 "fromMillis() requires a number argument".to_string(),
7749 )),
7750 }
7751 }
7752
7753 _ => Err(EvaluatorError::ReferenceError(format!(
7754 "Unknown function: {}",
7755 name
7756 ))),
7757 }
7758 }
7759
7760 fn apply_function(
7766 &mut self,
7767 func_node: &AstNode,
7768 values: &[JValue],
7769 data: &JValue,
7770 ) -> Result<JValue, EvaluatorError> {
7771 match func_node {
7772 AstNode::Lambda {
7773 params,
7774 body,
7775 signature,
7776 thunk,
7777 } => {
7778 self.invoke_lambda(params, body, signature.as_ref(), values, data, *thunk)
7780 }
7781 AstNode::Function {
7782 name,
7783 args,
7784 is_builtin,
7785 } => {
7786 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
7788
7789 if has_placeholder {
7790 let partial_lambda =
7792 self.create_partial_application(name, args, *is_builtin, data)?;
7793
7794 if let Some(stored) = self.lookup_lambda_from_value(&partial_lambda) {
7796 return self.invoke_stored_lambda(&stored, values, data);
7797 }
7798 Err(EvaluatorError::EvaluationError(
7799 "Failed to apply partial application".to_string(),
7800 ))
7801 } else {
7802 let result = self.evaluate_internal(func_node, data)?;
7805
7806 if let Some(stored) = self.lookup_lambda_from_value(&result) {
7808 return self.invoke_stored_lambda(&stored, values, data);
7809 }
7810
7811 Ok(result)
7813 }
7814 }
7815 AstNode::Variable(var_name) => {
7816 if let Some(stored_lambda) = self.context.lookup_lambda(var_name).cloned() {
7818 self.invoke_stored_lambda(&stored_lambda, values, data)
7819 } else if let Some(value) = self.context.lookup(var_name).cloned() {
7820 if let Some(stored) = self.lookup_lambda_from_value(&value) {
7823 return self.invoke_stored_lambda(&stored, values, data);
7824 }
7825 if values.is_empty() {
7827 self.evaluate_internal(func_node, data)
7828 } else {
7829 self.evaluate_internal(func_node, &values[0])
7830 }
7831 } else if self.is_builtin_function(var_name) {
7832 self.call_builtin_with_values(var_name, values)
7835 } else {
7836 if values.is_empty() {
7838 self.evaluate_internal(func_node, data)
7839 } else {
7840 self.evaluate_internal(func_node, &values[0])
7841 }
7842 }
7843 }
7844 _ => {
7845 if values.is_empty() {
7847 self.evaluate_internal(func_node, data)
7848 } else {
7849 self.evaluate_internal(func_node, &values[0])
7850 }
7851 }
7852 }
7853 }
7854
7855 fn execute_transform(
7857 &mut self,
7858 location: &AstNode,
7859 update: &AstNode,
7860 delete: Option<&AstNode>,
7861 _original_data: &JValue,
7862 ) -> Result<JValue, EvaluatorError> {
7863 let input = self
7865 .context
7866 .lookup("$")
7867 .ok_or_else(|| {
7868 EvaluatorError::EvaluationError("Transform requires $ binding".to_string())
7869 })?
7870 .clone();
7871
7872 let located_objects = self.evaluate_internal(location, &input)?;
7874
7875 let targets: Vec<JValue> = match located_objects {
7877 JValue::Array(arr) => arr.to_vec(),
7878 JValue::Object(_) => vec![located_objects],
7879 JValue::Null => Vec::new(),
7880 other => vec![other],
7881 };
7882
7883 let delete_fields: Vec<String> = if let Some(delete_node) = delete {
7889 let delete_val = self.evaluate_internal(delete_node, &input)?;
7890 match delete_val {
7891 JValue::Array(arr) => arr
7892 .iter()
7893 .filter_map(|v| match v {
7894 JValue::String(s) => Some(s.to_string()),
7895 _ => None,
7896 })
7897 .collect(),
7898 JValue::String(s) => vec![s.to_string()],
7899 JValue::Null => Vec::new(), _ => {
7901 return Err(EvaluatorError::EvaluationError(
7903 "T2012: The third argument of the transform operator must be an array of strings".to_string()
7904 ));
7905 }
7906 }
7907 } else {
7908 Vec::new()
7909 };
7910
7911 fn apply_transform_deep(
7913 evaluator: &mut Evaluator,
7914 value: &JValue,
7915 targets: &[JValue],
7916 update: &AstNode,
7917 delete_fields: &[String],
7918 ) -> Result<JValue, EvaluatorError> {
7919 if targets.iter().any(|t| t == value) {
7922 if let JValue::Object(map_rc) = value.clone() {
7924 let mut map = (*map_rc).clone();
7925 let update_val = evaluator.evaluate_internal(update, value)?;
7926 match update_val {
7928 JValue::Object(update_map) => {
7929 for (key, val) in update_map.iter() {
7930 map.insert(key.clone(), val.clone());
7931 }
7932 }
7933 JValue::Null => {
7934 }
7936 _ => {
7937 return Err(EvaluatorError::EvaluationError(
7938 "T2011: The second argument of the transform operator must evaluate to an object".to_string()
7939 ));
7940 }
7941 }
7942 for field in delete_fields {
7943 map.shift_remove(field);
7944 }
7945 return Ok(JValue::object(map));
7946 }
7947 return Ok(value.clone());
7948 }
7949
7950 match value {
7952 JValue::Object(map) => {
7953 let mut new_map = IndexMap::new();
7954 for (k, v) in map.iter() {
7955 new_map.insert(
7956 k.clone(),
7957 apply_transform_deep(evaluator, v, targets, update, delete_fields)?,
7958 );
7959 }
7960 Ok(JValue::object(new_map))
7961 }
7962 JValue::Array(arr) => {
7963 let mut new_arr = Vec::new();
7964 for item in arr.iter() {
7965 new_arr.push(apply_transform_deep(
7966 evaluator,
7967 item,
7968 targets,
7969 update,
7970 delete_fields,
7971 )?);
7972 }
7973 Ok(JValue::array(new_arr))
7974 }
7975 _ => Ok(value.clone()),
7976 }
7977 }
7978
7979 apply_transform_deep(self, &input, &targets, update, &delete_fields)
7981 }
7982
7983 fn invoke_lambda(
7985 &mut self,
7986 params: &[String],
7987 body: &AstNode,
7988 signature: Option<&String>,
7989 values: &[JValue],
7990 data: &JValue,
7991 thunk: bool,
7992 ) -> Result<JValue, EvaluatorError> {
7993 self.invoke_lambda_with_env(params, body, signature, values, data, None, None, thunk)
7994 }
7995
7996 fn invoke_lambda_with_env(
7998 &mut self,
7999 params: &[String],
8000 body: &AstNode,
8001 signature: Option<&String>,
8002 values: &[JValue],
8003 data: &JValue,
8004 captured_env: Option<&HashMap<String, JValue>>,
8005 captured_data: Option<&JValue>,
8006 thunk: bool,
8007 ) -> Result<JValue, EvaluatorError> {
8008 if thunk {
8010 let stored = StoredLambda {
8011 params: params.to_vec(),
8012 body: body.clone(),
8013 compiled_body: None, signature: signature.cloned(),
8015 captured_env: captured_env.cloned().unwrap_or_default(),
8016 captured_data: captured_data.cloned(),
8017 thunk,
8018 };
8019 return self.invoke_lambda_with_tco(&stored, values, data);
8020 }
8021
8022 self.context.push_scope();
8025
8026 if let Some(env) = captured_env {
8028 for (name, value) in env {
8029 self.context.bind(name.clone(), value.clone());
8030 }
8031 }
8032
8033 if let Some(sig_str) = signature {
8034 let coerced_values = match crate::signature::Signature::parse(sig_str) {
8036 Ok(sig) => match sig.validate_and_coerce(values) {
8037 Ok(coerced) => coerced,
8038 Err(e) => {
8039 self.context.pop_scope();
8040 match e {
8041 crate::signature::SignatureError::UndefinedArgument => {
8042 return Ok(JValue::Null);
8043 }
8044 crate::signature::SignatureError::ArgumentTypeMismatch {
8045 index,
8046 expected,
8047 } => {
8048 return Err(EvaluatorError::TypeError(
8049 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
8050 ));
8051 }
8052 crate::signature::SignatureError::ArrayTypeMismatch {
8053 index,
8054 expected,
8055 } => {
8056 return Err(EvaluatorError::TypeError(format!(
8057 "T0412: Argument {} of function must be an array of {}",
8058 index, expected
8059 )));
8060 }
8061 _ => {
8062 return Err(EvaluatorError::TypeError(format!(
8063 "Signature validation failed: {}",
8064 e
8065 )));
8066 }
8067 }
8068 }
8069 },
8070 Err(e) => {
8071 self.context.pop_scope();
8072 return Err(EvaluatorError::EvaluationError(format!(
8073 "Invalid signature: {}",
8074 e
8075 )));
8076 }
8077 };
8078 for (i, param) in params.iter().enumerate() {
8080 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Undefined);
8081 self.context.bind(param.clone(), value);
8082 }
8083 } else {
8084 for (i, param) in params.iter().enumerate() {
8086 let value = values.get(i).cloned().unwrap_or(JValue::Undefined);
8087 self.context.bind(param.clone(), value);
8088 }
8089 }
8090
8091 if let AstNode::String(body_str) = body {
8093 if body_str.starts_with("__partial_call:") {
8094 let parts: Vec<&str> = body_str.split(':').collect();
8096 if parts.len() >= 4 {
8097 let func_name = parts[1];
8098 let is_builtin = parts[2] == "true";
8099 let total_args: usize = parts[3].parse().unwrap_or(0);
8100
8101 let placeholder_positions: Vec<usize> = if let Some(env) = captured_env {
8103 if let Some(JValue::Array(positions)) = env.get("__placeholder_positions") {
8104 positions
8105 .iter()
8106 .filter_map(|v| v.as_f64().map(|n| n as usize))
8107 .collect()
8108 } else {
8109 vec![]
8110 }
8111 } else {
8112 vec![]
8113 };
8114
8115 let mut full_args: Vec<JValue> = vec![JValue::Null; total_args];
8117
8118 if let Some(env) = captured_env {
8120 for (key, value) in env {
8121 if key.starts_with("__bound_arg_") {
8122 if let Ok(pos) = key[12..].parse::<usize>() {
8123 if pos < total_args {
8124 full_args[pos] = value.clone();
8125 }
8126 }
8127 }
8128 }
8129 }
8130
8131 for (i, &pos) in placeholder_positions.iter().enumerate() {
8133 if pos < total_args {
8134 let value = values.get(i).cloned().unwrap_or(JValue::Null);
8135 full_args[pos] = value;
8136 }
8137 }
8138
8139 self.context.pop_scope();
8141 self.context.push_scope();
8142
8143 let mut temp_args: Vec<AstNode> = Vec::new();
8145 for (i, value) in full_args.iter().enumerate() {
8146 let temp_name = format!("__temp_arg_{}", i);
8147 self.context.bind(temp_name.clone(), value.clone());
8148 temp_args.push(AstNode::Variable(temp_name));
8149 }
8150
8151 let result =
8153 self.evaluate_function_call(func_name, &temp_args, is_builtin, data);
8154
8155 self.context.pop_scope();
8157
8158 return result;
8159 }
8160 }
8161 }
8162
8163 let body_data = captured_data.unwrap_or(data);
8166 let result = self.evaluate_internal(body, body_data)?;
8167
8168 let is_scalar = matches!(
8171 &result,
8172 JValue::Number(_)
8173 | JValue::Bool(_)
8174 | JValue::String(_)
8175 | JValue::Null
8176 | JValue::Undefined
8177 );
8178 if is_scalar {
8179 self.context.pop_scope();
8180 } else {
8181 let lambdas_to_keep = self.extract_lambda_ids(&result);
8182 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
8183 }
8184
8185 Ok(result)
8186 }
8187
8188 fn invoke_lambda_with_tco(
8192 &mut self,
8193 stored_lambda: &StoredLambda,
8194 initial_args: &[JValue],
8195 data: &JValue,
8196 ) -> Result<JValue, EvaluatorError> {
8197 let mut current_lambda = stored_lambda.clone();
8198 let mut current_args = initial_args.to_vec();
8199 let mut current_data = data.clone();
8200
8201 const MAX_TCO_ITERATIONS: usize = 100_000;
8204 let mut iterations = 0;
8205
8206 self.context.push_scope();
8210
8211 let result = loop {
8213 iterations += 1;
8214 if iterations > MAX_TCO_ITERATIONS {
8215 self.context.pop_scope();
8216 return Err(EvaluatorError::EvaluationError(
8217 "U1001: Stack overflow - maximum recursion depth (500) exceeded".to_string(),
8218 ));
8219 }
8220
8221 let result =
8223 self.invoke_lambda_body_for_tco(¤t_lambda, ¤t_args, ¤t_data)?;
8224
8225 match result {
8226 LambdaResult::JValue(v) => break v,
8227 LambdaResult::TailCall { lambda, args, data } => {
8228 current_lambda = *lambda;
8230 current_args = args;
8231 current_data = data;
8232 }
8233 }
8234 };
8235
8236 let lambdas_to_keep = self.extract_lambda_ids(&result);
8238 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
8239
8240 Ok(result)
8241 }
8242
8243 fn invoke_lambda_body_for_tco(
8248 &mut self,
8249 lambda: &StoredLambda,
8250 values: &[JValue],
8251 data: &JValue,
8252 ) -> Result<LambdaResult, EvaluatorError> {
8253 let coerced_values = if let Some(sig_str) = &lambda.signature {
8255 match crate::signature::Signature::parse(sig_str) {
8256 Ok(sig) => match sig.validate_and_coerce(values) {
8257 Ok(coerced) => coerced,
8258 Err(e) => match e {
8259 crate::signature::SignatureError::UndefinedArgument => {
8260 return Ok(LambdaResult::JValue(JValue::Null));
8261 }
8262 crate::signature::SignatureError::ArgumentTypeMismatch {
8263 index,
8264 expected,
8265 } => {
8266 return Err(EvaluatorError::TypeError(
8267 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
8268 ));
8269 }
8270 crate::signature::SignatureError::ArrayTypeMismatch { index, expected } => {
8271 return Err(EvaluatorError::TypeError(format!(
8272 "T0412: Argument {} of function must be an array of {}",
8273 index, expected
8274 )));
8275 }
8276 _ => {
8277 return Err(EvaluatorError::TypeError(format!(
8278 "Signature validation failed: {}",
8279 e
8280 )));
8281 }
8282 },
8283 },
8284 Err(e) => {
8285 return Err(EvaluatorError::EvaluationError(format!(
8286 "Invalid signature: {}",
8287 e
8288 )));
8289 }
8290 }
8291 } else {
8292 values.to_vec()
8293 };
8294
8295 for (name, value) in &lambda.captured_env {
8298 self.context.bind(name.clone(), value.clone());
8299 }
8300
8301 for (i, param) in lambda.params.iter().enumerate() {
8303 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Null);
8304 self.context.bind(param.clone(), value);
8305 }
8306
8307 let body_data = lambda.captured_data.as_ref().unwrap_or(data);
8309 self.evaluate_for_tco(&lambda.body, body_data)
8310 }
8311
8312 fn evaluate_for_tco(
8315 &mut self,
8316 node: &AstNode,
8317 data: &JValue,
8318 ) -> Result<LambdaResult, EvaluatorError> {
8319 match node {
8320 AstNode::Conditional {
8322 condition,
8323 then_branch,
8324 else_branch,
8325 } => {
8326 let cond_value = self.evaluate_internal(condition, data)?;
8327 let is_truthy = self.is_truthy(&cond_value);
8328
8329 if is_truthy {
8330 self.evaluate_for_tco(then_branch, data)
8331 } else if let Some(else_expr) = else_branch {
8332 self.evaluate_for_tco(else_expr, data)
8333 } else {
8334 Ok(LambdaResult::JValue(JValue::Null))
8335 }
8336 }
8337
8338 AstNode::Block(exprs) => {
8340 if exprs.is_empty() {
8341 return Ok(LambdaResult::JValue(JValue::Null));
8342 }
8343
8344 let mut result = JValue::Null;
8346 for (i, expr) in exprs.iter().enumerate() {
8347 if i == exprs.len() - 1 {
8348 return self.evaluate_for_tco(expr, data);
8350 } else {
8351 result = self.evaluate_internal(expr, data)?;
8352 }
8353 }
8354 Ok(LambdaResult::JValue(result))
8355 }
8356
8357 AstNode::Binary {
8359 op: BinaryOp::ColonEqual,
8360 lhs,
8361 rhs,
8362 } => {
8363 let var_name = match lhs.as_ref() {
8365 AstNode::Variable(name) => name.clone(),
8366 _ => {
8367 let result = self.evaluate_internal(node, data)?;
8369 return Ok(LambdaResult::JValue(result));
8370 }
8371 };
8372
8373 if let AstNode::Lambda {
8375 params,
8376 body,
8377 signature,
8378 thunk,
8379 } = rhs.as_ref()
8380 {
8381 let captured_env = self.capture_environment_for(body, params);
8382 let compiled_body = if !thunk {
8383 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
8384 try_compile_expr_with_allowed_vars(body, &var_refs)
8385 } else {
8386 None
8387 };
8388 let stored_lambda = StoredLambda {
8389 params: params.clone(),
8390 body: (**body).clone(),
8391 compiled_body,
8392 signature: signature.clone(),
8393 captured_env,
8394 captured_data: Some(data.clone()),
8395 thunk: *thunk,
8396 };
8397 self.context.bind_lambda(var_name, stored_lambda);
8398 let lambda_repr =
8399 JValue::lambda("anon", params.clone(), None::<String>, None::<String>);
8400 return Ok(LambdaResult::JValue(lambda_repr));
8401 }
8402
8403 let value = self.evaluate_internal(rhs, data)?;
8405 self.context.bind(var_name, value.clone());
8406 Ok(LambdaResult::JValue(value))
8407 }
8408
8409 AstNode::Function { name, args, .. } => {
8411 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
8413 if stored_lambda.thunk {
8414 let mut evaluated_args = Vec::with_capacity(args.len());
8415 for arg in args {
8416 evaluated_args.push(self.evaluate_internal(arg, data)?);
8417 }
8418 return Ok(LambdaResult::TailCall {
8419 lambda: Box::new(stored_lambda),
8420 args: evaluated_args,
8421 data: data.clone(),
8422 });
8423 }
8424 }
8425 let result = self.evaluate_internal(node, data)?;
8427 Ok(LambdaResult::JValue(result))
8428 }
8429
8430 AstNode::Call { procedure, args } => {
8432 let callable = self.evaluate_internal(procedure, data)?;
8434
8435 if let JValue::Lambda { lambda_id, .. } = &callable {
8437 if let Some(stored_lambda) = self.context.lookup_lambda(lambda_id).cloned() {
8438 if stored_lambda.thunk {
8439 let mut evaluated_args = Vec::with_capacity(args.len());
8440 for arg in args {
8441 evaluated_args.push(self.evaluate_internal(arg, data)?);
8442 }
8443 return Ok(LambdaResult::TailCall {
8444 lambda: Box::new(stored_lambda),
8445 args: evaluated_args,
8446 data: data.clone(),
8447 });
8448 }
8449 }
8450 }
8451 let result = self.evaluate_internal(node, data)?;
8453 Ok(LambdaResult::JValue(result))
8454 }
8455
8456 AstNode::Variable(_) => {
8459 let result = self.evaluate_internal(node, data)?;
8460 Ok(LambdaResult::JValue(result))
8461 }
8462
8463 _ => {
8465 let result = self.evaluate_internal(node, data)?;
8466 Ok(LambdaResult::JValue(result))
8467 }
8468 }
8469 }
8470
8471 fn match_with_custom_matcher(
8478 &mut self,
8479 str_value: &str,
8480 matcher_node: &AstNode,
8481 limit: Option<usize>,
8482 data: &JValue,
8483 ) -> Result<JValue, EvaluatorError> {
8484 let mut results = Vec::new();
8485 let mut count = 0;
8486
8487 let str_val = JValue::string(str_value.to_string());
8489 let mut current_match = self.apply_function(matcher_node, &[str_val], data)?;
8490
8491 while !current_match.is_undefined() && !current_match.is_null() {
8493 if let Some(lim) = limit {
8495 if count >= lim {
8496 break;
8497 }
8498 }
8499
8500 if let JValue::Object(ref match_obj) = current_match {
8502 let has_match = match_obj.contains_key("match");
8504 let has_start = match_obj.contains_key("start");
8505 let has_end = match_obj.contains_key("end");
8506 let has_groups = match_obj.contains_key("groups");
8507 let has_next = match_obj.contains_key("next");
8508
8509 if !has_match && !has_start && !has_end && !has_groups && !has_next {
8510 return Err(EvaluatorError::EvaluationError(
8512 "T1010: The matcher function did not return the correct object structure"
8513 .to_string(),
8514 ));
8515 }
8516
8517 let mut result_obj = IndexMap::new();
8519
8520 if let Some(match_val) = match_obj.get("match") {
8521 result_obj.insert("match".to_string(), match_val.clone());
8522 }
8523
8524 if let Some(start_val) = match_obj.get("start") {
8525 result_obj.insert("index".to_string(), start_val.clone());
8526 }
8527
8528 if let Some(groups_val) = match_obj.get("groups") {
8529 result_obj.insert("groups".to_string(), groups_val.clone());
8530 }
8531
8532 results.push(JValue::object(result_obj));
8533 count += 1;
8534
8535 if let Some(next_func) = match_obj.get("next") {
8537 if let Some(stored) = self.lookup_lambda_from_value(next_func) {
8538 current_match = self.invoke_stored_lambda(&stored, &[], data)?;
8539 continue;
8540 }
8541 }
8542
8543 break;
8545 } else {
8546 break;
8548 }
8549 }
8550
8551 if results.is_empty() {
8553 Ok(JValue::Undefined)
8554 } else {
8555 Ok(JValue::array(results))
8556 }
8557 }
8558
8559 fn replace_with_lambda(
8564 &mut self,
8565 str_value: &JValue,
8566 pattern_value: &JValue,
8567 lambda_value: &JValue,
8568 limit_value: Option<&JValue>,
8569 data: &JValue,
8570 ) -> Result<JValue, EvaluatorError> {
8571 let s = match str_value {
8573 JValue::String(s) => &**s,
8574 _ => {
8575 return Err(EvaluatorError::TypeError(
8576 "replace() requires string arguments".to_string(),
8577 ))
8578 }
8579 };
8580
8581 let (pattern, flags) =
8583 crate::functions::string::extract_regex(pattern_value).ok_or_else(|| {
8584 EvaluatorError::TypeError(
8585 "replace() pattern must be a regex when using lambda replacement".to_string(),
8586 )
8587 })?;
8588
8589 let re = crate::functions::string::build_regex(&pattern, &flags)?;
8591
8592 let limit = if let Some(lim_val) = limit_value {
8594 match lim_val {
8595 JValue::Number(n) => {
8596 let lim_f64 = *n;
8597 if lim_f64 < 0.0 {
8598 return Err(EvaluatorError::EvaluationError(format!(
8599 "D3011: Limit must be non-negative, got {}",
8600 lim_f64
8601 )));
8602 }
8603 Some(lim_f64 as usize)
8604 }
8605 _ => {
8606 return Err(EvaluatorError::TypeError(
8607 "replace() limit must be a number".to_string(),
8608 ))
8609 }
8610 }
8611 } else {
8612 None
8613 };
8614
8615 let mut result = String::new();
8617 let mut last_end = 0;
8618 let mut count = 0;
8619
8620 for cap in re.captures_iter(s) {
8621 if let Some(lim) = limit {
8623 if count >= lim {
8624 break;
8625 }
8626 }
8627
8628 let m = cap.get(0).unwrap();
8629 let match_start = m.start();
8630 let match_end = m.end();
8631 let match_str = m.as_str();
8632
8633 result.push_str(&s[last_end..match_start]);
8635
8636 let groups: Vec<JValue> = (1..cap.len())
8638 .map(|i| {
8639 cap.get(i)
8640 .map(|m| JValue::string(m.as_str().to_string()))
8641 .unwrap_or(JValue::Null)
8642 })
8643 .collect();
8644
8645 let mut match_map = IndexMap::new();
8646 match_map.insert("match".to_string(), JValue::string(match_str));
8647 match_map.insert("start".to_string(), JValue::Number(match_start as f64));
8648 match_map.insert("end".to_string(), JValue::Number(match_end as f64));
8649 match_map.insert("groups".to_string(), JValue::array(groups));
8650 let match_obj = JValue::object(match_map);
8651
8652 let stored_lambda = self.lookup_lambda_from_value(lambda_value).ok_or_else(|| {
8654 EvaluatorError::TypeError("Replacement must be a lambda function".to_string())
8655 })?;
8656 let lambda_result = self.invoke_stored_lambda(&stored_lambda, &[match_obj], data)?;
8657 let replacement_str = match lambda_result {
8658 JValue::String(s) => s,
8659 _ => {
8660 return Err(EvaluatorError::TypeError(format!(
8661 "D3012: Replacement function must return a string, got {:?}",
8662 lambda_result
8663 )))
8664 }
8665 };
8666
8667 result.push_str(&replacement_str);
8669
8670 last_end = match_end;
8671 count += 1;
8672 }
8673
8674 result.push_str(&s[last_end..]);
8676
8677 Ok(JValue::string(result))
8678 }
8679
8680 fn capture_current_environment(&self) -> HashMap<String, JValue> {
8682 self.context.all_bindings()
8683 }
8684
8685 fn capture_environment_for(
8688 &self,
8689 body: &AstNode,
8690 params: &[String],
8691 ) -> HashMap<String, JValue> {
8692 let free_vars = Self::collect_free_variables(body, params);
8693 if free_vars.is_empty() {
8694 return HashMap::new();
8695 }
8696 let mut result = HashMap::new();
8697 for var_name in &free_vars {
8698 if let Some(value) = self.context.lookup(var_name) {
8699 result.insert(var_name.clone(), value.clone());
8700 }
8701 }
8702 result
8703 }
8704
8705 fn collect_free_variables(body: &AstNode, params: &[String]) -> HashSet<String> {
8708 let mut free_vars = HashSet::new();
8709 let bound: HashSet<&str> = params.iter().map(|s| s.as_str()).collect();
8710 Self::collect_free_vars_walk(body, &bound, &mut free_vars);
8711 free_vars
8712 }
8713
8714 fn collect_free_vars_walk(node: &AstNode, bound: &HashSet<&str>, free: &mut HashSet<String>) {
8715 match node {
8716 AstNode::Variable(name) => {
8717 if !bound.contains(name.as_str()) {
8718 free.insert(name.clone());
8719 }
8720 }
8721 AstNode::Function { name, args, .. } => {
8722 if !bound.contains(name.as_str()) {
8724 free.insert(name.clone());
8725 }
8726 for arg in args {
8727 Self::collect_free_vars_walk(arg, bound, free);
8728 }
8729 }
8730 AstNode::Lambda { params, body, .. } => {
8731 let mut inner_bound = bound.clone();
8733 for p in params {
8734 inner_bound.insert(p.as_str());
8735 }
8736 Self::collect_free_vars_walk(body, &inner_bound, free);
8737 }
8738 AstNode::Binary { op, lhs, rhs } => {
8739 Self::collect_free_vars_walk(lhs, bound, free);
8740 Self::collect_free_vars_walk(rhs, bound, free);
8741 let _ = op;
8744 }
8745 AstNode::Unary { operand, .. } => {
8746 Self::collect_free_vars_walk(operand, bound, free);
8747 }
8748 AstNode::Path { steps } => {
8749 for step in steps {
8750 Self::collect_free_vars_walk(&step.node, bound, free);
8751 for stage in &step.stages {
8752 match stage {
8753 Stage::Filter(expr) => Self::collect_free_vars_walk(expr, bound, free),
8754 }
8755 }
8756 }
8757 }
8758 AstNode::Call { procedure, args } => {
8759 Self::collect_free_vars_walk(procedure, bound, free);
8760 for arg in args {
8761 Self::collect_free_vars_walk(arg, bound, free);
8762 }
8763 }
8764 AstNode::Conditional {
8765 condition,
8766 then_branch,
8767 else_branch,
8768 } => {
8769 Self::collect_free_vars_walk(condition, bound, free);
8770 Self::collect_free_vars_walk(then_branch, bound, free);
8771 if let Some(else_expr) = else_branch {
8772 Self::collect_free_vars_walk(else_expr, bound, free);
8773 }
8774 }
8775 AstNode::Block(exprs) => {
8776 let mut block_bound = bound.clone();
8777 for expr in exprs {
8778 Self::collect_free_vars_walk(expr, &block_bound, free);
8779 if let AstNode::Binary {
8781 op: BinaryOp::ColonEqual,
8782 lhs,
8783 ..
8784 } = expr
8785 {
8786 if let AstNode::Variable(var_name) = lhs.as_ref() {
8787 block_bound.insert(var_name.as_str());
8788 }
8789 }
8790 }
8791 }
8792 AstNode::Array(exprs) | AstNode::ArrayGroup(exprs) => {
8793 for expr in exprs {
8794 Self::collect_free_vars_walk(expr, bound, free);
8795 }
8796 }
8797 AstNode::Object(pairs) => {
8798 for (key, value) in pairs {
8799 Self::collect_free_vars_walk(key, bound, free);
8800 Self::collect_free_vars_walk(value, bound, free);
8801 }
8802 }
8803 AstNode::ObjectTransform { input, pattern } => {
8804 Self::collect_free_vars_walk(input, bound, free);
8805 for (key, value) in pattern {
8806 Self::collect_free_vars_walk(key, bound, free);
8807 Self::collect_free_vars_walk(value, bound, free);
8808 }
8809 }
8810 AstNode::Predicate(expr) | AstNode::FunctionApplication(expr) => {
8811 Self::collect_free_vars_walk(expr, bound, free);
8812 }
8813 AstNode::Sort { input, terms } => {
8814 Self::collect_free_vars_walk(input, bound, free);
8815 for (expr, _) in terms {
8816 Self::collect_free_vars_walk(expr, bound, free);
8817 }
8818 }
8819 AstNode::IndexBind { input, .. } => {
8820 Self::collect_free_vars_walk(input, bound, free);
8821 }
8822 AstNode::Transform {
8823 location,
8824 update,
8825 delete,
8826 } => {
8827 Self::collect_free_vars_walk(location, bound, free);
8828 Self::collect_free_vars_walk(update, bound, free);
8829 if let Some(del) = delete {
8830 Self::collect_free_vars_walk(del, bound, free);
8831 }
8832 }
8833 AstNode::String(_)
8835 | AstNode::Name(_)
8836 | AstNode::Number(_)
8837 | AstNode::Boolean(_)
8838 | AstNode::Null
8839 | AstNode::Undefined
8840 | AstNode::Placeholder
8841 | AstNode::Regex { .. }
8842 | AstNode::Wildcard
8843 | AstNode::Descendant
8844 | AstNode::ParentVariable(_) => {}
8845 }
8846 }
8847
8848 fn is_builtin_function(&self, name: &str) -> bool {
8850 matches!(
8851 name,
8852 "string" | "length" | "substring" | "substringBefore" | "substringAfter" |
8854 "uppercase" | "lowercase" | "trim" | "pad" | "contains" | "split" |
8855 "join" | "match" | "replace" | "eval" | "base64encode" | "base64decode" |
8856 "encodeUrlComponent" | "encodeUrl" | "decodeUrlComponent" | "decodeUrl" |
8857
8858 "number" | "abs" | "floor" | "ceil" | "round" | "power" | "sqrt" |
8860 "random" | "formatNumber" | "formatBase" | "formatInteger" | "parseInteger" |
8861
8862 "sum" | "max" | "min" | "average" |
8864
8865 "boolean" | "not" | "exists" |
8867
8868 "count" | "append" | "sort" | "reverse" | "shuffle" | "distinct" | "zip" |
8870
8871 "keys" | "lookup" | "spread" | "merge" | "sift" | "each" | "error" | "assert" | "type" |
8873
8874 "map" | "filter" | "reduce" | "singletonArray" |
8876
8877 "now" | "millis" | "fromMillis" | "toMillis"
8879 )
8880 }
8881
8882 fn call_builtin_with_values(
8885 &mut self,
8886 name: &str,
8887 values: &[JValue],
8888 ) -> Result<JValue, EvaluatorError> {
8889 use crate::functions;
8890
8891 if values.is_empty() {
8892 return Err(EvaluatorError::EvaluationError(format!(
8893 "{}() requires at least 1 argument",
8894 name
8895 )));
8896 }
8897
8898 let arg = &values[0];
8899
8900 match name {
8901 "string" => Ok(functions::string::string(arg, None)?),
8902 "number" => Ok(functions::numeric::number(arg)?),
8903 "boolean" => Ok(functions::boolean::boolean(arg)?),
8904 "not" => {
8905 let b = functions::boolean::boolean(arg)?;
8906 match b {
8907 JValue::Bool(val) => Ok(JValue::Bool(!val)),
8908 _ => Err(EvaluatorError::TypeError(
8909 "not() requires a boolean".to_string(),
8910 )),
8911 }
8912 }
8913 "exists" => Ok(JValue::Bool(!arg.is_null())),
8914 "abs" => match arg {
8915 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
8916 _ => Err(EvaluatorError::TypeError(
8917 "abs() requires a number argument".to_string(),
8918 )),
8919 },
8920 "floor" => match arg {
8921 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
8922 _ => Err(EvaluatorError::TypeError(
8923 "floor() requires a number argument".to_string(),
8924 )),
8925 },
8926 "ceil" => match arg {
8927 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
8928 _ => Err(EvaluatorError::TypeError(
8929 "ceil() requires a number argument".to_string(),
8930 )),
8931 },
8932 "round" => match arg {
8933 JValue::Number(n) => Ok(functions::numeric::round(*n, None)?),
8934 _ => Err(EvaluatorError::TypeError(
8935 "round() requires a number argument".to_string(),
8936 )),
8937 },
8938 "sqrt" => match arg {
8939 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
8940 _ => Err(EvaluatorError::TypeError(
8941 "sqrt() requires a number argument".to_string(),
8942 )),
8943 },
8944 "uppercase" => match arg {
8945 JValue::String(s) => Ok(JValue::string(s.to_uppercase())),
8946 JValue::Null => Ok(JValue::Null),
8947 _ => Err(EvaluatorError::TypeError(
8948 "uppercase() requires a string argument".to_string(),
8949 )),
8950 },
8951 "lowercase" => match arg {
8952 JValue::String(s) => Ok(JValue::string(s.to_lowercase())),
8953 JValue::Null => Ok(JValue::Null),
8954 _ => Err(EvaluatorError::TypeError(
8955 "lowercase() requires a string argument".to_string(),
8956 )),
8957 },
8958 "trim" => match arg {
8959 JValue::String(s) => Ok(JValue::string(s.trim().to_string())),
8960 JValue::Null => Ok(JValue::Null),
8961 _ => Err(EvaluatorError::TypeError(
8962 "trim() requires a string argument".to_string(),
8963 )),
8964 },
8965 "length" => match arg {
8966 JValue::String(s) => Ok(JValue::Number(s.chars().count() as f64)),
8967 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
8968 JValue::Null => Ok(JValue::Null),
8969 _ => Err(EvaluatorError::TypeError(
8970 "length() requires a string or array argument".to_string(),
8971 )),
8972 },
8973 "sum" => match arg {
8974 JValue::Array(arr) => {
8975 let mut total = 0.0;
8976 for item in arr.iter() {
8977 match item {
8978 JValue::Number(n) => {
8979 total += *n;
8980 }
8981 _ => {
8982 return Err(EvaluatorError::TypeError(
8983 "sum() requires all array elements to be numbers".to_string(),
8984 ));
8985 }
8986 }
8987 }
8988 Ok(JValue::Number(total))
8989 }
8990 JValue::Number(n) => Ok(JValue::Number(*n)),
8991 JValue::Null => Ok(JValue::Null),
8992 _ => Err(EvaluatorError::TypeError(
8993 "sum() requires an array of numbers".to_string(),
8994 )),
8995 },
8996 "count" => {
8997 match arg {
8998 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
8999 JValue::Null => Ok(JValue::Number(0.0)),
9000 _ => Ok(JValue::Number(1.0)), }
9002 }
9003 "max" => match arg {
9004 JValue::Array(arr) => {
9005 let mut max_val: Option<f64> = None;
9006 for item in arr.iter() {
9007 if let JValue::Number(n) = item {
9008 let f = *n;
9009 max_val = Some(max_val.map_or(f, |m| m.max(f)));
9010 }
9011 }
9012 max_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
9013 }
9014 JValue::Number(n) => Ok(JValue::Number(*n)),
9015 JValue::Null => Ok(JValue::Null),
9016 _ => Err(EvaluatorError::TypeError(
9017 "max() requires an array of numbers".to_string(),
9018 )),
9019 },
9020 "min" => match arg {
9021 JValue::Array(arr) => {
9022 let mut min_val: Option<f64> = None;
9023 for item in arr.iter() {
9024 if let JValue::Number(n) = item {
9025 let f = *n;
9026 min_val = Some(min_val.map_or(f, |m| m.min(f)));
9027 }
9028 }
9029 min_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
9030 }
9031 JValue::Number(n) => Ok(JValue::Number(*n)),
9032 JValue::Null => Ok(JValue::Null),
9033 _ => Err(EvaluatorError::TypeError(
9034 "min() requires an array of numbers".to_string(),
9035 )),
9036 },
9037 "average" => match arg {
9038 JValue::Array(arr) => {
9039 let nums: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
9040 if nums.is_empty() {
9041 Ok(JValue::Null)
9042 } else {
9043 let avg = nums.iter().sum::<f64>() / nums.len() as f64;
9044 Ok(JValue::Number(avg))
9045 }
9046 }
9047 JValue::Number(n) => Ok(JValue::Number(*n)),
9048 JValue::Null => Ok(JValue::Null),
9049 _ => Err(EvaluatorError::TypeError(
9050 "average() requires an array of numbers".to_string(),
9051 )),
9052 },
9053 "append" => {
9054 if values.len() < 2 {
9056 return Err(EvaluatorError::EvaluationError(
9057 "append() requires 2 arguments".to_string(),
9058 ));
9059 }
9060 let first = &values[0];
9061 let second = &values[1];
9062
9063 let mut result = match first {
9065 JValue::Array(arr) => arr.to_vec(),
9066 JValue::Null => vec![],
9067 other => vec![other.clone()],
9068 };
9069
9070 match second {
9072 JValue::Array(arr) => result.extend(arr.iter().cloned()),
9073 JValue::Null => {}
9074 other => result.push(other.clone()),
9075 }
9076
9077 Ok(JValue::array(result))
9078 }
9079 "reverse" => match arg {
9080 JValue::Array(arr) => {
9081 let mut reversed = arr.to_vec();
9082 reversed.reverse();
9083 Ok(JValue::array(reversed))
9084 }
9085 JValue::Null => Ok(JValue::Null),
9086 _ => Err(EvaluatorError::TypeError(
9087 "reverse() requires an array".to_string(),
9088 )),
9089 },
9090 "keys" => match arg {
9091 JValue::Object(obj) => {
9092 let keys: Vec<JValue> = obj.keys().map(|k| JValue::string(k.clone())).collect();
9093 Ok(JValue::array(keys))
9094 }
9095 JValue::Null => Ok(JValue::Null),
9096 _ => Err(EvaluatorError::TypeError(
9097 "keys() requires an object".to_string(),
9098 )),
9099 },
9100
9101 _ => Err(EvaluatorError::ReferenceError(format!(
9103 "Built-in function {} cannot be called with values directly",
9104 name
9105 ))),
9106 }
9107 }
9108
9109 fn collect_descendants(&self, value: &JValue) -> Vec<JValue> {
9111 let mut descendants = Vec::new();
9112
9113 match value {
9114 JValue::Null => {
9115 return descendants;
9117 }
9118 JValue::Object(obj) => {
9119 descendants.push(value.clone());
9121
9122 for val in obj.values() {
9123 descendants.extend(self.collect_descendants(val));
9125 }
9126 }
9127 JValue::Array(arr) => {
9128 for val in arr.iter() {
9131 descendants.extend(self.collect_descendants(val));
9133 }
9134 }
9135 _ => {
9136 descendants.push(value.clone());
9138 }
9139 }
9140
9141 descendants
9142 }
9143
9144 fn evaluate_predicate(
9146 &mut self,
9147 current: &JValue,
9148 predicate: &AstNode,
9149 ) -> Result<JValue, EvaluatorError> {
9150 if matches!(predicate, AstNode::Boolean(true)) {
9153 return match current {
9154 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
9155 JValue::Null => Ok(JValue::Null),
9156 other => Ok(JValue::array(vec![other.clone()])),
9157 };
9158 }
9159
9160 match current {
9161 JValue::Array(_arr) => {
9162 if let AstNode::Number(n) = predicate {
9166 return self.array_index(current, &JValue::Number(*n));
9168 }
9169
9170 if Self::is_filter_predicate(predicate) {
9173 if let Some(compiled) = try_compile_expr(predicate) {
9175 let shape = _arr.first().and_then(build_shape_cache);
9176 let mut filtered = Vec::with_capacity(_arr.len());
9177 for item in _arr.iter() {
9178 let result = if let Some(ref s) = shape {
9179 eval_compiled_shaped(&compiled, item, None, s)?
9180 } else {
9181 eval_compiled(&compiled, item, None)?
9182 };
9183 if compiled_is_truthy(&result) {
9184 filtered.push(item.clone());
9185 }
9186 }
9187 return Ok(JValue::array(filtered));
9188 }
9189 let mut filtered = Vec::new();
9191 for item in _arr.iter() {
9192 let item_result = self.evaluate_internal(predicate, item)?;
9193 if self.is_truthy(&item_result) {
9194 filtered.push(item.clone());
9195 }
9196 }
9197 return Ok(JValue::array(filtered));
9198 }
9199
9200 match self.evaluate_internal(predicate, current) {
9204 Ok(JValue::Number(_)) => {
9205 let pred_result = self.evaluate_internal(predicate, current)?;
9207 return self.array_index(current, &pred_result);
9208 }
9209 Ok(JValue::Array(indices)) => {
9210 let has_non_numeric =
9213 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
9214
9215 if has_non_numeric {
9216 return Ok(current.clone());
9218 }
9219
9220 let arr_len = _arr.len() as i64;
9222 let mut resolved_indices: Vec<i64> = indices
9223 .iter()
9224 .filter_map(|v| {
9225 if let JValue::Number(n) = v {
9226 let idx = *n as i64;
9227 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
9229 if actual_idx >= 0 && actual_idx < arr_len {
9231 Some(actual_idx)
9232 } else {
9233 None
9234 }
9235 } else {
9236 None
9237 }
9238 })
9239 .collect();
9240
9241 resolved_indices.sort();
9243 resolved_indices.dedup();
9244
9245 let result: Vec<JValue> = resolved_indices
9247 .iter()
9248 .map(|&idx| _arr[idx as usize].clone())
9249 .collect();
9250
9251 return Ok(JValue::array(result));
9252 }
9253 Ok(_) => {
9254 }
9257 Err(_) => {
9258 }
9261 }
9262
9263 if let Some(compiled) = try_compile_expr(predicate) {
9265 let shape = _arr.first().and_then(build_shape_cache);
9266 let mut filtered = Vec::with_capacity(_arr.len());
9267 for item in _arr.iter() {
9268 let result = if let Some(ref s) = shape {
9269 eval_compiled_shaped(&compiled, item, None, s)?
9270 } else {
9271 eval_compiled(&compiled, item, None)?
9272 };
9273 if compiled_is_truthy(&result) {
9274 filtered.push(item.clone());
9275 }
9276 }
9277 return Ok(JValue::array(filtered));
9278 }
9279
9280 let mut filtered = Vec::new();
9282 for item in _arr.iter() {
9283 let item_result = self.evaluate_internal(predicate, item)?;
9284
9285 if self.is_truthy(&item_result) {
9287 filtered.push(item.clone());
9288 }
9289 }
9290
9291 Ok(JValue::array(filtered))
9292 }
9293 JValue::Object(obj) => {
9294 let pred_result = self.evaluate_internal(predicate, current)?;
9298
9299 if let JValue::String(key) = &pred_result {
9301 return Ok(obj.get(&**key).cloned().unwrap_or(JValue::Null));
9302 }
9303
9304 if self.is_truthy(&pred_result) {
9307 Ok(current.clone())
9308 } else {
9309 Ok(JValue::Undefined)
9310 }
9311 }
9312 _ => {
9313 if let AstNode::Number(n) = predicate {
9319 let idx = n.floor() as i64;
9321 if idx == 0 || idx == -1 {
9322 return Ok(current.clone());
9323 } else {
9324 return Ok(JValue::Undefined);
9325 }
9326 }
9327
9328 let pred_result = self.evaluate_internal(predicate, current)?;
9330
9331 if let JValue::Number(n) = &pred_result {
9332 let idx = n.floor() as i64;
9334 if idx == 0 || idx == -1 {
9335 return Ok(current.clone());
9336 } else {
9337 return Ok(JValue::Undefined);
9338 }
9339 }
9340
9341 if self.is_truthy(&pred_result) {
9345 Ok(current.clone())
9346 } else {
9347 Ok(JValue::Undefined)
9349 }
9350 }
9351 }
9352 }
9353
9354 fn evaluate_sort_term(
9357 &mut self,
9358 term_expr: &AstNode,
9359 element: &JValue,
9360 ) -> Result<JValue, EvaluatorError> {
9361 let actual_element = if let JValue::Object(obj) = element {
9363 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
9364 obj.get("@").cloned().unwrap_or(JValue::Null)
9365 } else {
9366 element.clone()
9367 }
9368 } else {
9369 element.clone()
9370 };
9371
9372 if let AstNode::Path { steps } = term_expr {
9374 if steps.len() == 1 && steps[0].stages.is_empty() {
9375 if let AstNode::Name(field_name) = &steps[0].node {
9376 if let JValue::Object(obj) = &actual_element {
9378 return match obj.get(field_name) {
9379 Some(val) => Ok(val.clone()), None => Ok(JValue::Undefined), };
9382 } else {
9383 return Ok(JValue::Undefined);
9385 }
9386 }
9387 }
9388 }
9389
9390 let result = self.evaluate_internal(term_expr, element)?;
9393
9394 if result.is_null() {
9399 return Ok(JValue::Undefined);
9400 }
9401
9402 Ok(result)
9403 }
9404
9405 fn evaluate_sort(
9407 &mut self,
9408 data: &JValue,
9409 terms: &[(AstNode, bool)],
9410 ) -> Result<JValue, EvaluatorError> {
9411 if data.is_null() {
9413 return Ok(JValue::Null);
9414 }
9415
9416 let array = match data {
9418 JValue::Array(arr) => arr.clone(),
9419 other => return Ok(other.clone()),
9420 };
9421
9422 if array.is_empty() {
9424 return Ok(JValue::Array(array));
9425 }
9426
9427 let mut indexed_array: Vec<(usize, Vec<JValue>)> = Vec::new();
9429
9430 for (idx, element) in array.iter().enumerate() {
9431 let mut sort_keys = Vec::new();
9432
9433 for (term_expr, _ascending) in terms {
9435 let saved_dollar = self.context.lookup("$").cloned();
9437
9438 self.context.bind("$".to_string(), element.clone());
9440
9441 let sort_value = self.evaluate_sort_term(term_expr, element)?;
9443
9444 if let Some(val) = saved_dollar {
9446 self.context.bind("$".to_string(), val);
9447 } else {
9448 self.context.unbind("$");
9449 }
9450
9451 sort_keys.push(sort_value);
9452 }
9453
9454 indexed_array.push((idx, sort_keys));
9455 }
9456
9457 for term_idx in 0..terms.len() {
9461 let mut first_valid_type: Option<&str> = None;
9462
9463 for (_idx, sort_keys) in &indexed_array {
9464 let sort_value = &sort_keys[term_idx];
9465
9466 if sort_value.is_undefined() {
9468 continue;
9469 }
9470
9471 let value_type = match sort_value {
9474 JValue::Number(_) => "number",
9475 JValue::String(_) => "string",
9476 JValue::Bool(_) => "boolean",
9477 JValue::Array(_) => "array",
9478 JValue::Object(_) => "object", JValue::Null => "null", _ => "unknown",
9481 };
9482
9483 if value_type != "number" && value_type != "string" {
9486 return Err(EvaluatorError::TypeError("T2008: The expressions within an order-by clause must evaluate to numeric or string values".to_string()));
9487 }
9488
9489 if let Some(first_type) = first_valid_type {
9491 if first_type != value_type {
9492 return Err(EvaluatorError::TypeError(format!(
9493 "T2007: Type mismatch when comparing values in order-by clause: {} and {}",
9494 first_type, value_type
9495 )));
9496 }
9497 } else {
9498 first_valid_type = Some(value_type);
9499 }
9500 }
9501 }
9502
9503 indexed_array.sort_by(|a, b| {
9505 for (i, (_term_expr, ascending)) in terms.iter().enumerate() {
9507 let left = &a.1[i];
9508 let right = &b.1[i];
9509
9510 let cmp = self.compare_values(left, right);
9511
9512 if cmp != std::cmp::Ordering::Equal {
9513 return if *ascending { cmp } else { cmp.reverse() };
9514 }
9515 }
9516
9517 a.0.cmp(&b.0)
9519 });
9520
9521 let sorted: Vec<JValue> = indexed_array
9523 .iter()
9524 .map(|(idx, _)| array[*idx].clone())
9525 .collect();
9526
9527 Ok(JValue::array(sorted))
9528 }
9529
9530 fn compare_values(&self, left: &JValue, right: &JValue) -> Ordering {
9532 let left_undef = left.is_undefined();
9534 let right_undef = right.is_undefined();
9535
9536 if left_undef && right_undef {
9537 return Ordering::Equal;
9538 }
9539 if left_undef {
9540 return Ordering::Greater; }
9542 if right_undef {
9543 return Ordering::Less;
9544 }
9545
9546 match (left, right) {
9547 (JValue::Null, JValue::Null) => Ordering::Equal,
9549 (JValue::Null, _) => Ordering::Greater,
9550 (_, JValue::Null) => Ordering::Less,
9551
9552 (JValue::Number(a), JValue::Number(b)) => {
9554 let a_f64 = *a;
9555 let b_f64 = *b;
9556 a_f64.partial_cmp(&b_f64).unwrap_or(Ordering::Equal)
9557 }
9558
9559 (JValue::String(a), JValue::String(b)) => a.cmp(b),
9561
9562 (JValue::Bool(a), JValue::Bool(b)) => a.cmp(b),
9564
9565 (JValue::Array(a), JValue::Array(b)) => {
9567 for (a_elem, b_elem) in a.iter().zip(b.iter()) {
9568 let cmp = self.compare_values(a_elem, b_elem);
9569 if cmp != Ordering::Equal {
9570 return cmp;
9571 }
9572 }
9573 a.len().cmp(&b.len())
9574 }
9575
9576 (JValue::Bool(_), JValue::Number(_)) => Ordering::Less,
9579 (JValue::Bool(_), JValue::String(_)) => Ordering::Less,
9580 (JValue::Bool(_), JValue::Array(_)) => Ordering::Less,
9581 (JValue::Bool(_), JValue::Object(_)) => Ordering::Less,
9582
9583 (JValue::Number(_), JValue::Bool(_)) => Ordering::Greater,
9584 (JValue::Number(_), JValue::String(_)) => Ordering::Less,
9585 (JValue::Number(_), JValue::Array(_)) => Ordering::Less,
9586 (JValue::Number(_), JValue::Object(_)) => Ordering::Less,
9587
9588 (JValue::String(_), JValue::Bool(_)) => Ordering::Greater,
9589 (JValue::String(_), JValue::Number(_)) => Ordering::Greater,
9590 (JValue::String(_), JValue::Array(_)) => Ordering::Less,
9591 (JValue::String(_), JValue::Object(_)) => Ordering::Less,
9592
9593 (JValue::Array(_), JValue::Bool(_)) => Ordering::Greater,
9594 (JValue::Array(_), JValue::Number(_)) => Ordering::Greater,
9595 (JValue::Array(_), JValue::String(_)) => Ordering::Greater,
9596 (JValue::Array(_), JValue::Object(_)) => Ordering::Less,
9597
9598 (JValue::Object(_), _) => Ordering::Greater,
9599 _ => Ordering::Equal,
9600 }
9601 }
9602
9603 fn is_truthy(&self, value: &JValue) -> bool {
9605 match value {
9606 JValue::Null | JValue::Undefined => false,
9607 JValue::Bool(b) => *b,
9608 JValue::Number(n) => *n != 0.0,
9609 JValue::String(s) => !s.is_empty(),
9610 JValue::Array(arr) => !arr.is_empty(),
9611 JValue::Object(obj) => !obj.is_empty(),
9612 _ => false,
9613 }
9614 }
9615
9616 fn is_truthy_for_default(&self, value: &JValue) -> bool {
9622 match value {
9623 JValue::Lambda { .. } | JValue::Builtin { .. } => false,
9625 JValue::Array(arr) => {
9627 if arr.is_empty() {
9628 return false;
9629 }
9630 arr.iter().any(|elem| self.is_truthy(elem))
9632 }
9633 _ => self.is_truthy(value),
9635 }
9636 }
9637
9638 fn unwrap_singleton(&self, value: JValue) -> JValue {
9641 match value {
9642 JValue::Array(ref arr) if arr.len() == 1 => arr[0].clone(),
9643 _ => value,
9644 }
9645 }
9646
9647 fn extract_lambda_ids(&self, value: &JValue) -> Vec<String> {
9651 match value {
9653 JValue::Number(_)
9654 | JValue::Bool(_)
9655 | JValue::String(_)
9656 | JValue::Null
9657 | JValue::Undefined
9658 | JValue::Regex { .. }
9659 | JValue::Builtin { .. } => return Vec::new(),
9660 _ => {}
9661 }
9662 let mut ids = Vec::new();
9663 self.collect_lambda_ids(value, &mut ids);
9664 ids
9665 }
9666
9667 fn collect_lambda_ids(&self, value: &JValue, ids: &mut Vec<String>) {
9668 match value {
9669 JValue::Lambda { lambda_id, .. } => {
9670 let id_str = lambda_id.to_string();
9671 if !ids.contains(&id_str) {
9672 ids.push(id_str);
9673 if let Some(stored) = self.context.lookup_lambda(lambda_id) {
9678 let env_values: Vec<JValue> =
9679 stored.captured_env.values().cloned().collect();
9680 for env_value in &env_values {
9681 self.collect_lambda_ids(env_value, ids);
9682 }
9683 }
9684 }
9685 }
9686 JValue::Object(map) => {
9687 for v in map.values() {
9689 self.collect_lambda_ids(v, ids);
9690 }
9691 }
9692 JValue::Array(arr) => {
9693 for v in arr.iter() {
9695 self.collect_lambda_ids(v, ids);
9696 }
9697 }
9698 _ => {}
9699 }
9700 }
9701
9702 fn equals(&self, left: &JValue, right: &JValue) -> bool {
9704 crate::functions::array::values_equal(left, right)
9705 }
9706
9707 fn add(
9709 &self,
9710 left: &JValue,
9711 right: &JValue,
9712 left_is_explicit_null: bool,
9713 right_is_explicit_null: bool,
9714 ) -> Result<JValue, EvaluatorError> {
9715 match (left, right) {
9716 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a + *b)),
9717 (JValue::Null, JValue::Number(_)) if left_is_explicit_null => {
9719 Err(EvaluatorError::TypeError(
9720 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9721 ))
9722 }
9723 (JValue::Number(_), JValue::Null) if right_is_explicit_null => {
9724 Err(EvaluatorError::TypeError(
9725 "T2002: The right side of the + operator must evaluate to a number".to_string(),
9726 ))
9727 }
9728 (JValue::Null, JValue::Null) if left_is_explicit_null || right_is_explicit_null => {
9729 Err(EvaluatorError::TypeError(
9730 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9731 ))
9732 }
9733 (JValue::Null, JValue::Number(_)) | (JValue::Number(_), JValue::Null) => {
9735 Ok(JValue::Null)
9736 }
9737 (JValue::Bool(_), _) => Err(EvaluatorError::TypeError(
9739 "T2001: The left side of the '+' operator must evaluate to a number or a string"
9740 .to_string(),
9741 )),
9742 (_, JValue::Bool(_)) => Err(EvaluatorError::TypeError(
9743 "T2001: The right side of the '+' operator must evaluate to a number or a string"
9744 .to_string(),
9745 )),
9746 (JValue::Null, JValue::Null) => Ok(JValue::Null),
9748 _ => Err(EvaluatorError::TypeError(format!(
9749 "Cannot add {:?} and {:?}",
9750 left, right
9751 ))),
9752 }
9753 }
9754
9755 fn subtract(
9757 &self,
9758 left: &JValue,
9759 right: &JValue,
9760 left_is_explicit_null: bool,
9761 right_is_explicit_null: bool,
9762 ) -> Result<JValue, EvaluatorError> {
9763 match (left, right) {
9764 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a - *b)),
9765 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9767 "T2002: The left side of the - operator must evaluate to a number".to_string(),
9768 )),
9769 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9770 "T2002: The right side of the - operator must evaluate to a number".to_string(),
9771 )),
9772 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9774 _ => Err(EvaluatorError::TypeError(format!(
9775 "Cannot subtract {:?} and {:?}",
9776 left, right
9777 ))),
9778 }
9779 }
9780
9781 fn multiply(
9783 &self,
9784 left: &JValue,
9785 right: &JValue,
9786 left_is_explicit_null: bool,
9787 right_is_explicit_null: bool,
9788 ) -> Result<JValue, EvaluatorError> {
9789 match (left, right) {
9790 (JValue::Number(a), JValue::Number(b)) => {
9791 let result = *a * *b;
9792 if result.is_infinite() {
9794 return Err(EvaluatorError::EvaluationError(
9795 "D1001: Number out of range".to_string(),
9796 ));
9797 }
9798 Ok(JValue::Number(result))
9799 }
9800 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9802 "T2002: The left side of the * operator must evaluate to a number".to_string(),
9803 )),
9804 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9805 "T2002: The right side of the * operator must evaluate to a number".to_string(),
9806 )),
9807 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9809 _ => Err(EvaluatorError::TypeError(format!(
9810 "Cannot multiply {:?} and {:?}",
9811 left, right
9812 ))),
9813 }
9814 }
9815
9816 fn divide(
9818 &self,
9819 left: &JValue,
9820 right: &JValue,
9821 left_is_explicit_null: bool,
9822 right_is_explicit_null: bool,
9823 ) -> Result<JValue, EvaluatorError> {
9824 match (left, right) {
9825 (JValue::Number(a), JValue::Number(b)) => {
9826 let denominator = *b;
9827 if denominator == 0.0 {
9828 return Err(EvaluatorError::EvaluationError(
9829 "Division by zero".to_string(),
9830 ));
9831 }
9832 Ok(JValue::Number(*a / denominator))
9833 }
9834 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9836 "T2002: The left side of the / operator must evaluate to a number".to_string(),
9837 )),
9838 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9839 "T2002: The right side of the / operator must evaluate to a number".to_string(),
9840 )),
9841 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9843 _ => Err(EvaluatorError::TypeError(format!(
9844 "Cannot divide {:?} and {:?}",
9845 left, right
9846 ))),
9847 }
9848 }
9849
9850 fn modulo(
9852 &self,
9853 left: &JValue,
9854 right: &JValue,
9855 left_is_explicit_null: bool,
9856 right_is_explicit_null: bool,
9857 ) -> Result<JValue, EvaluatorError> {
9858 match (left, right) {
9859 (JValue::Number(a), JValue::Number(b)) => {
9860 let denominator = *b;
9861 if denominator == 0.0 {
9862 return Err(EvaluatorError::EvaluationError(
9863 "Division by zero".to_string(),
9864 ));
9865 }
9866 Ok(JValue::Number(*a % denominator))
9867 }
9868 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9870 "T2002: The left side of the % operator must evaluate to a number".to_string(),
9871 )),
9872 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9873 "T2002: The right side of the % operator must evaluate to a number".to_string(),
9874 )),
9875 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9877 _ => Err(EvaluatorError::TypeError(format!(
9878 "Cannot compute modulo of {:?} and {:?}",
9879 left, right
9880 ))),
9881 }
9882 }
9883
9884 fn type_name(value: &JValue) -> &'static str {
9886 match value {
9887 JValue::Null => "null",
9888 JValue::Bool(_) => "boolean",
9889 JValue::Number(_) => "number",
9890 JValue::String(_) => "string",
9891 JValue::Array(_) => "array",
9892 JValue::Object(_) => "object",
9893 _ => "unknown",
9894 }
9895 }
9896
9897 fn ordered_compare(
9903 &self,
9904 left: &JValue,
9905 right: &JValue,
9906 left_is_explicit_null: bool,
9907 right_is_explicit_null: bool,
9908 op_symbol: &str,
9909 compare_nums: fn(f64, f64) -> bool,
9910 compare_strs: fn(&str, &str) -> bool,
9911 ) -> Result<JValue, EvaluatorError> {
9912 match (left, right) {
9913 (JValue::Number(a), JValue::Number(b)) => {
9914 Ok(JValue::Bool(compare_nums(*a, *b)))
9915 }
9916 (JValue::String(a), JValue::String(b)) => Ok(JValue::Bool(compare_strs(a, b))),
9917 (JValue::Null, JValue::Null) => Ok(JValue::Null),
9919 (JValue::Null, _) if left_is_explicit_null => {
9921 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9922 }
9923 (_, JValue::Null) if right_is_explicit_null => {
9924 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9925 }
9926 (JValue::Bool(_), JValue::Null) | (JValue::Null, JValue::Bool(_)) => {
9928 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9929 }
9930 (JValue::Number(_), JValue::Null) | (JValue::Null, JValue::Number(_)) |
9932 (JValue::String(_), JValue::Null) | (JValue::Null, JValue::String(_)) => {
9933 Ok(JValue::Null)
9934 }
9935 (JValue::String(_), JValue::Number(_)) | (JValue::Number(_), JValue::String(_)) => {
9937 Err(EvaluatorError::EvaluationError(format!(
9938 "T2009: The expressions on either side of operator \"{}\" must be of the same data type",
9939 op_symbol
9940 )))
9941 }
9942 (JValue::Bool(_), _) | (_, JValue::Bool(_)) => {
9944 Err(EvaluatorError::EvaluationError(format!(
9945 "T2010: Cannot compare {} and {}",
9946 Self::type_name(left), Self::type_name(right)
9947 )))
9948 }
9949 _ => Err(EvaluatorError::EvaluationError(format!(
9951 "T2010: Cannot compare {} and {}",
9952 Self::type_name(left), Self::type_name(right)
9953 ))),
9954 }
9955 }
9956
9957 fn less_than(
9959 &self,
9960 left: &JValue,
9961 right: &JValue,
9962 left_is_explicit_null: bool,
9963 right_is_explicit_null: bool,
9964 ) -> Result<JValue, EvaluatorError> {
9965 self.ordered_compare(
9966 left,
9967 right,
9968 left_is_explicit_null,
9969 right_is_explicit_null,
9970 "<",
9971 |a, b| a < b,
9972 |a, b| a < b,
9973 )
9974 }
9975
9976 fn less_than_or_equal(
9978 &self,
9979 left: &JValue,
9980 right: &JValue,
9981 left_is_explicit_null: bool,
9982 right_is_explicit_null: bool,
9983 ) -> Result<JValue, EvaluatorError> {
9984 self.ordered_compare(
9985 left,
9986 right,
9987 left_is_explicit_null,
9988 right_is_explicit_null,
9989 "<=",
9990 |a, b| a <= b,
9991 |a, b| a <= b,
9992 )
9993 }
9994
9995 fn greater_than(
9997 &self,
9998 left: &JValue,
9999 right: &JValue,
10000 left_is_explicit_null: bool,
10001 right_is_explicit_null: bool,
10002 ) -> Result<JValue, EvaluatorError> {
10003 self.ordered_compare(
10004 left,
10005 right,
10006 left_is_explicit_null,
10007 right_is_explicit_null,
10008 ">",
10009 |a, b| a > b,
10010 |a, b| a > b,
10011 )
10012 }
10013
10014 fn greater_than_or_equal(
10016 &self,
10017 left: &JValue,
10018 right: &JValue,
10019 left_is_explicit_null: bool,
10020 right_is_explicit_null: bool,
10021 ) -> Result<JValue, EvaluatorError> {
10022 self.ordered_compare(
10023 left,
10024 right,
10025 left_is_explicit_null,
10026 right_is_explicit_null,
10027 ">=",
10028 |a, b| a >= b,
10029 |a, b| a >= b,
10030 )
10031 }
10032
10033 fn value_to_concat_string(value: &JValue) -> Result<String, EvaluatorError> {
10035 match value {
10036 JValue::String(s) => Ok(s.to_string()),
10037 JValue::Null => Ok(String::new()),
10038 JValue::Number(_) | JValue::Bool(_) | JValue::Array(_) | JValue::Object(_) => {
10039 match crate::functions::string::string(value, None) {
10040 Ok(JValue::String(s)) => Ok(s.to_string()),
10041 Ok(JValue::Null) => Ok(String::new()),
10042 _ => Err(EvaluatorError::TypeError(
10043 "Cannot concatenate complex types".to_string(),
10044 )),
10045 }
10046 }
10047 _ => Ok(String::new()),
10048 }
10049 }
10050
10051 fn concatenate(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
10053 let left_str = Self::value_to_concat_string(left)?;
10054 let right_str = Self::value_to_concat_string(right)?;
10055 Ok(JValue::string(format!("{}{}", left_str, right_str)))
10056 }
10057
10058 fn range(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
10060 let start_f64 = match left {
10062 JValue::Number(n) => Some(*n),
10063 JValue::Null => None,
10064 _ => {
10065 return Err(EvaluatorError::EvaluationError(
10066 "T2003: Left operand of range operator must be a number".to_string(),
10067 ));
10068 }
10069 };
10070
10071 if let Some(val) = start_f64 {
10073 if val.fract() != 0.0 {
10074 return Err(EvaluatorError::EvaluationError(
10075 "T2003: Left operand of range operator must be an integer".to_string(),
10076 ));
10077 }
10078 }
10079
10080 let end_f64 = match right {
10082 JValue::Number(n) => Some(*n),
10083 JValue::Null => None,
10084 _ => {
10085 return Err(EvaluatorError::EvaluationError(
10086 "T2004: Right operand of range operator must be a number".to_string(),
10087 ));
10088 }
10089 };
10090
10091 if let Some(val) = end_f64 {
10093 if val.fract() != 0.0 {
10094 return Err(EvaluatorError::EvaluationError(
10095 "T2004: Right operand of range operator must be an integer".to_string(),
10096 ));
10097 }
10098 }
10099
10100 if start_f64.is_none() || end_f64.is_none() {
10102 return Ok(JValue::array(vec![]));
10103 }
10104
10105 let start = start_f64.unwrap() as i64;
10106 let end = end_f64.unwrap() as i64;
10107
10108 let size = if start <= end {
10110 (end - start + 1) as usize
10111 } else {
10112 0
10113 };
10114 if size > 10_000_000 {
10115 return Err(EvaluatorError::EvaluationError(
10116 "D2014: Range operator results in too many elements (> 10,000,000)".to_string(),
10117 ));
10118 }
10119
10120 let mut result = Vec::with_capacity(size);
10121 if start <= end {
10122 for i in start..=end {
10123 result.push(JValue::Number(i as f64));
10124 }
10125 }
10126 Ok(JValue::array(result))
10128 }
10129
10130 fn array_index(&self, array: &JValue, index: &JValue) -> Result<JValue, EvaluatorError> {
10133 match (array, index) {
10134 (JValue::Array(arr), JValue::Number(n)) => {
10135 let idx = *n as i64;
10136 let len = arr.len() as i64;
10137
10138 let actual_idx = if idx < 0 { len + idx } else { idx };
10140
10141 if actual_idx < 0 || actual_idx >= len {
10142 Ok(JValue::Null)
10143 } else {
10144 Ok(arr[actual_idx as usize].clone())
10145 }
10146 }
10147 _ => Err(EvaluatorError::TypeError(
10148 "Array indexing requires array and number".to_string(),
10149 )),
10150 }
10151 }
10152
10153 fn array_filter(
10156 &mut self,
10157 _lhs_node: &AstNode,
10158 rhs_node: &AstNode,
10159 array: &JValue,
10160 _original_data: &JValue,
10161 ) -> Result<JValue, EvaluatorError> {
10162 match array {
10163 JValue::Array(arr) => {
10164 let mut filtered = Vec::with_capacity(arr.len() / 2);
10166
10167 for item in arr.iter() {
10168 let predicate_result = self.evaluate_internal(rhs_node, item)?;
10171
10172 if self.is_truthy(&predicate_result) {
10174 filtered.push(item.clone());
10175 }
10176 }
10177
10178 Ok(JValue::array(filtered))
10179 }
10180 _ => Err(EvaluatorError::TypeError(
10181 "Array filtering requires an array".to_string(),
10182 )),
10183 }
10184 }
10185
10186 fn in_operator(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
10187 if left.is_null() || right.is_null() {
10190 return Ok(JValue::Bool(false));
10191 }
10192
10193 match right {
10194 JValue::Array(arr) => Ok(JValue::Bool(arr.iter().any(|v| self.equals(left, v)))),
10195 JValue::Object(obj) => {
10196 if let JValue::String(key) = left {
10197 Ok(JValue::Bool(obj.contains_key(&**key)))
10198 } else {
10199 Ok(JValue::Bool(false))
10200 }
10201 }
10202 other => Ok(JValue::Bool(self.equals(left, other))),
10205 }
10206 }
10207
10208 fn create_partial_application(
10212 &mut self,
10213 name: &str,
10214 args: &[AstNode],
10215 is_builtin: bool,
10216 data: &JValue,
10217 ) -> Result<JValue, EvaluatorError> {
10218 let is_lambda = self.context.lookup_lambda(name).is_some()
10220 || (self
10221 .context
10222 .lookup(name)
10223 .map(|v| matches!(v, JValue::Lambda { .. }))
10224 .unwrap_or(false));
10225
10226 if !is_lambda && !is_builtin {
10229 if self.is_builtin_function(name) {
10231 return Err(EvaluatorError::EvaluationError(format!(
10232 "T1007: Attempted to partially apply a non-function. Did you mean ${}?",
10233 name
10234 )));
10235 }
10236 return Err(EvaluatorError::EvaluationError(
10237 "T1008: Attempted to partially apply a non-function".to_string(),
10238 ));
10239 }
10240
10241 let mut bound_args: Vec<(usize, JValue)> = Vec::new();
10243 let mut placeholder_positions: Vec<usize> = Vec::new();
10244
10245 for (i, arg) in args.iter().enumerate() {
10246 if matches!(arg, AstNode::Placeholder) {
10247 placeholder_positions.push(i);
10248 } else {
10249 let value = self.evaluate_internal(arg, data)?;
10250 bound_args.push((i, value));
10251 }
10252 }
10253
10254 let param_names: Vec<String> = placeholder_positions
10256 .iter()
10257 .enumerate()
10258 .map(|(i, _)| format!("__p{}", i))
10259 .collect();
10260
10261 let partial_id = format!(
10264 "__partial_{}_{}_{}",
10265 name,
10266 placeholder_positions.len(),
10267 bound_args.len()
10268 );
10269
10270 let stored_lambda = StoredLambda {
10273 params: param_names.clone(),
10274 body: AstNode::String(format!(
10275 "__partial_call:{}:{}:{}",
10276 name,
10277 is_builtin,
10278 args.len()
10279 )),
10280 compiled_body: None, signature: None,
10282 captured_env: {
10283 let mut env = self.capture_current_environment();
10284 for (pos, value) in &bound_args {
10286 env.insert(format!("__bound_arg_{}", pos), value.clone());
10287 }
10288 env.insert(
10290 "__placeholder_positions".to_string(),
10291 JValue::array(
10292 placeholder_positions
10293 .iter()
10294 .map(|p| JValue::Number(*p as f64))
10295 .collect::<Vec<_>>(),
10296 ),
10297 );
10298 env.insert(
10300 "__total_args".to_string(),
10301 JValue::Number(args.len() as f64),
10302 );
10303 env
10304 },
10305 captured_data: Some(data.clone()),
10306 thunk: false,
10307 };
10308
10309 self.context.bind_lambda(partial_id.clone(), stored_lambda);
10310
10311 let lambda_obj = JValue::lambda(
10313 partial_id.as_str(),
10314 param_names,
10315 Some(name.to_string()),
10316 None::<String>,
10317 );
10318
10319 Ok(lambda_obj)
10320 }
10321}
10322
10323impl Default for Evaluator {
10324 fn default() -> Self {
10325 Self::new()
10326 }
10327}
10328
10329#[cfg(test)]
10330mod tests {
10331 use super::*;
10332 use crate::ast::{BinaryOp, UnaryOp};
10333
10334 #[test]
10335 fn test_evaluate_literals() {
10336 let mut evaluator = Evaluator::new();
10337 let data = JValue::Null;
10338
10339 let result = evaluator
10341 .evaluate(&AstNode::string("hello"), &data)
10342 .unwrap();
10343 assert_eq!(result, JValue::string("hello"));
10344
10345 let result = evaluator.evaluate(&AstNode::number(42.0), &data).unwrap();
10347 assert_eq!(result, JValue::from(42i64));
10348
10349 let result = evaluator.evaluate(&AstNode::boolean(true), &data).unwrap();
10351 assert_eq!(result, JValue::Bool(true));
10352
10353 let result = evaluator.evaluate(&AstNode::null(), &data).unwrap();
10355 assert_eq!(result, JValue::Null);
10356 }
10357
10358 #[test]
10359 fn test_evaluate_variables() {
10360 let mut evaluator = Evaluator::new();
10361 let data = JValue::Null;
10362
10363 evaluator
10365 .context
10366 .bind("x".to_string(), JValue::from(100i64));
10367
10368 let result = evaluator.evaluate(&AstNode::variable("x"), &data).unwrap();
10370 assert_eq!(result, JValue::from(100i64));
10371
10372 let result = evaluator
10374 .evaluate(&AstNode::variable("undefined"), &data)
10375 .unwrap();
10376 assert_eq!(result, JValue::Null);
10377 }
10378
10379 #[test]
10380 fn test_evaluate_path() {
10381 let mut evaluator = Evaluator::new();
10382 let data = JValue::from(serde_json::json!({
10383 "foo": {
10384 "bar": {
10385 "baz": 42
10386 }
10387 }
10388 }));
10389 let path = AstNode::Path {
10391 steps: vec![PathStep::new(AstNode::Name("foo".to_string()))],
10392 };
10393 let result = evaluator.evaluate(&path, &data).unwrap();
10394 assert_eq!(
10395 result,
10396 JValue::from(serde_json::json!({"bar": {"baz": 42}}))
10397 );
10398
10399 let path = AstNode::Path {
10401 steps: vec![
10402 PathStep::new(AstNode::Name("foo".to_string())),
10403 PathStep::new(AstNode::Name("bar".to_string())),
10404 PathStep::new(AstNode::Name("baz".to_string())),
10405 ],
10406 };
10407 let result = evaluator.evaluate(&path, &data).unwrap();
10408 assert_eq!(result, JValue::from(42i64));
10409
10410 let path = AstNode::Path {
10412 steps: vec![PathStep::new(AstNode::Name("missing".to_string()))],
10413 };
10414 let result = evaluator.evaluate(&path, &data).unwrap();
10415 assert_eq!(result, JValue::Null);
10416 }
10417
10418 #[test]
10419 fn test_arithmetic_operations() {
10420 let mut evaluator = Evaluator::new();
10421 let data = JValue::Null;
10422
10423 let expr = AstNode::Binary {
10425 op: BinaryOp::Add,
10426 lhs: Box::new(AstNode::number(10.0)),
10427 rhs: Box::new(AstNode::number(5.0)),
10428 };
10429 let result = evaluator.evaluate(&expr, &data).unwrap();
10430 assert_eq!(result, JValue::Number(15.0));
10431
10432 let expr = AstNode::Binary {
10434 op: BinaryOp::Subtract,
10435 lhs: Box::new(AstNode::number(10.0)),
10436 rhs: Box::new(AstNode::number(5.0)),
10437 };
10438 let result = evaluator.evaluate(&expr, &data).unwrap();
10439 assert_eq!(result, JValue::Number(5.0));
10440
10441 let expr = AstNode::Binary {
10443 op: BinaryOp::Multiply,
10444 lhs: Box::new(AstNode::number(10.0)),
10445 rhs: Box::new(AstNode::number(5.0)),
10446 };
10447 let result = evaluator.evaluate(&expr, &data).unwrap();
10448 assert_eq!(result, JValue::Number(50.0));
10449
10450 let expr = AstNode::Binary {
10452 op: BinaryOp::Divide,
10453 lhs: Box::new(AstNode::number(10.0)),
10454 rhs: Box::new(AstNode::number(5.0)),
10455 };
10456 let result = evaluator.evaluate(&expr, &data).unwrap();
10457 assert_eq!(result, JValue::Number(2.0));
10458
10459 let expr = AstNode::Binary {
10461 op: BinaryOp::Modulo,
10462 lhs: Box::new(AstNode::number(10.0)),
10463 rhs: Box::new(AstNode::number(3.0)),
10464 };
10465 let result = evaluator.evaluate(&expr, &data).unwrap();
10466 assert_eq!(result, JValue::Number(1.0));
10467 }
10468
10469 #[test]
10470 fn test_division_by_zero() {
10471 let mut evaluator = Evaluator::new();
10472 let data = JValue::Null;
10473
10474 let expr = AstNode::Binary {
10475 op: BinaryOp::Divide,
10476 lhs: Box::new(AstNode::number(10.0)),
10477 rhs: Box::new(AstNode::number(0.0)),
10478 };
10479 let result = evaluator.evaluate(&expr, &data);
10480 assert!(result.is_err());
10481 }
10482
10483 #[test]
10484 fn test_comparison_operations() {
10485 let mut evaluator = Evaluator::new();
10486 let data = JValue::Null;
10487
10488 let expr = AstNode::Binary {
10490 op: BinaryOp::Equal,
10491 lhs: Box::new(AstNode::number(5.0)),
10492 rhs: Box::new(AstNode::number(5.0)),
10493 };
10494 assert_eq!(
10495 evaluator.evaluate(&expr, &data).unwrap(),
10496 JValue::Bool(true)
10497 );
10498
10499 let expr = AstNode::Binary {
10501 op: BinaryOp::NotEqual,
10502 lhs: Box::new(AstNode::number(5.0)),
10503 rhs: Box::new(AstNode::number(3.0)),
10504 };
10505 assert_eq!(
10506 evaluator.evaluate(&expr, &data).unwrap(),
10507 JValue::Bool(true)
10508 );
10509
10510 let expr = AstNode::Binary {
10512 op: BinaryOp::LessThan,
10513 lhs: Box::new(AstNode::number(3.0)),
10514 rhs: Box::new(AstNode::number(5.0)),
10515 };
10516 assert_eq!(
10517 evaluator.evaluate(&expr, &data).unwrap(),
10518 JValue::Bool(true)
10519 );
10520
10521 let expr = AstNode::Binary {
10523 op: BinaryOp::GreaterThan,
10524 lhs: Box::new(AstNode::number(5.0)),
10525 rhs: Box::new(AstNode::number(3.0)),
10526 };
10527 assert_eq!(
10528 evaluator.evaluate(&expr, &data).unwrap(),
10529 JValue::Bool(true)
10530 );
10531 }
10532
10533 #[test]
10534 fn test_logical_operations() {
10535 let mut evaluator = Evaluator::new();
10536 let data = JValue::Null;
10537
10538 let expr = AstNode::Binary {
10540 op: BinaryOp::And,
10541 lhs: Box::new(AstNode::boolean(true)),
10542 rhs: Box::new(AstNode::boolean(true)),
10543 };
10544 assert_eq!(
10545 evaluator.evaluate(&expr, &data).unwrap(),
10546 JValue::Bool(true)
10547 );
10548
10549 let expr = AstNode::Binary {
10551 op: BinaryOp::And,
10552 lhs: Box::new(AstNode::boolean(false)),
10553 rhs: Box::new(AstNode::boolean(true)),
10554 };
10555 assert_eq!(
10556 evaluator.evaluate(&expr, &data).unwrap(),
10557 JValue::Bool(false)
10558 );
10559
10560 let expr = AstNode::Binary {
10562 op: BinaryOp::Or,
10563 lhs: Box::new(AstNode::boolean(true)),
10564 rhs: Box::new(AstNode::boolean(false)),
10565 };
10566 assert_eq!(
10567 evaluator.evaluate(&expr, &data).unwrap(),
10568 JValue::Bool(true)
10569 );
10570
10571 let expr = AstNode::Binary {
10573 op: BinaryOp::Or,
10574 lhs: Box::new(AstNode::boolean(false)),
10575 rhs: Box::new(AstNode::boolean(false)),
10576 };
10577 assert_eq!(
10578 evaluator.evaluate(&expr, &data).unwrap(),
10579 JValue::Bool(false)
10580 );
10581 }
10582
10583 #[test]
10584 fn test_string_concatenation() {
10585 let mut evaluator = Evaluator::new();
10586 let data = JValue::Null;
10587
10588 let expr = AstNode::Binary {
10589 op: BinaryOp::Concatenate,
10590 lhs: Box::new(AstNode::string("Hello")),
10591 rhs: Box::new(AstNode::string(" World")),
10592 };
10593 let result = evaluator.evaluate(&expr, &data).unwrap();
10594 assert_eq!(result, JValue::string("Hello World"));
10595 }
10596
10597 #[test]
10598 fn test_range_operator() {
10599 let mut evaluator = Evaluator::new();
10600 let data = JValue::Null;
10601
10602 let expr = AstNode::Binary {
10604 op: BinaryOp::Range,
10605 lhs: Box::new(AstNode::number(1.0)),
10606 rhs: Box::new(AstNode::number(5.0)),
10607 };
10608 let result = evaluator.evaluate(&expr, &data).unwrap();
10609 assert_eq!(
10610 result,
10611 JValue::array(vec![
10612 JValue::Number(1.0),
10613 JValue::Number(2.0),
10614 JValue::Number(3.0),
10615 JValue::Number(4.0),
10616 JValue::Number(5.0)
10617 ])
10618 );
10619
10620 let expr = AstNode::Binary {
10622 op: BinaryOp::Range,
10623 lhs: Box::new(AstNode::number(5.0)),
10624 rhs: Box::new(AstNode::number(1.0)),
10625 };
10626 let result = evaluator.evaluate(&expr, &data).unwrap();
10627 assert_eq!(result, JValue::array(vec![]));
10628 }
10629
10630 #[test]
10631 fn test_in_operator() {
10632 let mut evaluator = Evaluator::new();
10633 let data = JValue::Null;
10634
10635 let expr = AstNode::Binary {
10637 op: BinaryOp::In,
10638 lhs: Box::new(AstNode::number(3.0)),
10639 rhs: Box::new(AstNode::Array(vec![
10640 AstNode::number(1.0),
10641 AstNode::number(2.0),
10642 AstNode::number(3.0),
10643 ])),
10644 };
10645 let result = evaluator.evaluate(&expr, &data).unwrap();
10646 assert_eq!(result, JValue::Bool(true));
10647
10648 let expr = AstNode::Binary {
10650 op: BinaryOp::In,
10651 lhs: Box::new(AstNode::number(5.0)),
10652 rhs: Box::new(AstNode::Array(vec![
10653 AstNode::number(1.0),
10654 AstNode::number(2.0),
10655 AstNode::number(3.0),
10656 ])),
10657 };
10658 let result = evaluator.evaluate(&expr, &data).unwrap();
10659 assert_eq!(result, JValue::Bool(false));
10660 }
10661
10662 #[test]
10663 fn test_unary_operations() {
10664 let mut evaluator = Evaluator::new();
10665 let data = JValue::Null;
10666
10667 let expr = AstNode::Unary {
10669 op: UnaryOp::Negate,
10670 operand: Box::new(AstNode::number(5.0)),
10671 };
10672 let result = evaluator.evaluate(&expr, &data).unwrap();
10673 assert_eq!(result, JValue::Number(-5.0));
10674
10675 let expr = AstNode::Unary {
10677 op: UnaryOp::Not,
10678 operand: Box::new(AstNode::boolean(true)),
10679 };
10680 let result = evaluator.evaluate(&expr, &data).unwrap();
10681 assert_eq!(result, JValue::Bool(false));
10682 }
10683
10684 #[test]
10685 fn test_array_construction() {
10686 let mut evaluator = Evaluator::new();
10687 let data = JValue::Null;
10688
10689 let expr = AstNode::Array(vec![
10690 AstNode::number(1.0),
10691 AstNode::number(2.0),
10692 AstNode::number(3.0),
10693 ]);
10694 let result = evaluator.evaluate(&expr, &data).unwrap();
10695 assert_eq!(result, JValue::from(serde_json::json!([1, 2, 3])));
10697 }
10698
10699 #[test]
10700 fn test_object_construction() {
10701 let mut evaluator = Evaluator::new();
10702 let data = JValue::Null;
10703
10704 let expr = AstNode::Object(vec![
10705 (AstNode::string("name"), AstNode::string("Alice")),
10706 (AstNode::string("age"), AstNode::number(30.0)),
10707 ]);
10708 let result = evaluator.evaluate(&expr, &data).unwrap();
10709 let mut expected = IndexMap::new();
10711 expected.insert("name".to_string(), JValue::string("Alice"));
10712 expected.insert("age".to_string(), JValue::Number(30.0));
10713 assert_eq!(result, JValue::object(expected));
10714 }
10715
10716 #[test]
10717 fn test_conditional() {
10718 let mut evaluator = Evaluator::new();
10719 let data = JValue::Null;
10720
10721 let expr = AstNode::Conditional {
10723 condition: Box::new(AstNode::boolean(true)),
10724 then_branch: Box::new(AstNode::string("yes")),
10725 else_branch: Some(Box::new(AstNode::string("no"))),
10726 };
10727 let result = evaluator.evaluate(&expr, &data).unwrap();
10728 assert_eq!(result, JValue::string("yes"));
10729
10730 let expr = AstNode::Conditional {
10732 condition: Box::new(AstNode::boolean(false)),
10733 then_branch: Box::new(AstNode::string("yes")),
10734 else_branch: Some(Box::new(AstNode::string("no"))),
10735 };
10736 let result = evaluator.evaluate(&expr, &data).unwrap();
10737 assert_eq!(result, JValue::string("no"));
10738
10739 let expr = AstNode::Conditional {
10741 condition: Box::new(AstNode::boolean(false)),
10742 then_branch: Box::new(AstNode::string("yes")),
10743 else_branch: None,
10744 };
10745 let result = evaluator.evaluate(&expr, &data).unwrap();
10746 assert_eq!(result, JValue::Undefined);
10747 }
10748
10749 #[test]
10750 fn test_block_expression() {
10751 let mut evaluator = Evaluator::new();
10752 let data = JValue::Null;
10753
10754 let expr = AstNode::Block(vec![
10755 AstNode::number(1.0),
10756 AstNode::number(2.0),
10757 AstNode::number(3.0),
10758 ]);
10759 let result = evaluator.evaluate(&expr, &data).unwrap();
10760 assert_eq!(result, JValue::from(3i64));
10762 }
10763
10764 #[test]
10765 fn test_function_calls() {
10766 let mut evaluator = Evaluator::new();
10767 let data = JValue::Null;
10768
10769 let expr = AstNode::Function {
10771 name: "uppercase".to_string(),
10772 args: vec![AstNode::string("hello")],
10773 is_builtin: true,
10774 };
10775 let result = evaluator.evaluate(&expr, &data).unwrap();
10776 assert_eq!(result, JValue::string("HELLO"));
10777
10778 let expr = AstNode::Function {
10780 name: "lowercase".to_string(),
10781 args: vec![AstNode::string("HELLO")],
10782 is_builtin: true,
10783 };
10784 let result = evaluator.evaluate(&expr, &data).unwrap();
10785 assert_eq!(result, JValue::string("hello"));
10786
10787 let expr = AstNode::Function {
10789 name: "length".to_string(),
10790 args: vec![AstNode::string("hello")],
10791 is_builtin: true,
10792 };
10793 let result = evaluator.evaluate(&expr, &data).unwrap();
10794 assert_eq!(result, JValue::from(5i64));
10795
10796 let expr = AstNode::Function {
10798 name: "sum".to_string(),
10799 args: vec![AstNode::Array(vec![
10800 AstNode::number(1.0),
10801 AstNode::number(2.0),
10802 AstNode::number(3.0),
10803 ])],
10804 is_builtin: true,
10805 };
10806 let result = evaluator.evaluate(&expr, &data).unwrap();
10807 assert_eq!(result, JValue::Number(6.0));
10808
10809 let expr = AstNode::Function {
10811 name: "count".to_string(),
10812 args: vec![AstNode::Array(vec![
10813 AstNode::number(1.0),
10814 AstNode::number(2.0),
10815 AstNode::number(3.0),
10816 ])],
10817 is_builtin: true,
10818 };
10819 let result = evaluator.evaluate(&expr, &data).unwrap();
10820 assert_eq!(result, JValue::from(3i64));
10821 }
10822
10823 #[test]
10824 fn test_complex_nested_data() {
10825 let mut evaluator = Evaluator::new();
10826 let data = JValue::from(serde_json::json!({
10827 "users": [
10828 {"name": "Alice", "age": 30},
10829 {"name": "Bob", "age": 25},
10830 {"name": "Charlie", "age": 35}
10831 ],
10832 "metadata": {
10833 "total": 3,
10834 "version": "1.0"
10835 }
10836 }));
10837 let path = AstNode::Path {
10839 steps: vec![
10840 PathStep::new(AstNode::Name("metadata".to_string())),
10841 PathStep::new(AstNode::Name("version".to_string())),
10842 ],
10843 };
10844 let result = evaluator.evaluate(&path, &data).unwrap();
10845 assert_eq!(result, JValue::string("1.0"));
10846 }
10847
10848 #[test]
10849 fn test_error_handling() {
10850 let mut evaluator = Evaluator::new();
10851 let data = JValue::Null;
10852
10853 let expr = AstNode::Binary {
10855 op: BinaryOp::Add,
10856 lhs: Box::new(AstNode::string("hello")),
10857 rhs: Box::new(AstNode::number(5.0)),
10858 };
10859 let result = evaluator.evaluate(&expr, &data);
10860 assert!(result.is_err());
10861
10862 let expr = AstNode::Function {
10864 name: "undefined_function".to_string(),
10865 args: vec![],
10866 is_builtin: false,
10867 };
10868 let result = evaluator.evaluate(&expr, &data);
10869 assert!(result.is_err());
10870 }
10871
10872 #[test]
10873 fn test_truthiness() {
10874 let evaluator = Evaluator::new();
10875
10876 assert!(!evaluator.is_truthy(&JValue::Null));
10877 assert!(!evaluator.is_truthy(&JValue::Bool(false)));
10878 assert!(evaluator.is_truthy(&JValue::Bool(true)));
10879 assert!(!evaluator.is_truthy(&JValue::from(0i64)));
10880 assert!(evaluator.is_truthy(&JValue::from(1i64)));
10881 assert!(!evaluator.is_truthy(&JValue::string("")));
10882 assert!(evaluator.is_truthy(&JValue::string("hello")));
10883 assert!(!evaluator.is_truthy(&JValue::array(vec![])));
10884 assert!(evaluator.is_truthy(&JValue::from(serde_json::json!([1, 2, 3]))));
10885 }
10886
10887 #[test]
10888 fn test_integration_with_parser() {
10889 use crate::parser::parse;
10890
10891 let mut evaluator = Evaluator::new();
10892 let data = JValue::from(serde_json::json!({
10893 "price": 10,
10894 "quantity": 5
10895 }));
10896 let ast = parse("price").unwrap();
10898 let result = evaluator.evaluate(&ast, &data).unwrap();
10899 assert_eq!(result, JValue::from(10i64));
10900
10901 let ast = parse("price * quantity").unwrap();
10903 let result = evaluator.evaluate(&ast, &data).unwrap();
10904 assert_eq!(result, JValue::Number(50.0));
10906
10907 let ast = parse("price > 5").unwrap();
10909 let result = evaluator.evaluate(&ast, &data).unwrap();
10910 assert_eq!(result, JValue::Bool(true));
10911 }
10912
10913 #[test]
10914 fn test_evaluate_dollar_function_uppercase() {
10915 use crate::parser::parse;
10916
10917 let mut evaluator = Evaluator::new();
10918 let ast = parse(r#"$uppercase("hello")"#).unwrap();
10919 let empty = JValue::object(IndexMap::new());
10920 let result = evaluator.evaluate(&ast, &empty).unwrap();
10921 assert_eq!(result, JValue::string("HELLO"));
10922 }
10923
10924 #[test]
10925 fn test_evaluate_dollar_function_sum() {
10926 use crate::parser::parse;
10927
10928 let mut evaluator = Evaluator::new();
10929 let ast = parse("$sum([1, 2, 3, 4, 5])").unwrap();
10930 let empty = JValue::object(IndexMap::new());
10931 let result = evaluator.evaluate(&ast, &empty).unwrap();
10932 assert_eq!(result, JValue::Number(15.0));
10933 }
10934
10935 #[test]
10936 fn test_evaluate_nested_dollar_functions() {
10937 use crate::parser::parse;
10938
10939 let mut evaluator = Evaluator::new();
10940 let ast = parse(r#"$length($lowercase("HELLO"))"#).unwrap();
10941 let empty = JValue::object(IndexMap::new());
10942 let result = evaluator.evaluate(&ast, &empty).unwrap();
10943 assert_eq!(result, JValue::Number(5.0));
10945 }
10946
10947 #[test]
10948 fn test_array_mapping() {
10949 use crate::parser::parse;
10950
10951 let mut evaluator = Evaluator::new();
10952 let data: JValue = serde_json::from_str(
10953 r#"{
10954 "products": [
10955 {"id": 1, "name": "Laptop", "price": 999.99},
10956 {"id": 2, "name": "Mouse", "price": 29.99},
10957 {"id": 3, "name": "Keyboard", "price": 79.99}
10958 ]
10959 }"#,
10960 )
10961 .map(|v: serde_json::Value| JValue::from(v))
10962 .unwrap();
10963
10964 let ast = parse("products.name").unwrap();
10966 let result = evaluator.evaluate(&ast, &data).unwrap();
10967 assert_eq!(
10968 result,
10969 JValue::array(vec![
10970 JValue::string("Laptop"),
10971 JValue::string("Mouse"),
10972 JValue::string("Keyboard")
10973 ])
10974 );
10975
10976 let ast = parse("products.price").unwrap();
10978 let result = evaluator.evaluate(&ast, &data).unwrap();
10979 assert_eq!(
10980 result,
10981 JValue::array(vec![
10982 JValue::Number(999.99),
10983 JValue::Number(29.99),
10984 JValue::Number(79.99)
10985 ])
10986 );
10987
10988 let ast = parse("$sum(products.price)").unwrap();
10990 let result = evaluator.evaluate(&ast, &data).unwrap();
10991 assert_eq!(result, JValue::Number(1109.97));
10992 }
10993
10994 #[test]
10995 fn test_empty_brackets() {
10996 use crate::parser::parse;
10997
10998 let mut evaluator = Evaluator::new();
10999
11000 let data: JValue = JValue::from(serde_json::json!({"foo": "bar"}));
11002 let ast = parse("foo[]").unwrap();
11003 let result = evaluator.evaluate(&ast, &data).unwrap();
11004 assert_eq!(
11005 result,
11006 JValue::array(vec![JValue::string("bar")]),
11007 "Empty brackets should wrap value in array"
11008 );
11009
11010 let data2: JValue = JValue::from(serde_json::json!({"arr": [1, 2, 3]}));
11012 let ast2 = parse("arr[]").unwrap();
11013 let result2 = evaluator.evaluate(&ast2, &data2).unwrap();
11014 assert_eq!(
11015 result2,
11016 JValue::array(vec![
11017 JValue::Number(1.0),
11018 JValue::Number(2.0),
11019 JValue::Number(3.0)
11020 ]),
11021 "Empty brackets should preserve array"
11022 );
11023 }
11024}