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 next_lambda_id: u64,
2434}
2435
2436impl Evaluator {
2437 pub fn new() -> Self {
2438 Evaluator {
2439 context: Context::new(),
2440 recursion_depth: 0,
2441 max_recursion_depth: 302,
2444 next_lambda_id: 0,
2445 }
2446 }
2447
2448 pub fn with_context(context: Context) -> Self {
2449 Evaluator {
2450 context,
2451 recursion_depth: 0,
2452 max_recursion_depth: 302,
2453 next_lambda_id: 0,
2454 }
2455 }
2456
2457 fn fresh_lambda_id(&mut self) -> u64 {
2459 let id = self.next_lambda_id;
2460 self.next_lambda_id += 1;
2461 id
2462 }
2463
2464 fn invoke_stored_lambda(
2468 &mut self,
2469 stored: &StoredLambda,
2470 args: &[JValue],
2471 data: &JValue,
2472 ) -> Result<JValue, EvaluatorError> {
2473 if let Some(ref ce) = stored.compiled_body {
2477 if stored.signature.is_none()
2478 && !stored.thunk
2479 && !stored
2480 .captured_env
2481 .values()
2482 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
2483 {
2484 let call_data = stored.captured_data.as_ref().unwrap_or(data);
2485 let vars: HashMap<&str, &JValue> = stored
2486 .params
2487 .iter()
2488 .zip(args.iter())
2489 .map(|(p, v)| (p.as_str(), v))
2490 .chain(stored.captured_env.iter().map(|(k, v)| (k.as_str(), v)))
2491 .collect();
2492 return eval_compiled(ce, call_data, Some(&vars));
2493 }
2494 }
2495
2496 let captured_env = if stored.captured_env.is_empty() {
2497 None
2498 } else {
2499 Some(&stored.captured_env)
2500 };
2501 let captured_data = stored.captured_data.as_ref();
2502 self.invoke_lambda_with_env(
2503 &stored.params,
2504 &stored.body,
2505 stored.signature.as_ref(),
2506 args,
2507 data,
2508 captured_env,
2509 captured_data,
2510 stored.thunk,
2511 )
2512 }
2513
2514 fn lookup_lambda_from_value(&self, value: &JValue) -> Option<StoredLambda> {
2518 if let JValue::Lambda { lambda_id, .. } = value {
2519 return self.context.lookup_lambda(lambda_id).cloned();
2520 }
2521 None
2522 }
2523
2524 fn get_callback_param_count(&self, func_node: &AstNode) -> usize {
2528 match func_node {
2529 AstNode::Lambda { params, .. } => params.len(),
2530 AstNode::Variable(var_name) => {
2531 if let Some(stored_lambda) = self.context.lookup_lambda(var_name) {
2533 return stored_lambda.params.len();
2534 }
2535 if let Some(value) = self.context.lookup(var_name) {
2537 if let Some(stored_lambda) = self.lookup_lambda_from_value(value) {
2538 return stored_lambda.params.len();
2539 }
2540 }
2541 usize::MAX
2543 }
2544 AstNode::Function { .. } => {
2545 usize::MAX
2548 }
2549 _ => usize::MAX,
2550 }
2551 }
2552
2553 fn merge_sort_specialized(arr: &mut [JValue], spec: &SpecializedSortComparator) {
2557 if arr.len() <= 1 {
2558 return;
2559 }
2560
2561 let keys: Vec<SortKey> = arr
2563 .iter()
2564 .map(|item| match item {
2565 JValue::Object(obj) => match obj.get(&spec.field) {
2566 Some(JValue::Number(n)) => SortKey::Num(*n),
2567 Some(JValue::String(s)) => SortKey::Str(s.clone()),
2568 _ => SortKey::None,
2569 },
2570 _ => SortKey::None,
2571 })
2572 .collect();
2573
2574 let mut perm: Vec<usize> = (0..arr.len()).collect();
2576 perm.sort_by(|&a, &b| compare_sort_keys(&keys[a], &keys[b], spec.descending));
2577
2578 let mut placed = vec![false; arr.len()];
2580 for i in 0..arr.len() {
2581 if placed[i] || perm[i] == i {
2582 continue;
2583 }
2584 let mut j = i;
2585 loop {
2586 let target = perm[j];
2587 placed[j] = true;
2588 if target == i {
2589 break;
2590 }
2591 arr.swap(j, target);
2592 j = target;
2593 }
2594 }
2595 }
2596
2597 fn merge_sort_with_comparator(
2601 &mut self,
2602 arr: &mut [JValue],
2603 comparator: &AstNode,
2604 data: &JValue,
2605 ) -> Result<(), EvaluatorError> {
2606 if arr.len() <= 1 {
2607 return Ok(());
2608 }
2609
2610 if let AstNode::Lambda { params, body, .. } = comparator {
2613 if params.len() >= 2 {
2614 if let Some(spec) = try_specialize_sort_comparator(body, ¶ms[0], ¶ms[1]) {
2615 Self::merge_sort_specialized(arr, &spec);
2616 return Ok(());
2617 }
2618 }
2619 }
2620
2621 let mid = arr.len() / 2;
2622
2623 self.merge_sort_with_comparator(&mut arr[..mid], comparator, data)?;
2625
2626 self.merge_sort_with_comparator(&mut arr[mid..], comparator, data)?;
2628
2629 let mut temp = Vec::with_capacity(arr.len());
2631 let (left, right) = arr.split_at(mid);
2632
2633 let mut i = 0;
2634 let mut j = 0;
2635
2636 if let AstNode::Lambda { params, body, .. } = comparator {
2639 if params.len() >= 2 {
2640 let param0 = params[0].clone();
2642 let param1 = params[1].clone();
2643 self.context.push_scope();
2644 while i < left.len() && j < right.len() {
2645 self.context.clear_current_scope();
2647 self.context.bind(param0.clone(), left[i].clone());
2648 self.context.bind(param1.clone(), right[j].clone());
2649
2650 let cmp_result = self.evaluate_internal(body, data)?;
2651
2652 if self.is_truthy(&cmp_result) {
2653 temp.push(right[j].clone());
2654 j += 1;
2655 } else {
2656 temp.push(left[i].clone());
2657 i += 1;
2658 }
2659 }
2660 self.context.pop_scope();
2661 } else {
2662 while i < left.len() && j < right.len() {
2664 let cmp_result = self.apply_function(
2665 comparator,
2666 &[left[i].clone(), right[j].clone()],
2667 data,
2668 )?;
2669 if self.is_truthy(&cmp_result) {
2670 temp.push(right[j].clone());
2671 j += 1;
2672 } else {
2673 temp.push(left[i].clone());
2674 i += 1;
2675 }
2676 }
2677 }
2678 } else {
2679 while i < left.len() && j < right.len() {
2681 let cmp_result =
2682 self.apply_function(comparator, &[left[i].clone(), right[j].clone()], data)?;
2683 if self.is_truthy(&cmp_result) {
2684 temp.push(right[j].clone());
2685 j += 1;
2686 } else {
2687 temp.push(left[i].clone());
2688 i += 1;
2689 }
2690 }
2691 }
2692
2693 temp.extend_from_slice(&left[i..]);
2695 temp.extend_from_slice(&right[j..]);
2696
2697 for (i, val) in temp.into_iter().enumerate() {
2699 arr[i] = val;
2700 }
2701
2702 Ok(())
2703 }
2704
2705 pub fn evaluate(&mut self, node: &AstNode, data: &JValue) -> Result<JValue, EvaluatorError> {
2710 if self.context.get_parent().is_none() {
2712 self.context.set_parent(data.clone());
2713 }
2714
2715 self.evaluate_internal(node, data)
2716 }
2717
2718 #[inline(always)]
2722 fn evaluate_leaf(
2723 &mut self,
2724 node: &AstNode,
2725 data: &JValue,
2726 ) -> Option<Result<JValue, EvaluatorError>> {
2727 match node {
2728 AstNode::String(s) => Some(Ok(JValue::string(s.clone()))),
2729 AstNode::Number(n) => {
2730 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2731 Some(Ok(JValue::from(*n as i64)))
2732 } else {
2733 Some(Ok(JValue::Number(*n)))
2734 }
2735 }
2736 AstNode::Boolean(b) => Some(Ok(JValue::Bool(*b))),
2737 AstNode::Null => Some(Ok(JValue::Null)),
2738 AstNode::Undefined => Some(Ok(JValue::Undefined)),
2739 AstNode::Name(field_name) => match data {
2740 JValue::Object(obj) => Some(Ok(obj
2742 .get(field_name)
2743 .cloned()
2744 .unwrap_or(JValue::Undefined))),
2745 _ => None,
2746 },
2747 AstNode::Variable(name) if !name.is_empty() => {
2748 if let JValue::Object(obj) = data {
2750 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2751 return None; }
2753 }
2754 self.context.lookup(name).map(|value| Ok(value.clone()))
2756 }
2757 _ => None,
2758 }
2759 }
2760
2761 fn evaluate_internal(
2763 &mut self,
2764 node: &AstNode,
2765 data: &JValue,
2766 ) -> Result<JValue, EvaluatorError> {
2767 if let Some(result) = self.evaluate_leaf(node, data) {
2769 return result;
2770 }
2771
2772 self.recursion_depth += 1;
2774 if self.recursion_depth > self.max_recursion_depth {
2775 self.recursion_depth -= 1;
2776 return Err(EvaluatorError::EvaluationError(format!(
2777 "U1001: Stack overflow - maximum recursion depth ({}) exceeded",
2778 self.max_recursion_depth
2779 )));
2780 }
2781
2782 const RED_ZONE: usize = 128 * 1024;
2790 const GROW_STACK_SIZE: usize = 8 * 1024 * 1024;
2791 let result = stacker::maybe_grow(RED_ZONE, GROW_STACK_SIZE, || {
2792 self.evaluate_internal_impl(node, data)
2793 });
2794
2795 self.recursion_depth -= 1;
2796 result
2797 }
2798
2799 fn evaluate_internal_impl(
2801 &mut self,
2802 node: &AstNode,
2803 data: &JValue,
2804 ) -> Result<JValue, EvaluatorError> {
2805 match node {
2806 AstNode::String(s) => Ok(JValue::string(s.clone())),
2807
2808 AstNode::Name(field_name) => {
2810 match data {
2811 JValue::Object(obj) => {
2812 Ok(obj.get(field_name).cloned().unwrap_or(JValue::Undefined))
2813 }
2814 JValue::Array(arr) => {
2815 let mut result = Vec::new();
2817 for item in arr.iter() {
2818 if let JValue::Object(obj) = item {
2819 if let Some(val) = obj.get(field_name) {
2820 result.push(val.clone());
2821 }
2822 }
2823 }
2824 if result.is_empty() {
2825 Ok(JValue::Undefined)
2826 } else if result.len() == 1 {
2827 Ok(result.into_iter().next().unwrap())
2828 } else {
2829 Ok(JValue::array(result))
2830 }
2831 }
2832 _ => Ok(JValue::Undefined),
2833 }
2834 }
2835
2836 AstNode::Number(n) => {
2837 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2839 Ok(JValue::from(*n as i64))
2841 } else {
2842 Ok(JValue::Number(*n))
2843 }
2844 }
2845 AstNode::Boolean(b) => Ok(JValue::Bool(*b)),
2846 AstNode::Null => Ok(JValue::Null),
2847 AstNode::Undefined => Ok(JValue::Undefined),
2848 AstNode::Placeholder => {
2849 Err(EvaluatorError::EvaluationError(
2852 "Placeholder '?' can only be used as a function argument".to_string(),
2853 ))
2854 }
2855 AstNode::Regex { pattern, flags } => {
2856 Ok(JValue::regex(pattern.as_str(), flags.as_str()))
2859 }
2860
2861 AstNode::Variable(name) => {
2862 if name.is_empty() {
2866 if let Some(value) = self.context.lookup("$") {
2867 return Ok(value.clone());
2868 }
2869 if let JValue::Object(obj) = data {
2871 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2872 if let Some(inner) = obj.get("@") {
2873 return Ok(inner.clone());
2874 }
2875 }
2876 }
2877 return Ok(data.clone());
2878 }
2879
2880 if let Some(value) = self.context.lookup(name) {
2884 return Ok(value.clone());
2885 }
2886
2887 if let JValue::Object(obj) = data {
2890 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2891 let binding_key = format!("${}", name);
2893 if let Some(binding_value) = obj.get(&binding_key) {
2894 return Ok(binding_value.clone());
2895 }
2896 }
2897 }
2898
2899 if let Some(stored_lambda) = self.context.lookup_lambda(name) {
2901 let lambda_repr = JValue::lambda(
2905 name.as_str(),
2906 stored_lambda.params.clone(),
2907 Some(name.to_string()),
2908 stored_lambda.signature.clone(),
2909 );
2910 return Ok(lambda_repr);
2911 }
2912
2913 if self.is_builtin_function(name) {
2915 let builtin_repr = JValue::builtin(name.as_str());
2918 return Ok(builtin_repr);
2919 }
2920
2921 Ok(JValue::Null)
2925 }
2926
2927 AstNode::ParentVariable(name) => {
2928 if name.is_empty() {
2930 return self.context.get_parent().cloned().ok_or_else(|| {
2931 EvaluatorError::ReferenceError("Parent context not available".to_string())
2932 });
2933 }
2934
2935 let parent_data = self.context.get_parent().ok_or_else(|| {
2938 EvaluatorError::ReferenceError("Parent context not available".to_string())
2939 })?;
2940
2941 match parent_data {
2943 JValue::Object(obj) => Ok(obj.get(name).cloned().unwrap_or(JValue::Null)),
2944 _ => Ok(JValue::Null),
2945 }
2946 }
2947
2948 AstNode::Path { steps } => self.evaluate_path(steps, data),
2949
2950 AstNode::Binary { op, lhs, rhs } => self.evaluate_binary_op(*op, lhs, rhs, data),
2951
2952 AstNode::Unary { op, operand } => self.evaluate_unary_op(*op, operand, data),
2953
2954 AstNode::Array(elements) => {
2956 let mut result = Vec::with_capacity(elements.len());
2960 for element in elements {
2961 let is_array_constructor = matches!(element, AstNode::Array(_));
2963
2964 let value = self.evaluate_internal(element, data)?;
2965
2966 if value.is_undefined() {
2969 continue;
2970 }
2971
2972 if is_array_constructor {
2973 result.push(value);
2975 } else if let JValue::Array(arr) = value {
2976 result.extend(arr.iter().cloned());
2978 } else {
2979 result.push(value);
2981 }
2982 }
2983 Ok(JValue::array(result))
2984 }
2985
2986 AstNode::Object(pairs) => {
2987 let mut result = IndexMap::with_capacity(pairs.len());
2988
2989 let all_literal_keys = pairs.iter().all(|(k, _)| matches!(k, AstNode::String(_)));
2991
2992 if all_literal_keys {
2993 for (key_node, value_node) in pairs.iter() {
2995 let key = match key_node {
2996 AstNode::String(s) => s,
2997 _ => unreachable!(),
2998 };
2999 let value = self.evaluate_internal(value_node, data)?;
3000 if value.is_undefined() {
3001 continue;
3002 }
3003 result.insert(key.clone(), value);
3004 }
3005 } else {
3006 let mut key_sources: HashMap<String, usize> = HashMap::new();
3007 for (pair_index, (key_node, value_node)) in pairs.iter().enumerate() {
3008 let key = match self.evaluate_internal(key_node, data)? {
3009 JValue::String(s) => s,
3010 JValue::Null => continue,
3011 other => {
3012 if other.is_undefined() {
3013 continue;
3014 }
3015 return Err(EvaluatorError::TypeError(format!(
3016 "Object key must be a string, got: {:?}",
3017 other
3018 )));
3019 }
3020 };
3021
3022 if let Some(&existing_idx) = key_sources.get(&*key) {
3023 if existing_idx != pair_index {
3024 return Err(EvaluatorError::EvaluationError(format!(
3025 "D1009: Multiple key expressions evaluate to same key: {}",
3026 key
3027 )));
3028 }
3029 }
3030 key_sources.insert(key.to_string(), pair_index);
3031
3032 let value = self.evaluate_internal(value_node, data)?;
3033 if value.is_undefined() {
3034 continue;
3035 }
3036 result.insert(key.to_string(), value);
3037 }
3038 }
3039 Ok(JValue::object(result))
3040 }
3041
3042 AstNode::ObjectTransform { input, pattern } => {
3044 let input_value = self.evaluate_internal(input, data)?;
3046
3047 if input_value.is_undefined() {
3049 return Ok(JValue::Undefined);
3050 }
3051
3052 let items: Vec<JValue> = match input_value {
3054 JValue::Array(ref arr) => (**arr).clone(),
3055 JValue::Null => return Ok(JValue::Null),
3056 other => vec![other],
3057 };
3058
3059 let items = if items.is_empty() {
3061 vec![JValue::Undefined]
3062 } else {
3063 items
3064 };
3065
3066 let mut groups: HashMap<String, (Vec<JValue>, usize)> = HashMap::new();
3070
3071 let saved_dollar = self.context.lookup("$").cloned();
3073
3074 for item in &items {
3075 self.context.bind("$".to_string(), item.clone());
3077
3078 for (pair_index, (key_node, _value_node)) in pattern.iter().enumerate() {
3079 let key = match self.evaluate_internal(key_node, item)? {
3081 JValue::String(s) => s,
3082 JValue::Null => continue, other => {
3084 if other.is_undefined() {
3086 continue;
3087 }
3088 return Err(EvaluatorError::TypeError(format!(
3089 "T1003: Object key must be a string, got: {:?}",
3090 other
3091 )));
3092 }
3093 };
3094
3095 if let Some((existing_data, existing_idx)) = groups.get_mut(&*key) {
3097 if *existing_idx != pair_index {
3099 return Err(EvaluatorError::EvaluationError(format!(
3101 "D1009: Multiple key expressions evaluate to same key: {}",
3102 key
3103 )));
3104 }
3105 existing_data.push(item.clone());
3107 } else {
3108 groups.insert(key.to_string(), (vec![item.clone()], pair_index));
3110 }
3111 }
3112 }
3113
3114 let mut result = IndexMap::new();
3116
3117 for (key, (grouped_data, expr_index)) in groups {
3118 let (_key_node, value_node) = &pattern[expr_index];
3120
3121 let context = if grouped_data.len() == 1 {
3125 grouped_data.into_iter().next().unwrap()
3126 } else {
3127 JValue::array(grouped_data)
3128 };
3129
3130 self.context.bind("$".to_string(), context.clone());
3132
3133 let value = self.evaluate_internal(value_node, &context)?;
3135
3136 if !value.is_undefined() {
3138 result.insert(key, value);
3139 }
3140 }
3141
3142 if let Some(saved) = saved_dollar {
3144 self.context.bind("$".to_string(), saved);
3145 } else {
3146 self.context.unbind("$");
3147 }
3148
3149 Ok(JValue::object(result))
3150 }
3151
3152 AstNode::Function {
3153 name,
3154 args,
3155 is_builtin,
3156 } => self.evaluate_function_call(name, args, *is_builtin, data),
3157
3158 AstNode::Call { procedure, args } => {
3161 let callable = self.evaluate_internal(procedure, data)?;
3163
3164 if let Some(stored_lambda) = self.lookup_lambda_from_value(&callable) {
3166 let mut evaluated_args = Vec::with_capacity(args.len());
3167 for arg in args.iter() {
3168 evaluated_args.push(self.evaluate_internal(arg, data)?);
3169 }
3170 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
3171 }
3172
3173 Err(EvaluatorError::TypeError(format!(
3175 "Cannot call non-function value: {:?}",
3176 callable
3177 )))
3178 }
3179
3180 AstNode::Conditional {
3181 condition,
3182 then_branch,
3183 else_branch,
3184 } => {
3185 let condition_value = self.evaluate_internal(condition, data)?;
3186 if self.is_truthy(&condition_value) {
3187 self.evaluate_internal(then_branch, data)
3188 } else if let Some(else_branch) = else_branch {
3189 self.evaluate_internal(else_branch, data)
3190 } else {
3191 Ok(JValue::Undefined)
3194 }
3195 }
3196
3197 AstNode::Block(expressions) => {
3198 self.context.push_scope();
3200
3201 let mut result = JValue::Null;
3202 for expr in expressions {
3203 result = self.evaluate_internal(expr, data)?;
3204 }
3205
3206 let lambdas_to_keep = self.extract_lambda_ids(&result);
3209 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
3210
3211 Ok(result)
3212 }
3213
3214 AstNode::Lambda {
3216 params,
3217 body,
3218 signature,
3219 thunk,
3220 } => {
3221 let lambda_id = format!("__lambda_{}_{}", params.len(), self.fresh_lambda_id());
3222
3223 let compiled_body = if !thunk {
3224 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
3225 try_compile_expr_with_allowed_vars(body, &var_refs)
3226 } else {
3227 None
3228 };
3229 let stored_lambda = StoredLambda {
3230 params: params.clone(),
3231 body: (**body).clone(),
3232 compiled_body,
3233 signature: signature.clone(),
3234 captured_env: self.capture_environment_for(body, params),
3235 captured_data: Some(data.clone()),
3236 thunk: *thunk,
3237 };
3238 self.context.bind_lambda(lambda_id.clone(), stored_lambda);
3239
3240 let lambda_obj = JValue::lambda(
3241 lambda_id.as_str(),
3242 params.clone(),
3243 None::<String>,
3244 signature.clone(),
3245 );
3246
3247 Ok(lambda_obj)
3248 }
3249
3250 AstNode::Wildcard => {
3252 match data {
3253 JValue::Object(obj) => {
3254 let mut result = Vec::new();
3255 for value in obj.values() {
3256 match value {
3258 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3259 _ => result.push(value.clone()),
3260 }
3261 }
3262 Ok(JValue::array(result))
3263 }
3264 JValue::Array(arr) => {
3265 Ok(JValue::Array(arr.clone()))
3267 }
3268 _ => Ok(JValue::Null),
3269 }
3270 }
3271
3272 AstNode::Descendant => {
3274 let descendants = self.collect_descendants(data);
3275 if descendants.is_empty() {
3276 Ok(JValue::Null) } else {
3278 Ok(JValue::array(descendants))
3279 }
3280 }
3281
3282 AstNode::Predicate(_) => Err(EvaluatorError::EvaluationError(
3283 "Predicate can only be used in path expressions".to_string(),
3284 )),
3285
3286 AstNode::ArrayGroup(elements) => {
3288 let mut result = Vec::new();
3289 for element in elements {
3290 let value = self.evaluate_internal(element, data)?;
3291 result.push(value);
3292 }
3293 Ok(JValue::array(result))
3294 }
3295
3296 AstNode::FunctionApplication(_) => Err(EvaluatorError::EvaluationError(
3297 "Function application can only be used in path expressions".to_string(),
3298 )),
3299
3300 AstNode::Sort { input, terms } => {
3301 let value = self.evaluate_internal(input, data)?;
3302 self.evaluate_sort(&value, terms)
3303 }
3304
3305 AstNode::IndexBind { input, variable } => {
3307 let value = self.evaluate_internal(input, data)?;
3308
3309 match value {
3312 JValue::Array(arr) => {
3313 let mut result = Vec::new();
3315 for (idx, item) in arr.iter().enumerate() {
3316 let mut wrapper = IndexMap::new();
3318 wrapper.insert("@".to_string(), item.clone());
3319 wrapper.insert(format!("${}", variable), JValue::Number(idx as f64));
3320 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
3321 result.push(JValue::object(wrapper));
3322 }
3323 Ok(JValue::array(result))
3324 }
3325 other => {
3327 let mut wrapper = IndexMap::new();
3328 wrapper.insert("@".to_string(), other);
3329 wrapper.insert(format!("${}", variable), JValue::from(0i64));
3330 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
3331 Ok(JValue::object(wrapper))
3332 }
3333 }
3334 }
3335
3336 AstNode::Transform {
3338 location,
3339 update,
3340 delete,
3341 } => {
3342 if self.context.lookup("$").is_some() {
3344 self.execute_transform(location, update, delete.as_deref(), data)
3346 } else {
3347 let transform_lambda = StoredLambda {
3350 params: vec!["$".to_string()],
3351 body: AstNode::Transform {
3352 location: location.clone(),
3353 update: update.clone(),
3354 delete: delete.clone(),
3355 },
3356 compiled_body: None, signature: None,
3358 captured_env: HashMap::new(),
3359 captured_data: None, thunk: false,
3361 };
3362
3363 let lambda_name = format!("__transform_{}", self.fresh_lambda_id());
3365 self.context.bind_lambda(lambda_name, transform_lambda);
3366
3367 Ok(JValue::string("<lambda>"))
3369 }
3370 }
3371 }
3372 }
3373
3374 fn apply_stages(&mut self, value: JValue, stages: &[Stage]) -> Result<JValue, EvaluatorError> {
3378 let mut result = match value {
3380 JValue::Null => return Ok(JValue::Null), JValue::Array(_) => value,
3382 other => JValue::array(vec![other]),
3383 };
3384
3385 for stage in stages {
3386 match stage {
3387 Stage::Filter(predicate_expr) => {
3388 result = self.evaluate_predicate_as_stage(&result, predicate_expr)?;
3390 }
3391 }
3392 }
3393 Ok(result)
3394 }
3395
3396 fn is_filter_predicate(predicate: &AstNode) -> bool {
3399 match predicate {
3400 AstNode::Binary { op, .. } => matches!(
3401 op,
3402 BinaryOp::GreaterThan
3403 | BinaryOp::GreaterThanOrEqual
3404 | BinaryOp::LessThan
3405 | BinaryOp::LessThanOrEqual
3406 | BinaryOp::Equal
3407 | BinaryOp::NotEqual
3408 | BinaryOp::And
3409 | BinaryOp::Or
3410 | BinaryOp::In
3411 ),
3412 AstNode::Unary {
3413 op: crate::ast::UnaryOp::Not,
3414 ..
3415 } => true,
3416 _ => false,
3417 }
3418 }
3419
3420 fn evaluate_predicate_as_stage(
3424 &mut self,
3425 current: &JValue,
3426 predicate: &AstNode,
3427 ) -> Result<JValue, EvaluatorError> {
3428 if matches!(predicate, AstNode::Boolean(true)) {
3430 return match current {
3431 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
3432 JValue::Null => Ok(JValue::Null),
3433 other => Ok(JValue::array(vec![other.clone()])),
3434 };
3435 }
3436
3437 match current {
3438 JValue::Array(arr) => {
3439 if let AstNode::Number(n) = predicate {
3444 let is_array_of_arrays =
3446 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3447
3448 if !is_array_of_arrays {
3449 return self.array_index(current, &JValue::Number(*n));
3451 }
3452
3453 let mut result = Vec::new();
3455 for item in arr.iter() {
3456 match item {
3457 JValue::Array(_) => {
3458 let indexed = self.array_index(item, &JValue::Number(*n))?;
3459 if !indexed.is_null() && !indexed.is_undefined() {
3460 result.push(indexed);
3461 }
3462 }
3463 _ => {
3464 if *n == 0.0 {
3465 result.push(item.clone());
3466 }
3467 }
3468 }
3469 }
3470 return Ok(JValue::array(result));
3471 }
3472
3473 if Self::is_filter_predicate(predicate) {
3476 if let Some(compiled) = try_compile_expr(predicate) {
3478 let shape = arr.first().and_then(build_shape_cache);
3479 let mut filtered = Vec::with_capacity(arr.len());
3480 for item in arr.iter() {
3481 let result = if let Some(ref s) = shape {
3482 eval_compiled_shaped(&compiled, item, None, s)?
3483 } else {
3484 eval_compiled(&compiled, item, None)?
3485 };
3486 if compiled_is_truthy(&result) {
3487 filtered.push(item.clone());
3488 }
3489 }
3490 return Ok(JValue::array(filtered));
3491 }
3492 let mut filtered = Vec::new();
3494 for item in arr.iter() {
3495 let item_result = self.evaluate_internal(predicate, item)?;
3496 if self.is_truthy(&item_result) {
3497 filtered.push(item.clone());
3498 }
3499 }
3500 return Ok(JValue::array(filtered));
3501 }
3502
3503 match self.evaluate_internal(predicate, current) {
3508 Ok(JValue::Number(n)) => {
3509 let n_val = n;
3510 let is_array_of_arrays =
3511 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3512
3513 if !is_array_of_arrays {
3514 let pred_result = JValue::Number(n_val);
3515 return self.array_index(current, &pred_result);
3516 }
3517
3518 let mut result = Vec::new();
3520 let pred_result = JValue::Number(n_val);
3521 for item in arr.iter() {
3522 match item {
3523 JValue::Array(_) => {
3524 let indexed = self.array_index(item, &pred_result)?;
3525 if !indexed.is_null() && !indexed.is_undefined() {
3526 result.push(indexed);
3527 }
3528 }
3529 _ => {
3530 if n_val == 0.0 {
3531 result.push(item.clone());
3532 }
3533 }
3534 }
3535 }
3536 return Ok(JValue::array(result));
3537 }
3538 Ok(JValue::Array(indices)) => {
3539 let has_non_numeric =
3542 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
3543
3544 if has_non_numeric {
3545 } else {
3547 let arr_len = arr.len() as i64;
3549 let mut resolved_indices: Vec<i64> = indices
3550 .iter()
3551 .filter_map(|v| {
3552 if let JValue::Number(n) = v {
3553 let idx = *n as i64;
3554 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
3556 if actual_idx >= 0 && actual_idx < arr_len {
3558 Some(actual_idx)
3559 } else {
3560 None
3561 }
3562 } else {
3563 None
3564 }
3565 })
3566 .collect();
3567
3568 resolved_indices.sort();
3570 resolved_indices.dedup();
3571
3572 let result: Vec<JValue> = resolved_indices
3574 .iter()
3575 .map(|&idx| arr[idx as usize].clone())
3576 .collect();
3577
3578 return Ok(JValue::array(result));
3579 }
3580 }
3581 Ok(_) => {
3582 }
3585 Err(_) => {
3586 }
3589 }
3590
3591 let mut filtered = Vec::new();
3593 for item in arr.iter() {
3594 let item_result = self.evaluate_internal(predicate, item)?;
3595 if self.is_truthy(&item_result) {
3596 filtered.push(item.clone());
3597 }
3598 }
3599 Ok(JValue::array(filtered))
3600 }
3601 JValue::Null => {
3602 Ok(JValue::Null)
3604 }
3605 other => {
3606 if let AstNode::Number(n) = predicate {
3612 if *n == 0.0 {
3614 return Ok(other.clone());
3615 } else {
3616 return Ok(JValue::Null);
3617 }
3618 }
3619
3620 match self.evaluate_internal(predicate, other) {
3622 Ok(JValue::Number(n)) => {
3623 if n == 0.0 {
3625 Ok(other.clone())
3626 } else {
3627 Ok(JValue::Null)
3628 }
3629 }
3630 Ok(pred_result) => {
3631 if self.is_truthy(&pred_result) {
3633 Ok(other.clone())
3634 } else {
3635 Ok(JValue::Null)
3636 }
3637 }
3638 Err(e) => Err(e),
3639 }
3640 }
3641 }
3642 }
3643
3644 fn evaluate_path(
3646 &mut self,
3647 steps: &[PathStep],
3648 data: &JValue,
3649 ) -> Result<JValue, EvaluatorError> {
3650 if steps.is_empty() {
3652 return Ok(data.clone());
3653 }
3654
3655 if steps.len() == 1 {
3658 if let AstNode::Name(field_name) = &steps[0].node {
3659 return match data {
3660 JValue::Object(obj) => {
3661 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
3663 if let Some(JValue::Object(inner)) = obj.get("@") {
3664 Ok(inner.get(field_name).cloned().unwrap_or(JValue::Undefined))
3665 } else {
3666 Ok(JValue::Undefined)
3667 }
3668 } else {
3669 Ok(obj.get(field_name).cloned().unwrap_or(JValue::Undefined))
3670 }
3671 }
3672 JValue::Array(arr) => {
3673 let has_tuples = arr.first().is_some_and(|item| {
3677 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
3678 });
3679
3680 if !has_tuples {
3681 let mut result = Vec::with_capacity(arr.len());
3683 for item in arr.iter() {
3684 if let JValue::Object(obj) = item {
3685 if let Some(val) = obj.get(field_name) {
3686 if !val.is_null() {
3687 match val {
3688 JValue::Array(arr_val) => {
3689 result.extend(arr_val.iter().cloned());
3690 }
3691 other => result.push(other.clone()),
3692 }
3693 }
3694 }
3695 } else if let JValue::Array(inner_arr) = item {
3696 let nested_result = self.evaluate_path(
3697 &[PathStep::new(AstNode::Name(field_name.clone()))],
3698 &JValue::Array(inner_arr.clone()),
3699 )?;
3700 match nested_result {
3701 JValue::Array(nested) => {
3702 result.extend(nested.iter().cloned());
3703 }
3704 JValue::Null => {}
3705 other => result.push(other),
3706 }
3707 }
3708 }
3709
3710 if result.is_empty() {
3711 Ok(JValue::Null)
3712 } else if result.len() == 1 {
3713 Ok(result.into_iter().next().unwrap())
3714 } else {
3715 Ok(JValue::array(result))
3716 }
3717 } else {
3718 let mut result = Vec::new();
3720 for item in arr.iter() {
3721 match item {
3722 JValue::Object(obj) => {
3723 let is_tuple =
3724 obj.get("__tuple__") == Some(&JValue::Bool(true));
3725
3726 if is_tuple {
3727 let inner = match obj.get("@") {
3728 Some(JValue::Object(inner)) => inner,
3729 _ => continue,
3730 };
3731
3732 if let Some(val) = inner.get(field_name) {
3733 if !val.is_null() {
3734 let wrap = |v: JValue| -> JValue {
3736 let mut wrapper = IndexMap::new();
3737 wrapper.insert("@".to_string(), v);
3738 wrapper.insert(
3739 "__tuple__".to_string(),
3740 JValue::Bool(true),
3741 );
3742 for (k, v) in obj.iter() {
3743 if k.starts_with('$') {
3744 wrapper
3745 .insert(k.clone(), v.clone());
3746 }
3747 }
3748 JValue::object(wrapper)
3749 };
3750
3751 match val {
3752 JValue::Array(arr_val) => {
3753 for item in arr_val.iter() {
3754 result.push(wrap(item.clone()));
3755 }
3756 }
3757 other => result.push(wrap(other.clone())),
3758 }
3759 }
3760 }
3761 } else {
3762 if let Some(val) = obj.get(field_name) {
3764 if !val.is_null() {
3765 match val {
3766 JValue::Array(arr_val) => {
3767 for item in arr_val.iter() {
3768 result.push(item.clone());
3769 }
3770 }
3771 other => result.push(other.clone()),
3772 }
3773 }
3774 }
3775 }
3776 }
3777 JValue::Array(inner_arr) => {
3778 let nested_result = self.evaluate_path(
3780 &[PathStep::new(AstNode::Name(field_name.clone()))],
3781 &JValue::Array(inner_arr.clone()),
3782 )?;
3783 match nested_result {
3785 JValue::Array(nested) => {
3786 result.extend(nested.iter().cloned());
3788 }
3789 JValue::Null => {} other => result.push(other),
3791 }
3792 }
3793 _ => {} }
3795 }
3796
3797 if result.is_empty() {
3801 Ok(JValue::Null)
3802 } else if result.len() == 1 {
3803 Ok(result.into_iter().next().unwrap())
3804 } else {
3805 Ok(JValue::array(result))
3806 }
3807 } }
3809 _ => Ok(JValue::Null),
3810 };
3811 }
3812 }
3813
3814 if steps.len() == 2 && steps[0].stages.is_empty() && steps[1].stages.is_empty() {
3817 if let (AstNode::Variable(var_name), AstNode::Name(field_name)) =
3818 (&steps[0].node, &steps[1].node)
3819 {
3820 if !var_name.is_empty() {
3821 if let Some(value) = self.context.lookup(var_name) {
3822 match value {
3823 JValue::Object(obj) => {
3824 return Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null));
3825 }
3826 JValue::Array(arr) => {
3827 let mut result = Vec::with_capacity(arr.len());
3829 for item in arr.iter() {
3830 if let JValue::Object(obj) = item {
3831 if let Some(val) = obj.get(field_name) {
3832 if !val.is_null() {
3833 match val {
3834 JValue::Array(inner) => {
3835 result.extend(inner.iter().cloned());
3836 }
3837 other => result.push(other.clone()),
3838 }
3839 }
3840 }
3841 }
3842 }
3843 return match result.len() {
3844 0 => Ok(JValue::Null),
3845 1 => Ok(result.pop().unwrap()),
3846 _ => Ok(JValue::array(result)),
3847 };
3848 }
3849 _ => {} }
3851 }
3852 }
3853 }
3854 }
3855
3856 let mut did_array_mapping = false;
3858
3859 let mut current: JValue = match &steps[0].node {
3861 AstNode::Wildcard => {
3862 match data {
3864 JValue::Object(obj) => {
3865 let mut result = Vec::new();
3866 for value in obj.values() {
3867 match value {
3869 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3870 _ => result.push(value.clone()),
3871 }
3872 }
3873 JValue::array(result)
3874 }
3875 JValue::Array(arr) => JValue::Array(arr.clone()),
3876 _ => JValue::Null,
3877 }
3878 }
3879 AstNode::Descendant => {
3880 let descendants = self.collect_descendants(data);
3882 JValue::array(descendants)
3883 }
3884 AstNode::ParentVariable(name) => {
3885 let parent_data = self.context.get_parent().ok_or_else(|| {
3887 EvaluatorError::ReferenceError("Parent context not available".to_string())
3888 })?;
3889
3890 if name.is_empty() {
3891 parent_data.clone()
3893 } else {
3894 match parent_data {
3896 JValue::Object(obj) => obj.get(name).cloned().unwrap_or(JValue::Null),
3897 _ => JValue::Null,
3898 }
3899 }
3900 }
3901 AstNode::Name(field_name) => {
3902 let stages = &steps[0].stages;
3904
3905 match data {
3906 JValue::Object(obj) => {
3907 let val = obj.get(field_name).cloned().unwrap_or(JValue::Undefined);
3908 if !stages.is_empty() {
3910 self.apply_stages(val, stages)?
3911 } else {
3912 val
3913 }
3914 }
3915 JValue::Array(arr) => {
3916 let mut result = Vec::new();
3918 for item in arr.iter() {
3919 match item {
3920 JValue::Object(obj) => {
3921 let val =
3922 obj.get(field_name).cloned().unwrap_or(JValue::Undefined);
3923 if !val.is_null() && !val.is_undefined() {
3924 if !stages.is_empty() {
3925 let processed_val = self.apply_stages(val, stages)?;
3927 match processed_val {
3929 JValue::Array(arr) => {
3930 result.extend(arr.iter().cloned())
3931 }
3932 JValue::Null => {} other => result.push(other), }
3935 } else {
3936 match val {
3938 JValue::Array(arr) => {
3939 result.extend(arr.iter().cloned())
3940 }
3941 other => result.push(other),
3942 }
3943 }
3944 }
3945 }
3946 JValue::Array(inner_arr) => {
3947 let nested_result = self.evaluate_path(
3949 &[steps[0].clone()],
3950 &JValue::Array(inner_arr.clone()),
3951 )?;
3952 match nested_result {
3953 JValue::Array(nested) => {
3954 result.extend(nested.iter().cloned())
3955 }
3956 JValue::Null => {} other => result.push(other),
3958 }
3959 }
3960 _ => {} }
3962 }
3963 JValue::array(result)
3964 }
3965 JValue::Null => JValue::Null,
3966 _ => JValue::Undefined,
3968 }
3969 }
3970 AstNode::String(string_literal) => {
3971 let stages = &steps[0].stages;
3974 let val = JValue::string(string_literal.clone());
3975
3976 if !stages.is_empty() {
3977 let result = self.apply_stages(val, stages)?;
3979 match result {
3982 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
3983 JValue::Array(arr) if arr.is_empty() => JValue::Null,
3984 other => other,
3985 }
3986 } else {
3987 val
3988 }
3989 }
3990 AstNode::Predicate(pred_expr) => {
3991 self.evaluate_predicate(data, pred_expr)?
3993 }
3994 AstNode::IndexBind { .. } => {
3995 self.evaluate_internal(&steps[0].node, data)?
3997 }
3998 _ => {
3999 self.evaluate_path_step(&steps[0].node, data, data)?
4001 }
4002 };
4003
4004 for step in steps[1..].iter() {
4006 if current.is_null() {
4009 return Ok(JValue::Null);
4010 }
4011 if current.is_undefined() {
4012 return Ok(JValue::Undefined);
4013 }
4014
4015 let is_tuple_array = if let JValue::Array(arr) = ¤t {
4018 arr.first().is_some_and(|first| {
4019 if let JValue::Object(obj) = first {
4020 obj.get("__tuple__") == Some(&JValue::Bool(true))
4021 } else {
4022 false
4023 }
4024 })
4025 } else {
4026 false
4027 };
4028
4029 let needs_tuple_context_binding = is_tuple_array
4039 && matches!(
4040 &step.node,
4041 AstNode::Object(_) | AstNode::FunctionApplication(_) | AstNode::Variable(_)
4042 );
4043
4044 if needs_tuple_context_binding {
4045 if let JValue::Array(arr) = ¤t {
4046 let mut results = Vec::new();
4047
4048 for tuple in arr.iter() {
4049 if let JValue::Object(tuple_obj) = tuple {
4050 let bindings: Vec<(String, JValue)> = tuple_obj
4052 .iter()
4053 .filter(|(k, _)| k.starts_with('$') && k.len() > 1) .map(|(k, v)| (k[1..].to_string(), v.clone())) .collect();
4056
4057 let saved_bindings: Vec<(String, Option<JValue>)> = bindings
4059 .iter()
4060 .map(|(name, _)| (name.clone(), self.context.lookup(name).cloned()))
4061 .collect();
4062
4063 for (name, value) in &bindings {
4065 self.context.bind(name.clone(), value.clone());
4066 }
4067
4068 let actual_data = tuple_obj.get("@").cloned().unwrap_or(JValue::Null);
4070
4071 let step_result = match &step.node {
4073 AstNode::Variable(_) => {
4074 self.evaluate_internal(&step.node, tuple)?
4076 }
4077 AstNode::Object(_) | AstNode::FunctionApplication(_) => {
4078 self.evaluate_internal(&step.node, &actual_data)?
4080 }
4081 _ => unreachable!(), };
4083
4084 for (name, saved_value) in &saved_bindings {
4086 if let Some(value) = saved_value {
4087 self.context.bind(name.clone(), value.clone());
4088 } else {
4089 self.context.unbind(name);
4090 }
4091 }
4092
4093 if !step_result.is_null() && !step_result.is_undefined() {
4095 if matches!(&step.node, AstNode::Object(_)) {
4098 results.push(step_result);
4099 } else if matches!(step_result, JValue::Array(_)) {
4100 if let JValue::Array(arr) = step_result {
4101 results.extend(arr.iter().cloned());
4102 }
4103 } else {
4104 results.push(step_result);
4105 }
4106 }
4107 }
4108 }
4109
4110 current = JValue::array(results);
4111 continue; }
4113 }
4114
4115 current = match &step.node {
4116 AstNode::Wildcard => {
4117 let stages = &step.stages;
4119 let wildcard_result = match ¤t {
4120 JValue::Object(obj) => {
4121 let mut result = Vec::new();
4122 for value in obj.values() {
4123 match value {
4125 JValue::Array(arr) => result.extend(arr.iter().cloned()),
4126 _ => result.push(value.clone()),
4127 }
4128 }
4129 JValue::array(result)
4130 }
4131 JValue::Array(arr) => {
4132 let mut all_values = Vec::new();
4134 for item in arr.iter() {
4135 match item {
4136 JValue::Object(obj) => {
4137 for value in obj.values() {
4138 match value {
4140 JValue::Array(arr) => {
4141 all_values.extend(arr.iter().cloned())
4142 }
4143 _ => all_values.push(value.clone()),
4144 }
4145 }
4146 }
4147 JValue::Array(inner) => {
4148 all_values.extend(inner.iter().cloned());
4149 }
4150 _ => {}
4151 }
4152 }
4153 JValue::array(all_values)
4154 }
4155 _ => JValue::Null,
4156 };
4157
4158 if !stages.is_empty() {
4160 self.apply_stages(wildcard_result, stages)?
4161 } else {
4162 wildcard_result
4163 }
4164 }
4165 AstNode::Descendant => {
4166 match ¤t {
4168 JValue::Array(arr) => {
4169 let mut all_descendants = Vec::new();
4171 for item in arr.iter() {
4172 all_descendants.extend(self.collect_descendants(item));
4173 }
4174 JValue::array(all_descendants)
4175 }
4176 _ => {
4177 let descendants = self.collect_descendants(¤t);
4179 JValue::array(descendants)
4180 }
4181 }
4182 }
4183 AstNode::Name(field_name) => {
4184 let stages = &step.stages;
4186
4187 match ¤t {
4188 JValue::Object(obj) => {
4189 did_array_mapping = false;
4194 let val = obj.get(field_name).cloned().unwrap_or(JValue::Undefined);
4195 if !stages.is_empty() {
4197 self.apply_stages(val, stages)?
4198 } else {
4199 val
4200 }
4201 }
4202 JValue::Array(arr) => {
4203 did_array_mapping = true; let has_tuples = arr.first().is_some_and(|item| {
4211 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
4212 });
4213
4214 if !has_tuples && stages.is_empty() {
4215 let mut result = Vec::with_capacity(arr.len());
4216 for item in arr.iter() {
4217 match item {
4218 JValue::Object(obj) => {
4219 if let Some(val) = obj.get(field_name) {
4220 if !val.is_null() {
4221 match val {
4222 JValue::Array(arr_val) => {
4223 result.extend(arr_val.iter().cloned())
4224 }
4225 other => result.push(other.clone()),
4226 }
4227 }
4228 }
4229 }
4230 JValue::Array(_) => {
4231 let nested_result =
4232 self.evaluate_path(&[step.clone()], item)?;
4233 match nested_result {
4234 JValue::Array(nested) => {
4235 result.extend(nested.iter().cloned())
4236 }
4237 JValue::Null => {}
4238 other => result.push(other),
4239 }
4240 }
4241 _ => {}
4242 }
4243 }
4244 JValue::array(result)
4245 } else {
4246 let mut result = Vec::new();
4248
4249 for item in arr.iter() {
4250 match item {
4251 JValue::Object(obj) => {
4252 let (actual_obj, tuple_bindings) = if obj
4254 .get("__tuple__")
4255 == Some(&JValue::Bool(true))
4256 {
4257 if let Some(JValue::Object(inner)) = obj.get("@") {
4259 let bindings: Vec<(String, JValue)> = obj
4261 .iter()
4262 .filter(|(k, _)| k.starts_with('$'))
4263 .map(|(k, v)| (k.clone(), v.clone()))
4264 .collect();
4265 (inner.clone(), Some(bindings))
4266 } else {
4267 continue; }
4269 } else {
4270 (obj.clone(), None)
4271 };
4272
4273 let val = actual_obj
4274 .get(field_name)
4275 .cloned()
4276 .unwrap_or(JValue::Null);
4277
4278 if !val.is_null() {
4279 let wrap_in_tuple = |v: JValue, bindings: &Option<Vec<(String, JValue)>>| -> JValue {
4281 if let Some(b) = bindings {
4282 let mut wrapper = IndexMap::new();
4283 wrapper.insert("@".to_string(), v);
4284 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4285 for (k, val) in b {
4286 wrapper.insert(k.clone(), val.clone());
4287 }
4288 JValue::object(wrapper)
4289 } else {
4290 v
4291 }
4292 };
4293
4294 if !stages.is_empty() {
4295 let processed_val =
4297 self.apply_stages(val, stages)?;
4298 match processed_val {
4300 JValue::Array(arr) => {
4301 for item in arr.iter() {
4302 result.push(wrap_in_tuple(
4303 item.clone(),
4304 &tuple_bindings,
4305 ));
4306 }
4307 }
4308 JValue::Null => {} other => result.push(wrap_in_tuple(
4310 other,
4311 &tuple_bindings,
4312 )),
4313 }
4314 } else {
4315 match val {
4318 JValue::Array(arr) => {
4319 for item in arr.iter() {
4320 result.push(wrap_in_tuple(
4321 item.clone(),
4322 &tuple_bindings,
4323 ));
4324 }
4325 }
4326 other => result.push(wrap_in_tuple(
4327 other,
4328 &tuple_bindings,
4329 )),
4330 }
4331 }
4332 }
4333 }
4334 JValue::Array(_) => {
4335 let nested_result =
4337 self.evaluate_path(&[step.clone()], item)?;
4338 match nested_result {
4339 JValue::Array(nested) => {
4340 result.extend(nested.iter().cloned())
4341 }
4342 JValue::Null => {}
4343 other => result.push(other),
4344 }
4345 }
4346 _ => {}
4347 }
4348 }
4349
4350 JValue::array(result)
4351 }
4352 }
4353 JValue::Null => JValue::Null,
4354 _ => JValue::Undefined,
4356 }
4357 }
4358 AstNode::String(string_literal) => {
4359 let stages = &step.stages;
4361 let val = JValue::string(string_literal.clone());
4362
4363 if !stages.is_empty() {
4364 let result = self.apply_stages(val, stages)?;
4366 match result {
4368 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
4369 JValue::Array(arr) if arr.is_empty() => JValue::Null,
4370 other => other,
4371 }
4372 } else {
4373 val
4374 }
4375 }
4376 AstNode::Predicate(pred_expr) => {
4377 self.evaluate_predicate(¤t, pred_expr)?
4379 }
4380 AstNode::ArrayGroup(elements) => {
4381 match ¤t {
4384 JValue::Array(arr) => {
4385 let mut result = Vec::new();
4386 for item in arr.iter() {
4387 let mut group_values = Vec::new();
4389 for element in elements {
4390 let value = self.evaluate_internal(element, item)?;
4391 let should_preserve_array = matches!(
4394 element,
4395 AstNode::Array(_) | AstNode::ArrayGroup(_)
4396 );
4397
4398 if should_preserve_array {
4399 group_values.push(value);
4401 } else {
4402 match value {
4404 JValue::Array(arr) => {
4405 group_values.extend(arr.iter().cloned())
4406 }
4407 other => group_values.push(other),
4408 }
4409 }
4410 }
4411 result.push(JValue::array(group_values));
4413 }
4414 JValue::array(result)
4415 }
4416 _ => {
4417 let mut result = Vec::new();
4419 for element in elements {
4420 let value = self.evaluate_internal(element, ¤t)?;
4421 result.push(value);
4422 }
4423 JValue::array(result)
4424 }
4425 }
4426 }
4427 AstNode::FunctionApplication(expr) => {
4428 match ¤t {
4432 JValue::Array(arr) => {
4433 let mapped: Vec<JValue> = if let Some(compiled) = try_compile_expr(expr)
4437 {
4438 let shape = arr.first().and_then(build_shape_cache);
4439 let mut result = Vec::with_capacity(arr.len());
4440 for item in arr.iter() {
4441 let value = if let Some(ref s) = shape {
4442 eval_compiled_shaped(&compiled, item, None, s)?
4443 } else {
4444 eval_compiled(&compiled, item, None)?
4445 };
4446 if !value.is_null() && !value.is_undefined() {
4447 result.push(value);
4448 }
4449 }
4450 result
4451 } else {
4452 let mut result = Vec::new();
4453 for item in arr.iter() {
4454 let saved_dollar = self.context.lookup("$").cloned();
4456
4457 self.context.bind("$".to_string(), item.clone());
4459
4460 let value = self.evaluate_internal(expr, item)?;
4462
4463 if let Some(saved) = saved_dollar {
4465 self.context.bind("$".to_string(), saved);
4466 } else {
4467 self.context.unbind("$");
4468 }
4469
4470 if !value.is_null() && !value.is_undefined() {
4472 result.push(value);
4473 }
4474 }
4475 result
4476 };
4477 JValue::array(mapped)
4480 }
4481 _ => {
4482 let saved_dollar = self.context.lookup("$").cloned();
4484 self.context.bind("$".to_string(), current.clone());
4485
4486 let value = self.evaluate_internal(expr, ¤t)?;
4487
4488 if let Some(saved) = saved_dollar {
4489 self.context.bind("$".to_string(), saved);
4490 } else {
4491 self.context.unbind("$");
4492 }
4493
4494 value
4495 }
4496 }
4497 }
4498 AstNode::Sort { terms, .. } => {
4499 self.evaluate_sort(¤t, terms)?
4501 }
4502 AstNode::IndexBind { variable, .. } => {
4503 match ¤t {
4506 JValue::Array(arr) => {
4507 let mut result = Vec::new();
4508 for (idx, item) in arr.iter().enumerate() {
4509 let mut wrapper = IndexMap::new();
4510 wrapper.insert("@".to_string(), item.clone());
4511 wrapper
4512 .insert(format!("${}", variable), JValue::Number(idx as f64));
4513 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4514 result.push(JValue::object(wrapper));
4515 }
4516 JValue::array(result)
4517 }
4518 other => {
4519 let mut wrapper = IndexMap::new();
4521 wrapper.insert("@".to_string(), other.clone());
4522 wrapper.insert(format!("${}", variable), JValue::from(0i64));
4523 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4524 JValue::object(wrapper)
4525 }
4526 }
4527 }
4528 _ => self.evaluate_path_step(&step.node, ¤t, data)?,
4530 };
4531 }
4532
4533 let has_explicit_array_keep = steps.iter().any(|step| {
4541 if let AstNode::Predicate(pred) = &step.node {
4543 if matches!(**pred, AstNode::Boolean(true)) {
4544 return true;
4545 }
4546 }
4547 step.stages.iter().any(|stage| {
4549 let crate::ast::Stage::Filter(pred) = stage;
4550 matches!(**pred, AstNode::Boolean(true))
4551 })
4552 });
4553
4554 let should_unwrap = !has_explicit_array_keep
4564 && (steps.iter().any(|step| !step.stages.is_empty()) || did_array_mapping);
4565
4566 let result = match ¤t {
4567 JValue::Array(arr) if arr.is_empty() => JValue::Null,
4569 JValue::Array(arr) if arr.len() == 1 && should_unwrap => arr[0].clone(),
4571 _ => current,
4573 };
4574
4575 Ok(result)
4576 }
4577
4578 fn evaluate_path_step(
4580 &mut self,
4581 step: &AstNode,
4582 current: &JValue,
4583 original_data: &JValue,
4584 ) -> Result<JValue, EvaluatorError> {
4585 if matches!(current, JValue::Array(_)) && matches!(step, AstNode::Object(_)) {
4588 match (current, step) {
4589 (JValue::Array(arr), AstNode::Object(pairs)) => {
4590 if let Some(compiled) = try_compile_expr(&AstNode::Object(pairs.clone())) {
4592 let shape = arr.first().and_then(build_shape_cache);
4593 let mut mapped = Vec::with_capacity(arr.len());
4594 for item in arr.iter() {
4595 let result = if let Some(ref s) = shape {
4596 eval_compiled_shaped(&compiled, item, None, s)?
4597 } else {
4598 eval_compiled(&compiled, item, None)?
4599 };
4600 if !result.is_undefined() {
4601 mapped.push(result);
4602 }
4603 }
4604 return Ok(JValue::array(mapped));
4605 }
4606 let mapped: Result<Vec<JValue>, EvaluatorError> = arr
4608 .iter()
4609 .map(|item| self.evaluate_internal(step, item))
4610 .collect();
4611 Ok(JValue::array(mapped?))
4612 }
4613 _ => unreachable!(),
4614 }
4615 } else {
4616 if let AstNode::Variable(name) = step {
4619 if name.is_empty() {
4620 if let JValue::Array(arr) = current {
4622 return Ok(JValue::Array(arr.clone()));
4624 } else {
4625 return Ok(current.clone());
4627 }
4628 }
4629 }
4630
4631 if matches!(step, AstNode::Variable(_)) {
4635 if let JValue::Array(arr) = current {
4636 let is_tuple_array = arr.first().is_some_and(|first| {
4638 if let JValue::Object(obj) = first {
4639 obj.get("__tuple__") == Some(&JValue::Bool(true))
4640 } else {
4641 false
4642 }
4643 });
4644
4645 if is_tuple_array {
4646 let mut results = Vec::new();
4648 for tuple in arr.iter() {
4649 let val = self.evaluate_internal(step, tuple)?;
4652 if !val.is_null() && !val.is_undefined() {
4653 results.push(val);
4654 }
4655 }
4656 return Ok(JValue::array(results));
4657 }
4658 }
4659 }
4660
4661 if matches!(
4670 step,
4671 AstNode::Binary { .. }
4672 | AstNode::Function { .. }
4673 | AstNode::Variable(_)
4674 | AstNode::ParentVariable(_)
4675 | AstNode::Array(_)
4676 | AstNode::Object(_)
4677 | AstNode::Sort { .. }
4678 | AstNode::Block(_)
4679 ) {
4680 return self.evaluate_internal(step, original_data);
4682 }
4683
4684 let step_value = self.evaluate_internal(step, original_data)?;
4686 Ok(match (current, &step_value) {
4687 (JValue::Object(obj), JValue::String(key)) => {
4688 obj.get(&**key).cloned().unwrap_or(JValue::Undefined)
4689 }
4690 (JValue::Array(arr), JValue::Number(n)) => {
4691 let index = *n as i64;
4692 let len = arr.len() as i64;
4693
4694 let actual_idx = if index < 0 { len + index } else { index };
4696
4697 if actual_idx < 0 || actual_idx >= len {
4698 JValue::Undefined
4699 } else {
4700 arr[actual_idx as usize].clone()
4701 }
4702 }
4703 _ => JValue::Undefined,
4704 })
4705 }
4706 }
4707
4708 fn evaluate_binary_op(
4710 &mut self,
4711 op: crate::ast::BinaryOp,
4712 lhs: &AstNode,
4713 rhs: &AstNode,
4714 data: &JValue,
4715 ) -> Result<JValue, EvaluatorError> {
4716 use crate::ast::BinaryOp;
4717
4718 if op == BinaryOp::Coalesce {
4722 return match self.evaluate_internal(lhs, data) {
4724 Ok(value) => {
4725 if matches!(lhs, AstNode::Null) {
4728 Ok(value)
4729 }
4730 else if value.is_undefined()
4732 && (matches!(lhs, AstNode::Path { .. })
4733 || matches!(lhs, AstNode::String(_))
4734 || matches!(lhs, AstNode::Variable(_)))
4735 {
4736 self.evaluate_internal(rhs, data)
4737 } else {
4738 Ok(value)
4739 }
4740 }
4741 Err(_) => {
4742 self.evaluate_internal(rhs, data)
4744 }
4745 };
4746 }
4747
4748 if op == BinaryOp::Default {
4751 let left = self.evaluate_internal(lhs, data)?;
4752 if self.is_truthy_for_default(&left) {
4753 return Ok(left);
4754 }
4755 return self.evaluate_internal(rhs, data);
4756 }
4757
4758 if op == BinaryOp::ChainPipe {
4762 if let AstNode::Regex { pattern, flags } = rhs {
4764 let lhs_value = self.evaluate_internal(lhs, data)?;
4766 return match lhs_value {
4768 JValue::String(s) => {
4769 let case_insensitive = flags.contains('i');
4771 let regex_pattern = if case_insensitive {
4772 format!("(?i){}", pattern)
4773 } else {
4774 pattern.clone()
4775 };
4776 match regex::Regex::new(®ex_pattern) {
4777 Ok(re) => {
4778 if let Some(m) = re.find(&s) {
4779 let mut result = IndexMap::new();
4781 result.insert(
4782 "match".to_string(),
4783 JValue::string(m.as_str().to_string()),
4784 );
4785 result.insert(
4786 "start".to_string(),
4787 JValue::Number(m.start() as f64),
4788 );
4789 result
4790 .insert("end".to_string(), JValue::Number(m.end() as f64));
4791
4792 let mut groups = Vec::new();
4794 for cap in re.captures_iter(&s).take(1) {
4795 for i in 1..cap.len() {
4796 if let Some(c) = cap.get(i) {
4797 groups.push(JValue::string(c.as_str().to_string()));
4798 }
4799 }
4800 }
4801 if !groups.is_empty() {
4802 result.insert("groups".to_string(), JValue::array(groups));
4803 }
4804
4805 Ok(JValue::object(result))
4806 } else {
4807 Ok(JValue::Null)
4808 }
4809 }
4810 Err(e) => Err(EvaluatorError::EvaluationError(format!(
4811 "Invalid regex: {}",
4812 e
4813 ))),
4814 }
4815 }
4816 JValue::Null => Ok(JValue::Null),
4817 _ => Err(EvaluatorError::TypeError(
4818 "Left side of ~> /regex/ must be a string".to_string(),
4819 )),
4820 };
4821 }
4822
4823 let lhs_value_for_check = self.evaluate_internal(lhs, data)?;
4826 if lhs_value_for_check.is_undefined() || lhs_value_for_check.is_null() {
4827 return Ok(JValue::Undefined);
4828 }
4829
4830 match rhs {
4832 AstNode::Function {
4833 name,
4834 args,
4835 is_builtin,
4836 } => {
4837 let has_placeholder =
4840 args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
4841
4842 if has_placeholder {
4843 let lhs_value = self.evaluate_internal(lhs, data)?;
4845 let mut filled_args = Vec::new();
4846 let mut lhs_used = false;
4847
4848 for arg in args.iter() {
4849 if matches!(arg, AstNode::Placeholder) && !lhs_used {
4850 let temp_name = format!("__pipe_arg_{}", filled_args.len());
4853 self.context.bind(temp_name.clone(), lhs_value.clone());
4854 filled_args.push(AstNode::Variable(temp_name));
4855 lhs_used = true;
4856 } else {
4857 filled_args.push(arg.clone());
4858 }
4859 }
4860
4861 let result =
4863 self.evaluate_function_call(name, &filled_args, *is_builtin, data);
4864
4865 for (i, arg) in args.iter().enumerate() {
4867 if matches!(arg, AstNode::Placeholder) {
4868 self.context.unbind(&format!("__pipe_arg_{}", i));
4869 }
4870 }
4871
4872 return result.map(|v| self.unwrap_singleton(v));
4874 } else {
4875 let mut all_args = vec![lhs.clone()];
4877 all_args.extend_from_slice(args);
4878 return self
4880 .evaluate_function_call(name, &all_args, *is_builtin, data)
4881 .map(|v| self.unwrap_singleton(v));
4882 }
4883 }
4884 AstNode::Variable(var_name) => {
4885 let all_args = vec![lhs.clone()];
4888 return self
4890 .evaluate_function_call(var_name, &all_args, true, data)
4891 .map(|v| self.unwrap_singleton(v));
4892 }
4893 AstNode::Binary {
4894 op: BinaryOp::ChainPipe,
4895 ..
4896 } => {
4897 let lhs_value = self.evaluate_internal(lhs, data)?;
4900 return self.evaluate_internal(rhs, &lhs_value);
4901 }
4902 AstNode::Transform { .. } => {
4903 let lhs_value = self.evaluate_internal(lhs, data)?;
4906
4907 let saved_binding = self.context.lookup("$").cloned();
4909 self.context.bind("$".to_string(), lhs_value.clone());
4910
4911 let result = self.evaluate_internal(rhs, data);
4912
4913 if let Some(saved) = saved_binding {
4915 self.context.bind("$".to_string(), saved);
4916 } else {
4917 self.context.unbind("$");
4918 }
4919
4920 return result.map(|v| self.unwrap_singleton(v));
4922 }
4923 AstNode::Lambda {
4924 params,
4925 body,
4926 signature,
4927 thunk,
4928 } => {
4929 let lhs_value = self.evaluate_internal(lhs, data)?;
4931 return self
4933 .invoke_lambda(params, body, signature.as_ref(), &[lhs_value], data, *thunk)
4934 .map(|v| self.unwrap_singleton(v));
4935 }
4936 AstNode::Path { steps } => {
4937 if let Some(first_step) = steps.first() {
4940 match &first_step.node {
4941 AstNode::Function {
4942 name,
4943 args,
4944 is_builtin,
4945 } => {
4946 let mut all_args = vec![lhs.clone()];
4948 all_args.extend_from_slice(args);
4949
4950 let mut result = self.evaluate_function_call(
4952 name,
4953 &all_args,
4954 *is_builtin,
4955 data,
4956 )?;
4957
4958 for stage in &first_step.stages {
4960 match stage {
4961 Stage::Filter(filter_expr) => {
4962 result = self.evaluate_predicate_as_stage(
4963 &result,
4964 filter_expr,
4965 )?;
4966 }
4967 }
4968 }
4969
4970 if steps.len() > 1 {
4972 let remaining_path = AstNode::Path {
4973 steps: steps[1..].to_vec(),
4974 };
4975 result = self.evaluate_internal(&remaining_path, &result)?;
4976 }
4977
4978 if !first_step.stages.is_empty() || steps.len() > 1 {
4981 return Ok(result);
4982 } else {
4983 return Ok(self.unwrap_singleton(result));
4984 }
4985 }
4986 AstNode::Variable(var_name) => {
4987 let all_args = vec![lhs.clone()];
4989 let mut result =
4990 self.evaluate_function_call(var_name, &all_args, true, data)?;
4991
4992 for stage in &first_step.stages {
4994 match stage {
4995 Stage::Filter(filter_expr) => {
4996 result = self.evaluate_predicate_as_stage(
4997 &result,
4998 filter_expr,
4999 )?;
5000 }
5001 }
5002 }
5003
5004 if steps.len() > 1 {
5006 let remaining_path = AstNode::Path {
5007 steps: steps[1..].to_vec(),
5008 };
5009 result = self.evaluate_internal(&remaining_path, &result)?;
5010 }
5011
5012 if !first_step.stages.is_empty() || steps.len() > 1 {
5015 return Ok(result);
5016 } else {
5017 return Ok(self.unwrap_singleton(result));
5018 }
5019 }
5020 _ => {
5021 let lhs_value = self.evaluate_internal(lhs, data)?;
5023 return self
5024 .evaluate_internal(rhs, &lhs_value)
5025 .map(|v| self.unwrap_singleton(v));
5026 }
5027 }
5028 }
5029
5030 let lhs_value = self.evaluate_internal(lhs, data)?;
5032 return self
5033 .evaluate_internal(rhs, &lhs_value)
5034 .map(|v| self.unwrap_singleton(v));
5035 }
5036 _ => {
5037 return Err(EvaluatorError::TypeError(
5038 "Right side of ~> must be a function call or function reference"
5039 .to_string(),
5040 ));
5041 }
5042 }
5043 }
5044
5045 if op == BinaryOp::ColonEqual {
5047 let var_name = match lhs {
5049 AstNode::Variable(name) => name.clone(),
5050 _ => {
5051 return Err(EvaluatorError::TypeError(
5052 "Left side of := must be a variable".to_string(),
5053 ))
5054 }
5055 };
5056
5057 if let AstNode::Lambda {
5059 params,
5060 body,
5061 signature,
5062 thunk,
5063 } = rhs
5064 {
5065 let captured_env = self.capture_environment_for(body, params);
5068 let compiled_body = if !thunk {
5069 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
5070 try_compile_expr_with_allowed_vars(body, &var_refs)
5071 } else {
5072 None
5073 };
5074 let stored_lambda = StoredLambda {
5075 params: params.clone(),
5076 body: (**body).clone(),
5077 compiled_body,
5078 signature: signature.clone(),
5079 captured_env,
5080 captured_data: Some(data.clone()),
5081 thunk: *thunk,
5082 };
5083 let lambda_params = stored_lambda.params.clone();
5084 let lambda_sig = stored_lambda.signature.clone();
5085 self.context.bind_lambda(var_name.clone(), stored_lambda);
5086
5087 let lambda_repr = JValue::lambda(
5089 var_name.as_str(),
5090 lambda_params,
5091 Some(var_name.clone()),
5092 lambda_sig,
5093 );
5094 return Ok(lambda_repr);
5095 }
5096
5097 if let AstNode::Binary {
5103 op: BinaryOp::ChainPipe,
5104 lhs: chain_lhs,
5105 rhs: chain_rhs,
5106 } = rhs
5107 {
5108 let is_function_composition = match chain_lhs.as_ref() {
5111 AstNode::Variable(name)
5113 if self.is_builtin_function(name)
5114 || self.context.lookup_lambda(name).is_some() =>
5115 {
5116 true
5117 }
5118 AstNode::Binary {
5120 op: BinaryOp::ChainPipe,
5121 ..
5122 } => true,
5123 AstNode::Function { args, .. }
5126 if args.iter().any(|a| matches!(a, AstNode::Placeholder)) =>
5127 {
5128 true
5129 }
5130 _ => false,
5132 };
5133
5134 if is_function_composition {
5135 let param_name = "$".to_string();
5139
5140 let first_pipe = AstNode::Binary {
5142 op: BinaryOp::ChainPipe,
5143 lhs: Box::new(AstNode::Variable(param_name.clone())),
5144 rhs: chain_lhs.clone(),
5145 };
5146
5147 let composed_body = AstNode::Binary {
5149 op: BinaryOp::ChainPipe,
5150 lhs: Box::new(first_pipe),
5151 rhs: chain_rhs.clone(),
5152 };
5153
5154 let stored_lambda = StoredLambda {
5155 params: vec![param_name],
5156 body: composed_body,
5157 compiled_body: None, signature: None,
5159 captured_env: self.capture_current_environment(),
5160 captured_data: Some(data.clone()),
5161 thunk: false,
5162 };
5163 self.context.bind_lambda(var_name.clone(), stored_lambda);
5164
5165 let lambda_repr = JValue::lambda(
5167 var_name.as_str(),
5168 vec!["$".to_string()],
5169 Some(var_name.clone()),
5170 None::<String>,
5171 );
5172 return Ok(lambda_repr);
5173 }
5174 }
5176
5177 let value = self.evaluate_internal(rhs, data)?;
5179
5180 if let Some(stored) = self.lookup_lambda_from_value(&value) {
5182 self.context.bind_lambda(var_name.clone(), stored);
5183 }
5184
5185 self.context.bind(var_name, value.clone());
5187 return Ok(value);
5188 }
5189
5190 if op == BinaryOp::In {
5193 let left = self.evaluate_internal(lhs, data)?;
5194
5195 if matches!(left, JValue::Array(_)) {
5197 let right_result = self.evaluate_internal(rhs, data);
5199
5200 if let Ok(JValue::Number(_)) = right_result {
5201 return self.array_index(&left, &right_result.unwrap());
5203 } else {
5204 return self.array_filter(lhs, rhs, &left, data);
5207 }
5208 }
5209 }
5210
5211 if op == BinaryOp::And {
5213 let left = self.evaluate_internal(lhs, data)?;
5214 if !self.is_truthy(&left) {
5215 return Ok(JValue::Bool(false));
5217 }
5218 let right = self.evaluate_internal(rhs, data)?;
5219 return Ok(JValue::Bool(self.is_truthy(&right)));
5220 }
5221
5222 if op == BinaryOp::Or {
5223 let left = self.evaluate_internal(lhs, data)?;
5224 if self.is_truthy(&left) {
5225 return Ok(JValue::Bool(true));
5227 }
5228 let right = self.evaluate_internal(rhs, data)?;
5229 return Ok(JValue::Bool(self.is_truthy(&right)));
5230 }
5231
5232 let left_is_explicit_null = matches!(lhs, AstNode::Null);
5234 let right_is_explicit_null = matches!(rhs, AstNode::Null);
5235
5236 let left = self.evaluate_internal(lhs, data)?;
5238 let right = self.evaluate_internal(rhs, data)?;
5239
5240 match op {
5241 BinaryOp::Add => self.add(&left, &right, left_is_explicit_null, right_is_explicit_null),
5242 BinaryOp::Subtract => {
5243 self.subtract(&left, &right, left_is_explicit_null, right_is_explicit_null)
5244 }
5245 BinaryOp::Multiply => {
5246 self.multiply(&left, &right, left_is_explicit_null, right_is_explicit_null)
5247 }
5248 BinaryOp::Divide => {
5249 self.divide(&left, &right, left_is_explicit_null, right_is_explicit_null)
5250 }
5251 BinaryOp::Modulo => {
5252 self.modulo(&left, &right, left_is_explicit_null, right_is_explicit_null)
5253 }
5254
5255 BinaryOp::Equal => Ok(JValue::Bool(self.equals(&left, &right))),
5256 BinaryOp::NotEqual => Ok(JValue::Bool(!self.equals(&left, &right))),
5257 BinaryOp::LessThan => {
5258 self.less_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
5259 }
5260 BinaryOp::LessThanOrEqual => self.less_than_or_equal(
5261 &left,
5262 &right,
5263 left_is_explicit_null,
5264 right_is_explicit_null,
5265 ),
5266 BinaryOp::GreaterThan => {
5267 self.greater_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
5268 }
5269 BinaryOp::GreaterThanOrEqual => self.greater_than_or_equal(
5270 &left,
5271 &right,
5272 left_is_explicit_null,
5273 right_is_explicit_null,
5274 ),
5275
5276 BinaryOp::And | BinaryOp::Or => unreachable!(),
5278
5279 BinaryOp::Concatenate => self.concatenate(&left, &right),
5280 BinaryOp::Range => self.range(&left, &right),
5281 BinaryOp::In => self.in_operator(&left, &right),
5282
5283 BinaryOp::ColonEqual | BinaryOp::Coalesce | BinaryOp::Default | BinaryOp::ChainPipe => {
5285 unreachable!()
5286 }
5287 }
5288 }
5289
5290 fn evaluate_unary_op(
5292 &mut self,
5293 op: crate::ast::UnaryOp,
5294 operand: &AstNode,
5295 data: &JValue,
5296 ) -> Result<JValue, EvaluatorError> {
5297 use crate::ast::UnaryOp;
5298
5299 let value = self.evaluate_internal(operand, data)?;
5300
5301 match op {
5302 UnaryOp::Negate => match value {
5303 JValue::Null | JValue::Undefined => Ok(JValue::Null),
5305 JValue::Number(n) => Ok(JValue::Number(-n)),
5306 _ => Err(EvaluatorError::TypeError(
5307 "D1002: Cannot negate non-number value".to_string(),
5308 )),
5309 },
5310 UnaryOp::Not => Ok(JValue::Bool(!self.is_truthy(&value))),
5311 }
5312 }
5313
5314 fn try_fused_aggregate(
5321 &mut self,
5322 name: &str,
5323 arg: &AstNode,
5324 data: &JValue,
5325 ) -> Result<Option<JValue>, EvaluatorError> {
5326 if !matches!(name, "sum" | "max" | "min" | "average") {
5328 return Ok(None);
5329 }
5330
5331 let AstNode::Path { steps } = arg else {
5333 return Ok(None);
5334 };
5335
5336 if steps.len() != 2 {
5339 return Ok(None);
5340 }
5341
5342 let field_step = &steps[1];
5344 if !field_step.stages.is_empty() {
5345 return Ok(None);
5346 }
5347 let AstNode::Name(extract_field) = &field_step.node else {
5348 return Ok(None);
5349 };
5350
5351 let arr_step = &steps[0];
5353 let AstNode::Name(arr_name) = &arr_step.node else {
5354 return Ok(None);
5355 };
5356
5357 let arr = match data {
5359 JValue::Object(obj) => match obj.get(arr_name) {
5360 Some(JValue::Array(arr)) => arr,
5361 _ => return Ok(None),
5362 },
5363 _ => return Ok(None),
5364 };
5365
5366 let filter_compiled = match arr_step.stages.as_slice() {
5368 [] => None,
5369 [Stage::Filter(pred)] => try_compile_expr(pred),
5370 _ => return Ok(None),
5371 };
5372 if !arr_step.stages.is_empty() && filter_compiled.is_none() {
5374 return Ok(None);
5375 }
5376
5377 let shape = arr.first().and_then(build_shape_cache);
5379
5380 let mut total = 0.0f64;
5382 let mut count = 0usize;
5383 let mut max_val = f64::NEG_INFINITY;
5384 let mut min_val = f64::INFINITY;
5385 let mut has_any = false;
5386
5387 for item in arr.iter() {
5388 if let Some(ref compiled) = filter_compiled {
5390 let result = if let Some(ref s) = shape {
5391 eval_compiled_shaped(compiled, item, None, s)?
5392 } else {
5393 eval_compiled(compiled, item, None)?
5394 };
5395 if !compiled_is_truthy(&result) {
5396 continue;
5397 }
5398 }
5399
5400 let val = match item {
5402 JValue::Object(obj) => match obj.get(extract_field) {
5403 Some(JValue::Number(n)) => *n,
5404 Some(_) | None => continue, },
5406 _ => continue,
5407 };
5408
5409 has_any = true;
5410 match name {
5411 "sum" => total += val,
5412 "max" => max_val = max_val.max(val),
5413 "min" => min_val = min_val.min(val),
5414 "average" => {
5415 total += val;
5416 count += 1;
5417 }
5418 _ => unreachable!(),
5419 }
5420 }
5421
5422 if !has_any {
5423 return Ok(Some(match name {
5424 "sum" => JValue::from(0i64),
5425 "average" | "max" | "min" => JValue::Null,
5426 _ => unreachable!(),
5427 }));
5428 }
5429
5430 Ok(Some(match name {
5431 "sum" => JValue::Number(total),
5432 "max" => JValue::Number(max_val),
5433 "min" => JValue::Number(min_val),
5434 "average" => JValue::Number(total / count as f64),
5435 _ => unreachable!(),
5436 }))
5437 }
5438
5439 fn evaluate_function_call(
5441 &mut self,
5442 name: &str,
5443 args: &[AstNode],
5444 is_builtin: bool,
5445 data: &JValue,
5446 ) -> Result<JValue, EvaluatorError> {
5447 use crate::functions;
5448
5449 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
5451 if has_placeholder {
5452 return self.create_partial_application(name, args, is_builtin, data);
5453 }
5454
5455 if let Some(value) = self.context.lookup(name).cloned() {
5462 if let Some(stored_lambda) = self.lookup_lambda_from_value(&value) {
5463 let mut evaluated_args = Vec::with_capacity(args.len());
5464 for arg in args {
5465 evaluated_args.push(self.evaluate_internal(arg, data)?);
5466 }
5467 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5468 }
5469 if let JValue::Builtin { name: builtin_name } = &value {
5470 let mut evaluated_args = Vec::with_capacity(args.len());
5472 for arg in args {
5473 evaluated_args.push(self.evaluate_internal(arg, data)?);
5474 }
5475 return self.call_builtin_with_values(builtin_name, &evaluated_args);
5476 }
5477 }
5478
5479 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
5482 let mut evaluated_args = Vec::with_capacity(args.len());
5483 for arg in args {
5484 evaluated_args.push(self.evaluate_internal(arg, data)?);
5485 }
5486 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5487 }
5488
5489 if !is_builtin && name != "__lambda__" {
5492 return Err(EvaluatorError::ReferenceError(format!(
5493 "Unknown function: {}",
5494 name
5495 )));
5496 }
5497
5498 if name == "exists" && args.len() == 1 {
5501 let arg = &args[0];
5502
5503 if matches!(arg, AstNode::Null) {
5505 return Ok(JValue::Bool(true)); }
5507
5508 if let AstNode::Variable(var_name) = arg {
5510 if self.is_builtin_function(var_name) {
5511 return Ok(JValue::Bool(true)); }
5513
5514 if self.context.lookup_lambda(var_name).is_some() {
5516 return Ok(JValue::Bool(true)); }
5518
5519 if let Some(val) = self.context.lookup(var_name) {
5521 if val.is_undefined() {
5523 return Ok(JValue::Bool(false));
5524 }
5525 return Ok(JValue::Bool(true)); } else {
5527 return Ok(JValue::Bool(false)); }
5529 }
5530
5531 let value = self.evaluate_internal(arg, data)?;
5533 return Ok(JValue::Bool(!value.is_null() && !value.is_undefined()));
5534 }
5535
5536 for arg in args {
5539 if let AstNode::Variable(var_name) = arg {
5541 if !var_name.is_empty()
5543 && !self.is_builtin_function(var_name)
5544 && self.context.lookup(var_name).is_none()
5545 {
5546 if propagates_undefined(name) {
5548 return Ok(JValue::Null); }
5550 }
5551 }
5552 if let AstNode::Name(field_name) = arg {
5554 let field_exists =
5555 matches!(data, JValue::Object(obj) if obj.contains_key(field_name));
5556 if !field_exists && propagates_undefined(name) {
5557 return Ok(JValue::Null);
5558 }
5559 }
5560 if let AstNode::Path { steps } = arg {
5565 if let Ok(JValue::Null) = self.evaluate_internal(arg, data) {
5572 if steps.len() == 1 {
5575 let field_name = match &steps[0].node {
5577 AstNode::Name(n) => Some(n.as_str()),
5578 AstNode::String(s) => Some(s.as_str()),
5579 _ => None,
5580 };
5581 if let Some(field) = field_name {
5582 match data {
5583 JValue::Object(obj) => {
5584 if !obj.contains_key(field) {
5585 if propagates_undefined(name) {
5587 return Ok(JValue::Null);
5588 }
5589 }
5590 }
5592 JValue::Null if propagates_undefined(name) => {
5594 return Ok(JValue::Null);
5595 }
5596 _ => {}
5597 }
5598 }
5599 }
5600 else if steps.len() > 1 {
5602 let mut current = data;
5604 let mut failed_due_to_missing_field = false;
5605
5606 for (i, step) in steps.iter().enumerate() {
5607 if let AstNode::Name(field_name) = &step.node {
5608 match current {
5609 JValue::Object(obj) => {
5610 if let Some(val) = obj.get(field_name) {
5611 current = val;
5612 } else {
5613 failed_due_to_missing_field = true;
5615 break;
5616 }
5617 }
5618 JValue::Array(_) => {
5619 break;
5621 }
5622 JValue::Null => {
5623 if i > 0 {
5625 failed_due_to_missing_field = false;
5627 }
5628 break;
5629 }
5630 _ => break,
5631 }
5632 }
5633 }
5634
5635 if failed_due_to_missing_field && propagates_undefined(name) {
5636 return Ok(JValue::Null);
5637 }
5638 }
5639 }
5640 }
5641 }
5642
5643 if args.len() == 1 {
5646 if let Some(result) = self.try_fused_aggregate(name, &args[0], data)? {
5647 return Ok(result);
5648 }
5649 }
5650
5651 let mut evaluated_args = Vec::with_capacity(args.len());
5652 for arg in args {
5653 evaluated_args.push(self.evaluate_internal(arg, data)?);
5654 }
5655
5656 let context_functions_zero_arg = ["string", "number", "boolean", "uppercase", "lowercase"];
5661 let context_functions_missing_first = [
5662 "substringBefore",
5663 "substringAfter",
5664 "contains",
5665 "split",
5666 "replace",
5667 ];
5668
5669 if evaluated_args.is_empty() && context_functions_zero_arg.contains(&name) {
5670 evaluated_args.push(data.clone());
5672 } else if evaluated_args.len() == 1 && context_functions_missing_first.contains(&name) {
5673 if matches!(data, JValue::String(_)) {
5677 evaluated_args.insert(0, data.clone());
5678 }
5679 }
5680
5681 if name == "string"
5684 && args.is_empty()
5685 && !evaluated_args.is_empty()
5686 && evaluated_args[0].is_null()
5687 {
5688 return Ok(JValue::Null);
5690 }
5691
5692 match name {
5693 "string" => {
5694 if evaluated_args.len() > 2 {
5695 return Err(EvaluatorError::EvaluationError(
5696 "string() takes at most 2 arguments".to_string(),
5697 ));
5698 }
5699
5700 let prettify = if evaluated_args.len() == 2 {
5701 match &evaluated_args[1] {
5702 JValue::Bool(b) => Some(*b),
5703 _ => {
5704 return Err(EvaluatorError::TypeError(
5705 "string() prettify parameter must be a boolean".to_string(),
5706 ))
5707 }
5708 }
5709 } else {
5710 None
5711 };
5712
5713 Ok(functions::string::string(&evaluated_args[0], prettify)?)
5714 }
5715 "length" => {
5716 if evaluated_args.len() != 1 {
5717 return Err(EvaluatorError::EvaluationError(
5718 "length() requires exactly 1 argument".to_string(),
5719 ));
5720 }
5721 match &evaluated_args[0] {
5722 JValue::String(s) => Ok(functions::string::length(s)?),
5723 _ => Err(EvaluatorError::TypeError(
5724 "T0410: Argument 1 of function length does not match function signature"
5725 .to_string(),
5726 )),
5727 }
5728 }
5729 "uppercase" => {
5730 if evaluated_args.len() != 1 {
5731 return Err(EvaluatorError::EvaluationError(
5732 "uppercase() requires exactly 1 argument".to_string(),
5733 ));
5734 }
5735 if evaluated_args[0].is_undefined() {
5736 return Ok(JValue::Undefined);
5737 }
5738 match &evaluated_args[0] {
5739 JValue::String(s) => Ok(functions::string::uppercase(s)?),
5740 _ => Err(EvaluatorError::TypeError(
5741 "T0410: Argument 1 of function uppercase does not match function signature"
5742 .to_string(),
5743 )),
5744 }
5745 }
5746 "lowercase" => {
5747 if evaluated_args.len() != 1 {
5748 return Err(EvaluatorError::EvaluationError(
5749 "lowercase() requires exactly 1 argument".to_string(),
5750 ));
5751 }
5752 if evaluated_args[0].is_undefined() {
5753 return Ok(JValue::Undefined);
5754 }
5755 match &evaluated_args[0] {
5756 JValue::String(s) => Ok(functions::string::lowercase(s)?),
5757 _ => Err(EvaluatorError::TypeError(
5758 "T0410: Argument 1 of function lowercase does not match function signature"
5759 .to_string(),
5760 )),
5761 }
5762 }
5763 "number" => {
5764 if evaluated_args.is_empty() {
5765 return Err(EvaluatorError::EvaluationError(
5766 "number() requires at least 1 argument".to_string(),
5767 ));
5768 }
5769 if evaluated_args.len() > 1 {
5770 return Err(EvaluatorError::TypeError(
5771 "T0410: Argument 2 of function number does not match function signature"
5772 .to_string(),
5773 ));
5774 }
5775 if evaluated_args[0].is_undefined() {
5776 return Ok(JValue::Undefined);
5777 }
5778 Ok(functions::numeric::number(&evaluated_args[0])?)
5779 }
5780 "sum" => {
5781 if evaluated_args.len() != 1 {
5782 return Err(EvaluatorError::EvaluationError(
5783 "sum() requires exactly 1 argument".to_string(),
5784 ));
5785 }
5786 if evaluated_args[0].is_undefined() {
5788 return Ok(JValue::Undefined);
5789 }
5790 match &evaluated_args[0] {
5791 JValue::Null => Ok(JValue::Null),
5792 JValue::Array(arr) => {
5793 Ok(aggregation::sum(arr)?)
5795 }
5796 JValue::Number(n) => Ok(JValue::Number(*n)),
5798 other => Ok(functions::numeric::sum(&[other.clone()])?),
5799 }
5800 }
5801 "count" => {
5802 if evaluated_args.len() != 1 {
5803 return Err(EvaluatorError::EvaluationError(
5804 "count() requires exactly 1 argument".to_string(),
5805 ));
5806 }
5807 if evaluated_args[0].is_undefined() {
5809 return Ok(JValue::from(0i64));
5810 }
5811 match &evaluated_args[0] {
5812 JValue::Null => Ok(JValue::from(0i64)), JValue::Array(arr) => Ok(functions::array::count(arr)?),
5814 _ => Ok(JValue::from(1i64)), }
5816 }
5817 "substring" => {
5818 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5819 return Err(EvaluatorError::EvaluationError(
5820 "substring() requires 2 or 3 arguments".to_string(),
5821 ));
5822 }
5823 if evaluated_args[0].is_undefined() {
5824 return Ok(JValue::Undefined);
5825 }
5826 match (&evaluated_args[0], &evaluated_args[1]) {
5827 (JValue::String(s), JValue::Number(start)) => {
5828 let length = if evaluated_args.len() == 3 {
5829 match &evaluated_args[2] {
5830 JValue::Number(l) => Some(*l as i64),
5831 _ => return Err(EvaluatorError::TypeError(
5832 "T0410: Argument 3 of function substring does not match function signature".to_string(),
5833 )),
5834 }
5835 } else {
5836 None
5837 };
5838 Ok(functions::string::substring(s, *start as i64, length)?)
5839 }
5840 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5841 "T0410: Argument 2 of function substring does not match function signature"
5842 .to_string(),
5843 )),
5844 _ => Err(EvaluatorError::TypeError(
5845 "T0410: Argument 1 of function substring does not match function signature"
5846 .to_string(),
5847 )),
5848 }
5849 }
5850 "substringBefore" => {
5851 if evaluated_args.len() != 2 {
5852 return Err(EvaluatorError::TypeError(
5853 "T0411: Context value is not a compatible type with argument 2 of function substringBefore".to_string(),
5854 ));
5855 }
5856 if evaluated_args[0].is_undefined() {
5857 return Ok(JValue::Undefined);
5858 }
5859 match (&evaluated_args[0], &evaluated_args[1]) {
5860 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_before(s, sep)?),
5861 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5862 "T0410: Argument 2 of function substringBefore does not match function signature".to_string(),
5863 )),
5864 _ => Err(EvaluatorError::TypeError(
5865 "T0410: Argument 1 of function substringBefore does not match function signature".to_string(),
5866 )),
5867 }
5868 }
5869 "substringAfter" => {
5870 if evaluated_args.len() != 2 {
5871 return Err(EvaluatorError::TypeError(
5872 "T0411: Context value is not a compatible type with argument 2 of function substringAfter".to_string(),
5873 ));
5874 }
5875 if evaluated_args[0].is_undefined() {
5876 return Ok(JValue::Undefined);
5877 }
5878 match (&evaluated_args[0], &evaluated_args[1]) {
5879 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_after(s, sep)?),
5880 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5881 "T0410: Argument 2 of function substringAfter does not match function signature".to_string(),
5882 )),
5883 _ => Err(EvaluatorError::TypeError(
5884 "T0410: Argument 1 of function substringAfter does not match function signature".to_string(),
5885 )),
5886 }
5887 }
5888 "pad" => {
5889 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
5890 return Err(EvaluatorError::EvaluationError(
5891 "pad() requires 2 or 3 arguments".to_string(),
5892 ));
5893 }
5894
5895 let string = match &evaluated_args[0] {
5897 JValue::String(s) => s.clone(),
5898 JValue::Null => return Ok(JValue::Null),
5899 _ => {
5900 return Err(EvaluatorError::TypeError(
5901 "pad() first argument must be a string".to_string(),
5902 ))
5903 }
5904 };
5905
5906 let width = match &evaluated_args.get(1) {
5908 Some(JValue::Number(n)) => *n as i32,
5909 _ => {
5910 return Err(EvaluatorError::TypeError(
5911 "pad() second argument must be a number".to_string(),
5912 ))
5913 }
5914 };
5915
5916 let pad_string = match evaluated_args.get(2) {
5918 Some(JValue::String(s)) if !s.is_empty() => s.clone(),
5919 _ => Rc::from(" "),
5920 };
5921
5922 let abs_width = width.unsigned_abs() as usize;
5923 let char_count = string.chars().count();
5925
5926 if char_count >= abs_width {
5927 return Ok(JValue::string(string));
5929 }
5930
5931 let padding_needed = abs_width - char_count;
5932
5933 let pad_chars: Vec<char> = pad_string.chars().collect();
5934 let mut padding = String::with_capacity(padding_needed);
5935 for i in 0..padding_needed {
5936 padding.push(pad_chars[i % pad_chars.len()]);
5937 }
5938
5939 let result = if width < 0 {
5940 format!("{}{}", padding, string)
5942 } else {
5943 format!("{}{}", string, padding)
5945 };
5946
5947 Ok(JValue::string(result))
5948 }
5949
5950 "trim" => {
5951 if evaluated_args.is_empty() {
5952 return Ok(JValue::Null); }
5954 if evaluated_args.len() != 1 {
5955 return Err(EvaluatorError::EvaluationError(
5956 "trim() requires at most 1 argument".to_string(),
5957 ));
5958 }
5959 match &evaluated_args[0] {
5960 JValue::Null => Ok(JValue::Null),
5961 JValue::String(s) => Ok(functions::string::trim(s)?),
5962 _ => Err(EvaluatorError::TypeError(
5963 "trim() requires a string argument".to_string(),
5964 )),
5965 }
5966 }
5967 "contains" => {
5968 if evaluated_args.len() != 2 {
5969 return Err(EvaluatorError::EvaluationError(
5970 "contains() requires exactly 2 arguments".to_string(),
5971 ));
5972 }
5973 if evaluated_args[0].is_null() {
5974 return Ok(JValue::Null);
5975 }
5976 if evaluated_args[0].is_undefined() {
5977 return Ok(JValue::Undefined);
5978 }
5979 match &evaluated_args[0] {
5980 JValue::String(s) => Ok(functions::string::contains(s, &evaluated_args[1])?),
5981 _ => Err(EvaluatorError::TypeError(
5982 "contains() requires a string as the first argument".to_string(),
5983 )),
5984 }
5985 }
5986 "split" => {
5987 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5988 return Err(EvaluatorError::EvaluationError(
5989 "split() requires 2 or 3 arguments".to_string(),
5990 ));
5991 }
5992 if evaluated_args[0].is_null() {
5993 return Ok(JValue::Null);
5994 }
5995 if evaluated_args[0].is_undefined() {
5996 return Ok(JValue::Undefined);
5997 }
5998 match &evaluated_args[0] {
5999 JValue::String(s) => {
6000 let limit = if evaluated_args.len() == 3 {
6001 match &evaluated_args[2] {
6002 JValue::Number(n) => {
6003 let f = *n;
6004 if f < 0.0 {
6006 return Err(EvaluatorError::EvaluationError(
6007 "D3020: Third argument of split function must be a positive number".to_string(),
6008 ));
6009 }
6010 Some(f.floor() as usize)
6012 }
6013 _ => {
6014 return Err(EvaluatorError::TypeError(
6015 "split() limit must be a number".to_string(),
6016 ))
6017 }
6018 }
6019 } else {
6020 None
6021 };
6022 Ok(functions::string::split(s, &evaluated_args[1], limit)?)
6023 }
6024 _ => Err(EvaluatorError::TypeError(
6025 "split() requires a string as the first argument".to_string(),
6026 )),
6027 }
6028 }
6029 "join" => {
6030 if evaluated_args.is_empty() {
6033 return Err(EvaluatorError::TypeError(
6034 "T0410: Argument 1 of function $join does not match function signature"
6035 .to_string(),
6036 ));
6037 }
6038 if evaluated_args[0].is_null() {
6039 return Ok(JValue::Null);
6040 }
6041 if evaluated_args[0].is_undefined() {
6042 return Ok(JValue::Undefined);
6043 }
6044
6045 use crate::signature::Signature;
6048
6049 let signature = Signature::parse("<a<s>s?:s>").map_err(|e| {
6050 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
6051 })?;
6052
6053 let coerced_args = match signature.validate_and_coerce(&evaluated_args) {
6054 Ok(args) => args,
6055 Err(crate::signature::SignatureError::UndefinedArgument) => {
6056 let sig_first_arg = Signature::parse("<a<s>:a<s>>").map_err(|e| {
6059 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
6060 })?;
6061
6062 match sig_first_arg.validate_and_coerce(&evaluated_args[0..1]) {
6063 Ok(args) => args,
6064 Err(crate::signature::SignatureError::ArrayTypeMismatch {
6065 index,
6066 expected,
6067 }) => {
6068 return Err(EvaluatorError::TypeError(format!(
6069 "T0412: Argument {} of function $join must be an array of {}",
6070 index, expected
6071 )));
6072 }
6073 Err(e) => {
6074 return Err(EvaluatorError::TypeError(format!(
6075 "Signature validation failed: {}",
6076 e
6077 )));
6078 }
6079 }
6080 }
6081 Err(crate::signature::SignatureError::ArgumentTypeMismatch {
6082 index,
6083 expected,
6084 }) => {
6085 return Err(EvaluatorError::TypeError(
6086 format!("T0410: Argument {} of function $join does not match function signature (expected {})", index, expected)
6087 ));
6088 }
6089 Err(crate::signature::SignatureError::ArrayTypeMismatch {
6090 index,
6091 expected,
6092 }) => {
6093 return Err(EvaluatorError::TypeError(format!(
6094 "T0412: Argument {} of function $join must be an array of {}",
6095 index, expected
6096 )));
6097 }
6098 Err(e) => {
6099 return Err(EvaluatorError::TypeError(format!(
6100 "Signature validation failed: {}",
6101 e
6102 )));
6103 }
6104 };
6105
6106 match &coerced_args[0] {
6108 JValue::Array(arr) => {
6109 let separator = if coerced_args.len() == 2 {
6110 match &coerced_args[1] {
6111 JValue::String(s) => Some(&**s),
6112 JValue::Null => None, _ => None, }
6115 } else {
6116 None };
6118 Ok(functions::string::join(arr, separator)?)
6119 }
6120 JValue::Null => Ok(JValue::Null),
6121 _ => unreachable!("Signature validation should ensure array type"),
6122 }
6123 }
6124 "replace" => {
6125 if evaluated_args.len() < 3 || evaluated_args.len() > 4 {
6126 return Err(EvaluatorError::EvaluationError(
6127 "replace() requires 3 or 4 arguments".to_string(),
6128 ));
6129 }
6130 if evaluated_args[0].is_null() {
6131 return Ok(JValue::Null);
6132 }
6133 if evaluated_args[0].is_undefined() {
6134 return Ok(JValue::Undefined);
6135 }
6136
6137 let replacement_is_lambda = matches!(
6139 evaluated_args[2],
6140 JValue::Lambda { .. } | JValue::Builtin { .. }
6141 );
6142
6143 if replacement_is_lambda {
6144 return self.replace_with_lambda(
6146 &evaluated_args[0],
6147 &evaluated_args[1],
6148 &evaluated_args[2],
6149 if evaluated_args.len() == 4 {
6150 Some(&evaluated_args[3])
6151 } else {
6152 None
6153 },
6154 data,
6155 );
6156 }
6157
6158 match (&evaluated_args[0], &evaluated_args[2]) {
6160 (JValue::String(s), JValue::String(replacement)) => {
6161 let limit = if evaluated_args.len() == 4 {
6162 match &evaluated_args[3] {
6163 JValue::Number(n) => {
6164 let lim_f64 = *n;
6165 if lim_f64 < 0.0 {
6166 return Err(EvaluatorError::EvaluationError(format!(
6167 "D3011: Limit must be non-negative, got {}",
6168 lim_f64
6169 )));
6170 }
6171 Some(lim_f64 as usize)
6172 }
6173 _ => {
6174 return Err(EvaluatorError::TypeError(
6175 "replace() limit must be a number".to_string(),
6176 ))
6177 }
6178 }
6179 } else {
6180 None
6181 };
6182 Ok(functions::string::replace(
6183 s,
6184 &evaluated_args[1],
6185 replacement,
6186 limit,
6187 )?)
6188 }
6189 _ => Err(EvaluatorError::TypeError(
6190 "replace() requires string arguments".to_string(),
6191 )),
6192 }
6193 }
6194 "match" => {
6195 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
6198 return Err(EvaluatorError::EvaluationError(
6199 "match() requires 1 to 3 arguments".to_string(),
6200 ));
6201 }
6202 if evaluated_args[0].is_null() {
6203 return Ok(JValue::Null);
6204 }
6205 if evaluated_args[0].is_undefined() {
6206 return Ok(JValue::Undefined);
6207 }
6208
6209 let s = match &evaluated_args[0] {
6210 JValue::String(s) => s.clone(),
6211 _ => {
6212 return Err(EvaluatorError::TypeError(
6213 "match() first argument must be a string".to_string(),
6214 ))
6215 }
6216 };
6217
6218 let limit = if evaluated_args.len() == 3 {
6220 match &evaluated_args[2] {
6221 JValue::Number(n) => Some(*n as usize),
6222 JValue::Null => None,
6223 _ => {
6224 return Err(EvaluatorError::TypeError(
6225 "match() limit must be a number".to_string(),
6226 ))
6227 }
6228 }
6229 } else {
6230 None
6231 };
6232
6233 let pattern_value = evaluated_args.get(1);
6235 let is_custom_matcher = pattern_value.is_some_and(|val| {
6236 matches!(val, JValue::Lambda { .. } | JValue::Builtin { .. })
6237 });
6238
6239 if is_custom_matcher {
6240 return self.match_with_custom_matcher(&s, &args[1], limit, data);
6243 }
6244
6245 let (pattern, flags) = match pattern_value {
6247 Some(val) => crate::functions::string::extract_regex(val).ok_or_else(|| {
6248 EvaluatorError::TypeError(
6249 "match() second argument must be a regex pattern or matcher function"
6250 .to_string(),
6251 )
6252 })?,
6253 None => (".*".to_string(), "".to_string()),
6254 };
6255
6256 let is_global = flags.contains('g');
6258 let regex_pattern = if flags.contains('i') {
6259 format!("(?i){}", pattern)
6260 } else {
6261 pattern.clone()
6262 };
6263
6264 let re = regex::Regex::new(®ex_pattern).map_err(|e| {
6265 EvaluatorError::EvaluationError(format!("Invalid regex pattern: {}", e))
6266 })?;
6267
6268 let mut results = Vec::new();
6269 let mut count = 0;
6270
6271 for caps in re.captures_iter(&s) {
6272 if let Some(lim) = limit {
6273 if count >= lim {
6274 break;
6275 }
6276 }
6277
6278 let full_match = caps.get(0).unwrap();
6279 let mut match_obj = IndexMap::new();
6280 match_obj.insert(
6281 "match".to_string(),
6282 JValue::string(full_match.as_str().to_string()),
6283 );
6284 match_obj.insert(
6285 "index".to_string(),
6286 JValue::Number(full_match.start() as f64),
6287 );
6288
6289 let mut groups: Vec<JValue> = Vec::new();
6291 for i in 1..caps.len() {
6292 if let Some(group) = caps.get(i) {
6293 groups.push(JValue::string(group.as_str().to_string()));
6294 } else {
6295 groups.push(JValue::Null);
6296 }
6297 }
6298 if !groups.is_empty() {
6299 match_obj.insert("groups".to_string(), JValue::array(groups));
6300 }
6301
6302 results.push(JValue::object(match_obj));
6303 count += 1;
6304
6305 if !is_global {
6307 break;
6308 }
6309 }
6310
6311 if results.is_empty() {
6312 Ok(JValue::Null)
6313 } else if results.len() == 1 && !is_global {
6314 Ok(results.into_iter().next().unwrap())
6316 } else {
6317 Ok(JValue::array(results))
6318 }
6319 }
6320 "max" => {
6321 if evaluated_args.len() != 1 {
6322 return Err(EvaluatorError::EvaluationError(
6323 "max() requires exactly 1 argument".to_string(),
6324 ));
6325 }
6326 if evaluated_args[0].is_undefined() {
6328 return Ok(JValue::Undefined);
6329 }
6330 match &evaluated_args[0] {
6331 JValue::Null => Ok(JValue::Null),
6332 JValue::Array(arr) => {
6333 Ok(aggregation::max(arr)?)
6335 }
6336 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
6338 "max() requires an array or number argument".to_string(),
6339 )),
6340 }
6341 }
6342 "min" => {
6343 if evaluated_args.len() != 1 {
6344 return Err(EvaluatorError::EvaluationError(
6345 "min() requires exactly 1 argument".to_string(),
6346 ));
6347 }
6348 if evaluated_args[0].is_undefined() {
6350 return Ok(JValue::Undefined);
6351 }
6352 match &evaluated_args[0] {
6353 JValue::Null => Ok(JValue::Null),
6354 JValue::Array(arr) => {
6355 Ok(aggregation::min(arr)?)
6357 }
6358 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
6360 "min() requires an array or number argument".to_string(),
6361 )),
6362 }
6363 }
6364 "average" => {
6365 if evaluated_args.len() != 1 {
6366 return Err(EvaluatorError::EvaluationError(
6367 "average() requires exactly 1 argument".to_string(),
6368 ));
6369 }
6370 if evaluated_args[0].is_undefined() {
6372 return Ok(JValue::Undefined);
6373 }
6374 match &evaluated_args[0] {
6375 JValue::Null => Ok(JValue::Null),
6376 JValue::Array(arr) => {
6377 Ok(aggregation::average(arr)?)
6379 }
6380 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
6382 "average() requires an array or number argument".to_string(),
6383 )),
6384 }
6385 }
6386 "abs" => {
6387 if evaluated_args.len() != 1 {
6388 return Err(EvaluatorError::EvaluationError(
6389 "abs() requires exactly 1 argument".to_string(),
6390 ));
6391 }
6392 match &evaluated_args[0] {
6393 JValue::Null => Ok(JValue::Null),
6394 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
6395 _ => Err(EvaluatorError::TypeError(
6396 "abs() requires a number argument".to_string(),
6397 )),
6398 }
6399 }
6400 "floor" => {
6401 if evaluated_args.len() != 1 {
6402 return Err(EvaluatorError::EvaluationError(
6403 "floor() requires exactly 1 argument".to_string(),
6404 ));
6405 }
6406 match &evaluated_args[0] {
6407 JValue::Null => Ok(JValue::Null),
6408 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
6409 _ => Err(EvaluatorError::TypeError(
6410 "floor() requires a number argument".to_string(),
6411 )),
6412 }
6413 }
6414 "ceil" => {
6415 if evaluated_args.len() != 1 {
6416 return Err(EvaluatorError::EvaluationError(
6417 "ceil() requires exactly 1 argument".to_string(),
6418 ));
6419 }
6420 match &evaluated_args[0] {
6421 JValue::Null => Ok(JValue::Null),
6422 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
6423 _ => Err(EvaluatorError::TypeError(
6424 "ceil() requires a number argument".to_string(),
6425 )),
6426 }
6427 }
6428 "round" => {
6429 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6430 return Err(EvaluatorError::EvaluationError(
6431 "round() requires 1 or 2 arguments".to_string(),
6432 ));
6433 }
6434 match &evaluated_args[0] {
6435 JValue::Null => Ok(JValue::Null),
6436 JValue::Number(n) => {
6437 let precision = if evaluated_args.len() == 2 {
6438 match &evaluated_args[1] {
6439 JValue::Number(p) => Some(*p as i32),
6440 _ => {
6441 return Err(EvaluatorError::TypeError(
6442 "round() precision must be a number".to_string(),
6443 ))
6444 }
6445 }
6446 } else {
6447 None
6448 };
6449 Ok(functions::numeric::round(*n, precision)?)
6450 }
6451 _ => Err(EvaluatorError::TypeError(
6452 "round() requires a number argument".to_string(),
6453 )),
6454 }
6455 }
6456 "sqrt" => {
6457 if evaluated_args.len() != 1 {
6458 return Err(EvaluatorError::EvaluationError(
6459 "sqrt() requires exactly 1 argument".to_string(),
6460 ));
6461 }
6462 match &evaluated_args[0] {
6463 JValue::Null => Ok(JValue::Null),
6464 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
6465 _ => Err(EvaluatorError::TypeError(
6466 "sqrt() requires a number argument".to_string(),
6467 )),
6468 }
6469 }
6470 "power" => {
6471 if evaluated_args.len() != 2 {
6472 return Err(EvaluatorError::EvaluationError(
6473 "power() requires exactly 2 arguments".to_string(),
6474 ));
6475 }
6476 if evaluated_args[0].is_null() {
6477 return Ok(JValue::Null);
6478 }
6479 if evaluated_args[0].is_undefined() {
6480 return Ok(JValue::Undefined);
6481 }
6482 match (&evaluated_args[0], &evaluated_args[1]) {
6483 (JValue::Number(base), JValue::Number(exp)) => {
6484 Ok(functions::numeric::power(*base, *exp)?)
6485 }
6486 _ => Err(EvaluatorError::TypeError(
6487 "power() requires number arguments".to_string(),
6488 )),
6489 }
6490 }
6491 "formatNumber" => {
6492 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
6493 return Err(EvaluatorError::EvaluationError(
6494 "formatNumber() requires 2 or 3 arguments".to_string(),
6495 ));
6496 }
6497 if evaluated_args[0].is_null() {
6498 return Ok(JValue::Null);
6499 }
6500 if evaluated_args[0].is_undefined() {
6501 return Ok(JValue::Undefined);
6502 }
6503 match (&evaluated_args[0], &evaluated_args[1]) {
6504 (JValue::Number(num), JValue::String(picture)) => {
6505 let options = if evaluated_args.len() == 3 {
6506 Some(&evaluated_args[2])
6507 } else {
6508 None
6509 };
6510 Ok(functions::numeric::format_number(*num, picture, options)?)
6511 }
6512 _ => Err(EvaluatorError::TypeError(
6513 "formatNumber() requires a number and a string".to_string(),
6514 )),
6515 }
6516 }
6517 "formatBase" => {
6518 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6519 return Err(EvaluatorError::EvaluationError(
6520 "formatBase() requires 1 or 2 arguments".to_string(),
6521 ));
6522 }
6523 if evaluated_args[0].is_null() {
6525 return Ok(JValue::Null);
6526 }
6527 if evaluated_args[0].is_undefined() {
6528 return Ok(JValue::Undefined);
6529 }
6530 match &evaluated_args[0] {
6531 JValue::Number(num) => {
6532 let radix = if evaluated_args.len() == 2 {
6533 match &evaluated_args[1] {
6534 JValue::Number(r) => Some(r.trunc() as i64),
6535 _ => {
6536 return Err(EvaluatorError::TypeError(
6537 "formatBase() radix must be a number".to_string(),
6538 ))
6539 }
6540 }
6541 } else {
6542 None
6543 };
6544 Ok(functions::numeric::format_base(*num, radix)?)
6545 }
6546 _ => Err(EvaluatorError::TypeError(
6547 "formatBase() requires a number".to_string(),
6548 )),
6549 }
6550 }
6551 "append" => {
6552 if evaluated_args.len() != 2 {
6553 return Err(EvaluatorError::EvaluationError(
6554 "append() requires exactly 2 arguments".to_string(),
6555 ));
6556 }
6557 let first = &evaluated_args[0];
6559 let second = &evaluated_args[1];
6560
6561 if second.is_null() || second.is_undefined() {
6563 return Ok(first.clone());
6564 }
6565
6566 if first.is_null() || first.is_undefined() {
6568 return Ok(second.clone());
6569 }
6570
6571 let arr = match first {
6573 JValue::Array(a) => a.to_vec(),
6574 other => vec![other.clone()], };
6576
6577 Ok(functions::array::append(&arr, second)?)
6578 }
6579 "reverse" => {
6580 if evaluated_args.len() != 1 {
6581 return Err(EvaluatorError::EvaluationError(
6582 "reverse() requires exactly 1 argument".to_string(),
6583 ));
6584 }
6585 match &evaluated_args[0] {
6586 JValue::Null => Ok(JValue::Null),
6587 JValue::Undefined => Ok(JValue::Undefined),
6588 JValue::Array(arr) => Ok(functions::array::reverse(arr)?),
6589 _ => Err(EvaluatorError::TypeError(
6590 "reverse() requires an array argument".to_string(),
6591 )),
6592 }
6593 }
6594 "shuffle" => {
6595 if evaluated_args.len() != 1 {
6596 return Err(EvaluatorError::EvaluationError(
6597 "shuffle() requires exactly 1 argument".to_string(),
6598 ));
6599 }
6600 if evaluated_args[0].is_null() {
6601 return Ok(JValue::Null);
6602 }
6603 if evaluated_args[0].is_undefined() {
6604 return Ok(JValue::Undefined);
6605 }
6606 match &evaluated_args[0] {
6607 JValue::Array(arr) => Ok(functions::array::shuffle(arr)?),
6608 _ => Err(EvaluatorError::TypeError(
6609 "shuffle() requires an array argument".to_string(),
6610 )),
6611 }
6612 }
6613
6614 "sift" => {
6615 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6617 return Err(EvaluatorError::EvaluationError(
6618 "sift() requires 1 or 2 arguments".to_string(),
6619 ));
6620 }
6621
6622 let func_arg = if evaluated_args.len() == 1 {
6624 &args[0]
6625 } else {
6626 &args[1]
6627 };
6628
6629 let param_count = self.get_callback_param_count(func_arg);
6631
6632 let sift_object = |evaluator: &mut Self,
6634 obj: &IndexMap<String, JValue>,
6635 func_node: &AstNode,
6636 context_data: &JValue,
6637 param_count: usize|
6638 -> Result<JValue, EvaluatorError> {
6639 let obj_value = if param_count >= 3 {
6641 Some(JValue::object(obj.clone()))
6642 } else {
6643 None
6644 };
6645
6646 let mut result = IndexMap::new();
6647 for (key, value) in obj.iter() {
6648 let call_args = match param_count {
6650 1 => vec![value.clone()],
6651 2 => vec![value.clone(), JValue::string(key.clone())],
6652 _ => vec![
6653 value.clone(),
6654 JValue::string(key.clone()),
6655 obj_value.as_ref().unwrap().clone(),
6656 ],
6657 };
6658
6659 let pred_result =
6660 evaluator.apply_function(func_node, &call_args, context_data)?;
6661 if evaluator.is_truthy(&pred_result) {
6662 result.insert(key.clone(), value.clone());
6663 }
6664 }
6665 if result.is_empty() {
6667 Ok(JValue::Undefined)
6668 } else {
6669 Ok(JValue::object(result))
6670 }
6671 };
6672
6673 if evaluated_args.len() == 1 {
6675 match data {
6677 JValue::Object(o) => sift_object(self, o, &args[0], data, param_count),
6678 JValue::Array(arr) => {
6679 let mut results = Vec::new();
6681 for item in arr.iter() {
6682 if let JValue::Object(o) = item {
6683 let sifted = sift_object(self, o, &args[0], item, param_count)?;
6684 if !sifted.is_undefined() {
6686 results.push(sifted);
6687 }
6688 }
6689 }
6690 Ok(JValue::array(results))
6691 }
6692 JValue::Null => Ok(JValue::Null),
6693 _ => Ok(JValue::Undefined),
6694 }
6695 } else {
6696 match &evaluated_args[0] {
6698 JValue::Object(o) => sift_object(self, o, &args[1], data, param_count),
6699 JValue::Null => Ok(JValue::Null),
6700 _ => Err(EvaluatorError::TypeError(
6701 "sift() first argument must be an object".to_string(),
6702 )),
6703 }
6704 }
6705 }
6706
6707 "zip" => {
6708 if evaluated_args.is_empty() {
6709 return Err(EvaluatorError::EvaluationError(
6710 "zip() requires at least 1 argument".to_string(),
6711 ));
6712 }
6713
6714 let mut arrays: Vec<Vec<JValue>> = Vec::with_capacity(evaluated_args.len());
6717 for arg in &evaluated_args {
6718 match arg {
6719 JValue::Array(arr) => {
6720 if arr.is_empty() {
6721 return Ok(JValue::array(vec![]));
6723 }
6724 arrays.push(arr.to_vec());
6725 }
6726 JValue::Null => {
6727 return Ok(JValue::array(vec![]));
6729 }
6730 other => {
6731 arrays.push(vec![other.clone()]);
6733 }
6734 }
6735 }
6736
6737 if arrays.is_empty() {
6738 return Ok(JValue::array(vec![]));
6739 }
6740
6741 let min_len = arrays.iter().map(|a| a.len()).min().unwrap_or(0);
6743
6744 let mut result = Vec::with_capacity(min_len);
6746 for i in 0..min_len {
6747 let mut tuple = Vec::with_capacity(arrays.len());
6748 for array in &arrays {
6749 tuple.push(array[i].clone());
6750 }
6751 result.push(JValue::array(tuple));
6752 }
6753
6754 Ok(JValue::array(result))
6755 }
6756
6757 "sort" => {
6758 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6759 return Err(EvaluatorError::EvaluationError(
6760 "sort() requires 1 or 2 arguments".to_string(),
6761 ));
6762 }
6763
6764 let array_value = &evaluated_args[0];
6766
6767 if array_value.is_null() {
6769 return Ok(JValue::Null);
6770 }
6771 if array_value.is_undefined() {
6772 return Ok(JValue::Undefined);
6773 }
6774
6775 let mut arr = match array_value {
6776 JValue::Array(arr) => arr.to_vec(),
6777 other => vec![other.clone()],
6778 };
6779
6780 if args.len() == 2 {
6781 self.merge_sort_with_comparator(&mut arr, &args[1], data)?;
6784 Ok(JValue::array(arr))
6785 } else {
6786 Ok(functions::array::sort(&arr)?)
6788 }
6789 }
6790 "distinct" => {
6791 if evaluated_args.len() != 1 {
6792 return Err(EvaluatorError::EvaluationError(
6793 "distinct() requires exactly 1 argument".to_string(),
6794 ));
6795 }
6796 match &evaluated_args[0] {
6797 JValue::Array(arr) => Ok(functions::array::distinct(arr)?),
6798 _ => Err(EvaluatorError::TypeError(
6799 "distinct() requires an array argument".to_string(),
6800 )),
6801 }
6802 }
6803 "exists" => {
6804 if evaluated_args.len() != 1 {
6805 return Err(EvaluatorError::EvaluationError(
6806 "exists() requires exactly 1 argument".to_string(),
6807 ));
6808 }
6809 Ok(functions::array::exists(&evaluated_args[0])?)
6810 }
6811 "keys" => {
6812 if evaluated_args.len() != 1 {
6813 return Err(EvaluatorError::EvaluationError(
6814 "keys() requires exactly 1 argument".to_string(),
6815 ));
6816 }
6817
6818 let unwrap_single = |keys: Vec<JValue>| -> JValue {
6820 if keys.len() == 1 {
6821 keys.into_iter().next().unwrap()
6822 } else {
6823 JValue::array(keys)
6824 }
6825 };
6826
6827 match &evaluated_args[0] {
6828 JValue::Null => Ok(JValue::Null),
6829 JValue::Lambda { .. } | JValue::Builtin { .. } => Ok(JValue::Null),
6830 JValue::Object(obj) => {
6831 if obj.is_empty() {
6833 Ok(JValue::Null)
6834 } else {
6835 let keys: Vec<JValue> =
6836 obj.keys().map(|k| JValue::string(k.clone())).collect();
6837 Ok(unwrap_single(keys))
6838 }
6839 }
6840 JValue::Array(arr) => {
6841 let mut all_keys = Vec::new();
6843 for item in arr.iter() {
6844 if matches!(item, JValue::Lambda { .. } | JValue::Builtin { .. }) {
6846 continue;
6847 }
6848 if let JValue::Object(obj) = item {
6849 for key in obj.keys() {
6850 if !all_keys.contains(&JValue::string(key.clone())) {
6851 all_keys.push(JValue::string(key.clone()));
6852 }
6853 }
6854 }
6855 }
6856 if all_keys.is_empty() {
6857 Ok(JValue::Null)
6858 } else {
6859 Ok(unwrap_single(all_keys))
6860 }
6861 }
6862 _ => Ok(JValue::Null),
6864 }
6865 }
6866 "lookup" => {
6867 if evaluated_args.len() != 2 {
6868 return Err(EvaluatorError::EvaluationError(
6869 "lookup() requires exactly 2 arguments".to_string(),
6870 ));
6871 }
6872 if evaluated_args[0].is_null() {
6873 return Ok(JValue::Null);
6874 }
6875 if evaluated_args[0].is_undefined() {
6876 return Ok(JValue::Undefined);
6877 }
6878
6879 let key = match &evaluated_args[1] {
6880 JValue::String(k) => &**k,
6881 _ => {
6882 return Err(EvaluatorError::TypeError(
6883 "lookup() requires a string key".to_string(),
6884 ))
6885 }
6886 };
6887
6888 fn lookup_recursive(val: &JValue, key: &str) -> Vec<JValue> {
6890 match val {
6891 JValue::Array(arr) => {
6892 let mut results = Vec::new();
6893 for item in arr.iter() {
6894 let nested = lookup_recursive(item, key);
6895 results.extend(nested.iter().cloned());
6896 }
6897 results
6898 }
6899 JValue::Object(obj) => {
6900 if let Some(v) = obj.get(key) {
6901 vec![v.clone()]
6902 } else {
6903 vec![]
6904 }
6905 }
6906 _ => vec![],
6907 }
6908 }
6909
6910 let results = lookup_recursive(&evaluated_args[0], key);
6911 if results.is_empty() {
6912 Ok(JValue::Null)
6913 } else if results.len() == 1 {
6914 Ok(results[0].clone())
6915 } else {
6916 Ok(JValue::array(results))
6917 }
6918 }
6919 "spread" => {
6920 if evaluated_args.len() != 1 {
6921 return Err(EvaluatorError::EvaluationError(
6922 "spread() requires exactly 1 argument".to_string(),
6923 ));
6924 }
6925 match &evaluated_args[0] {
6926 JValue::Null => Ok(JValue::Null),
6927 lambda @ (JValue::Lambda { .. } | JValue::Builtin { .. }) => Ok(lambda.clone()),
6930 JValue::Object(obj) => Ok(functions::object::spread(obj)?),
6931 JValue::Array(arr) => {
6932 let mut result = Vec::new();
6934 for item in arr.iter() {
6935 match item {
6936 JValue::Lambda { .. } | JValue::Builtin { .. } => {
6937 continue;
6939 }
6940 JValue::Object(obj) => {
6941 let spread_result = functions::object::spread(obj)?;
6942 if let JValue::Array(spread_items) = spread_result {
6943 result.extend(spread_items.iter().cloned());
6944 } else {
6945 result.push(spread_result);
6946 }
6947 }
6948 other => result.push(other.clone()),
6950 }
6951 }
6952 Ok(JValue::array(result))
6953 }
6954 other => Ok(other.clone()),
6956 }
6957 }
6958 "merge" => {
6959 if evaluated_args.is_empty() {
6960 return Err(EvaluatorError::EvaluationError(
6961 "merge() requires at least 1 argument".to_string(),
6962 ));
6963 }
6964 if evaluated_args.len() == 1 {
6967 match &evaluated_args[0] {
6968 JValue::Array(arr) => Ok(functions::object::merge(arr)?),
6969 JValue::Null => Ok(JValue::Null),
6970 JValue::Undefined => Ok(JValue::Undefined),
6971 JValue::Object(_) => {
6972 Ok(evaluated_args[0].clone())
6974 }
6975 _ => Err(EvaluatorError::TypeError(
6976 "merge() requires objects or an array of objects".to_string(),
6977 )),
6978 }
6979 } else {
6980 Ok(functions::object::merge(&evaluated_args)?)
6981 }
6982 }
6983
6984 "map" => {
6985 if args.len() != 2 {
6986 return Err(EvaluatorError::EvaluationError(
6987 "map() requires exactly 2 arguments".to_string(),
6988 ));
6989 }
6990
6991 let array = self.evaluate_internal(&args[0], data)?;
6993
6994 match array {
6995 JValue::Array(arr) => {
6996 let param_count = self.get_callback_param_count(&args[1]);
6998
6999 if param_count == 1 {
7001 if let AstNode::Lambda {
7002 params,
7003 body,
7004 signature: None,
7005 thunk: false,
7006 } = &args[1]
7007 {
7008 let var_refs: Vec<&str> =
7009 params.iter().map(|s| s.as_str()).collect();
7010 if let Some(compiled) =
7011 try_compile_expr_with_allowed_vars(body, &var_refs)
7012 {
7013 let param_name = params[0].as_str();
7014 let mut result = Vec::with_capacity(arr.len());
7015 let mut vars = HashMap::new();
7016 for item in arr.iter() {
7017 vars.insert(param_name, item);
7018 let mapped = eval_compiled(&compiled, data, Some(&vars))?;
7019 if !mapped.is_undefined() {
7020 result.push(mapped);
7021 }
7022 }
7023 return Ok(JValue::array(result));
7024 }
7025 }
7026 if let AstNode::Variable(var_name) = &args[1] {
7028 if let Some(stored) = self.context.lookup_lambda(var_name) {
7029 if let Some(ref ce) = stored.compiled_body.clone() {
7030 let param_name = stored.params[0].clone();
7031 let captured_data = stored.captured_data.clone();
7032 let captured_env_clone = stored.captured_env.clone();
7033 let ce_clone = ce.clone();
7034 if !captured_env_clone.values().any(|v| {
7035 matches!(
7036 v,
7037 JValue::Lambda { .. } | JValue::Builtin { .. }
7038 )
7039 }) {
7040 let call_data = captured_data.as_ref().unwrap_or(data);
7041 let mut result = Vec::with_capacity(arr.len());
7042 let mut vars: HashMap<&str, &JValue> =
7043 captured_env_clone
7044 .iter()
7045 .map(|(k, v)| (k.as_str(), v))
7046 .collect();
7047 for item in arr.iter() {
7048 vars.insert(param_name.as_str(), item);
7049 let mapped = eval_compiled(
7050 &ce_clone,
7051 call_data,
7052 Some(&vars),
7053 )?;
7054 if !mapped.is_undefined() {
7055 result.push(mapped);
7056 }
7057 }
7058 return Ok(JValue::array(result));
7059 }
7060 }
7061 }
7062 }
7063 }
7064
7065 let arr_value = if param_count >= 3 {
7067 Some(JValue::Array(arr.clone()))
7068 } else {
7069 None
7070 };
7071
7072 let mut result = Vec::with_capacity(arr.len());
7073 for (index, item) in arr.iter().enumerate() {
7074 let call_args = match param_count {
7076 1 => vec![item.clone()],
7077 2 => vec![item.clone(), JValue::Number(index as f64)],
7078 _ => vec![
7079 item.clone(),
7080 JValue::Number(index as f64),
7081 arr_value.as_ref().unwrap().clone(),
7082 ],
7083 };
7084
7085 let mapped = self.apply_function(&args[1], &call_args, data)?;
7086 if !mapped.is_undefined() {
7089 result.push(mapped);
7090 }
7091 }
7092 Ok(JValue::array(result))
7093 }
7094 JValue::Null => Ok(JValue::Null),
7095 JValue::Undefined => Ok(JValue::Undefined),
7096 _ => Err(EvaluatorError::TypeError(
7097 "map() first argument must be an array".to_string(),
7098 )),
7099 }
7100 }
7101
7102 "filter" => {
7103 if args.len() != 2 {
7104 return Err(EvaluatorError::EvaluationError(
7105 "filter() requires exactly 2 arguments".to_string(),
7106 ));
7107 }
7108
7109 let array = self.evaluate_internal(&args[0], data)?;
7111
7112 if array.is_undefined() {
7114 return Ok(JValue::Undefined);
7115 }
7116
7117 if array.is_null() {
7119 return Ok(JValue::Undefined);
7120 }
7121
7122 let single_holder;
7126 let (items, was_single_value): (&[JValue], bool) = match &array {
7127 JValue::Array(arr) => (arr.as_slice(), false),
7128 _ => {
7129 single_holder = [array];
7130 (&single_holder[..], true)
7131 }
7132 };
7133
7134 let param_count = self.get_callback_param_count(&args[1]);
7136
7137 if param_count == 1 {
7139 if let AstNode::Lambda {
7140 params,
7141 body,
7142 signature: None,
7143 thunk: false,
7144 } = &args[1]
7145 {
7146 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
7147 if let Some(compiled) = try_compile_expr_with_allowed_vars(body, &var_refs)
7148 {
7149 let param_name = params[0].as_str();
7150 let mut result = Vec::with_capacity(items.len() / 2);
7151 let mut vars = HashMap::new();
7152 for item in items.iter() {
7153 vars.insert(param_name, item);
7154 let pred_result = eval_compiled(&compiled, data, Some(&vars))?;
7155 if compiled_is_truthy(&pred_result) {
7156 result.push(item.clone());
7157 }
7158 }
7159 if was_single_value {
7160 if result.len() == 1 {
7161 return Ok(result.remove(0));
7162 } else if result.is_empty() {
7163 return Ok(JValue::Undefined);
7164 }
7165 }
7166 return Ok(JValue::array(result));
7167 }
7168 }
7169 if let AstNode::Variable(var_name) = &args[1] {
7171 if let Some(stored) = self.context.lookup_lambda(var_name) {
7172 if let Some(ref ce) = stored.compiled_body.clone() {
7173 let param_name = stored.params[0].clone();
7174 let captured_data = stored.captured_data.clone();
7175 let captured_env_clone = stored.captured_env.clone();
7176 let ce_clone = ce.clone();
7177 if !captured_env_clone.values().any(|v| {
7178 matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. })
7179 }) {
7180 let call_data = captured_data.as_ref().unwrap_or(data);
7181 let mut result = Vec::with_capacity(items.len() / 2);
7182 let mut vars: HashMap<&str, &JValue> = captured_env_clone
7183 .iter()
7184 .map(|(k, v)| (k.as_str(), v))
7185 .collect();
7186 for item in items.iter() {
7187 vars.insert(param_name.as_str(), item);
7188 let pred_result =
7189 eval_compiled(&ce_clone, call_data, Some(&vars))?;
7190 if compiled_is_truthy(&pred_result) {
7191 result.push(item.clone());
7192 }
7193 }
7194 if was_single_value {
7195 if result.len() == 1 {
7196 return Ok(result.remove(0));
7197 } else if result.is_empty() {
7198 return Ok(JValue::Undefined);
7199 }
7200 }
7201 return Ok(JValue::array(result));
7202 }
7203 }
7204 }
7205 }
7206 }
7207
7208 let arr_value = if param_count >= 3 {
7210 Some(JValue::array(items.to_vec()))
7211 } else {
7212 None
7213 };
7214
7215 let mut result = Vec::with_capacity(items.len() / 2);
7216
7217 for (index, item) in items.iter().enumerate() {
7218 let call_args = match param_count {
7220 1 => vec![item.clone()],
7221 2 => vec![item.clone(), JValue::Number(index as f64)],
7222 _ => vec![
7223 item.clone(),
7224 JValue::Number(index as f64),
7225 arr_value.as_ref().unwrap().clone(),
7226 ],
7227 };
7228
7229 let predicate_result = self.apply_function(&args[1], &call_args, data)?;
7230 if self.is_truthy(&predicate_result) {
7231 result.push(item.clone());
7232 }
7233 }
7234
7235 if was_single_value {
7238 if result.len() == 1 {
7239 return Ok(result.remove(0));
7240 } else if result.is_empty() {
7241 return Ok(JValue::Undefined);
7242 }
7243 }
7244
7245 Ok(JValue::array(result))
7246 }
7247
7248 "reduce" => {
7249 if args.len() < 2 || args.len() > 3 {
7250 return Err(EvaluatorError::EvaluationError(
7251 "reduce() requires 2 or 3 arguments".to_string(),
7252 ));
7253 }
7254
7255 if let AstNode::Lambda { params, .. } = &args[1] {
7257 if params.len() < 2 {
7258 return Err(EvaluatorError::EvaluationError(
7259 "D3050: The second argument of reduce must be a function with at least two arguments".to_string(),
7260 ));
7261 }
7262 } else if let AstNode::Function { name, .. } = &args[1] {
7263 let _ = name; }
7267
7268 let array = self.evaluate_internal(&args[0], data)?;
7270
7271 let single_holder;
7274 let items: &[JValue] = match &array {
7275 JValue::Array(arr) => arr.as_slice(),
7276 JValue::Null => return Ok(JValue::Null),
7277 _ => {
7278 single_holder = [array];
7279 &single_holder[..]
7280 }
7281 };
7282
7283 if items.is_empty() {
7284 return if args.len() == 3 {
7286 self.evaluate_internal(&args[2], data)
7287 } else {
7288 Ok(JValue::Null)
7289 };
7290 }
7291
7292 let mut accumulator = if args.len() == 3 {
7294 self.evaluate_internal(&args[2], data)?
7295 } else {
7296 items[0].clone()
7297 };
7298
7299 let start_idx = if args.len() == 3 { 0 } else { 1 };
7300
7301 let param_count = self.get_callback_param_count(&args[1]);
7303
7304 if param_count == 2 {
7306 if let AstNode::Lambda {
7307 params,
7308 body,
7309 signature: None,
7310 thunk: false,
7311 } = &args[1]
7312 {
7313 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
7314 if let Some(compiled) = try_compile_expr_with_allowed_vars(body, &var_refs)
7315 {
7316 let acc_name = params[0].as_str();
7317 let item_name = params[1].as_str();
7318 for item in items[start_idx..].iter() {
7319 let vars: HashMap<&str, &JValue> =
7320 HashMap::from([(acc_name, &accumulator), (item_name, item)]);
7321 accumulator = eval_compiled(&compiled, data, Some(&vars))?;
7322 }
7323 return Ok(accumulator);
7324 }
7325 }
7326 if let AstNode::Variable(var_name) = &args[1] {
7328 if let Some(stored) = self.context.lookup_lambda(var_name) {
7329 if stored.params.len() == 2 {
7330 if let Some(ref ce) = stored.compiled_body.clone() {
7331 let acc_param = stored.params[0].clone();
7332 let item_param = stored.params[1].clone();
7333 let captured_data = stored.captured_data.clone();
7334 let captured_env_clone = stored.captured_env.clone();
7335 let ce_clone = ce.clone();
7336 if !captured_env_clone.values().any(|v| {
7337 matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. })
7338 }) {
7339 let call_data = captured_data.as_ref().unwrap_or(data);
7340 for item in items[start_idx..].iter() {
7341 let mut vars: HashMap<&str, &JValue> =
7342 captured_env_clone
7343 .iter()
7344 .map(|(k, v)| (k.as_str(), v))
7345 .collect();
7346 vars.insert(acc_param.as_str(), &accumulator);
7347 vars.insert(item_param.as_str(), item);
7348 let new_acc =
7351 eval_compiled(&ce_clone, call_data, Some(&vars))?;
7352 drop(vars);
7353 accumulator = new_acc;
7354 }
7355 return Ok(accumulator);
7356 }
7357 }
7358 }
7359 }
7360 }
7361 }
7362
7363 let arr_value = if param_count >= 4 {
7365 Some(JValue::array(items.to_vec()))
7366 } else {
7367 None
7368 };
7369
7370 for (idx, item) in items[start_idx..].iter().enumerate() {
7372 let actual_idx = start_idx + idx;
7375
7376 let call_args = match param_count {
7378 2 => vec![accumulator.clone(), item.clone()],
7379 3 => vec![
7380 accumulator.clone(),
7381 item.clone(),
7382 JValue::Number(actual_idx as f64),
7383 ],
7384 _ => vec![
7385 accumulator.clone(),
7386 item.clone(),
7387 JValue::Number(actual_idx as f64),
7388 arr_value.as_ref().unwrap().clone(),
7389 ],
7390 };
7391
7392 accumulator = self.apply_function(&args[1], &call_args, data)?;
7393 }
7394
7395 Ok(accumulator)
7396 }
7397
7398 "single" => {
7399 if args.is_empty() || args.len() > 2 {
7400 return Err(EvaluatorError::EvaluationError(
7401 "single() requires 1 or 2 arguments".to_string(),
7402 ));
7403 }
7404
7405 let array = self.evaluate_internal(&args[0], data)?;
7407
7408 let arr = match array {
7410 JValue::Array(arr) => arr.to_vec(),
7411 JValue::Null => return Ok(JValue::Null),
7412 other => vec![other],
7413 };
7414
7415 if args.len() == 1 {
7416 match arr.len() {
7418 0 => Err(EvaluatorError::EvaluationError(
7419 "single() argument is empty".to_string(),
7420 )),
7421 1 => Ok(arr.into_iter().next().unwrap()),
7422 count => Err(EvaluatorError::EvaluationError(format!(
7423 "single() argument has {} values (expected exactly 1)",
7424 count
7425 ))),
7426 }
7427 } else {
7428 let arr_value = JValue::array(arr.clone());
7430 let mut matches = Vec::new();
7431 for (index, item) in arr.into_iter().enumerate() {
7432 let predicate_result = self.apply_function(
7434 &args[1],
7435 &[
7436 item.clone(),
7437 JValue::Number(index as f64),
7438 arr_value.clone(),
7439 ],
7440 data,
7441 )?;
7442 if self.is_truthy(&predicate_result) {
7443 matches.push(item);
7444 }
7445 }
7446
7447 match matches.len() {
7448 0 => Err(EvaluatorError::EvaluationError(
7449 "single() predicate matches no values".to_string(),
7450 )),
7451 1 => Ok(matches.into_iter().next().unwrap()),
7452 count => Err(EvaluatorError::EvaluationError(format!(
7453 "single() predicate matches {} values (expected exactly 1)",
7454 count
7455 ))),
7456 }
7457 }
7458 }
7459
7460 "each" => {
7461 if args.is_empty() || args.len() > 2 {
7464 return Err(EvaluatorError::EvaluationError(
7465 "each() requires 1 or 2 arguments".to_string(),
7466 ));
7467 }
7468
7469 let (obj_value, func_arg) = if args.len() == 1 {
7471 (data.clone(), &args[0])
7473 } else {
7474 (self.evaluate_internal(&args[0], data)?, &args[1])
7476 };
7477
7478 let param_count = self.get_callback_param_count(func_arg);
7480
7481 match obj_value {
7482 JValue::Object(obj) => {
7483 let mut result = Vec::new();
7484 for (key, value) in obj.iter() {
7485 let call_args = match param_count {
7488 1 => vec![value.clone()],
7489 _ => vec![value.clone(), JValue::string(key.clone())],
7490 };
7491
7492 let fn_result = self.apply_function(func_arg, &call_args, data)?;
7493 if !fn_result.is_null() && !fn_result.is_undefined() {
7495 result.push(fn_result);
7496 }
7497 }
7498 Ok(JValue::array(result))
7499 }
7500 JValue::Null => Ok(JValue::Null),
7501 _ => Err(EvaluatorError::TypeError(
7502 "each() first argument must be an object".to_string(),
7503 )),
7504 }
7505 }
7506
7507 "not" => {
7508 if evaluated_args.len() != 1 {
7509 return Err(EvaluatorError::EvaluationError(
7510 "not() requires exactly 1 argument".to_string(),
7511 ));
7512 }
7513 if evaluated_args[0].is_undefined() {
7516 return Ok(JValue::Undefined);
7517 }
7518 Ok(JValue::Bool(!self.is_truthy(&evaluated_args[0])))
7519 }
7520 "boolean" => {
7521 if evaluated_args.len() != 1 {
7522 return Err(EvaluatorError::EvaluationError(
7523 "boolean() requires exactly 1 argument".to_string(),
7524 ));
7525 }
7526 if evaluated_args[0].is_undefined() {
7527 return Ok(JValue::Undefined);
7528 }
7529 Ok(functions::boolean::boolean(&evaluated_args[0])?)
7530 }
7531 "type" => {
7532 if evaluated_args.len() != 1 {
7533 return Err(EvaluatorError::EvaluationError(
7534 "type() requires exactly 1 argument".to_string(),
7535 ));
7536 }
7537 match &evaluated_args[0] {
7541 JValue::Null => Ok(JValue::string("null")),
7542 JValue::Bool(_) => Ok(JValue::string("boolean")),
7543 JValue::Number(_) => Ok(JValue::string("number")),
7544 JValue::String(_) => Ok(JValue::string("string")),
7545 JValue::Array(_) => Ok(JValue::string("array")),
7546 JValue::Object(_) => Ok(JValue::string("object")),
7547 JValue::Undefined => Ok(JValue::Undefined),
7548 JValue::Lambda { .. } | JValue::Builtin { .. } => {
7549 Ok(JValue::string("function"))
7550 }
7551 JValue::Regex { .. } => Ok(JValue::string("regex")),
7552 }
7553 }
7554
7555 "base64encode" => {
7556 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7557 return Ok(JValue::Null);
7558 }
7559 if evaluated_args.len() != 1 {
7560 return Err(EvaluatorError::EvaluationError(
7561 "base64encode() requires exactly 1 argument".to_string(),
7562 ));
7563 }
7564 match &evaluated_args[0] {
7565 JValue::String(s) => Ok(functions::encoding::base64encode(s)?),
7566 _ => Err(EvaluatorError::TypeError(
7567 "base64encode() requires a string argument".to_string(),
7568 )),
7569 }
7570 }
7571 "base64decode" => {
7572 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7573 return Ok(JValue::Null);
7574 }
7575 if evaluated_args.len() != 1 {
7576 return Err(EvaluatorError::EvaluationError(
7577 "base64decode() requires exactly 1 argument".to_string(),
7578 ));
7579 }
7580 match &evaluated_args[0] {
7581 JValue::String(s) => Ok(functions::encoding::base64decode(s)?),
7582 _ => Err(EvaluatorError::TypeError(
7583 "base64decode() requires a string argument".to_string(),
7584 )),
7585 }
7586 }
7587 "encodeUrlComponent" => {
7588 if evaluated_args.len() != 1 {
7589 return Err(EvaluatorError::EvaluationError(
7590 "encodeUrlComponent() requires exactly 1 argument".to_string(),
7591 ));
7592 }
7593 if evaluated_args[0].is_null() {
7594 return Ok(JValue::Null);
7595 }
7596 if evaluated_args[0].is_undefined() {
7597 return Ok(JValue::Undefined);
7598 }
7599 match &evaluated_args[0] {
7600 JValue::String(s) => Ok(functions::encoding::encode_url_component(s)?),
7601 _ => Err(EvaluatorError::TypeError(
7602 "encodeUrlComponent() requires a string argument".to_string(),
7603 )),
7604 }
7605 }
7606 "decodeUrlComponent" => {
7607 if evaluated_args.len() != 1 {
7608 return Err(EvaluatorError::EvaluationError(
7609 "decodeUrlComponent() requires exactly 1 argument".to_string(),
7610 ));
7611 }
7612 if evaluated_args[0].is_null() {
7613 return Ok(JValue::Null);
7614 }
7615 if evaluated_args[0].is_undefined() {
7616 return Ok(JValue::Undefined);
7617 }
7618 match &evaluated_args[0] {
7619 JValue::String(s) => Ok(functions::encoding::decode_url_component(s)?),
7620 _ => Err(EvaluatorError::TypeError(
7621 "decodeUrlComponent() requires a string argument".to_string(),
7622 )),
7623 }
7624 }
7625 "encodeUrl" => {
7626 if evaluated_args.len() != 1 {
7627 return Err(EvaluatorError::EvaluationError(
7628 "encodeUrl() requires exactly 1 argument".to_string(),
7629 ));
7630 }
7631 if evaluated_args[0].is_null() {
7632 return Ok(JValue::Null);
7633 }
7634 if evaluated_args[0].is_undefined() {
7635 return Ok(JValue::Undefined);
7636 }
7637 match &evaluated_args[0] {
7638 JValue::String(s) => Ok(functions::encoding::encode_url(s)?),
7639 _ => Err(EvaluatorError::TypeError(
7640 "encodeUrl() requires a string argument".to_string(),
7641 )),
7642 }
7643 }
7644 "decodeUrl" => {
7645 if evaluated_args.len() != 1 {
7646 return Err(EvaluatorError::EvaluationError(
7647 "decodeUrl() requires exactly 1 argument".to_string(),
7648 ));
7649 }
7650 if evaluated_args[0].is_null() {
7651 return Ok(JValue::Null);
7652 }
7653 if evaluated_args[0].is_undefined() {
7654 return Ok(JValue::Undefined);
7655 }
7656 match &evaluated_args[0] {
7657 JValue::String(s) => Ok(functions::encoding::decode_url(s)?),
7658 _ => Err(EvaluatorError::TypeError(
7659 "decodeUrl() requires a string argument".to_string(),
7660 )),
7661 }
7662 }
7663
7664 "error" => {
7665 if evaluated_args.is_empty() {
7667 return Err(EvaluatorError::EvaluationError(
7669 "D3137: $error() function evaluated".to_string(),
7670 ));
7671 }
7672
7673 match &evaluated_args[0] {
7674 JValue::String(s) => {
7675 Err(EvaluatorError::EvaluationError(format!("D3137: {}", s)))
7676 }
7677 _ => Err(EvaluatorError::TypeError(
7678 "T0410: Argument 1 of function error does not match function signature"
7679 .to_string(),
7680 )),
7681 }
7682 }
7683 "assert" => {
7684 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7686 return Err(EvaluatorError::EvaluationError(
7687 "assert() requires 1 or 2 arguments".to_string(),
7688 ));
7689 }
7690
7691 let condition = match &evaluated_args[0] {
7693 JValue::Bool(b) => *b,
7694 _ => {
7695 return Err(EvaluatorError::TypeError(
7696 "T0410: Argument 1 of function $assert does not match function signature".to_string(),
7697 ));
7698 }
7699 };
7700
7701 if !condition {
7702 let message = if evaluated_args.len() == 2 {
7703 match &evaluated_args[1] {
7704 JValue::String(s) => s.clone(),
7705 _ => Rc::from("$assert() statement failed"),
7706 }
7707 } else {
7708 Rc::from("$assert() statement failed")
7709 };
7710 return Err(EvaluatorError::EvaluationError(format!(
7711 "D3141: {}",
7712 message
7713 )));
7714 }
7715
7716 Ok(JValue::Null)
7717 }
7718
7719 "eval" => {
7720 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7722 return Err(EvaluatorError::EvaluationError(
7723 "T0410: Argument 1 of function $eval must be a string".to_string(),
7724 ));
7725 }
7726
7727 if evaluated_args[0].is_null() {
7729 return Ok(JValue::Null);
7730 }
7731 if evaluated_args[0].is_undefined() {
7732 return Ok(JValue::Undefined);
7733 }
7734
7735 let expr_str = match &evaluated_args[0] {
7737 JValue::String(s) => &**s,
7738 _ => {
7739 return Err(EvaluatorError::EvaluationError(
7740 "T0410: Argument 1 of function $eval must be a string".to_string(),
7741 ));
7742 }
7743 };
7744
7745 let parsed_ast = match parser::parse(expr_str) {
7747 Ok(ast) => ast,
7748 Err(e) => {
7749 return Err(EvaluatorError::EvaluationError(format!(
7751 "D3120: The expression passed to $eval cannot be parsed: {}",
7752 e
7753 )));
7754 }
7755 };
7756
7757 let eval_context = if evaluated_args.len() == 2 {
7759 &evaluated_args[1]
7760 } else {
7761 data
7762 };
7763
7764 match self.evaluate_internal(&parsed_ast, eval_context) {
7766 Ok(result) => Ok(result),
7767 Err(e) => {
7768 let err_msg = e.to_string();
7770 if err_msg.starts_with("D3121") || err_msg.contains("Unknown function") {
7771 Err(EvaluatorError::EvaluationError(format!(
7772 "D3121: {}",
7773 err_msg
7774 )))
7775 } else {
7776 Err(e)
7777 }
7778 }
7779 }
7780 }
7781
7782 "now" => {
7783 if !evaluated_args.is_empty() {
7784 return Err(EvaluatorError::EvaluationError(
7785 "now() takes no arguments".to_string(),
7786 ));
7787 }
7788 Ok(crate::datetime::now())
7789 }
7790
7791 "millis" => {
7792 if !evaluated_args.is_empty() {
7793 return Err(EvaluatorError::EvaluationError(
7794 "millis() takes no arguments".to_string(),
7795 ));
7796 }
7797 Ok(crate::datetime::millis())
7798 }
7799
7800 "toMillis" => {
7801 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7802 return Err(EvaluatorError::EvaluationError(
7803 "toMillis() requires 1 or 2 arguments".to_string(),
7804 ));
7805 }
7806
7807 match &evaluated_args[0] {
7808 JValue::String(s) => {
7809 if evaluated_args.len() == 2 {
7811 match &evaluated_args[1] {
7812 JValue::String(picture) => {
7813 Ok(crate::datetime::to_millis_with_picture(s, picture)?)
7815 }
7816 JValue::Null => Ok(JValue::Null),
7817 JValue::Undefined => Ok(JValue::Undefined),
7818 _ => Err(EvaluatorError::TypeError(
7819 "toMillis() second argument must be a string".to_string(),
7820 )),
7821 }
7822 } else {
7823 Ok(crate::datetime::to_millis(s)?)
7825 }
7826 }
7827 JValue::Null => Ok(JValue::Null),
7828 JValue::Undefined => Ok(JValue::Undefined),
7829 _ => Err(EvaluatorError::TypeError(
7830 "toMillis() requires a string argument".to_string(),
7831 )),
7832 }
7833 }
7834
7835 "fromMillis" => {
7836 if evaluated_args.len() != 1 {
7837 return Err(EvaluatorError::EvaluationError(
7838 "fromMillis() requires exactly 1 argument".to_string(),
7839 ));
7840 }
7841
7842 match &evaluated_args[0] {
7843 JValue::Number(n) => {
7844 let millis = (if n.fract() == 0.0 {
7845 Ok(*n as i64)
7846 } else {
7847 Err(())
7848 })
7849 .map_err(|_| {
7850 EvaluatorError::TypeError(
7851 "fromMillis() requires an integer".to_string(),
7852 )
7853 })?;
7854 Ok(crate::datetime::from_millis(millis)?)
7855 }
7856 JValue::Null => Ok(JValue::Null),
7857 JValue::Undefined => Ok(JValue::Undefined),
7858 _ => Err(EvaluatorError::TypeError(
7859 "fromMillis() requires a number argument".to_string(),
7860 )),
7861 }
7862 }
7863
7864 _ => Err(EvaluatorError::ReferenceError(format!(
7865 "Unknown function: {}",
7866 name
7867 ))),
7868 }
7869 }
7870
7871 fn apply_function(
7877 &mut self,
7878 func_node: &AstNode,
7879 values: &[JValue],
7880 data: &JValue,
7881 ) -> Result<JValue, EvaluatorError> {
7882 match func_node {
7883 AstNode::Lambda {
7884 params,
7885 body,
7886 signature,
7887 thunk,
7888 } => {
7889 self.invoke_lambda(params, body, signature.as_ref(), values, data, *thunk)
7891 }
7892 AstNode::Function {
7893 name,
7894 args,
7895 is_builtin,
7896 } => {
7897 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
7899
7900 if has_placeholder {
7901 let partial_lambda =
7903 self.create_partial_application(name, args, *is_builtin, data)?;
7904
7905 if let Some(stored) = self.lookup_lambda_from_value(&partial_lambda) {
7907 return self.invoke_stored_lambda(&stored, values, data);
7908 }
7909 Err(EvaluatorError::EvaluationError(
7910 "Failed to apply partial application".to_string(),
7911 ))
7912 } else {
7913 let result = self.evaluate_internal(func_node, data)?;
7916
7917 if let Some(stored) = self.lookup_lambda_from_value(&result) {
7919 return self.invoke_stored_lambda(&stored, values, data);
7920 }
7921
7922 Ok(result)
7924 }
7925 }
7926 AstNode::Variable(var_name) => {
7927 if let Some(stored_lambda) = self.context.lookup_lambda(var_name).cloned() {
7929 self.invoke_stored_lambda(&stored_lambda, values, data)
7930 } else if let Some(value) = self.context.lookup(var_name).cloned() {
7931 if let Some(stored) = self.lookup_lambda_from_value(&value) {
7934 return self.invoke_stored_lambda(&stored, values, data);
7935 }
7936 if values.is_empty() {
7938 self.evaluate_internal(func_node, data)
7939 } else {
7940 self.evaluate_internal(func_node, &values[0])
7941 }
7942 } else if self.is_builtin_function(var_name) {
7943 self.call_builtin_with_values(var_name, values)
7946 } else {
7947 if values.is_empty() {
7949 self.evaluate_internal(func_node, data)
7950 } else {
7951 self.evaluate_internal(func_node, &values[0])
7952 }
7953 }
7954 }
7955 _ => {
7956 if values.is_empty() {
7958 self.evaluate_internal(func_node, data)
7959 } else {
7960 self.evaluate_internal(func_node, &values[0])
7961 }
7962 }
7963 }
7964 }
7965
7966 fn execute_transform(
7968 &mut self,
7969 location: &AstNode,
7970 update: &AstNode,
7971 delete: Option<&AstNode>,
7972 _original_data: &JValue,
7973 ) -> Result<JValue, EvaluatorError> {
7974 let input = self
7976 .context
7977 .lookup("$")
7978 .ok_or_else(|| {
7979 EvaluatorError::EvaluationError("Transform requires $ binding".to_string())
7980 })?
7981 .clone();
7982
7983 let located_objects = self.evaluate_internal(location, &input)?;
7985
7986 let targets: Vec<JValue> = match located_objects {
7988 JValue::Array(arr) => arr.to_vec(),
7989 JValue::Object(_) => vec![located_objects],
7990 JValue::Null => Vec::new(),
7991 other => vec![other],
7992 };
7993
7994 let delete_fields: Vec<String> = if let Some(delete_node) = delete {
8000 let delete_val = self.evaluate_internal(delete_node, &input)?;
8001 match delete_val {
8002 JValue::Array(arr) => arr
8003 .iter()
8004 .filter_map(|v| match v {
8005 JValue::String(s) => Some(s.to_string()),
8006 _ => None,
8007 })
8008 .collect(),
8009 JValue::String(s) => vec![s.to_string()],
8010 JValue::Null | JValue::Undefined => Vec::new(), _ => {
8012 return Err(EvaluatorError::EvaluationError(
8014 "T2012: The third argument of the transform operator must be an array of strings".to_string()
8015 ));
8016 }
8017 }
8018 } else {
8019 Vec::new()
8020 };
8021
8022 fn apply_transform_deep(
8024 evaluator: &mut Evaluator,
8025 value: &JValue,
8026 targets: &[JValue],
8027 update: &AstNode,
8028 delete_fields: &[String],
8029 ) -> Result<JValue, EvaluatorError> {
8030 if targets.iter().any(|t| t == value) {
8033 if let JValue::Object(map_rc) = value.clone() {
8035 let mut map = (*map_rc).clone();
8036 let update_val = evaluator.evaluate_internal(update, value)?;
8037 match update_val {
8039 JValue::Object(update_map) => {
8040 for (key, val) in update_map.iter() {
8041 map.insert(key.clone(), val.clone());
8042 }
8043 }
8044 JValue::Null | JValue::Undefined => {
8045 }
8047 _ => {
8048 return Err(EvaluatorError::EvaluationError(
8049 "T2011: The second argument of the transform operator must evaluate to an object".to_string()
8050 ));
8051 }
8052 }
8053 for field in delete_fields {
8054 map.shift_remove(field);
8055 }
8056 return Ok(JValue::object(map));
8057 }
8058 return Ok(value.clone());
8059 }
8060
8061 match value {
8063 JValue::Object(map) => {
8064 let mut new_map = IndexMap::new();
8065 for (k, v) in map.iter() {
8066 new_map.insert(
8067 k.clone(),
8068 apply_transform_deep(evaluator, v, targets, update, delete_fields)?,
8069 );
8070 }
8071 Ok(JValue::object(new_map))
8072 }
8073 JValue::Array(arr) => {
8074 let mut new_arr = Vec::new();
8075 for item in arr.iter() {
8076 new_arr.push(apply_transform_deep(
8077 evaluator,
8078 item,
8079 targets,
8080 update,
8081 delete_fields,
8082 )?);
8083 }
8084 Ok(JValue::array(new_arr))
8085 }
8086 _ => Ok(value.clone()),
8087 }
8088 }
8089
8090 apply_transform_deep(self, &input, &targets, update, &delete_fields)
8092 }
8093
8094 fn invoke_lambda(
8096 &mut self,
8097 params: &[String],
8098 body: &AstNode,
8099 signature: Option<&String>,
8100 values: &[JValue],
8101 data: &JValue,
8102 thunk: bool,
8103 ) -> Result<JValue, EvaluatorError> {
8104 self.invoke_lambda_with_env(params, body, signature, values, data, None, None, thunk)
8105 }
8106
8107 fn invoke_lambda_with_env(
8109 &mut self,
8110 params: &[String],
8111 body: &AstNode,
8112 signature: Option<&String>,
8113 values: &[JValue],
8114 data: &JValue,
8115 captured_env: Option<&HashMap<String, JValue>>,
8116 captured_data: Option<&JValue>,
8117 thunk: bool,
8118 ) -> Result<JValue, EvaluatorError> {
8119 if thunk {
8121 let stored = StoredLambda {
8122 params: params.to_vec(),
8123 body: body.clone(),
8124 compiled_body: None, signature: signature.cloned(),
8126 captured_env: captured_env.cloned().unwrap_or_default(),
8127 captured_data: captured_data.cloned(),
8128 thunk,
8129 };
8130 return self.invoke_lambda_with_tco(&stored, values, data);
8131 }
8132
8133 self.context.push_scope();
8136
8137 if let Some(env) = captured_env {
8139 for (name, value) in env {
8140 self.context.bind(name.clone(), value.clone());
8141 }
8142 }
8143
8144 if let Some(sig_str) = signature {
8145 let coerced_values = match crate::signature::Signature::parse(sig_str) {
8147 Ok(sig) => match sig.validate_and_coerce(values) {
8148 Ok(coerced) => coerced,
8149 Err(e) => {
8150 self.context.pop_scope();
8151 match e {
8152 crate::signature::SignatureError::UndefinedArgument => {
8153 return Ok(JValue::Null);
8154 }
8155 crate::signature::SignatureError::ArgumentTypeMismatch {
8156 index,
8157 expected,
8158 } => {
8159 return Err(EvaluatorError::TypeError(
8160 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
8161 ));
8162 }
8163 crate::signature::SignatureError::ArrayTypeMismatch {
8164 index,
8165 expected,
8166 } => {
8167 return Err(EvaluatorError::TypeError(format!(
8168 "T0412: Argument {} of function must be an array of {}",
8169 index, expected
8170 )));
8171 }
8172 _ => {
8173 return Err(EvaluatorError::TypeError(format!(
8174 "Signature validation failed: {}",
8175 e
8176 )));
8177 }
8178 }
8179 }
8180 },
8181 Err(e) => {
8182 self.context.pop_scope();
8183 return Err(EvaluatorError::EvaluationError(format!(
8184 "Invalid signature: {}",
8185 e
8186 )));
8187 }
8188 };
8189 for (i, param) in params.iter().enumerate() {
8191 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Undefined);
8192 self.context.bind(param.clone(), value);
8193 }
8194 } else {
8195 for (i, param) in params.iter().enumerate() {
8197 let value = values.get(i).cloned().unwrap_or(JValue::Undefined);
8198 self.context.bind(param.clone(), value);
8199 }
8200 }
8201
8202 if let AstNode::String(body_str) = body {
8204 if body_str.starts_with("__partial_call:") {
8205 let parts: Vec<&str> = body_str.split(':').collect();
8207 if parts.len() >= 4 {
8208 let func_name = parts[1];
8209 let is_builtin = parts[2] == "true";
8210 let total_args: usize = parts[3].parse().unwrap_or(0);
8211
8212 let placeholder_positions: Vec<usize> = if let Some(env) = captured_env {
8214 if let Some(JValue::Array(positions)) = env.get("__placeholder_positions") {
8215 positions
8216 .iter()
8217 .filter_map(|v| v.as_f64().map(|n| n as usize))
8218 .collect()
8219 } else {
8220 vec![]
8221 }
8222 } else {
8223 vec![]
8224 };
8225
8226 let mut full_args: Vec<JValue> = vec![JValue::Null; total_args];
8228
8229 if let Some(env) = captured_env {
8231 for (key, value) in env {
8232 if key.starts_with("__bound_arg_") {
8233 if let Ok(pos) = key[12..].parse::<usize>() {
8234 if pos < total_args {
8235 full_args[pos] = value.clone();
8236 }
8237 }
8238 }
8239 }
8240 }
8241
8242 for (i, &pos) in placeholder_positions.iter().enumerate() {
8244 if pos < total_args {
8245 let value = values.get(i).cloned().unwrap_or(JValue::Null);
8246 full_args[pos] = value;
8247 }
8248 }
8249
8250 self.context.pop_scope();
8252 self.context.push_scope();
8253
8254 let mut temp_args: Vec<AstNode> = Vec::new();
8256 for (i, value) in full_args.iter().enumerate() {
8257 let temp_name = format!("__temp_arg_{}", i);
8258 self.context.bind(temp_name.clone(), value.clone());
8259 temp_args.push(AstNode::Variable(temp_name));
8260 }
8261
8262 let result =
8264 self.evaluate_function_call(func_name, &temp_args, is_builtin, data);
8265
8266 self.context.pop_scope();
8268
8269 return result;
8270 }
8271 }
8272 }
8273
8274 let body_data = captured_data.unwrap_or(data);
8277 let result = self.evaluate_internal(body, body_data)?;
8278
8279 let is_scalar = matches!(
8282 &result,
8283 JValue::Number(_)
8284 | JValue::Bool(_)
8285 | JValue::String(_)
8286 | JValue::Null
8287 | JValue::Undefined
8288 );
8289 if is_scalar {
8290 self.context.pop_scope();
8291 } else {
8292 let lambdas_to_keep = self.extract_lambda_ids(&result);
8293 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
8294 }
8295
8296 Ok(result)
8297 }
8298
8299 fn invoke_lambda_with_tco(
8303 &mut self,
8304 stored_lambda: &StoredLambda,
8305 initial_args: &[JValue],
8306 data: &JValue,
8307 ) -> Result<JValue, EvaluatorError> {
8308 let mut current_lambda = stored_lambda.clone();
8309 let mut current_args = initial_args.to_vec();
8310 let mut current_data = data.clone();
8311
8312 const MAX_TCO_ITERATIONS: usize = 100_000;
8315 let mut iterations = 0;
8316
8317 self.context.push_scope();
8321
8322 let result = loop {
8324 iterations += 1;
8325 if iterations > MAX_TCO_ITERATIONS {
8326 self.context.pop_scope();
8327 return Err(EvaluatorError::EvaluationError(
8328 "U1001: Stack overflow - maximum recursion depth (500) exceeded".to_string(),
8329 ));
8330 }
8331
8332 let result =
8334 self.invoke_lambda_body_for_tco(¤t_lambda, ¤t_args, ¤t_data)?;
8335
8336 match result {
8337 LambdaResult::JValue(v) => break v,
8338 LambdaResult::TailCall { lambda, args, data } => {
8339 current_lambda = *lambda;
8341 current_args = args;
8342 current_data = data;
8343 }
8344 }
8345 };
8346
8347 let lambdas_to_keep = self.extract_lambda_ids(&result);
8349 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
8350
8351 Ok(result)
8352 }
8353
8354 fn invoke_lambda_body_for_tco(
8359 &mut self,
8360 lambda: &StoredLambda,
8361 values: &[JValue],
8362 data: &JValue,
8363 ) -> Result<LambdaResult, EvaluatorError> {
8364 let coerced_values = if let Some(sig_str) = &lambda.signature {
8366 match crate::signature::Signature::parse(sig_str) {
8367 Ok(sig) => match sig.validate_and_coerce(values) {
8368 Ok(coerced) => coerced,
8369 Err(e) => match e {
8370 crate::signature::SignatureError::UndefinedArgument => {
8371 return Ok(LambdaResult::JValue(JValue::Null));
8372 }
8373 crate::signature::SignatureError::ArgumentTypeMismatch {
8374 index,
8375 expected,
8376 } => {
8377 return Err(EvaluatorError::TypeError(
8378 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
8379 ));
8380 }
8381 crate::signature::SignatureError::ArrayTypeMismatch { index, expected } => {
8382 return Err(EvaluatorError::TypeError(format!(
8383 "T0412: Argument {} of function must be an array of {}",
8384 index, expected
8385 )));
8386 }
8387 _ => {
8388 return Err(EvaluatorError::TypeError(format!(
8389 "Signature validation failed: {}",
8390 e
8391 )));
8392 }
8393 },
8394 },
8395 Err(e) => {
8396 return Err(EvaluatorError::EvaluationError(format!(
8397 "Invalid signature: {}",
8398 e
8399 )));
8400 }
8401 }
8402 } else {
8403 values.to_vec()
8404 };
8405
8406 for (name, value) in &lambda.captured_env {
8409 self.context.bind(name.clone(), value.clone());
8410 }
8411
8412 for (i, param) in lambda.params.iter().enumerate() {
8414 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Null);
8415 self.context.bind(param.clone(), value);
8416 }
8417
8418 let body_data = lambda.captured_data.as_ref().unwrap_or(data);
8420 self.evaluate_for_tco(&lambda.body, body_data)
8421 }
8422
8423 fn evaluate_for_tco(
8426 &mut self,
8427 node: &AstNode,
8428 data: &JValue,
8429 ) -> Result<LambdaResult, EvaluatorError> {
8430 match node {
8431 AstNode::Conditional {
8433 condition,
8434 then_branch,
8435 else_branch,
8436 } => {
8437 let cond_value = self.evaluate_internal(condition, data)?;
8438 let is_truthy = self.is_truthy(&cond_value);
8439
8440 if is_truthy {
8441 self.evaluate_for_tco(then_branch, data)
8442 } else if let Some(else_expr) = else_branch {
8443 self.evaluate_for_tco(else_expr, data)
8444 } else {
8445 Ok(LambdaResult::JValue(JValue::Null))
8446 }
8447 }
8448
8449 AstNode::Block(exprs) => {
8451 if exprs.is_empty() {
8452 return Ok(LambdaResult::JValue(JValue::Null));
8453 }
8454
8455 let mut result = JValue::Null;
8457 for (i, expr) in exprs.iter().enumerate() {
8458 if i == exprs.len() - 1 {
8459 return self.evaluate_for_tco(expr, data);
8461 } else {
8462 result = self.evaluate_internal(expr, data)?;
8463 }
8464 }
8465 Ok(LambdaResult::JValue(result))
8466 }
8467
8468 AstNode::Binary {
8470 op: BinaryOp::ColonEqual,
8471 lhs,
8472 rhs,
8473 } => {
8474 let var_name = match lhs.as_ref() {
8476 AstNode::Variable(name) => name.clone(),
8477 _ => {
8478 let result = self.evaluate_internal(node, data)?;
8480 return Ok(LambdaResult::JValue(result));
8481 }
8482 };
8483
8484 if let AstNode::Lambda {
8486 params,
8487 body,
8488 signature,
8489 thunk,
8490 } = rhs.as_ref()
8491 {
8492 let captured_env = self.capture_environment_for(body, params);
8493 let compiled_body = if !thunk {
8494 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
8495 try_compile_expr_with_allowed_vars(body, &var_refs)
8496 } else {
8497 None
8498 };
8499 let stored_lambda = StoredLambda {
8500 params: params.clone(),
8501 body: (**body).clone(),
8502 compiled_body,
8503 signature: signature.clone(),
8504 captured_env,
8505 captured_data: Some(data.clone()),
8506 thunk: *thunk,
8507 };
8508 self.context.bind_lambda(var_name, stored_lambda);
8509 let lambda_repr =
8510 JValue::lambda("anon", params.clone(), None::<String>, None::<String>);
8511 return Ok(LambdaResult::JValue(lambda_repr));
8512 }
8513
8514 let value = self.evaluate_internal(rhs, data)?;
8516 self.context.bind(var_name, value.clone());
8517 Ok(LambdaResult::JValue(value))
8518 }
8519
8520 AstNode::Function { name, args, .. } => {
8522 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
8524 if stored_lambda.thunk {
8525 let mut evaluated_args = Vec::with_capacity(args.len());
8526 for arg in args {
8527 evaluated_args.push(self.evaluate_internal(arg, data)?);
8528 }
8529 return Ok(LambdaResult::TailCall {
8530 lambda: Box::new(stored_lambda),
8531 args: evaluated_args,
8532 data: data.clone(),
8533 });
8534 }
8535 }
8536 let result = self.evaluate_internal(node, data)?;
8538 Ok(LambdaResult::JValue(result))
8539 }
8540
8541 AstNode::Call { procedure, args } => {
8543 let callable = self.evaluate_internal(procedure, data)?;
8545
8546 if let JValue::Lambda { lambda_id, .. } = &callable {
8548 if let Some(stored_lambda) = self.context.lookup_lambda(lambda_id).cloned() {
8549 if stored_lambda.thunk {
8550 let mut evaluated_args = Vec::with_capacity(args.len());
8551 for arg in args {
8552 evaluated_args.push(self.evaluate_internal(arg, data)?);
8553 }
8554 return Ok(LambdaResult::TailCall {
8555 lambda: Box::new(stored_lambda),
8556 args: evaluated_args,
8557 data: data.clone(),
8558 });
8559 }
8560 }
8561 }
8562 let result = self.evaluate_internal(node, data)?;
8564 Ok(LambdaResult::JValue(result))
8565 }
8566
8567 AstNode::Variable(_) => {
8570 let result = self.evaluate_internal(node, data)?;
8571 Ok(LambdaResult::JValue(result))
8572 }
8573
8574 _ => {
8576 let result = self.evaluate_internal(node, data)?;
8577 Ok(LambdaResult::JValue(result))
8578 }
8579 }
8580 }
8581
8582 fn match_with_custom_matcher(
8589 &mut self,
8590 str_value: &str,
8591 matcher_node: &AstNode,
8592 limit: Option<usize>,
8593 data: &JValue,
8594 ) -> Result<JValue, EvaluatorError> {
8595 let mut results = Vec::new();
8596 let mut count = 0;
8597
8598 let str_val = JValue::string(str_value.to_string());
8600 let mut current_match = self.apply_function(matcher_node, &[str_val], data)?;
8601
8602 while !current_match.is_undefined() && !current_match.is_null() {
8604 if let Some(lim) = limit {
8606 if count >= lim {
8607 break;
8608 }
8609 }
8610
8611 if let JValue::Object(ref match_obj) = current_match {
8613 let has_match = match_obj.contains_key("match");
8615 let has_start = match_obj.contains_key("start");
8616 let has_end = match_obj.contains_key("end");
8617 let has_groups = match_obj.contains_key("groups");
8618 let has_next = match_obj.contains_key("next");
8619
8620 if !has_match && !has_start && !has_end && !has_groups && !has_next {
8621 return Err(EvaluatorError::EvaluationError(
8623 "T1010: The matcher function did not return the correct object structure"
8624 .to_string(),
8625 ));
8626 }
8627
8628 let mut result_obj = IndexMap::new();
8630
8631 if let Some(match_val) = match_obj.get("match") {
8632 result_obj.insert("match".to_string(), match_val.clone());
8633 }
8634
8635 if let Some(start_val) = match_obj.get("start") {
8636 result_obj.insert("index".to_string(), start_val.clone());
8637 }
8638
8639 if let Some(groups_val) = match_obj.get("groups") {
8640 result_obj.insert("groups".to_string(), groups_val.clone());
8641 }
8642
8643 results.push(JValue::object(result_obj));
8644 count += 1;
8645
8646 if let Some(next_func) = match_obj.get("next") {
8648 if let Some(stored) = self.lookup_lambda_from_value(next_func) {
8649 current_match = self.invoke_stored_lambda(&stored, &[], data)?;
8650 continue;
8651 }
8652 }
8653
8654 break;
8656 } else {
8657 break;
8659 }
8660 }
8661
8662 if results.is_empty() {
8664 Ok(JValue::Undefined)
8665 } else {
8666 Ok(JValue::array(results))
8667 }
8668 }
8669
8670 fn replace_with_lambda(
8675 &mut self,
8676 str_value: &JValue,
8677 pattern_value: &JValue,
8678 lambda_value: &JValue,
8679 limit_value: Option<&JValue>,
8680 data: &JValue,
8681 ) -> Result<JValue, EvaluatorError> {
8682 let s = match str_value {
8684 JValue::String(s) => &**s,
8685 _ => {
8686 return Err(EvaluatorError::TypeError(
8687 "replace() requires string arguments".to_string(),
8688 ))
8689 }
8690 };
8691
8692 let (pattern, flags) =
8694 crate::functions::string::extract_regex(pattern_value).ok_or_else(|| {
8695 EvaluatorError::TypeError(
8696 "replace() pattern must be a regex when using lambda replacement".to_string(),
8697 )
8698 })?;
8699
8700 let re = crate::functions::string::build_regex(&pattern, &flags)?;
8702
8703 let limit = if let Some(lim_val) = limit_value {
8705 match lim_val {
8706 JValue::Number(n) => {
8707 let lim_f64 = *n;
8708 if lim_f64 < 0.0 {
8709 return Err(EvaluatorError::EvaluationError(format!(
8710 "D3011: Limit must be non-negative, got {}",
8711 lim_f64
8712 )));
8713 }
8714 Some(lim_f64 as usize)
8715 }
8716 _ => {
8717 return Err(EvaluatorError::TypeError(
8718 "replace() limit must be a number".to_string(),
8719 ))
8720 }
8721 }
8722 } else {
8723 None
8724 };
8725
8726 let mut result = String::new();
8728 let mut last_end = 0;
8729 let mut count = 0;
8730
8731 for cap in re.captures_iter(s) {
8732 if let Some(lim) = limit {
8734 if count >= lim {
8735 break;
8736 }
8737 }
8738
8739 let m = cap.get(0).unwrap();
8740 let match_start = m.start();
8741 let match_end = m.end();
8742 let match_str = m.as_str();
8743
8744 result.push_str(&s[last_end..match_start]);
8746
8747 let groups: Vec<JValue> = (1..cap.len())
8749 .map(|i| {
8750 cap.get(i)
8751 .map(|m| JValue::string(m.as_str().to_string()))
8752 .unwrap_or(JValue::Null)
8753 })
8754 .collect();
8755
8756 let mut match_map = IndexMap::new();
8757 match_map.insert("match".to_string(), JValue::string(match_str));
8758 match_map.insert("start".to_string(), JValue::Number(match_start as f64));
8759 match_map.insert("end".to_string(), JValue::Number(match_end as f64));
8760 match_map.insert("groups".to_string(), JValue::array(groups));
8761 let match_obj = JValue::object(match_map);
8762
8763 let stored_lambda = self.lookup_lambda_from_value(lambda_value).ok_or_else(|| {
8765 EvaluatorError::TypeError("Replacement must be a lambda function".to_string())
8766 })?;
8767 let lambda_result = self.invoke_stored_lambda(&stored_lambda, &[match_obj], data)?;
8768 let replacement_str = match lambda_result {
8769 JValue::String(s) => s,
8770 _ => {
8771 return Err(EvaluatorError::TypeError(format!(
8772 "D3012: Replacement function must return a string, got {:?}",
8773 lambda_result
8774 )))
8775 }
8776 };
8777
8778 result.push_str(&replacement_str);
8780
8781 last_end = match_end;
8782 count += 1;
8783 }
8784
8785 result.push_str(&s[last_end..]);
8787
8788 Ok(JValue::string(result))
8789 }
8790
8791 fn capture_current_environment(&self) -> HashMap<String, JValue> {
8793 self.context.all_bindings()
8794 }
8795
8796 fn capture_environment_for(
8799 &self,
8800 body: &AstNode,
8801 params: &[String],
8802 ) -> HashMap<String, JValue> {
8803 let free_vars = Self::collect_free_variables(body, params);
8804 if free_vars.is_empty() {
8805 return HashMap::new();
8806 }
8807 let mut result = HashMap::new();
8808 for var_name in &free_vars {
8809 if let Some(value) = self.context.lookup(var_name) {
8810 result.insert(var_name.clone(), value.clone());
8811 }
8812 }
8813 result
8814 }
8815
8816 fn collect_free_variables(body: &AstNode, params: &[String]) -> HashSet<String> {
8819 let mut free_vars = HashSet::new();
8820 let bound: HashSet<&str> = params.iter().map(|s| s.as_str()).collect();
8821 Self::collect_free_vars_walk(body, &bound, &mut free_vars);
8822 free_vars
8823 }
8824
8825 fn collect_free_vars_walk(node: &AstNode, bound: &HashSet<&str>, free: &mut HashSet<String>) {
8826 match node {
8827 AstNode::Variable(name) => {
8828 if !bound.contains(name.as_str()) {
8829 free.insert(name.clone());
8830 }
8831 }
8832 AstNode::Function { name, args, .. } => {
8833 if !bound.contains(name.as_str()) {
8835 free.insert(name.clone());
8836 }
8837 for arg in args {
8838 Self::collect_free_vars_walk(arg, bound, free);
8839 }
8840 }
8841 AstNode::Lambda { params, body, .. } => {
8842 let mut inner_bound = bound.clone();
8844 for p in params {
8845 inner_bound.insert(p.as_str());
8846 }
8847 Self::collect_free_vars_walk(body, &inner_bound, free);
8848 }
8849 AstNode::Binary { op, lhs, rhs } => {
8850 Self::collect_free_vars_walk(lhs, bound, free);
8851 Self::collect_free_vars_walk(rhs, bound, free);
8852 let _ = op;
8855 }
8856 AstNode::Unary { operand, .. } => {
8857 Self::collect_free_vars_walk(operand, bound, free);
8858 }
8859 AstNode::Path { steps } => {
8860 for step in steps {
8861 Self::collect_free_vars_walk(&step.node, bound, free);
8862 for stage in &step.stages {
8863 match stage {
8864 Stage::Filter(expr) => Self::collect_free_vars_walk(expr, bound, free),
8865 }
8866 }
8867 }
8868 }
8869 AstNode::Call { procedure, args } => {
8870 Self::collect_free_vars_walk(procedure, bound, free);
8871 for arg in args {
8872 Self::collect_free_vars_walk(arg, bound, free);
8873 }
8874 }
8875 AstNode::Conditional {
8876 condition,
8877 then_branch,
8878 else_branch,
8879 } => {
8880 Self::collect_free_vars_walk(condition, bound, free);
8881 Self::collect_free_vars_walk(then_branch, bound, free);
8882 if let Some(else_expr) = else_branch {
8883 Self::collect_free_vars_walk(else_expr, bound, free);
8884 }
8885 }
8886 AstNode::Block(exprs) => {
8887 let mut block_bound = bound.clone();
8888 for expr in exprs {
8889 Self::collect_free_vars_walk(expr, &block_bound, free);
8890 if let AstNode::Binary {
8892 op: BinaryOp::ColonEqual,
8893 lhs,
8894 ..
8895 } = expr
8896 {
8897 if let AstNode::Variable(var_name) = lhs.as_ref() {
8898 block_bound.insert(var_name.as_str());
8899 }
8900 }
8901 }
8902 }
8903 AstNode::Array(exprs) | AstNode::ArrayGroup(exprs) => {
8904 for expr in exprs {
8905 Self::collect_free_vars_walk(expr, bound, free);
8906 }
8907 }
8908 AstNode::Object(pairs) => {
8909 for (key, value) in pairs {
8910 Self::collect_free_vars_walk(key, bound, free);
8911 Self::collect_free_vars_walk(value, bound, free);
8912 }
8913 }
8914 AstNode::ObjectTransform { input, pattern } => {
8915 Self::collect_free_vars_walk(input, bound, free);
8916 for (key, value) in pattern {
8917 Self::collect_free_vars_walk(key, bound, free);
8918 Self::collect_free_vars_walk(value, bound, free);
8919 }
8920 }
8921 AstNode::Predicate(expr) | AstNode::FunctionApplication(expr) => {
8922 Self::collect_free_vars_walk(expr, bound, free);
8923 }
8924 AstNode::Sort { input, terms } => {
8925 Self::collect_free_vars_walk(input, bound, free);
8926 for (expr, _) in terms {
8927 Self::collect_free_vars_walk(expr, bound, free);
8928 }
8929 }
8930 AstNode::IndexBind { input, .. } => {
8931 Self::collect_free_vars_walk(input, bound, free);
8932 }
8933 AstNode::Transform {
8934 location,
8935 update,
8936 delete,
8937 } => {
8938 Self::collect_free_vars_walk(location, bound, free);
8939 Self::collect_free_vars_walk(update, bound, free);
8940 if let Some(del) = delete {
8941 Self::collect_free_vars_walk(del, bound, free);
8942 }
8943 }
8944 AstNode::String(_)
8946 | AstNode::Name(_)
8947 | AstNode::Number(_)
8948 | AstNode::Boolean(_)
8949 | AstNode::Null
8950 | AstNode::Undefined
8951 | AstNode::Placeholder
8952 | AstNode::Regex { .. }
8953 | AstNode::Wildcard
8954 | AstNode::Descendant
8955 | AstNode::ParentVariable(_) => {}
8956 }
8957 }
8958
8959 fn is_builtin_function(&self, name: &str) -> bool {
8961 matches!(
8962 name,
8963 "string" | "length" | "substring" | "substringBefore" | "substringAfter" |
8965 "uppercase" | "lowercase" | "trim" | "pad" | "contains" | "split" |
8966 "join" | "match" | "replace" | "eval" | "base64encode" | "base64decode" |
8967 "encodeUrlComponent" | "encodeUrl" | "decodeUrlComponent" | "decodeUrl" |
8968
8969 "number" | "abs" | "floor" | "ceil" | "round" | "power" | "sqrt" |
8971 "random" | "formatNumber" | "formatBase" | "formatInteger" | "parseInteger" |
8972
8973 "sum" | "max" | "min" | "average" |
8975
8976 "boolean" | "not" | "exists" |
8978
8979 "count" | "append" | "sort" | "reverse" | "shuffle" | "distinct" | "zip" |
8981
8982 "keys" | "lookup" | "spread" | "merge" | "sift" | "each" | "error" | "assert" | "type" |
8984
8985 "map" | "filter" | "reduce" | "singletonArray" |
8987
8988 "now" | "millis" | "fromMillis" | "toMillis"
8990 )
8991 }
8992
8993 fn call_builtin_with_values(
8996 &mut self,
8997 name: &str,
8998 values: &[JValue],
8999 ) -> Result<JValue, EvaluatorError> {
9000 use crate::functions;
9001
9002 if values.is_empty() {
9003 return Err(EvaluatorError::EvaluationError(format!(
9004 "{}() requires at least 1 argument",
9005 name
9006 )));
9007 }
9008
9009 let arg = &values[0];
9010
9011 match name {
9012 "string" => Ok(functions::string::string(arg, None)?),
9013 "number" => Ok(functions::numeric::number(arg)?),
9014 "boolean" => Ok(functions::boolean::boolean(arg)?),
9015 "not" => {
9016 let b = functions::boolean::boolean(arg)?;
9017 match b {
9018 JValue::Bool(val) => Ok(JValue::Bool(!val)),
9019 _ => Err(EvaluatorError::TypeError(
9020 "not() requires a boolean".to_string(),
9021 )),
9022 }
9023 }
9024 "exists" => Ok(JValue::Bool(!arg.is_null())),
9025 "abs" => match arg {
9026 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
9027 _ => Err(EvaluatorError::TypeError(
9028 "abs() requires a number argument".to_string(),
9029 )),
9030 },
9031 "floor" => match arg {
9032 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
9033 _ => Err(EvaluatorError::TypeError(
9034 "floor() requires a number argument".to_string(),
9035 )),
9036 },
9037 "ceil" => match arg {
9038 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
9039 _ => Err(EvaluatorError::TypeError(
9040 "ceil() requires a number argument".to_string(),
9041 )),
9042 },
9043 "round" => match arg {
9044 JValue::Number(n) => Ok(functions::numeric::round(*n, None)?),
9045 _ => Err(EvaluatorError::TypeError(
9046 "round() requires a number argument".to_string(),
9047 )),
9048 },
9049 "sqrt" => match arg {
9050 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
9051 _ => Err(EvaluatorError::TypeError(
9052 "sqrt() requires a number argument".to_string(),
9053 )),
9054 },
9055 "uppercase" => match arg {
9056 JValue::String(s) => Ok(JValue::string(s.to_uppercase())),
9057 JValue::Null => Ok(JValue::Null),
9058 _ => Err(EvaluatorError::TypeError(
9059 "uppercase() requires a string argument".to_string(),
9060 )),
9061 },
9062 "lowercase" => match arg {
9063 JValue::String(s) => Ok(JValue::string(s.to_lowercase())),
9064 JValue::Null => Ok(JValue::Null),
9065 _ => Err(EvaluatorError::TypeError(
9066 "lowercase() requires a string argument".to_string(),
9067 )),
9068 },
9069 "trim" => match arg {
9070 JValue::String(s) => Ok(JValue::string(s.trim().to_string())),
9071 JValue::Null => Ok(JValue::Null),
9072 _ => Err(EvaluatorError::TypeError(
9073 "trim() requires a string argument".to_string(),
9074 )),
9075 },
9076 "length" => match arg {
9077 JValue::String(s) => Ok(JValue::Number(s.chars().count() as f64)),
9078 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
9079 JValue::Null => Ok(JValue::Null),
9080 _ => Err(EvaluatorError::TypeError(
9081 "length() requires a string or array argument".to_string(),
9082 )),
9083 },
9084 "sum" => match arg {
9085 JValue::Array(arr) => {
9086 let mut total = 0.0;
9087 for item in arr.iter() {
9088 match item {
9089 JValue::Number(n) => {
9090 total += *n;
9091 }
9092 _ => {
9093 return Err(EvaluatorError::TypeError(
9094 "sum() requires all array elements to be numbers".to_string(),
9095 ));
9096 }
9097 }
9098 }
9099 Ok(JValue::Number(total))
9100 }
9101 JValue::Number(n) => Ok(JValue::Number(*n)),
9102 JValue::Null => Ok(JValue::Null),
9103 _ => Err(EvaluatorError::TypeError(
9104 "sum() requires an array of numbers".to_string(),
9105 )),
9106 },
9107 "count" => {
9108 match arg {
9109 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
9110 JValue::Null => Ok(JValue::Number(0.0)),
9111 _ => Ok(JValue::Number(1.0)), }
9113 }
9114 "max" => match arg {
9115 JValue::Array(arr) => {
9116 let mut max_val: Option<f64> = None;
9117 for item in arr.iter() {
9118 if let JValue::Number(n) = item {
9119 let f = *n;
9120 max_val = Some(max_val.map_or(f, |m| m.max(f)));
9121 }
9122 }
9123 max_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
9124 }
9125 JValue::Number(n) => Ok(JValue::Number(*n)),
9126 JValue::Null => Ok(JValue::Null),
9127 _ => Err(EvaluatorError::TypeError(
9128 "max() requires an array of numbers".to_string(),
9129 )),
9130 },
9131 "min" => match arg {
9132 JValue::Array(arr) => {
9133 let mut min_val: Option<f64> = None;
9134 for item in arr.iter() {
9135 if let JValue::Number(n) = item {
9136 let f = *n;
9137 min_val = Some(min_val.map_or(f, |m| m.min(f)));
9138 }
9139 }
9140 min_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
9141 }
9142 JValue::Number(n) => Ok(JValue::Number(*n)),
9143 JValue::Null => Ok(JValue::Null),
9144 _ => Err(EvaluatorError::TypeError(
9145 "min() requires an array of numbers".to_string(),
9146 )),
9147 },
9148 "average" => match arg {
9149 JValue::Array(arr) => {
9150 let nums: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
9151 if nums.is_empty() {
9152 Ok(JValue::Null)
9153 } else {
9154 let avg = nums.iter().sum::<f64>() / nums.len() as f64;
9155 Ok(JValue::Number(avg))
9156 }
9157 }
9158 JValue::Number(n) => Ok(JValue::Number(*n)),
9159 JValue::Null => Ok(JValue::Null),
9160 _ => Err(EvaluatorError::TypeError(
9161 "average() requires an array of numbers".to_string(),
9162 )),
9163 },
9164 "append" => {
9165 if values.len() < 2 {
9167 return Err(EvaluatorError::EvaluationError(
9168 "append() requires 2 arguments".to_string(),
9169 ));
9170 }
9171 let first = &values[0];
9172 let second = &values[1];
9173
9174 let mut result = match first {
9176 JValue::Array(arr) => arr.to_vec(),
9177 JValue::Null => vec![],
9178 other => vec![other.clone()],
9179 };
9180
9181 match second {
9183 JValue::Array(arr) => result.extend(arr.iter().cloned()),
9184 JValue::Null => {}
9185 other => result.push(other.clone()),
9186 }
9187
9188 Ok(JValue::array(result))
9189 }
9190 "reverse" => match arg {
9191 JValue::Array(arr) => {
9192 let mut reversed = arr.to_vec();
9193 reversed.reverse();
9194 Ok(JValue::array(reversed))
9195 }
9196 JValue::Null => Ok(JValue::Null),
9197 _ => Err(EvaluatorError::TypeError(
9198 "reverse() requires an array".to_string(),
9199 )),
9200 },
9201 "keys" => match arg {
9202 JValue::Object(obj) => {
9203 let keys: Vec<JValue> = obj.keys().map(|k| JValue::string(k.clone())).collect();
9204 Ok(JValue::array(keys))
9205 }
9206 JValue::Null => Ok(JValue::Null),
9207 _ => Err(EvaluatorError::TypeError(
9208 "keys() requires an object".to_string(),
9209 )),
9210 },
9211
9212 _ => Err(EvaluatorError::ReferenceError(format!(
9214 "Built-in function {} cannot be called with values directly",
9215 name
9216 ))),
9217 }
9218 }
9219
9220 fn collect_descendants(&self, value: &JValue) -> Vec<JValue> {
9222 let mut descendants = Vec::new();
9223
9224 match value {
9225 JValue::Null => {
9226 return descendants;
9228 }
9229 JValue::Object(obj) => {
9230 descendants.push(value.clone());
9232
9233 for val in obj.values() {
9234 descendants.extend(self.collect_descendants(val));
9236 }
9237 }
9238 JValue::Array(arr) => {
9239 for val in arr.iter() {
9242 descendants.extend(self.collect_descendants(val));
9244 }
9245 }
9246 _ => {
9247 descendants.push(value.clone());
9249 }
9250 }
9251
9252 descendants
9253 }
9254
9255 fn evaluate_predicate(
9257 &mut self,
9258 current: &JValue,
9259 predicate: &AstNode,
9260 ) -> Result<JValue, EvaluatorError> {
9261 if matches!(predicate, AstNode::Boolean(true)) {
9264 return match current {
9265 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
9266 JValue::Null => Ok(JValue::Null),
9267 other => Ok(JValue::array(vec![other.clone()])),
9268 };
9269 }
9270
9271 match current {
9272 JValue::Array(_arr) => {
9273 if let AstNode::Number(n) = predicate {
9277 return self.array_index(current, &JValue::Number(*n));
9279 }
9280
9281 if Self::is_filter_predicate(predicate) {
9284 if let Some(compiled) = try_compile_expr(predicate) {
9286 let shape = _arr.first().and_then(build_shape_cache);
9287 let mut filtered = Vec::with_capacity(_arr.len());
9288 for item in _arr.iter() {
9289 let result = if let Some(ref s) = shape {
9290 eval_compiled_shaped(&compiled, item, None, s)?
9291 } else {
9292 eval_compiled(&compiled, item, None)?
9293 };
9294 if compiled_is_truthy(&result) {
9295 filtered.push(item.clone());
9296 }
9297 }
9298 return Ok(JValue::array(filtered));
9299 }
9300 let mut filtered = Vec::new();
9302 for item in _arr.iter() {
9303 let item_result = self.evaluate_internal(predicate, item)?;
9304 if self.is_truthy(&item_result) {
9305 filtered.push(item.clone());
9306 }
9307 }
9308 return Ok(JValue::array(filtered));
9309 }
9310
9311 match self.evaluate_internal(predicate, current) {
9315 Ok(JValue::Number(_)) => {
9316 let pred_result = self.evaluate_internal(predicate, current)?;
9318 return self.array_index(current, &pred_result);
9319 }
9320 Ok(JValue::Array(indices)) => {
9321 let has_non_numeric =
9324 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
9325
9326 if has_non_numeric {
9327 return Ok(current.clone());
9329 }
9330
9331 let arr_len = _arr.len() as i64;
9333 let mut resolved_indices: Vec<i64> = indices
9334 .iter()
9335 .filter_map(|v| {
9336 if let JValue::Number(n) = v {
9337 let idx = *n as i64;
9338 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
9340 if actual_idx >= 0 && actual_idx < arr_len {
9342 Some(actual_idx)
9343 } else {
9344 None
9345 }
9346 } else {
9347 None
9348 }
9349 })
9350 .collect();
9351
9352 resolved_indices.sort();
9354 resolved_indices.dedup();
9355
9356 let result: Vec<JValue> = resolved_indices
9358 .iter()
9359 .map(|&idx| _arr[idx as usize].clone())
9360 .collect();
9361
9362 return Ok(JValue::array(result));
9363 }
9364 Ok(_) => {
9365 }
9368 Err(_) => {
9369 }
9372 }
9373
9374 if let Some(compiled) = try_compile_expr(predicate) {
9376 let shape = _arr.first().and_then(build_shape_cache);
9377 let mut filtered = Vec::with_capacity(_arr.len());
9378 for item in _arr.iter() {
9379 let result = if let Some(ref s) = shape {
9380 eval_compiled_shaped(&compiled, item, None, s)?
9381 } else {
9382 eval_compiled(&compiled, item, None)?
9383 };
9384 if compiled_is_truthy(&result) {
9385 filtered.push(item.clone());
9386 }
9387 }
9388 return Ok(JValue::array(filtered));
9389 }
9390
9391 let mut filtered = Vec::new();
9393 for item in _arr.iter() {
9394 let item_result = self.evaluate_internal(predicate, item)?;
9395
9396 if self.is_truthy(&item_result) {
9398 filtered.push(item.clone());
9399 }
9400 }
9401
9402 Ok(JValue::array(filtered))
9403 }
9404 JValue::Object(obj) => {
9405 let pred_result = self.evaluate_internal(predicate, current)?;
9409
9410 if let JValue::String(key) = &pred_result {
9412 return Ok(obj.get(&**key).cloned().unwrap_or(JValue::Null));
9413 }
9414
9415 if self.is_truthy(&pred_result) {
9418 Ok(current.clone())
9419 } else {
9420 Ok(JValue::Undefined)
9421 }
9422 }
9423 _ => {
9424 if let AstNode::Number(n) = predicate {
9430 let idx = n.floor() as i64;
9432 if idx == 0 || idx == -1 {
9433 return Ok(current.clone());
9434 } else {
9435 return Ok(JValue::Undefined);
9436 }
9437 }
9438
9439 let pred_result = self.evaluate_internal(predicate, current)?;
9441
9442 if let JValue::Number(n) = &pred_result {
9443 let idx = n.floor() as i64;
9445 if idx == 0 || idx == -1 {
9446 return Ok(current.clone());
9447 } else {
9448 return Ok(JValue::Undefined);
9449 }
9450 }
9451
9452 if self.is_truthy(&pred_result) {
9456 Ok(current.clone())
9457 } else {
9458 Ok(JValue::Undefined)
9460 }
9461 }
9462 }
9463 }
9464
9465 fn evaluate_sort_term(
9468 &mut self,
9469 term_expr: &AstNode,
9470 element: &JValue,
9471 ) -> Result<JValue, EvaluatorError> {
9472 let actual_element = if let JValue::Object(obj) = element {
9474 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
9475 obj.get("@").cloned().unwrap_or(JValue::Null)
9476 } else {
9477 element.clone()
9478 }
9479 } else {
9480 element.clone()
9481 };
9482
9483 if let AstNode::Path { steps } = term_expr {
9485 if steps.len() == 1 && steps[0].stages.is_empty() {
9486 if let AstNode::Name(field_name) = &steps[0].node {
9487 if let JValue::Object(obj) = &actual_element {
9489 return match obj.get(field_name) {
9490 Some(val) => Ok(val.clone()), None => Ok(JValue::Undefined), };
9493 } else {
9494 return Ok(JValue::Undefined);
9496 }
9497 }
9498 }
9499 }
9500
9501 let result = self.evaluate_internal(term_expr, element)?;
9504
9505 if result.is_null() {
9510 return Ok(JValue::Undefined);
9511 }
9512
9513 Ok(result)
9514 }
9515
9516 fn evaluate_sort(
9518 &mut self,
9519 data: &JValue,
9520 terms: &[(AstNode, bool)],
9521 ) -> Result<JValue, EvaluatorError> {
9522 if data.is_null() {
9524 return Ok(JValue::Null);
9525 }
9526
9527 let array = match data {
9529 JValue::Array(arr) => arr.clone(),
9530 other => return Ok(other.clone()),
9531 };
9532
9533 if array.is_empty() {
9535 return Ok(JValue::Array(array));
9536 }
9537
9538 let mut indexed_array: Vec<(usize, Vec<JValue>)> = Vec::new();
9540
9541 for (idx, element) in array.iter().enumerate() {
9542 let mut sort_keys = Vec::new();
9543
9544 for (term_expr, _ascending) in terms {
9546 let saved_dollar = self.context.lookup("$").cloned();
9548
9549 self.context.bind("$".to_string(), element.clone());
9551
9552 let sort_value = self.evaluate_sort_term(term_expr, element)?;
9554
9555 if let Some(val) = saved_dollar {
9557 self.context.bind("$".to_string(), val);
9558 } else {
9559 self.context.unbind("$");
9560 }
9561
9562 sort_keys.push(sort_value);
9563 }
9564
9565 indexed_array.push((idx, sort_keys));
9566 }
9567
9568 for term_idx in 0..terms.len() {
9572 let mut first_valid_type: Option<&str> = None;
9573
9574 for (_idx, sort_keys) in &indexed_array {
9575 let sort_value = &sort_keys[term_idx];
9576
9577 if sort_value.is_undefined() {
9579 continue;
9580 }
9581
9582 let value_type = match sort_value {
9585 JValue::Number(_) => "number",
9586 JValue::String(_) => "string",
9587 JValue::Bool(_) => "boolean",
9588 JValue::Array(_) => "array",
9589 JValue::Object(_) => "object", JValue::Null => "null", _ => "unknown",
9592 };
9593
9594 if value_type != "number" && value_type != "string" {
9597 return Err(EvaluatorError::TypeError("T2008: The expressions within an order-by clause must evaluate to numeric or string values".to_string()));
9598 }
9599
9600 if let Some(first_type) = first_valid_type {
9602 if first_type != value_type {
9603 return Err(EvaluatorError::TypeError(format!(
9604 "T2007: Type mismatch when comparing values in order-by clause: {} and {}",
9605 first_type, value_type
9606 )));
9607 }
9608 } else {
9609 first_valid_type = Some(value_type);
9610 }
9611 }
9612 }
9613
9614 indexed_array.sort_by(|a, b| {
9616 for (i, (_term_expr, ascending)) in terms.iter().enumerate() {
9618 let left = &a.1[i];
9619 let right = &b.1[i];
9620
9621 let cmp = self.compare_values(left, right);
9622
9623 if cmp != std::cmp::Ordering::Equal {
9624 return if *ascending { cmp } else { cmp.reverse() };
9625 }
9626 }
9627
9628 a.0.cmp(&b.0)
9630 });
9631
9632 let sorted: Vec<JValue> = indexed_array
9634 .iter()
9635 .map(|(idx, _)| array[*idx].clone())
9636 .collect();
9637
9638 Ok(JValue::array(sorted))
9639 }
9640
9641 fn compare_values(&self, left: &JValue, right: &JValue) -> Ordering {
9643 let left_undef = left.is_undefined();
9645 let right_undef = right.is_undefined();
9646
9647 if left_undef && right_undef {
9648 return Ordering::Equal;
9649 }
9650 if left_undef {
9651 return Ordering::Greater; }
9653 if right_undef {
9654 return Ordering::Less;
9655 }
9656
9657 match (left, right) {
9658 (JValue::Null, JValue::Null) => Ordering::Equal,
9660 (JValue::Null, _) => Ordering::Greater,
9661 (_, JValue::Null) => Ordering::Less,
9662
9663 (JValue::Number(a), JValue::Number(b)) => {
9665 let a_f64 = *a;
9666 let b_f64 = *b;
9667 a_f64.partial_cmp(&b_f64).unwrap_or(Ordering::Equal)
9668 }
9669
9670 (JValue::String(a), JValue::String(b)) => a.cmp(b),
9672
9673 (JValue::Bool(a), JValue::Bool(b)) => a.cmp(b),
9675
9676 (JValue::Array(a), JValue::Array(b)) => {
9678 for (a_elem, b_elem) in a.iter().zip(b.iter()) {
9679 let cmp = self.compare_values(a_elem, b_elem);
9680 if cmp != Ordering::Equal {
9681 return cmp;
9682 }
9683 }
9684 a.len().cmp(&b.len())
9685 }
9686
9687 (JValue::Bool(_), JValue::Number(_)) => Ordering::Less,
9690 (JValue::Bool(_), JValue::String(_)) => Ordering::Less,
9691 (JValue::Bool(_), JValue::Array(_)) => Ordering::Less,
9692 (JValue::Bool(_), JValue::Object(_)) => Ordering::Less,
9693
9694 (JValue::Number(_), JValue::Bool(_)) => Ordering::Greater,
9695 (JValue::Number(_), JValue::String(_)) => Ordering::Less,
9696 (JValue::Number(_), JValue::Array(_)) => Ordering::Less,
9697 (JValue::Number(_), JValue::Object(_)) => Ordering::Less,
9698
9699 (JValue::String(_), JValue::Bool(_)) => Ordering::Greater,
9700 (JValue::String(_), JValue::Number(_)) => Ordering::Greater,
9701 (JValue::String(_), JValue::Array(_)) => Ordering::Less,
9702 (JValue::String(_), JValue::Object(_)) => Ordering::Less,
9703
9704 (JValue::Array(_), JValue::Bool(_)) => Ordering::Greater,
9705 (JValue::Array(_), JValue::Number(_)) => Ordering::Greater,
9706 (JValue::Array(_), JValue::String(_)) => Ordering::Greater,
9707 (JValue::Array(_), JValue::Object(_)) => Ordering::Less,
9708
9709 (JValue::Object(_), _) => Ordering::Greater,
9710 _ => Ordering::Equal,
9711 }
9712 }
9713
9714 fn is_truthy(&self, value: &JValue) -> bool {
9716 match value {
9717 JValue::Null | JValue::Undefined => false,
9718 JValue::Bool(b) => *b,
9719 JValue::Number(n) => *n != 0.0,
9720 JValue::String(s) => !s.is_empty(),
9721 JValue::Array(arr) => !arr.is_empty(),
9722 JValue::Object(obj) => !obj.is_empty(),
9723 _ => false,
9724 }
9725 }
9726
9727 fn is_truthy_for_default(&self, value: &JValue) -> bool {
9733 match value {
9734 JValue::Lambda { .. } | JValue::Builtin { .. } => false,
9736 JValue::Array(arr) => {
9738 if arr.is_empty() {
9739 return false;
9740 }
9741 arr.iter().any(|elem| self.is_truthy(elem))
9743 }
9744 _ => self.is_truthy(value),
9746 }
9747 }
9748
9749 fn unwrap_singleton(&self, value: JValue) -> JValue {
9752 match value {
9753 JValue::Array(ref arr) if arr.len() == 1 => arr[0].clone(),
9754 _ => value,
9755 }
9756 }
9757
9758 fn extract_lambda_ids(&self, value: &JValue) -> Vec<String> {
9762 match value {
9764 JValue::Number(_)
9765 | JValue::Bool(_)
9766 | JValue::String(_)
9767 | JValue::Null
9768 | JValue::Undefined
9769 | JValue::Regex { .. }
9770 | JValue::Builtin { .. } => return Vec::new(),
9771 _ => {}
9772 }
9773 let mut ids = Vec::new();
9774 self.collect_lambda_ids(value, &mut ids);
9775 ids
9776 }
9777
9778 fn collect_lambda_ids(&self, value: &JValue, ids: &mut Vec<String>) {
9779 match value {
9780 JValue::Lambda { lambda_id, .. } => {
9781 let id_str = lambda_id.to_string();
9782 if !ids.contains(&id_str) {
9783 ids.push(id_str);
9784 if let Some(stored) = self.context.lookup_lambda(lambda_id) {
9789 let env_values: Vec<JValue> =
9790 stored.captured_env.values().cloned().collect();
9791 for env_value in &env_values {
9792 self.collect_lambda_ids(env_value, ids);
9793 }
9794 }
9795 }
9796 }
9797 JValue::Object(map) => {
9798 for v in map.values() {
9800 self.collect_lambda_ids(v, ids);
9801 }
9802 }
9803 JValue::Array(arr) => {
9804 for v in arr.iter() {
9806 self.collect_lambda_ids(v, ids);
9807 }
9808 }
9809 _ => {}
9810 }
9811 }
9812
9813 fn equals(&self, left: &JValue, right: &JValue) -> bool {
9815 crate::functions::array::values_equal(left, right)
9816 }
9817
9818 fn add(
9820 &self,
9821 left: &JValue,
9822 right: &JValue,
9823 left_is_explicit_null: bool,
9824 right_is_explicit_null: bool,
9825 ) -> Result<JValue, EvaluatorError> {
9826 match (left, right) {
9827 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a + *b)),
9828 (JValue::Null, JValue::Number(_)) if left_is_explicit_null => {
9830 Err(EvaluatorError::TypeError(
9831 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9832 ))
9833 }
9834 (JValue::Number(_), JValue::Null) if right_is_explicit_null => {
9835 Err(EvaluatorError::TypeError(
9836 "T2002: The right side of the + operator must evaluate to a number".to_string(),
9837 ))
9838 }
9839 (JValue::Null, JValue::Null) if left_is_explicit_null || right_is_explicit_null => {
9840 Err(EvaluatorError::TypeError(
9841 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9842 ))
9843 }
9844 (JValue::Null | JValue::Undefined, JValue::Number(_))
9846 | (JValue::Number(_), JValue::Null | JValue::Undefined) => Ok(JValue::Null),
9847 (JValue::Bool(_), _) => Err(EvaluatorError::TypeError(
9849 "T2001: The left side of the '+' operator must evaluate to a number or a string"
9850 .to_string(),
9851 )),
9852 (_, JValue::Bool(_)) => Err(EvaluatorError::TypeError(
9853 "T2001: The right side of the '+' operator must evaluate to a number or a string"
9854 .to_string(),
9855 )),
9856 (JValue::Null | JValue::Undefined, JValue::Null | JValue::Undefined) => {
9858 Ok(JValue::Null)
9859 }
9860 _ => Err(EvaluatorError::TypeError(format!(
9861 "Cannot add {:?} and {:?}",
9862 left, right
9863 ))),
9864 }
9865 }
9866
9867 fn subtract(
9869 &self,
9870 left: &JValue,
9871 right: &JValue,
9872 left_is_explicit_null: bool,
9873 right_is_explicit_null: bool,
9874 ) -> Result<JValue, EvaluatorError> {
9875 match (left, right) {
9876 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a - *b)),
9877 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9879 "T2002: The left side of the - operator must evaluate to a number".to_string(),
9880 )),
9881 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9882 "T2002: The right side of the - operator must evaluate to a number".to_string(),
9883 )),
9884 (JValue::Null | JValue::Undefined, _) | (_, JValue::Null | JValue::Undefined) => {
9886 Ok(JValue::Null)
9887 }
9888 _ => Err(EvaluatorError::TypeError(format!(
9889 "Cannot subtract {:?} and {:?}",
9890 left, right
9891 ))),
9892 }
9893 }
9894
9895 fn multiply(
9897 &self,
9898 left: &JValue,
9899 right: &JValue,
9900 left_is_explicit_null: bool,
9901 right_is_explicit_null: bool,
9902 ) -> Result<JValue, EvaluatorError> {
9903 match (left, right) {
9904 (JValue::Number(a), JValue::Number(b)) => {
9905 let result = *a * *b;
9906 if result.is_infinite() {
9908 return Err(EvaluatorError::EvaluationError(
9909 "D1001: Number out of range".to_string(),
9910 ));
9911 }
9912 Ok(JValue::Number(result))
9913 }
9914 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9916 "T2002: The left side of the * operator must evaluate to a number".to_string(),
9917 )),
9918 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9919 "T2002: The right side of the * operator must evaluate to a number".to_string(),
9920 )),
9921 (JValue::Null | JValue::Undefined, _) | (_, JValue::Null | JValue::Undefined) => {
9923 Ok(JValue::Null)
9924 }
9925 _ => Err(EvaluatorError::TypeError(format!(
9926 "Cannot multiply {:?} and {:?}",
9927 left, right
9928 ))),
9929 }
9930 }
9931
9932 fn divide(
9934 &self,
9935 left: &JValue,
9936 right: &JValue,
9937 left_is_explicit_null: bool,
9938 right_is_explicit_null: bool,
9939 ) -> Result<JValue, EvaluatorError> {
9940 match (left, right) {
9941 (JValue::Number(a), JValue::Number(b)) => {
9942 let denominator = *b;
9943 if denominator == 0.0 {
9944 return Err(EvaluatorError::EvaluationError(
9945 "Division by zero".to_string(),
9946 ));
9947 }
9948 Ok(JValue::Number(*a / denominator))
9949 }
9950 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9952 "T2002: The left side of the / operator must evaluate to a number".to_string(),
9953 )),
9954 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9955 "T2002: The right side of the / operator must evaluate to a number".to_string(),
9956 )),
9957 (JValue::Null | JValue::Undefined, _) | (_, JValue::Null | JValue::Undefined) => {
9959 Ok(JValue::Null)
9960 }
9961 _ => Err(EvaluatorError::TypeError(format!(
9962 "Cannot divide {:?} and {:?}",
9963 left, right
9964 ))),
9965 }
9966 }
9967
9968 fn modulo(
9970 &self,
9971 left: &JValue,
9972 right: &JValue,
9973 left_is_explicit_null: bool,
9974 right_is_explicit_null: bool,
9975 ) -> Result<JValue, EvaluatorError> {
9976 match (left, right) {
9977 (JValue::Number(a), JValue::Number(b)) => {
9978 let denominator = *b;
9979 if denominator == 0.0 {
9980 return Err(EvaluatorError::EvaluationError(
9981 "Division by zero".to_string(),
9982 ));
9983 }
9984 Ok(JValue::Number(*a % denominator))
9985 }
9986 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9988 "T2002: The left side of the % operator must evaluate to a number".to_string(),
9989 )),
9990 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9991 "T2002: The right side of the % operator must evaluate to a number".to_string(),
9992 )),
9993 (JValue::Null | JValue::Undefined, _) | (_, JValue::Null | JValue::Undefined) => {
9995 Ok(JValue::Null)
9996 }
9997 _ => Err(EvaluatorError::TypeError(format!(
9998 "Cannot compute modulo of {:?} and {:?}",
9999 left, right
10000 ))),
10001 }
10002 }
10003
10004 fn type_name(value: &JValue) -> &'static str {
10006 match value {
10007 JValue::Null => "null",
10008 JValue::Bool(_) => "boolean",
10009 JValue::Number(_) => "number",
10010 JValue::String(_) => "string",
10011 JValue::Array(_) => "array",
10012 JValue::Object(_) => "object",
10013 _ => "unknown",
10014 }
10015 }
10016
10017 fn ordered_compare(
10023 &self,
10024 left: &JValue,
10025 right: &JValue,
10026 left_is_explicit_null: bool,
10027 right_is_explicit_null: bool,
10028 op_symbol: &str,
10029 compare_nums: fn(f64, f64) -> bool,
10030 compare_strs: fn(&str, &str) -> bool,
10031 ) -> Result<JValue, EvaluatorError> {
10032 match (left, right) {
10033 (JValue::Number(a), JValue::Number(b)) => {
10034 Ok(JValue::Bool(compare_nums(*a, *b)))
10035 }
10036 (JValue::String(a), JValue::String(b)) => Ok(JValue::Bool(compare_strs(a, b))),
10037 (JValue::Null, JValue::Null) => Ok(JValue::Null),
10039 (JValue::Null, _) if left_is_explicit_null => {
10041 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
10042 }
10043 (_, JValue::Null) if right_is_explicit_null => {
10044 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
10045 }
10046 (JValue::Bool(_), JValue::Null) | (JValue::Null, JValue::Bool(_)) => {
10048 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
10049 }
10050 (JValue::Number(_), JValue::Null) | (JValue::Null, JValue::Number(_)) |
10052 (JValue::String(_), JValue::Null) | (JValue::Null, JValue::String(_)) => {
10053 Ok(JValue::Null)
10054 }
10055 (JValue::String(_), JValue::Number(_)) | (JValue::Number(_), JValue::String(_)) => {
10057 Err(EvaluatorError::EvaluationError(format!(
10058 "T2009: The expressions on either side of operator \"{}\" must be of the same data type",
10059 op_symbol
10060 )))
10061 }
10062 (JValue::Bool(_), _) | (_, JValue::Bool(_)) => {
10064 Err(EvaluatorError::EvaluationError(format!(
10065 "T2010: Cannot compare {} and {}",
10066 Self::type_name(left), Self::type_name(right)
10067 )))
10068 }
10069 _ => Err(EvaluatorError::EvaluationError(format!(
10071 "T2010: Cannot compare {} and {}",
10072 Self::type_name(left), Self::type_name(right)
10073 ))),
10074 }
10075 }
10076
10077 fn less_than(
10079 &self,
10080 left: &JValue,
10081 right: &JValue,
10082 left_is_explicit_null: bool,
10083 right_is_explicit_null: bool,
10084 ) -> Result<JValue, EvaluatorError> {
10085 self.ordered_compare(
10086 left,
10087 right,
10088 left_is_explicit_null,
10089 right_is_explicit_null,
10090 "<",
10091 |a, b| a < b,
10092 |a, b| a < b,
10093 )
10094 }
10095
10096 fn less_than_or_equal(
10098 &self,
10099 left: &JValue,
10100 right: &JValue,
10101 left_is_explicit_null: bool,
10102 right_is_explicit_null: bool,
10103 ) -> Result<JValue, EvaluatorError> {
10104 self.ordered_compare(
10105 left,
10106 right,
10107 left_is_explicit_null,
10108 right_is_explicit_null,
10109 "<=",
10110 |a, b| a <= b,
10111 |a, b| a <= b,
10112 )
10113 }
10114
10115 fn greater_than(
10117 &self,
10118 left: &JValue,
10119 right: &JValue,
10120 left_is_explicit_null: bool,
10121 right_is_explicit_null: bool,
10122 ) -> Result<JValue, EvaluatorError> {
10123 self.ordered_compare(
10124 left,
10125 right,
10126 left_is_explicit_null,
10127 right_is_explicit_null,
10128 ">",
10129 |a, b| a > b,
10130 |a, b| a > b,
10131 )
10132 }
10133
10134 fn greater_than_or_equal(
10136 &self,
10137 left: &JValue,
10138 right: &JValue,
10139 left_is_explicit_null: bool,
10140 right_is_explicit_null: bool,
10141 ) -> Result<JValue, EvaluatorError> {
10142 self.ordered_compare(
10143 left,
10144 right,
10145 left_is_explicit_null,
10146 right_is_explicit_null,
10147 ">=",
10148 |a, b| a >= b,
10149 |a, b| a >= b,
10150 )
10151 }
10152
10153 fn value_to_concat_string(value: &JValue) -> Result<String, EvaluatorError> {
10155 match value {
10156 JValue::String(s) => Ok(s.to_string()),
10157 JValue::Null => Ok(String::new()),
10158 JValue::Number(_) | JValue::Bool(_) | JValue::Array(_) | JValue::Object(_) => {
10159 match crate::functions::string::string(value, None) {
10160 Ok(JValue::String(s)) => Ok(s.to_string()),
10161 Ok(JValue::Null) => Ok(String::new()),
10162 _ => Err(EvaluatorError::TypeError(
10163 "Cannot concatenate complex types".to_string(),
10164 )),
10165 }
10166 }
10167 _ => Ok(String::new()),
10168 }
10169 }
10170
10171 fn concatenate(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
10173 let left_str = Self::value_to_concat_string(left)?;
10174 let right_str = Self::value_to_concat_string(right)?;
10175 Ok(JValue::string(format!("{}{}", left_str, right_str)))
10176 }
10177
10178 fn range(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
10180 let start_f64 = match left {
10182 JValue::Number(n) => Some(*n),
10183 JValue::Null => None,
10184 _ => {
10185 return Err(EvaluatorError::EvaluationError(
10186 "T2003: Left operand of range operator must be a number".to_string(),
10187 ));
10188 }
10189 };
10190
10191 if let Some(val) = start_f64 {
10193 if val.fract() != 0.0 {
10194 return Err(EvaluatorError::EvaluationError(
10195 "T2003: Left operand of range operator must be an integer".to_string(),
10196 ));
10197 }
10198 }
10199
10200 let end_f64 = match right {
10202 JValue::Number(n) => Some(*n),
10203 JValue::Null => None,
10204 _ => {
10205 return Err(EvaluatorError::EvaluationError(
10206 "T2004: Right operand of range operator must be a number".to_string(),
10207 ));
10208 }
10209 };
10210
10211 if let Some(val) = end_f64 {
10213 if val.fract() != 0.0 {
10214 return Err(EvaluatorError::EvaluationError(
10215 "T2004: Right operand of range operator must be an integer".to_string(),
10216 ));
10217 }
10218 }
10219
10220 if start_f64.is_none() || end_f64.is_none() {
10222 return Ok(JValue::array(vec![]));
10223 }
10224
10225 let start = start_f64.unwrap() as i64;
10226 let end = end_f64.unwrap() as i64;
10227
10228 let size = if start <= end {
10230 (end - start + 1) as usize
10231 } else {
10232 0
10233 };
10234 if size > 10_000_000 {
10235 return Err(EvaluatorError::EvaluationError(
10236 "D2014: Range operator results in too many elements (> 10,000,000)".to_string(),
10237 ));
10238 }
10239
10240 let mut result = Vec::with_capacity(size);
10241 if start <= end {
10242 for i in start..=end {
10243 result.push(JValue::Number(i as f64));
10244 }
10245 }
10246 Ok(JValue::array(result))
10248 }
10249
10250 fn array_index(&self, array: &JValue, index: &JValue) -> Result<JValue, EvaluatorError> {
10253 match (array, index) {
10254 (JValue::Array(arr), JValue::Number(n)) => {
10255 let idx = *n as i64;
10256 let len = arr.len() as i64;
10257
10258 let actual_idx = if idx < 0 { len + idx } else { idx };
10260
10261 if actual_idx < 0 || actual_idx >= len {
10262 Ok(JValue::Undefined)
10263 } else {
10264 Ok(arr[actual_idx as usize].clone())
10265 }
10266 }
10267 _ => Err(EvaluatorError::TypeError(
10268 "Array indexing requires array and number".to_string(),
10269 )),
10270 }
10271 }
10272
10273 fn array_filter(
10276 &mut self,
10277 _lhs_node: &AstNode,
10278 rhs_node: &AstNode,
10279 array: &JValue,
10280 _original_data: &JValue,
10281 ) -> Result<JValue, EvaluatorError> {
10282 match array {
10283 JValue::Array(arr) => {
10284 let mut filtered = Vec::with_capacity(arr.len() / 2);
10286
10287 for item in arr.iter() {
10288 let predicate_result = self.evaluate_internal(rhs_node, item)?;
10291
10292 if self.is_truthy(&predicate_result) {
10294 filtered.push(item.clone());
10295 }
10296 }
10297
10298 Ok(JValue::array(filtered))
10299 }
10300 _ => Err(EvaluatorError::TypeError(
10301 "Array filtering requires an array".to_string(),
10302 )),
10303 }
10304 }
10305
10306 fn in_operator(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
10307 if left.is_null() || right.is_null() {
10310 return Ok(JValue::Bool(false));
10311 }
10312
10313 match right {
10314 JValue::Array(arr) => Ok(JValue::Bool(arr.iter().any(|v| self.equals(left, v)))),
10315 JValue::Object(obj) => {
10316 if let JValue::String(key) = left {
10317 Ok(JValue::Bool(obj.contains_key(&**key)))
10318 } else {
10319 Ok(JValue::Bool(false))
10320 }
10321 }
10322 other => Ok(JValue::Bool(self.equals(left, other))),
10325 }
10326 }
10327
10328 fn create_partial_application(
10332 &mut self,
10333 name: &str,
10334 args: &[AstNode],
10335 is_builtin: bool,
10336 data: &JValue,
10337 ) -> Result<JValue, EvaluatorError> {
10338 let is_lambda = self.context.lookup_lambda(name).is_some()
10340 || (self
10341 .context
10342 .lookup(name)
10343 .map(|v| matches!(v, JValue::Lambda { .. }))
10344 .unwrap_or(false));
10345
10346 if !is_lambda && !is_builtin {
10349 if self.is_builtin_function(name) {
10351 return Err(EvaluatorError::EvaluationError(format!(
10352 "T1007: Attempted to partially apply a non-function. Did you mean ${}?",
10353 name
10354 )));
10355 }
10356 return Err(EvaluatorError::EvaluationError(
10357 "T1008: Attempted to partially apply a non-function".to_string(),
10358 ));
10359 }
10360
10361 let mut bound_args: Vec<(usize, JValue)> = Vec::new();
10363 let mut placeholder_positions: Vec<usize> = Vec::new();
10364
10365 for (i, arg) in args.iter().enumerate() {
10366 if matches!(arg, AstNode::Placeholder) {
10367 placeholder_positions.push(i);
10368 } else {
10369 let value = self.evaluate_internal(arg, data)?;
10370 bound_args.push((i, value));
10371 }
10372 }
10373
10374 let param_names: Vec<String> = placeholder_positions
10376 .iter()
10377 .enumerate()
10378 .map(|(i, _)| format!("__p{}", i))
10379 .collect();
10380
10381 let partial_id = format!(
10384 "__partial_{}_{}_{}",
10385 name,
10386 placeholder_positions.len(),
10387 bound_args.len()
10388 );
10389
10390 let stored_lambda = StoredLambda {
10393 params: param_names.clone(),
10394 body: AstNode::String(format!(
10395 "__partial_call:{}:{}:{}",
10396 name,
10397 is_builtin,
10398 args.len()
10399 )),
10400 compiled_body: None, signature: None,
10402 captured_env: {
10403 let mut env = self.capture_current_environment();
10404 for (pos, value) in &bound_args {
10406 env.insert(format!("__bound_arg_{}", pos), value.clone());
10407 }
10408 env.insert(
10410 "__placeholder_positions".to_string(),
10411 JValue::array(
10412 placeholder_positions
10413 .iter()
10414 .map(|p| JValue::Number(*p as f64))
10415 .collect::<Vec<_>>(),
10416 ),
10417 );
10418 env.insert(
10420 "__total_args".to_string(),
10421 JValue::Number(args.len() as f64),
10422 );
10423 env
10424 },
10425 captured_data: Some(data.clone()),
10426 thunk: false,
10427 };
10428
10429 self.context.bind_lambda(partial_id.clone(), stored_lambda);
10430
10431 let lambda_obj = JValue::lambda(
10433 partial_id.as_str(),
10434 param_names,
10435 Some(name.to_string()),
10436 None::<String>,
10437 );
10438
10439 Ok(lambda_obj)
10440 }
10441}
10442
10443impl Default for Evaluator {
10444 fn default() -> Self {
10445 Self::new()
10446 }
10447}
10448
10449#[cfg(test)]
10450mod tests {
10451 use super::*;
10452 use crate::ast::{BinaryOp, UnaryOp};
10453
10454 #[test]
10455 fn test_evaluate_literals() {
10456 let mut evaluator = Evaluator::new();
10457 let data = JValue::Null;
10458
10459 let result = evaluator
10461 .evaluate(&AstNode::string("hello"), &data)
10462 .unwrap();
10463 assert_eq!(result, JValue::string("hello"));
10464
10465 let result = evaluator.evaluate(&AstNode::number(42.0), &data).unwrap();
10467 assert_eq!(result, JValue::from(42i64));
10468
10469 let result = evaluator.evaluate(&AstNode::boolean(true), &data).unwrap();
10471 assert_eq!(result, JValue::Bool(true));
10472
10473 let result = evaluator.evaluate(&AstNode::null(), &data).unwrap();
10475 assert_eq!(result, JValue::Null);
10476 }
10477
10478 #[test]
10479 fn test_evaluate_variables() {
10480 let mut evaluator = Evaluator::new();
10481 let data = JValue::Null;
10482
10483 evaluator
10485 .context
10486 .bind("x".to_string(), JValue::from(100i64));
10487
10488 let result = evaluator.evaluate(&AstNode::variable("x"), &data).unwrap();
10490 assert_eq!(result, JValue::from(100i64));
10491
10492 let result = evaluator
10494 .evaluate(&AstNode::variable("undefined"), &data)
10495 .unwrap();
10496 assert_eq!(result, JValue::Null);
10497 }
10498
10499 #[test]
10500 fn test_evaluate_path() {
10501 let mut evaluator = Evaluator::new();
10502 let data = JValue::from(serde_json::json!({
10503 "foo": {
10504 "bar": {
10505 "baz": 42
10506 }
10507 }
10508 }));
10509 let path = AstNode::Path {
10511 steps: vec![PathStep::new(AstNode::Name("foo".to_string()))],
10512 };
10513 let result = evaluator.evaluate(&path, &data).unwrap();
10514 assert_eq!(
10515 result,
10516 JValue::from(serde_json::json!({"bar": {"baz": 42}}))
10517 );
10518
10519 let path = AstNode::Path {
10521 steps: vec![
10522 PathStep::new(AstNode::Name("foo".to_string())),
10523 PathStep::new(AstNode::Name("bar".to_string())),
10524 PathStep::new(AstNode::Name("baz".to_string())),
10525 ],
10526 };
10527 let result = evaluator.evaluate(&path, &data).unwrap();
10528 assert_eq!(result, JValue::from(42i64));
10529
10530 let path = AstNode::Path {
10532 steps: vec![PathStep::new(AstNode::Name("missing".to_string()))],
10533 };
10534 let result = evaluator.evaluate(&path, &data).unwrap();
10535 assert_eq!(result, JValue::Undefined);
10536 }
10537
10538 #[test]
10539 fn test_arithmetic_operations() {
10540 let mut evaluator = Evaluator::new();
10541 let data = JValue::Null;
10542
10543 let expr = AstNode::Binary {
10545 op: BinaryOp::Add,
10546 lhs: Box::new(AstNode::number(10.0)),
10547 rhs: Box::new(AstNode::number(5.0)),
10548 };
10549 let result = evaluator.evaluate(&expr, &data).unwrap();
10550 assert_eq!(result, JValue::Number(15.0));
10551
10552 let expr = AstNode::Binary {
10554 op: BinaryOp::Subtract,
10555 lhs: Box::new(AstNode::number(10.0)),
10556 rhs: Box::new(AstNode::number(5.0)),
10557 };
10558 let result = evaluator.evaluate(&expr, &data).unwrap();
10559 assert_eq!(result, JValue::Number(5.0));
10560
10561 let expr = AstNode::Binary {
10563 op: BinaryOp::Multiply,
10564 lhs: Box::new(AstNode::number(10.0)),
10565 rhs: Box::new(AstNode::number(5.0)),
10566 };
10567 let result = evaluator.evaluate(&expr, &data).unwrap();
10568 assert_eq!(result, JValue::Number(50.0));
10569
10570 let expr = AstNode::Binary {
10572 op: BinaryOp::Divide,
10573 lhs: Box::new(AstNode::number(10.0)),
10574 rhs: Box::new(AstNode::number(5.0)),
10575 };
10576 let result = evaluator.evaluate(&expr, &data).unwrap();
10577 assert_eq!(result, JValue::Number(2.0));
10578
10579 let expr = AstNode::Binary {
10581 op: BinaryOp::Modulo,
10582 lhs: Box::new(AstNode::number(10.0)),
10583 rhs: Box::new(AstNode::number(3.0)),
10584 };
10585 let result = evaluator.evaluate(&expr, &data).unwrap();
10586 assert_eq!(result, JValue::Number(1.0));
10587 }
10588
10589 #[test]
10590 fn test_division_by_zero() {
10591 let mut evaluator = Evaluator::new();
10592 let data = JValue::Null;
10593
10594 let expr = AstNode::Binary {
10595 op: BinaryOp::Divide,
10596 lhs: Box::new(AstNode::number(10.0)),
10597 rhs: Box::new(AstNode::number(0.0)),
10598 };
10599 let result = evaluator.evaluate(&expr, &data);
10600 assert!(result.is_err());
10601 }
10602
10603 #[test]
10604 fn test_comparison_operations() {
10605 let mut evaluator = Evaluator::new();
10606 let data = JValue::Null;
10607
10608 let expr = AstNode::Binary {
10610 op: BinaryOp::Equal,
10611 lhs: Box::new(AstNode::number(5.0)),
10612 rhs: Box::new(AstNode::number(5.0)),
10613 };
10614 assert_eq!(
10615 evaluator.evaluate(&expr, &data).unwrap(),
10616 JValue::Bool(true)
10617 );
10618
10619 let expr = AstNode::Binary {
10621 op: BinaryOp::NotEqual,
10622 lhs: Box::new(AstNode::number(5.0)),
10623 rhs: Box::new(AstNode::number(3.0)),
10624 };
10625 assert_eq!(
10626 evaluator.evaluate(&expr, &data).unwrap(),
10627 JValue::Bool(true)
10628 );
10629
10630 let expr = AstNode::Binary {
10632 op: BinaryOp::LessThan,
10633 lhs: Box::new(AstNode::number(3.0)),
10634 rhs: Box::new(AstNode::number(5.0)),
10635 };
10636 assert_eq!(
10637 evaluator.evaluate(&expr, &data).unwrap(),
10638 JValue::Bool(true)
10639 );
10640
10641 let expr = AstNode::Binary {
10643 op: BinaryOp::GreaterThan,
10644 lhs: Box::new(AstNode::number(5.0)),
10645 rhs: Box::new(AstNode::number(3.0)),
10646 };
10647 assert_eq!(
10648 evaluator.evaluate(&expr, &data).unwrap(),
10649 JValue::Bool(true)
10650 );
10651 }
10652
10653 #[test]
10654 fn test_logical_operations() {
10655 let mut evaluator = Evaluator::new();
10656 let data = JValue::Null;
10657
10658 let expr = AstNode::Binary {
10660 op: BinaryOp::And,
10661 lhs: Box::new(AstNode::boolean(true)),
10662 rhs: Box::new(AstNode::boolean(true)),
10663 };
10664 assert_eq!(
10665 evaluator.evaluate(&expr, &data).unwrap(),
10666 JValue::Bool(true)
10667 );
10668
10669 let expr = AstNode::Binary {
10671 op: BinaryOp::And,
10672 lhs: Box::new(AstNode::boolean(false)),
10673 rhs: Box::new(AstNode::boolean(true)),
10674 };
10675 assert_eq!(
10676 evaluator.evaluate(&expr, &data).unwrap(),
10677 JValue::Bool(false)
10678 );
10679
10680 let expr = AstNode::Binary {
10682 op: BinaryOp::Or,
10683 lhs: Box::new(AstNode::boolean(true)),
10684 rhs: Box::new(AstNode::boolean(false)),
10685 };
10686 assert_eq!(
10687 evaluator.evaluate(&expr, &data).unwrap(),
10688 JValue::Bool(true)
10689 );
10690
10691 let expr = AstNode::Binary {
10693 op: BinaryOp::Or,
10694 lhs: Box::new(AstNode::boolean(false)),
10695 rhs: Box::new(AstNode::boolean(false)),
10696 };
10697 assert_eq!(
10698 evaluator.evaluate(&expr, &data).unwrap(),
10699 JValue::Bool(false)
10700 );
10701 }
10702
10703 #[test]
10704 fn test_string_concatenation() {
10705 let mut evaluator = Evaluator::new();
10706 let data = JValue::Null;
10707
10708 let expr = AstNode::Binary {
10709 op: BinaryOp::Concatenate,
10710 lhs: Box::new(AstNode::string("Hello")),
10711 rhs: Box::new(AstNode::string(" World")),
10712 };
10713 let result = evaluator.evaluate(&expr, &data).unwrap();
10714 assert_eq!(result, JValue::string("Hello World"));
10715 }
10716
10717 #[test]
10718 fn test_range_operator() {
10719 let mut evaluator = Evaluator::new();
10720 let data = JValue::Null;
10721
10722 let expr = AstNode::Binary {
10724 op: BinaryOp::Range,
10725 lhs: Box::new(AstNode::number(1.0)),
10726 rhs: Box::new(AstNode::number(5.0)),
10727 };
10728 let result = evaluator.evaluate(&expr, &data).unwrap();
10729 assert_eq!(
10730 result,
10731 JValue::array(vec![
10732 JValue::Number(1.0),
10733 JValue::Number(2.0),
10734 JValue::Number(3.0),
10735 JValue::Number(4.0),
10736 JValue::Number(5.0)
10737 ])
10738 );
10739
10740 let expr = AstNode::Binary {
10742 op: BinaryOp::Range,
10743 lhs: Box::new(AstNode::number(5.0)),
10744 rhs: Box::new(AstNode::number(1.0)),
10745 };
10746 let result = evaluator.evaluate(&expr, &data).unwrap();
10747 assert_eq!(result, JValue::array(vec![]));
10748 }
10749
10750 #[test]
10751 fn test_in_operator() {
10752 let mut evaluator = Evaluator::new();
10753 let data = JValue::Null;
10754
10755 let expr = AstNode::Binary {
10757 op: BinaryOp::In,
10758 lhs: Box::new(AstNode::number(3.0)),
10759 rhs: Box::new(AstNode::Array(vec![
10760 AstNode::number(1.0),
10761 AstNode::number(2.0),
10762 AstNode::number(3.0),
10763 ])),
10764 };
10765 let result = evaluator.evaluate(&expr, &data).unwrap();
10766 assert_eq!(result, JValue::Bool(true));
10767
10768 let expr = AstNode::Binary {
10770 op: BinaryOp::In,
10771 lhs: Box::new(AstNode::number(5.0)),
10772 rhs: Box::new(AstNode::Array(vec![
10773 AstNode::number(1.0),
10774 AstNode::number(2.0),
10775 AstNode::number(3.0),
10776 ])),
10777 };
10778 let result = evaluator.evaluate(&expr, &data).unwrap();
10779 assert_eq!(result, JValue::Bool(false));
10780 }
10781
10782 #[test]
10783 fn test_unary_operations() {
10784 let mut evaluator = Evaluator::new();
10785 let data = JValue::Null;
10786
10787 let expr = AstNode::Unary {
10789 op: UnaryOp::Negate,
10790 operand: Box::new(AstNode::number(5.0)),
10791 };
10792 let result = evaluator.evaluate(&expr, &data).unwrap();
10793 assert_eq!(result, JValue::Number(-5.0));
10794
10795 let expr = AstNode::Unary {
10797 op: UnaryOp::Not,
10798 operand: Box::new(AstNode::boolean(true)),
10799 };
10800 let result = evaluator.evaluate(&expr, &data).unwrap();
10801 assert_eq!(result, JValue::Bool(false));
10802 }
10803
10804 #[test]
10805 fn test_array_construction() {
10806 let mut evaluator = Evaluator::new();
10807 let data = JValue::Null;
10808
10809 let expr = AstNode::Array(vec![
10810 AstNode::number(1.0),
10811 AstNode::number(2.0),
10812 AstNode::number(3.0),
10813 ]);
10814 let result = evaluator.evaluate(&expr, &data).unwrap();
10815 assert_eq!(result, JValue::from(serde_json::json!([1, 2, 3])));
10817 }
10818
10819 #[test]
10820 fn test_object_construction() {
10821 let mut evaluator = Evaluator::new();
10822 let data = JValue::Null;
10823
10824 let expr = AstNode::Object(vec![
10825 (AstNode::string("name"), AstNode::string("Alice")),
10826 (AstNode::string("age"), AstNode::number(30.0)),
10827 ]);
10828 let result = evaluator.evaluate(&expr, &data).unwrap();
10829 let mut expected = IndexMap::new();
10831 expected.insert("name".to_string(), JValue::string("Alice"));
10832 expected.insert("age".to_string(), JValue::Number(30.0));
10833 assert_eq!(result, JValue::object(expected));
10834 }
10835
10836 #[test]
10837 fn test_conditional() {
10838 let mut evaluator = Evaluator::new();
10839 let data = JValue::Null;
10840
10841 let expr = AstNode::Conditional {
10843 condition: Box::new(AstNode::boolean(true)),
10844 then_branch: Box::new(AstNode::string("yes")),
10845 else_branch: Some(Box::new(AstNode::string("no"))),
10846 };
10847 let result = evaluator.evaluate(&expr, &data).unwrap();
10848 assert_eq!(result, JValue::string("yes"));
10849
10850 let expr = AstNode::Conditional {
10852 condition: Box::new(AstNode::boolean(false)),
10853 then_branch: Box::new(AstNode::string("yes")),
10854 else_branch: Some(Box::new(AstNode::string("no"))),
10855 };
10856 let result = evaluator.evaluate(&expr, &data).unwrap();
10857 assert_eq!(result, JValue::string("no"));
10858
10859 let expr = AstNode::Conditional {
10861 condition: Box::new(AstNode::boolean(false)),
10862 then_branch: Box::new(AstNode::string("yes")),
10863 else_branch: None,
10864 };
10865 let result = evaluator.evaluate(&expr, &data).unwrap();
10866 assert_eq!(result, JValue::Undefined);
10867 }
10868
10869 #[test]
10870 fn test_block_expression() {
10871 let mut evaluator = Evaluator::new();
10872 let data = JValue::Null;
10873
10874 let expr = AstNode::Block(vec![
10875 AstNode::number(1.0),
10876 AstNode::number(2.0),
10877 AstNode::number(3.0),
10878 ]);
10879 let result = evaluator.evaluate(&expr, &data).unwrap();
10880 assert_eq!(result, JValue::from(3i64));
10882 }
10883
10884 #[test]
10885 fn test_function_calls() {
10886 let mut evaluator = Evaluator::new();
10887 let data = JValue::Null;
10888
10889 let expr = AstNode::Function {
10891 name: "uppercase".to_string(),
10892 args: vec![AstNode::string("hello")],
10893 is_builtin: true,
10894 };
10895 let result = evaluator.evaluate(&expr, &data).unwrap();
10896 assert_eq!(result, JValue::string("HELLO"));
10897
10898 let expr = AstNode::Function {
10900 name: "lowercase".to_string(),
10901 args: vec![AstNode::string("HELLO")],
10902 is_builtin: true,
10903 };
10904 let result = evaluator.evaluate(&expr, &data).unwrap();
10905 assert_eq!(result, JValue::string("hello"));
10906
10907 let expr = AstNode::Function {
10909 name: "length".to_string(),
10910 args: vec![AstNode::string("hello")],
10911 is_builtin: true,
10912 };
10913 let result = evaluator.evaluate(&expr, &data).unwrap();
10914 assert_eq!(result, JValue::from(5i64));
10915
10916 let expr = AstNode::Function {
10918 name: "sum".to_string(),
10919 args: vec![AstNode::Array(vec![
10920 AstNode::number(1.0),
10921 AstNode::number(2.0),
10922 AstNode::number(3.0),
10923 ])],
10924 is_builtin: true,
10925 };
10926 let result = evaluator.evaluate(&expr, &data).unwrap();
10927 assert_eq!(result, JValue::Number(6.0));
10928
10929 let expr = AstNode::Function {
10931 name: "count".to_string(),
10932 args: vec![AstNode::Array(vec![
10933 AstNode::number(1.0),
10934 AstNode::number(2.0),
10935 AstNode::number(3.0),
10936 ])],
10937 is_builtin: true,
10938 };
10939 let result = evaluator.evaluate(&expr, &data).unwrap();
10940 assert_eq!(result, JValue::from(3i64));
10941 }
10942
10943 #[test]
10944 fn test_complex_nested_data() {
10945 let mut evaluator = Evaluator::new();
10946 let data = JValue::from(serde_json::json!({
10947 "users": [
10948 {"name": "Alice", "age": 30},
10949 {"name": "Bob", "age": 25},
10950 {"name": "Charlie", "age": 35}
10951 ],
10952 "metadata": {
10953 "total": 3,
10954 "version": "1.0"
10955 }
10956 }));
10957 let path = AstNode::Path {
10959 steps: vec![
10960 PathStep::new(AstNode::Name("metadata".to_string())),
10961 PathStep::new(AstNode::Name("version".to_string())),
10962 ],
10963 };
10964 let result = evaluator.evaluate(&path, &data).unwrap();
10965 assert_eq!(result, JValue::string("1.0"));
10966 }
10967
10968 #[test]
10969 fn test_error_handling() {
10970 let mut evaluator = Evaluator::new();
10971 let data = JValue::Null;
10972
10973 let expr = AstNode::Binary {
10975 op: BinaryOp::Add,
10976 lhs: Box::new(AstNode::string("hello")),
10977 rhs: Box::new(AstNode::number(5.0)),
10978 };
10979 let result = evaluator.evaluate(&expr, &data);
10980 assert!(result.is_err());
10981
10982 let expr = AstNode::Function {
10984 name: "undefined_function".to_string(),
10985 args: vec![],
10986 is_builtin: false,
10987 };
10988 let result = evaluator.evaluate(&expr, &data);
10989 assert!(result.is_err());
10990 }
10991
10992 #[test]
10993 fn test_truthiness() {
10994 let evaluator = Evaluator::new();
10995
10996 assert!(!evaluator.is_truthy(&JValue::Null));
10997 assert!(!evaluator.is_truthy(&JValue::Bool(false)));
10998 assert!(evaluator.is_truthy(&JValue::Bool(true)));
10999 assert!(!evaluator.is_truthy(&JValue::from(0i64)));
11000 assert!(evaluator.is_truthy(&JValue::from(1i64)));
11001 assert!(!evaluator.is_truthy(&JValue::string("")));
11002 assert!(evaluator.is_truthy(&JValue::string("hello")));
11003 assert!(!evaluator.is_truthy(&JValue::array(vec![])));
11004 assert!(evaluator.is_truthy(&JValue::from(serde_json::json!([1, 2, 3]))));
11005 }
11006
11007 #[test]
11008 fn test_integration_with_parser() {
11009 use crate::parser::parse;
11010
11011 let mut evaluator = Evaluator::new();
11012 let data = JValue::from(serde_json::json!({
11013 "price": 10,
11014 "quantity": 5
11015 }));
11016 let ast = parse("price").unwrap();
11018 let result = evaluator.evaluate(&ast, &data).unwrap();
11019 assert_eq!(result, JValue::from(10i64));
11020
11021 let ast = parse("price * quantity").unwrap();
11023 let result = evaluator.evaluate(&ast, &data).unwrap();
11024 assert_eq!(result, JValue::Number(50.0));
11026
11027 let ast = parse("price > 5").unwrap();
11029 let result = evaluator.evaluate(&ast, &data).unwrap();
11030 assert_eq!(result, JValue::Bool(true));
11031 }
11032
11033 #[test]
11034 fn test_evaluate_dollar_function_uppercase() {
11035 use crate::parser::parse;
11036
11037 let mut evaluator = Evaluator::new();
11038 let ast = parse(r#"$uppercase("hello")"#).unwrap();
11039 let empty = JValue::object(IndexMap::new());
11040 let result = evaluator.evaluate(&ast, &empty).unwrap();
11041 assert_eq!(result, JValue::string("HELLO"));
11042 }
11043
11044 #[test]
11045 fn test_evaluate_dollar_function_sum() {
11046 use crate::parser::parse;
11047
11048 let mut evaluator = Evaluator::new();
11049 let ast = parse("$sum([1, 2, 3, 4, 5])").unwrap();
11050 let empty = JValue::object(IndexMap::new());
11051 let result = evaluator.evaluate(&ast, &empty).unwrap();
11052 assert_eq!(result, JValue::Number(15.0));
11053 }
11054
11055 #[test]
11056 fn test_evaluate_nested_dollar_functions() {
11057 use crate::parser::parse;
11058
11059 let mut evaluator = Evaluator::new();
11060 let ast = parse(r#"$length($lowercase("HELLO"))"#).unwrap();
11061 let empty = JValue::object(IndexMap::new());
11062 let result = evaluator.evaluate(&ast, &empty).unwrap();
11063 assert_eq!(result, JValue::Number(5.0));
11065 }
11066
11067 #[test]
11068 fn test_array_mapping() {
11069 use crate::parser::parse;
11070
11071 let mut evaluator = Evaluator::new();
11072 let data: JValue = serde_json::from_str(
11073 r#"{
11074 "products": [
11075 {"id": 1, "name": "Laptop", "price": 999.99},
11076 {"id": 2, "name": "Mouse", "price": 29.99},
11077 {"id": 3, "name": "Keyboard", "price": 79.99}
11078 ]
11079 }"#,
11080 )
11081 .map(|v: serde_json::Value| JValue::from(v))
11082 .unwrap();
11083
11084 let ast = parse("products.name").unwrap();
11086 let result = evaluator.evaluate(&ast, &data).unwrap();
11087 assert_eq!(
11088 result,
11089 JValue::array(vec![
11090 JValue::string("Laptop"),
11091 JValue::string("Mouse"),
11092 JValue::string("Keyboard")
11093 ])
11094 );
11095
11096 let ast = parse("products.price").unwrap();
11098 let result = evaluator.evaluate(&ast, &data).unwrap();
11099 assert_eq!(
11100 result,
11101 JValue::array(vec![
11102 JValue::Number(999.99),
11103 JValue::Number(29.99),
11104 JValue::Number(79.99)
11105 ])
11106 );
11107
11108 let ast = parse("$sum(products.price)").unwrap();
11110 let result = evaluator.evaluate(&ast, &data).unwrap();
11111 assert_eq!(result, JValue::Number(1109.97));
11112 }
11113
11114 #[test]
11115 fn test_empty_brackets() {
11116 use crate::parser::parse;
11117
11118 let mut evaluator = Evaluator::new();
11119
11120 let data: JValue = JValue::from(serde_json::json!({"foo": "bar"}));
11122 let ast = parse("foo[]").unwrap();
11123 let result = evaluator.evaluate(&ast, &data).unwrap();
11124 assert_eq!(
11125 result,
11126 JValue::array(vec![JValue::string("bar")]),
11127 "Empty brackets should wrap value in array"
11128 );
11129
11130 let data2: JValue = JValue::from(serde_json::json!({"arr": [1, 2, 3]}));
11132 let ast2 = parse("arr[]").unwrap();
11133 let result2 = evaluator.evaluate(&ast2, &data2).unwrap();
11134 assert_eq!(
11135 result2,
11136 JValue::array(vec![
11137 JValue::Number(1.0),
11138 JValue::Number(2.0),
11139 JValue::Number(3.0)
11140 ]),
11141 "Empty brackets should preserve array"
11142 );
11143 }
11144}