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 { ord.reverse() } else { ord }
48}
49
50fn try_specialize_sort_comparator(
53 body: &AstNode,
54 left_param: &str,
55 right_param: &str,
56) -> Option<SpecializedSortComparator> {
57 let AstNode::Binary { op, lhs, rhs } = body else {
58 return None;
59 };
60
61 let is_ascending = |op: &BinaryOp| -> Option<bool> {
63 match op {
64 BinaryOp::GreaterThan | BinaryOp::GreaterThanOrEqual => Some(true),
65 BinaryOp::LessThan | BinaryOp::LessThanOrEqual => Some(false),
66 _ => None,
67 }
68 };
69
70 let extract_var_field = |node: &AstNode, param: &str| -> Option<String> {
72 let AstNode::Path { steps } = node else { return None };
73 if steps.len() != 2 { return None; }
74 let AstNode::Variable(var) = &steps[0].node else { return None };
75 if var != param { return None; }
76 let AstNode::Name(field) = &steps[1].node else { return None };
77 if !steps[0].stages.is_empty() || !steps[1].stages.is_empty() { return None; }
78 Some(field.clone())
79 };
80
81 for flipped in [false, true] {
83 let (lhs_param, rhs_param) = if flipped {
84 (right_param, left_param)
85 } else {
86 (left_param, right_param)
87 };
88 if let (Some(lhs_field), Some(rhs_field)) = (
89 extract_var_field(lhs, lhs_param),
90 extract_var_field(rhs, rhs_param),
91 ) {
92 if lhs_field == rhs_field {
93 let descending = match op {
94 BinaryOp::Subtract => flipped,
97 _ => {
99 let ascending = is_ascending(op)?;
100 if flipped { ascending } else { !ascending }
101 }
102 };
103 return Some(SpecializedSortComparator {
104 field: lhs_field,
105 descending,
106 });
107 }
108 }
109 }
110 None
111}
112
113type ShapeCache = HashMap<String, usize>;
127
128fn build_shape_cache(first_element: &JValue) -> Option<ShapeCache> {
131 match first_element {
132 JValue::Object(obj) => {
133 let mut cache = HashMap::with_capacity(obj.len());
134 for (idx, (key, _)) in obj.iter().enumerate() {
135 cache.insert(key.clone(), idx);
136 }
137 Some(cache)
138 }
139 _ => None,
140 }
141}
142
143#[derive(Debug, Clone, Copy)]
145pub(crate) enum CompiledCmp {
146 Eq,
147 Ne,
148 Lt,
149 Le,
150 Gt,
151 Ge,
152}
153
154#[derive(Debug, Clone, Copy)]
156pub(crate) enum CompiledArithOp {
157 Add,
158 Sub,
159 Mul,
160 Div,
161 Mod,
162}
163
164#[derive(Clone, Debug)]
170pub(crate) enum CompiledExpr {
171 Literal(JValue),
174 ExplicitNull,
178 FieldLookup(String),
180 NestedFieldLookup(String, String),
182 VariableLookup(String),
185
186 Compare {
188 op: CompiledCmp,
189 lhs: Box<CompiledExpr>,
190 rhs: Box<CompiledExpr>,
191 },
192
193 Arithmetic {
195 op: CompiledArithOp,
196 lhs: Box<CompiledExpr>,
197 rhs: Box<CompiledExpr>,
198 },
199
200 Concat(Box<CompiledExpr>, Box<CompiledExpr>),
202
203 And(Box<CompiledExpr>, Box<CompiledExpr>),
205 Or(Box<CompiledExpr>, Box<CompiledExpr>),
206 Not(Box<CompiledExpr>),
207 Negate(Box<CompiledExpr>),
209
210 Conditional {
212 condition: Box<CompiledExpr>,
213 then_expr: Box<CompiledExpr>,
214 else_expr: Option<Box<CompiledExpr>>,
215 },
216
217 ObjectConstruct(Vec<(String, CompiledExpr)>),
220 ArrayConstruct(Vec<(CompiledExpr, bool)>),
228
229 #[allow(dead_code)]
235 ContextVar(String),
236
237 FieldPath(Vec<CompiledStep>),
240
241 BuiltinCall {
244 name: &'static str,
245 args: Vec<CompiledExpr>,
246 },
247
248 Block(Vec<CompiledExpr>),
250
251 Coalesce(Box<CompiledExpr>, Box<CompiledExpr>),
253}
254
255#[derive(Clone, Debug)]
257pub(crate) struct CompiledStep {
258 pub field: String,
260 pub filter: Option<CompiledExpr>,
262}
263
264pub(crate) fn try_compile_expr(node: &AstNode) -> Option<CompiledExpr> {
268 try_compile_expr_inner(node, None)
269}
270
271pub(crate) fn try_compile_expr_with_allowed_vars(
275 node: &AstNode,
276 allowed_vars: &[&str],
277) -> Option<CompiledExpr> {
278 try_compile_expr_inner(node, Some(allowed_vars))
279}
280
281fn try_compile_expr_inner(
282 node: &AstNode,
283 allowed_vars: Option<&[&str]>,
284) -> Option<CompiledExpr> {
285 match node {
286 AstNode::String(s) => Some(CompiledExpr::Literal(JValue::string(s.clone()))),
288 AstNode::Number(n) => Some(CompiledExpr::Literal(JValue::Number(*n))),
289 AstNode::Boolean(b) => Some(CompiledExpr::Literal(JValue::Bool(*b))),
290 AstNode::Null => Some(CompiledExpr::ExplicitNull),
291
292 AstNode::Name(field) => Some(CompiledExpr::FieldLookup(field.clone())),
294
295 AstNode::Variable(var) if var.is_empty() => {
302 Some(CompiledExpr::VariableLookup(var.clone()))
303 }
304 AstNode::Variable(var) => {
305 if let Some(allowed) = allowed_vars {
306 if allowed.contains(&var.as_str()) {
308 return Some(CompiledExpr::VariableLookup(var.clone()));
309 }
310 }
311 None
315 }
316
317 AstNode::Path { steps } => try_compile_path(steps, allowed_vars),
319
320 AstNode::Binary { op, lhs, rhs } => {
322 let compiled_lhs = try_compile_expr_inner(lhs, allowed_vars)?;
323 let compiled_rhs = try_compile_expr_inner(rhs, allowed_vars)?;
324 match op {
325 BinaryOp::Equal => Some(CompiledExpr::Compare {
327 op: CompiledCmp::Eq,
328 lhs: Box::new(compiled_lhs),
329 rhs: Box::new(compiled_rhs),
330 }),
331 BinaryOp::NotEqual => Some(CompiledExpr::Compare {
332 op: CompiledCmp::Ne,
333 lhs: Box::new(compiled_lhs),
334 rhs: Box::new(compiled_rhs),
335 }),
336 BinaryOp::LessThan => Some(CompiledExpr::Compare {
337 op: CompiledCmp::Lt,
338 lhs: Box::new(compiled_lhs),
339 rhs: Box::new(compiled_rhs),
340 }),
341 BinaryOp::LessThanOrEqual => Some(CompiledExpr::Compare {
342 op: CompiledCmp::Le,
343 lhs: Box::new(compiled_lhs),
344 rhs: Box::new(compiled_rhs),
345 }),
346 BinaryOp::GreaterThan => Some(CompiledExpr::Compare {
347 op: CompiledCmp::Gt,
348 lhs: Box::new(compiled_lhs),
349 rhs: Box::new(compiled_rhs),
350 }),
351 BinaryOp::GreaterThanOrEqual => Some(CompiledExpr::Compare {
352 op: CompiledCmp::Ge,
353 lhs: Box::new(compiled_lhs),
354 rhs: Box::new(compiled_rhs),
355 }),
356 BinaryOp::Add => Some(CompiledExpr::Arithmetic {
358 op: CompiledArithOp::Add,
359 lhs: Box::new(compiled_lhs),
360 rhs: Box::new(compiled_rhs),
361 }),
362 BinaryOp::Subtract => Some(CompiledExpr::Arithmetic {
363 op: CompiledArithOp::Sub,
364 lhs: Box::new(compiled_lhs),
365 rhs: Box::new(compiled_rhs),
366 }),
367 BinaryOp::Multiply => Some(CompiledExpr::Arithmetic {
368 op: CompiledArithOp::Mul,
369 lhs: Box::new(compiled_lhs),
370 rhs: Box::new(compiled_rhs),
371 }),
372 BinaryOp::Divide => Some(CompiledExpr::Arithmetic {
373 op: CompiledArithOp::Div,
374 lhs: Box::new(compiled_lhs),
375 rhs: Box::new(compiled_rhs),
376 }),
377 BinaryOp::Modulo => Some(CompiledExpr::Arithmetic {
378 op: CompiledArithOp::Mod,
379 lhs: Box::new(compiled_lhs),
380 rhs: Box::new(compiled_rhs),
381 }),
382 BinaryOp::And => Some(CompiledExpr::And(
384 Box::new(compiled_lhs),
385 Box::new(compiled_rhs),
386 )),
387 BinaryOp::Or => Some(CompiledExpr::Or(
388 Box::new(compiled_lhs),
389 Box::new(compiled_rhs),
390 )),
391 BinaryOp::Concatenate => Some(CompiledExpr::Concat(
393 Box::new(compiled_lhs),
394 Box::new(compiled_rhs),
395 )),
396 BinaryOp::Coalesce => Some(CompiledExpr::Coalesce(
398 Box::new(compiled_lhs),
399 Box::new(compiled_rhs),
400 )),
401 _ => None,
403 }
404 }
405
406 AstNode::Unary { op, operand } => {
408 let compiled = try_compile_expr_inner(operand, allowed_vars)?;
409 match op {
410 crate::ast::UnaryOp::Not => Some(CompiledExpr::Not(Box::new(compiled))),
411 crate::ast::UnaryOp::Negate => Some(CompiledExpr::Negate(Box::new(compiled))),
412 }
413 }
414
415 AstNode::Conditional {
417 condition,
418 then_branch,
419 else_branch,
420 } => {
421 let cond = try_compile_expr_inner(condition, allowed_vars)?;
422 let then_e = try_compile_expr_inner(then_branch, allowed_vars)?;
423 let else_e = match else_branch {
424 Some(e) => Some(Box::new(try_compile_expr_inner(e, allowed_vars)?)),
425 None => None,
426 };
427 Some(CompiledExpr::Conditional {
428 condition: Box::new(cond),
429 then_expr: Box::new(then_e),
430 else_expr: else_e,
431 })
432 }
433
434 AstNode::Object(pairs) => {
436 let mut fields = Vec::with_capacity(pairs.len());
437 for (key_node, val_node) in pairs {
438 let key = match key_node {
440 AstNode::String(s) => s.clone(),
441 _ => return None,
442 };
443 let val = try_compile_expr_inner(val_node, allowed_vars)?;
444 fields.push((key, val));
445 }
446 Some(CompiledExpr::ObjectConstruct(fields))
447 }
448
449 AstNode::Array(elems) => {
451 let mut compiled = Vec::with_capacity(elems.len());
452 for elem in elems {
453 let is_nested = matches!(elem, AstNode::Array(_));
456 compiled.push((try_compile_expr_inner(elem, allowed_vars)?, is_nested));
457 }
458 Some(CompiledExpr::ArrayConstruct(compiled))
459 }
460
461 AstNode::Block(exprs) if !exprs.is_empty() => {
463 let compiled: Option<Vec<CompiledExpr>> = exprs
464 .iter()
465 .map(|e| try_compile_expr_inner(e, allowed_vars))
466 .collect();
467 compiled.map(CompiledExpr::Block)
468 }
469
470 AstNode::Function {
472 name,
473 args,
474 is_builtin: true,
475 } => {
476 if is_compilable_builtin(name) {
477 if let Some(max) = compilable_builtin_max_args(name) {
480 if args.len() > max {
481 return None;
482 }
483 }
484 let compiled_args: Option<Vec<CompiledExpr>> = args
485 .iter()
486 .map(|a| try_compile_expr_inner(a, allowed_vars))
487 .collect();
488 compiled_args.map(|cargs| CompiledExpr::BuiltinCall {
489 name: static_builtin_name(name),
490 args: cargs,
491 })
492 } else {
493 None
494 }
495 }
496
497 _ => None,
499 }
500}
501
502fn is_compilable_builtin(name: &str) -> bool {
505 matches!(
506 name,
507 "string"
508 | "length"
509 | "substring"
510 | "substringBefore"
511 | "substringAfter"
512 | "uppercase"
513 | "lowercase"
514 | "trim"
515 | "contains"
516 | "split"
517 | "join"
518 | "number"
519 | "floor"
520 | "ceil"
521 | "round"
522 | "abs"
523 | "sqrt"
524 | "sum"
525 | "max"
526 | "min"
527 | "average"
528 | "count"
529 | "boolean"
530 | "not"
531 | "keys"
532 | "append"
533 | "reverse"
534 | "distinct"
535 | "merge"
536 )
537}
538
539fn compilable_builtin_max_args(name: &str) -> Option<usize> {
544 match name {
545 "string" => Some(2),
546 "length" | "uppercase" | "lowercase" | "trim" => Some(1),
547 "substring" | "split" => Some(3),
548 "substringBefore" | "substringAfter" | "contains" | "join" | "append" | "round" => Some(2),
549 "number" | "floor" | "ceil" | "abs" | "sqrt" => Some(1),
550 "sum" | "max" | "min" | "average" | "count" => Some(1),
551 "boolean" | "not" | "keys" | "reverse" | "distinct" => Some(1),
552 "merge" => None, _ => None,
554 }
555}
556
557fn static_builtin_name(name: &str) -> &'static str {
560 match name {
561 "string" => "string",
562 "length" => "length",
563 "substring" => "substring",
564 "substringBefore" => "substringBefore",
565 "substringAfter" => "substringAfter",
566 "uppercase" => "uppercase",
567 "lowercase" => "lowercase",
568 "trim" => "trim",
569 "contains" => "contains",
570 "split" => "split",
571 "join" => "join",
572 "number" => "number",
573 "floor" => "floor",
574 "ceil" => "ceil",
575 "round" => "round",
576 "abs" => "abs",
577 "sqrt" => "sqrt",
578 "sum" => "sum",
579 "max" => "max",
580 "min" => "min",
581 "average" => "average",
582 "count" => "count",
583 "boolean" => "boolean",
584 "not" => "not",
585 "keys" => "keys",
586 "append" => "append",
587 "reverse" => "reverse",
588 "distinct" => "distinct",
589 "merge" => "merge",
590 _ => unreachable!("Not a compilable builtin: {}", name),
591 }
592}
593
594#[inline(always)]
602pub(crate) fn eval_compiled(
603 expr: &CompiledExpr,
604 data: &JValue,
605 vars: Option<&HashMap<&str, &JValue>>,
606) -> Result<JValue, EvaluatorError> {
607 eval_compiled_inner(expr, data, vars, None, None)
608}
609
610#[inline(always)]
614fn eval_compiled_shaped(
615 expr: &CompiledExpr,
616 data: &JValue,
617 vars: Option<&HashMap<&str, &JValue>>,
618 shape: &ShapeCache,
619) -> Result<JValue, EvaluatorError> {
620 eval_compiled_inner(expr, data, vars, None, Some(shape))
621}
622
623fn eval_compiled_inner(
624 expr: &CompiledExpr,
625 data: &JValue,
626 vars: Option<&HashMap<&str, &JValue>>,
627 ctx: Option<&Context>,
628 shape: Option<&ShapeCache>,
629) -> Result<JValue, EvaluatorError> {
630 match expr {
631 CompiledExpr::Literal(v) => Ok(v.clone()),
633
634 CompiledExpr::ExplicitNull => Ok(JValue::Null),
637
638 CompiledExpr::FieldLookup(field) => match data {
639 JValue::Object(obj) => {
640 if let Some(shape) = shape {
642 if let Some(&idx) = shape.get(field.as_str()) {
643 return Ok(obj
644 .get_index(idx)
645 .map(|(_, v)| v.clone())
646 .unwrap_or(JValue::Undefined));
647 }
648 }
649 Ok(obj.get(field.as_str()).cloned().unwrap_or(JValue::Undefined))
650 }
651 _ => Ok(JValue::Undefined),
652 },
653
654 CompiledExpr::NestedFieldLookup(outer, inner) => match data {
655 JValue::Object(obj) => {
656 let outer_val = if let Some(shape) = shape {
658 if let Some(&idx) = shape.get(outer.as_str()) {
659 obj.get_index(idx).map(|(_, v)| v)
660 } else {
661 obj.get(outer.as_str())
662 }
663 } else {
664 obj.get(outer.as_str())
665 };
666 Ok(outer_val
667 .and_then(|v| match v {
668 JValue::Object(nested) => nested.get(inner.as_str()).cloned(),
669 _ => None,
670 })
671 .unwrap_or(JValue::Undefined))
672 }
673 _ => Ok(JValue::Undefined),
674 },
675
676 CompiledExpr::VariableLookup(var) => {
677 if let Some(vars) = vars {
678 if let Some(val) = vars.get(var.as_str()) {
679 return Ok((*val).clone());
680 }
681 }
682 if var.is_empty() {
684 return Ok(data.clone());
685 }
686 Ok(JValue::Undefined)
687 }
688
689 CompiledExpr::Compare { op, lhs, rhs } => {
691 let lhs_explicit_null = is_compiled_explicit_null(lhs);
692 let rhs_explicit_null = is_compiled_explicit_null(rhs);
693 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
694 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
695 match op {
696 CompiledCmp::Eq => Ok(JValue::Bool(
697 crate::functions::array::values_equal(&left, &right),
698 )),
699 CompiledCmp::Ne => Ok(JValue::Bool(
700 !crate::functions::array::values_equal(&left, &right),
701 )),
702 CompiledCmp::Lt => compiled_ordered_cmp(
703 &left, &right, lhs_explicit_null, rhs_explicit_null,
704 |a, b| a < b, |a, b| a < b,
705 ),
706 CompiledCmp::Le => compiled_ordered_cmp(
707 &left, &right, lhs_explicit_null, rhs_explicit_null,
708 |a, b| a <= b, |a, b| a <= b,
709 ),
710 CompiledCmp::Gt => compiled_ordered_cmp(
711 &left, &right, lhs_explicit_null, rhs_explicit_null,
712 |a, b| a > b, |a, b| a > b,
713 ),
714 CompiledCmp::Ge => compiled_ordered_cmp(
715 &left, &right, lhs_explicit_null, rhs_explicit_null,
716 |a, b| a >= b, |a, b| a >= b,
717 ),
718 }
719 }
720
721 CompiledExpr::Arithmetic { op, lhs, rhs } => {
723 let lhs_explicit_null = is_compiled_explicit_null(lhs);
724 let rhs_explicit_null = is_compiled_explicit_null(rhs);
725 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
726 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
727 compiled_arithmetic(*op, &left, &right, lhs_explicit_null, rhs_explicit_null)
728 }
729
730 CompiledExpr::Concat(lhs, rhs) => {
732 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
733 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
734 let ls = compiled_to_concat_string(&left)?;
735 let rs = compiled_to_concat_string(&right)?;
736 Ok(JValue::string(format!("{}{}", ls, rs)))
737 }
738
739 CompiledExpr::And(lhs, rhs) => {
741 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
742 if !compiled_is_truthy(&left) {
743 return Ok(JValue::Bool(false));
744 }
745 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
746 Ok(JValue::Bool(compiled_is_truthy(&right)))
747 }
748 CompiledExpr::Or(lhs, rhs) => {
749 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
750 if compiled_is_truthy(&left) {
751 return Ok(JValue::Bool(true));
752 }
753 let right = eval_compiled_inner(rhs, data, vars, ctx, shape)?;
754 Ok(JValue::Bool(compiled_is_truthy(&right)))
755 }
756 CompiledExpr::Not(inner) => {
757 let val = eval_compiled_inner(inner, data, vars, ctx, shape)?;
758 Ok(JValue::Bool(!compiled_is_truthy(&val)))
759 }
760 CompiledExpr::Negate(inner) => {
761 let val = eval_compiled_inner(inner, data, vars, ctx, shape)?;
762 match val {
763 JValue::Number(n) => Ok(JValue::Number(-n)),
764 JValue::Null => Ok(JValue::Null),
765 v if v.is_undefined() => Ok(JValue::Undefined),
767 _ => Err(EvaluatorError::TypeError(
768 "D1002: Cannot negate non-number value".to_string(),
769 )),
770 }
771 }
772
773 CompiledExpr::Conditional {
775 condition,
776 then_expr,
777 else_expr,
778 } => {
779 let cond = eval_compiled_inner(condition, data, vars, ctx, shape)?;
780 if compiled_is_truthy(&cond) {
781 eval_compiled_inner(then_expr, data, vars, ctx, shape)
782 } else if let Some(else_e) = else_expr {
783 eval_compiled_inner(else_e, data, vars, ctx, shape)
784 } else {
785 Ok(JValue::Undefined)
786 }
787 }
788
789 CompiledExpr::ObjectConstruct(fields) => {
791 let mut result = IndexMap::with_capacity(fields.len());
792 for (key, expr) in fields {
793 let value = eval_compiled_inner(expr, data, vars, ctx, shape)?;
794 if !value.is_undefined() {
795 result.insert(key.clone(), value);
796 }
797 }
798 Ok(JValue::object(result))
799 }
800
801 CompiledExpr::ArrayConstruct(elems) => {
803 let mut result = Vec::new();
804 for (elem_expr, is_nested) in elems {
805 let value = eval_compiled_inner(elem_expr, data, vars, ctx, shape)?;
806 if value.is_undefined() {
808 continue;
809 }
810 if *is_nested {
811 result.push(value);
813 } else if let JValue::Array(arr) = value {
814 result.extend(arr.iter().cloned());
816 } else {
817 result.push(value);
818 }
819 }
820 Ok(JValue::array(result))
821 }
822
823 CompiledExpr::ContextVar(name) => {
830 if let Some(vars) = vars {
832 if let Some(val) = vars.get(name.as_str()) {
833 return Ok((*val).clone());
834 }
835 }
836 if let Some(ctx) = ctx {
838 if let Some(val) = ctx.lookup(name) {
839 return Ok(val.clone());
840 }
841 }
842 Ok(JValue::Undefined)
843 }
844
845 CompiledExpr::FieldPath(steps) => {
847 compiled_eval_field_path(steps, data, vars, ctx, shape)
848 }
849
850 CompiledExpr::BuiltinCall { name, args } => {
852 let mut evaled_args = Vec::with_capacity(args.len());
853 for arg in args.iter() {
854 evaled_args.push(eval_compiled_inner(arg, data, vars, ctx, shape)?);
855 }
856 call_pure_builtin(name, &evaled_args, data)
857 }
858
859 CompiledExpr::Block(exprs) => {
861 let mut result = JValue::Undefined;
862 for expr in exprs.iter() {
863 result = eval_compiled_inner(expr, data, vars, ctx, shape)?;
864 }
865 Ok(result)
866 }
867
868 CompiledExpr::Coalesce(lhs, rhs) => {
871 let left = eval_compiled_inner(lhs, data, vars, ctx, shape)?;
872 if left.is_undefined() {
873 eval_compiled_inner(rhs, data, vars, ctx, shape)
874 } else {
875 Ok(left)
876 }
877 }
878 }
879}
880
881#[inline]
883pub(crate) fn compiled_is_truthy(value: &JValue) -> bool {
884 match value {
885 JValue::Null | JValue::Undefined => false,
886 JValue::Bool(b) => *b,
887 JValue::Number(n) => *n != 0.0,
888 JValue::String(s) => !s.is_empty(),
889 JValue::Array(a) => !a.is_empty(),
890 JValue::Object(o) => !o.is_empty(),
891 _ => false,
892 }
893}
894
895#[inline]
898fn is_compiled_explicit_null(expr: &CompiledExpr) -> bool {
899 matches!(expr, CompiledExpr::ExplicitNull)
900}
901
902#[inline]
905pub(crate) fn compiled_ordered_cmp(
906 left: &JValue,
907 right: &JValue,
908 left_is_explicit_null: bool,
909 right_is_explicit_null: bool,
910 cmp_num: fn(f64, f64) -> bool,
911 cmp_str: fn(&str, &str) -> bool,
912) -> Result<JValue, EvaluatorError> {
913 match (left, right) {
914 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Bool(cmp_num(*a, *b))),
915 (JValue::String(a), JValue::String(b)) => Ok(JValue::Bool(cmp_str(a, b))),
916 (JValue::Null, JValue::Null) | (JValue::Undefined, JValue::Undefined) => Ok(JValue::Null),
918 (JValue::Undefined, JValue::Null) | (JValue::Null, JValue::Undefined) => Ok(JValue::Null),
919 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::EvaluationError(
921 "T2010: Type mismatch in comparison".to_string(),
922 )),
923 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::EvaluationError(
924 "T2010: Type mismatch in comparison".to_string(),
925 )),
926 (JValue::Bool(_), JValue::Null | JValue::Undefined)
928 | (JValue::Null | JValue::Undefined, JValue::Bool(_)) => {
929 Err(EvaluatorError::EvaluationError(
930 "T2010: Type mismatch in comparison".to_string(),
931 ))
932 }
933 (JValue::Number(_) | JValue::String(_), JValue::Null | JValue::Undefined)
935 | (JValue::Null | JValue::Undefined, JValue::Number(_) | JValue::String(_)) => {
936 Ok(JValue::Null)
937 }
938 (JValue::String(_), JValue::Number(_)) | (JValue::Number(_), JValue::String(_)) => {
940 Err(EvaluatorError::EvaluationError(
941 "T2009: The expressions on either side of operator must be of the same data type"
942 .to_string(),
943 ))
944 }
945 _ => Err(EvaluatorError::EvaluationError(
946 "T2010: Type mismatch in comparison".to_string(),
947 )),
948 }
949}
950
951#[inline]
954pub(crate) fn compiled_arithmetic(
955 op: CompiledArithOp,
956 left: &JValue,
957 right: &JValue,
958 left_is_explicit_null: bool,
959 right_is_explicit_null: bool,
960) -> Result<JValue, EvaluatorError> {
961 let op_sym = match op {
962 CompiledArithOp::Add => "+",
963 CompiledArithOp::Sub => "-",
964 CompiledArithOp::Mul => "*",
965 CompiledArithOp::Div => "/",
966 CompiledArithOp::Mod => "%",
967 };
968 match (left, right) {
969 (JValue::Number(a), JValue::Number(b)) => {
970 let result = match op {
971 CompiledArithOp::Add => *a + *b,
972 CompiledArithOp::Sub => *a - *b,
973 CompiledArithOp::Mul => {
974 let r = *a * *b;
975 if r.is_infinite() {
976 return Err(EvaluatorError::EvaluationError(
977 "D1001: Number out of range".to_string(),
978 ));
979 }
980 r
981 }
982 CompiledArithOp::Div => {
983 if *b == 0.0 {
984 return Err(EvaluatorError::EvaluationError(
985 "Division by zero".to_string(),
986 ));
987 }
988 *a / *b
989 }
990 CompiledArithOp::Mod => {
991 if *b == 0.0 {
992 return Err(EvaluatorError::EvaluationError(
993 "Division by zero".to_string(),
994 ));
995 }
996 *a % *b
997 }
998 };
999 Ok(JValue::Number(result))
1000 }
1001 (JValue::Null | JValue::Undefined, _) if left_is_explicit_null => {
1003 Err(EvaluatorError::TypeError(format!(
1004 "T2002: The left side of the {} operator must evaluate to a number",
1005 op_sym
1006 )))
1007 }
1008 (_, JValue::Null | JValue::Undefined) if right_is_explicit_null => {
1009 Err(EvaluatorError::TypeError(format!(
1010 "T2002: The right side of the {} operator must evaluate to a number",
1011 op_sym
1012 )))
1013 }
1014 (JValue::Null | JValue::Undefined, _) | (_, JValue::Null | JValue::Undefined) => {
1016 Ok(JValue::Null)
1017 }
1018 _ => Err(EvaluatorError::TypeError(format!(
1019 "Cannot apply {} to {:?} and {:?}",
1020 op_sym, left, right
1021 ))),
1022 }
1023}
1024
1025#[inline]
1027pub(crate) fn compiled_to_concat_string(value: &JValue) -> Result<String, EvaluatorError> {
1028 match value {
1029 JValue::String(s) => Ok(s.to_string()),
1030 JValue::Null | JValue::Undefined => Ok(String::new()),
1031 JValue::Number(_) | JValue::Bool(_) | JValue::Array(_) | JValue::Object(_) => {
1032 match crate::functions::string::string(value, None) {
1033 Ok(JValue::String(s)) => Ok(s.to_string()),
1034 Ok(JValue::Null) => Ok(String::new()),
1035 _ => Err(EvaluatorError::TypeError(
1036 "Cannot concatenate complex types".to_string(),
1037 )),
1038 }
1039 }
1040 _ => Ok(String::new()),
1041 }
1042}
1043
1044
1045fn try_compile_path(steps: &[crate::ast::PathStep], allowed_vars: Option<&[&str]>) -> Option<CompiledExpr> {
1054 use crate::ast::{AstNode, Stage};
1055
1056 if steps.is_empty() {
1057 return None;
1058 }
1059
1060 let field_steps: &[crate::ast::PathStep] = match &steps[0].node {
1065 AstNode::Variable(var) if var.is_empty() && steps[0].stages.is_empty() => &steps[1..],
1066 AstNode::Variable(_) => return None,
1067 AstNode::Name(_) => steps,
1068 _ => return None,
1069 };
1070
1071 let mut compiled_steps = Vec::with_capacity(field_steps.len());
1073 for step in field_steps {
1074 let field = match &step.node {
1075 AstNode::Name(name) => name.clone(),
1076 _ => return None,
1077 };
1078
1079 let filter = match step.stages.as_slice() {
1080 [] => None,
1081 [Stage::Filter(filter_node)] => {
1082 if matches!(**filter_node, AstNode::Number(_)) {
1085 return None;
1086 }
1087 Some(try_compile_expr_inner(filter_node, allowed_vars)?)
1088 }
1089 _ => return None,
1090 };
1091
1092 compiled_steps.push(CompiledStep { field, filter });
1093 }
1094
1095 if compiled_steps.is_empty() {
1096 return Some(CompiledExpr::VariableLookup(String::new()));
1098 }
1099
1100 if allowed_vars.is_some() {
1105 if compiled_steps.len() == 1 && compiled_steps[0].filter.is_none() {
1106 return Some(CompiledExpr::FieldLookup(compiled_steps.remove(0).field));
1107 }
1108 if compiled_steps.len() == 2
1109 && compiled_steps[0].filter.is_none()
1110 && compiled_steps[1].filter.is_none()
1111 {
1112 let outer = compiled_steps.remove(0).field;
1113 let inner = compiled_steps.remove(0).field;
1114 return Some(CompiledExpr::NestedFieldLookup(outer, inner));
1115 }
1116 }
1117
1118 Some(CompiledExpr::FieldPath(compiled_steps))
1119}
1120
1121fn compiled_eval_field_path(
1130 steps: &[CompiledStep],
1131 data: &JValue,
1132 vars: Option<&HashMap<&str, &JValue>>,
1133 ctx: Option<&Context>,
1134 shape: Option<&ShapeCache>,
1135) -> Result<JValue, EvaluatorError> {
1136 let mut current = data.clone();
1137 let mut did_array_mapping = false;
1140 for step in steps {
1141 let is_array = matches!(current, JValue::Array(_));
1143 current = compiled_field_step(&step.field, ¤t);
1145 if is_array {
1146 did_array_mapping = true;
1147 } else {
1148 did_array_mapping = false;
1150 }
1151 if let Some(filter) = &step.filter {
1153 current = compiled_apply_filter(filter, ¤t, vars, ctx, shape)?;
1154 did_array_mapping = true;
1156 }
1157 }
1158 if did_array_mapping {
1160 Ok(match current {
1161 JValue::Array(ref arr) if arr.len() == 1 => arr[0].clone(),
1162 other => other,
1163 })
1164 } else {
1165 Ok(current)
1166 }
1167}
1168
1169fn compiled_field_step(field: &str, value: &JValue) -> JValue {
1176 match value {
1177 JValue::Object(obj) => {
1178 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
1180 if let Some(JValue::Object(inner)) = obj.get("@") {
1181 return inner.get(field).cloned().unwrap_or(JValue::Undefined);
1182 }
1183 return JValue::Undefined;
1184 }
1185 obj.get(field).cloned().unwrap_or(JValue::Undefined)
1186 }
1187 JValue::Array(arr) => {
1188 let mut result = Vec::new();
1189 for item in arr.iter() {
1190 let extracted = compiled_field_step(field, item);
1191 match extracted {
1192 JValue::Undefined => {}
1193 JValue::Array(inner) => result.extend(inner.iter().cloned()),
1194 other => result.push(other),
1195 }
1196 }
1197 if result.is_empty() {
1198 JValue::Undefined
1199 } else {
1200 JValue::array(result)
1201 }
1202 }
1203 _ => JValue::Undefined,
1204 }
1205}
1206
1207fn compiled_apply_filter(
1213 filter: &CompiledExpr,
1214 value: &JValue,
1215 vars: Option<&HashMap<&str, &JValue>>,
1216 ctx: Option<&Context>,
1217 shape: Option<&ShapeCache>,
1218) -> Result<JValue, EvaluatorError> {
1219 match value {
1220 JValue::Array(arr) => {
1221 let mut result = Vec::new();
1222 for item in arr.iter() {
1223 let pred = eval_compiled_inner(filter, item, vars, ctx, shape)?;
1224 if compiled_is_truthy(&pred) {
1225 result.push(item.clone());
1226 }
1227 }
1228 if result.is_empty() {
1229 Ok(JValue::Undefined)
1230 } else if result.len() == 1 {
1231 Ok(result.remove(0))
1232 } else {
1233 Ok(JValue::array(result))
1234 }
1235 }
1236 JValue::Undefined => Ok(JValue::Undefined),
1237 _ => {
1238 let pred = eval_compiled_inner(filter, value, vars, ctx, shape)?;
1239 if compiled_is_truthy(&pred) {
1240 Ok(value.clone())
1241 } else {
1242 Ok(JValue::Undefined)
1243 }
1244 }
1245 }
1246}
1247
1248fn call_pure_builtin(
1254 name: &str,
1255 args: &[JValue],
1256 data: &JValue,
1257) -> Result<JValue, EvaluatorError> {
1258 use crate::functions;
1259
1260 let args_storage: Vec<JValue>;
1262 let effective_args: &[JValue] = if args.is_empty() {
1263 match name {
1264 "string" => {
1265 if data.is_undefined() || data.is_null() {
1268 return Ok(JValue::Undefined);
1269 }
1270 args_storage = vec![data.clone()];
1271 &args_storage
1272 }
1273 "number" | "boolean" | "uppercase" | "lowercase" => {
1274 args_storage = vec![data.clone()];
1275 &args_storage
1276 }
1277 _ => args,
1278 }
1279 } else if args.len() == 1 {
1280 match name {
1281 "substringBefore" | "substringAfter" | "contains" | "split" => {
1282 if matches!(data, JValue::String(_)) {
1283 args_storage = std::iter::once(data.clone())
1284 .chain(args.iter().cloned())
1285 .collect();
1286 &args_storage
1287 } else {
1288 args
1289 }
1290 }
1291 _ => args,
1292 }
1293 } else {
1294 args
1295 };
1296
1297 if effective_args.first().is_some_and(JValue::is_undefined)
1301 && propagates_undefined(name)
1302 {
1303 return Ok(JValue::Undefined);
1304 }
1305
1306 match name {
1307 "string" => {
1309 let prettify = match effective_args.get(1) {
1311 None => None,
1312 Some(JValue::Bool(b)) => Some(*b),
1313 Some(_) => {
1314 return Err(EvaluatorError::TypeError(
1315 "string() prettify parameter must be a boolean".to_string(),
1316 ))
1317 }
1318 };
1319 let arg = effective_args.first().unwrap_or(&JValue::Null);
1320 Ok(functions::string::string(arg, prettify)?)
1321 }
1322 "length" => match effective_args.first() {
1323 Some(JValue::String(s)) => Ok(functions::string::length(s)?),
1324 Some(JValue::Undefined) => Ok(JValue::Undefined),
1326 None => Err(EvaluatorError::EvaluationError(
1329 "length() requires exactly 1 argument".to_string(),
1330 )),
1331 _ => Err(EvaluatorError::TypeError(
1333 "T0410: Argument 1 of function length does not match function signature"
1334 .to_string(),
1335 )),
1336 },
1337 "uppercase" => match effective_args.first() {
1338 Some(JValue::String(s)) => Ok(functions::string::uppercase(s)?),
1339 Some(JValue::Undefined) | None => Ok(JValue::Undefined),
1340 _ => Err(EvaluatorError::TypeError(
1341 "T0410: Argument 1 of function uppercase does not match function signature"
1342 .to_string(),
1343 )),
1344 },
1345 "lowercase" => match effective_args.first() {
1346 Some(JValue::String(s)) => Ok(functions::string::lowercase(s)?),
1347 Some(JValue::Undefined) | None => Ok(JValue::Undefined),
1348 _ => Err(EvaluatorError::TypeError(
1349 "T0410: Argument 1 of function lowercase does not match function signature"
1350 .to_string(),
1351 )),
1352 },
1353 "trim" => match effective_args.first() {
1354 None | Some(JValue::Null | JValue::Undefined) => Ok(JValue::Null),
1355 Some(JValue::String(s)) => Ok(functions::string::trim(s)?),
1356 _ => Err(EvaluatorError::TypeError(
1357 "trim() requires a string argument".to_string(),
1358 )),
1359 },
1360 "substring" => {
1361 if effective_args.len() < 2 {
1362 return Err(EvaluatorError::EvaluationError(
1363 "substring() requires at least 2 arguments".to_string(),
1364 ));
1365 }
1366 match (&effective_args[0], &effective_args[1]) {
1367 (JValue::String(s), JValue::Number(start)) => {
1368 let length = match effective_args.get(2) {
1370 None => None,
1371 Some(JValue::Number(l)) => Some(*l as i64),
1372 Some(_) => {
1373 return Err(EvaluatorError::TypeError(
1374 "T0410: Argument 3 of function substring does not match function signature"
1375 .to_string(),
1376 ))
1377 }
1378 };
1379 Ok(functions::string::substring(s, *start as i64, length)?)
1380 }
1381 _ => Err(EvaluatorError::TypeError(
1382 "T0410: Argument 1 of function substring does not match function signature"
1383 .to_string(),
1384 )),
1385 }
1386 }
1387 "substringBefore" => {
1388 if effective_args.len() != 2 {
1389 return Err(EvaluatorError::TypeError(
1390 "T0411: Context value is not a compatible type with argument 2 of function substringBefore".to_string(),
1391 ));
1392 }
1393 match (&effective_args[0], &effective_args[1]) {
1394 (JValue::String(s), JValue::String(sep)) => {
1395 Ok(functions::string::substring_before(s, sep)?)
1396 }
1397 (JValue::Undefined, _) => Ok(JValue::Undefined),
1399 _ => Err(EvaluatorError::TypeError(
1400 "T0410: Argument 1 of function substringBefore does not match function signature".to_string(),
1401 )),
1402 }
1403 }
1404 "substringAfter" => {
1405 if effective_args.len() != 2 {
1406 return Err(EvaluatorError::TypeError(
1407 "T0411: Context value is not a compatible type with argument 2 of function substringAfter".to_string(),
1408 ));
1409 }
1410 match (&effective_args[0], &effective_args[1]) {
1411 (JValue::String(s), JValue::String(sep)) => {
1412 Ok(functions::string::substring_after(s, sep)?)
1413 }
1414 (JValue::Undefined, _) => Ok(JValue::Undefined),
1416 _ => Err(EvaluatorError::TypeError(
1417 "T0410: Argument 1 of function substringAfter does not match function signature".to_string(),
1418 )),
1419 }
1420 }
1421 "contains" => {
1422 if effective_args.len() != 2 {
1423 return Err(EvaluatorError::EvaluationError(
1424 "contains() requires exactly 2 arguments".to_string(),
1425 ));
1426 }
1427 match &effective_args[0] {
1428 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1429 JValue::String(s) => Ok(functions::string::contains(s, &effective_args[1])?),
1430 _ => Err(EvaluatorError::TypeError(
1431 "contains() requires a string as the first argument".to_string(),
1432 )),
1433 }
1434 }
1435 "split" => {
1436 if effective_args.len() < 2 {
1437 return Err(EvaluatorError::EvaluationError(
1438 "split() requires at least 2 arguments".to_string(),
1439 ));
1440 }
1441 match &effective_args[0] {
1442 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1443 JValue::String(s) => {
1444 let limit = match effective_args.get(2) {
1446 None => None,
1447 Some(JValue::Number(n)) => {
1448 if *n < 0.0 {
1449 return Err(EvaluatorError::EvaluationError(
1450 "D3020: Third argument of split function must be a positive number"
1451 .to_string(),
1452 ));
1453 }
1454 Some(n.floor() as usize)
1455 }
1456 Some(_) => {
1457 return Err(EvaluatorError::TypeError(
1458 "split() limit must be a number".to_string(),
1459 ))
1460 }
1461 };
1462 Ok(functions::string::split(s, &effective_args[1], limit)?)
1463 }
1464 _ => Err(EvaluatorError::TypeError(
1465 "split() requires a string as the first argument".to_string(),
1466 )),
1467 }
1468 }
1469 "join" => {
1470 if effective_args.is_empty() {
1471 return Err(EvaluatorError::TypeError(
1472 "T0410: Argument 1 of function $join does not match function signature"
1473 .to_string(),
1474 ));
1475 }
1476 match &effective_args[0] {
1477 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1478 JValue::Bool(_) | JValue::Number(_) | JValue::Object(_) => {
1480 Err(EvaluatorError::TypeError(
1481 "T0412: Argument 1 of function $join must be an array of String"
1482 .to_string(),
1483 ))
1484 }
1485 JValue::Array(arr) => {
1486 for item in arr.iter() {
1488 if !matches!(item, JValue::String(_)) {
1489 return Err(EvaluatorError::TypeError(
1490 "T0412: Argument 1 of function $join must be an array of String"
1491 .to_string(),
1492 ));
1493 }
1494 }
1495 let separator = match effective_args.get(1) {
1497 None | Some(JValue::Undefined) => None,
1498 Some(JValue::String(s)) => Some(&**s),
1499 Some(_) => {
1500 return Err(EvaluatorError::TypeError(
1501 "T0410: Argument 2 of function $join does not match function signature (expected String)"
1502 .to_string(),
1503 ))
1504 }
1505 };
1506 Ok(functions::string::join(arr, separator)?)
1507 }
1508 JValue::String(s) => Ok(JValue::String(s.clone())),
1509 _ => Err(EvaluatorError::TypeError(
1510 "T0412: Argument 1 of function $join must be an array of String"
1511 .to_string(),
1512 )),
1513 }
1514 }
1515
1516 "number" => match effective_args.first() {
1518 Some(v) => Ok(functions::numeric::number(v)?),
1519 None => Err(EvaluatorError::EvaluationError(
1520 "number() requires at least 1 argument".to_string(),
1521 )),
1522 },
1523 "floor" => match effective_args.first() {
1524 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1525 Some(JValue::Number(n)) => Ok(functions::numeric::floor(*n)?),
1526 _ => Err(EvaluatorError::TypeError(
1527 "floor() requires a number argument".to_string(),
1528 )),
1529 },
1530 "ceil" => match effective_args.first() {
1531 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1532 Some(JValue::Number(n)) => Ok(functions::numeric::ceil(*n)?),
1533 _ => Err(EvaluatorError::TypeError(
1534 "ceil() requires a number argument".to_string(),
1535 )),
1536 },
1537 "round" => match effective_args.first() {
1538 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1539 Some(JValue::Number(n)) => {
1540 let precision = effective_args.get(1).and_then(|v| {
1541 if let JValue::Number(p) = v { Some(*p as i32) } else { None }
1542 });
1543 Ok(functions::numeric::round(*n, precision)?)
1544 }
1545 _ => Err(EvaluatorError::TypeError(
1546 "round() requires a number argument".to_string(),
1547 )),
1548 },
1549 "abs" => match effective_args.first() {
1550 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1551 Some(JValue::Number(n)) => Ok(functions::numeric::abs(*n)?),
1552 _ => Err(EvaluatorError::TypeError(
1553 "abs() requires a number argument".to_string(),
1554 )),
1555 },
1556 "sqrt" => match effective_args.first() {
1557 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1558 Some(JValue::Number(n)) => Ok(functions::numeric::sqrt(*n)?),
1559 _ => Err(EvaluatorError::TypeError(
1560 "sqrt() requires a number argument".to_string(),
1561 )),
1562 },
1563
1564 "sum" => match effective_args.first() {
1566 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1567 None => Err(EvaluatorError::EvaluationError(
1568 "sum() requires exactly 1 argument".to_string(),
1569 )),
1570 Some(JValue::Null) => Ok(JValue::Null),
1571 Some(JValue::Array(arr)) => Ok(aggregation::sum(arr)?),
1572 Some(JValue::Number(n)) => Ok(JValue::Number(*n)),
1573 Some(other) => Ok(functions::numeric::sum(&[other.clone()])?),
1574 },
1575 "max" => match effective_args.first() {
1576 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1577 Some(JValue::Null) | None => Ok(JValue::Null),
1578 Some(JValue::Array(arr)) => Ok(aggregation::max(arr)?),
1579 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1580 _ => Err(EvaluatorError::TypeError(
1581 "max() requires an array or number argument".to_string(),
1582 )),
1583 },
1584 "min" => match effective_args.first() {
1585 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1586 Some(JValue::Null) | None => Ok(JValue::Null),
1587 Some(JValue::Array(arr)) => Ok(aggregation::min(arr)?),
1588 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1589 _ => Err(EvaluatorError::TypeError(
1590 "min() requires an array or number argument".to_string(),
1591 )),
1592 },
1593 "average" => match effective_args.first() {
1594 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1595 Some(JValue::Null) | None => Ok(JValue::Null),
1596 Some(JValue::Array(arr)) => Ok(aggregation::average(arr)?),
1597 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1598 _ => Err(EvaluatorError::TypeError(
1599 "average() requires an array or number argument".to_string(),
1600 )),
1601 },
1602 "count" => match effective_args.first() {
1603 Some(v) if v.is_undefined() => Ok(JValue::from(0i64)),
1604 Some(JValue::Null) | None => Ok(JValue::from(0i64)),
1605 Some(JValue::Array(arr)) => Ok(functions::array::count(arr)?),
1606 _ => Ok(JValue::from(1i64)),
1607 },
1608
1609 "boolean" => match effective_args.first() {
1611 Some(v) => Ok(functions::boolean::boolean(v)?),
1612 None => Err(EvaluatorError::EvaluationError(
1613 "boolean() requires 1 argument".to_string(),
1614 )),
1615 },
1616 "not" => match effective_args.first() {
1617 Some(v) => Ok(JValue::Bool(!compiled_is_truthy(v))),
1618 None => Err(EvaluatorError::EvaluationError(
1619 "not() requires 1 argument".to_string(),
1620 )),
1621 },
1622
1623 "append" => {
1625 if effective_args.len() != 2 {
1626 return Err(EvaluatorError::EvaluationError(
1627 "append() requires exactly 2 arguments".to_string(),
1628 ));
1629 }
1630 let first = &effective_args[0];
1631 let second = &effective_args[1];
1632 if matches!(second, JValue::Null | JValue::Undefined) {
1633 return Ok(first.clone());
1634 }
1635 if matches!(first, JValue::Null | JValue::Undefined) {
1636 return Ok(second.clone());
1637 }
1638 let arr = match first {
1639 JValue::Array(a) => a.to_vec(),
1640 other => vec![other.clone()],
1641 };
1642 Ok(functions::array::append(&arr, second)?)
1643 }
1644 "reverse" => match effective_args.first() {
1645 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1646 Some(JValue::Array(arr)) => Ok(functions::array::reverse(arr)?),
1647 _ => Err(EvaluatorError::TypeError(
1648 "reverse() requires an array argument".to_string(),
1649 )),
1650 },
1651 "distinct" => match effective_args.first() {
1652 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1653 Some(JValue::Array(arr)) => Ok(functions::array::distinct(arr)?),
1654 _ => Err(EvaluatorError::TypeError(
1655 "distinct() requires an array argument".to_string(),
1656 )),
1657 },
1658
1659 "keys" => match effective_args.first() {
1661 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1662 Some(JValue::Lambda { .. } | JValue::Builtin { .. }) => Ok(JValue::Null),
1663 Some(JValue::Object(obj)) => {
1664 if obj.is_empty() {
1665 Ok(JValue::Null)
1666 } else {
1667 let keys: Vec<JValue> =
1668 obj.keys().map(|k| JValue::string(k.clone())).collect();
1669 if keys.len() == 1 {
1670 Ok(keys.into_iter().next().unwrap())
1671 } else {
1672 Ok(JValue::array(keys))
1673 }
1674 }
1675 }
1676 Some(JValue::Array(arr)) => {
1677 let mut all_keys: Vec<JValue> = Vec::new();
1678 for item in arr.iter() {
1679 if let JValue::Object(obj) = item {
1680 for key in obj.keys() {
1681 let k = JValue::string(key.clone());
1682 if !all_keys.contains(&k) {
1683 all_keys.push(k);
1684 }
1685 }
1686 }
1687 }
1688 if all_keys.is_empty() {
1689 Ok(JValue::Null)
1690 } else if all_keys.len() == 1 {
1691 Ok(all_keys.into_iter().next().unwrap())
1692 } else {
1693 Ok(JValue::array(all_keys))
1694 }
1695 }
1696 _ => Ok(JValue::Null),
1697 },
1698 "merge" => match effective_args.len() {
1699 0 => Err(EvaluatorError::EvaluationError(
1700 "merge() requires at least 1 argument".to_string(),
1701 )),
1702 1 => match &effective_args[0] {
1703 JValue::Array(arr) => Ok(functions::object::merge(arr)?),
1704 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1705 JValue::Object(_) => Ok(effective_args[0].clone()),
1706 _ => Err(EvaluatorError::TypeError(
1707 "merge() requires objects or an array of objects".to_string(),
1708 )),
1709 },
1710 _ => Ok(functions::object::merge(effective_args)?),
1711 },
1712
1713 _ => unreachable!("call_pure_builtin called with non-compilable builtin: {}", name),
1714 }
1715}
1716
1717const UNDEFINED_PROPAGATING_FUNCTIONS: &[&str] = &[
1725 "not",
1726 "boolean",
1727 "length",
1728 "number",
1729 "uppercase",
1730 "lowercase",
1731 "substring",
1732 "substringBefore",
1733 "substringAfter",
1734 "string",
1735];
1736
1737fn propagates_undefined(name: &str) -> bool {
1739 UNDEFINED_PROPAGATING_FUNCTIONS.contains(&name)
1740}
1741
1742mod aggregation {
1745 use super::*;
1746
1747 fn for_each_numeric(
1750 arr: &[JValue],
1751 func_name: &str,
1752 mut f: impl FnMut(f64),
1753 ) -> Result<(), EvaluatorError> {
1754 fn recurse(
1755 arr: &[JValue],
1756 func_name: &str,
1757 f: &mut dyn FnMut(f64),
1758 ) -> Result<(), EvaluatorError> {
1759 for value in arr.iter() {
1760 match value {
1761 JValue::Array(inner) => recurse(inner, func_name, f)?,
1762 JValue::Number(n) => {
1763 f(*n);
1764 }
1765 _ => {
1766 return Err(EvaluatorError::TypeError(format!(
1767 "{}() requires all array elements to be numbers",
1768 func_name
1769 )));
1770 }
1771 }
1772 }
1773 Ok(())
1774 }
1775 recurse(arr, func_name, &mut f)
1776 }
1777
1778 fn count_numeric(arr: &[JValue], func_name: &str) -> Result<usize, EvaluatorError> {
1780 let mut count = 0usize;
1781 for_each_numeric(arr, func_name, |_| count += 1)?;
1782 Ok(count)
1783 }
1784
1785 pub fn sum(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1786 if arr.is_empty() {
1787 return Ok(JValue::from(0i64));
1788 }
1789 let mut total = 0.0f64;
1790 for_each_numeric(arr, "sum", |n| total += n)?;
1791 Ok(JValue::Number(total))
1792 }
1793
1794 pub fn max(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1795 if arr.is_empty() {
1796 return Ok(JValue::Null);
1797 }
1798 let mut max_val = f64::NEG_INFINITY;
1799 for_each_numeric(arr, "max", |n| {
1800 if n > max_val {
1801 max_val = n;
1802 }
1803 })?;
1804 Ok(JValue::Number(max_val))
1805 }
1806
1807 pub fn min(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1808 if arr.is_empty() {
1809 return Ok(JValue::Null);
1810 }
1811 let mut min_val = f64::INFINITY;
1812 for_each_numeric(arr, "min", |n| {
1813 if n < min_val {
1814 min_val = n;
1815 }
1816 })?;
1817 Ok(JValue::Number(min_val))
1818 }
1819
1820 pub fn average(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1821 if arr.is_empty() {
1822 return Ok(JValue::Null);
1823 }
1824 let mut total = 0.0f64;
1825 let count = count_numeric(arr, "average")?;
1826 for_each_numeric(arr, "average", |n| total += n)?;
1827 Ok(JValue::Number(total / count as f64))
1828 }
1829}
1830
1831#[derive(Error, Debug)]
1833pub enum EvaluatorError {
1834 #[error("Type error: {0}")]
1835 TypeError(String),
1836
1837 #[error("Reference error: {0}")]
1838 ReferenceError(String),
1839
1840 #[error("Evaluation error: {0}")]
1841 EvaluationError(String),
1842}
1843
1844impl From<crate::functions::FunctionError> for EvaluatorError {
1845 fn from(e: crate::functions::FunctionError) -> Self {
1846 EvaluatorError::EvaluationError(e.to_string())
1847 }
1848}
1849
1850impl From<crate::datetime::DateTimeError> for EvaluatorError {
1851 fn from(e: crate::datetime::DateTimeError) -> Self {
1852 EvaluatorError::EvaluationError(e.to_string())
1853 }
1854}
1855
1856enum LambdaResult {
1859 JValue(JValue),
1861 TailCall {
1863 lambda: Box<StoredLambda>,
1865 args: Vec<JValue>,
1867 data: JValue,
1869 },
1870}
1871
1872#[derive(Clone, Debug)]
1876pub struct StoredLambda {
1877 pub params: Vec<String>,
1878 pub body: AstNode,
1879 pub(crate) compiled_body: Option<CompiledExpr>,
1882 pub signature: Option<String>,
1883 pub captured_env: HashMap<String, JValue>,
1885 pub captured_data: Option<JValue>,
1887 pub thunk: bool,
1889}
1890
1891struct Scope {
1893 bindings: HashMap<String, JValue>,
1894 lambdas: HashMap<String, StoredLambda>,
1895}
1896
1897impl Scope {
1898 fn new() -> Self {
1899 Scope {
1900 bindings: HashMap::new(),
1901 lambdas: HashMap::new(),
1902 }
1903 }
1904}
1905
1906pub struct Context {
1911 scope_stack: Vec<Scope>,
1912 parent_data: Option<JValue>,
1913}
1914
1915impl Context {
1916 pub fn new() -> Self {
1917 Context {
1918 scope_stack: vec![Scope::new()],
1919 parent_data: None,
1920 }
1921 }
1922
1923 fn push_scope(&mut self) {
1925 self.scope_stack.push(Scope::new());
1926 }
1927
1928 fn pop_scope(&mut self) {
1930 if self.scope_stack.len() > 1 {
1931 self.scope_stack.pop();
1932 }
1933 }
1934
1935 fn pop_scope_preserving_lambdas(&mut self, lambda_ids: &[String]) {
1937 if self.scope_stack.len() > 1 {
1938 let popped = self.scope_stack.pop().unwrap();
1939 if !lambda_ids.is_empty() {
1940 let top = self.scope_stack.last_mut().unwrap();
1941 for id in lambda_ids {
1942 if let Some(stored) = popped.lambdas.get(id) {
1943 top.lambdas.insert(id.clone(), stored.clone());
1944 }
1945 }
1946 }
1947 }
1948 }
1949
1950 fn clear_current_scope(&mut self) {
1952 let top = self.scope_stack.last_mut().unwrap();
1953 top.bindings.clear();
1954 top.lambdas.clear();
1955 }
1956
1957 pub fn bind(&mut self, name: String, value: JValue) {
1958 self.scope_stack
1959 .last_mut()
1960 .unwrap()
1961 .bindings
1962 .insert(name, value);
1963 }
1964
1965 pub fn bind_lambda(&mut self, name: String, lambda: StoredLambda) {
1966 self.scope_stack
1967 .last_mut()
1968 .unwrap()
1969 .lambdas
1970 .insert(name, lambda);
1971 }
1972
1973 pub fn unbind(&mut self, name: &str) {
1974 let top = self.scope_stack.last_mut().unwrap();
1976 top.bindings.remove(name);
1977 top.lambdas.remove(name);
1978 }
1979
1980 pub fn lookup(&self, name: &str) -> Option<&JValue> {
1981 for scope in self.scope_stack.iter().rev() {
1983 if let Some(value) = scope.bindings.get(name) {
1984 return Some(value);
1985 }
1986 }
1987 None
1988 }
1989
1990 pub fn lookup_lambda(&self, name: &str) -> Option<&StoredLambda> {
1991 for scope in self.scope_stack.iter().rev() {
1993 if let Some(lambda) = scope.lambdas.get(name) {
1994 return Some(lambda);
1995 }
1996 }
1997 None
1998 }
1999
2000 pub fn set_parent(&mut self, data: JValue) {
2001 self.parent_data = Some(data);
2002 }
2003
2004 pub fn get_parent(&self) -> Option<&JValue> {
2005 self.parent_data.as_ref()
2006 }
2007
2008 fn all_bindings(&self) -> HashMap<String, JValue> {
2011 let mut result = HashMap::new();
2012 for scope in &self.scope_stack {
2013 for (k, v) in &scope.bindings {
2014 result.insert(k.clone(), v.clone());
2015 }
2016 }
2017 result
2018 }
2019}
2020
2021impl Default for Context {
2022 fn default() -> Self {
2023 Self::new()
2024 }
2025}
2026
2027pub struct Evaluator {
2029 context: Context,
2030 recursion_depth: usize,
2031 max_recursion_depth: usize,
2032}
2033
2034impl Evaluator {
2035 pub fn new() -> Self {
2036 Evaluator {
2037 context: Context::new(),
2038 recursion_depth: 0,
2039 max_recursion_depth: 302,
2042 }
2043 }
2044
2045 pub fn with_context(context: Context) -> Self {
2046 Evaluator {
2047 context,
2048 recursion_depth: 0,
2049 max_recursion_depth: 302,
2050 }
2051 }
2052
2053 fn invoke_stored_lambda(
2057 &mut self,
2058 stored: &StoredLambda,
2059 args: &[JValue],
2060 data: &JValue,
2061 ) -> Result<JValue, EvaluatorError> {
2062 if let Some(ref ce) = stored.compiled_body {
2066 if stored.signature.is_none()
2067 && !stored.thunk
2068 && !stored
2069 .captured_env
2070 .values()
2071 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
2072 {
2073 let call_data = stored.captured_data.as_ref().unwrap_or(data);
2074 let vars: HashMap<&str, &JValue> = stored
2075 .params
2076 .iter()
2077 .zip(args.iter())
2078 .map(|(p, v)| (p.as_str(), v))
2079 .chain(stored.captured_env.iter().map(|(k, v)| (k.as_str(), v)))
2080 .collect();
2081 return eval_compiled(ce, call_data, Some(&vars));
2082 }
2083 }
2084
2085 let captured_env = if stored.captured_env.is_empty() {
2086 None
2087 } else {
2088 Some(&stored.captured_env)
2089 };
2090 let captured_data = stored.captured_data.as_ref();
2091 self.invoke_lambda_with_env(
2092 &stored.params,
2093 &stored.body,
2094 stored.signature.as_ref(),
2095 args,
2096 data,
2097 captured_env,
2098 captured_data,
2099 stored.thunk,
2100 )
2101 }
2102
2103 fn lookup_lambda_from_value(&self, value: &JValue) -> Option<StoredLambda> {
2107 if let JValue::Lambda { lambda_id, .. } = value {
2108 return self.context.lookup_lambda(lambda_id).cloned();
2109 }
2110 None
2111 }
2112
2113 fn get_callback_param_count(&self, func_node: &AstNode) -> usize {
2117 match func_node {
2118 AstNode::Lambda { params, .. } => params.len(),
2119 AstNode::Variable(var_name) => {
2120 if let Some(stored_lambda) = self.context.lookup_lambda(var_name) {
2122 return stored_lambda.params.len();
2123 }
2124 if let Some(value) = self.context.lookup(var_name) {
2126 if let Some(stored_lambda) = self.lookup_lambda_from_value(value) {
2127 return stored_lambda.params.len();
2128 }
2129 }
2130 usize::MAX
2132 }
2133 AstNode::Function { .. } => {
2134 usize::MAX
2137 }
2138 _ => usize::MAX,
2139 }
2140 }
2141
2142 fn merge_sort_specialized(arr: &mut [JValue], spec: &SpecializedSortComparator) {
2146 if arr.len() <= 1 {
2147 return;
2148 }
2149
2150 let keys: Vec<SortKey> = arr
2152 .iter()
2153 .map(|item| match item {
2154 JValue::Object(obj) => match obj.get(&spec.field) {
2155 Some(JValue::Number(n)) => SortKey::Num(*n),
2156 Some(JValue::String(s)) => SortKey::Str(s.clone()),
2157 _ => SortKey::None,
2158 },
2159 _ => SortKey::None,
2160 })
2161 .collect();
2162
2163 let mut perm: Vec<usize> = (0..arr.len()).collect();
2165 perm.sort_by(|&a, &b| compare_sort_keys(&keys[a], &keys[b], spec.descending));
2166
2167 let mut placed = vec![false; arr.len()];
2169 for i in 0..arr.len() {
2170 if placed[i] || perm[i] == i {
2171 continue;
2172 }
2173 let mut j = i;
2174 loop {
2175 let target = perm[j];
2176 placed[j] = true;
2177 if target == i {
2178 break;
2179 }
2180 arr.swap(j, target);
2181 j = target;
2182 }
2183 }
2184 }
2185
2186 fn merge_sort_with_comparator(
2190 &mut self,
2191 arr: &mut [JValue],
2192 comparator: &AstNode,
2193 data: &JValue,
2194 ) -> Result<(), EvaluatorError> {
2195 if arr.len() <= 1 {
2196 return Ok(());
2197 }
2198
2199 if let AstNode::Lambda { params, body, .. } = comparator {
2202 if params.len() >= 2 {
2203 if let Some(spec) =
2204 try_specialize_sort_comparator(body, ¶ms[0], ¶ms[1])
2205 {
2206 Self::merge_sort_specialized(arr, &spec);
2207 return Ok(());
2208 }
2209 }
2210 }
2211
2212 let mid = arr.len() / 2;
2213
2214 self.merge_sort_with_comparator(&mut arr[..mid], comparator, data)?;
2216
2217 self.merge_sort_with_comparator(&mut arr[mid..], comparator, data)?;
2219
2220 let mut temp = Vec::with_capacity(arr.len());
2222 let (left, right) = arr.split_at(mid);
2223
2224 let mut i = 0;
2225 let mut j = 0;
2226
2227 if let AstNode::Lambda { params, body, .. } = comparator {
2230 if params.len() >= 2 {
2231 let param0 = params[0].clone();
2233 let param1 = params[1].clone();
2234 self.context.push_scope();
2235 while i < left.len() && j < right.len() {
2236 self.context.clear_current_scope();
2238 self.context.bind(param0.clone(), left[i].clone());
2239 self.context.bind(param1.clone(), right[j].clone());
2240
2241 let cmp_result = self.evaluate_internal(body, data)?;
2242
2243 if self.is_truthy(&cmp_result) {
2244 temp.push(right[j].clone());
2245 j += 1;
2246 } else {
2247 temp.push(left[i].clone());
2248 i += 1;
2249 }
2250 }
2251 self.context.pop_scope();
2252 } else {
2253 while i < left.len() && j < right.len() {
2255 let cmp_result = self.apply_function(
2256 comparator,
2257 &[left[i].clone(), right[j].clone()],
2258 data,
2259 )?;
2260 if self.is_truthy(&cmp_result) {
2261 temp.push(right[j].clone());
2262 j += 1;
2263 } else {
2264 temp.push(left[i].clone());
2265 i += 1;
2266 }
2267 }
2268 }
2269 } else {
2270 while i < left.len() && j < right.len() {
2272 let cmp_result = self.apply_function(
2273 comparator,
2274 &[left[i].clone(), right[j].clone()],
2275 data,
2276 )?;
2277 if self.is_truthy(&cmp_result) {
2278 temp.push(right[j].clone());
2279 j += 1;
2280 } else {
2281 temp.push(left[i].clone());
2282 i += 1;
2283 }
2284 }
2285 }
2286
2287 temp.extend_from_slice(&left[i..]);
2289 temp.extend_from_slice(&right[j..]);
2290
2291 for (i, val) in temp.into_iter().enumerate() {
2293 arr[i] = val;
2294 }
2295
2296 Ok(())
2297 }
2298
2299 pub fn evaluate(&mut self, node: &AstNode, data: &JValue) -> Result<JValue, EvaluatorError> {
2304 if self.context.get_parent().is_none() {
2306 self.context.set_parent(data.clone());
2307 }
2308
2309 self.evaluate_internal(node, data)
2310 }
2311
2312 #[inline(always)]
2316 fn evaluate_leaf(&mut self, node: &AstNode, data: &JValue) -> Option<Result<JValue, EvaluatorError>> {
2317 match node {
2318 AstNode::String(s) => Some(Ok(JValue::string(s.clone()))),
2319 AstNode::Number(n) => {
2320 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2321 Some(Ok(JValue::from(*n as i64)))
2322 } else {
2323 Some(Ok(JValue::Number(*n)))
2324 }
2325 }
2326 AstNode::Boolean(b) => Some(Ok(JValue::Bool(*b))),
2327 AstNode::Null => Some(Ok(JValue::Null)),
2328 AstNode::Undefined => Some(Ok(JValue::Undefined)),
2329 AstNode::Name(field_name) => match data {
2330 JValue::Object(obj) => Some(Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null))),
2332 _ => None,
2333 },
2334 AstNode::Variable(name) if !name.is_empty() => {
2335 if let JValue::Object(obj) = data {
2337 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2338 return None; }
2340 }
2341 self.context.lookup(name).map(|value| Ok(value.clone()))
2343 }
2344 _ => None,
2345 }
2346 }
2347
2348 fn evaluate_internal(
2350 &mut self,
2351 node: &AstNode,
2352 data: &JValue,
2353 ) -> Result<JValue, EvaluatorError> {
2354 if let Some(result) = self.evaluate_leaf(node, data) {
2356 return result;
2357 }
2358
2359 self.recursion_depth += 1;
2361 if self.recursion_depth > self.max_recursion_depth {
2362 self.recursion_depth -= 1;
2363 return Err(EvaluatorError::EvaluationError(format!(
2364 "U1001: Stack overflow - maximum recursion depth ({}) exceeded",
2365 self.max_recursion_depth
2366 )));
2367 }
2368
2369 let result = self.evaluate_internal_impl(node, data);
2370
2371 self.recursion_depth -= 1;
2372 result
2373 }
2374
2375 fn evaluate_internal_impl(
2377 &mut self,
2378 node: &AstNode,
2379 data: &JValue,
2380 ) -> Result<JValue, EvaluatorError> {
2381 match node {
2382 AstNode::String(s) => Ok(JValue::string(s.clone())),
2383
2384 AstNode::Name(field_name) => {
2386 match data {
2387 JValue::Object(obj) => Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null)),
2388 JValue::Array(arr) => {
2389 let mut result = Vec::new();
2391 for item in arr.iter() {
2392 if let JValue::Object(obj) = item {
2393 if let Some(val) = obj.get(field_name) {
2394 result.push(val.clone());
2395 }
2396 }
2397 }
2398 if result.is_empty() {
2399 Ok(JValue::Null)
2400 } else if result.len() == 1 {
2401 Ok(result.into_iter().next().unwrap())
2402 } else {
2403 Ok(JValue::array(result))
2404 }
2405 }
2406 _ => Ok(JValue::Null),
2407 }
2408 }
2409
2410 AstNode::Number(n) => {
2411 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2413 Ok(JValue::from(*n as i64))
2415 } else {
2416 Ok(JValue::Number(*n))
2417 }
2418 }
2419 AstNode::Boolean(b) => Ok(JValue::Bool(*b)),
2420 AstNode::Null => Ok(JValue::Null),
2421 AstNode::Undefined => Ok(JValue::Undefined),
2422 AstNode::Placeholder => {
2423 Err(EvaluatorError::EvaluationError(
2426 "Placeholder '?' can only be used as a function argument".to_string(),
2427 ))
2428 }
2429 AstNode::Regex { pattern, flags } => {
2430 Ok(JValue::regex(pattern.as_str(), flags.as_str()))
2433 }
2434
2435 AstNode::Variable(name) => {
2436 if name.is_empty() {
2440 if let Some(value) = self.context.lookup("$") {
2441 return Ok(value.clone());
2442 }
2443 if let JValue::Object(obj) = data {
2445 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2446 if let Some(inner) = obj.get("@") {
2447 return Ok(inner.clone());
2448 }
2449 }
2450 }
2451 return Ok(data.clone());
2452 }
2453
2454 if let Some(value) = self.context.lookup(name) {
2458 return Ok(value.clone());
2459 }
2460
2461 if let JValue::Object(obj) = data {
2464 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2465 let binding_key = format!("${}", name);
2467 if let Some(binding_value) = obj.get(&binding_key) {
2468 return Ok(binding_value.clone());
2469 }
2470 }
2471 }
2472
2473 if let Some(stored_lambda) = self.context.lookup_lambda(name) {
2475 let lambda_repr = JValue::lambda(
2479 name.as_str(),
2480 stored_lambda.params.clone(),
2481 Some(name.to_string()),
2482 stored_lambda.signature.clone(),
2483 );
2484 return Ok(lambda_repr);
2485 }
2486
2487 if self.is_builtin_function(name) {
2489 let builtin_repr = JValue::builtin(name.as_str());
2492 return Ok(builtin_repr);
2493 }
2494
2495 Ok(JValue::Null)
2499 }
2500
2501 AstNode::ParentVariable(name) => {
2502 if name.is_empty() {
2504 return self.context.get_parent().cloned().ok_or_else(|| {
2505 EvaluatorError::ReferenceError("Parent context not available".to_string())
2506 });
2507 }
2508
2509 let parent_data = self.context.get_parent().ok_or_else(|| {
2512 EvaluatorError::ReferenceError("Parent context not available".to_string())
2513 })?;
2514
2515 match parent_data {
2517 JValue::Object(obj) => Ok(obj.get(name).cloned().unwrap_or(JValue::Null)),
2518 _ => Ok(JValue::Null),
2519 }
2520 }
2521
2522 AstNode::Path { steps } => self.evaluate_path(steps, data),
2523
2524 AstNode::Binary { op, lhs, rhs } => self.evaluate_binary_op(*op, lhs, rhs, data),
2525
2526 AstNode::Unary { op, operand } => self.evaluate_unary_op(*op, operand, data),
2527
2528 AstNode::Array(elements) => {
2530 let mut result = Vec::with_capacity(elements.len());
2534 for element in elements {
2535 let is_array_constructor = matches!(element, AstNode::Array(_));
2537
2538 let value = self.evaluate_internal(element, data)?;
2539
2540 if value.is_undefined() {
2543 continue;
2544 }
2545
2546 if is_array_constructor {
2547 result.push(value);
2549 } else if let JValue::Array(arr) = value {
2550 result.extend(arr.iter().cloned());
2552 } else {
2553 result.push(value);
2555 }
2556 }
2557 Ok(JValue::array(result))
2558 }
2559
2560 AstNode::Object(pairs) => {
2561 let mut result = IndexMap::with_capacity(pairs.len());
2562
2563 let all_literal_keys = pairs
2565 .iter()
2566 .all(|(k, _)| matches!(k, AstNode::String(_)));
2567
2568 if all_literal_keys {
2569 for (key_node, value_node) in pairs.iter() {
2571 let key = match key_node {
2572 AstNode::String(s) => s,
2573 _ => unreachable!(),
2574 };
2575 let value = self.evaluate_internal(value_node, data)?;
2576 if value.is_undefined() {
2577 continue;
2578 }
2579 result.insert(key.clone(), value);
2580 }
2581 } else {
2582 let mut key_sources: HashMap<String, usize> = HashMap::new();
2583 for (pair_index, (key_node, value_node)) in pairs.iter().enumerate() {
2584 let key = match self.evaluate_internal(key_node, data)? {
2585 JValue::String(s) => s,
2586 JValue::Null => continue,
2587 other => {
2588 if other.is_undefined() {
2589 continue;
2590 }
2591 return Err(EvaluatorError::TypeError(format!(
2592 "Object key must be a string, got: {:?}",
2593 other
2594 )));
2595 }
2596 };
2597
2598 if let Some(&existing_idx) = key_sources.get(&*key) {
2599 if existing_idx != pair_index {
2600 return Err(EvaluatorError::EvaluationError(format!(
2601 "D1009: Multiple key expressions evaluate to same key: {}",
2602 key
2603 )));
2604 }
2605 }
2606 key_sources.insert(key.to_string(), pair_index);
2607
2608 let value = self.evaluate_internal(value_node, data)?;
2609 if value.is_undefined() {
2610 continue;
2611 }
2612 result.insert(key.to_string(), value);
2613 }
2614 }
2615 Ok(JValue::object(result))
2616 }
2617
2618 AstNode::ObjectTransform { input, pattern } => {
2620 let input_value = self.evaluate_internal(input, data)?;
2622
2623 if input_value.is_undefined() {
2625 return Ok(JValue::Undefined);
2626 }
2627
2628 let items: Vec<JValue> = match input_value {
2630 JValue::Array(ref arr) => (**arr).clone(),
2631 JValue::Null => return Ok(JValue::Null),
2632 other => vec![other],
2633 };
2634
2635 let items = if items.is_empty() {
2637 vec![JValue::Undefined]
2638 } else {
2639 items
2640 };
2641
2642 let mut groups: HashMap<String, (Vec<JValue>, usize)> = HashMap::new();
2646
2647 let saved_dollar = self.context.lookup("$").cloned();
2649
2650 for item in &items {
2651 self.context.bind("$".to_string(), item.clone());
2653
2654 for (pair_index, (key_node, _value_node)) in pattern.iter().enumerate() {
2655 let key = match self.evaluate_internal(key_node, item)? {
2657 JValue::String(s) => s,
2658 JValue::Null => continue, other => {
2660 if other.is_undefined() {
2662 continue;
2663 }
2664 return Err(EvaluatorError::TypeError(format!(
2665 "T1003: Object key must be a string, got: {:?}",
2666 other
2667 )));
2668 }
2669 };
2670
2671 if let Some((existing_data, existing_idx)) = groups.get_mut(&*key) {
2673 if *existing_idx != pair_index {
2675 return Err(EvaluatorError::EvaluationError(format!(
2677 "D1009: Multiple key expressions evaluate to same key: {}",
2678 key
2679 )));
2680 }
2681 existing_data.push(item.clone());
2683 } else {
2684 groups.insert(key.to_string(), (vec![item.clone()], pair_index));
2686 }
2687 }
2688 }
2689
2690 let mut result = IndexMap::new();
2692
2693 for (key, (grouped_data, expr_index)) in groups {
2694 let (_key_node, value_node) = &pattern[expr_index];
2696
2697 let context = if grouped_data.len() == 1 {
2701 grouped_data.into_iter().next().unwrap()
2702 } else {
2703 JValue::array(grouped_data)
2704 };
2705
2706 self.context.bind("$".to_string(), context.clone());
2708
2709 let value = self.evaluate_internal(value_node, &context)?;
2711
2712 if !value.is_undefined() {
2714 result.insert(key, value);
2715 }
2716 }
2717
2718 if let Some(saved) = saved_dollar {
2720 self.context.bind("$".to_string(), saved);
2721 } else {
2722 self.context.unbind("$");
2723 }
2724
2725 Ok(JValue::object(result))
2726 }
2727
2728 AstNode::Function {
2729 name,
2730 args,
2731 is_builtin,
2732 } => self.evaluate_function_call(name, args, *is_builtin, data),
2733
2734 AstNode::Call { procedure, args } => {
2737 let callable = self.evaluate_internal(procedure, data)?;
2739
2740 if let Some(stored_lambda) = self.lookup_lambda_from_value(&callable) {
2742 let mut evaluated_args = Vec::with_capacity(args.len());
2743 for arg in args.iter() {
2744 evaluated_args.push(self.evaluate_internal(arg, data)?);
2745 }
2746 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
2747 }
2748
2749 Err(EvaluatorError::TypeError(format!(
2751 "Cannot call non-function value: {:?}",
2752 callable
2753 )))
2754 }
2755
2756 AstNode::Conditional {
2757 condition,
2758 then_branch,
2759 else_branch,
2760 } => {
2761 let condition_value = self.evaluate_internal(condition, data)?;
2762 if self.is_truthy(&condition_value) {
2763 self.evaluate_internal(then_branch, data)
2764 } else if let Some(else_branch) = else_branch {
2765 self.evaluate_internal(else_branch, data)
2766 } else {
2767 Ok(JValue::Undefined)
2770 }
2771 }
2772
2773 AstNode::Block(expressions) => {
2774 self.context.push_scope();
2776
2777 let mut result = JValue::Null;
2778 for expr in expressions {
2779 result = self.evaluate_internal(expr, data)?;
2780 }
2781
2782 let lambdas_to_keep = self.extract_lambda_ids(&result);
2785 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
2786
2787 Ok(result)
2788 }
2789
2790 AstNode::Lambda {
2792 params,
2793 body,
2794 signature,
2795 thunk,
2796 } => {
2797 let lambda_id = format!("__lambda_{}_{:p}", params.len(), body.as_ref());
2798
2799 let compiled_body = if !thunk {
2800 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
2801 try_compile_expr_with_allowed_vars(body, &var_refs)
2802 } else {
2803 None
2804 };
2805 let stored_lambda = StoredLambda {
2806 params: params.clone(),
2807 body: (**body).clone(),
2808 compiled_body,
2809 signature: signature.clone(),
2810 captured_env: self.capture_environment_for(body, params),
2811 captured_data: Some(data.clone()),
2812 thunk: *thunk,
2813 };
2814 self.context.bind_lambda(lambda_id.clone(), stored_lambda);
2815
2816 let lambda_obj = JValue::lambda(
2817 lambda_id.as_str(),
2818 params.clone(),
2819 None::<String>,
2820 signature.clone(),
2821 );
2822
2823 Ok(lambda_obj)
2824 }
2825
2826 AstNode::Wildcard => {
2828 match data {
2829 JValue::Object(obj) => {
2830 let mut result = Vec::new();
2831 for value in obj.values() {
2832 match value {
2834 JValue::Array(arr) => result.extend(arr.iter().cloned()),
2835 _ => result.push(value.clone()),
2836 }
2837 }
2838 Ok(JValue::array(result))
2839 }
2840 JValue::Array(arr) => {
2841 Ok(JValue::Array(arr.clone()))
2843 }
2844 _ => Ok(JValue::Null),
2845 }
2846 }
2847
2848 AstNode::Descendant => {
2850 let descendants = self.collect_descendants(data);
2851 if descendants.is_empty() {
2852 Ok(JValue::Null) } else {
2854 Ok(JValue::array(descendants))
2855 }
2856 }
2857
2858 AstNode::Predicate(_) => Err(EvaluatorError::EvaluationError(
2859 "Predicate can only be used in path expressions".to_string(),
2860 )),
2861
2862 AstNode::ArrayGroup(elements) => {
2864 let mut result = Vec::new();
2865 for element in elements {
2866 let value = self.evaluate_internal(element, data)?;
2867 result.push(value);
2868 }
2869 Ok(JValue::array(result))
2870 }
2871
2872 AstNode::FunctionApplication(_) => Err(EvaluatorError::EvaluationError(
2873 "Function application can only be used in path expressions".to_string(),
2874 )),
2875
2876 AstNode::Sort { input, terms } => {
2877 let value = self.evaluate_internal(input, data)?;
2878 self.evaluate_sort(&value, terms)
2879 }
2880
2881 AstNode::IndexBind { input, variable } => {
2883 let value = self.evaluate_internal(input, data)?;
2884
2885 match value {
2888 JValue::Array(arr) => {
2889 let mut result = Vec::new();
2891 for (idx, item) in arr.iter().enumerate() {
2892 let mut wrapper = IndexMap::new();
2894 wrapper.insert("@".to_string(), item.clone());
2895 wrapper.insert(format!("${}", variable), JValue::Number(idx as f64));
2896 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
2897 result.push(JValue::object(wrapper));
2898 }
2899 Ok(JValue::array(result))
2900 }
2901 other => {
2903 let mut wrapper = IndexMap::new();
2904 wrapper.insert("@".to_string(), other);
2905 wrapper.insert(format!("${}", variable), JValue::from(0i64));
2906 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
2907 Ok(JValue::object(wrapper))
2908 }
2909 }
2910 }
2911
2912 AstNode::Transform {
2914 location,
2915 update,
2916 delete,
2917 } => {
2918 if self.context.lookup("$").is_some() {
2920 self.execute_transform(location, update, delete.as_deref(), data)
2922 } else {
2923 let transform_lambda = StoredLambda {
2926 params: vec!["$".to_string()],
2927 body: AstNode::Transform {
2928 location: location.clone(),
2929 update: update.clone(),
2930 delete: delete.clone(),
2931 },
2932 compiled_body: None, signature: None,
2934 captured_env: HashMap::new(),
2935 captured_data: None, thunk: false,
2937 };
2938
2939 let lambda_name = format!("__transform_{:p}", location);
2941 self.context.bind_lambda(lambda_name, transform_lambda);
2942
2943 Ok(JValue::string("<lambda>"))
2945 }
2946 }
2947 }
2948 }
2949
2950 fn apply_stages(&mut self, value: JValue, stages: &[Stage]) -> Result<JValue, EvaluatorError> {
2954 let mut result = match value {
2956 JValue::Null => return Ok(JValue::Null), JValue::Array(_) => value,
2958 other => JValue::array(vec![other]),
2959 };
2960
2961 for stage in stages {
2962 match stage {
2963 Stage::Filter(predicate_expr) => {
2964 result = self.evaluate_predicate_as_stage(&result, predicate_expr)?;
2966 }
2967 }
2968 }
2969 Ok(result)
2970 }
2971
2972 fn is_filter_predicate(predicate: &AstNode) -> bool {
2975 match predicate {
2976 AstNode::Binary { op, .. } => matches!(
2977 op,
2978 BinaryOp::GreaterThan
2979 | BinaryOp::GreaterThanOrEqual
2980 | BinaryOp::LessThan
2981 | BinaryOp::LessThanOrEqual
2982 | BinaryOp::Equal
2983 | BinaryOp::NotEqual
2984 | BinaryOp::And
2985 | BinaryOp::Or
2986 | BinaryOp::In
2987 ),
2988 AstNode::Unary {
2989 op: crate::ast::UnaryOp::Not,
2990 ..
2991 } => true,
2992 _ => false,
2993 }
2994 }
2995
2996 fn evaluate_predicate_as_stage(
3000 &mut self,
3001 current: &JValue,
3002 predicate: &AstNode,
3003 ) -> Result<JValue, EvaluatorError> {
3004 if matches!(predicate, AstNode::Boolean(true)) {
3006 return match current {
3007 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
3008 JValue::Null => Ok(JValue::Null),
3009 other => Ok(JValue::array(vec![other.clone()])),
3010 };
3011 }
3012
3013 match current {
3014 JValue::Array(arr) => {
3015 if let AstNode::Number(n) = predicate {
3020 let is_array_of_arrays =
3022 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3023
3024 if !is_array_of_arrays {
3025 return self.array_index(current, &JValue::Number(*n));
3027 }
3028
3029 let mut result = Vec::new();
3031 for item in arr.iter() {
3032 match item {
3033 JValue::Array(_) => {
3034 let indexed = self.array_index(item, &JValue::Number(*n))?;
3035 if !indexed.is_null() {
3036 result.push(indexed);
3037 }
3038 }
3039 _ => {
3040 if *n == 0.0 {
3041 result.push(item.clone());
3042 }
3043 }
3044 }
3045 }
3046 return Ok(JValue::array(result));
3047 }
3048
3049 if Self::is_filter_predicate(predicate) {
3052 if let Some(compiled) = try_compile_expr(predicate) {
3054 let shape = arr.first().and_then(build_shape_cache);
3055 let mut filtered = Vec::with_capacity(arr.len());
3056 for item in arr.iter() {
3057 let result = if let Some(ref s) = shape {
3058 eval_compiled_shaped(&compiled, item, None, s)?
3059 } else {
3060 eval_compiled(&compiled, item, None)?
3061 };
3062 if compiled_is_truthy(&result) {
3063 filtered.push(item.clone());
3064 }
3065 }
3066 return Ok(JValue::array(filtered));
3067 }
3068 let mut filtered = Vec::new();
3070 for item in arr.iter() {
3071 let item_result = self.evaluate_internal(predicate, item)?;
3072 if self.is_truthy(&item_result) {
3073 filtered.push(item.clone());
3074 }
3075 }
3076 return Ok(JValue::array(filtered));
3077 }
3078
3079 match self.evaluate_internal(predicate, current) {
3084 Ok(JValue::Number(n)) => {
3085 let n_val = n;
3086 let is_array_of_arrays =
3087 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3088
3089 if !is_array_of_arrays {
3090 let pred_result = JValue::Number(n_val);
3091 return self.array_index(current, &pred_result);
3092 }
3093
3094 let mut result = Vec::new();
3096 let pred_result = JValue::Number(n_val);
3097 for item in arr.iter() {
3098 match item {
3099 JValue::Array(_) => {
3100 let indexed = self.array_index(item, &pred_result)?;
3101 if !indexed.is_null() {
3102 result.push(indexed);
3103 }
3104 }
3105 _ => {
3106 if n_val == 0.0 {
3107 result.push(item.clone());
3108 }
3109 }
3110 }
3111 }
3112 return Ok(JValue::array(result));
3113 }
3114 Ok(JValue::Array(indices)) => {
3115 let has_non_numeric =
3118 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
3119
3120 if has_non_numeric {
3121 } else {
3123 let arr_len = arr.len() as i64;
3125 let mut resolved_indices: Vec<i64> = indices
3126 .iter()
3127 .filter_map(|v| {
3128 if let JValue::Number(n) = v {
3129 let idx = *n as i64;
3130 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
3132 if actual_idx >= 0 && actual_idx < arr_len {
3134 Some(actual_idx)
3135 } else {
3136 None
3137 }
3138 } else {
3139 None
3140 }
3141 })
3142 .collect();
3143
3144 resolved_indices.sort();
3146 resolved_indices.dedup();
3147
3148 let result: Vec<JValue> = resolved_indices
3150 .iter()
3151 .map(|&idx| arr[idx as usize].clone())
3152 .collect();
3153
3154 return Ok(JValue::array(result));
3155 }
3156 }
3157 Ok(_) => {
3158 }
3161 Err(_) => {
3162 }
3165 }
3166
3167 let mut filtered = Vec::new();
3169 for item in arr.iter() {
3170 let item_result = self.evaluate_internal(predicate, item)?;
3171 if self.is_truthy(&item_result) {
3172 filtered.push(item.clone());
3173 }
3174 }
3175 Ok(JValue::array(filtered))
3176 }
3177 JValue::Null => {
3178 Ok(JValue::Null)
3180 }
3181 other => {
3182 if let AstNode::Number(n) = predicate {
3188 if *n == 0.0 {
3190 return Ok(other.clone());
3191 } else {
3192 return Ok(JValue::Null);
3193 }
3194 }
3195
3196 match self.evaluate_internal(predicate, other) {
3198 Ok(JValue::Number(n)) => {
3199 if n == 0.0 {
3201 Ok(other.clone())
3202 } else {
3203 Ok(JValue::Null)
3204 }
3205 }
3206 Ok(pred_result) => {
3207 if self.is_truthy(&pred_result) {
3209 Ok(other.clone())
3210 } else {
3211 Ok(JValue::Null)
3212 }
3213 }
3214 Err(e) => Err(e),
3215 }
3216 }
3217 }
3218 }
3219
3220 fn evaluate_path(
3222 &mut self,
3223 steps: &[PathStep],
3224 data: &JValue,
3225 ) -> Result<JValue, EvaluatorError> {
3226 if steps.is_empty() {
3228 return Ok(data.clone());
3229 }
3230
3231 if steps.len() == 1 {
3234 if let AstNode::Name(field_name) = &steps[0].node {
3235 return match data {
3236 JValue::Object(obj) => {
3237 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
3239 if let Some(JValue::Object(inner)) = obj.get("@") {
3240 Ok(inner.get(field_name).cloned().unwrap_or(JValue::Null))
3241 } else {
3242 Ok(JValue::Null)
3243 }
3244 } else {
3245 Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null))
3246 }
3247 }
3248 JValue::Array(arr) => {
3249 let has_tuples = arr.first().is_some_and(|item| {
3253 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
3254 });
3255
3256 if !has_tuples {
3257 let mut result = Vec::with_capacity(arr.len());
3259 for item in arr.iter() {
3260 if let JValue::Object(obj) = item {
3261 if let Some(val) = obj.get(field_name) {
3262 if !val.is_null() {
3263 match val {
3264 JValue::Array(arr_val) => {
3265 result.extend(arr_val.iter().cloned());
3266 }
3267 other => result.push(other.clone()),
3268 }
3269 }
3270 }
3271 } else if let JValue::Array(inner_arr) = item {
3272 let nested_result = self.evaluate_path(
3273 &[PathStep::new(AstNode::Name(field_name.clone()))],
3274 &JValue::Array(inner_arr.clone()),
3275 )?;
3276 match nested_result {
3277 JValue::Array(nested) => {
3278 result.extend(nested.iter().cloned());
3279 }
3280 JValue::Null => {}
3281 other => result.push(other),
3282 }
3283 }
3284 }
3285
3286 if result.is_empty() {
3287 Ok(JValue::Null)
3288 } else if result.len() == 1 {
3289 Ok(result.into_iter().next().unwrap())
3290 } else {
3291 Ok(JValue::array(result))
3292 }
3293 } else {
3294 let mut result = Vec::new();
3296 for item in arr.iter() {
3297 match item {
3298 JValue::Object(obj) => {
3299 let is_tuple =
3300 obj.get("__tuple__") == Some(&JValue::Bool(true));
3301
3302 if is_tuple {
3303 let inner = match obj.get("@") {
3304 Some(JValue::Object(inner)) => inner,
3305 _ => continue,
3306 };
3307
3308 if let Some(val) = inner.get(field_name) {
3309 if !val.is_null() {
3310 let wrap = |v: JValue| -> JValue {
3312 let mut wrapper = IndexMap::new();
3313 wrapper.insert("@".to_string(), v);
3314 wrapper.insert(
3315 "__tuple__".to_string(),
3316 JValue::Bool(true),
3317 );
3318 for (k, v) in obj.iter() {
3319 if k.starts_with('$') {
3320 wrapper.insert(k.clone(), v.clone());
3321 }
3322 }
3323 JValue::object(wrapper)
3324 };
3325
3326 match val {
3327 JValue::Array(arr_val) => {
3328 for item in arr_val.iter() {
3329 result.push(wrap(item.clone()));
3330 }
3331 }
3332 other => result.push(wrap(other.clone())),
3333 }
3334 }
3335 }
3336 } else {
3337 if let Some(val) = obj.get(field_name) {
3339 if !val.is_null() {
3340 match val {
3341 JValue::Array(arr_val) => {
3342 for item in arr_val.iter() {
3343 result.push(item.clone());
3344 }
3345 }
3346 other => result.push(other.clone()),
3347 }
3348 }
3349 }
3350 }
3351 }
3352 JValue::Array(inner_arr) => {
3353 let nested_result = self.evaluate_path(
3355 &[PathStep::new(AstNode::Name(field_name.clone()))],
3356 &JValue::Array(inner_arr.clone()),
3357 )?;
3358 match nested_result {
3360 JValue::Array(nested) => {
3361 result.extend(nested.iter().cloned());
3363 }
3364 JValue::Null => {} other => result.push(other),
3366 }
3367 }
3368 _ => {} }
3370 }
3371
3372 if result.is_empty() {
3376 Ok(JValue::Null)
3377 } else if result.len() == 1 {
3378 Ok(result.into_iter().next().unwrap())
3379 } else {
3380 Ok(JValue::array(result))
3381 }
3382 } }
3384 _ => Ok(JValue::Null),
3385 };
3386 }
3387 }
3388
3389 if steps.len() == 2
3392 && steps[0].stages.is_empty()
3393 && steps[1].stages.is_empty()
3394 {
3395 if let (AstNode::Variable(var_name), AstNode::Name(field_name)) =
3396 (&steps[0].node, &steps[1].node)
3397 {
3398 if !var_name.is_empty() {
3399 if let Some(value) = self.context.lookup(var_name) {
3400 match value {
3401 JValue::Object(obj) => {
3402 return Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null));
3403 }
3404 JValue::Array(arr) => {
3405 let mut result = Vec::with_capacity(arr.len());
3407 for item in arr.iter() {
3408 if let JValue::Object(obj) = item {
3409 if let Some(val) = obj.get(field_name) {
3410 if !val.is_null() {
3411 match val {
3412 JValue::Array(inner) => {
3413 result.extend(inner.iter().cloned());
3414 }
3415 other => result.push(other.clone()),
3416 }
3417 }
3418 }
3419 }
3420 }
3421 return match result.len() {
3422 0 => Ok(JValue::Null),
3423 1 => Ok(result.pop().unwrap()),
3424 _ => Ok(JValue::array(result)),
3425 };
3426 }
3427 _ => {} }
3429 }
3430 }
3431 }
3432 }
3433
3434 let mut did_array_mapping = false;
3436
3437 let mut current: JValue = match &steps[0].node {
3439 AstNode::Wildcard => {
3440 match data {
3442 JValue::Object(obj) => {
3443 let mut result = Vec::new();
3444 for value in obj.values() {
3445 match value {
3447 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3448 _ => result.push(value.clone()),
3449 }
3450 }
3451 JValue::array(result)
3452 }
3453 JValue::Array(arr) => JValue::Array(arr.clone()),
3454 _ => JValue::Null,
3455 }
3456 }
3457 AstNode::Descendant => {
3458 let descendants = self.collect_descendants(data);
3460 JValue::array(descendants)
3461 }
3462 AstNode::ParentVariable(name) => {
3463 let parent_data = self.context.get_parent().ok_or_else(|| {
3465 EvaluatorError::ReferenceError("Parent context not available".to_string())
3466 })?;
3467
3468 if name.is_empty() {
3469 parent_data.clone()
3471 } else {
3472 match parent_data {
3474 JValue::Object(obj) => obj.get(name).cloned().unwrap_or(JValue::Null),
3475 _ => JValue::Null,
3476 }
3477 }
3478 }
3479 AstNode::Name(field_name) => {
3480 let stages = &steps[0].stages;
3482
3483 match data {
3484 JValue::Object(obj) => {
3485 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3486 if !stages.is_empty() {
3488 self.apply_stages(val, stages)?
3489 } else {
3490 val
3491 }
3492 }
3493 JValue::Array(arr) => {
3494 let mut result = Vec::new();
3496 for item in arr.iter() {
3497 match item {
3498 JValue::Object(obj) => {
3499 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3500 if !val.is_null() {
3501 if !stages.is_empty() {
3502 let processed_val = self.apply_stages(val, stages)?;
3504 match processed_val {
3506 JValue::Array(arr) => {
3507 result.extend(arr.iter().cloned())
3508 }
3509 JValue::Null => {} other => result.push(other), }
3512 } else {
3513 match val {
3515 JValue::Array(arr) => {
3516 result.extend(arr.iter().cloned())
3517 }
3518 other => result.push(other),
3519 }
3520 }
3521 }
3522 }
3523 JValue::Array(inner_arr) => {
3524 let nested_result = self.evaluate_path(
3526 &[steps[0].clone()],
3527 &JValue::Array(inner_arr.clone()),
3528 )?;
3529 match nested_result {
3530 JValue::Array(nested) => {
3531 result.extend(nested.iter().cloned())
3532 }
3533 JValue::Null => {} other => result.push(other),
3535 }
3536 }
3537 _ => {} }
3539 }
3540 JValue::array(result)
3541 }
3542 JValue::Null => JValue::Null,
3543 _ => JValue::Undefined,
3545 }
3546 }
3547 AstNode::String(string_literal) => {
3548 let stages = &steps[0].stages;
3551 let val = JValue::string(string_literal.clone());
3552
3553 if !stages.is_empty() {
3554 let result = self.apply_stages(val, stages)?;
3556 match result {
3559 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
3560 JValue::Array(arr) if arr.is_empty() => JValue::Null,
3561 other => other,
3562 }
3563 } else {
3564 val
3565 }
3566 }
3567 AstNode::Predicate(pred_expr) => {
3568 self.evaluate_predicate(data, pred_expr)?
3570 }
3571 AstNode::IndexBind { .. } => {
3572 self.evaluate_internal(&steps[0].node, data)?
3574 }
3575 _ => {
3576 self.evaluate_path_step(&steps[0].node, data, data)?
3578 }
3579 };
3580
3581 for step in steps[1..].iter() {
3583 if current.is_null() || current.is_undefined() {
3586 return Ok(JValue::Null);
3587 }
3588
3589 let is_tuple_array = if let JValue::Array(arr) = ¤t {
3592 arr.first().is_some_and(|first| {
3593 if let JValue::Object(obj) = first {
3594 obj.get("__tuple__") == Some(&JValue::Bool(true))
3595 } else {
3596 false
3597 }
3598 })
3599 } else {
3600 false
3601 };
3602
3603 let needs_tuple_context_binding = is_tuple_array
3613 && matches!(
3614 &step.node,
3615 AstNode::Object(_) | AstNode::FunctionApplication(_) | AstNode::Variable(_)
3616 );
3617
3618 if needs_tuple_context_binding {
3619 if let JValue::Array(arr) = ¤t {
3620 let mut results = Vec::new();
3621
3622 for tuple in arr.iter() {
3623 if let JValue::Object(tuple_obj) = tuple {
3624 let bindings: Vec<(String, JValue)> = tuple_obj
3626 .iter()
3627 .filter(|(k, _)| k.starts_with('$') && k.len() > 1) .map(|(k, v)| (k[1..].to_string(), v.clone())) .collect();
3630
3631 let saved_bindings: Vec<(String, Option<JValue>)> = bindings
3633 .iter()
3634 .map(|(name, _)| (name.clone(), self.context.lookup(name).cloned()))
3635 .collect();
3636
3637 for (name, value) in &bindings {
3639 self.context.bind(name.clone(), value.clone());
3640 }
3641
3642 let actual_data = tuple_obj.get("@").cloned().unwrap_or(JValue::Null);
3644
3645 let step_result = match &step.node {
3647 AstNode::Variable(_) => {
3648 self.evaluate_internal(&step.node, tuple)?
3650 }
3651 AstNode::Object(_) | AstNode::FunctionApplication(_) => {
3652 self.evaluate_internal(&step.node, &actual_data)?
3654 }
3655 _ => unreachable!(), };
3657
3658 for (name, saved_value) in &saved_bindings {
3660 if let Some(value) = saved_value {
3661 self.context.bind(name.clone(), value.clone());
3662 } else {
3663 self.context.unbind(name);
3664 }
3665 }
3666
3667 if !step_result.is_null() && !step_result.is_undefined() {
3669 if matches!(&step.node, AstNode::Object(_)) {
3672 results.push(step_result);
3673 } else if matches!(step_result, JValue::Array(_)) {
3674 if let JValue::Array(arr) = step_result {
3675 results.extend(arr.iter().cloned());
3676 }
3677 } else {
3678 results.push(step_result);
3679 }
3680 }
3681 }
3682 }
3683
3684 current = JValue::array(results);
3685 continue; }
3687 }
3688
3689 current = match &step.node {
3690 AstNode::Wildcard => {
3691 let stages = &step.stages;
3693 let wildcard_result = match ¤t {
3694 JValue::Object(obj) => {
3695 let mut result = Vec::new();
3696 for value in obj.values() {
3697 match value {
3699 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3700 _ => result.push(value.clone()),
3701 }
3702 }
3703 JValue::array(result)
3704 }
3705 JValue::Array(arr) => {
3706 let mut all_values = Vec::new();
3708 for item in arr.iter() {
3709 match item {
3710 JValue::Object(obj) => {
3711 for value in obj.values() {
3712 match value {
3714 JValue::Array(arr) => {
3715 all_values.extend(arr.iter().cloned())
3716 }
3717 _ => all_values.push(value.clone()),
3718 }
3719 }
3720 }
3721 JValue::Array(inner) => {
3722 all_values.extend(inner.iter().cloned());
3723 }
3724 _ => {}
3725 }
3726 }
3727 JValue::array(all_values)
3728 }
3729 _ => JValue::Null,
3730 };
3731
3732 if !stages.is_empty() {
3734 self.apply_stages(wildcard_result, stages)?
3735 } else {
3736 wildcard_result
3737 }
3738 }
3739 AstNode::Descendant => {
3740 match ¤t {
3742 JValue::Array(arr) => {
3743 let mut all_descendants = Vec::new();
3745 for item in arr.iter() {
3746 all_descendants.extend(self.collect_descendants(item));
3747 }
3748 JValue::array(all_descendants)
3749 }
3750 _ => {
3751 let descendants = self.collect_descendants(¤t);
3753 JValue::array(descendants)
3754 }
3755 }
3756 }
3757 AstNode::Name(field_name) => {
3758 let stages = &step.stages;
3760
3761 match ¤t {
3762 JValue::Object(obj) => {
3763 did_array_mapping = false;
3768 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3769 if !stages.is_empty() {
3771 self.apply_stages(val, stages)?
3772 } else {
3773 val
3774 }
3775 }
3776 JValue::Array(arr) => {
3777 did_array_mapping = true; let has_tuples = arr.first().is_some_and(|item| {
3785 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
3786 });
3787
3788 if !has_tuples && stages.is_empty() {
3789 let mut result = Vec::with_capacity(arr.len());
3790 for item in arr.iter() {
3791 match item {
3792 JValue::Object(obj) => {
3793 if let Some(val) = obj.get(field_name) {
3794 if !val.is_null() {
3795 match val {
3796 JValue::Array(arr_val) => {
3797 result.extend(arr_val.iter().cloned())
3798 }
3799 other => result.push(other.clone()),
3800 }
3801 }
3802 }
3803 }
3804 JValue::Array(_) => {
3805 let nested_result =
3806 self.evaluate_path(&[step.clone()], item)?;
3807 match nested_result {
3808 JValue::Array(nested) => {
3809 result.extend(nested.iter().cloned())
3810 }
3811 JValue::Null => {}
3812 other => result.push(other),
3813 }
3814 }
3815 _ => {}
3816 }
3817 }
3818 JValue::array(result)
3819 } else {
3820 let mut result = Vec::new();
3822
3823 for item in arr.iter() {
3824 match item {
3825 JValue::Object(obj) => {
3826 let (actual_obj, tuple_bindings) =
3828 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
3829 if let Some(JValue::Object(inner)) = obj.get("@") {
3831 let bindings: Vec<(String, JValue)> = obj
3833 .iter()
3834 .filter(|(k, _)| k.starts_with('$'))
3835 .map(|(k, v)| (k.clone(), v.clone()))
3836 .collect();
3837 (inner.clone(), Some(bindings))
3838 } else {
3839 continue; }
3841 } else {
3842 (obj.clone(), None)
3843 };
3844
3845 let val = actual_obj
3846 .get(field_name)
3847 .cloned()
3848 .unwrap_or(JValue::Null);
3849
3850 if !val.is_null() {
3851 let wrap_in_tuple = |v: JValue, bindings: &Option<Vec<(String, JValue)>>| -> JValue {
3853 if let Some(b) = bindings {
3854 let mut wrapper = IndexMap::new();
3855 wrapper.insert("@".to_string(), v);
3856 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
3857 for (k, val) in b {
3858 wrapper.insert(k.clone(), val.clone());
3859 }
3860 JValue::object(wrapper)
3861 } else {
3862 v
3863 }
3864 };
3865
3866 if !stages.is_empty() {
3867 let processed_val =
3869 self.apply_stages(val, stages)?;
3870 match processed_val {
3872 JValue::Array(arr) => {
3873 for item in arr.iter() {
3874 result.push(wrap_in_tuple(
3875 item.clone(),
3876 &tuple_bindings,
3877 ));
3878 }
3879 }
3880 JValue::Null => {} other => result.push(wrap_in_tuple(
3882 other,
3883 &tuple_bindings,
3884 )),
3885 }
3886 } else {
3887 match val {
3890 JValue::Array(arr) => {
3891 for item in arr.iter() {
3892 result.push(wrap_in_tuple(
3893 item.clone(),
3894 &tuple_bindings,
3895 ));
3896 }
3897 }
3898 other => result.push(wrap_in_tuple(
3899 other,
3900 &tuple_bindings,
3901 )),
3902 }
3903 }
3904 }
3905 }
3906 JValue::Array(_) => {
3907 let nested_result =
3909 self.evaluate_path(&[step.clone()], item)?;
3910 match nested_result {
3911 JValue::Array(nested) => {
3912 result.extend(nested.iter().cloned())
3913 }
3914 JValue::Null => {}
3915 other => result.push(other),
3916 }
3917 }
3918 _ => {}
3919 }
3920 }
3921
3922 JValue::array(result)
3923 }
3924 }
3925 JValue::Null => JValue::Null,
3926 _ => JValue::Undefined,
3928 }
3929 }
3930 AstNode::String(string_literal) => {
3931 let stages = &step.stages;
3933 let val = JValue::string(string_literal.clone());
3934
3935 if !stages.is_empty() {
3936 let result = self.apply_stages(val, stages)?;
3938 match result {
3940 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
3941 JValue::Array(arr) if arr.is_empty() => JValue::Null,
3942 other => other,
3943 }
3944 } else {
3945 val
3946 }
3947 }
3948 AstNode::Predicate(pred_expr) => {
3949 self.evaluate_predicate(¤t, pred_expr)?
3951 }
3952 AstNode::ArrayGroup(elements) => {
3953 match ¤t {
3956 JValue::Array(arr) => {
3957 let mut result = Vec::new();
3958 for item in arr.iter() {
3959 let mut group_values = Vec::new();
3961 for element in elements {
3962 let value = self.evaluate_internal(element, item)?;
3963 let should_preserve_array = matches!(
3966 element,
3967 AstNode::Array(_) | AstNode::ArrayGroup(_)
3968 );
3969
3970 if should_preserve_array {
3971 group_values.push(value);
3973 } else {
3974 match value {
3976 JValue::Array(arr) => {
3977 group_values.extend(arr.iter().cloned())
3978 }
3979 other => group_values.push(other),
3980 }
3981 }
3982 }
3983 result.push(JValue::array(group_values));
3985 }
3986 JValue::array(result)
3987 }
3988 _ => {
3989 let mut result = Vec::new();
3991 for element in elements {
3992 let value = self.evaluate_internal(element, ¤t)?;
3993 result.push(value);
3994 }
3995 JValue::array(result)
3996 }
3997 }
3998 }
3999 AstNode::FunctionApplication(expr) => {
4000 match ¤t {
4004 JValue::Array(arr) => {
4005 let mapped: Vec<JValue> =
4009 if let Some(compiled) = try_compile_expr(expr) {
4010 let shape = arr.first().and_then(build_shape_cache);
4011 let mut result = Vec::with_capacity(arr.len());
4012 for item in arr.iter() {
4013 let value = if let Some(ref s) = shape {
4014 eval_compiled_shaped(&compiled, item, None, s)?
4015 } else {
4016 eval_compiled(&compiled, item, None)?
4017 };
4018 if !value.is_null() && !value.is_undefined() {
4019 result.push(value);
4020 }
4021 }
4022 result
4023 } else {
4024 let mut result = Vec::new();
4025 for item in arr.iter() {
4026 let saved_dollar = self.context.lookup("$").cloned();
4028
4029 self.context.bind("$".to_string(), item.clone());
4031
4032 let value = self.evaluate_internal(expr, item)?;
4034
4035 if let Some(saved) = saved_dollar {
4037 self.context.bind("$".to_string(), saved);
4038 } else {
4039 self.context.unbind("$");
4040 }
4041
4042 if !value.is_null() && !value.is_undefined() {
4044 result.push(value);
4045 }
4046 }
4047 result
4048 };
4049 JValue::array(mapped)
4052 }
4053 _ => {
4054 let saved_dollar = self.context.lookup("$").cloned();
4056 self.context.bind("$".to_string(), current.clone());
4057
4058 let value = self.evaluate_internal(expr, ¤t)?;
4059
4060 if let Some(saved) = saved_dollar {
4061 self.context.bind("$".to_string(), saved);
4062 } else {
4063 self.context.unbind("$");
4064 }
4065
4066 value
4067 }
4068 }
4069 }
4070 AstNode::Sort { terms, .. } => {
4071 self.evaluate_sort(¤t, terms)?
4073 }
4074 AstNode::IndexBind { variable, .. } => {
4075 match ¤t {
4078 JValue::Array(arr) => {
4079 let mut result = Vec::new();
4080 for (idx, item) in arr.iter().enumerate() {
4081 let mut wrapper = IndexMap::new();
4082 wrapper.insert("@".to_string(), item.clone());
4083 wrapper
4084 .insert(format!("${}", variable), JValue::Number(idx as f64));
4085 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4086 result.push(JValue::object(wrapper));
4087 }
4088 JValue::array(result)
4089 }
4090 other => {
4091 let mut wrapper = IndexMap::new();
4093 wrapper.insert("@".to_string(), other.clone());
4094 wrapper.insert(format!("${}", variable), JValue::from(0i64));
4095 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4096 JValue::object(wrapper)
4097 }
4098 }
4099 }
4100 _ => self.evaluate_path_step(&step.node, ¤t, data)?,
4102 };
4103 }
4104
4105 let has_explicit_array_keep = steps.iter().any(|step| {
4113 if let AstNode::Predicate(pred) = &step.node {
4115 if matches!(**pred, AstNode::Boolean(true)) {
4116 return true;
4117 }
4118 }
4119 step.stages.iter().any(|stage| {
4121 let crate::ast::Stage::Filter(pred) = stage;
4122 matches!(**pred, AstNode::Boolean(true))
4123 })
4124 });
4125
4126 let should_unwrap = !has_explicit_array_keep
4136 && (steps.iter().any(|step| !step.stages.is_empty()) || did_array_mapping);
4137
4138 let result = match ¤t {
4139 JValue::Array(arr) if arr.is_empty() => JValue::Null,
4141 JValue::Array(arr) if arr.len() == 1 && should_unwrap => arr[0].clone(),
4143 _ => current,
4145 };
4146
4147 Ok(result)
4148 }
4149
4150 fn evaluate_path_step(
4152 &mut self,
4153 step: &AstNode,
4154 current: &JValue,
4155 original_data: &JValue,
4156 ) -> Result<JValue, EvaluatorError> {
4157 if matches!(current, JValue::Array(_)) && matches!(step, AstNode::Object(_)) {
4160 match (current, step) {
4161 (JValue::Array(arr), AstNode::Object(pairs)) => {
4162 if let Some(compiled) = try_compile_expr(&AstNode::Object(pairs.clone())) {
4164 let shape = arr.first().and_then(build_shape_cache);
4165 let mut mapped = Vec::with_capacity(arr.len());
4166 for item in arr.iter() {
4167 let result = if let Some(ref s) = shape {
4168 eval_compiled_shaped(&compiled, item, None, s)?
4169 } else {
4170 eval_compiled(&compiled, item, None)?
4171 };
4172 if !result.is_undefined() {
4173 mapped.push(result);
4174 }
4175 }
4176 return Ok(JValue::array(mapped));
4177 }
4178 let mapped: Result<Vec<JValue>, EvaluatorError> = arr
4180 .iter()
4181 .map(|item| self.evaluate_internal(step, item))
4182 .collect();
4183 Ok(JValue::array(mapped?))
4184 }
4185 _ => unreachable!(),
4186 }
4187 } else {
4188 if let AstNode::Variable(name) = step {
4191 if name.is_empty() {
4192 if let JValue::Array(arr) = current {
4194 return Ok(JValue::Array(arr.clone()));
4196 } else {
4197 return Ok(current.clone());
4199 }
4200 }
4201 }
4202
4203 if matches!(step, AstNode::Variable(_)) {
4207 if let JValue::Array(arr) = current {
4208 let is_tuple_array = arr.first().is_some_and(|first| {
4210 if let JValue::Object(obj) = first {
4211 obj.get("__tuple__") == Some(&JValue::Bool(true))
4212 } else {
4213 false
4214 }
4215 });
4216
4217 if is_tuple_array {
4218 let mut results = Vec::new();
4220 for tuple in arr.iter() {
4221 let val = self.evaluate_internal(step, tuple)?;
4224 if !val.is_null() && !val.is_undefined() {
4225 results.push(val);
4226 }
4227 }
4228 return Ok(JValue::array(results));
4229 }
4230 }
4231 }
4232
4233 if matches!(
4242 step,
4243 AstNode::Binary { .. }
4244 | AstNode::Function { .. }
4245 | AstNode::Variable(_)
4246 | AstNode::ParentVariable(_)
4247 | AstNode::Array(_)
4248 | AstNode::Object(_)
4249 | AstNode::Sort { .. }
4250 | AstNode::Block(_)
4251 ) {
4252 return self.evaluate_internal(step, original_data);
4254 }
4255
4256 let step_value = self.evaluate_internal(step, original_data)?;
4258 Ok(match (current, &step_value) {
4259 (JValue::Object(obj), JValue::String(key)) => {
4260 obj.get(&**key).cloned().unwrap_or(JValue::Null)
4261 }
4262 (JValue::Array(arr), JValue::Number(n)) => {
4263 let index = *n as i64;
4264 let len = arr.len() as i64;
4265
4266 let actual_idx = if index < 0 { len + index } else { index };
4268
4269 if actual_idx < 0 || actual_idx >= len {
4270 JValue::Null
4271 } else {
4272 arr[actual_idx as usize].clone()
4273 }
4274 }
4275 _ => JValue::Null,
4276 })
4277 }
4278 }
4279
4280 fn evaluate_binary_op(
4282 &mut self,
4283 op: crate::ast::BinaryOp,
4284 lhs: &AstNode,
4285 rhs: &AstNode,
4286 data: &JValue,
4287 ) -> Result<JValue, EvaluatorError> {
4288 use crate::ast::BinaryOp;
4289
4290 if op == BinaryOp::Coalesce {
4294 return match self.evaluate_internal(lhs, data) {
4296 Ok(value) => {
4297 if matches!(lhs, AstNode::Null) {
4300 Ok(value)
4301 }
4302 else if value.is_null()
4304 && (matches!(lhs, AstNode::Path { .. })
4305 || matches!(lhs, AstNode::String(_))
4306 || matches!(lhs, AstNode::Variable(_)))
4307 {
4308 self.evaluate_internal(rhs, data)
4309 } else {
4310 Ok(value)
4311 }
4312 }
4313 Err(_) => {
4314 self.evaluate_internal(rhs, data)
4316 }
4317 };
4318 }
4319
4320 if op == BinaryOp::Default {
4323 let left = self.evaluate_internal(lhs, data)?;
4324 if self.is_truthy_for_default(&left) {
4325 return Ok(left);
4326 }
4327 return self.evaluate_internal(rhs, data);
4328 }
4329
4330 if op == BinaryOp::ChainPipe {
4334 if let AstNode::Regex { pattern, flags } = rhs {
4336 let lhs_value = self.evaluate_internal(lhs, data)?;
4338 return match lhs_value {
4340 JValue::String(s) => {
4341 let case_insensitive = flags.contains('i');
4343 let regex_pattern = if case_insensitive {
4344 format!("(?i){}", pattern)
4345 } else {
4346 pattern.clone()
4347 };
4348 match regex::Regex::new(®ex_pattern) {
4349 Ok(re) => {
4350 if let Some(m) = re.find(&s) {
4351 let mut result = IndexMap::new();
4353 result.insert(
4354 "match".to_string(),
4355 JValue::string(m.as_str().to_string()),
4356 );
4357 result.insert(
4358 "start".to_string(),
4359 JValue::Number(m.start() as f64),
4360 );
4361 result
4362 .insert("end".to_string(), JValue::Number(m.end() as f64));
4363
4364 let mut groups = Vec::new();
4366 for cap in re.captures_iter(&s).take(1) {
4367 for i in 1..cap.len() {
4368 if let Some(c) = cap.get(i) {
4369 groups.push(JValue::string(c.as_str().to_string()));
4370 }
4371 }
4372 }
4373 if !groups.is_empty() {
4374 result.insert("groups".to_string(), JValue::array(groups));
4375 }
4376
4377 Ok(JValue::object(result))
4378 } else {
4379 Ok(JValue::Null)
4380 }
4381 }
4382 Err(e) => Err(EvaluatorError::EvaluationError(format!(
4383 "Invalid regex: {}",
4384 e
4385 ))),
4386 }
4387 }
4388 JValue::Null => Ok(JValue::Null),
4389 _ => Err(EvaluatorError::TypeError(
4390 "Left side of ~> /regex/ must be a string".to_string(),
4391 )),
4392 };
4393 }
4394
4395 let lhs_value_for_check = self.evaluate_internal(lhs, data)?;
4398 if lhs_value_for_check.is_undefined() || lhs_value_for_check.is_null() {
4399 return Ok(JValue::Undefined);
4400 }
4401
4402 match rhs {
4404 AstNode::Function {
4405 name,
4406 args,
4407 is_builtin,
4408 } => {
4409 let has_placeholder =
4412 args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
4413
4414 if has_placeholder {
4415 let lhs_value = self.evaluate_internal(lhs, data)?;
4417 let mut filled_args = Vec::new();
4418 let mut lhs_used = false;
4419
4420 for arg in args.iter() {
4421 if matches!(arg, AstNode::Placeholder) && !lhs_used {
4422 let temp_name = format!("__pipe_arg_{}", filled_args.len());
4425 self.context.bind(temp_name.clone(), lhs_value.clone());
4426 filled_args.push(AstNode::Variable(temp_name));
4427 lhs_used = true;
4428 } else {
4429 filled_args.push(arg.clone());
4430 }
4431 }
4432
4433 let result =
4435 self.evaluate_function_call(name, &filled_args, *is_builtin, data);
4436
4437 for (i, arg) in args.iter().enumerate() {
4439 if matches!(arg, AstNode::Placeholder) {
4440 self.context.unbind(&format!("__pipe_arg_{}", i));
4441 }
4442 }
4443
4444 return result.map(|v| self.unwrap_singleton(v));
4446 } else {
4447 let mut all_args = vec![lhs.clone()];
4449 all_args.extend_from_slice(args);
4450 return self
4452 .evaluate_function_call(name, &all_args, *is_builtin, data)
4453 .map(|v| self.unwrap_singleton(v));
4454 }
4455 }
4456 AstNode::Variable(var_name) => {
4457 let all_args = vec![lhs.clone()];
4460 return self
4462 .evaluate_function_call(var_name, &all_args, true, data)
4463 .map(|v| self.unwrap_singleton(v));
4464 }
4465 AstNode::Binary {
4466 op: BinaryOp::ChainPipe,
4467 ..
4468 } => {
4469 let lhs_value = self.evaluate_internal(lhs, data)?;
4472 return self.evaluate_internal(rhs, &lhs_value);
4473 }
4474 AstNode::Transform { .. } => {
4475 let lhs_value = self.evaluate_internal(lhs, data)?;
4478
4479 let saved_binding = self.context.lookup("$").cloned();
4481 self.context.bind("$".to_string(), lhs_value.clone());
4482
4483 let result = self.evaluate_internal(rhs, data);
4484
4485 if let Some(saved) = saved_binding {
4487 self.context.bind("$".to_string(), saved);
4488 } else {
4489 self.context.unbind("$");
4490 }
4491
4492 return result.map(|v| self.unwrap_singleton(v));
4494 }
4495 AstNode::Lambda {
4496 params,
4497 body,
4498 signature,
4499 thunk,
4500 } => {
4501 let lhs_value = self.evaluate_internal(lhs, data)?;
4503 return self
4505 .invoke_lambda(params, body, signature.as_ref(), &[lhs_value], data, *thunk)
4506 .map(|v| self.unwrap_singleton(v));
4507 }
4508 AstNode::Path { steps } => {
4509 if let Some(first_step) = steps.first() {
4512 match &first_step.node {
4513 AstNode::Function {
4514 name,
4515 args,
4516 is_builtin,
4517 } => {
4518 let mut all_args = vec![lhs.clone()];
4520 all_args.extend_from_slice(args);
4521
4522 let mut result = self.evaluate_function_call(
4524 name,
4525 &all_args,
4526 *is_builtin,
4527 data,
4528 )?;
4529
4530 for stage in &first_step.stages {
4532 match stage {
4533 Stage::Filter(filter_expr) => {
4534 result = self.evaluate_predicate_as_stage(
4535 &result,
4536 filter_expr,
4537 )?;
4538 }
4539 }
4540 }
4541
4542 if steps.len() > 1 {
4544 let remaining_path = AstNode::Path {
4545 steps: steps[1..].to_vec(),
4546 };
4547 result = self.evaluate_internal(&remaining_path, &result)?;
4548 }
4549
4550 if !first_step.stages.is_empty() || steps.len() > 1 {
4553 return Ok(result);
4554 } else {
4555 return Ok(self.unwrap_singleton(result));
4556 }
4557 }
4558 AstNode::Variable(var_name) => {
4559 let all_args = vec![lhs.clone()];
4561 let mut result =
4562 self.evaluate_function_call(var_name, &all_args, true, data)?;
4563
4564 for stage in &first_step.stages {
4566 match stage {
4567 Stage::Filter(filter_expr) => {
4568 result = self.evaluate_predicate_as_stage(
4569 &result,
4570 filter_expr,
4571 )?;
4572 }
4573 }
4574 }
4575
4576 if steps.len() > 1 {
4578 let remaining_path = AstNode::Path {
4579 steps: steps[1..].to_vec(),
4580 };
4581 result = self.evaluate_internal(&remaining_path, &result)?;
4582 }
4583
4584 if !first_step.stages.is_empty() || steps.len() > 1 {
4587 return Ok(result);
4588 } else {
4589 return Ok(self.unwrap_singleton(result));
4590 }
4591 }
4592 _ => {
4593 let lhs_value = self.evaluate_internal(lhs, data)?;
4595 return self
4596 .evaluate_internal(rhs, &lhs_value)
4597 .map(|v| self.unwrap_singleton(v));
4598 }
4599 }
4600 }
4601
4602 let lhs_value = self.evaluate_internal(lhs, data)?;
4604 return self
4605 .evaluate_internal(rhs, &lhs_value)
4606 .map(|v| self.unwrap_singleton(v));
4607 }
4608 _ => {
4609 return Err(EvaluatorError::TypeError(
4610 "Right side of ~> must be a function call or function reference"
4611 .to_string(),
4612 ));
4613 }
4614 }
4615 }
4616
4617 if op == BinaryOp::ColonEqual {
4619 let var_name = match lhs {
4621 AstNode::Variable(name) => name.clone(),
4622 _ => {
4623 return Err(EvaluatorError::TypeError(
4624 "Left side of := must be a variable".to_string(),
4625 ))
4626 }
4627 };
4628
4629 if let AstNode::Lambda {
4631 params,
4632 body,
4633 signature,
4634 thunk,
4635 } = rhs
4636 {
4637 let captured_env = self.capture_environment_for(body, params);
4640 let compiled_body = if !thunk {
4641 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
4642 try_compile_expr_with_allowed_vars(body, &var_refs)
4643 } else {
4644 None
4645 };
4646 let stored_lambda = StoredLambda {
4647 params: params.clone(),
4648 body: (**body).clone(),
4649 compiled_body,
4650 signature: signature.clone(),
4651 captured_env,
4652 captured_data: Some(data.clone()),
4653 thunk: *thunk,
4654 };
4655 let lambda_params = stored_lambda.params.clone();
4656 let lambda_sig = stored_lambda.signature.clone();
4657 self.context.bind_lambda(var_name.clone(), stored_lambda);
4658
4659 let lambda_repr = JValue::lambda(
4661 var_name.as_str(),
4662 lambda_params,
4663 Some(var_name.clone()),
4664 lambda_sig,
4665 );
4666 return Ok(lambda_repr);
4667 }
4668
4669 if let AstNode::Binary {
4675 op: BinaryOp::ChainPipe,
4676 lhs: chain_lhs,
4677 rhs: chain_rhs,
4678 } = rhs
4679 {
4680 let is_function_composition = match chain_lhs.as_ref() {
4683 AstNode::Variable(name)
4685 if self.is_builtin_function(name)
4686 || self.context.lookup_lambda(name).is_some() =>
4687 {
4688 true
4689 }
4690 AstNode::Binary {
4692 op: BinaryOp::ChainPipe,
4693 ..
4694 } => true,
4695 AstNode::Function { args, .. }
4698 if args.iter().any(|a| matches!(a, AstNode::Placeholder)) =>
4699 {
4700 true
4701 }
4702 _ => false,
4704 };
4705
4706 if is_function_composition {
4707 let param_name = "$".to_string();
4711
4712 let first_pipe = AstNode::Binary {
4714 op: BinaryOp::ChainPipe,
4715 lhs: Box::new(AstNode::Variable(param_name.clone())),
4716 rhs: chain_lhs.clone(),
4717 };
4718
4719 let composed_body = AstNode::Binary {
4721 op: BinaryOp::ChainPipe,
4722 lhs: Box::new(first_pipe),
4723 rhs: chain_rhs.clone(),
4724 };
4725
4726 let stored_lambda = StoredLambda {
4727 params: vec![param_name],
4728 body: composed_body,
4729 compiled_body: None, signature: None,
4731 captured_env: self.capture_current_environment(),
4732 captured_data: Some(data.clone()),
4733 thunk: false,
4734 };
4735 self.context.bind_lambda(var_name.clone(), stored_lambda);
4736
4737 let lambda_repr = JValue::lambda(
4739 var_name.as_str(),
4740 vec!["$".to_string()],
4741 Some(var_name.clone()),
4742 None::<String>,
4743 );
4744 return Ok(lambda_repr);
4745 }
4746 }
4748
4749 let value = self.evaluate_internal(rhs, data)?;
4751
4752 if let Some(stored) = self.lookup_lambda_from_value(&value) {
4754 self.context.bind_lambda(var_name.clone(), stored);
4755 }
4756
4757 self.context.bind(var_name, value.clone());
4759 return Ok(value);
4760 }
4761
4762 if op == BinaryOp::In {
4765 let left = self.evaluate_internal(lhs, data)?;
4766
4767 if matches!(left, JValue::Array(_)) {
4769 let right_result = self.evaluate_internal(rhs, data);
4771
4772 if let Ok(JValue::Number(_)) = right_result {
4773 return self.array_index(&left, &right_result.unwrap());
4775 } else {
4776 return self.array_filter(lhs, rhs, &left, data);
4779 }
4780 }
4781 }
4782
4783 if op == BinaryOp::And {
4785 let left = self.evaluate_internal(lhs, data)?;
4786 if !self.is_truthy(&left) {
4787 return Ok(JValue::Bool(false));
4789 }
4790 let right = self.evaluate_internal(rhs, data)?;
4791 return Ok(JValue::Bool(self.is_truthy(&right)));
4792 }
4793
4794 if op == BinaryOp::Or {
4795 let left = self.evaluate_internal(lhs, data)?;
4796 if self.is_truthy(&left) {
4797 return Ok(JValue::Bool(true));
4799 }
4800 let right = self.evaluate_internal(rhs, data)?;
4801 return Ok(JValue::Bool(self.is_truthy(&right)));
4802 }
4803
4804 let left_is_explicit_null = matches!(lhs, AstNode::Null);
4806 let right_is_explicit_null = matches!(rhs, AstNode::Null);
4807
4808 let left = self.evaluate_internal(lhs, data)?;
4810 let right = self.evaluate_internal(rhs, data)?;
4811
4812 match op {
4813 BinaryOp::Add => self.add(&left, &right, left_is_explicit_null, right_is_explicit_null),
4814 BinaryOp::Subtract => {
4815 self.subtract(&left, &right, left_is_explicit_null, right_is_explicit_null)
4816 }
4817 BinaryOp::Multiply => {
4818 self.multiply(&left, &right, left_is_explicit_null, right_is_explicit_null)
4819 }
4820 BinaryOp::Divide => {
4821 self.divide(&left, &right, left_is_explicit_null, right_is_explicit_null)
4822 }
4823 BinaryOp::Modulo => {
4824 self.modulo(&left, &right, left_is_explicit_null, right_is_explicit_null)
4825 }
4826
4827 BinaryOp::Equal => Ok(JValue::Bool(self.equals(&left, &right))),
4828 BinaryOp::NotEqual => Ok(JValue::Bool(!self.equals(&left, &right))),
4829 BinaryOp::LessThan => {
4830 self.less_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
4831 }
4832 BinaryOp::LessThanOrEqual => self.less_than_or_equal(
4833 &left,
4834 &right,
4835 left_is_explicit_null,
4836 right_is_explicit_null,
4837 ),
4838 BinaryOp::GreaterThan => {
4839 self.greater_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
4840 }
4841 BinaryOp::GreaterThanOrEqual => self.greater_than_or_equal(
4842 &left,
4843 &right,
4844 left_is_explicit_null,
4845 right_is_explicit_null,
4846 ),
4847
4848 BinaryOp::And | BinaryOp::Or => unreachable!(),
4850
4851 BinaryOp::Concatenate => self.concatenate(&left, &right),
4852 BinaryOp::Range => self.range(&left, &right),
4853 BinaryOp::In => self.in_operator(&left, &right),
4854
4855 BinaryOp::ColonEqual | BinaryOp::Coalesce | BinaryOp::Default | BinaryOp::ChainPipe => {
4857 unreachable!()
4858 }
4859 }
4860 }
4861
4862 fn evaluate_unary_op(
4864 &mut self,
4865 op: crate::ast::UnaryOp,
4866 operand: &AstNode,
4867 data: &JValue,
4868 ) -> Result<JValue, EvaluatorError> {
4869 use crate::ast::UnaryOp;
4870
4871 let value = self.evaluate_internal(operand, data)?;
4872
4873 match op {
4874 UnaryOp::Negate => match value {
4875 JValue::Null => Ok(JValue::Null),
4877 JValue::Number(n) => Ok(JValue::Number(-n)),
4878 _ => Err(EvaluatorError::TypeError(
4879 "D1002: Cannot negate non-number value".to_string(),
4880 )),
4881 },
4882 UnaryOp::Not => Ok(JValue::Bool(!self.is_truthy(&value))),
4883 }
4884 }
4885
4886 fn try_fused_aggregate(
4893 &mut self,
4894 name: &str,
4895 arg: &AstNode,
4896 data: &JValue,
4897 ) -> Result<Option<JValue>, EvaluatorError> {
4898 if !matches!(name, "sum" | "max" | "min" | "average") {
4900 return Ok(None);
4901 }
4902
4903 let AstNode::Path { steps } = arg else {
4905 return Ok(None);
4906 };
4907
4908 if steps.len() != 2 {
4911 return Ok(None);
4912 }
4913
4914 let field_step = &steps[1];
4916 if !field_step.stages.is_empty() {
4917 return Ok(None);
4918 }
4919 let AstNode::Name(extract_field) = &field_step.node else {
4920 return Ok(None);
4921 };
4922
4923 let arr_step = &steps[0];
4925 let AstNode::Name(arr_name) = &arr_step.node else {
4926 return Ok(None);
4927 };
4928
4929 let arr = match data {
4931 JValue::Object(obj) => match obj.get(arr_name) {
4932 Some(JValue::Array(arr)) => arr,
4933 _ => return Ok(None),
4934 },
4935 _ => return Ok(None),
4936 };
4937
4938 let filter_compiled = match arr_step.stages.as_slice() {
4940 [] => None,
4941 [Stage::Filter(pred)] => try_compile_expr(pred),
4942 _ => return Ok(None),
4943 };
4944 if !arr_step.stages.is_empty() && filter_compiled.is_none() {
4946 return Ok(None);
4947 }
4948
4949 let shape = arr.first().and_then(build_shape_cache);
4951
4952 let mut total = 0.0f64;
4954 let mut count = 0usize;
4955 let mut max_val = f64::NEG_INFINITY;
4956 let mut min_val = f64::INFINITY;
4957 let mut has_any = false;
4958
4959 for item in arr.iter() {
4960 if let Some(ref compiled) = filter_compiled {
4962 let result = if let Some(ref s) = shape {
4963 eval_compiled_shaped(compiled, item, None, s)?
4964 } else {
4965 eval_compiled(compiled, item, None)?
4966 };
4967 if !compiled_is_truthy(&result) {
4968 continue;
4969 }
4970 }
4971
4972 let val = match item {
4974 JValue::Object(obj) => match obj.get(extract_field) {
4975 Some(JValue::Number(n)) => *n,
4976 Some(_) | None => continue, },
4978 _ => continue,
4979 };
4980
4981 has_any = true;
4982 match name {
4983 "sum" => total += val,
4984 "max" => max_val = max_val.max(val),
4985 "min" => min_val = min_val.min(val),
4986 "average" => { total += val; count += 1; }
4987 _ => unreachable!(),
4988 }
4989 }
4990
4991 if !has_any {
4992 return Ok(Some(match name {
4993 "sum" => JValue::from(0i64),
4994 "average" | "max" | "min" => JValue::Null,
4995 _ => unreachable!(),
4996 }));
4997 }
4998
4999 Ok(Some(match name {
5000 "sum" => JValue::Number(total),
5001 "max" => JValue::Number(max_val),
5002 "min" => JValue::Number(min_val),
5003 "average" => JValue::Number(total / count as f64),
5004 _ => unreachable!(),
5005 }))
5006 }
5007
5008 fn evaluate_function_call(
5010 &mut self,
5011 name: &str,
5012 args: &[AstNode],
5013 is_builtin: bool,
5014 data: &JValue,
5015 ) -> Result<JValue, EvaluatorError> {
5016 use crate::functions;
5017
5018 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
5020 if has_placeholder {
5021 return self.create_partial_application(name, args, is_builtin, data);
5022 }
5023
5024 if let Some(value) = self.context.lookup(name).cloned() {
5031 if let Some(stored_lambda) = self.lookup_lambda_from_value(&value) {
5032 let mut evaluated_args = Vec::with_capacity(args.len());
5033 for arg in args {
5034 evaluated_args.push(self.evaluate_internal(arg, data)?);
5035 }
5036 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5037 }
5038 if let JValue::Builtin { name: builtin_name } = &value {
5039 let mut evaluated_args = Vec::with_capacity(args.len());
5041 for arg in args {
5042 evaluated_args.push(self.evaluate_internal(arg, data)?);
5043 }
5044 return self.call_builtin_with_values(builtin_name, &evaluated_args);
5045 }
5046 }
5047
5048 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
5051 let mut evaluated_args = Vec::with_capacity(args.len());
5052 for arg in args {
5053 evaluated_args.push(self.evaluate_internal(arg, data)?);
5054 }
5055 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5056 }
5057
5058 if !is_builtin && name != "__lambda__" {
5061 return Err(EvaluatorError::ReferenceError(format!(
5062 "Unknown function: {}",
5063 name
5064 )));
5065 }
5066
5067 if name == "exists" && args.len() == 1 {
5070 let arg = &args[0];
5071
5072 if matches!(arg, AstNode::Null) {
5074 return Ok(JValue::Bool(true)); }
5076
5077 if let AstNode::Variable(var_name) = arg {
5079 if self.is_builtin_function(var_name) {
5080 return Ok(JValue::Bool(true)); }
5082
5083 if self.context.lookup_lambda(var_name).is_some() {
5085 return Ok(JValue::Bool(true)); }
5087
5088 if let Some(val) = self.context.lookup(var_name) {
5090 if val.is_undefined() {
5092 return Ok(JValue::Bool(false));
5093 }
5094 return Ok(JValue::Bool(true)); } else {
5096 return Ok(JValue::Bool(false)); }
5098 }
5099
5100 let value = self.evaluate_internal(arg, data)?;
5102 return Ok(JValue::Bool(
5103 !value.is_null() && !value.is_undefined(),
5104 ));
5105 }
5106
5107 for arg in args {
5110 if let AstNode::Variable(var_name) = arg {
5112 if !var_name.is_empty()
5114 && !self.is_builtin_function(var_name)
5115 && self.context.lookup(var_name).is_none()
5116 {
5117 if propagates_undefined(name) {
5119 return Ok(JValue::Null); }
5121 }
5122 }
5123 if let AstNode::Name(field_name) = arg {
5125 let field_exists =
5126 matches!(data, JValue::Object(obj) if obj.contains_key(field_name));
5127 if !field_exists && propagates_undefined(name) {
5128 return Ok(JValue::Null);
5129 }
5130 }
5131 if let AstNode::Path { steps } = arg {
5136 if let Ok(JValue::Null) = self.evaluate_internal(arg, data) {
5143 if steps.len() == 1 {
5146 let field_name = match &steps[0].node {
5148 AstNode::Name(n) => Some(n.as_str()),
5149 AstNode::String(s) => Some(s.as_str()),
5150 _ => None,
5151 };
5152 if let Some(field) = field_name {
5153 match data {
5154 JValue::Object(obj) => {
5155 if !obj.contains_key(field) {
5156 if propagates_undefined(name) {
5158 return Ok(JValue::Null);
5159 }
5160 }
5161 }
5163 JValue::Null => {
5164 if propagates_undefined(name) {
5166 return Ok(JValue::Null);
5167 }
5168 }
5169 _ => {}
5170 }
5171 }
5172 }
5173 else if steps.len() > 1 {
5175 let mut current = data;
5177 let mut failed_due_to_missing_field = false;
5178
5179 for (i, step) in steps.iter().enumerate() {
5180 if let AstNode::Name(field_name) = &step.node {
5181 match current {
5182 JValue::Object(obj) => {
5183 if let Some(val) = obj.get(field_name) {
5184 current = val;
5185 } else {
5186 failed_due_to_missing_field = true;
5188 break;
5189 }
5190 }
5191 JValue::Array(_) => {
5192 break;
5194 }
5195 JValue::Null => {
5196 if i > 0 {
5198 failed_due_to_missing_field = false;
5200 }
5201 break;
5202 }
5203 _ => break,
5204 }
5205 }
5206 }
5207
5208 if failed_due_to_missing_field && propagates_undefined(name) {
5209 return Ok(JValue::Null);
5210 }
5211 }
5212 }
5213 }
5214 }
5215
5216 if args.len() == 1 {
5219 if let Some(result) = self.try_fused_aggregate(name, &args[0], data)? {
5220 return Ok(result);
5221 }
5222 }
5223
5224 let mut evaluated_args = Vec::with_capacity(args.len());
5225 for arg in args {
5226 evaluated_args.push(self.evaluate_internal(arg, data)?);
5227 }
5228
5229 let context_functions_zero_arg = ["string", "number", "boolean", "uppercase", "lowercase"];
5234 let context_functions_missing_first = [
5235 "substringBefore",
5236 "substringAfter",
5237 "contains",
5238 "split",
5239 "replace",
5240 ];
5241
5242 if evaluated_args.is_empty() && context_functions_zero_arg.contains(&name) {
5243 evaluated_args.push(data.clone());
5245 } else if evaluated_args.len() == 1 && context_functions_missing_first.contains(&name) {
5246 if matches!(data, JValue::String(_)) {
5250 evaluated_args.insert(0, data.clone());
5251 }
5252 }
5253
5254 if name == "string"
5257 && args.is_empty()
5258 && !evaluated_args.is_empty()
5259 && evaluated_args[0].is_null()
5260 {
5261 return Ok(JValue::Null);
5263 }
5264
5265 match name {
5266 "string" => {
5267 if evaluated_args.len() > 2 {
5268 return Err(EvaluatorError::EvaluationError(
5269 "string() takes at most 2 arguments".to_string(),
5270 ));
5271 }
5272
5273 let prettify = if evaluated_args.len() == 2 {
5274 match &evaluated_args[1] {
5275 JValue::Bool(b) => Some(*b),
5276 _ => {
5277 return Err(EvaluatorError::TypeError(
5278 "string() prettify parameter must be a boolean".to_string(),
5279 ))
5280 }
5281 }
5282 } else {
5283 None
5284 };
5285
5286 Ok(functions::string::string(&evaluated_args[0], prettify)?)
5287 }
5288 "length" => {
5289 if evaluated_args.len() != 1 {
5290 return Err(EvaluatorError::EvaluationError(
5291 "length() requires exactly 1 argument".to_string(),
5292 ));
5293 }
5294 match &evaluated_args[0] {
5295 JValue::String(s) => Ok(functions::string::length(s)?),
5296 _ => Err(EvaluatorError::TypeError(
5297 "T0410: Argument 1 of function length does not match function signature"
5298 .to_string(),
5299 )),
5300 }
5301 }
5302 "uppercase" => {
5303 if evaluated_args.len() != 1 {
5304 return Err(EvaluatorError::EvaluationError(
5305 "uppercase() requires exactly 1 argument".to_string(),
5306 ));
5307 }
5308 match &evaluated_args[0] {
5309 JValue::String(s) => Ok(functions::string::uppercase(s)?),
5310 _ => Err(EvaluatorError::TypeError(
5311 "T0410: Argument 1 of function uppercase does not match function signature"
5312 .to_string(),
5313 )),
5314 }
5315 }
5316 "lowercase" => {
5317 if evaluated_args.len() != 1 {
5318 return Err(EvaluatorError::EvaluationError(
5319 "lowercase() requires exactly 1 argument".to_string(),
5320 ));
5321 }
5322 match &evaluated_args[0] {
5323 JValue::String(s) => Ok(functions::string::lowercase(s)?),
5324 _ => Err(EvaluatorError::TypeError(
5325 "T0410: Argument 1 of function lowercase does not match function signature"
5326 .to_string(),
5327 )),
5328 }
5329 }
5330 "number" => {
5331 if evaluated_args.is_empty() {
5332 return Err(EvaluatorError::EvaluationError(
5333 "number() requires at least 1 argument".to_string(),
5334 ));
5335 }
5336 if evaluated_args.len() > 1 {
5337 return Err(EvaluatorError::TypeError(
5338 "T0410: Argument 2 of function number does not match function signature"
5339 .to_string(),
5340 ));
5341 }
5342 Ok(functions::numeric::number(&evaluated_args[0])?)
5343 }
5344 "sum" => {
5345 if evaluated_args.len() != 1 {
5346 return Err(EvaluatorError::EvaluationError(
5347 "sum() requires exactly 1 argument".to_string(),
5348 ));
5349 }
5350 if evaluated_args[0].is_undefined() {
5352 return Ok(JValue::Undefined);
5353 }
5354 match &evaluated_args[0] {
5355 JValue::Null => Ok(JValue::Null),
5356 JValue::Array(arr) => {
5357 Ok(aggregation::sum(arr)?)
5359 }
5360 JValue::Number(n) => Ok(JValue::Number(*n)),
5362 other => Ok(functions::numeric::sum(&[other.clone()])?),
5363 }
5364 }
5365 "count" => {
5366 if evaluated_args.len() != 1 {
5367 return Err(EvaluatorError::EvaluationError(
5368 "count() requires exactly 1 argument".to_string(),
5369 ));
5370 }
5371 if evaluated_args[0].is_undefined() {
5373 return Ok(JValue::from(0i64));
5374 }
5375 match &evaluated_args[0] {
5376 JValue::Null => Ok(JValue::from(0i64)), JValue::Array(arr) => Ok(functions::array::count(arr)?),
5378 _ => Ok(JValue::from(1i64)), }
5380 }
5381 "substring" => {
5382 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5383 return Err(EvaluatorError::EvaluationError(
5384 "substring() requires 2 or 3 arguments".to_string(),
5385 ));
5386 }
5387 match (&evaluated_args[0], &evaluated_args[1]) {
5388 (JValue::String(s), JValue::Number(start)) => {
5389 let length = if evaluated_args.len() == 3 {
5390 match &evaluated_args[2] {
5391 JValue::Number(l) => Some(*l as i64),
5392 _ => return Err(EvaluatorError::TypeError(
5393 "T0410: Argument 3 of function substring does not match function signature".to_string(),
5394 )),
5395 }
5396 } else {
5397 None
5398 };
5399 Ok(functions::string::substring(s, *start as i64, length)?)
5400 }
5401 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5402 "T0410: Argument 2 of function substring does not match function signature"
5403 .to_string(),
5404 )),
5405 _ => Err(EvaluatorError::TypeError(
5406 "T0410: Argument 1 of function substring does not match function signature"
5407 .to_string(),
5408 )),
5409 }
5410 }
5411 "substringBefore" => {
5412 if evaluated_args.len() != 2 {
5413 return Err(EvaluatorError::TypeError(
5414 "T0411: Context value is not a compatible type with argument 2 of function substringBefore".to_string(),
5415 ));
5416 }
5417 match (&evaluated_args[0], &evaluated_args[1]) {
5418 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_before(s, sep)?),
5419 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5420 "T0410: Argument 2 of function substringBefore does not match function signature".to_string(),
5421 )),
5422 _ => Err(EvaluatorError::TypeError(
5423 "T0410: Argument 1 of function substringBefore does not match function signature".to_string(),
5424 )),
5425 }
5426 }
5427 "substringAfter" => {
5428 if evaluated_args.len() != 2 {
5429 return Err(EvaluatorError::TypeError(
5430 "T0411: Context value is not a compatible type with argument 2 of function substringAfter".to_string(),
5431 ));
5432 }
5433 match (&evaluated_args[0], &evaluated_args[1]) {
5434 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_after(s, sep)?),
5435 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5436 "T0410: Argument 2 of function substringAfter does not match function signature".to_string(),
5437 )),
5438 _ => Err(EvaluatorError::TypeError(
5439 "T0410: Argument 1 of function substringAfter does not match function signature".to_string(),
5440 )),
5441 }
5442 }
5443 "pad" => {
5444 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
5445 return Err(EvaluatorError::EvaluationError(
5446 "pad() requires 2 or 3 arguments".to_string(),
5447 ));
5448 }
5449
5450 let string = match &evaluated_args[0] {
5452 JValue::String(s) => s.clone(),
5453 JValue::Null => return Ok(JValue::Null),
5454 _ => {
5455 return Err(EvaluatorError::TypeError(
5456 "pad() first argument must be a string".to_string(),
5457 ))
5458 }
5459 };
5460
5461 let width = match &evaluated_args.get(1) {
5463 Some(JValue::Number(n)) => *n as i32,
5464 _ => {
5465 return Err(EvaluatorError::TypeError(
5466 "pad() second argument must be a number".to_string(),
5467 ))
5468 }
5469 };
5470
5471 let pad_string = match evaluated_args.get(2) {
5473 Some(JValue::String(s)) if !s.is_empty() => s.clone(),
5474 _ => Rc::from(" "),
5475 };
5476
5477 let abs_width = width.unsigned_abs() as usize;
5478 let char_count = string.chars().count();
5480
5481 if char_count >= abs_width {
5482 return Ok(JValue::string(string));
5484 }
5485
5486 let padding_needed = abs_width - char_count;
5487
5488 let pad_chars: Vec<char> = pad_string.chars().collect();
5489 let mut padding = String::with_capacity(padding_needed);
5490 for i in 0..padding_needed {
5491 padding.push(pad_chars[i % pad_chars.len()]);
5492 }
5493
5494 let result = if width < 0 {
5495 format!("{}{}", padding, string)
5497 } else {
5498 format!("{}{}", string, padding)
5500 };
5501
5502 Ok(JValue::string(result))
5503 }
5504
5505 "trim" => {
5506 if evaluated_args.is_empty() {
5507 return Ok(JValue::Null); }
5509 if evaluated_args.len() != 1 {
5510 return Err(EvaluatorError::EvaluationError(
5511 "trim() requires at most 1 argument".to_string(),
5512 ));
5513 }
5514 match &evaluated_args[0] {
5515 JValue::Null => Ok(JValue::Null),
5516 JValue::String(s) => Ok(functions::string::trim(s)?),
5517 _ => Err(EvaluatorError::TypeError(
5518 "trim() requires a string argument".to_string(),
5519 )),
5520 }
5521 }
5522 "contains" => {
5523 if evaluated_args.len() != 2 {
5524 return Err(EvaluatorError::EvaluationError(
5525 "contains() requires exactly 2 arguments".to_string(),
5526 ));
5527 }
5528 if evaluated_args[0].is_null() {
5529 return Ok(JValue::Null);
5530 }
5531 match &evaluated_args[0] {
5532 JValue::String(s) => Ok(functions::string::contains(s, &evaluated_args[1])?),
5533 _ => Err(EvaluatorError::TypeError(
5534 "contains() requires a string as the first argument".to_string(),
5535 )),
5536 }
5537 }
5538 "split" => {
5539 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5540 return Err(EvaluatorError::EvaluationError(
5541 "split() requires 2 or 3 arguments".to_string(),
5542 ));
5543 }
5544 if evaluated_args[0].is_null() {
5545 return Ok(JValue::Null);
5546 }
5547 match &evaluated_args[0] {
5548 JValue::String(s) => {
5549 let limit = if evaluated_args.len() == 3 {
5550 match &evaluated_args[2] {
5551 JValue::Number(n) => {
5552 let f = *n;
5553 if f < 0.0 {
5555 return Err(EvaluatorError::EvaluationError(
5556 "D3020: Third argument of split function must be a positive number".to_string(),
5557 ));
5558 }
5559 Some(f.floor() as usize)
5561 }
5562 _ => {
5563 return Err(EvaluatorError::TypeError(
5564 "split() limit must be a number".to_string(),
5565 ))
5566 }
5567 }
5568 } else {
5569 None
5570 };
5571 Ok(functions::string::split(s, &evaluated_args[1], limit)?)
5572 }
5573 _ => Err(EvaluatorError::TypeError(
5574 "split() requires a string as the first argument".to_string(),
5575 )),
5576 }
5577 }
5578 "join" => {
5579 if evaluated_args.is_empty() {
5582 return Err(EvaluatorError::TypeError(
5583 "T0410: Argument 1 of function $join does not match function signature"
5584 .to_string(),
5585 ));
5586 }
5587 if evaluated_args[0].is_null() {
5588 return Ok(JValue::Null);
5589 }
5590
5591 use crate::signature::Signature;
5594
5595 let signature = Signature::parse("<a<s>s?:s>").map_err(|e| {
5596 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
5597 })?;
5598
5599 let coerced_args = match signature.validate_and_coerce(&evaluated_args) {
5600 Ok(args) => args,
5601 Err(crate::signature::SignatureError::UndefinedArgument) => {
5602 let sig_first_arg = Signature::parse("<a<s>:a<s>>").map_err(|e| {
5605 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
5606 })?;
5607
5608 match sig_first_arg.validate_and_coerce(&evaluated_args[0..1]) {
5609 Ok(args) => args,
5610 Err(crate::signature::SignatureError::ArrayTypeMismatch {
5611 index,
5612 expected,
5613 }) => {
5614 return Err(EvaluatorError::TypeError(format!(
5615 "T0412: Argument {} of function $join must be an array of {}",
5616 index, expected
5617 )));
5618 }
5619 Err(e) => {
5620 return Err(EvaluatorError::TypeError(format!(
5621 "Signature validation failed: {}",
5622 e
5623 )));
5624 }
5625 }
5626 }
5627 Err(crate::signature::SignatureError::ArgumentTypeMismatch {
5628 index,
5629 expected,
5630 }) => {
5631 return Err(EvaluatorError::TypeError(
5632 format!("T0410: Argument {} of function $join does not match function signature (expected {})", index, expected)
5633 ));
5634 }
5635 Err(crate::signature::SignatureError::ArrayTypeMismatch {
5636 index,
5637 expected,
5638 }) => {
5639 return Err(EvaluatorError::TypeError(format!(
5640 "T0412: Argument {} of function $join must be an array of {}",
5641 index, expected
5642 )));
5643 }
5644 Err(e) => {
5645 return Err(EvaluatorError::TypeError(format!(
5646 "Signature validation failed: {}",
5647 e
5648 )));
5649 }
5650 };
5651
5652 match &coerced_args[0] {
5654 JValue::Array(arr) => {
5655 let separator = if coerced_args.len() == 2 {
5656 match &coerced_args[1] {
5657 JValue::String(s) => Some(&**s),
5658 JValue::Null => None, _ => None, }
5661 } else {
5662 None };
5664 Ok(functions::string::join(arr, separator)?)
5665 }
5666 JValue::Null => Ok(JValue::Null),
5667 _ => unreachable!("Signature validation should ensure array type"),
5668 }
5669 }
5670 "replace" => {
5671 if evaluated_args.len() < 3 || evaluated_args.len() > 4 {
5672 return Err(EvaluatorError::EvaluationError(
5673 "replace() requires 3 or 4 arguments".to_string(),
5674 ));
5675 }
5676 if evaluated_args[0].is_null() {
5677 return Ok(JValue::Null);
5678 }
5679
5680 let replacement_is_lambda = matches!(
5682 evaluated_args[2],
5683 JValue::Lambda { .. } | JValue::Builtin { .. }
5684 );
5685
5686 if replacement_is_lambda {
5687 return self.replace_with_lambda(
5689 &evaluated_args[0],
5690 &evaluated_args[1],
5691 &evaluated_args[2],
5692 if evaluated_args.len() == 4 {
5693 Some(&evaluated_args[3])
5694 } else {
5695 None
5696 },
5697 data,
5698 );
5699 }
5700
5701 match (&evaluated_args[0], &evaluated_args[2]) {
5703 (JValue::String(s), JValue::String(replacement)) => {
5704 let limit = if evaluated_args.len() == 4 {
5705 match &evaluated_args[3] {
5706 JValue::Number(n) => {
5707 let lim_f64 = *n;
5708 if lim_f64 < 0.0 {
5709 return Err(EvaluatorError::EvaluationError(format!(
5710 "D3011: Limit must be non-negative, got {}",
5711 lim_f64
5712 )));
5713 }
5714 Some(lim_f64 as usize)
5715 }
5716 _ => {
5717 return Err(EvaluatorError::TypeError(
5718 "replace() limit must be a number".to_string(),
5719 ))
5720 }
5721 }
5722 } else {
5723 None
5724 };
5725 Ok(functions::string::replace(
5726 s,
5727 &evaluated_args[1],
5728 replacement,
5729 limit,
5730 )?)
5731 }
5732 _ => Err(EvaluatorError::TypeError(
5733 "replace() requires string arguments".to_string(),
5734 )),
5735 }
5736 }
5737 "match" => {
5738 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
5741 return Err(EvaluatorError::EvaluationError(
5742 "match() requires 1 to 3 arguments".to_string(),
5743 ));
5744 }
5745 if evaluated_args[0].is_null() {
5746 return Ok(JValue::Null);
5747 }
5748
5749 let s = match &evaluated_args[0] {
5750 JValue::String(s) => s.clone(),
5751 _ => {
5752 return Err(EvaluatorError::TypeError(
5753 "match() first argument must be a string".to_string(),
5754 ))
5755 }
5756 };
5757
5758 let limit = if evaluated_args.len() == 3 {
5760 match &evaluated_args[2] {
5761 JValue::Number(n) => Some(*n as usize),
5762 JValue::Null => None,
5763 _ => {
5764 return Err(EvaluatorError::TypeError(
5765 "match() limit must be a number".to_string(),
5766 ))
5767 }
5768 }
5769 } else {
5770 None
5771 };
5772
5773 let pattern_value = evaluated_args.get(1);
5775 let is_custom_matcher = pattern_value.is_some_and(|val| {
5776 matches!(val, JValue::Lambda { .. } | JValue::Builtin { .. })
5777 });
5778
5779 if is_custom_matcher {
5780 return self.match_with_custom_matcher(&s, &args[1], limit, data);
5783 }
5784
5785 let (pattern, flags) = match pattern_value {
5787 Some(val) => crate::functions::string::extract_regex(val).ok_or_else(|| {
5788 EvaluatorError::TypeError(
5789 "match() second argument must be a regex pattern or matcher function"
5790 .to_string(),
5791 )
5792 })?,
5793 None => (".*".to_string(), "".to_string()),
5794 };
5795
5796 let is_global = flags.contains('g');
5798 let regex_pattern = if flags.contains('i') {
5799 format!("(?i){}", pattern)
5800 } else {
5801 pattern.clone()
5802 };
5803
5804 let re = regex::Regex::new(®ex_pattern).map_err(|e| {
5805 EvaluatorError::EvaluationError(format!("Invalid regex pattern: {}", e))
5806 })?;
5807
5808 let mut results = Vec::new();
5809 let mut count = 0;
5810
5811 for caps in re.captures_iter(&s) {
5812 if let Some(lim) = limit {
5813 if count >= lim {
5814 break;
5815 }
5816 }
5817
5818 let full_match = caps.get(0).unwrap();
5819 let mut match_obj = IndexMap::new();
5820 match_obj.insert(
5821 "match".to_string(),
5822 JValue::string(full_match.as_str().to_string()),
5823 );
5824 match_obj.insert(
5825 "index".to_string(),
5826 JValue::Number(full_match.start() as f64),
5827 );
5828
5829 let mut groups: Vec<JValue> = Vec::new();
5831 for i in 1..caps.len() {
5832 if let Some(group) = caps.get(i) {
5833 groups.push(JValue::string(group.as_str().to_string()));
5834 } else {
5835 groups.push(JValue::Null);
5836 }
5837 }
5838 if !groups.is_empty() {
5839 match_obj.insert("groups".to_string(), JValue::array(groups));
5840 }
5841
5842 results.push(JValue::object(match_obj));
5843 count += 1;
5844
5845 if !is_global {
5847 break;
5848 }
5849 }
5850
5851 if results.is_empty() {
5852 Ok(JValue::Null)
5853 } else if results.len() == 1 && !is_global {
5854 Ok(results.into_iter().next().unwrap())
5856 } else {
5857 Ok(JValue::array(results))
5858 }
5859 }
5860 "max" => {
5861 if evaluated_args.len() != 1 {
5862 return Err(EvaluatorError::EvaluationError(
5863 "max() requires exactly 1 argument".to_string(),
5864 ));
5865 }
5866 if evaluated_args[0].is_undefined() {
5868 return Ok(JValue::Undefined);
5869 }
5870 match &evaluated_args[0] {
5871 JValue::Null => Ok(JValue::Null),
5872 JValue::Array(arr) => {
5873 Ok(aggregation::max(arr)?)
5875 }
5876 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
5878 "max() requires an array or number argument".to_string(),
5879 )),
5880 }
5881 }
5882 "min" => {
5883 if evaluated_args.len() != 1 {
5884 return Err(EvaluatorError::EvaluationError(
5885 "min() requires exactly 1 argument".to_string(),
5886 ));
5887 }
5888 if evaluated_args[0].is_undefined() {
5890 return Ok(JValue::Undefined);
5891 }
5892 match &evaluated_args[0] {
5893 JValue::Null => Ok(JValue::Null),
5894 JValue::Array(arr) => {
5895 Ok(aggregation::min(arr)?)
5897 }
5898 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
5900 "min() requires an array or number argument".to_string(),
5901 )),
5902 }
5903 }
5904 "average" => {
5905 if evaluated_args.len() != 1 {
5906 return Err(EvaluatorError::EvaluationError(
5907 "average() requires exactly 1 argument".to_string(),
5908 ));
5909 }
5910 if evaluated_args[0].is_undefined() {
5912 return Ok(JValue::Undefined);
5913 }
5914 match &evaluated_args[0] {
5915 JValue::Null => Ok(JValue::Null),
5916 JValue::Array(arr) => {
5917 Ok(aggregation::average(arr)?)
5919 }
5920 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
5922 "average() requires an array or number argument".to_string(),
5923 )),
5924 }
5925 }
5926 "abs" => {
5927 if evaluated_args.len() != 1 {
5928 return Err(EvaluatorError::EvaluationError(
5929 "abs() requires exactly 1 argument".to_string(),
5930 ));
5931 }
5932 match &evaluated_args[0] {
5933 JValue::Null => Ok(JValue::Null),
5934 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
5935 _ => Err(EvaluatorError::TypeError(
5936 "abs() requires a number argument".to_string(),
5937 )),
5938 }
5939 }
5940 "floor" => {
5941 if evaluated_args.len() != 1 {
5942 return Err(EvaluatorError::EvaluationError(
5943 "floor() requires exactly 1 argument".to_string(),
5944 ));
5945 }
5946 match &evaluated_args[0] {
5947 JValue::Null => Ok(JValue::Null),
5948 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
5949 _ => Err(EvaluatorError::TypeError(
5950 "floor() requires a number argument".to_string(),
5951 )),
5952 }
5953 }
5954 "ceil" => {
5955 if evaluated_args.len() != 1 {
5956 return Err(EvaluatorError::EvaluationError(
5957 "ceil() requires exactly 1 argument".to_string(),
5958 ));
5959 }
5960 match &evaluated_args[0] {
5961 JValue::Null => Ok(JValue::Null),
5962 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
5963 _ => Err(EvaluatorError::TypeError(
5964 "ceil() requires a number argument".to_string(),
5965 )),
5966 }
5967 }
5968 "round" => {
5969 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
5970 return Err(EvaluatorError::EvaluationError(
5971 "round() requires 1 or 2 arguments".to_string(),
5972 ));
5973 }
5974 match &evaluated_args[0] {
5975 JValue::Null => Ok(JValue::Null),
5976 JValue::Number(n) => {
5977 let precision = if evaluated_args.len() == 2 {
5978 match &evaluated_args[1] {
5979 JValue::Number(p) => Some(*p as i32),
5980 _ => {
5981 return Err(EvaluatorError::TypeError(
5982 "round() precision must be a number".to_string(),
5983 ))
5984 }
5985 }
5986 } else {
5987 None
5988 };
5989 Ok(functions::numeric::round(*n, precision)?)
5990 }
5991 _ => Err(EvaluatorError::TypeError(
5992 "round() requires a number argument".to_string(),
5993 )),
5994 }
5995 }
5996 "sqrt" => {
5997 if evaluated_args.len() != 1 {
5998 return Err(EvaluatorError::EvaluationError(
5999 "sqrt() requires exactly 1 argument".to_string(),
6000 ));
6001 }
6002 match &evaluated_args[0] {
6003 JValue::Null => Ok(JValue::Null),
6004 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
6005 _ => Err(EvaluatorError::TypeError(
6006 "sqrt() requires a number argument".to_string(),
6007 )),
6008 }
6009 }
6010 "power" => {
6011 if evaluated_args.len() != 2 {
6012 return Err(EvaluatorError::EvaluationError(
6013 "power() requires exactly 2 arguments".to_string(),
6014 ));
6015 }
6016 if evaluated_args[0].is_null() {
6017 return Ok(JValue::Null);
6018 }
6019 match (&evaluated_args[0], &evaluated_args[1]) {
6020 (JValue::Number(base), JValue::Number(exp)) => {
6021 Ok(functions::numeric::power(*base, *exp)?)
6022 }
6023 _ => Err(EvaluatorError::TypeError(
6024 "power() requires number arguments".to_string(),
6025 )),
6026 }
6027 }
6028 "formatNumber" => {
6029 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
6030 return Err(EvaluatorError::EvaluationError(
6031 "formatNumber() requires 2 or 3 arguments".to_string(),
6032 ));
6033 }
6034 if evaluated_args[0].is_null() {
6035 return Ok(JValue::Null);
6036 }
6037 match (&evaluated_args[0], &evaluated_args[1]) {
6038 (JValue::Number(num), JValue::String(picture)) => {
6039 let options = if evaluated_args.len() == 3 {
6040 Some(&evaluated_args[2])
6041 } else {
6042 None
6043 };
6044 Ok(functions::numeric::format_number(*num, picture, options)?)
6045 }
6046 _ => Err(EvaluatorError::TypeError(
6047 "formatNumber() requires a number and a string".to_string(),
6048 )),
6049 }
6050 }
6051 "formatBase" => {
6052 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6053 return Err(EvaluatorError::EvaluationError(
6054 "formatBase() requires 1 or 2 arguments".to_string(),
6055 ));
6056 }
6057 if evaluated_args[0].is_null() {
6059 return Ok(JValue::Null);
6060 }
6061 match &evaluated_args[0] {
6062 JValue::Number(num) => {
6063 let radix = if evaluated_args.len() == 2 {
6064 match &evaluated_args[1] {
6065 JValue::Number(r) => Some(r.trunc() as i64),
6066 _ => {
6067 return Err(EvaluatorError::TypeError(
6068 "formatBase() radix must be a number".to_string(),
6069 ))
6070 }
6071 }
6072 } else {
6073 None
6074 };
6075 Ok(functions::numeric::format_base(*num, radix)?)
6076 }
6077 _ => Err(EvaluatorError::TypeError(
6078 "formatBase() requires a number".to_string(),
6079 )),
6080 }
6081 }
6082 "append" => {
6083 if evaluated_args.len() != 2 {
6084 return Err(EvaluatorError::EvaluationError(
6085 "append() requires exactly 2 arguments".to_string(),
6086 ));
6087 }
6088 let first = &evaluated_args[0];
6090 let second = &evaluated_args[1];
6091
6092 if second.is_null() {
6094 return Ok(first.clone());
6095 }
6096
6097 if first.is_null() {
6099 return Ok(second.clone());
6100 }
6101
6102 let arr = match first {
6104 JValue::Array(a) => a.to_vec(),
6105 other => vec![other.clone()], };
6107
6108 Ok(functions::array::append(&arr, second)?)
6109 }
6110 "reverse" => {
6111 if evaluated_args.len() != 1 {
6112 return Err(EvaluatorError::EvaluationError(
6113 "reverse() requires exactly 1 argument".to_string(),
6114 ));
6115 }
6116 match &evaluated_args[0] {
6117 JValue::Null => Ok(JValue::Null), JValue::Array(arr) => Ok(functions::array::reverse(arr)?),
6119 _ => Err(EvaluatorError::TypeError(
6120 "reverse() requires an array argument".to_string(),
6121 )),
6122 }
6123 }
6124 "shuffle" => {
6125 if evaluated_args.len() != 1 {
6126 return Err(EvaluatorError::EvaluationError(
6127 "shuffle() requires exactly 1 argument".to_string(),
6128 ));
6129 }
6130 if evaluated_args[0].is_null() {
6131 return Ok(JValue::Null);
6132 }
6133 match &evaluated_args[0] {
6134 JValue::Array(arr) => Ok(functions::array::shuffle(arr)?),
6135 _ => Err(EvaluatorError::TypeError(
6136 "shuffle() requires an array argument".to_string(),
6137 )),
6138 }
6139 }
6140
6141 "sift" => {
6142 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6144 return Err(EvaluatorError::EvaluationError(
6145 "sift() requires 1 or 2 arguments".to_string(),
6146 ));
6147 }
6148
6149 let func_arg = if evaluated_args.len() == 1 {
6151 &args[0]
6152 } else {
6153 &args[1]
6154 };
6155
6156 let param_count = self.get_callback_param_count(func_arg);
6158
6159 let sift_object = |evaluator: &mut Self,
6161 obj: &IndexMap<String, JValue>,
6162 func_node: &AstNode,
6163 context_data: &JValue,
6164 param_count: usize|
6165 -> Result<JValue, EvaluatorError> {
6166 let obj_value = if param_count >= 3 {
6168 Some(JValue::object(obj.clone()))
6169 } else {
6170 None
6171 };
6172
6173 let mut result = IndexMap::new();
6174 for (key, value) in obj.iter() {
6175 let call_args = match param_count {
6177 1 => vec![value.clone()],
6178 2 => vec![value.clone(), JValue::string(key.clone())],
6179 _ => vec![
6180 value.clone(),
6181 JValue::string(key.clone()),
6182 obj_value.as_ref().unwrap().clone(),
6183 ],
6184 };
6185
6186 let pred_result =
6187 evaluator.apply_function(func_node, &call_args, context_data)?;
6188 if evaluator.is_truthy(&pred_result) {
6189 result.insert(key.clone(), value.clone());
6190 }
6191 }
6192 if result.is_empty() {
6194 Ok(JValue::Undefined)
6195 } else {
6196 Ok(JValue::object(result))
6197 }
6198 };
6199
6200 if evaluated_args.len() == 1 {
6202 match data {
6204 JValue::Object(o) => sift_object(self, o, &args[0], data, param_count),
6205 JValue::Array(arr) => {
6206 let mut results = Vec::new();
6208 for item in arr.iter() {
6209 if let JValue::Object(o) = item {
6210 let sifted = sift_object(self, o, &args[0], item, param_count)?;
6211 if !sifted.is_undefined() {
6213 results.push(sifted);
6214 }
6215 }
6216 }
6217 Ok(JValue::array(results))
6218 }
6219 JValue::Null => Ok(JValue::Null),
6220 _ => Ok(JValue::Undefined),
6221 }
6222 } else {
6223 match &evaluated_args[0] {
6225 JValue::Object(o) => sift_object(self, o, &args[1], data, param_count),
6226 JValue::Null => Ok(JValue::Null),
6227 _ => Err(EvaluatorError::TypeError(
6228 "sift() first argument must be an object".to_string(),
6229 )),
6230 }
6231 }
6232 }
6233
6234 "zip" => {
6235 if evaluated_args.is_empty() {
6236 return Err(EvaluatorError::EvaluationError(
6237 "zip() requires at least 1 argument".to_string(),
6238 ));
6239 }
6240
6241 let mut arrays: Vec<Vec<JValue>> = Vec::with_capacity(evaluated_args.len());
6244 for arg in &evaluated_args {
6245 match arg {
6246 JValue::Array(arr) => {
6247 if arr.is_empty() {
6248 return Ok(JValue::array(vec![]));
6250 }
6251 arrays.push(arr.to_vec());
6252 }
6253 JValue::Null => {
6254 return Ok(JValue::array(vec![]));
6256 }
6257 other => {
6258 arrays.push(vec![other.clone()]);
6260 }
6261 }
6262 }
6263
6264 if arrays.is_empty() {
6265 return Ok(JValue::array(vec![]));
6266 }
6267
6268 let min_len = arrays.iter().map(|a| a.len()).min().unwrap_or(0);
6270
6271 let mut result = Vec::with_capacity(min_len);
6273 for i in 0..min_len {
6274 let mut tuple = Vec::with_capacity(arrays.len());
6275 for array in &arrays {
6276 tuple.push(array[i].clone());
6277 }
6278 result.push(JValue::array(tuple));
6279 }
6280
6281 Ok(JValue::array(result))
6282 }
6283
6284 "sort" => {
6285 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6286 return Err(EvaluatorError::EvaluationError(
6287 "sort() requires 1 or 2 arguments".to_string(),
6288 ));
6289 }
6290
6291 let array_value = &evaluated_args[0];
6293
6294 if array_value.is_null() {
6296 return Ok(JValue::Null);
6297 }
6298
6299 let mut arr = match array_value {
6300 JValue::Array(arr) => arr.to_vec(),
6301 other => vec![other.clone()],
6302 };
6303
6304 if args.len() == 2 {
6305 self.merge_sort_with_comparator(&mut arr, &args[1], data)?;
6308 Ok(JValue::array(arr))
6309 } else {
6310 Ok(functions::array::sort(&arr)?)
6312 }
6313 }
6314 "distinct" => {
6315 if evaluated_args.len() != 1 {
6316 return Err(EvaluatorError::EvaluationError(
6317 "distinct() requires exactly 1 argument".to_string(),
6318 ));
6319 }
6320 match &evaluated_args[0] {
6321 JValue::Array(arr) => Ok(functions::array::distinct(arr)?),
6322 _ => Err(EvaluatorError::TypeError(
6323 "distinct() requires an array argument".to_string(),
6324 )),
6325 }
6326 }
6327 "exists" => {
6328 if evaluated_args.len() != 1 {
6329 return Err(EvaluatorError::EvaluationError(
6330 "exists() requires exactly 1 argument".to_string(),
6331 ));
6332 }
6333 Ok(functions::array::exists(&evaluated_args[0])?)
6334 }
6335 "keys" => {
6336 if evaluated_args.len() != 1 {
6337 return Err(EvaluatorError::EvaluationError(
6338 "keys() requires exactly 1 argument".to_string(),
6339 ));
6340 }
6341
6342 let unwrap_single = |keys: Vec<JValue>| -> JValue {
6344 if keys.len() == 1 {
6345 keys.into_iter().next().unwrap()
6346 } else {
6347 JValue::array(keys)
6348 }
6349 };
6350
6351 match &evaluated_args[0] {
6352 JValue::Null => Ok(JValue::Null),
6353 JValue::Lambda { .. } | JValue::Builtin { .. } => Ok(JValue::Null),
6354 JValue::Object(obj) => {
6355 if obj.is_empty() {
6357 Ok(JValue::Null)
6358 } else {
6359 let keys: Vec<JValue> =
6360 obj.keys().map(|k| JValue::string(k.clone())).collect();
6361 Ok(unwrap_single(keys))
6362 }
6363 }
6364 JValue::Array(arr) => {
6365 let mut all_keys = Vec::new();
6367 for item in arr.iter() {
6368 if matches!(item, JValue::Lambda { .. } | JValue::Builtin { .. }) {
6370 continue;
6371 }
6372 if let JValue::Object(obj) = item {
6373 for key in obj.keys() {
6374 if !all_keys.contains(&JValue::string(key.clone())) {
6375 all_keys.push(JValue::string(key.clone()));
6376 }
6377 }
6378 }
6379 }
6380 if all_keys.is_empty() {
6381 Ok(JValue::Null)
6382 } else {
6383 Ok(unwrap_single(all_keys))
6384 }
6385 }
6386 _ => Ok(JValue::Null),
6388 }
6389 }
6390 "lookup" => {
6391 if evaluated_args.len() != 2 {
6392 return Err(EvaluatorError::EvaluationError(
6393 "lookup() requires exactly 2 arguments".to_string(),
6394 ));
6395 }
6396 if evaluated_args[0].is_null() {
6397 return Ok(JValue::Null);
6398 }
6399
6400 let key = match &evaluated_args[1] {
6401 JValue::String(k) => &**k,
6402 _ => {
6403 return Err(EvaluatorError::TypeError(
6404 "lookup() requires a string key".to_string(),
6405 ))
6406 }
6407 };
6408
6409 fn lookup_recursive(val: &JValue, key: &str) -> Vec<JValue> {
6411 match val {
6412 JValue::Array(arr) => {
6413 let mut results = Vec::new();
6414 for item in arr.iter() {
6415 let nested = lookup_recursive(item, key);
6416 results.extend(nested.iter().cloned());
6417 }
6418 results
6419 }
6420 JValue::Object(obj) => {
6421 if let Some(v) = obj.get(key) {
6422 vec![v.clone()]
6423 } else {
6424 vec![]
6425 }
6426 }
6427 _ => vec![],
6428 }
6429 }
6430
6431 let results = lookup_recursive(&evaluated_args[0], key);
6432 if results.is_empty() {
6433 Ok(JValue::Null)
6434 } else if results.len() == 1 {
6435 Ok(results[0].clone())
6436 } else {
6437 Ok(JValue::array(results))
6438 }
6439 }
6440 "spread" => {
6441 if evaluated_args.len() != 1 {
6442 return Err(EvaluatorError::EvaluationError(
6443 "spread() requires exactly 1 argument".to_string(),
6444 ));
6445 }
6446 match &evaluated_args[0] {
6447 JValue::Null => Ok(JValue::Null),
6448 JValue::Lambda { .. } | JValue::Builtin { .. } => Ok(JValue::Undefined),
6449 JValue::Object(obj) => Ok(functions::object::spread(obj)?),
6450 JValue::Array(arr) => {
6451 let mut result = Vec::new();
6453 for item in arr.iter() {
6454 match item {
6455 JValue::Lambda { .. } | JValue::Builtin { .. } => {
6456 continue;
6458 }
6459 JValue::Object(obj) => {
6460 let spread_result = functions::object::spread(obj)?;
6461 if let JValue::Array(spread_items) = spread_result {
6462 result.extend(spread_items.iter().cloned());
6463 } else {
6464 result.push(spread_result);
6465 }
6466 }
6467 other => result.push(other.clone()),
6469 }
6470 }
6471 Ok(JValue::array(result))
6472 }
6473 other => Ok(other.clone()),
6475 }
6476 }
6477 "merge" => {
6478 if evaluated_args.is_empty() {
6479 return Err(EvaluatorError::EvaluationError(
6480 "merge() requires at least 1 argument".to_string(),
6481 ));
6482 }
6483 if evaluated_args.len() == 1 {
6486 match &evaluated_args[0] {
6487 JValue::Array(arr) => Ok(functions::object::merge(arr)?),
6488 JValue::Null => Ok(JValue::Null), JValue::Object(_) => {
6490 Ok(evaluated_args[0].clone())
6492 }
6493 _ => Err(EvaluatorError::TypeError(
6494 "merge() requires objects or an array of objects".to_string(),
6495 )),
6496 }
6497 } else {
6498 Ok(functions::object::merge(&evaluated_args)?)
6499 }
6500 }
6501
6502 "map" => {
6503 if args.len() != 2 {
6504 return Err(EvaluatorError::EvaluationError(
6505 "map() requires exactly 2 arguments".to_string(),
6506 ));
6507 }
6508
6509 let array = self.evaluate_internal(&args[0], data)?;
6511
6512 match array {
6513 JValue::Array(arr) => {
6514 let param_count = self.get_callback_param_count(&args[1]);
6516
6517 if param_count == 1 {
6519 if let AstNode::Lambda {
6520 params, body, signature: None, thunk: false,
6521 } = &args[1]
6522 {
6523 let var_refs: Vec<&str> =
6524 params.iter().map(|s| s.as_str()).collect();
6525 if let Some(compiled) =
6526 try_compile_expr_with_allowed_vars(body, &var_refs)
6527 {
6528 let param_name = params[0].as_str();
6529 let mut result = Vec::with_capacity(arr.len());
6530 let mut vars = HashMap::new();
6531 for item in arr.iter() {
6532 vars.insert(param_name, item);
6533 let mapped = eval_compiled(&compiled, data, Some(&vars))?;
6534 if !mapped.is_undefined() {
6535 result.push(mapped);
6536 }
6537 }
6538 return Ok(JValue::array(result));
6539 }
6540 }
6541 if let AstNode::Variable(var_name) = &args[1] {
6543 if let Some(stored) = self.context.lookup_lambda(var_name) {
6544 if let Some(ref ce) = stored.compiled_body.clone() {
6545 let param_name = stored.params[0].clone();
6546 let captured_data = stored.captured_data.clone();
6547 let captured_env_clone = stored.captured_env.clone();
6548 let ce_clone = ce.clone();
6549 if !captured_env_clone
6550 .values()
6551 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
6552 {
6553 let call_data =
6554 captured_data.as_ref().unwrap_or(data);
6555 let mut result = Vec::with_capacity(arr.len());
6556 let mut vars: HashMap<&str, &JValue> =
6557 captured_env_clone
6558 .iter()
6559 .map(|(k, v)| (k.as_str(), v))
6560 .collect();
6561 for item in arr.iter() {
6562 vars.insert(param_name.as_str(), item);
6563 let mapped = eval_compiled(
6564 &ce_clone,
6565 call_data,
6566 Some(&vars),
6567 )?;
6568 if !mapped.is_undefined() {
6569 result.push(mapped);
6570 }
6571 }
6572 return Ok(JValue::array(result));
6573 }
6574 }
6575 }
6576 }
6577 }
6578
6579 let arr_value = if param_count >= 3 {
6581 Some(JValue::Array(arr.clone()))
6582 } else {
6583 None
6584 };
6585
6586 let mut result = Vec::with_capacity(arr.len());
6587 for (index, item) in arr.iter().enumerate() {
6588 let call_args = match param_count {
6590 1 => vec![item.clone()],
6591 2 => vec![item.clone(), JValue::Number(index as f64)],
6592 _ => vec![
6593 item.clone(),
6594 JValue::Number(index as f64),
6595 arr_value.as_ref().unwrap().clone(),
6596 ],
6597 };
6598
6599 let mapped = self.apply_function(&args[1], &call_args, data)?;
6600 if !mapped.is_undefined() {
6603 result.push(mapped);
6604 }
6605 }
6606 Ok(JValue::array(result))
6607 }
6608 JValue::Null => Ok(JValue::Null),
6609 _ => Err(EvaluatorError::TypeError(
6610 "map() first argument must be an array".to_string(),
6611 )),
6612 }
6613 }
6614
6615 "filter" => {
6616 if args.len() != 2 {
6617 return Err(EvaluatorError::EvaluationError(
6618 "filter() requires exactly 2 arguments".to_string(),
6619 ));
6620 }
6621
6622 let array = self.evaluate_internal(&args[0], data)?;
6624
6625 if array.is_undefined() {
6627 return Ok(JValue::Undefined);
6628 }
6629
6630 if array.is_null() {
6632 return Ok(JValue::Undefined);
6633 }
6634
6635 let single_holder;
6639 let (items, was_single_value): (&[JValue], bool) = match &array {
6640 JValue::Array(arr) => (arr.as_slice(), false),
6641 _ => { single_holder = [array]; (&single_holder[..], true) }
6642 };
6643
6644 let param_count = self.get_callback_param_count(&args[1]);
6646
6647 if param_count == 1 {
6649 if let AstNode::Lambda {
6650 params, body, signature: None, thunk: false,
6651 } = &args[1]
6652 {
6653 let var_refs: Vec<&str> =
6654 params.iter().map(|s| s.as_str()).collect();
6655 if let Some(compiled) =
6656 try_compile_expr_with_allowed_vars(body, &var_refs)
6657 {
6658 let param_name = params[0].as_str();
6659 let mut result = Vec::with_capacity(items.len() / 2);
6660 let mut vars = HashMap::new();
6661 for item in items.iter() {
6662 vars.insert(param_name, item);
6663 let pred_result = eval_compiled(&compiled, data, Some(&vars))?;
6664 if compiled_is_truthy(&pred_result) {
6665 result.push(item.clone());
6666 }
6667 }
6668 if was_single_value {
6669 if result.len() == 1 {
6670 return Ok(result.remove(0));
6671 } else if result.is_empty() {
6672 return Ok(JValue::Undefined);
6673 }
6674 }
6675 return Ok(JValue::array(result));
6676 }
6677 }
6678 if let AstNode::Variable(var_name) = &args[1] {
6680 if let Some(stored) = self.context.lookup_lambda(var_name) {
6681 if let Some(ref ce) = stored.compiled_body.clone() {
6682 let param_name = stored.params[0].clone();
6683 let captured_data = stored.captured_data.clone();
6684 let captured_env_clone = stored.captured_env.clone();
6685 let ce_clone = ce.clone();
6686 if !captured_env_clone
6687 .values()
6688 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
6689 {
6690 let call_data = captured_data.as_ref().unwrap_or(data);
6691 let mut result = Vec::with_capacity(items.len() / 2);
6692 let mut vars: HashMap<&str, &JValue> = captured_env_clone
6693 .iter()
6694 .map(|(k, v)| (k.as_str(), v))
6695 .collect();
6696 for item in items.iter() {
6697 vars.insert(param_name.as_str(), item);
6698 let pred_result =
6699 eval_compiled(&ce_clone, call_data, Some(&vars))?;
6700 if compiled_is_truthy(&pred_result) {
6701 result.push(item.clone());
6702 }
6703 }
6704 if was_single_value {
6705 if result.len() == 1 {
6706 return Ok(result.remove(0));
6707 } else if result.is_empty() {
6708 return Ok(JValue::Undefined);
6709 }
6710 }
6711 return Ok(JValue::array(result));
6712 }
6713 }
6714 }
6715 }
6716 }
6717
6718 let arr_value = if param_count >= 3 {
6720 Some(JValue::array(items.to_vec()))
6721 } else {
6722 None
6723 };
6724
6725 let mut result = Vec::with_capacity(items.len() / 2);
6726
6727 for (index, item) in items.iter().enumerate() {
6728 let call_args = match param_count {
6730 1 => vec![item.clone()],
6731 2 => vec![item.clone(), JValue::Number(index as f64)],
6732 _ => vec![
6733 item.clone(),
6734 JValue::Number(index as f64),
6735 arr_value.as_ref().unwrap().clone(),
6736 ],
6737 };
6738
6739 let predicate_result = self.apply_function(&args[1], &call_args, data)?;
6740 if self.is_truthy(&predicate_result) {
6741 result.push(item.clone());
6742 }
6743 }
6744
6745 if was_single_value {
6748 if result.len() == 1 {
6749 return Ok(result.remove(0));
6750 } else if result.is_empty() {
6751 return Ok(JValue::Undefined);
6752 }
6753 }
6754
6755 Ok(JValue::array(result))
6756 }
6757
6758 "reduce" => {
6759 if args.len() < 2 || args.len() > 3 {
6760 return Err(EvaluatorError::EvaluationError(
6761 "reduce() requires 2 or 3 arguments".to_string(),
6762 ));
6763 }
6764
6765 if let AstNode::Lambda { params, .. } = &args[1] {
6767 if params.len() < 2 {
6768 return Err(EvaluatorError::EvaluationError(
6769 "D3050: The second argument of reduce must be a function with at least two arguments".to_string(),
6770 ));
6771 }
6772 } else if let AstNode::Function { name, .. } = &args[1] {
6773 let _ = name; }
6777
6778 let array = self.evaluate_internal(&args[0], data)?;
6780
6781 let single_holder;
6784 let items: &[JValue] = match &array {
6785 JValue::Array(arr) => arr.as_slice(),
6786 JValue::Null => return Ok(JValue::Null),
6787 _ => { single_holder = [array]; &single_holder[..] }
6788 };
6789
6790 if items.is_empty() {
6791 return if args.len() == 3 {
6793 self.evaluate_internal(&args[2], data)
6794 } else {
6795 Ok(JValue::Null)
6796 };
6797 }
6798
6799 let mut accumulator = if args.len() == 3 {
6801 self.evaluate_internal(&args[2], data)?
6802 } else {
6803 items[0].clone()
6804 };
6805
6806 let start_idx = if args.len() == 3 { 0 } else { 1 };
6807
6808 let param_count = self.get_callback_param_count(&args[1]);
6810
6811 if param_count == 2 {
6813 if let AstNode::Lambda {
6814 params, body, signature: None, thunk: false,
6815 } = &args[1]
6816 {
6817 let var_refs: Vec<&str> =
6818 params.iter().map(|s| s.as_str()).collect();
6819 if let Some(compiled) =
6820 try_compile_expr_with_allowed_vars(body, &var_refs)
6821 {
6822 let acc_name = params[0].as_str();
6823 let item_name = params[1].as_str();
6824 for item in items[start_idx..].iter() {
6825 let vars: HashMap<&str, &JValue> = HashMap::from([
6826 (acc_name, &accumulator),
6827 (item_name, item),
6828 ]);
6829 accumulator = eval_compiled(&compiled, data, Some(&vars))?;
6830 }
6831 return Ok(accumulator);
6832 }
6833 }
6834 if let AstNode::Variable(var_name) = &args[1] {
6836 if let Some(stored) = self.context.lookup_lambda(var_name) {
6837 if stored.params.len() == 2 {
6838 if let Some(ref ce) = stored.compiled_body.clone() {
6839 let acc_param = stored.params[0].clone();
6840 let item_param = stored.params[1].clone();
6841 let captured_data = stored.captured_data.clone();
6842 let captured_env_clone = stored.captured_env.clone();
6843 let ce_clone = ce.clone();
6844 if !captured_env_clone.values().any(|v| {
6845 matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. })
6846 }) {
6847 let call_data =
6848 captured_data.as_ref().unwrap_or(data);
6849 for item in items[start_idx..].iter() {
6850 let mut vars: HashMap<&str, &JValue> =
6851 captured_env_clone
6852 .iter()
6853 .map(|(k, v)| (k.as_str(), v))
6854 .collect();
6855 vars.insert(acc_param.as_str(), &accumulator);
6856 vars.insert(item_param.as_str(), item);
6857 let new_acc =
6860 eval_compiled(&ce_clone, call_data, Some(&vars))?;
6861 drop(vars);
6862 accumulator = new_acc;
6863 }
6864 return Ok(accumulator);
6865 }
6866 }
6867 }
6868 }
6869 }
6870 }
6871
6872 let arr_value = if param_count >= 4 {
6874 Some(JValue::array(items.to_vec()))
6875 } else {
6876 None
6877 };
6878
6879 for (idx, item) in items[start_idx..].iter().enumerate() {
6881 let actual_idx = start_idx + idx;
6884
6885 let call_args = match param_count {
6887 2 => vec![accumulator.clone(), item.clone()],
6888 3 => vec![
6889 accumulator.clone(),
6890 item.clone(),
6891 JValue::Number(actual_idx as f64),
6892 ],
6893 _ => vec![
6894 accumulator.clone(),
6895 item.clone(),
6896 JValue::Number(actual_idx as f64),
6897 arr_value.as_ref().unwrap().clone(),
6898 ],
6899 };
6900
6901 accumulator = self.apply_function(&args[1], &call_args, data)?;
6902 }
6903
6904 Ok(accumulator)
6905 }
6906
6907 "single" => {
6908 if args.is_empty() || args.len() > 2 {
6909 return Err(EvaluatorError::EvaluationError(
6910 "single() requires 1 or 2 arguments".to_string(),
6911 ));
6912 }
6913
6914 let array = self.evaluate_internal(&args[0], data)?;
6916
6917 let arr = match array {
6919 JValue::Array(arr) => arr.to_vec(),
6920 JValue::Null => return Ok(JValue::Null),
6921 other => vec![other],
6922 };
6923
6924 if args.len() == 1 {
6925 match arr.len() {
6927 0 => Err(EvaluatorError::EvaluationError(
6928 "single() argument is empty".to_string(),
6929 )),
6930 1 => Ok(arr.into_iter().next().unwrap()),
6931 count => Err(EvaluatorError::EvaluationError(format!(
6932 "single() argument has {} values (expected exactly 1)",
6933 count
6934 ))),
6935 }
6936 } else {
6937 let arr_value = JValue::array(arr.clone());
6939 let mut matches = Vec::new();
6940 for (index, item) in arr.into_iter().enumerate() {
6941 let predicate_result = self.apply_function(
6943 &args[1],
6944 &[
6945 item.clone(),
6946 JValue::Number(index as f64),
6947 arr_value.clone(),
6948 ],
6949 data,
6950 )?;
6951 if self.is_truthy(&predicate_result) {
6952 matches.push(item);
6953 }
6954 }
6955
6956 match matches.len() {
6957 0 => Err(EvaluatorError::EvaluationError(
6958 "single() predicate matches no values".to_string(),
6959 )),
6960 1 => Ok(matches.into_iter().next().unwrap()),
6961 count => Err(EvaluatorError::EvaluationError(format!(
6962 "single() predicate matches {} values (expected exactly 1)",
6963 count
6964 ))),
6965 }
6966 }
6967 }
6968
6969 "each" => {
6970 if args.is_empty() || args.len() > 2 {
6973 return Err(EvaluatorError::EvaluationError(
6974 "each() requires 1 or 2 arguments".to_string(),
6975 ));
6976 }
6977
6978 let (obj_value, func_arg) = if args.len() == 1 {
6980 (data.clone(), &args[0])
6982 } else {
6983 (self.evaluate_internal(&args[0], data)?, &args[1])
6985 };
6986
6987 let param_count = self.get_callback_param_count(func_arg);
6989
6990 match obj_value {
6991 JValue::Object(obj) => {
6992 let mut result = Vec::new();
6993 for (key, value) in obj.iter() {
6994 let call_args = match param_count {
6997 1 => vec![value.clone()],
6998 _ => vec![value.clone(), JValue::string(key.clone())],
6999 };
7000
7001 let fn_result = self.apply_function(func_arg, &call_args, data)?;
7002 if !fn_result.is_null() && !fn_result.is_undefined() {
7004 result.push(fn_result);
7005 }
7006 }
7007 Ok(JValue::array(result))
7008 }
7009 JValue::Null => Ok(JValue::Null),
7010 _ => Err(EvaluatorError::TypeError(
7011 "each() first argument must be an object".to_string(),
7012 )),
7013 }
7014 }
7015
7016 "not" => {
7017 if evaluated_args.len() != 1 {
7018 return Err(EvaluatorError::EvaluationError(
7019 "not() requires exactly 1 argument".to_string(),
7020 ));
7021 }
7022 Ok(JValue::Bool(!self.is_truthy(&evaluated_args[0])))
7025 }
7026 "boolean" => {
7027 if evaluated_args.len() != 1 {
7028 return Err(EvaluatorError::EvaluationError(
7029 "boolean() requires exactly 1 argument".to_string(),
7030 ));
7031 }
7032 Ok(functions::boolean::boolean(&evaluated_args[0])?)
7033 }
7034 "type" => {
7035 if evaluated_args.len() != 1 {
7036 return Err(EvaluatorError::EvaluationError(
7037 "type() requires exactly 1 argument".to_string(),
7038 ));
7039 }
7040 match &evaluated_args[0] {
7044 JValue::Null => Ok(JValue::string("null")),
7045 JValue::Bool(_) => Ok(JValue::string("boolean")),
7046 JValue::Number(_) => Ok(JValue::string("number")),
7047 JValue::String(_) => Ok(JValue::string("string")),
7048 JValue::Array(_) => Ok(JValue::string("array")),
7049 JValue::Object(_) => Ok(JValue::string("object")),
7050 JValue::Undefined => Ok(JValue::Undefined),
7051 JValue::Lambda { .. } | JValue::Builtin { .. } => {
7052 Ok(JValue::string("function"))
7053 }
7054 JValue::Regex { .. } => Ok(JValue::string("regex")),
7055 }
7056 }
7057
7058 "base64encode" => {
7059 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7060 return Ok(JValue::Null);
7061 }
7062 if evaluated_args.len() != 1 {
7063 return Err(EvaluatorError::EvaluationError(
7064 "base64encode() requires exactly 1 argument".to_string(),
7065 ));
7066 }
7067 match &evaluated_args[0] {
7068 JValue::String(s) => Ok(functions::encoding::base64encode(s)?),
7069 _ => Err(EvaluatorError::TypeError(
7070 "base64encode() requires a string argument".to_string(),
7071 )),
7072 }
7073 }
7074 "base64decode" => {
7075 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7076 return Ok(JValue::Null);
7077 }
7078 if evaluated_args.len() != 1 {
7079 return Err(EvaluatorError::EvaluationError(
7080 "base64decode() requires exactly 1 argument".to_string(),
7081 ));
7082 }
7083 match &evaluated_args[0] {
7084 JValue::String(s) => Ok(functions::encoding::base64decode(s)?),
7085 _ => Err(EvaluatorError::TypeError(
7086 "base64decode() requires a string argument".to_string(),
7087 )),
7088 }
7089 }
7090 "encodeUrlComponent" => {
7091 if evaluated_args.len() != 1 {
7092 return Err(EvaluatorError::EvaluationError(
7093 "encodeUrlComponent() requires exactly 1 argument".to_string(),
7094 ));
7095 }
7096 if evaluated_args[0].is_null() {
7097 return Ok(JValue::Null);
7098 }
7099 match &evaluated_args[0] {
7100 JValue::String(s) => Ok(functions::encoding::encode_url_component(s)?),
7101 _ => Err(EvaluatorError::TypeError(
7102 "encodeUrlComponent() requires a string argument".to_string(),
7103 )),
7104 }
7105 }
7106 "decodeUrlComponent" => {
7107 if evaluated_args.len() != 1 {
7108 return Err(EvaluatorError::EvaluationError(
7109 "decodeUrlComponent() requires exactly 1 argument".to_string(),
7110 ));
7111 }
7112 if evaluated_args[0].is_null() {
7113 return Ok(JValue::Null);
7114 }
7115 match &evaluated_args[0] {
7116 JValue::String(s) => Ok(functions::encoding::decode_url_component(s)?),
7117 _ => Err(EvaluatorError::TypeError(
7118 "decodeUrlComponent() requires a string argument".to_string(),
7119 )),
7120 }
7121 }
7122 "encodeUrl" => {
7123 if evaluated_args.len() != 1 {
7124 return Err(EvaluatorError::EvaluationError(
7125 "encodeUrl() requires exactly 1 argument".to_string(),
7126 ));
7127 }
7128 if evaluated_args[0].is_null() {
7129 return Ok(JValue::Null);
7130 }
7131 match &evaluated_args[0] {
7132 JValue::String(s) => Ok(functions::encoding::encode_url(s)?),
7133 _ => Err(EvaluatorError::TypeError(
7134 "encodeUrl() requires a string argument".to_string(),
7135 )),
7136 }
7137 }
7138 "decodeUrl" => {
7139 if evaluated_args.len() != 1 {
7140 return Err(EvaluatorError::EvaluationError(
7141 "decodeUrl() requires exactly 1 argument".to_string(),
7142 ));
7143 }
7144 if evaluated_args[0].is_null() {
7145 return Ok(JValue::Null);
7146 }
7147 match &evaluated_args[0] {
7148 JValue::String(s) => Ok(functions::encoding::decode_url(s)?),
7149 _ => Err(EvaluatorError::TypeError(
7150 "decodeUrl() requires a string argument".to_string(),
7151 )),
7152 }
7153 }
7154
7155 "error" => {
7156 if evaluated_args.is_empty() {
7158 return Err(EvaluatorError::EvaluationError(
7160 "D3137: $error() function evaluated".to_string(),
7161 ));
7162 }
7163
7164 match &evaluated_args[0] {
7165 JValue::String(s) => {
7166 Err(EvaluatorError::EvaluationError(format!("D3137: {}", s)))
7167 }
7168 _ => Err(EvaluatorError::TypeError(
7169 "T0410: Argument 1 of function error does not match function signature"
7170 .to_string(),
7171 )),
7172 }
7173 }
7174 "assert" => {
7175 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7177 return Err(EvaluatorError::EvaluationError(
7178 "assert() requires 1 or 2 arguments".to_string(),
7179 ));
7180 }
7181
7182 let condition = match &evaluated_args[0] {
7184 JValue::Bool(b) => *b,
7185 _ => {
7186 return Err(EvaluatorError::TypeError(
7187 "T0410: Argument 1 of function $assert does not match function signature".to_string(),
7188 ));
7189 }
7190 };
7191
7192 if !condition {
7193 let message = if evaluated_args.len() == 2 {
7194 match &evaluated_args[1] {
7195 JValue::String(s) => s.clone(),
7196 _ => Rc::from("$assert() statement failed"),
7197 }
7198 } else {
7199 Rc::from("$assert() statement failed")
7200 };
7201 return Err(EvaluatorError::EvaluationError(format!(
7202 "D3141: {}",
7203 message
7204 )));
7205 }
7206
7207 Ok(JValue::Null)
7208 }
7209
7210 "eval" => {
7211 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7213 return Err(EvaluatorError::EvaluationError(
7214 "T0410: Argument 1 of function $eval must be a string".to_string(),
7215 ));
7216 }
7217
7218 if evaluated_args[0].is_null() {
7220 return Ok(JValue::Null);
7221 }
7222
7223 let expr_str = match &evaluated_args[0] {
7225 JValue::String(s) => &**s,
7226 _ => {
7227 return Err(EvaluatorError::EvaluationError(
7228 "T0410: Argument 1 of function $eval must be a string".to_string(),
7229 ));
7230 }
7231 };
7232
7233 let parsed_ast = match parser::parse(expr_str) {
7235 Ok(ast) => ast,
7236 Err(e) => {
7237 return Err(EvaluatorError::EvaluationError(format!(
7239 "D3120: The expression passed to $eval cannot be parsed: {}",
7240 e
7241 )));
7242 }
7243 };
7244
7245 let eval_context = if evaluated_args.len() == 2 {
7247 &evaluated_args[1]
7248 } else {
7249 data
7250 };
7251
7252 match self.evaluate_internal(&parsed_ast, eval_context) {
7254 Ok(result) => Ok(result),
7255 Err(e) => {
7256 let err_msg = e.to_string();
7258 if err_msg.starts_with("D3121") || err_msg.contains("Unknown function") {
7259 Err(EvaluatorError::EvaluationError(format!(
7260 "D3121: {}",
7261 err_msg
7262 )))
7263 } else {
7264 Err(e)
7265 }
7266 }
7267 }
7268 }
7269
7270 "now" => {
7271 if !evaluated_args.is_empty() {
7272 return Err(EvaluatorError::EvaluationError(
7273 "now() takes no arguments".to_string(),
7274 ));
7275 }
7276 Ok(crate::datetime::now())
7277 }
7278
7279 "millis" => {
7280 if !evaluated_args.is_empty() {
7281 return Err(EvaluatorError::EvaluationError(
7282 "millis() takes no arguments".to_string(),
7283 ));
7284 }
7285 Ok(crate::datetime::millis())
7286 }
7287
7288 "toMillis" => {
7289 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7290 return Err(EvaluatorError::EvaluationError(
7291 "toMillis() requires 1 or 2 arguments".to_string(),
7292 ));
7293 }
7294
7295 match &evaluated_args[0] {
7296 JValue::String(s) => {
7297 if evaluated_args.len() == 2 {
7299 match &evaluated_args[1] {
7300 JValue::String(picture) => {
7301 Ok(crate::datetime::to_millis_with_picture(s, picture)?)
7303 }
7304 JValue::Null => Ok(JValue::Null),
7305 _ => Err(EvaluatorError::TypeError(
7306 "toMillis() second argument must be a string".to_string(),
7307 )),
7308 }
7309 } else {
7310 Ok(crate::datetime::to_millis(s)?)
7312 }
7313 }
7314 JValue::Null => Ok(JValue::Null),
7315 _ => Err(EvaluatorError::TypeError(
7316 "toMillis() requires a string argument".to_string(),
7317 )),
7318 }
7319 }
7320
7321 "fromMillis" => {
7322 if evaluated_args.len() != 1 {
7323 return Err(EvaluatorError::EvaluationError(
7324 "fromMillis() requires exactly 1 argument".to_string(),
7325 ));
7326 }
7327
7328 match &evaluated_args[0] {
7329 JValue::Number(n) => {
7330 let millis = (if n.fract() == 0.0 {
7331 Ok(*n as i64)
7332 } else {
7333 Err(())
7334 })
7335 .map_err(|_| {
7336 EvaluatorError::TypeError(
7337 "fromMillis() requires an integer".to_string(),
7338 )
7339 })?;
7340 Ok(crate::datetime::from_millis(millis)?)
7341 }
7342 JValue::Null => Ok(JValue::Null),
7343 _ => Err(EvaluatorError::TypeError(
7344 "fromMillis() requires a number argument".to_string(),
7345 )),
7346 }
7347 }
7348
7349 _ => Err(EvaluatorError::ReferenceError(format!(
7350 "Unknown function: {}",
7351 name
7352 ))),
7353 }
7354 }
7355
7356 fn apply_function(
7362 &mut self,
7363 func_node: &AstNode,
7364 values: &[JValue],
7365 data: &JValue,
7366 ) -> Result<JValue, EvaluatorError> {
7367 match func_node {
7368 AstNode::Lambda {
7369 params,
7370 body,
7371 signature,
7372 thunk,
7373 } => {
7374 self.invoke_lambda(params, body, signature.as_ref(), values, data, *thunk)
7376 }
7377 AstNode::Function {
7378 name,
7379 args,
7380 is_builtin,
7381 } => {
7382 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
7384
7385 if has_placeholder {
7386 let partial_lambda =
7388 self.create_partial_application(name, args, *is_builtin, data)?;
7389
7390 if let Some(stored) = self.lookup_lambda_from_value(&partial_lambda) {
7392 return self.invoke_stored_lambda(&stored, values, data);
7393 }
7394 Err(EvaluatorError::EvaluationError(
7395 "Failed to apply partial application".to_string(),
7396 ))
7397 } else {
7398 let result = self.evaluate_internal(func_node, data)?;
7401
7402 if let Some(stored) = self.lookup_lambda_from_value(&result) {
7404 return self.invoke_stored_lambda(&stored, values, data);
7405 }
7406
7407 Ok(result)
7409 }
7410 }
7411 AstNode::Variable(var_name) => {
7412 if let Some(stored_lambda) = self.context.lookup_lambda(var_name).cloned() {
7414 self.invoke_stored_lambda(&stored_lambda, values, data)
7415 } else if let Some(value) = self.context.lookup(var_name).cloned() {
7416 if let Some(stored) = self.lookup_lambda_from_value(&value) {
7419 return self.invoke_stored_lambda(&stored, values, data);
7420 }
7421 if values.is_empty() {
7423 self.evaluate_internal(func_node, data)
7424 } else {
7425 self.evaluate_internal(func_node, &values[0])
7426 }
7427 } else if self.is_builtin_function(var_name) {
7428 self.call_builtin_with_values(var_name, values)
7431 } else {
7432 if values.is_empty() {
7434 self.evaluate_internal(func_node, data)
7435 } else {
7436 self.evaluate_internal(func_node, &values[0])
7437 }
7438 }
7439 }
7440 _ => {
7441 if values.is_empty() {
7443 self.evaluate_internal(func_node, data)
7444 } else {
7445 self.evaluate_internal(func_node, &values[0])
7446 }
7447 }
7448 }
7449 }
7450
7451 fn execute_transform(
7453 &mut self,
7454 location: &AstNode,
7455 update: &AstNode,
7456 delete: Option<&AstNode>,
7457 _original_data: &JValue,
7458 ) -> Result<JValue, EvaluatorError> {
7459 let input = self
7461 .context
7462 .lookup("$")
7463 .ok_or_else(|| {
7464 EvaluatorError::EvaluationError("Transform requires $ binding".to_string())
7465 })?
7466 .clone();
7467
7468 let located_objects = self.evaluate_internal(location, &input)?;
7470
7471 let targets: Vec<JValue> = match located_objects {
7473 JValue::Array(arr) => arr.to_vec(),
7474 JValue::Object(_) => vec![located_objects],
7475 JValue::Null => Vec::new(),
7476 other => vec![other],
7477 };
7478
7479 let delete_fields: Vec<String> = if let Some(delete_node) = delete {
7485 let delete_val = self.evaluate_internal(delete_node, &input)?;
7486 match delete_val {
7487 JValue::Array(arr) => arr
7488 .iter()
7489 .filter_map(|v| match v {
7490 JValue::String(s) => Some(s.to_string()),
7491 _ => None,
7492 })
7493 .collect(),
7494 JValue::String(s) => vec![s.to_string()],
7495 JValue::Null => Vec::new(), _ => {
7497 return Err(EvaluatorError::EvaluationError(
7499 "T2012: The third argument of the transform operator must be an array of strings".to_string()
7500 ));
7501 }
7502 }
7503 } else {
7504 Vec::new()
7505 };
7506
7507 fn apply_transform_deep(
7509 evaluator: &mut Evaluator,
7510 value: &JValue,
7511 targets: &[JValue],
7512 update: &AstNode,
7513 delete_fields: &[String],
7514 ) -> Result<JValue, EvaluatorError> {
7515 if targets.iter().any(|t| t == value) {
7518 if let JValue::Object(map_rc) = value.clone() {
7520 let mut map = (*map_rc).clone();
7521 let update_val = evaluator.evaluate_internal(update, value)?;
7522 match update_val {
7524 JValue::Object(update_map) => {
7525 for (key, val) in update_map.iter() {
7526 map.insert(key.clone(), val.clone());
7527 }
7528 }
7529 JValue::Null => {
7530 }
7532 _ => {
7533 return Err(EvaluatorError::EvaluationError(
7534 "T2011: The second argument of the transform operator must evaluate to an object".to_string()
7535 ));
7536 }
7537 }
7538 for field in delete_fields {
7539 map.shift_remove(field);
7540 }
7541 return Ok(JValue::object(map));
7542 }
7543 return Ok(value.clone());
7544 }
7545
7546 match value {
7548 JValue::Object(map) => {
7549 let mut new_map = IndexMap::new();
7550 for (k, v) in map.iter() {
7551 new_map.insert(
7552 k.clone(),
7553 apply_transform_deep(evaluator, v, targets, update, delete_fields)?,
7554 );
7555 }
7556 Ok(JValue::object(new_map))
7557 }
7558 JValue::Array(arr) => {
7559 let mut new_arr = Vec::new();
7560 for item in arr.iter() {
7561 new_arr.push(apply_transform_deep(
7562 evaluator,
7563 item,
7564 targets,
7565 update,
7566 delete_fields,
7567 )?);
7568 }
7569 Ok(JValue::array(new_arr))
7570 }
7571 _ => Ok(value.clone()),
7572 }
7573 }
7574
7575 apply_transform_deep(self, &input, &targets, update, &delete_fields)
7577 }
7578
7579 fn invoke_lambda(
7581 &mut self,
7582 params: &[String],
7583 body: &AstNode,
7584 signature: Option<&String>,
7585 values: &[JValue],
7586 data: &JValue,
7587 thunk: bool,
7588 ) -> Result<JValue, EvaluatorError> {
7589 self.invoke_lambda_with_env(params, body, signature, values, data, None, None, thunk)
7590 }
7591
7592 fn invoke_lambda_with_env(
7594 &mut self,
7595 params: &[String],
7596 body: &AstNode,
7597 signature: Option<&String>,
7598 values: &[JValue],
7599 data: &JValue,
7600 captured_env: Option<&HashMap<String, JValue>>,
7601 captured_data: Option<&JValue>,
7602 thunk: bool,
7603 ) -> Result<JValue, EvaluatorError> {
7604 if thunk {
7606 let stored = StoredLambda {
7607 params: params.to_vec(),
7608 body: body.clone(),
7609 compiled_body: None, signature: signature.cloned(),
7611 captured_env: captured_env.cloned().unwrap_or_default(),
7612 captured_data: captured_data.cloned(),
7613 thunk,
7614 };
7615 return self.invoke_lambda_with_tco(&stored, values, data);
7616 }
7617
7618 self.context.push_scope();
7621
7622 if let Some(env) = captured_env {
7624 for (name, value) in env {
7625 self.context.bind(name.clone(), value.clone());
7626 }
7627 }
7628
7629 if let Some(sig_str) = signature {
7630 let coerced_values = match crate::signature::Signature::parse(sig_str) {
7632 Ok(sig) => {
7633 match sig.validate_and_coerce(values) {
7634 Ok(coerced) => coerced,
7635 Err(e) => {
7636 self.context.pop_scope();
7637 match e {
7638 crate::signature::SignatureError::UndefinedArgument => {
7639 return Ok(JValue::Null);
7640 }
7641 crate::signature::SignatureError::ArgumentTypeMismatch {
7642 index,
7643 expected,
7644 } => {
7645 return Err(EvaluatorError::TypeError(
7646 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
7647 ));
7648 }
7649 crate::signature::SignatureError::ArrayTypeMismatch {
7650 index,
7651 expected,
7652 } => {
7653 return Err(EvaluatorError::TypeError(format!(
7654 "T0412: Argument {} of function must be an array of {}",
7655 index, expected
7656 )));
7657 }
7658 _ => {
7659 return Err(EvaluatorError::TypeError(format!(
7660 "Signature validation failed: {}",
7661 e
7662 )));
7663 }
7664 }
7665 }
7666 }
7667 }
7668 Err(e) => {
7669 self.context.pop_scope();
7670 return Err(EvaluatorError::EvaluationError(format!(
7671 "Invalid signature: {}",
7672 e
7673 )));
7674 }
7675 };
7676 for (i, param) in params.iter().enumerate() {
7678 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Undefined);
7679 self.context.bind(param.clone(), value);
7680 }
7681 } else {
7682 for (i, param) in params.iter().enumerate() {
7684 let value = values.get(i).cloned().unwrap_or(JValue::Undefined);
7685 self.context.bind(param.clone(), value);
7686 }
7687 }
7688
7689 if let AstNode::String(body_str) = body {
7691 if body_str.starts_with("__partial_call:") {
7692 let parts: Vec<&str> = body_str.split(':').collect();
7694 if parts.len() >= 4 {
7695 let func_name = parts[1];
7696 let is_builtin = parts[2] == "true";
7697 let total_args: usize = parts[3].parse().unwrap_or(0);
7698
7699 let placeholder_positions: Vec<usize> = if let Some(env) = captured_env {
7701 if let Some(JValue::Array(positions)) = env.get("__placeholder_positions") {
7702 positions
7703 .iter()
7704 .filter_map(|v| v.as_f64().map(|n| n as usize))
7705 .collect()
7706 } else {
7707 vec![]
7708 }
7709 } else {
7710 vec![]
7711 };
7712
7713 let mut full_args: Vec<JValue> = vec![JValue::Null; total_args];
7715
7716 if let Some(env) = captured_env {
7718 for (key, value) in env {
7719 if key.starts_with("__bound_arg_") {
7720 if let Ok(pos) = key[12..].parse::<usize>() {
7721 if pos < total_args {
7722 full_args[pos] = value.clone();
7723 }
7724 }
7725 }
7726 }
7727 }
7728
7729 for (i, &pos) in placeholder_positions.iter().enumerate() {
7731 if pos < total_args {
7732 let value = values.get(i).cloned().unwrap_or(JValue::Null);
7733 full_args[pos] = value;
7734 }
7735 }
7736
7737 self.context.pop_scope();
7739 self.context.push_scope();
7740
7741 let mut temp_args: Vec<AstNode> = Vec::new();
7743 for (i, value) in full_args.iter().enumerate() {
7744 let temp_name = format!("__temp_arg_{}", i);
7745 self.context.bind(temp_name.clone(), value.clone());
7746 temp_args.push(AstNode::Variable(temp_name));
7747 }
7748
7749 let result =
7751 self.evaluate_function_call(func_name, &temp_args, is_builtin, data);
7752
7753 self.context.pop_scope();
7755
7756 return result;
7757 }
7758 }
7759 }
7760
7761 let body_data = captured_data.unwrap_or(data);
7764 let result = self.evaluate_internal(body, body_data)?;
7765
7766 let is_scalar = matches!(&result,
7769 JValue::Number(_) | JValue::Bool(_) | JValue::String(_)
7770 | JValue::Null | JValue::Undefined);
7771 if is_scalar {
7772 self.context.pop_scope();
7773 } else {
7774 let lambdas_to_keep = self.extract_lambda_ids(&result);
7775 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
7776 }
7777
7778 Ok(result)
7779 }
7780
7781 fn invoke_lambda_with_tco(
7785 &mut self,
7786 stored_lambda: &StoredLambda,
7787 initial_args: &[JValue],
7788 data: &JValue,
7789 ) -> Result<JValue, EvaluatorError> {
7790 let mut current_lambda = stored_lambda.clone();
7791 let mut current_args = initial_args.to_vec();
7792 let mut current_data = data.clone();
7793
7794 const MAX_TCO_ITERATIONS: usize = 100_000;
7797 let mut iterations = 0;
7798
7799 self.context.push_scope();
7803
7804 let result = loop {
7806 iterations += 1;
7807 if iterations > MAX_TCO_ITERATIONS {
7808 self.context.pop_scope();
7809 return Err(EvaluatorError::EvaluationError(
7810 "U1001: Stack overflow - maximum recursion depth (500) exceeded".to_string(),
7811 ));
7812 }
7813
7814 let result =
7816 self.invoke_lambda_body_for_tco(¤t_lambda, ¤t_args, ¤t_data)?;
7817
7818 match result {
7819 LambdaResult::JValue(v) => break v,
7820 LambdaResult::TailCall { lambda, args, data } => {
7821 current_lambda = *lambda;
7823 current_args = args;
7824 current_data = data;
7825 }
7826 }
7827 };
7828
7829 let lambdas_to_keep = self.extract_lambda_ids(&result);
7831 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
7832
7833 Ok(result)
7834 }
7835
7836 fn invoke_lambda_body_for_tco(
7841 &mut self,
7842 lambda: &StoredLambda,
7843 values: &[JValue],
7844 data: &JValue,
7845 ) -> Result<LambdaResult, EvaluatorError> {
7846 let coerced_values = if let Some(sig_str) = &lambda.signature {
7848 match crate::signature::Signature::parse(sig_str) {
7849 Ok(sig) => match sig.validate_and_coerce(values) {
7850 Ok(coerced) => coerced,
7851 Err(e) => match e {
7852 crate::signature::SignatureError::UndefinedArgument => {
7853 return Ok(LambdaResult::JValue(JValue::Null));
7854 }
7855 crate::signature::SignatureError::ArgumentTypeMismatch {
7856 index,
7857 expected,
7858 } => {
7859 return Err(EvaluatorError::TypeError(
7860 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
7861 ));
7862 }
7863 crate::signature::SignatureError::ArrayTypeMismatch { index, expected } => {
7864 return Err(EvaluatorError::TypeError(format!(
7865 "T0412: Argument {} of function must be an array of {}",
7866 index, expected
7867 )));
7868 }
7869 _ => {
7870 return Err(EvaluatorError::TypeError(format!(
7871 "Signature validation failed: {}",
7872 e
7873 )));
7874 }
7875 },
7876 },
7877 Err(e) => {
7878 return Err(EvaluatorError::EvaluationError(format!(
7879 "Invalid signature: {}",
7880 e
7881 )));
7882 }
7883 }
7884 } else {
7885 values.to_vec()
7886 };
7887
7888 for (name, value) in &lambda.captured_env {
7891 self.context.bind(name.clone(), value.clone());
7892 }
7893
7894 for (i, param) in lambda.params.iter().enumerate() {
7896 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Null);
7897 self.context.bind(param.clone(), value);
7898 }
7899
7900 let body_data = lambda.captured_data.as_ref().unwrap_or(data);
7902 self.evaluate_for_tco(&lambda.body, body_data)
7903 }
7904
7905 fn evaluate_for_tco(
7908 &mut self,
7909 node: &AstNode,
7910 data: &JValue,
7911 ) -> Result<LambdaResult, EvaluatorError> {
7912 match node {
7913 AstNode::Conditional {
7915 condition,
7916 then_branch,
7917 else_branch,
7918 } => {
7919 let cond_value = self.evaluate_internal(condition, data)?;
7920 let is_truthy = self.is_truthy(&cond_value);
7921
7922 if is_truthy {
7923 self.evaluate_for_tco(then_branch, data)
7924 } else if let Some(else_expr) = else_branch {
7925 self.evaluate_for_tco(else_expr, data)
7926 } else {
7927 Ok(LambdaResult::JValue(JValue::Null))
7928 }
7929 }
7930
7931 AstNode::Block(exprs) => {
7933 if exprs.is_empty() {
7934 return Ok(LambdaResult::JValue(JValue::Null));
7935 }
7936
7937 let mut result = JValue::Null;
7939 for (i, expr) in exprs.iter().enumerate() {
7940 if i == exprs.len() - 1 {
7941 return self.evaluate_for_tco(expr, data);
7943 } else {
7944 result = self.evaluate_internal(expr, data)?;
7945 }
7946 }
7947 Ok(LambdaResult::JValue(result))
7948 }
7949
7950 AstNode::Binary {
7952 op: BinaryOp::ColonEqual,
7953 lhs,
7954 rhs,
7955 } => {
7956 let var_name = match lhs.as_ref() {
7958 AstNode::Variable(name) => name.clone(),
7959 _ => {
7960 let result = self.evaluate_internal(node, data)?;
7962 return Ok(LambdaResult::JValue(result));
7963 }
7964 };
7965
7966 if let AstNode::Lambda {
7968 params,
7969 body,
7970 signature,
7971 thunk,
7972 } = rhs.as_ref()
7973 {
7974 let captured_env = self.capture_environment_for(body, params);
7975 let compiled_body = if !thunk {
7976 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
7977 try_compile_expr_with_allowed_vars(body, &var_refs)
7978 } else {
7979 None
7980 };
7981 let stored_lambda = StoredLambda {
7982 params: params.clone(),
7983 body: (**body).clone(),
7984 compiled_body,
7985 signature: signature.clone(),
7986 captured_env,
7987 captured_data: Some(data.clone()),
7988 thunk: *thunk,
7989 };
7990 self.context.bind_lambda(var_name, stored_lambda);
7991 let lambda_repr =
7992 JValue::lambda("anon", params.clone(), None::<String>, None::<String>);
7993 return Ok(LambdaResult::JValue(lambda_repr));
7994 }
7995
7996 let value = self.evaluate_internal(rhs, data)?;
7998 self.context.bind(var_name, value.clone());
7999 Ok(LambdaResult::JValue(value))
8000 }
8001
8002 AstNode::Function { name, args, .. } => {
8004 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
8006 if stored_lambda.thunk {
8007 let mut evaluated_args = Vec::with_capacity(args.len());
8008 for arg in args {
8009 evaluated_args.push(self.evaluate_internal(arg, data)?);
8010 }
8011 return Ok(LambdaResult::TailCall {
8012 lambda: Box::new(stored_lambda),
8013 args: evaluated_args,
8014 data: data.clone(),
8015 });
8016 }
8017 }
8018 let result = self.evaluate_internal(node, data)?;
8020 Ok(LambdaResult::JValue(result))
8021 }
8022
8023 AstNode::Call { procedure, args } => {
8025 let callable = self.evaluate_internal(procedure, data)?;
8027
8028 if let JValue::Lambda { lambda_id, .. } = &callable {
8030 if let Some(stored_lambda) = self.context.lookup_lambda(lambda_id).cloned() {
8031 if stored_lambda.thunk {
8032 let mut evaluated_args = Vec::with_capacity(args.len());
8033 for arg in args {
8034 evaluated_args.push(self.evaluate_internal(arg, data)?);
8035 }
8036 return Ok(LambdaResult::TailCall {
8037 lambda: Box::new(stored_lambda),
8038 args: evaluated_args,
8039 data: data.clone(),
8040 });
8041 }
8042 }
8043 }
8044 let result = self.evaluate_internal(node, data)?;
8046 Ok(LambdaResult::JValue(result))
8047 }
8048
8049 AstNode::Variable(_) => {
8052 let result = self.evaluate_internal(node, data)?;
8053 Ok(LambdaResult::JValue(result))
8054 }
8055
8056 _ => {
8058 let result = self.evaluate_internal(node, data)?;
8059 Ok(LambdaResult::JValue(result))
8060 }
8061 }
8062 }
8063
8064 fn match_with_custom_matcher(
8071 &mut self,
8072 str_value: &str,
8073 matcher_node: &AstNode,
8074 limit: Option<usize>,
8075 data: &JValue,
8076 ) -> Result<JValue, EvaluatorError> {
8077 let mut results = Vec::new();
8078 let mut count = 0;
8079
8080 let str_val = JValue::string(str_value.to_string());
8082 let mut current_match = self.apply_function(matcher_node, &[str_val], data)?;
8083
8084 while !current_match.is_undefined() && !current_match.is_null() {
8086 if let Some(lim) = limit {
8088 if count >= lim {
8089 break;
8090 }
8091 }
8092
8093 if let JValue::Object(ref match_obj) = current_match {
8095 let has_match = match_obj.contains_key("match");
8097 let has_start = match_obj.contains_key("start");
8098 let has_end = match_obj.contains_key("end");
8099 let has_groups = match_obj.contains_key("groups");
8100 let has_next = match_obj.contains_key("next");
8101
8102 if !has_match && !has_start && !has_end && !has_groups && !has_next {
8103 return Err(EvaluatorError::EvaluationError(
8105 "T1010: The matcher function did not return the correct object structure"
8106 .to_string(),
8107 ));
8108 }
8109
8110 let mut result_obj = IndexMap::new();
8112
8113 if let Some(match_val) = match_obj.get("match") {
8114 result_obj.insert("match".to_string(), match_val.clone());
8115 }
8116
8117 if let Some(start_val) = match_obj.get("start") {
8118 result_obj.insert("index".to_string(), start_val.clone());
8119 }
8120
8121 if let Some(groups_val) = match_obj.get("groups") {
8122 result_obj.insert("groups".to_string(), groups_val.clone());
8123 }
8124
8125 results.push(JValue::object(result_obj));
8126 count += 1;
8127
8128 if let Some(next_func) = match_obj.get("next") {
8130 if let Some(stored) = self.lookup_lambda_from_value(next_func) {
8131 current_match = self.invoke_stored_lambda(&stored, &[], data)?;
8132 continue;
8133 }
8134 }
8135
8136 break;
8138 } else {
8139 break;
8141 }
8142 }
8143
8144 if results.is_empty() {
8146 Ok(JValue::Undefined)
8147 } else {
8148 Ok(JValue::array(results))
8149 }
8150 }
8151
8152 fn replace_with_lambda(
8157 &mut self,
8158 str_value: &JValue,
8159 pattern_value: &JValue,
8160 lambda_value: &JValue,
8161 limit_value: Option<&JValue>,
8162 data: &JValue,
8163 ) -> Result<JValue, EvaluatorError> {
8164 let s = match str_value {
8166 JValue::String(s) => &**s,
8167 _ => {
8168 return Err(EvaluatorError::TypeError(
8169 "replace() requires string arguments".to_string(),
8170 ))
8171 }
8172 };
8173
8174 let (pattern, flags) =
8176 crate::functions::string::extract_regex(pattern_value).ok_or_else(|| {
8177 EvaluatorError::TypeError(
8178 "replace() pattern must be a regex when using lambda replacement".to_string(),
8179 )
8180 })?;
8181
8182 let re = crate::functions::string::build_regex(&pattern, &flags)?;
8184
8185 let limit = if let Some(lim_val) = limit_value {
8187 match lim_val {
8188 JValue::Number(n) => {
8189 let lim_f64 = *n;
8190 if lim_f64 < 0.0 {
8191 return Err(EvaluatorError::EvaluationError(format!(
8192 "D3011: Limit must be non-negative, got {}",
8193 lim_f64
8194 )));
8195 }
8196 Some(lim_f64 as usize)
8197 }
8198 _ => {
8199 return Err(EvaluatorError::TypeError(
8200 "replace() limit must be a number".to_string(),
8201 ))
8202 }
8203 }
8204 } else {
8205 None
8206 };
8207
8208 let mut result = String::new();
8210 let mut last_end = 0;
8211 let mut count = 0;
8212
8213 for cap in re.captures_iter(s) {
8214 if let Some(lim) = limit {
8216 if count >= lim {
8217 break;
8218 }
8219 }
8220
8221 let m = cap.get(0).unwrap();
8222 let match_start = m.start();
8223 let match_end = m.end();
8224 let match_str = m.as_str();
8225
8226 result.push_str(&s[last_end..match_start]);
8228
8229 let groups: Vec<JValue> = (1..cap.len())
8231 .map(|i| {
8232 cap.get(i)
8233 .map(|m| JValue::string(m.as_str().to_string()))
8234 .unwrap_or(JValue::Null)
8235 })
8236 .collect();
8237
8238 let mut match_map = IndexMap::new();
8239 match_map.insert("match".to_string(), JValue::string(match_str));
8240 match_map.insert("start".to_string(), JValue::Number(match_start as f64));
8241 match_map.insert("end".to_string(), JValue::Number(match_end as f64));
8242 match_map.insert("groups".to_string(), JValue::array(groups));
8243 let match_obj = JValue::object(match_map);
8244
8245 let stored_lambda = self.lookup_lambda_from_value(lambda_value).ok_or_else(|| {
8247 EvaluatorError::TypeError("Replacement must be a lambda function".to_string())
8248 })?;
8249 let lambda_result = self.invoke_stored_lambda(&stored_lambda, &[match_obj], data)?;
8250 let replacement_str = match lambda_result {
8251 JValue::String(s) => s,
8252 _ => {
8253 return Err(EvaluatorError::TypeError(format!(
8254 "D3012: Replacement function must return a string, got {:?}",
8255 lambda_result
8256 )))
8257 }
8258 };
8259
8260 result.push_str(&replacement_str);
8262
8263 last_end = match_end;
8264 count += 1;
8265 }
8266
8267 result.push_str(&s[last_end..]);
8269
8270 Ok(JValue::string(result))
8271 }
8272
8273 fn capture_current_environment(&self) -> HashMap<String, JValue> {
8275 self.context.all_bindings()
8276 }
8277
8278 fn capture_environment_for(
8281 &self,
8282 body: &AstNode,
8283 params: &[String],
8284 ) -> HashMap<String, JValue> {
8285 let free_vars = Self::collect_free_variables(body, params);
8286 if free_vars.is_empty() {
8287 return HashMap::new();
8288 }
8289 let mut result = HashMap::new();
8290 for var_name in &free_vars {
8291 if let Some(value) = self.context.lookup(var_name) {
8292 result.insert(var_name.clone(), value.clone());
8293 }
8294 }
8295 result
8296 }
8297
8298 fn collect_free_variables(body: &AstNode, params: &[String]) -> HashSet<String> {
8301 let mut free_vars = HashSet::new();
8302 let bound: HashSet<&str> = params.iter().map(|s| s.as_str()).collect();
8303 Self::collect_free_vars_walk(body, &bound, &mut free_vars);
8304 free_vars
8305 }
8306
8307 fn collect_free_vars_walk(node: &AstNode, bound: &HashSet<&str>, free: &mut HashSet<String>) {
8308 match node {
8309 AstNode::Variable(name) => {
8310 if !bound.contains(name.as_str()) {
8311 free.insert(name.clone());
8312 }
8313 }
8314 AstNode::Function { name, args, .. } => {
8315 if !bound.contains(name.as_str()) {
8317 free.insert(name.clone());
8318 }
8319 for arg in args {
8320 Self::collect_free_vars_walk(arg, bound, free);
8321 }
8322 }
8323 AstNode::Lambda { params, body, .. } => {
8324 let mut inner_bound = bound.clone();
8326 for p in params {
8327 inner_bound.insert(p.as_str());
8328 }
8329 Self::collect_free_vars_walk(body, &inner_bound, free);
8330 }
8331 AstNode::Binary { op, lhs, rhs } => {
8332 Self::collect_free_vars_walk(lhs, bound, free);
8333 Self::collect_free_vars_walk(rhs, bound, free);
8334 let _ = op;
8337 }
8338 AstNode::Unary { operand, .. } => {
8339 Self::collect_free_vars_walk(operand, bound, free);
8340 }
8341 AstNode::Path { steps } => {
8342 for step in steps {
8343 Self::collect_free_vars_walk(&step.node, bound, free);
8344 for stage in &step.stages {
8345 match stage {
8346 Stage::Filter(expr) => Self::collect_free_vars_walk(expr, bound, free),
8347 }
8348 }
8349 }
8350 }
8351 AstNode::Call { procedure, args } => {
8352 Self::collect_free_vars_walk(procedure, bound, free);
8353 for arg in args {
8354 Self::collect_free_vars_walk(arg, bound, free);
8355 }
8356 }
8357 AstNode::Conditional {
8358 condition,
8359 then_branch,
8360 else_branch,
8361 } => {
8362 Self::collect_free_vars_walk(condition, bound, free);
8363 Self::collect_free_vars_walk(then_branch, bound, free);
8364 if let Some(else_expr) = else_branch {
8365 Self::collect_free_vars_walk(else_expr, bound, free);
8366 }
8367 }
8368 AstNode::Block(exprs) => {
8369 let mut block_bound = bound.clone();
8370 for expr in exprs {
8371 Self::collect_free_vars_walk(expr, &block_bound, free);
8372 if let AstNode::Binary {
8374 op: BinaryOp::ColonEqual,
8375 lhs,
8376 ..
8377 } = expr
8378 {
8379 if let AstNode::Variable(var_name) = lhs.as_ref() {
8380 block_bound.insert(var_name.as_str());
8381 }
8382 }
8383 }
8384 }
8385 AstNode::Array(exprs) | AstNode::ArrayGroup(exprs) => {
8386 for expr in exprs {
8387 Self::collect_free_vars_walk(expr, bound, free);
8388 }
8389 }
8390 AstNode::Object(pairs) => {
8391 for (key, value) in pairs {
8392 Self::collect_free_vars_walk(key, bound, free);
8393 Self::collect_free_vars_walk(value, bound, free);
8394 }
8395 }
8396 AstNode::ObjectTransform { input, pattern } => {
8397 Self::collect_free_vars_walk(input, bound, free);
8398 for (key, value) in pattern {
8399 Self::collect_free_vars_walk(key, bound, free);
8400 Self::collect_free_vars_walk(value, bound, free);
8401 }
8402 }
8403 AstNode::Predicate(expr) | AstNode::FunctionApplication(expr) => {
8404 Self::collect_free_vars_walk(expr, bound, free);
8405 }
8406 AstNode::Sort { input, terms } => {
8407 Self::collect_free_vars_walk(input, bound, free);
8408 for (expr, _) in terms {
8409 Self::collect_free_vars_walk(expr, bound, free);
8410 }
8411 }
8412 AstNode::IndexBind { input, .. } => {
8413 Self::collect_free_vars_walk(input, bound, free);
8414 }
8415 AstNode::Transform {
8416 location,
8417 update,
8418 delete,
8419 } => {
8420 Self::collect_free_vars_walk(location, bound, free);
8421 Self::collect_free_vars_walk(update, bound, free);
8422 if let Some(del) = delete {
8423 Self::collect_free_vars_walk(del, bound, free);
8424 }
8425 }
8426 AstNode::String(_)
8428 | AstNode::Name(_)
8429 | AstNode::Number(_)
8430 | AstNode::Boolean(_)
8431 | AstNode::Null
8432 | AstNode::Undefined
8433 | AstNode::Placeholder
8434 | AstNode::Regex { .. }
8435 | AstNode::Wildcard
8436 | AstNode::Descendant
8437 | AstNode::ParentVariable(_) => {}
8438 }
8439 }
8440
8441 fn is_builtin_function(&self, name: &str) -> bool {
8443 matches!(
8444 name,
8445 "string" | "length" | "substring" | "substringBefore" | "substringAfter" |
8447 "uppercase" | "lowercase" | "trim" | "pad" | "contains" | "split" |
8448 "join" | "match" | "replace" | "eval" | "base64encode" | "base64decode" |
8449 "encodeUrlComponent" | "encodeUrl" | "decodeUrlComponent" | "decodeUrl" |
8450
8451 "number" | "abs" | "floor" | "ceil" | "round" | "power" | "sqrt" |
8453 "random" | "formatNumber" | "formatBase" | "formatInteger" | "parseInteger" |
8454
8455 "sum" | "max" | "min" | "average" |
8457
8458 "boolean" | "not" | "exists" |
8460
8461 "count" | "append" | "sort" | "reverse" | "shuffle" | "distinct" | "zip" |
8463
8464 "keys" | "lookup" | "spread" | "merge" | "sift" | "each" | "error" | "assert" | "type" |
8466
8467 "map" | "filter" | "reduce" | "singletonArray" |
8469
8470 "now" | "millis" | "fromMillis" | "toMillis"
8472 )
8473 }
8474
8475 fn call_builtin_with_values(
8478 &mut self,
8479 name: &str,
8480 values: &[JValue],
8481 ) -> Result<JValue, EvaluatorError> {
8482 use crate::functions;
8483
8484 if values.is_empty() {
8485 return Err(EvaluatorError::EvaluationError(format!(
8486 "{}() requires at least 1 argument",
8487 name
8488 )));
8489 }
8490
8491 let arg = &values[0];
8492
8493 match name {
8494 "string" => Ok(functions::string::string(arg, None)?),
8495 "number" => Ok(functions::numeric::number(arg)?),
8496 "boolean" => Ok(functions::boolean::boolean(arg)?),
8497 "not" => {
8498 let b = functions::boolean::boolean(arg)?;
8499 match b {
8500 JValue::Bool(val) => Ok(JValue::Bool(!val)),
8501 _ => Err(EvaluatorError::TypeError(
8502 "not() requires a boolean".to_string(),
8503 )),
8504 }
8505 }
8506 "exists" => Ok(JValue::Bool(!arg.is_null())),
8507 "abs" => match arg {
8508 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
8509 _ => Err(EvaluatorError::TypeError(
8510 "abs() requires a number argument".to_string(),
8511 )),
8512 },
8513 "floor" => match arg {
8514 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
8515 _ => Err(EvaluatorError::TypeError(
8516 "floor() requires a number argument".to_string(),
8517 )),
8518 },
8519 "ceil" => match arg {
8520 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
8521 _ => Err(EvaluatorError::TypeError(
8522 "ceil() requires a number argument".to_string(),
8523 )),
8524 },
8525 "round" => match arg {
8526 JValue::Number(n) => Ok(functions::numeric::round(*n, None)?),
8527 _ => Err(EvaluatorError::TypeError(
8528 "round() requires a number argument".to_string(),
8529 )),
8530 },
8531 "sqrt" => match arg {
8532 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
8533 _ => Err(EvaluatorError::TypeError(
8534 "sqrt() requires a number argument".to_string(),
8535 )),
8536 },
8537 "uppercase" => match arg {
8538 JValue::String(s) => Ok(JValue::string(s.to_uppercase())),
8539 JValue::Null => Ok(JValue::Null),
8540 _ => Err(EvaluatorError::TypeError(
8541 "uppercase() requires a string argument".to_string(),
8542 )),
8543 },
8544 "lowercase" => match arg {
8545 JValue::String(s) => Ok(JValue::string(s.to_lowercase())),
8546 JValue::Null => Ok(JValue::Null),
8547 _ => Err(EvaluatorError::TypeError(
8548 "lowercase() requires a string argument".to_string(),
8549 )),
8550 },
8551 "trim" => match arg {
8552 JValue::String(s) => Ok(JValue::string(s.trim().to_string())),
8553 JValue::Null => Ok(JValue::Null),
8554 _ => Err(EvaluatorError::TypeError(
8555 "trim() requires a string argument".to_string(),
8556 )),
8557 },
8558 "length" => match arg {
8559 JValue::String(s) => Ok(JValue::Number(s.chars().count() as f64)),
8560 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
8561 JValue::Null => Ok(JValue::Null),
8562 _ => Err(EvaluatorError::TypeError(
8563 "length() requires a string or array argument".to_string(),
8564 )),
8565 },
8566 "sum" => match arg {
8567 JValue::Array(arr) => {
8568 let mut total = 0.0;
8569 for item in arr.iter() {
8570 match item {
8571 JValue::Number(n) => {
8572 total += *n;
8573 }
8574 _ => {
8575 return Err(EvaluatorError::TypeError(
8576 "sum() requires all array elements to be numbers".to_string(),
8577 ));
8578 }
8579 }
8580 }
8581 Ok(JValue::Number(total))
8582 }
8583 JValue::Number(n) => Ok(JValue::Number(*n)),
8584 JValue::Null => Ok(JValue::Null),
8585 _ => Err(EvaluatorError::TypeError(
8586 "sum() requires an array of numbers".to_string(),
8587 )),
8588 },
8589 "count" => {
8590 match arg {
8591 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
8592 JValue::Null => Ok(JValue::Number(0.0)),
8593 _ => Ok(JValue::Number(1.0)), }
8595 }
8596 "max" => match arg {
8597 JValue::Array(arr) => {
8598 let mut max_val: Option<f64> = None;
8599 for item in arr.iter() {
8600 if let JValue::Number(n) = item {
8601 let f = *n;
8602 max_val = Some(max_val.map_or(f, |m| m.max(f)));
8603 }
8604 }
8605 max_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
8606 }
8607 JValue::Number(n) => Ok(JValue::Number(*n)),
8608 JValue::Null => Ok(JValue::Null),
8609 _ => Err(EvaluatorError::TypeError(
8610 "max() requires an array of numbers".to_string(),
8611 )),
8612 },
8613 "min" => match arg {
8614 JValue::Array(arr) => {
8615 let mut min_val: Option<f64> = None;
8616 for item in arr.iter() {
8617 if let JValue::Number(n) = item {
8618 let f = *n;
8619 min_val = Some(min_val.map_or(f, |m| m.min(f)));
8620 }
8621 }
8622 min_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
8623 }
8624 JValue::Number(n) => Ok(JValue::Number(*n)),
8625 JValue::Null => Ok(JValue::Null),
8626 _ => Err(EvaluatorError::TypeError(
8627 "min() requires an array of numbers".to_string(),
8628 )),
8629 },
8630 "average" => match arg {
8631 JValue::Array(arr) => {
8632 let nums: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
8633 if nums.is_empty() {
8634 Ok(JValue::Null)
8635 } else {
8636 let avg = nums.iter().sum::<f64>() / nums.len() as f64;
8637 Ok(JValue::Number(avg))
8638 }
8639 }
8640 JValue::Number(n) => Ok(JValue::Number(*n)),
8641 JValue::Null => Ok(JValue::Null),
8642 _ => Err(EvaluatorError::TypeError(
8643 "average() requires an array of numbers".to_string(),
8644 )),
8645 },
8646 "append" => {
8647 if values.len() < 2 {
8649 return Err(EvaluatorError::EvaluationError(
8650 "append() requires 2 arguments".to_string(),
8651 ));
8652 }
8653 let first = &values[0];
8654 let second = &values[1];
8655
8656 let mut result = match first {
8658 JValue::Array(arr) => arr.to_vec(),
8659 JValue::Null => vec![],
8660 other => vec![other.clone()],
8661 };
8662
8663 match second {
8665 JValue::Array(arr) => result.extend(arr.iter().cloned()),
8666 JValue::Null => {}
8667 other => result.push(other.clone()),
8668 }
8669
8670 Ok(JValue::array(result))
8671 }
8672 "reverse" => match arg {
8673 JValue::Array(arr) => {
8674 let mut reversed = arr.to_vec();
8675 reversed.reverse();
8676 Ok(JValue::array(reversed))
8677 }
8678 JValue::Null => Ok(JValue::Null),
8679 _ => Err(EvaluatorError::TypeError(
8680 "reverse() requires an array".to_string(),
8681 )),
8682 },
8683 "keys" => match arg {
8684 JValue::Object(obj) => {
8685 let keys: Vec<JValue> = obj.keys().map(|k| JValue::string(k.clone())).collect();
8686 Ok(JValue::array(keys))
8687 }
8688 JValue::Null => Ok(JValue::Null),
8689 _ => Err(EvaluatorError::TypeError(
8690 "keys() requires an object".to_string(),
8691 )),
8692 },
8693
8694 _ => Err(EvaluatorError::ReferenceError(format!(
8696 "Built-in function {} cannot be called with values directly",
8697 name
8698 ))),
8699 }
8700 }
8701
8702 fn collect_descendants(&self, value: &JValue) -> Vec<JValue> {
8704 let mut descendants = Vec::new();
8705
8706 match value {
8707 JValue::Null => {
8708 return descendants;
8710 }
8711 JValue::Object(obj) => {
8712 descendants.push(value.clone());
8714
8715 for val in obj.values() {
8716 descendants.extend(self.collect_descendants(val));
8718 }
8719 }
8720 JValue::Array(arr) => {
8721 for val in arr.iter() {
8724 descendants.extend(self.collect_descendants(val));
8726 }
8727 }
8728 _ => {
8729 descendants.push(value.clone());
8731 }
8732 }
8733
8734 descendants
8735 }
8736
8737 fn evaluate_predicate(
8739 &mut self,
8740 current: &JValue,
8741 predicate: &AstNode,
8742 ) -> Result<JValue, EvaluatorError> {
8743 if matches!(predicate, AstNode::Boolean(true)) {
8746 return match current {
8747 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
8748 JValue::Null => Ok(JValue::Null),
8749 other => Ok(JValue::array(vec![other.clone()])),
8750 };
8751 }
8752
8753 match current {
8754 JValue::Array(_arr) => {
8755 if let AstNode::Number(n) = predicate {
8759 return self.array_index(current, &JValue::Number(*n));
8761 }
8762
8763 if Self::is_filter_predicate(predicate) {
8766 if let Some(compiled) = try_compile_expr(predicate) {
8768 let shape = _arr.first().and_then(build_shape_cache);
8769 let mut filtered = Vec::with_capacity(_arr.len());
8770 for item in _arr.iter() {
8771 let result = if let Some(ref s) = shape {
8772 eval_compiled_shaped(&compiled, item, None, s)?
8773 } else {
8774 eval_compiled(&compiled, item, None)?
8775 };
8776 if compiled_is_truthy(&result) {
8777 filtered.push(item.clone());
8778 }
8779 }
8780 return Ok(JValue::array(filtered));
8781 }
8782 let mut filtered = Vec::new();
8784 for item in _arr.iter() {
8785 let item_result = self.evaluate_internal(predicate, item)?;
8786 if self.is_truthy(&item_result) {
8787 filtered.push(item.clone());
8788 }
8789 }
8790 return Ok(JValue::array(filtered));
8791 }
8792
8793 match self.evaluate_internal(predicate, current) {
8797 Ok(JValue::Number(_)) => {
8798 let pred_result = self.evaluate_internal(predicate, current)?;
8800 return self.array_index(current, &pred_result);
8801 }
8802 Ok(JValue::Array(indices)) => {
8803 let has_non_numeric =
8806 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
8807
8808 if has_non_numeric {
8809 return Ok(current.clone());
8811 }
8812
8813 let arr_len = _arr.len() as i64;
8815 let mut resolved_indices: Vec<i64> = indices
8816 .iter()
8817 .filter_map(|v| {
8818 if let JValue::Number(n) = v {
8819 let idx = *n as i64;
8820 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
8822 if actual_idx >= 0 && actual_idx < arr_len {
8824 Some(actual_idx)
8825 } else {
8826 None
8827 }
8828 } else {
8829 None
8830 }
8831 })
8832 .collect();
8833
8834 resolved_indices.sort();
8836 resolved_indices.dedup();
8837
8838 let result: Vec<JValue> = resolved_indices
8840 .iter()
8841 .map(|&idx| _arr[idx as usize].clone())
8842 .collect();
8843
8844 return Ok(JValue::array(result));
8845 }
8846 Ok(_) => {
8847 }
8850 Err(_) => {
8851 }
8854 }
8855
8856 if let Some(compiled) = try_compile_expr(predicate) {
8858 let shape = _arr.first().and_then(build_shape_cache);
8859 let mut filtered = Vec::with_capacity(_arr.len());
8860 for item in _arr.iter() {
8861 let result = if let Some(ref s) = shape {
8862 eval_compiled_shaped(&compiled, item, None, s)?
8863 } else {
8864 eval_compiled(&compiled, item, None)?
8865 };
8866 if compiled_is_truthy(&result) {
8867 filtered.push(item.clone());
8868 }
8869 }
8870 return Ok(JValue::array(filtered));
8871 }
8872
8873 let mut filtered = Vec::new();
8875 for item in _arr.iter() {
8876 let item_result = self.evaluate_internal(predicate, item)?;
8877
8878 if self.is_truthy(&item_result) {
8880 filtered.push(item.clone());
8881 }
8882 }
8883
8884 Ok(JValue::array(filtered))
8885 }
8886 JValue::Object(obj) => {
8887 let pred_result = self.evaluate_internal(predicate, current)?;
8891
8892 if let JValue::String(key) = &pred_result {
8894 return Ok(obj.get(&**key).cloned().unwrap_or(JValue::Null));
8895 }
8896
8897 if self.is_truthy(&pred_result) {
8900 Ok(current.clone())
8901 } else {
8902 Ok(JValue::Undefined)
8903 }
8904 }
8905 _ => {
8906 if let AstNode::Number(n) = predicate {
8912 let idx = n.floor() as i64;
8914 if idx == 0 || idx == -1 {
8915 return Ok(current.clone());
8916 } else {
8917 return Ok(JValue::Undefined);
8918 }
8919 }
8920
8921 let pred_result = self.evaluate_internal(predicate, current)?;
8923
8924 if let JValue::Number(n) = &pred_result {
8925 let idx = n.floor() as i64;
8927 if idx == 0 || idx == -1 {
8928 return Ok(current.clone());
8929 } else {
8930 return Ok(JValue::Undefined);
8931 }
8932 }
8933
8934 if self.is_truthy(&pred_result) {
8938 Ok(current.clone())
8939 } else {
8940 Ok(JValue::Undefined)
8942 }
8943 }
8944 }
8945 }
8946
8947 fn evaluate_sort_term(
8950 &mut self,
8951 term_expr: &AstNode,
8952 element: &JValue,
8953 ) -> Result<JValue, EvaluatorError> {
8954 let actual_element = if let JValue::Object(obj) = element {
8956 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
8957 obj.get("@").cloned().unwrap_or(JValue::Null)
8958 } else {
8959 element.clone()
8960 }
8961 } else {
8962 element.clone()
8963 };
8964
8965 if let AstNode::Path { steps } = term_expr {
8967 if steps.len() == 1 && steps[0].stages.is_empty() {
8968 if let AstNode::Name(field_name) = &steps[0].node {
8969 if let JValue::Object(obj) = &actual_element {
8971 return match obj.get(field_name) {
8972 Some(val) => Ok(val.clone()), None => Ok(JValue::Undefined), };
8975 } else {
8976 return Ok(JValue::Undefined);
8978 }
8979 }
8980 }
8981 }
8982
8983 let result = self.evaluate_internal(term_expr, element)?;
8986
8987 if result.is_null() {
8992 return Ok(JValue::Undefined);
8993 }
8994
8995 Ok(result)
8996 }
8997
8998 fn evaluate_sort(
9000 &mut self,
9001 data: &JValue,
9002 terms: &[(AstNode, bool)],
9003 ) -> Result<JValue, EvaluatorError> {
9004 if data.is_null() {
9006 return Ok(JValue::Null);
9007 }
9008
9009 let array = match data {
9011 JValue::Array(arr) => arr.clone(),
9012 other => return Ok(other.clone()),
9013 };
9014
9015 if array.is_empty() {
9017 return Ok(JValue::Array(array));
9018 }
9019
9020 let mut indexed_array: Vec<(usize, Vec<JValue>)> = Vec::new();
9022
9023 for (idx, element) in array.iter().enumerate() {
9024 let mut sort_keys = Vec::new();
9025
9026 for (term_expr, _ascending) in terms {
9028 let saved_dollar = self.context.lookup("$").cloned();
9030
9031 self.context.bind("$".to_string(), element.clone());
9033
9034 let sort_value = self.evaluate_sort_term(term_expr, element)?;
9036
9037 if let Some(val) = saved_dollar {
9039 self.context.bind("$".to_string(), val);
9040 } else {
9041 self.context.unbind("$");
9042 }
9043
9044 sort_keys.push(sort_value);
9045 }
9046
9047 indexed_array.push((idx, sort_keys));
9048 }
9049
9050 for term_idx in 0..terms.len() {
9054 let mut first_valid_type: Option<&str> = None;
9055
9056 for (_idx, sort_keys) in &indexed_array {
9057 let sort_value = &sort_keys[term_idx];
9058
9059 if sort_value.is_undefined() {
9061 continue;
9062 }
9063
9064 let value_type = match sort_value {
9067 JValue::Number(_) => "number",
9068 JValue::String(_) => "string",
9069 JValue::Bool(_) => "boolean",
9070 JValue::Array(_) => "array",
9071 JValue::Object(_) => "object", JValue::Null => "null", _ => "unknown",
9074 };
9075
9076 if value_type != "number" && value_type != "string" {
9079 return Err(EvaluatorError::TypeError("T2008: The expressions within an order-by clause must evaluate to numeric or string values".to_string()));
9080 }
9081
9082 if let Some(first_type) = first_valid_type {
9084 if first_type != value_type {
9085 return Err(EvaluatorError::TypeError(format!(
9086 "T2007: Type mismatch when comparing values in order-by clause: {} and {}",
9087 first_type, value_type
9088 )));
9089 }
9090 } else {
9091 first_valid_type = Some(value_type);
9092 }
9093 }
9094 }
9095
9096 indexed_array.sort_by(|a, b| {
9098 for (i, (_term_expr, ascending)) in terms.iter().enumerate() {
9100 let left = &a.1[i];
9101 let right = &b.1[i];
9102
9103 let cmp = self.compare_values(left, right);
9104
9105 if cmp != std::cmp::Ordering::Equal {
9106 return if *ascending { cmp } else { cmp.reverse() };
9107 }
9108 }
9109
9110 a.0.cmp(&b.0)
9112 });
9113
9114 let sorted: Vec<JValue> = indexed_array
9116 .iter()
9117 .map(|(idx, _)| array[*idx].clone())
9118 .collect();
9119
9120 Ok(JValue::array(sorted))
9121 }
9122
9123 fn compare_values(&self, left: &JValue, right: &JValue) -> Ordering {
9125 let left_undef = left.is_undefined();
9127 let right_undef = right.is_undefined();
9128
9129 if left_undef && right_undef {
9130 return Ordering::Equal;
9131 }
9132 if left_undef {
9133 return Ordering::Greater; }
9135 if right_undef {
9136 return Ordering::Less;
9137 }
9138
9139 match (left, right) {
9140 (JValue::Null, JValue::Null) => Ordering::Equal,
9142 (JValue::Null, _) => Ordering::Greater,
9143 (_, JValue::Null) => Ordering::Less,
9144
9145 (JValue::Number(a), JValue::Number(b)) => {
9147 let a_f64 = *a;
9148 let b_f64 = *b;
9149 a_f64.partial_cmp(&b_f64).unwrap_or(Ordering::Equal)
9150 }
9151
9152 (JValue::String(a), JValue::String(b)) => a.cmp(b),
9154
9155 (JValue::Bool(a), JValue::Bool(b)) => a.cmp(b),
9157
9158 (JValue::Array(a), JValue::Array(b)) => {
9160 for (a_elem, b_elem) in a.iter().zip(b.iter()) {
9161 let cmp = self.compare_values(a_elem, b_elem);
9162 if cmp != Ordering::Equal {
9163 return cmp;
9164 }
9165 }
9166 a.len().cmp(&b.len())
9167 }
9168
9169 (JValue::Bool(_), JValue::Number(_)) => Ordering::Less,
9172 (JValue::Bool(_), JValue::String(_)) => Ordering::Less,
9173 (JValue::Bool(_), JValue::Array(_)) => Ordering::Less,
9174 (JValue::Bool(_), JValue::Object(_)) => Ordering::Less,
9175
9176 (JValue::Number(_), JValue::Bool(_)) => Ordering::Greater,
9177 (JValue::Number(_), JValue::String(_)) => Ordering::Less,
9178 (JValue::Number(_), JValue::Array(_)) => Ordering::Less,
9179 (JValue::Number(_), JValue::Object(_)) => Ordering::Less,
9180
9181 (JValue::String(_), JValue::Bool(_)) => Ordering::Greater,
9182 (JValue::String(_), JValue::Number(_)) => Ordering::Greater,
9183 (JValue::String(_), JValue::Array(_)) => Ordering::Less,
9184 (JValue::String(_), JValue::Object(_)) => Ordering::Less,
9185
9186 (JValue::Array(_), JValue::Bool(_)) => Ordering::Greater,
9187 (JValue::Array(_), JValue::Number(_)) => Ordering::Greater,
9188 (JValue::Array(_), JValue::String(_)) => Ordering::Greater,
9189 (JValue::Array(_), JValue::Object(_)) => Ordering::Less,
9190
9191 (JValue::Object(_), _) => Ordering::Greater,
9192 _ => Ordering::Equal,
9193 }
9194 }
9195
9196 fn is_truthy(&self, value: &JValue) -> bool {
9198 match value {
9199 JValue::Null | JValue::Undefined => false,
9200 JValue::Bool(b) => *b,
9201 JValue::Number(n) => *n != 0.0,
9202 JValue::String(s) => !s.is_empty(),
9203 JValue::Array(arr) => !arr.is_empty(),
9204 JValue::Object(obj) => !obj.is_empty(),
9205 _ => false,
9206 }
9207 }
9208
9209 fn is_truthy_for_default(&self, value: &JValue) -> bool {
9215 match value {
9216 JValue::Lambda { .. } | JValue::Builtin { .. } => false,
9218 JValue::Array(arr) => {
9220 if arr.is_empty() {
9221 return false;
9222 }
9223 arr.iter().any(|elem| self.is_truthy(elem))
9225 }
9226 _ => self.is_truthy(value),
9228 }
9229 }
9230
9231 fn unwrap_singleton(&self, value: JValue) -> JValue {
9234 match value {
9235 JValue::Array(ref arr) if arr.len() == 1 => arr[0].clone(),
9236 _ => value,
9237 }
9238 }
9239
9240 fn extract_lambda_ids(&self, value: &JValue) -> Vec<String> {
9244 match value {
9246 JValue::Number(_) | JValue::Bool(_) | JValue::String(_)
9247 | JValue::Null | JValue::Undefined | JValue::Regex { .. }
9248 | JValue::Builtin { .. } => return Vec::new(),
9249 _ => {}
9250 }
9251 let mut ids = Vec::new();
9252 self.collect_lambda_ids(value, &mut ids);
9253 ids
9254 }
9255
9256 fn collect_lambda_ids(&self, value: &JValue, ids: &mut Vec<String>) {
9257 match value {
9258 JValue::Lambda { lambda_id, .. } => {
9259 let id_str = lambda_id.to_string();
9260 if !ids.contains(&id_str) {
9261 ids.push(id_str);
9262 if let Some(stored) = self.context.lookup_lambda(lambda_id) {
9267 let env_values: Vec<JValue> =
9268 stored.captured_env.values().cloned().collect();
9269 for env_value in &env_values {
9270 self.collect_lambda_ids(env_value, ids);
9271 }
9272 }
9273 }
9274 }
9275 JValue::Object(map) => {
9276 for v in map.values() {
9278 self.collect_lambda_ids(v, ids);
9279 }
9280 }
9281 JValue::Array(arr) => {
9282 for v in arr.iter() {
9284 self.collect_lambda_ids(v, ids);
9285 }
9286 }
9287 _ => {}
9288 }
9289 }
9290
9291 fn equals(&self, left: &JValue, right: &JValue) -> bool {
9293 crate::functions::array::values_equal(left, right)
9294 }
9295
9296 fn add(
9298 &self,
9299 left: &JValue,
9300 right: &JValue,
9301 left_is_explicit_null: bool,
9302 right_is_explicit_null: bool,
9303 ) -> Result<JValue, EvaluatorError> {
9304 match (left, right) {
9305 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a + *b)),
9306 (JValue::Null, JValue::Number(_)) if left_is_explicit_null => {
9308 Err(EvaluatorError::TypeError(
9309 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9310 ))
9311 }
9312 (JValue::Number(_), JValue::Null) if right_is_explicit_null => {
9313 Err(EvaluatorError::TypeError(
9314 "T2002: The right side of the + operator must evaluate to a number".to_string(),
9315 ))
9316 }
9317 (JValue::Null, JValue::Null) if left_is_explicit_null || right_is_explicit_null => {
9318 Err(EvaluatorError::TypeError(
9319 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9320 ))
9321 }
9322 (JValue::Null, JValue::Number(_)) | (JValue::Number(_), JValue::Null) => {
9324 Ok(JValue::Null)
9325 }
9326 (JValue::Bool(_), _) => Err(EvaluatorError::TypeError(
9328 "T2001: The left side of the '+' operator must evaluate to a number or a string"
9329 .to_string(),
9330 )),
9331 (_, JValue::Bool(_)) => Err(EvaluatorError::TypeError(
9332 "T2001: The right side of the '+' operator must evaluate to a number or a string"
9333 .to_string(),
9334 )),
9335 (JValue::Null, JValue::Null) => Ok(JValue::Null),
9337 _ => Err(EvaluatorError::TypeError(format!(
9338 "Cannot add {:?} and {:?}",
9339 left, right
9340 ))),
9341 }
9342 }
9343
9344 fn subtract(
9346 &self,
9347 left: &JValue,
9348 right: &JValue,
9349 left_is_explicit_null: bool,
9350 right_is_explicit_null: bool,
9351 ) -> Result<JValue, EvaluatorError> {
9352 match (left, right) {
9353 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a - *b)),
9354 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9356 "T2002: The left side of the - operator must evaluate to a number".to_string(),
9357 )),
9358 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9359 "T2002: The right side of the - operator must evaluate to a number".to_string(),
9360 )),
9361 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9363 _ => Err(EvaluatorError::TypeError(format!(
9364 "Cannot subtract {:?} and {:?}",
9365 left, right
9366 ))),
9367 }
9368 }
9369
9370 fn multiply(
9372 &self,
9373 left: &JValue,
9374 right: &JValue,
9375 left_is_explicit_null: bool,
9376 right_is_explicit_null: bool,
9377 ) -> Result<JValue, EvaluatorError> {
9378 match (left, right) {
9379 (JValue::Number(a), JValue::Number(b)) => {
9380 let result = *a * *b;
9381 if result.is_infinite() {
9383 return Err(EvaluatorError::EvaluationError(
9384 "D1001: Number out of range".to_string(),
9385 ));
9386 }
9387 Ok(JValue::Number(result))
9388 }
9389 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9391 "T2002: The left side of the * operator must evaluate to a number".to_string(),
9392 )),
9393 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9394 "T2002: The right side of the * operator must evaluate to a number".to_string(),
9395 )),
9396 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9398 _ => Err(EvaluatorError::TypeError(format!(
9399 "Cannot multiply {:?} and {:?}",
9400 left, right
9401 ))),
9402 }
9403 }
9404
9405 fn divide(
9407 &self,
9408 left: &JValue,
9409 right: &JValue,
9410 left_is_explicit_null: bool,
9411 right_is_explicit_null: bool,
9412 ) -> Result<JValue, EvaluatorError> {
9413 match (left, right) {
9414 (JValue::Number(a), JValue::Number(b)) => {
9415 let denominator = *b;
9416 if denominator == 0.0 {
9417 return Err(EvaluatorError::EvaluationError(
9418 "Division by zero".to_string(),
9419 ));
9420 }
9421 Ok(JValue::Number(*a / denominator))
9422 }
9423 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9425 "T2002: The left side of the / operator must evaluate to a number".to_string(),
9426 )),
9427 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9428 "T2002: The right side of the / operator must evaluate to a number".to_string(),
9429 )),
9430 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9432 _ => Err(EvaluatorError::TypeError(format!(
9433 "Cannot divide {:?} and {:?}",
9434 left, right
9435 ))),
9436 }
9437 }
9438
9439 fn modulo(
9441 &self,
9442 left: &JValue,
9443 right: &JValue,
9444 left_is_explicit_null: bool,
9445 right_is_explicit_null: bool,
9446 ) -> Result<JValue, EvaluatorError> {
9447 match (left, right) {
9448 (JValue::Number(a), JValue::Number(b)) => {
9449 let denominator = *b;
9450 if denominator == 0.0 {
9451 return Err(EvaluatorError::EvaluationError(
9452 "Division by zero".to_string(),
9453 ));
9454 }
9455 Ok(JValue::Number(*a % denominator))
9456 }
9457 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9459 "T2002: The left side of the % operator must evaluate to a number".to_string(),
9460 )),
9461 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9462 "T2002: The right side of the % operator must evaluate to a number".to_string(),
9463 )),
9464 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9466 _ => Err(EvaluatorError::TypeError(format!(
9467 "Cannot compute modulo of {:?} and {:?}",
9468 left, right
9469 ))),
9470 }
9471 }
9472
9473 fn type_name(value: &JValue) -> &'static str {
9475 match value {
9476 JValue::Null => "null",
9477 JValue::Bool(_) => "boolean",
9478 JValue::Number(_) => "number",
9479 JValue::String(_) => "string",
9480 JValue::Array(_) => "array",
9481 JValue::Object(_) => "object",
9482 _ => "unknown",
9483 }
9484 }
9485
9486 fn ordered_compare(
9492 &self,
9493 left: &JValue,
9494 right: &JValue,
9495 left_is_explicit_null: bool,
9496 right_is_explicit_null: bool,
9497 op_symbol: &str,
9498 compare_nums: fn(f64, f64) -> bool,
9499 compare_strs: fn(&str, &str) -> bool,
9500 ) -> Result<JValue, EvaluatorError> {
9501 match (left, right) {
9502 (JValue::Number(a), JValue::Number(b)) => {
9503 Ok(JValue::Bool(compare_nums(*a, *b)))
9504 }
9505 (JValue::String(a), JValue::String(b)) => Ok(JValue::Bool(compare_strs(a, b))),
9506 (JValue::Null, JValue::Null) => Ok(JValue::Null),
9508 (JValue::Null, _) if left_is_explicit_null => {
9510 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9511 }
9512 (_, JValue::Null) if right_is_explicit_null => {
9513 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9514 }
9515 (JValue::Bool(_), JValue::Null) | (JValue::Null, JValue::Bool(_)) => {
9517 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9518 }
9519 (JValue::Number(_), JValue::Null) | (JValue::Null, JValue::Number(_)) |
9521 (JValue::String(_), JValue::Null) | (JValue::Null, JValue::String(_)) => {
9522 Ok(JValue::Null)
9523 }
9524 (JValue::String(_), JValue::Number(_)) | (JValue::Number(_), JValue::String(_)) => {
9526 Err(EvaluatorError::EvaluationError(format!(
9527 "T2009: The expressions on either side of operator \"{}\" must be of the same data type",
9528 op_symbol
9529 )))
9530 }
9531 (JValue::Bool(_), _) | (_, JValue::Bool(_)) => {
9533 Err(EvaluatorError::EvaluationError(format!(
9534 "T2010: Cannot compare {} and {}",
9535 Self::type_name(left), Self::type_name(right)
9536 )))
9537 }
9538 _ => Err(EvaluatorError::EvaluationError(format!(
9540 "T2010: Cannot compare {} and {}",
9541 Self::type_name(left), Self::type_name(right)
9542 ))),
9543 }
9544 }
9545
9546 fn less_than(
9548 &self,
9549 left: &JValue,
9550 right: &JValue,
9551 left_is_explicit_null: bool,
9552 right_is_explicit_null: bool,
9553 ) -> Result<JValue, EvaluatorError> {
9554 self.ordered_compare(
9555 left,
9556 right,
9557 left_is_explicit_null,
9558 right_is_explicit_null,
9559 "<",
9560 |a, b| a < b,
9561 |a, b| a < b,
9562 )
9563 }
9564
9565 fn less_than_or_equal(
9567 &self,
9568 left: &JValue,
9569 right: &JValue,
9570 left_is_explicit_null: bool,
9571 right_is_explicit_null: bool,
9572 ) -> Result<JValue, EvaluatorError> {
9573 self.ordered_compare(
9574 left,
9575 right,
9576 left_is_explicit_null,
9577 right_is_explicit_null,
9578 "<=",
9579 |a, b| a <= b,
9580 |a, b| a <= b,
9581 )
9582 }
9583
9584 fn greater_than(
9586 &self,
9587 left: &JValue,
9588 right: &JValue,
9589 left_is_explicit_null: bool,
9590 right_is_explicit_null: bool,
9591 ) -> Result<JValue, EvaluatorError> {
9592 self.ordered_compare(
9593 left,
9594 right,
9595 left_is_explicit_null,
9596 right_is_explicit_null,
9597 ">",
9598 |a, b| a > b,
9599 |a, b| a > b,
9600 )
9601 }
9602
9603 fn greater_than_or_equal(
9605 &self,
9606 left: &JValue,
9607 right: &JValue,
9608 left_is_explicit_null: bool,
9609 right_is_explicit_null: bool,
9610 ) -> Result<JValue, EvaluatorError> {
9611 self.ordered_compare(
9612 left,
9613 right,
9614 left_is_explicit_null,
9615 right_is_explicit_null,
9616 ">=",
9617 |a, b| a >= b,
9618 |a, b| a >= b,
9619 )
9620 }
9621
9622 fn value_to_concat_string(value: &JValue) -> Result<String, EvaluatorError> {
9624 match value {
9625 JValue::String(s) => Ok(s.to_string()),
9626 JValue::Null => Ok(String::new()),
9627 JValue::Number(_) | JValue::Bool(_) | JValue::Array(_) | JValue::Object(_) => {
9628 match crate::functions::string::string(value, None) {
9629 Ok(JValue::String(s)) => Ok(s.to_string()),
9630 Ok(JValue::Null) => Ok(String::new()),
9631 _ => Err(EvaluatorError::TypeError(
9632 "Cannot concatenate complex types".to_string(),
9633 )),
9634 }
9635 }
9636 _ => Ok(String::new()),
9637 }
9638 }
9639
9640 fn concatenate(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
9642 let left_str = Self::value_to_concat_string(left)?;
9643 let right_str = Self::value_to_concat_string(right)?;
9644 Ok(JValue::string(format!("{}{}", left_str, right_str)))
9645 }
9646
9647 fn range(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
9649 let start_f64 = match left {
9651 JValue::Number(n) => Some(*n),
9652 JValue::Null => None,
9653 _ => {
9654 return Err(EvaluatorError::EvaluationError(
9655 "T2003: Left operand of range operator must be a number".to_string(),
9656 ));
9657 }
9658 };
9659
9660 if let Some(val) = start_f64 {
9662 if val.fract() != 0.0 {
9663 return Err(EvaluatorError::EvaluationError(
9664 "T2003: Left operand of range operator must be an integer".to_string(),
9665 ));
9666 }
9667 }
9668
9669 let end_f64 = match right {
9671 JValue::Number(n) => Some(*n),
9672 JValue::Null => None,
9673 _ => {
9674 return Err(EvaluatorError::EvaluationError(
9675 "T2004: Right operand of range operator must be a number".to_string(),
9676 ));
9677 }
9678 };
9679
9680 if let Some(val) = end_f64 {
9682 if val.fract() != 0.0 {
9683 return Err(EvaluatorError::EvaluationError(
9684 "T2004: Right operand of range operator must be an integer".to_string(),
9685 ));
9686 }
9687 }
9688
9689 if start_f64.is_none() || end_f64.is_none() {
9691 return Ok(JValue::array(vec![]));
9692 }
9693
9694 let start = start_f64.unwrap() as i64;
9695 let end = end_f64.unwrap() as i64;
9696
9697 let size = if start <= end {
9699 (end - start + 1) as usize
9700 } else {
9701 0
9702 };
9703 if size > 10_000_000 {
9704 return Err(EvaluatorError::EvaluationError(
9705 "D2014: Range operator results in too many elements (> 10,000,000)".to_string(),
9706 ));
9707 }
9708
9709 let mut result = Vec::with_capacity(size);
9710 if start <= end {
9711 for i in start..=end {
9712 result.push(JValue::Number(i as f64));
9713 }
9714 }
9715 Ok(JValue::array(result))
9717 }
9718
9719 fn array_index(&self, array: &JValue, index: &JValue) -> Result<JValue, EvaluatorError> {
9722 match (array, index) {
9723 (JValue::Array(arr), JValue::Number(n)) => {
9724 let idx = *n as i64;
9725 let len = arr.len() as i64;
9726
9727 let actual_idx = if idx < 0 { len + idx } else { idx };
9729
9730 if actual_idx < 0 || actual_idx >= len {
9731 Ok(JValue::Null)
9732 } else {
9733 Ok(arr[actual_idx as usize].clone())
9734 }
9735 }
9736 _ => Err(EvaluatorError::TypeError(
9737 "Array indexing requires array and number".to_string(),
9738 )),
9739 }
9740 }
9741
9742 fn array_filter(
9745 &mut self,
9746 _lhs_node: &AstNode,
9747 rhs_node: &AstNode,
9748 array: &JValue,
9749 _original_data: &JValue,
9750 ) -> Result<JValue, EvaluatorError> {
9751 match array {
9752 JValue::Array(arr) => {
9753 let mut filtered = Vec::with_capacity(arr.len() / 2);
9755
9756 for item in arr.iter() {
9757 let predicate_result = self.evaluate_internal(rhs_node, item)?;
9760
9761 if self.is_truthy(&predicate_result) {
9763 filtered.push(item.clone());
9764 }
9765 }
9766
9767 Ok(JValue::array(filtered))
9768 }
9769 _ => Err(EvaluatorError::TypeError(
9770 "Array filtering requires an array".to_string(),
9771 )),
9772 }
9773 }
9774
9775 fn in_operator(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
9776 if left.is_null() || right.is_null() {
9779 return Ok(JValue::Bool(false));
9780 }
9781
9782 match right {
9783 JValue::Array(arr) => Ok(JValue::Bool(arr.iter().any(|v| self.equals(left, v)))),
9784 JValue::Object(obj) => {
9785 if let JValue::String(key) = left {
9786 Ok(JValue::Bool(obj.contains_key(&**key)))
9787 } else {
9788 Ok(JValue::Bool(false))
9789 }
9790 }
9791 other => Ok(JValue::Bool(self.equals(left, other))),
9794 }
9795 }
9796
9797 fn create_partial_application(
9801 &mut self,
9802 name: &str,
9803 args: &[AstNode],
9804 is_builtin: bool,
9805 data: &JValue,
9806 ) -> Result<JValue, EvaluatorError> {
9807 let is_lambda = self.context.lookup_lambda(name).is_some()
9809 || (self
9810 .context
9811 .lookup(name)
9812 .map(|v| matches!(v, JValue::Lambda { .. }))
9813 .unwrap_or(false));
9814
9815 if !is_lambda && !is_builtin {
9818 if self.is_builtin_function(name) {
9820 return Err(EvaluatorError::EvaluationError(format!(
9821 "T1007: Attempted to partially apply a non-function. Did you mean ${}?",
9822 name
9823 )));
9824 }
9825 return Err(EvaluatorError::EvaluationError(
9826 "T1008: Attempted to partially apply a non-function".to_string(),
9827 ));
9828 }
9829
9830 let mut bound_args: Vec<(usize, JValue)> = Vec::new();
9832 let mut placeholder_positions: Vec<usize> = Vec::new();
9833
9834 for (i, arg) in args.iter().enumerate() {
9835 if matches!(arg, AstNode::Placeholder) {
9836 placeholder_positions.push(i);
9837 } else {
9838 let value = self.evaluate_internal(arg, data)?;
9839 bound_args.push((i, value));
9840 }
9841 }
9842
9843 let param_names: Vec<String> = placeholder_positions
9845 .iter()
9846 .enumerate()
9847 .map(|(i, _)| format!("__p{}", i))
9848 .collect();
9849
9850 let partial_id = format!(
9853 "__partial_{}_{}_{}",
9854 name,
9855 placeholder_positions.len(),
9856 bound_args.len()
9857 );
9858
9859 let stored_lambda = StoredLambda {
9862 params: param_names.clone(),
9863 body: AstNode::String(format!(
9864 "__partial_call:{}:{}:{}",
9865 name,
9866 is_builtin,
9867 args.len()
9868 )),
9869 compiled_body: None, signature: None,
9871 captured_env: {
9872 let mut env = self.capture_current_environment();
9873 for (pos, value) in &bound_args {
9875 env.insert(format!("__bound_arg_{}", pos), value.clone());
9876 }
9877 env.insert(
9879 "__placeholder_positions".to_string(),
9880 JValue::array(
9881 placeholder_positions
9882 .iter()
9883 .map(|p| JValue::Number(*p as f64))
9884 .collect::<Vec<_>>(),
9885 ),
9886 );
9887 env.insert(
9889 "__total_args".to_string(),
9890 JValue::Number(args.len() as f64),
9891 );
9892 env
9893 },
9894 captured_data: Some(data.clone()),
9895 thunk: false,
9896 };
9897
9898 self.context.bind_lambda(partial_id.clone(), stored_lambda);
9899
9900 let lambda_obj = JValue::lambda(
9902 partial_id.as_str(),
9903 param_names,
9904 Some(name.to_string()),
9905 None::<String>,
9906 );
9907
9908 Ok(lambda_obj)
9909 }
9910}
9911
9912impl Default for Evaluator {
9913 fn default() -> Self {
9914 Self::new()
9915 }
9916}
9917
9918#[cfg(test)]
9919mod tests {
9920 use super::*;
9921 use crate::ast::{BinaryOp, UnaryOp};
9922
9923 #[test]
9924 fn test_evaluate_literals() {
9925 let mut evaluator = Evaluator::new();
9926 let data = JValue::Null;
9927
9928 let result = evaluator
9930 .evaluate(&AstNode::string("hello"), &data)
9931 .unwrap();
9932 assert_eq!(result, JValue::string("hello"));
9933
9934 let result = evaluator.evaluate(&AstNode::number(42.0), &data).unwrap();
9936 assert_eq!(result, JValue::from(42i64));
9937
9938 let result = evaluator.evaluate(&AstNode::boolean(true), &data).unwrap();
9940 assert_eq!(result, JValue::Bool(true));
9941
9942 let result = evaluator.evaluate(&AstNode::null(), &data).unwrap();
9944 assert_eq!(result, JValue::Null);
9945 }
9946
9947 #[test]
9948 fn test_evaluate_variables() {
9949 let mut evaluator = Evaluator::new();
9950 let data = JValue::Null;
9951
9952 evaluator
9954 .context
9955 .bind("x".to_string(), JValue::from(100i64));
9956
9957 let result = evaluator.evaluate(&AstNode::variable("x"), &data).unwrap();
9959 assert_eq!(result, JValue::from(100i64));
9960
9961 let result = evaluator
9963 .evaluate(&AstNode::variable("undefined"), &data)
9964 .unwrap();
9965 assert_eq!(result, JValue::Null);
9966 }
9967
9968 #[test]
9969 fn test_evaluate_path() {
9970 let mut evaluator = Evaluator::new();
9971 let data = JValue::from(serde_json::json!({
9972 "foo": {
9973 "bar": {
9974 "baz": 42
9975 }
9976 }
9977 }));
9978 let path = AstNode::Path {
9980 steps: vec![PathStep::new(AstNode::Name("foo".to_string()))],
9981 };
9982 let result = evaluator.evaluate(&path, &data).unwrap();
9983 assert_eq!(
9984 result,
9985 JValue::from(serde_json::json!({"bar": {"baz": 42}}))
9986 );
9987
9988 let path = AstNode::Path {
9990 steps: vec![
9991 PathStep::new(AstNode::Name("foo".to_string())),
9992 PathStep::new(AstNode::Name("bar".to_string())),
9993 PathStep::new(AstNode::Name("baz".to_string())),
9994 ],
9995 };
9996 let result = evaluator.evaluate(&path, &data).unwrap();
9997 assert_eq!(result, JValue::from(42i64));
9998
9999 let path = AstNode::Path {
10001 steps: vec![PathStep::new(AstNode::Name("missing".to_string()))],
10002 };
10003 let result = evaluator.evaluate(&path, &data).unwrap();
10004 assert_eq!(result, JValue::Null);
10005 }
10006
10007 #[test]
10008 fn test_arithmetic_operations() {
10009 let mut evaluator = Evaluator::new();
10010 let data = JValue::Null;
10011
10012 let expr = AstNode::Binary {
10014 op: BinaryOp::Add,
10015 lhs: Box::new(AstNode::number(10.0)),
10016 rhs: Box::new(AstNode::number(5.0)),
10017 };
10018 let result = evaluator.evaluate(&expr, &data).unwrap();
10019 assert_eq!(result, JValue::Number(15.0));
10020
10021 let expr = AstNode::Binary {
10023 op: BinaryOp::Subtract,
10024 lhs: Box::new(AstNode::number(10.0)),
10025 rhs: Box::new(AstNode::number(5.0)),
10026 };
10027 let result = evaluator.evaluate(&expr, &data).unwrap();
10028 assert_eq!(result, JValue::Number(5.0));
10029
10030 let expr = AstNode::Binary {
10032 op: BinaryOp::Multiply,
10033 lhs: Box::new(AstNode::number(10.0)),
10034 rhs: Box::new(AstNode::number(5.0)),
10035 };
10036 let result = evaluator.evaluate(&expr, &data).unwrap();
10037 assert_eq!(result, JValue::Number(50.0));
10038
10039 let expr = AstNode::Binary {
10041 op: BinaryOp::Divide,
10042 lhs: Box::new(AstNode::number(10.0)),
10043 rhs: Box::new(AstNode::number(5.0)),
10044 };
10045 let result = evaluator.evaluate(&expr, &data).unwrap();
10046 assert_eq!(result, JValue::Number(2.0));
10047
10048 let expr = AstNode::Binary {
10050 op: BinaryOp::Modulo,
10051 lhs: Box::new(AstNode::number(10.0)),
10052 rhs: Box::new(AstNode::number(3.0)),
10053 };
10054 let result = evaluator.evaluate(&expr, &data).unwrap();
10055 assert_eq!(result, JValue::Number(1.0));
10056 }
10057
10058 #[test]
10059 fn test_division_by_zero() {
10060 let mut evaluator = Evaluator::new();
10061 let data = JValue::Null;
10062
10063 let expr = AstNode::Binary {
10064 op: BinaryOp::Divide,
10065 lhs: Box::new(AstNode::number(10.0)),
10066 rhs: Box::new(AstNode::number(0.0)),
10067 };
10068 let result = evaluator.evaluate(&expr, &data);
10069 assert!(result.is_err());
10070 }
10071
10072 #[test]
10073 fn test_comparison_operations() {
10074 let mut evaluator = Evaluator::new();
10075 let data = JValue::Null;
10076
10077 let expr = AstNode::Binary {
10079 op: BinaryOp::Equal,
10080 lhs: Box::new(AstNode::number(5.0)),
10081 rhs: Box::new(AstNode::number(5.0)),
10082 };
10083 assert_eq!(
10084 evaluator.evaluate(&expr, &data).unwrap(),
10085 JValue::Bool(true)
10086 );
10087
10088 let expr = AstNode::Binary {
10090 op: BinaryOp::NotEqual,
10091 lhs: Box::new(AstNode::number(5.0)),
10092 rhs: Box::new(AstNode::number(3.0)),
10093 };
10094 assert_eq!(
10095 evaluator.evaluate(&expr, &data).unwrap(),
10096 JValue::Bool(true)
10097 );
10098
10099 let expr = AstNode::Binary {
10101 op: BinaryOp::LessThan,
10102 lhs: Box::new(AstNode::number(3.0)),
10103 rhs: Box::new(AstNode::number(5.0)),
10104 };
10105 assert_eq!(
10106 evaluator.evaluate(&expr, &data).unwrap(),
10107 JValue::Bool(true)
10108 );
10109
10110 let expr = AstNode::Binary {
10112 op: BinaryOp::GreaterThan,
10113 lhs: Box::new(AstNode::number(5.0)),
10114 rhs: Box::new(AstNode::number(3.0)),
10115 };
10116 assert_eq!(
10117 evaluator.evaluate(&expr, &data).unwrap(),
10118 JValue::Bool(true)
10119 );
10120 }
10121
10122 #[test]
10123 fn test_logical_operations() {
10124 let mut evaluator = Evaluator::new();
10125 let data = JValue::Null;
10126
10127 let expr = AstNode::Binary {
10129 op: BinaryOp::And,
10130 lhs: Box::new(AstNode::boolean(true)),
10131 rhs: Box::new(AstNode::boolean(true)),
10132 };
10133 assert_eq!(
10134 evaluator.evaluate(&expr, &data).unwrap(),
10135 JValue::Bool(true)
10136 );
10137
10138 let expr = AstNode::Binary {
10140 op: BinaryOp::And,
10141 lhs: Box::new(AstNode::boolean(false)),
10142 rhs: Box::new(AstNode::boolean(true)),
10143 };
10144 assert_eq!(
10145 evaluator.evaluate(&expr, &data).unwrap(),
10146 JValue::Bool(false)
10147 );
10148
10149 let expr = AstNode::Binary {
10151 op: BinaryOp::Or,
10152 lhs: Box::new(AstNode::boolean(true)),
10153 rhs: Box::new(AstNode::boolean(false)),
10154 };
10155 assert_eq!(
10156 evaluator.evaluate(&expr, &data).unwrap(),
10157 JValue::Bool(true)
10158 );
10159
10160 let expr = AstNode::Binary {
10162 op: BinaryOp::Or,
10163 lhs: Box::new(AstNode::boolean(false)),
10164 rhs: Box::new(AstNode::boolean(false)),
10165 };
10166 assert_eq!(
10167 evaluator.evaluate(&expr, &data).unwrap(),
10168 JValue::Bool(false)
10169 );
10170 }
10171
10172 #[test]
10173 fn test_string_concatenation() {
10174 let mut evaluator = Evaluator::new();
10175 let data = JValue::Null;
10176
10177 let expr = AstNode::Binary {
10178 op: BinaryOp::Concatenate,
10179 lhs: Box::new(AstNode::string("Hello")),
10180 rhs: Box::new(AstNode::string(" World")),
10181 };
10182 let result = evaluator.evaluate(&expr, &data).unwrap();
10183 assert_eq!(result, JValue::string("Hello World"));
10184 }
10185
10186 #[test]
10187 fn test_range_operator() {
10188 let mut evaluator = Evaluator::new();
10189 let data = JValue::Null;
10190
10191 let expr = AstNode::Binary {
10193 op: BinaryOp::Range,
10194 lhs: Box::new(AstNode::number(1.0)),
10195 rhs: Box::new(AstNode::number(5.0)),
10196 };
10197 let result = evaluator.evaluate(&expr, &data).unwrap();
10198 assert_eq!(
10199 result,
10200 JValue::array(vec![
10201 JValue::Number(1.0),
10202 JValue::Number(2.0),
10203 JValue::Number(3.0),
10204 JValue::Number(4.0),
10205 JValue::Number(5.0)
10206 ])
10207 );
10208
10209 let expr = AstNode::Binary {
10211 op: BinaryOp::Range,
10212 lhs: Box::new(AstNode::number(5.0)),
10213 rhs: Box::new(AstNode::number(1.0)),
10214 };
10215 let result = evaluator.evaluate(&expr, &data).unwrap();
10216 assert_eq!(result, JValue::array(vec![]));
10217 }
10218
10219 #[test]
10220 fn test_in_operator() {
10221 let mut evaluator = Evaluator::new();
10222 let data = JValue::Null;
10223
10224 let expr = AstNode::Binary {
10226 op: BinaryOp::In,
10227 lhs: Box::new(AstNode::number(3.0)),
10228 rhs: Box::new(AstNode::Array(vec![
10229 AstNode::number(1.0),
10230 AstNode::number(2.0),
10231 AstNode::number(3.0),
10232 ])),
10233 };
10234 let result = evaluator.evaluate(&expr, &data).unwrap();
10235 assert_eq!(result, JValue::Bool(true));
10236
10237 let expr = AstNode::Binary {
10239 op: BinaryOp::In,
10240 lhs: Box::new(AstNode::number(5.0)),
10241 rhs: Box::new(AstNode::Array(vec![
10242 AstNode::number(1.0),
10243 AstNode::number(2.0),
10244 AstNode::number(3.0),
10245 ])),
10246 };
10247 let result = evaluator.evaluate(&expr, &data).unwrap();
10248 assert_eq!(result, JValue::Bool(false));
10249 }
10250
10251 #[test]
10252 fn test_unary_operations() {
10253 let mut evaluator = Evaluator::new();
10254 let data = JValue::Null;
10255
10256 let expr = AstNode::Unary {
10258 op: UnaryOp::Negate,
10259 operand: Box::new(AstNode::number(5.0)),
10260 };
10261 let result = evaluator.evaluate(&expr, &data).unwrap();
10262 assert_eq!(result, JValue::Number(-5.0));
10263
10264 let expr = AstNode::Unary {
10266 op: UnaryOp::Not,
10267 operand: Box::new(AstNode::boolean(true)),
10268 };
10269 let result = evaluator.evaluate(&expr, &data).unwrap();
10270 assert_eq!(result, JValue::Bool(false));
10271 }
10272
10273 #[test]
10274 fn test_array_construction() {
10275 let mut evaluator = Evaluator::new();
10276 let data = JValue::Null;
10277
10278 let expr = AstNode::Array(vec![
10279 AstNode::number(1.0),
10280 AstNode::number(2.0),
10281 AstNode::number(3.0),
10282 ]);
10283 let result = evaluator.evaluate(&expr, &data).unwrap();
10284 assert_eq!(result, JValue::from(serde_json::json!([1, 2, 3])));
10286 }
10287
10288 #[test]
10289 fn test_object_construction() {
10290 let mut evaluator = Evaluator::new();
10291 let data = JValue::Null;
10292
10293 let expr = AstNode::Object(vec![
10294 (AstNode::string("name"), AstNode::string("Alice")),
10295 (AstNode::string("age"), AstNode::number(30.0)),
10296 ]);
10297 let result = evaluator.evaluate(&expr, &data).unwrap();
10298 let mut expected = IndexMap::new();
10300 expected.insert("name".to_string(), JValue::string("Alice"));
10301 expected.insert("age".to_string(), JValue::Number(30.0));
10302 assert_eq!(result, JValue::object(expected));
10303 }
10304
10305 #[test]
10306 fn test_conditional() {
10307 let mut evaluator = Evaluator::new();
10308 let data = JValue::Null;
10309
10310 let expr = AstNode::Conditional {
10312 condition: Box::new(AstNode::boolean(true)),
10313 then_branch: Box::new(AstNode::string("yes")),
10314 else_branch: Some(Box::new(AstNode::string("no"))),
10315 };
10316 let result = evaluator.evaluate(&expr, &data).unwrap();
10317 assert_eq!(result, JValue::string("yes"));
10318
10319 let expr = AstNode::Conditional {
10321 condition: Box::new(AstNode::boolean(false)),
10322 then_branch: Box::new(AstNode::string("yes")),
10323 else_branch: Some(Box::new(AstNode::string("no"))),
10324 };
10325 let result = evaluator.evaluate(&expr, &data).unwrap();
10326 assert_eq!(result, JValue::string("no"));
10327
10328 let expr = AstNode::Conditional {
10330 condition: Box::new(AstNode::boolean(false)),
10331 then_branch: Box::new(AstNode::string("yes")),
10332 else_branch: None,
10333 };
10334 let result = evaluator.evaluate(&expr, &data).unwrap();
10335 assert_eq!(result, JValue::Undefined);
10336 }
10337
10338 #[test]
10339 fn test_block_expression() {
10340 let mut evaluator = Evaluator::new();
10341 let data = JValue::Null;
10342
10343 let expr = AstNode::Block(vec![
10344 AstNode::number(1.0),
10345 AstNode::number(2.0),
10346 AstNode::number(3.0),
10347 ]);
10348 let result = evaluator.evaluate(&expr, &data).unwrap();
10349 assert_eq!(result, JValue::from(3i64));
10351 }
10352
10353 #[test]
10354 fn test_function_calls() {
10355 let mut evaluator = Evaluator::new();
10356 let data = JValue::Null;
10357
10358 let expr = AstNode::Function {
10360 name: "uppercase".to_string(),
10361 args: vec![AstNode::string("hello")],
10362 is_builtin: true,
10363 };
10364 let result = evaluator.evaluate(&expr, &data).unwrap();
10365 assert_eq!(result, JValue::string("HELLO"));
10366
10367 let expr = AstNode::Function {
10369 name: "lowercase".to_string(),
10370 args: vec![AstNode::string("HELLO")],
10371 is_builtin: true,
10372 };
10373 let result = evaluator.evaluate(&expr, &data).unwrap();
10374 assert_eq!(result, JValue::string("hello"));
10375
10376 let expr = AstNode::Function {
10378 name: "length".to_string(),
10379 args: vec![AstNode::string("hello")],
10380 is_builtin: true,
10381 };
10382 let result = evaluator.evaluate(&expr, &data).unwrap();
10383 assert_eq!(result, JValue::from(5i64));
10384
10385 let expr = AstNode::Function {
10387 name: "sum".to_string(),
10388 args: vec![AstNode::Array(vec![
10389 AstNode::number(1.0),
10390 AstNode::number(2.0),
10391 AstNode::number(3.0),
10392 ])],
10393 is_builtin: true,
10394 };
10395 let result = evaluator.evaluate(&expr, &data).unwrap();
10396 assert_eq!(result, JValue::Number(6.0));
10397
10398 let expr = AstNode::Function {
10400 name: "count".to_string(),
10401 args: vec![AstNode::Array(vec![
10402 AstNode::number(1.0),
10403 AstNode::number(2.0),
10404 AstNode::number(3.0),
10405 ])],
10406 is_builtin: true,
10407 };
10408 let result = evaluator.evaluate(&expr, &data).unwrap();
10409 assert_eq!(result, JValue::from(3i64));
10410 }
10411
10412 #[test]
10413 fn test_complex_nested_data() {
10414 let mut evaluator = Evaluator::new();
10415 let data = JValue::from(serde_json::json!({
10416 "users": [
10417 {"name": "Alice", "age": 30},
10418 {"name": "Bob", "age": 25},
10419 {"name": "Charlie", "age": 35}
10420 ],
10421 "metadata": {
10422 "total": 3,
10423 "version": "1.0"
10424 }
10425 }));
10426 let path = AstNode::Path {
10428 steps: vec![
10429 PathStep::new(AstNode::Name("metadata".to_string())),
10430 PathStep::new(AstNode::Name("version".to_string())),
10431 ],
10432 };
10433 let result = evaluator.evaluate(&path, &data).unwrap();
10434 assert_eq!(result, JValue::string("1.0"));
10435 }
10436
10437 #[test]
10438 fn test_error_handling() {
10439 let mut evaluator = Evaluator::new();
10440 let data = JValue::Null;
10441
10442 let expr = AstNode::Binary {
10444 op: BinaryOp::Add,
10445 lhs: Box::new(AstNode::string("hello")),
10446 rhs: Box::new(AstNode::number(5.0)),
10447 };
10448 let result = evaluator.evaluate(&expr, &data);
10449 assert!(result.is_err());
10450
10451 let expr = AstNode::Function {
10453 name: "undefined_function".to_string(),
10454 args: vec![],
10455 is_builtin: false,
10456 };
10457 let result = evaluator.evaluate(&expr, &data);
10458 assert!(result.is_err());
10459 }
10460
10461 #[test]
10462 fn test_truthiness() {
10463 let evaluator = Evaluator::new();
10464
10465 assert!(!evaluator.is_truthy(&JValue::Null));
10466 assert!(!evaluator.is_truthy(&JValue::Bool(false)));
10467 assert!(evaluator.is_truthy(&JValue::Bool(true)));
10468 assert!(!evaluator.is_truthy(&JValue::from(0i64)));
10469 assert!(evaluator.is_truthy(&JValue::from(1i64)));
10470 assert!(!evaluator.is_truthy(&JValue::string("")));
10471 assert!(evaluator.is_truthy(&JValue::string("hello")));
10472 assert!(!evaluator.is_truthy(&JValue::array(vec![])));
10473 assert!(evaluator.is_truthy(&JValue::from(serde_json::json!([1, 2, 3]))));
10474 }
10475
10476 #[test]
10477 fn test_integration_with_parser() {
10478 use crate::parser::parse;
10479
10480 let mut evaluator = Evaluator::new();
10481 let data = JValue::from(serde_json::json!({
10482 "price": 10,
10483 "quantity": 5
10484 }));
10485 let ast = parse("price").unwrap();
10487 let result = evaluator.evaluate(&ast, &data).unwrap();
10488 assert_eq!(result, JValue::from(10i64));
10489
10490 let ast = parse("price * quantity").unwrap();
10492 let result = evaluator.evaluate(&ast, &data).unwrap();
10493 assert_eq!(result, JValue::Number(50.0));
10495
10496 let ast = parse("price > 5").unwrap();
10498 let result = evaluator.evaluate(&ast, &data).unwrap();
10499 assert_eq!(result, JValue::Bool(true));
10500 }
10501
10502 #[test]
10503 fn test_evaluate_dollar_function_uppercase() {
10504 use crate::parser::parse;
10505
10506 let mut evaluator = Evaluator::new();
10507 let ast = parse(r#"$uppercase("hello")"#).unwrap();
10508 let empty = JValue::object(IndexMap::new());
10509 let result = evaluator.evaluate(&ast, &empty).unwrap();
10510 assert_eq!(result, JValue::string("HELLO"));
10511 }
10512
10513 #[test]
10514 fn test_evaluate_dollar_function_sum() {
10515 use crate::parser::parse;
10516
10517 let mut evaluator = Evaluator::new();
10518 let ast = parse("$sum([1, 2, 3, 4, 5])").unwrap();
10519 let empty = JValue::object(IndexMap::new());
10520 let result = evaluator.evaluate(&ast, &empty).unwrap();
10521 assert_eq!(result, JValue::Number(15.0));
10522 }
10523
10524 #[test]
10525 fn test_evaluate_nested_dollar_functions() {
10526 use crate::parser::parse;
10527
10528 let mut evaluator = Evaluator::new();
10529 let ast = parse(r#"$length($lowercase("HELLO"))"#).unwrap();
10530 let empty = JValue::object(IndexMap::new());
10531 let result = evaluator.evaluate(&ast, &empty).unwrap();
10532 assert_eq!(result, JValue::Number(5.0));
10534 }
10535
10536 #[test]
10537 fn test_array_mapping() {
10538 use crate::parser::parse;
10539
10540 let mut evaluator = Evaluator::new();
10541 let data: JValue = serde_json::from_str(
10542 r#"{
10543 "products": [
10544 {"id": 1, "name": "Laptop", "price": 999.99},
10545 {"id": 2, "name": "Mouse", "price": 29.99},
10546 {"id": 3, "name": "Keyboard", "price": 79.99}
10547 ]
10548 }"#,
10549 )
10550 .map(|v: serde_json::Value| JValue::from(v))
10551 .unwrap();
10552
10553 let ast = parse("products.name").unwrap();
10555 let result = evaluator.evaluate(&ast, &data).unwrap();
10556 assert_eq!(
10557 result,
10558 JValue::array(vec![
10559 JValue::string("Laptop"),
10560 JValue::string("Mouse"),
10561 JValue::string("Keyboard")
10562 ])
10563 );
10564
10565 let ast = parse("products.price").unwrap();
10567 let result = evaluator.evaluate(&ast, &data).unwrap();
10568 assert_eq!(
10569 result,
10570 JValue::array(vec![
10571 JValue::Number(999.99),
10572 JValue::Number(29.99),
10573 JValue::Number(79.99)
10574 ])
10575 );
10576
10577 let ast = parse("$sum(products.price)").unwrap();
10579 let result = evaluator.evaluate(&ast, &data).unwrap();
10580 assert_eq!(result, JValue::Number(1109.97));
10581 }
10582
10583 #[test]
10584 fn test_empty_brackets() {
10585 use crate::parser::parse;
10586
10587 let mut evaluator = Evaluator::new();
10588
10589 let data: JValue = JValue::from(serde_json::json!({"foo": "bar"}));
10591 let ast = parse("foo[]").unwrap();
10592 let result = evaluator.evaluate(&ast, &data).unwrap();
10593 assert_eq!(
10594 result,
10595 JValue::array(vec![JValue::string("bar")]),
10596 "Empty brackets should wrap value in array"
10597 );
10598
10599 let data2: JValue = JValue::from(serde_json::json!({"arr": [1, 2, 3]}));
10601 let ast2 = parse("arr[]").unwrap();
10602 let result2 = evaluator.evaluate(&ast2, &data2).unwrap();
10603 assert_eq!(
10604 result2,
10605 JValue::array(vec![
10606 JValue::Number(1.0),
10607 JValue::Number(2.0),
10608 JValue::Number(3.0)
10609 ]),
10610 "Empty brackets should preserve array"
10611 );
10612 }
10613}