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#[allow(dead_code)]
1047#[inline]
1048pub(crate) fn compiled_equal(lhs: &JValue, rhs: &JValue) -> JValue {
1049 JValue::Bool(crate::functions::array::values_equal(lhs, rhs))
1050}
1051
1052#[allow(dead_code)]
1055#[inline]
1056pub(crate) fn compiled_concat(lhs: JValue, rhs: JValue) -> Result<JValue, EvaluatorError> {
1057 let l = compiled_to_concat_string(&lhs)?;
1058 let r = compiled_to_concat_string(&rhs)?;
1059 Ok(JValue::string(l + &r))
1060}
1061
1062#[allow(dead_code)]
1065#[inline]
1066pub(crate) fn call_pure_builtin_by_name(
1067 name: &str,
1068 args: &[JValue],
1069 data: &JValue,
1070) -> Result<JValue, EvaluatorError> {
1071 call_pure_builtin(name, args, data)
1072}
1073
1074fn try_compile_path(steps: &[crate::ast::PathStep], allowed_vars: Option<&[&str]>) -> Option<CompiledExpr> {
1083 use crate::ast::{AstNode, Stage};
1084
1085 if steps.is_empty() {
1086 return None;
1087 }
1088
1089 let field_steps: &[crate::ast::PathStep] = match &steps[0].node {
1094 AstNode::Variable(var) if var.is_empty() && steps[0].stages.is_empty() => &steps[1..],
1095 AstNode::Variable(_) => return None,
1096 AstNode::Name(_) => steps,
1097 _ => return None,
1098 };
1099
1100 let mut compiled_steps = Vec::with_capacity(field_steps.len());
1102 for step in field_steps {
1103 let field = match &step.node {
1104 AstNode::Name(name) => name.clone(),
1105 _ => return None,
1106 };
1107
1108 let filter = match step.stages.as_slice() {
1109 [] => None,
1110 [Stage::Filter(filter_node)] => {
1111 if matches!(**filter_node, AstNode::Number(_)) {
1114 return None;
1115 }
1116 Some(try_compile_expr_inner(filter_node, allowed_vars)?)
1117 }
1118 _ => return None,
1119 };
1120
1121 compiled_steps.push(CompiledStep { field, filter });
1122 }
1123
1124 if compiled_steps.is_empty() {
1125 return Some(CompiledExpr::VariableLookup(String::new()));
1127 }
1128
1129 if allowed_vars.is_some() {
1134 if compiled_steps.len() == 1 && compiled_steps[0].filter.is_none() {
1135 return Some(CompiledExpr::FieldLookup(compiled_steps.remove(0).field));
1136 }
1137 if compiled_steps.len() == 2
1138 && compiled_steps[0].filter.is_none()
1139 && compiled_steps[1].filter.is_none()
1140 {
1141 let outer = compiled_steps.remove(0).field;
1142 let inner = compiled_steps.remove(0).field;
1143 return Some(CompiledExpr::NestedFieldLookup(outer, inner));
1144 }
1145 }
1146
1147 Some(CompiledExpr::FieldPath(compiled_steps))
1148}
1149
1150fn compiled_eval_field_path(
1159 steps: &[CompiledStep],
1160 data: &JValue,
1161 vars: Option<&HashMap<&str, &JValue>>,
1162 ctx: Option<&Context>,
1163 shape: Option<&ShapeCache>,
1164) -> Result<JValue, EvaluatorError> {
1165 let mut current = data.clone();
1166 let mut did_array_mapping = false;
1169 for step in steps {
1170 let is_array = matches!(current, JValue::Array(_));
1172 current = compiled_field_step(&step.field, ¤t);
1174 if is_array {
1175 did_array_mapping = true;
1176 } else {
1177 did_array_mapping = false;
1179 }
1180 if let Some(filter) = &step.filter {
1182 current = compiled_apply_filter(filter, ¤t, vars, ctx, shape)?;
1183 did_array_mapping = true;
1185 }
1186 }
1187 if did_array_mapping {
1189 Ok(match current {
1190 JValue::Array(ref arr) if arr.len() == 1 => arr[0].clone(),
1191 other => other,
1192 })
1193 } else {
1194 Ok(current)
1195 }
1196}
1197
1198fn compiled_field_step(field: &str, value: &JValue) -> JValue {
1205 match value {
1206 JValue::Object(obj) => {
1207 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
1209 if let Some(JValue::Object(inner)) = obj.get("@") {
1210 return inner.get(field).cloned().unwrap_or(JValue::Undefined);
1211 }
1212 return JValue::Undefined;
1213 }
1214 obj.get(field).cloned().unwrap_or(JValue::Undefined)
1215 }
1216 JValue::Array(arr) => {
1217 let mut result = Vec::new();
1218 for item in arr.iter() {
1219 let extracted = compiled_field_step(field, item);
1220 match extracted {
1221 JValue::Undefined => {}
1222 JValue::Array(inner) => result.extend(inner.iter().cloned()),
1223 other => result.push(other),
1224 }
1225 }
1226 if result.is_empty() {
1227 JValue::Undefined
1228 } else {
1229 JValue::array(result)
1230 }
1231 }
1232 _ => JValue::Undefined,
1233 }
1234}
1235
1236fn compiled_apply_filter(
1242 filter: &CompiledExpr,
1243 value: &JValue,
1244 vars: Option<&HashMap<&str, &JValue>>,
1245 ctx: Option<&Context>,
1246 shape: Option<&ShapeCache>,
1247) -> Result<JValue, EvaluatorError> {
1248 match value {
1249 JValue::Array(arr) => {
1250 let mut result = Vec::new();
1251 for item in arr.iter() {
1252 let pred = eval_compiled_inner(filter, item, vars, ctx, shape)?;
1253 if compiled_is_truthy(&pred) {
1254 result.push(item.clone());
1255 }
1256 }
1257 if result.is_empty() {
1258 Ok(JValue::Undefined)
1259 } else if result.len() == 1 {
1260 Ok(result.remove(0))
1261 } else {
1262 Ok(JValue::array(result))
1263 }
1264 }
1265 JValue::Undefined => Ok(JValue::Undefined),
1266 _ => {
1267 let pred = eval_compiled_inner(filter, value, vars, ctx, shape)?;
1268 if compiled_is_truthy(&pred) {
1269 Ok(value.clone())
1270 } else {
1271 Ok(JValue::Undefined)
1272 }
1273 }
1274 }
1275}
1276
1277fn call_pure_builtin(
1283 name: &str,
1284 args: &[JValue],
1285 data: &JValue,
1286) -> Result<JValue, EvaluatorError> {
1287 use crate::functions;
1288
1289 let args_storage: Vec<JValue>;
1291 let effective_args: &[JValue] = if args.is_empty() {
1292 match name {
1293 "string" => {
1294 if data.is_undefined() || data.is_null() {
1297 return Ok(JValue::Undefined);
1298 }
1299 args_storage = vec![data.clone()];
1300 &args_storage
1301 }
1302 "number" | "boolean" | "uppercase" | "lowercase" => {
1303 args_storage = vec![data.clone()];
1304 &args_storage
1305 }
1306 _ => args,
1307 }
1308 } else if args.len() == 1 {
1309 match name {
1310 "substringBefore" | "substringAfter" | "contains" | "split" => {
1311 if matches!(data, JValue::String(_)) {
1312 args_storage = std::iter::once(data.clone())
1313 .chain(args.iter().cloned())
1314 .collect();
1315 &args_storage
1316 } else {
1317 args
1318 }
1319 }
1320 _ => args,
1321 }
1322 } else {
1323 args
1324 };
1325
1326 if effective_args.first().is_some_and(JValue::is_undefined)
1330 && propagates_undefined(name)
1331 {
1332 return Ok(JValue::Undefined);
1333 }
1334
1335 match name {
1336 "string" => {
1338 let prettify = match effective_args.get(1) {
1340 None => None,
1341 Some(JValue::Bool(b)) => Some(*b),
1342 Some(_) => {
1343 return Err(EvaluatorError::TypeError(
1344 "string() prettify parameter must be a boolean".to_string(),
1345 ))
1346 }
1347 };
1348 let arg = effective_args.first().unwrap_or(&JValue::Null);
1349 Ok(functions::string::string(arg, prettify)?)
1350 }
1351 "length" => match effective_args.first() {
1352 Some(JValue::String(s)) => Ok(functions::string::length(s)?),
1353 Some(JValue::Undefined) => Ok(JValue::Undefined),
1355 None => Err(EvaluatorError::EvaluationError(
1358 "length() requires exactly 1 argument".to_string(),
1359 )),
1360 _ => Err(EvaluatorError::TypeError(
1362 "T0410: Argument 1 of function length does not match function signature"
1363 .to_string(),
1364 )),
1365 },
1366 "uppercase" => match effective_args.first() {
1367 Some(JValue::String(s)) => Ok(functions::string::uppercase(s)?),
1368 Some(JValue::Undefined) | None => Ok(JValue::Undefined),
1369 _ => Err(EvaluatorError::TypeError(
1370 "T0410: Argument 1 of function uppercase does not match function signature"
1371 .to_string(),
1372 )),
1373 },
1374 "lowercase" => match effective_args.first() {
1375 Some(JValue::String(s)) => Ok(functions::string::lowercase(s)?),
1376 Some(JValue::Undefined) | None => Ok(JValue::Undefined),
1377 _ => Err(EvaluatorError::TypeError(
1378 "T0410: Argument 1 of function lowercase does not match function signature"
1379 .to_string(),
1380 )),
1381 },
1382 "trim" => match effective_args.first() {
1383 None | Some(JValue::Null | JValue::Undefined) => Ok(JValue::Null),
1384 Some(JValue::String(s)) => Ok(functions::string::trim(s)?),
1385 _ => Err(EvaluatorError::TypeError(
1386 "trim() requires a string argument".to_string(),
1387 )),
1388 },
1389 "substring" => {
1390 if effective_args.len() < 2 {
1391 return Err(EvaluatorError::EvaluationError(
1392 "substring() requires at least 2 arguments".to_string(),
1393 ));
1394 }
1395 match (&effective_args[0], &effective_args[1]) {
1396 (JValue::String(s), JValue::Number(start)) => {
1397 let length = match effective_args.get(2) {
1399 None => None,
1400 Some(JValue::Number(l)) => Some(*l as i64),
1401 Some(_) => {
1402 return Err(EvaluatorError::TypeError(
1403 "T0410: Argument 3 of function substring does not match function signature"
1404 .to_string(),
1405 ))
1406 }
1407 };
1408 Ok(functions::string::substring(s, *start as i64, length)?)
1409 }
1410 _ => Err(EvaluatorError::TypeError(
1411 "T0410: Argument 1 of function substring does not match function signature"
1412 .to_string(),
1413 )),
1414 }
1415 }
1416 "substringBefore" => {
1417 if effective_args.len() != 2 {
1418 return Err(EvaluatorError::TypeError(
1419 "T0411: Context value is not a compatible type with argument 2 of function substringBefore".to_string(),
1420 ));
1421 }
1422 match (&effective_args[0], &effective_args[1]) {
1423 (JValue::String(s), JValue::String(sep)) => {
1424 Ok(functions::string::substring_before(s, sep)?)
1425 }
1426 (JValue::Undefined, _) => Ok(JValue::Undefined),
1428 _ => Err(EvaluatorError::TypeError(
1429 "T0410: Argument 1 of function substringBefore does not match function signature".to_string(),
1430 )),
1431 }
1432 }
1433 "substringAfter" => {
1434 if effective_args.len() != 2 {
1435 return Err(EvaluatorError::TypeError(
1436 "T0411: Context value is not a compatible type with argument 2 of function substringAfter".to_string(),
1437 ));
1438 }
1439 match (&effective_args[0], &effective_args[1]) {
1440 (JValue::String(s), JValue::String(sep)) => {
1441 Ok(functions::string::substring_after(s, sep)?)
1442 }
1443 (JValue::Undefined, _) => Ok(JValue::Undefined),
1445 _ => Err(EvaluatorError::TypeError(
1446 "T0410: Argument 1 of function substringAfter does not match function signature".to_string(),
1447 )),
1448 }
1449 }
1450 "contains" => {
1451 if effective_args.len() != 2 {
1452 return Err(EvaluatorError::EvaluationError(
1453 "contains() requires exactly 2 arguments".to_string(),
1454 ));
1455 }
1456 match &effective_args[0] {
1457 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1458 JValue::String(s) => Ok(functions::string::contains(s, &effective_args[1])?),
1459 _ => Err(EvaluatorError::TypeError(
1460 "contains() requires a string as the first argument".to_string(),
1461 )),
1462 }
1463 }
1464 "split" => {
1465 if effective_args.len() < 2 {
1466 return Err(EvaluatorError::EvaluationError(
1467 "split() requires at least 2 arguments".to_string(),
1468 ));
1469 }
1470 match &effective_args[0] {
1471 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1472 JValue::String(s) => {
1473 let limit = match effective_args.get(2) {
1475 None => None,
1476 Some(JValue::Number(n)) => {
1477 if *n < 0.0 {
1478 return Err(EvaluatorError::EvaluationError(
1479 "D3020: Third argument of split function must be a positive number"
1480 .to_string(),
1481 ));
1482 }
1483 Some(n.floor() as usize)
1484 }
1485 Some(_) => {
1486 return Err(EvaluatorError::TypeError(
1487 "split() limit must be a number".to_string(),
1488 ))
1489 }
1490 };
1491 Ok(functions::string::split(s, &effective_args[1], limit)?)
1492 }
1493 _ => Err(EvaluatorError::TypeError(
1494 "split() requires a string as the first argument".to_string(),
1495 )),
1496 }
1497 }
1498 "join" => {
1499 if effective_args.is_empty() {
1500 return Err(EvaluatorError::TypeError(
1501 "T0410: Argument 1 of function $join does not match function signature"
1502 .to_string(),
1503 ));
1504 }
1505 match &effective_args[0] {
1506 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1507 JValue::Bool(_) | JValue::Number(_) | JValue::Object(_) => {
1509 Err(EvaluatorError::TypeError(
1510 "T0412: Argument 1 of function $join must be an array of String"
1511 .to_string(),
1512 ))
1513 }
1514 JValue::Array(arr) => {
1515 for item in arr.iter() {
1517 if !matches!(item, JValue::String(_)) {
1518 return Err(EvaluatorError::TypeError(
1519 "T0412: Argument 1 of function $join must be an array of String"
1520 .to_string(),
1521 ));
1522 }
1523 }
1524 let separator = match effective_args.get(1) {
1526 None | Some(JValue::Undefined) => None,
1527 Some(JValue::String(s)) => Some(&**s),
1528 Some(_) => {
1529 return Err(EvaluatorError::TypeError(
1530 "T0410: Argument 2 of function $join does not match function signature (expected String)"
1531 .to_string(),
1532 ))
1533 }
1534 };
1535 Ok(functions::string::join(arr, separator)?)
1536 }
1537 JValue::String(s) => Ok(JValue::String(s.clone())),
1538 _ => Err(EvaluatorError::TypeError(
1539 "T0412: Argument 1 of function $join must be an array of String"
1540 .to_string(),
1541 )),
1542 }
1543 }
1544
1545 "number" => match effective_args.first() {
1547 Some(v) => Ok(functions::numeric::number(v)?),
1548 None => Err(EvaluatorError::EvaluationError(
1549 "number() requires at least 1 argument".to_string(),
1550 )),
1551 },
1552 "floor" => match effective_args.first() {
1553 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1554 Some(JValue::Number(n)) => Ok(functions::numeric::floor(*n)?),
1555 _ => Err(EvaluatorError::TypeError(
1556 "floor() requires a number argument".to_string(),
1557 )),
1558 },
1559 "ceil" => match effective_args.first() {
1560 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1561 Some(JValue::Number(n)) => Ok(functions::numeric::ceil(*n)?),
1562 _ => Err(EvaluatorError::TypeError(
1563 "ceil() requires a number argument".to_string(),
1564 )),
1565 },
1566 "round" => match effective_args.first() {
1567 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1568 Some(JValue::Number(n)) => {
1569 let precision = effective_args.get(1).and_then(|v| {
1570 if let JValue::Number(p) = v { Some(*p as i32) } else { None }
1571 });
1572 Ok(functions::numeric::round(*n, precision)?)
1573 }
1574 _ => Err(EvaluatorError::TypeError(
1575 "round() requires a number argument".to_string(),
1576 )),
1577 },
1578 "abs" => match effective_args.first() {
1579 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1580 Some(JValue::Number(n)) => Ok(functions::numeric::abs(*n)?),
1581 _ => Err(EvaluatorError::TypeError(
1582 "abs() requires a number argument".to_string(),
1583 )),
1584 },
1585 "sqrt" => match effective_args.first() {
1586 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1587 Some(JValue::Number(n)) => Ok(functions::numeric::sqrt(*n)?),
1588 _ => Err(EvaluatorError::TypeError(
1589 "sqrt() requires a number argument".to_string(),
1590 )),
1591 },
1592
1593 "sum" => match effective_args.first() {
1595 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1596 None => Err(EvaluatorError::EvaluationError(
1597 "sum() requires exactly 1 argument".to_string(),
1598 )),
1599 Some(JValue::Null) => Ok(JValue::Null),
1600 Some(JValue::Array(arr)) => Ok(aggregation::sum(arr)?),
1601 Some(JValue::Number(n)) => Ok(JValue::Number(*n)),
1602 Some(other) => Ok(functions::numeric::sum(&[other.clone()])?),
1603 },
1604 "max" => match effective_args.first() {
1605 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1606 Some(JValue::Null) | None => Ok(JValue::Null),
1607 Some(JValue::Array(arr)) => Ok(aggregation::max(arr)?),
1608 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1609 _ => Err(EvaluatorError::TypeError(
1610 "max() requires an array or number argument".to_string(),
1611 )),
1612 },
1613 "min" => match effective_args.first() {
1614 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1615 Some(JValue::Null) | None => Ok(JValue::Null),
1616 Some(JValue::Array(arr)) => Ok(aggregation::min(arr)?),
1617 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1618 _ => Err(EvaluatorError::TypeError(
1619 "min() requires an array or number argument".to_string(),
1620 )),
1621 },
1622 "average" => match effective_args.first() {
1623 Some(v) if v.is_undefined() => Ok(JValue::Undefined),
1624 Some(JValue::Null) | None => Ok(JValue::Null),
1625 Some(JValue::Array(arr)) => Ok(aggregation::average(arr)?),
1626 Some(v @ JValue::Number(_)) => Ok(v.clone()),
1627 _ => Err(EvaluatorError::TypeError(
1628 "average() requires an array or number argument".to_string(),
1629 )),
1630 },
1631 "count" => match effective_args.first() {
1632 Some(v) if v.is_undefined() => Ok(JValue::from(0i64)),
1633 Some(JValue::Null) | None => Ok(JValue::from(0i64)),
1634 Some(JValue::Array(arr)) => Ok(functions::array::count(arr)?),
1635 _ => Ok(JValue::from(1i64)),
1636 },
1637
1638 "boolean" => match effective_args.first() {
1640 Some(v) => Ok(functions::boolean::boolean(v)?),
1641 None => Err(EvaluatorError::EvaluationError(
1642 "boolean() requires 1 argument".to_string(),
1643 )),
1644 },
1645 "not" => match effective_args.first() {
1646 Some(v) => Ok(JValue::Bool(!compiled_is_truthy(v))),
1647 None => Err(EvaluatorError::EvaluationError(
1648 "not() requires 1 argument".to_string(),
1649 )),
1650 },
1651
1652 "append" => {
1654 if effective_args.len() != 2 {
1655 return Err(EvaluatorError::EvaluationError(
1656 "append() requires exactly 2 arguments".to_string(),
1657 ));
1658 }
1659 let first = &effective_args[0];
1660 let second = &effective_args[1];
1661 if matches!(second, JValue::Null | JValue::Undefined) {
1662 return Ok(first.clone());
1663 }
1664 if matches!(first, JValue::Null | JValue::Undefined) {
1665 return Ok(second.clone());
1666 }
1667 let arr = match first {
1668 JValue::Array(a) => a.to_vec(),
1669 other => vec![other.clone()],
1670 };
1671 Ok(functions::array::append(&arr, second)?)
1672 }
1673 "reverse" => match effective_args.first() {
1674 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1675 Some(JValue::Array(arr)) => Ok(functions::array::reverse(arr)?),
1676 _ => Err(EvaluatorError::TypeError(
1677 "reverse() requires an array argument".to_string(),
1678 )),
1679 },
1680 "distinct" => match effective_args.first() {
1681 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1682 Some(JValue::Array(arr)) => Ok(functions::array::distinct(arr)?),
1683 _ => Err(EvaluatorError::TypeError(
1684 "distinct() requires an array argument".to_string(),
1685 )),
1686 },
1687
1688 "keys" => match effective_args.first() {
1690 Some(JValue::Null | JValue::Undefined) | None => Ok(JValue::Null),
1691 Some(JValue::Lambda { .. } | JValue::Builtin { .. }) => Ok(JValue::Null),
1692 Some(JValue::Object(obj)) => {
1693 if obj.is_empty() {
1694 Ok(JValue::Null)
1695 } else {
1696 let keys: Vec<JValue> =
1697 obj.keys().map(|k| JValue::string(k.clone())).collect();
1698 if keys.len() == 1 {
1699 Ok(keys.into_iter().next().unwrap())
1700 } else {
1701 Ok(JValue::array(keys))
1702 }
1703 }
1704 }
1705 Some(JValue::Array(arr)) => {
1706 let mut all_keys: Vec<JValue> = Vec::new();
1707 for item in arr.iter() {
1708 if let JValue::Object(obj) = item {
1709 for key in obj.keys() {
1710 let k = JValue::string(key.clone());
1711 if !all_keys.contains(&k) {
1712 all_keys.push(k);
1713 }
1714 }
1715 }
1716 }
1717 if all_keys.is_empty() {
1718 Ok(JValue::Null)
1719 } else if all_keys.len() == 1 {
1720 Ok(all_keys.into_iter().next().unwrap())
1721 } else {
1722 Ok(JValue::array(all_keys))
1723 }
1724 }
1725 _ => Ok(JValue::Null),
1726 },
1727 "merge" => match effective_args.len() {
1728 0 => Err(EvaluatorError::EvaluationError(
1729 "merge() requires at least 1 argument".to_string(),
1730 )),
1731 1 => match &effective_args[0] {
1732 JValue::Array(arr) => Ok(functions::object::merge(arr)?),
1733 JValue::Null | JValue::Undefined => Ok(JValue::Null),
1734 JValue::Object(_) => Ok(effective_args[0].clone()),
1735 _ => Err(EvaluatorError::TypeError(
1736 "merge() requires objects or an array of objects".to_string(),
1737 )),
1738 },
1739 _ => Ok(functions::object::merge(effective_args)?),
1740 },
1741
1742 _ => unreachable!("call_pure_builtin called with non-compilable builtin: {}", name),
1743 }
1744}
1745
1746const UNDEFINED_PROPAGATING_FUNCTIONS: &[&str] = &[
1754 "not",
1755 "boolean",
1756 "length",
1757 "number",
1758 "uppercase",
1759 "lowercase",
1760 "substring",
1761 "substringBefore",
1762 "substringAfter",
1763 "string",
1764];
1765
1766fn propagates_undefined(name: &str) -> bool {
1768 UNDEFINED_PROPAGATING_FUNCTIONS.contains(&name)
1769}
1770
1771mod aggregation {
1774 use super::*;
1775
1776 fn for_each_numeric(
1779 arr: &[JValue],
1780 func_name: &str,
1781 mut f: impl FnMut(f64),
1782 ) -> Result<(), EvaluatorError> {
1783 fn recurse(
1784 arr: &[JValue],
1785 func_name: &str,
1786 f: &mut dyn FnMut(f64),
1787 ) -> Result<(), EvaluatorError> {
1788 for value in arr.iter() {
1789 match value {
1790 JValue::Array(inner) => recurse(inner, func_name, f)?,
1791 JValue::Number(n) => {
1792 f(*n);
1793 }
1794 _ => {
1795 return Err(EvaluatorError::TypeError(format!(
1796 "{}() requires all array elements to be numbers",
1797 func_name
1798 )));
1799 }
1800 }
1801 }
1802 Ok(())
1803 }
1804 recurse(arr, func_name, &mut f)
1805 }
1806
1807 fn count_numeric(arr: &[JValue], func_name: &str) -> Result<usize, EvaluatorError> {
1809 let mut count = 0usize;
1810 for_each_numeric(arr, func_name, |_| count += 1)?;
1811 Ok(count)
1812 }
1813
1814 pub fn sum(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1815 if arr.is_empty() {
1816 return Ok(JValue::from(0i64));
1817 }
1818 let mut total = 0.0f64;
1819 for_each_numeric(arr, "sum", |n| total += n)?;
1820 Ok(JValue::Number(total))
1821 }
1822
1823 pub fn max(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1824 if arr.is_empty() {
1825 return Ok(JValue::Null);
1826 }
1827 let mut max_val = f64::NEG_INFINITY;
1828 for_each_numeric(arr, "max", |n| {
1829 if n > max_val {
1830 max_val = n;
1831 }
1832 })?;
1833 Ok(JValue::Number(max_val))
1834 }
1835
1836 pub fn min(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1837 if arr.is_empty() {
1838 return Ok(JValue::Null);
1839 }
1840 let mut min_val = f64::INFINITY;
1841 for_each_numeric(arr, "min", |n| {
1842 if n < min_val {
1843 min_val = n;
1844 }
1845 })?;
1846 Ok(JValue::Number(min_val))
1847 }
1848
1849 pub fn average(arr: &[JValue]) -> Result<JValue, EvaluatorError> {
1850 if arr.is_empty() {
1851 return Ok(JValue::Null);
1852 }
1853 let mut total = 0.0f64;
1854 let count = count_numeric(arr, "average")?;
1855 for_each_numeric(arr, "average", |n| total += n)?;
1856 Ok(JValue::Number(total / count as f64))
1857 }
1858}
1859
1860#[derive(Error, Debug)]
1862pub enum EvaluatorError {
1863 #[error("Type error: {0}")]
1864 TypeError(String),
1865
1866 #[error("Reference error: {0}")]
1867 ReferenceError(String),
1868
1869 #[error("Evaluation error: {0}")]
1870 EvaluationError(String),
1871}
1872
1873impl From<crate::functions::FunctionError> for EvaluatorError {
1874 fn from(e: crate::functions::FunctionError) -> Self {
1875 EvaluatorError::EvaluationError(e.to_string())
1876 }
1877}
1878
1879impl From<crate::datetime::DateTimeError> for EvaluatorError {
1880 fn from(e: crate::datetime::DateTimeError) -> Self {
1881 EvaluatorError::EvaluationError(e.to_string())
1882 }
1883}
1884
1885enum LambdaResult {
1888 JValue(JValue),
1890 TailCall {
1892 lambda: Box<StoredLambda>,
1894 args: Vec<JValue>,
1896 data: JValue,
1898 },
1899}
1900
1901#[derive(Clone, Debug)]
1905pub struct StoredLambda {
1906 pub params: Vec<String>,
1907 pub body: AstNode,
1908 pub(crate) compiled_body: Option<CompiledExpr>,
1911 pub signature: Option<String>,
1912 pub captured_env: HashMap<String, JValue>,
1914 pub captured_data: Option<JValue>,
1916 pub thunk: bool,
1918}
1919
1920struct Scope {
1922 bindings: HashMap<String, JValue>,
1923 lambdas: HashMap<String, StoredLambda>,
1924}
1925
1926impl Scope {
1927 fn new() -> Self {
1928 Scope {
1929 bindings: HashMap::new(),
1930 lambdas: HashMap::new(),
1931 }
1932 }
1933}
1934
1935pub struct Context {
1940 scope_stack: Vec<Scope>,
1941 parent_data: Option<JValue>,
1942}
1943
1944impl Context {
1945 pub fn new() -> Self {
1946 Context {
1947 scope_stack: vec![Scope::new()],
1948 parent_data: None,
1949 }
1950 }
1951
1952 fn push_scope(&mut self) {
1954 self.scope_stack.push(Scope::new());
1955 }
1956
1957 fn pop_scope(&mut self) {
1959 if self.scope_stack.len() > 1 {
1960 self.scope_stack.pop();
1961 }
1962 }
1963
1964 fn pop_scope_preserving_lambdas(&mut self, lambda_ids: &[String]) {
1966 if self.scope_stack.len() > 1 {
1967 let popped = self.scope_stack.pop().unwrap();
1968 if !lambda_ids.is_empty() {
1969 let top = self.scope_stack.last_mut().unwrap();
1970 for id in lambda_ids {
1971 if let Some(stored) = popped.lambdas.get(id) {
1972 top.lambdas.insert(id.clone(), stored.clone());
1973 }
1974 }
1975 }
1976 }
1977 }
1978
1979 fn clear_current_scope(&mut self) {
1981 let top = self.scope_stack.last_mut().unwrap();
1982 top.bindings.clear();
1983 top.lambdas.clear();
1984 }
1985
1986 pub fn bind(&mut self, name: String, value: JValue) {
1987 self.scope_stack
1988 .last_mut()
1989 .unwrap()
1990 .bindings
1991 .insert(name, value);
1992 }
1993
1994 pub fn bind_lambda(&mut self, name: String, lambda: StoredLambda) {
1995 self.scope_stack
1996 .last_mut()
1997 .unwrap()
1998 .lambdas
1999 .insert(name, lambda);
2000 }
2001
2002 pub fn unbind(&mut self, name: &str) {
2003 let top = self.scope_stack.last_mut().unwrap();
2005 top.bindings.remove(name);
2006 top.lambdas.remove(name);
2007 }
2008
2009 pub fn lookup(&self, name: &str) -> Option<&JValue> {
2010 for scope in self.scope_stack.iter().rev() {
2012 if let Some(value) = scope.bindings.get(name) {
2013 return Some(value);
2014 }
2015 }
2016 None
2017 }
2018
2019 pub fn lookup_lambda(&self, name: &str) -> Option<&StoredLambda> {
2020 for scope in self.scope_stack.iter().rev() {
2022 if let Some(lambda) = scope.lambdas.get(name) {
2023 return Some(lambda);
2024 }
2025 }
2026 None
2027 }
2028
2029 pub fn set_parent(&mut self, data: JValue) {
2030 self.parent_data = Some(data);
2031 }
2032
2033 pub fn get_parent(&self) -> Option<&JValue> {
2034 self.parent_data.as_ref()
2035 }
2036
2037 fn all_bindings(&self) -> HashMap<String, JValue> {
2040 let mut result = HashMap::new();
2041 for scope in &self.scope_stack {
2042 for (k, v) in &scope.bindings {
2043 result.insert(k.clone(), v.clone());
2044 }
2045 }
2046 result
2047 }
2048}
2049
2050impl Default for Context {
2051 fn default() -> Self {
2052 Self::new()
2053 }
2054}
2055
2056pub struct Evaluator {
2058 context: Context,
2059 recursion_depth: usize,
2060 max_recursion_depth: usize,
2061}
2062
2063impl Evaluator {
2064 pub fn new() -> Self {
2065 Evaluator {
2066 context: Context::new(),
2067 recursion_depth: 0,
2068 max_recursion_depth: 302,
2071 }
2072 }
2073
2074 pub fn with_context(context: Context) -> Self {
2075 Evaluator {
2076 context,
2077 recursion_depth: 0,
2078 max_recursion_depth: 302,
2079 }
2080 }
2081
2082 fn invoke_stored_lambda(
2086 &mut self,
2087 stored: &StoredLambda,
2088 args: &[JValue],
2089 data: &JValue,
2090 ) -> Result<JValue, EvaluatorError> {
2091 if let Some(ref ce) = stored.compiled_body {
2095 if stored.signature.is_none()
2096 && !stored.thunk
2097 && !stored
2098 .captured_env
2099 .values()
2100 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
2101 {
2102 let call_data = stored.captured_data.as_ref().unwrap_or(data);
2103 let vars: HashMap<&str, &JValue> = stored
2104 .params
2105 .iter()
2106 .zip(args.iter())
2107 .map(|(p, v)| (p.as_str(), v))
2108 .chain(stored.captured_env.iter().map(|(k, v)| (k.as_str(), v)))
2109 .collect();
2110 return eval_compiled(ce, call_data, Some(&vars));
2111 }
2112 }
2113
2114 let captured_env = if stored.captured_env.is_empty() {
2115 None
2116 } else {
2117 Some(&stored.captured_env)
2118 };
2119 let captured_data = stored.captured_data.as_ref();
2120 self.invoke_lambda_with_env(
2121 &stored.params,
2122 &stored.body,
2123 stored.signature.as_ref(),
2124 args,
2125 data,
2126 captured_env,
2127 captured_data,
2128 stored.thunk,
2129 )
2130 }
2131
2132 fn lookup_lambda_from_value(&self, value: &JValue) -> Option<StoredLambda> {
2136 if let JValue::Lambda { lambda_id, .. } = value {
2137 return self.context.lookup_lambda(lambda_id).cloned();
2138 }
2139 None
2140 }
2141
2142 fn get_callback_param_count(&self, func_node: &AstNode) -> usize {
2146 match func_node {
2147 AstNode::Lambda { params, .. } => params.len(),
2148 AstNode::Variable(var_name) => {
2149 if let Some(stored_lambda) = self.context.lookup_lambda(var_name) {
2151 return stored_lambda.params.len();
2152 }
2153 if let Some(value) = self.context.lookup(var_name) {
2155 if let Some(stored_lambda) = self.lookup_lambda_from_value(value) {
2156 return stored_lambda.params.len();
2157 }
2158 }
2159 usize::MAX
2161 }
2162 AstNode::Function { .. } => {
2163 usize::MAX
2166 }
2167 _ => usize::MAX,
2168 }
2169 }
2170
2171 fn merge_sort_specialized(arr: &mut [JValue], spec: &SpecializedSortComparator) {
2175 if arr.len() <= 1 {
2176 return;
2177 }
2178
2179 let keys: Vec<SortKey> = arr
2181 .iter()
2182 .map(|item| match item {
2183 JValue::Object(obj) => match obj.get(&spec.field) {
2184 Some(JValue::Number(n)) => SortKey::Num(*n),
2185 Some(JValue::String(s)) => SortKey::Str(s.clone()),
2186 _ => SortKey::None,
2187 },
2188 _ => SortKey::None,
2189 })
2190 .collect();
2191
2192 let mut perm: Vec<usize> = (0..arr.len()).collect();
2194 perm.sort_by(|&a, &b| compare_sort_keys(&keys[a], &keys[b], spec.descending));
2195
2196 let mut placed = vec![false; arr.len()];
2198 for i in 0..arr.len() {
2199 if placed[i] || perm[i] == i {
2200 continue;
2201 }
2202 let mut j = i;
2203 loop {
2204 let target = perm[j];
2205 placed[j] = true;
2206 if target == i {
2207 break;
2208 }
2209 arr.swap(j, target);
2210 j = target;
2211 }
2212 }
2213 }
2214
2215 fn merge_sort_with_comparator(
2219 &mut self,
2220 arr: &mut [JValue],
2221 comparator: &AstNode,
2222 data: &JValue,
2223 ) -> Result<(), EvaluatorError> {
2224 if arr.len() <= 1 {
2225 return Ok(());
2226 }
2227
2228 if let AstNode::Lambda { params, body, .. } = comparator {
2231 if params.len() >= 2 {
2232 if let Some(spec) =
2233 try_specialize_sort_comparator(body, ¶ms[0], ¶ms[1])
2234 {
2235 Self::merge_sort_specialized(arr, &spec);
2236 return Ok(());
2237 }
2238 }
2239 }
2240
2241 let mid = arr.len() / 2;
2242
2243 self.merge_sort_with_comparator(&mut arr[..mid], comparator, data)?;
2245
2246 self.merge_sort_with_comparator(&mut arr[mid..], comparator, data)?;
2248
2249 let mut temp = Vec::with_capacity(arr.len());
2251 let (left, right) = arr.split_at(mid);
2252
2253 let mut i = 0;
2254 let mut j = 0;
2255
2256 if let AstNode::Lambda { params, body, .. } = comparator {
2259 if params.len() >= 2 {
2260 let param0 = params[0].clone();
2262 let param1 = params[1].clone();
2263 self.context.push_scope();
2264 while i < left.len() && j < right.len() {
2265 self.context.clear_current_scope();
2267 self.context.bind(param0.clone(), left[i].clone());
2268 self.context.bind(param1.clone(), right[j].clone());
2269
2270 let cmp_result = self.evaluate_internal(body, data)?;
2271
2272 if self.is_truthy(&cmp_result) {
2273 temp.push(right[j].clone());
2274 j += 1;
2275 } else {
2276 temp.push(left[i].clone());
2277 i += 1;
2278 }
2279 }
2280 self.context.pop_scope();
2281 } else {
2282 while i < left.len() && j < right.len() {
2284 let cmp_result = self.apply_function(
2285 comparator,
2286 &[left[i].clone(), right[j].clone()],
2287 data,
2288 )?;
2289 if self.is_truthy(&cmp_result) {
2290 temp.push(right[j].clone());
2291 j += 1;
2292 } else {
2293 temp.push(left[i].clone());
2294 i += 1;
2295 }
2296 }
2297 }
2298 } else {
2299 while i < left.len() && j < right.len() {
2301 let cmp_result = self.apply_function(
2302 comparator,
2303 &[left[i].clone(), right[j].clone()],
2304 data,
2305 )?;
2306 if self.is_truthy(&cmp_result) {
2307 temp.push(right[j].clone());
2308 j += 1;
2309 } else {
2310 temp.push(left[i].clone());
2311 i += 1;
2312 }
2313 }
2314 }
2315
2316 temp.extend_from_slice(&left[i..]);
2318 temp.extend_from_slice(&right[j..]);
2319
2320 for (i, val) in temp.into_iter().enumerate() {
2322 arr[i] = val;
2323 }
2324
2325 Ok(())
2326 }
2327
2328 pub fn evaluate(&mut self, node: &AstNode, data: &JValue) -> Result<JValue, EvaluatorError> {
2333 if self.context.get_parent().is_none() {
2335 self.context.set_parent(data.clone());
2336 }
2337
2338 self.evaluate_internal(node, data)
2339 }
2340
2341 #[inline(always)]
2345 fn evaluate_leaf(&mut self, node: &AstNode, data: &JValue) -> Option<Result<JValue, EvaluatorError>> {
2346 match node {
2347 AstNode::String(s) => Some(Ok(JValue::string(s.clone()))),
2348 AstNode::Number(n) => {
2349 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2350 Some(Ok(JValue::from(*n as i64)))
2351 } else {
2352 Some(Ok(JValue::Number(*n)))
2353 }
2354 }
2355 AstNode::Boolean(b) => Some(Ok(JValue::Bool(*b))),
2356 AstNode::Null => Some(Ok(JValue::Null)),
2357 AstNode::Undefined => Some(Ok(JValue::Undefined)),
2358 AstNode::Name(field_name) => match data {
2359 JValue::Object(obj) => Some(Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null))),
2361 _ => None,
2362 },
2363 AstNode::Variable(name) if !name.is_empty() => {
2364 if let JValue::Object(obj) = data {
2366 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2367 return None; }
2369 }
2370 self.context.lookup(name).map(|value| Ok(value.clone()))
2372 }
2373 _ => None,
2374 }
2375 }
2376
2377 fn evaluate_internal(
2379 &mut self,
2380 node: &AstNode,
2381 data: &JValue,
2382 ) -> Result<JValue, EvaluatorError> {
2383 if let Some(result) = self.evaluate_leaf(node, data) {
2385 return result;
2386 }
2387
2388 self.recursion_depth += 1;
2390 if self.recursion_depth > self.max_recursion_depth {
2391 self.recursion_depth -= 1;
2392 return Err(EvaluatorError::EvaluationError(format!(
2393 "U1001: Stack overflow - maximum recursion depth ({}) exceeded",
2394 self.max_recursion_depth
2395 )));
2396 }
2397
2398 let result = self.evaluate_internal_impl(node, data);
2399
2400 self.recursion_depth -= 1;
2401 result
2402 }
2403
2404 fn evaluate_internal_impl(
2406 &mut self,
2407 node: &AstNode,
2408 data: &JValue,
2409 ) -> Result<JValue, EvaluatorError> {
2410 match node {
2411 AstNode::String(s) => Ok(JValue::string(s.clone())),
2412
2413 AstNode::Name(field_name) => {
2415 match data {
2416 JValue::Object(obj) => Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null)),
2417 JValue::Array(arr) => {
2418 let mut result = Vec::new();
2420 for item in arr.iter() {
2421 if let JValue::Object(obj) = item {
2422 if let Some(val) = obj.get(field_name) {
2423 result.push(val.clone());
2424 }
2425 }
2426 }
2427 if result.is_empty() {
2428 Ok(JValue::Null)
2429 } else if result.len() == 1 {
2430 Ok(result.into_iter().next().unwrap())
2431 } else {
2432 Ok(JValue::array(result))
2433 }
2434 }
2435 _ => Ok(JValue::Null),
2436 }
2437 }
2438
2439 AstNode::Number(n) => {
2440 if n.fract() == 0.0 && n.is_finite() && n.abs() < (1i64 << 53) as f64 {
2442 Ok(JValue::from(*n as i64))
2444 } else {
2445 Ok(JValue::Number(*n))
2446 }
2447 }
2448 AstNode::Boolean(b) => Ok(JValue::Bool(*b)),
2449 AstNode::Null => Ok(JValue::Null),
2450 AstNode::Undefined => Ok(JValue::Undefined),
2451 AstNode::Placeholder => {
2452 Err(EvaluatorError::EvaluationError(
2455 "Placeholder '?' can only be used as a function argument".to_string(),
2456 ))
2457 }
2458 AstNode::Regex { pattern, flags } => {
2459 Ok(JValue::regex(pattern.as_str(), flags.as_str()))
2462 }
2463
2464 AstNode::Variable(name) => {
2465 if name.is_empty() {
2469 if let Some(value) = self.context.lookup("$") {
2470 return Ok(value.clone());
2471 }
2472 if let JValue::Object(obj) = data {
2474 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2475 if let Some(inner) = obj.get("@") {
2476 return Ok(inner.clone());
2477 }
2478 }
2479 }
2480 return Ok(data.clone());
2481 }
2482
2483 if let Some(value) = self.context.lookup(name) {
2487 return Ok(value.clone());
2488 }
2489
2490 if let JValue::Object(obj) = data {
2493 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
2494 let binding_key = format!("${}", name);
2496 if let Some(binding_value) = obj.get(&binding_key) {
2497 return Ok(binding_value.clone());
2498 }
2499 }
2500 }
2501
2502 if let Some(stored_lambda) = self.context.lookup_lambda(name) {
2504 let lambda_repr = JValue::lambda(
2508 name.as_str(),
2509 stored_lambda.params.clone(),
2510 Some(name.to_string()),
2511 stored_lambda.signature.clone(),
2512 );
2513 return Ok(lambda_repr);
2514 }
2515
2516 if self.is_builtin_function(name) {
2518 let builtin_repr = JValue::builtin(name.as_str());
2521 return Ok(builtin_repr);
2522 }
2523
2524 Ok(JValue::Null)
2528 }
2529
2530 AstNode::ParentVariable(name) => {
2531 if name.is_empty() {
2533 return self.context.get_parent().cloned().ok_or_else(|| {
2534 EvaluatorError::ReferenceError("Parent context not available".to_string())
2535 });
2536 }
2537
2538 let parent_data = self.context.get_parent().ok_or_else(|| {
2541 EvaluatorError::ReferenceError("Parent context not available".to_string())
2542 })?;
2543
2544 match parent_data {
2546 JValue::Object(obj) => Ok(obj.get(name).cloned().unwrap_or(JValue::Null)),
2547 _ => Ok(JValue::Null),
2548 }
2549 }
2550
2551 AstNode::Path { steps } => self.evaluate_path(steps, data),
2552
2553 AstNode::Binary { op, lhs, rhs } => self.evaluate_binary_op(*op, lhs, rhs, data),
2554
2555 AstNode::Unary { op, operand } => self.evaluate_unary_op(*op, operand, data),
2556
2557 AstNode::Array(elements) => {
2559 let mut result = Vec::with_capacity(elements.len());
2563 for element in elements {
2564 let is_array_constructor = matches!(element, AstNode::Array(_));
2566
2567 let value = self.evaluate_internal(element, data)?;
2568
2569 if value.is_undefined() {
2572 continue;
2573 }
2574
2575 if is_array_constructor {
2576 result.push(value);
2578 } else if let JValue::Array(arr) = value {
2579 result.extend(arr.iter().cloned());
2581 } else {
2582 result.push(value);
2584 }
2585 }
2586 Ok(JValue::array(result))
2587 }
2588
2589 AstNode::Object(pairs) => {
2590 let mut result = IndexMap::with_capacity(pairs.len());
2591
2592 let all_literal_keys = pairs
2594 .iter()
2595 .all(|(k, _)| matches!(k, AstNode::String(_)));
2596
2597 if all_literal_keys {
2598 for (key_node, value_node) in pairs.iter() {
2600 let key = match key_node {
2601 AstNode::String(s) => s,
2602 _ => unreachable!(),
2603 };
2604 let value = self.evaluate_internal(value_node, data)?;
2605 if value.is_undefined() {
2606 continue;
2607 }
2608 result.insert(key.clone(), value);
2609 }
2610 } else {
2611 let mut key_sources: HashMap<String, usize> = HashMap::new();
2612 for (pair_index, (key_node, value_node)) in pairs.iter().enumerate() {
2613 let key = match self.evaluate_internal(key_node, data)? {
2614 JValue::String(s) => s,
2615 JValue::Null => continue,
2616 other => {
2617 if other.is_undefined() {
2618 continue;
2619 }
2620 return Err(EvaluatorError::TypeError(format!(
2621 "Object key must be a string, got: {:?}",
2622 other
2623 )));
2624 }
2625 };
2626
2627 if let Some(&existing_idx) = key_sources.get(&*key) {
2628 if existing_idx != pair_index {
2629 return Err(EvaluatorError::EvaluationError(format!(
2630 "D1009: Multiple key expressions evaluate to same key: {}",
2631 key
2632 )));
2633 }
2634 }
2635 key_sources.insert(key.to_string(), pair_index);
2636
2637 let value = self.evaluate_internal(value_node, data)?;
2638 if value.is_undefined() {
2639 continue;
2640 }
2641 result.insert(key.to_string(), value);
2642 }
2643 }
2644 Ok(JValue::object(result))
2645 }
2646
2647 AstNode::ObjectTransform { input, pattern } => {
2649 let input_value = self.evaluate_internal(input, data)?;
2651
2652 if input_value.is_undefined() {
2654 return Ok(JValue::Undefined);
2655 }
2656
2657 let items: Vec<JValue> = match input_value {
2659 JValue::Array(ref arr) => (**arr).clone(),
2660 JValue::Null => return Ok(JValue::Null),
2661 other => vec![other],
2662 };
2663
2664 let items = if items.is_empty() {
2666 vec![JValue::Undefined]
2667 } else {
2668 items
2669 };
2670
2671 let mut groups: HashMap<String, (Vec<JValue>, usize)> = HashMap::new();
2675
2676 let saved_dollar = self.context.lookup("$").cloned();
2678
2679 for item in &items {
2680 self.context.bind("$".to_string(), item.clone());
2682
2683 for (pair_index, (key_node, _value_node)) in pattern.iter().enumerate() {
2684 let key = match self.evaluate_internal(key_node, item)? {
2686 JValue::String(s) => s,
2687 JValue::Null => continue, other => {
2689 if other.is_undefined() {
2691 continue;
2692 }
2693 return Err(EvaluatorError::TypeError(format!(
2694 "T1003: Object key must be a string, got: {:?}",
2695 other
2696 )));
2697 }
2698 };
2699
2700 if let Some((existing_data, existing_idx)) = groups.get_mut(&*key) {
2702 if *existing_idx != pair_index {
2704 return Err(EvaluatorError::EvaluationError(format!(
2706 "D1009: Multiple key expressions evaluate to same key: {}",
2707 key
2708 )));
2709 }
2710 existing_data.push(item.clone());
2712 } else {
2713 groups.insert(key.to_string(), (vec![item.clone()], pair_index));
2715 }
2716 }
2717 }
2718
2719 let mut result = IndexMap::new();
2721
2722 for (key, (grouped_data, expr_index)) in groups {
2723 let (_key_node, value_node) = &pattern[expr_index];
2725
2726 let context = if grouped_data.len() == 1 {
2730 grouped_data.into_iter().next().unwrap()
2731 } else {
2732 JValue::array(grouped_data)
2733 };
2734
2735 self.context.bind("$".to_string(), context.clone());
2737
2738 let value = self.evaluate_internal(value_node, &context)?;
2740
2741 if !value.is_undefined() {
2743 result.insert(key, value);
2744 }
2745 }
2746
2747 if let Some(saved) = saved_dollar {
2749 self.context.bind("$".to_string(), saved);
2750 } else {
2751 self.context.unbind("$");
2752 }
2753
2754 Ok(JValue::object(result))
2755 }
2756
2757 AstNode::Function {
2758 name,
2759 args,
2760 is_builtin,
2761 } => self.evaluate_function_call(name, args, *is_builtin, data),
2762
2763 AstNode::Call { procedure, args } => {
2766 let callable = self.evaluate_internal(procedure, data)?;
2768
2769 if let Some(stored_lambda) = self.lookup_lambda_from_value(&callable) {
2771 let mut evaluated_args = Vec::with_capacity(args.len());
2772 for arg in args.iter() {
2773 evaluated_args.push(self.evaluate_internal(arg, data)?);
2774 }
2775 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
2776 }
2777
2778 Err(EvaluatorError::TypeError(format!(
2780 "Cannot call non-function value: {:?}",
2781 callable
2782 )))
2783 }
2784
2785 AstNode::Conditional {
2786 condition,
2787 then_branch,
2788 else_branch,
2789 } => {
2790 let condition_value = self.evaluate_internal(condition, data)?;
2791 if self.is_truthy(&condition_value) {
2792 self.evaluate_internal(then_branch, data)
2793 } else if let Some(else_branch) = else_branch {
2794 self.evaluate_internal(else_branch, data)
2795 } else {
2796 Ok(JValue::Undefined)
2799 }
2800 }
2801
2802 AstNode::Block(expressions) => {
2803 self.context.push_scope();
2805
2806 let mut result = JValue::Null;
2807 for expr in expressions {
2808 result = self.evaluate_internal(expr, data)?;
2809 }
2810
2811 let lambdas_to_keep = self.extract_lambda_ids(&result);
2814 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
2815
2816 Ok(result)
2817 }
2818
2819 AstNode::Lambda {
2821 params,
2822 body,
2823 signature,
2824 thunk,
2825 } => {
2826 let lambda_id = format!("__lambda_{}_{:p}", params.len(), body.as_ref());
2827
2828 let compiled_body = if !thunk {
2829 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
2830 try_compile_expr_with_allowed_vars(body, &var_refs)
2831 } else {
2832 None
2833 };
2834 let stored_lambda = StoredLambda {
2835 params: params.clone(),
2836 body: (**body).clone(),
2837 compiled_body,
2838 signature: signature.clone(),
2839 captured_env: self.capture_environment_for(body, params),
2840 captured_data: Some(data.clone()),
2841 thunk: *thunk,
2842 };
2843 self.context.bind_lambda(lambda_id.clone(), stored_lambda);
2844
2845 let lambda_obj = JValue::lambda(
2846 lambda_id.as_str(),
2847 params.clone(),
2848 None::<String>,
2849 signature.clone(),
2850 );
2851
2852 Ok(lambda_obj)
2853 }
2854
2855 AstNode::Wildcard => {
2857 match data {
2858 JValue::Object(obj) => {
2859 let mut result = Vec::new();
2860 for value in obj.values() {
2861 match value {
2863 JValue::Array(arr) => result.extend(arr.iter().cloned()),
2864 _ => result.push(value.clone()),
2865 }
2866 }
2867 Ok(JValue::array(result))
2868 }
2869 JValue::Array(arr) => {
2870 Ok(JValue::Array(arr.clone()))
2872 }
2873 _ => Ok(JValue::Null),
2874 }
2875 }
2876
2877 AstNode::Descendant => {
2879 let descendants = self.collect_descendants(data);
2880 if descendants.is_empty() {
2881 Ok(JValue::Null) } else {
2883 Ok(JValue::array(descendants))
2884 }
2885 }
2886
2887 AstNode::Predicate(_) => Err(EvaluatorError::EvaluationError(
2888 "Predicate can only be used in path expressions".to_string(),
2889 )),
2890
2891 AstNode::ArrayGroup(elements) => {
2893 let mut result = Vec::new();
2894 for element in elements {
2895 let value = self.evaluate_internal(element, data)?;
2896 result.push(value);
2897 }
2898 Ok(JValue::array(result))
2899 }
2900
2901 AstNode::FunctionApplication(_) => Err(EvaluatorError::EvaluationError(
2902 "Function application can only be used in path expressions".to_string(),
2903 )),
2904
2905 AstNode::Sort { input, terms } => {
2906 let value = self.evaluate_internal(input, data)?;
2907 self.evaluate_sort(&value, terms)
2908 }
2909
2910 AstNode::IndexBind { input, variable } => {
2912 let value = self.evaluate_internal(input, data)?;
2913
2914 match value {
2917 JValue::Array(arr) => {
2918 let mut result = Vec::new();
2920 for (idx, item) in arr.iter().enumerate() {
2921 let mut wrapper = IndexMap::new();
2923 wrapper.insert("@".to_string(), item.clone());
2924 wrapper.insert(format!("${}", variable), JValue::Number(idx as f64));
2925 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
2926 result.push(JValue::object(wrapper));
2927 }
2928 Ok(JValue::array(result))
2929 }
2930 other => {
2932 let mut wrapper = IndexMap::new();
2933 wrapper.insert("@".to_string(), other);
2934 wrapper.insert(format!("${}", variable), JValue::from(0i64));
2935 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
2936 Ok(JValue::object(wrapper))
2937 }
2938 }
2939 }
2940
2941 AstNode::Transform {
2943 location,
2944 update,
2945 delete,
2946 } => {
2947 if self.context.lookup("$").is_some() {
2949 self.execute_transform(location, update, delete.as_deref(), data)
2951 } else {
2952 let transform_lambda = StoredLambda {
2955 params: vec!["$".to_string()],
2956 body: AstNode::Transform {
2957 location: location.clone(),
2958 update: update.clone(),
2959 delete: delete.clone(),
2960 },
2961 compiled_body: None, signature: None,
2963 captured_env: HashMap::new(),
2964 captured_data: None, thunk: false,
2966 };
2967
2968 let lambda_name = format!("__transform_{:p}", location);
2970 self.context.bind_lambda(lambda_name, transform_lambda);
2971
2972 Ok(JValue::string("<lambda>"))
2974 }
2975 }
2976 }
2977 }
2978
2979 fn apply_stages(&mut self, value: JValue, stages: &[Stage]) -> Result<JValue, EvaluatorError> {
2983 let mut result = match value {
2985 JValue::Null => return Ok(JValue::Null), JValue::Array(_) => value,
2987 other => JValue::array(vec![other]),
2988 };
2989
2990 for stage in stages {
2991 match stage {
2992 Stage::Filter(predicate_expr) => {
2993 result = self.evaluate_predicate_as_stage(&result, predicate_expr)?;
2995 }
2996 }
2997 }
2998 Ok(result)
2999 }
3000
3001 fn is_filter_predicate(predicate: &AstNode) -> bool {
3004 match predicate {
3005 AstNode::Binary { op, .. } => matches!(
3006 op,
3007 BinaryOp::GreaterThan
3008 | BinaryOp::GreaterThanOrEqual
3009 | BinaryOp::LessThan
3010 | BinaryOp::LessThanOrEqual
3011 | BinaryOp::Equal
3012 | BinaryOp::NotEqual
3013 | BinaryOp::And
3014 | BinaryOp::Or
3015 | BinaryOp::In
3016 ),
3017 AstNode::Unary {
3018 op: crate::ast::UnaryOp::Not,
3019 ..
3020 } => true,
3021 _ => false,
3022 }
3023 }
3024
3025 fn evaluate_predicate_as_stage(
3029 &mut self,
3030 current: &JValue,
3031 predicate: &AstNode,
3032 ) -> Result<JValue, EvaluatorError> {
3033 if matches!(predicate, AstNode::Boolean(true)) {
3035 return match current {
3036 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
3037 JValue::Null => Ok(JValue::Null),
3038 other => Ok(JValue::array(vec![other.clone()])),
3039 };
3040 }
3041
3042 match current {
3043 JValue::Array(arr) => {
3044 if let AstNode::Number(n) = predicate {
3049 let is_array_of_arrays =
3051 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3052
3053 if !is_array_of_arrays {
3054 return self.array_index(current, &JValue::Number(*n));
3056 }
3057
3058 let mut result = Vec::new();
3060 for item in arr.iter() {
3061 match item {
3062 JValue::Array(_) => {
3063 let indexed = self.array_index(item, &JValue::Number(*n))?;
3064 if !indexed.is_null() {
3065 result.push(indexed);
3066 }
3067 }
3068 _ => {
3069 if *n == 0.0 {
3070 result.push(item.clone());
3071 }
3072 }
3073 }
3074 }
3075 return Ok(JValue::array(result));
3076 }
3077
3078 if Self::is_filter_predicate(predicate) {
3081 if let Some(compiled) = try_compile_expr(predicate) {
3083 let shape = arr.first().and_then(build_shape_cache);
3084 let mut filtered = Vec::with_capacity(arr.len());
3085 for item in arr.iter() {
3086 let result = if let Some(ref s) = shape {
3087 eval_compiled_shaped(&compiled, item, None, s)?
3088 } else {
3089 eval_compiled(&compiled, item, None)?
3090 };
3091 if compiled_is_truthy(&result) {
3092 filtered.push(item.clone());
3093 }
3094 }
3095 return Ok(JValue::array(filtered));
3096 }
3097 let mut filtered = Vec::new();
3099 for item in arr.iter() {
3100 let item_result = self.evaluate_internal(predicate, item)?;
3101 if self.is_truthy(&item_result) {
3102 filtered.push(item.clone());
3103 }
3104 }
3105 return Ok(JValue::array(filtered));
3106 }
3107
3108 match self.evaluate_internal(predicate, current) {
3113 Ok(JValue::Number(n)) => {
3114 let n_val = n;
3115 let is_array_of_arrays =
3116 arr.iter().any(|item| matches!(item, JValue::Array(_)));
3117
3118 if !is_array_of_arrays {
3119 let pred_result = JValue::Number(n_val);
3120 return self.array_index(current, &pred_result);
3121 }
3122
3123 let mut result = Vec::new();
3125 let pred_result = JValue::Number(n_val);
3126 for item in arr.iter() {
3127 match item {
3128 JValue::Array(_) => {
3129 let indexed = self.array_index(item, &pred_result)?;
3130 if !indexed.is_null() {
3131 result.push(indexed);
3132 }
3133 }
3134 _ => {
3135 if n_val == 0.0 {
3136 result.push(item.clone());
3137 }
3138 }
3139 }
3140 }
3141 return Ok(JValue::array(result));
3142 }
3143 Ok(JValue::Array(indices)) => {
3144 let has_non_numeric =
3147 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
3148
3149 if has_non_numeric {
3150 } else {
3152 let arr_len = arr.len() as i64;
3154 let mut resolved_indices: Vec<i64> = indices
3155 .iter()
3156 .filter_map(|v| {
3157 if let JValue::Number(n) = v {
3158 let idx = *n as i64;
3159 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
3161 if actual_idx >= 0 && actual_idx < arr_len {
3163 Some(actual_idx)
3164 } else {
3165 None
3166 }
3167 } else {
3168 None
3169 }
3170 })
3171 .collect();
3172
3173 resolved_indices.sort();
3175 resolved_indices.dedup();
3176
3177 let result: Vec<JValue> = resolved_indices
3179 .iter()
3180 .map(|&idx| arr[idx as usize].clone())
3181 .collect();
3182
3183 return Ok(JValue::array(result));
3184 }
3185 }
3186 Ok(_) => {
3187 }
3190 Err(_) => {
3191 }
3194 }
3195
3196 let mut filtered = Vec::new();
3198 for item in arr.iter() {
3199 let item_result = self.evaluate_internal(predicate, item)?;
3200 if self.is_truthy(&item_result) {
3201 filtered.push(item.clone());
3202 }
3203 }
3204 Ok(JValue::array(filtered))
3205 }
3206 JValue::Null => {
3207 Ok(JValue::Null)
3209 }
3210 other => {
3211 if let AstNode::Number(n) = predicate {
3217 if *n == 0.0 {
3219 return Ok(other.clone());
3220 } else {
3221 return Ok(JValue::Null);
3222 }
3223 }
3224
3225 match self.evaluate_internal(predicate, other) {
3227 Ok(JValue::Number(n)) => {
3228 if n == 0.0 {
3230 Ok(other.clone())
3231 } else {
3232 Ok(JValue::Null)
3233 }
3234 }
3235 Ok(pred_result) => {
3236 if self.is_truthy(&pred_result) {
3238 Ok(other.clone())
3239 } else {
3240 Ok(JValue::Null)
3241 }
3242 }
3243 Err(e) => Err(e),
3244 }
3245 }
3246 }
3247 }
3248
3249 fn evaluate_path(
3251 &mut self,
3252 steps: &[PathStep],
3253 data: &JValue,
3254 ) -> Result<JValue, EvaluatorError> {
3255 if steps.is_empty() {
3257 return Ok(data.clone());
3258 }
3259
3260 if steps.len() == 1 {
3263 if let AstNode::Name(field_name) = &steps[0].node {
3264 return match data {
3265 JValue::Object(obj) => {
3266 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
3268 if let Some(JValue::Object(inner)) = obj.get("@") {
3269 Ok(inner.get(field_name).cloned().unwrap_or(JValue::Null))
3270 } else {
3271 Ok(JValue::Null)
3272 }
3273 } else {
3274 Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null))
3275 }
3276 }
3277 JValue::Array(arr) => {
3278 let has_tuples = arr.first().is_some_and(|item| {
3282 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
3283 });
3284
3285 if !has_tuples {
3286 let mut result = Vec::with_capacity(arr.len());
3288 for item in arr.iter() {
3289 if let JValue::Object(obj) = item {
3290 if let Some(val) = obj.get(field_name) {
3291 if !val.is_null() {
3292 match val {
3293 JValue::Array(arr_val) => {
3294 result.extend(arr_val.iter().cloned());
3295 }
3296 other => result.push(other.clone()),
3297 }
3298 }
3299 }
3300 } else if let JValue::Array(inner_arr) = item {
3301 let nested_result = self.evaluate_path(
3302 &[PathStep::new(AstNode::Name(field_name.clone()))],
3303 &JValue::Array(inner_arr.clone()),
3304 )?;
3305 match nested_result {
3306 JValue::Array(nested) => {
3307 result.extend(nested.iter().cloned());
3308 }
3309 JValue::Null => {}
3310 other => result.push(other),
3311 }
3312 }
3313 }
3314
3315 if result.is_empty() {
3316 Ok(JValue::Null)
3317 } else if result.len() == 1 {
3318 Ok(result.into_iter().next().unwrap())
3319 } else {
3320 Ok(JValue::array(result))
3321 }
3322 } else {
3323 let mut result = Vec::new();
3325 for item in arr.iter() {
3326 match item {
3327 JValue::Object(obj) => {
3328 let is_tuple =
3329 obj.get("__tuple__") == Some(&JValue::Bool(true));
3330
3331 if is_tuple {
3332 let inner = match obj.get("@") {
3333 Some(JValue::Object(inner)) => inner,
3334 _ => continue,
3335 };
3336
3337 if let Some(val) = inner.get(field_name) {
3338 if !val.is_null() {
3339 let wrap = |v: JValue| -> JValue {
3341 let mut wrapper = IndexMap::new();
3342 wrapper.insert("@".to_string(), v);
3343 wrapper.insert(
3344 "__tuple__".to_string(),
3345 JValue::Bool(true),
3346 );
3347 for (k, v) in obj.iter() {
3348 if k.starts_with('$') {
3349 wrapper.insert(k.clone(), v.clone());
3350 }
3351 }
3352 JValue::object(wrapper)
3353 };
3354
3355 match val {
3356 JValue::Array(arr_val) => {
3357 for item in arr_val.iter() {
3358 result.push(wrap(item.clone()));
3359 }
3360 }
3361 other => result.push(wrap(other.clone())),
3362 }
3363 }
3364 }
3365 } else {
3366 if let Some(val) = obj.get(field_name) {
3368 if !val.is_null() {
3369 match val {
3370 JValue::Array(arr_val) => {
3371 for item in arr_val.iter() {
3372 result.push(item.clone());
3373 }
3374 }
3375 other => result.push(other.clone()),
3376 }
3377 }
3378 }
3379 }
3380 }
3381 JValue::Array(inner_arr) => {
3382 let nested_result = self.evaluate_path(
3384 &[PathStep::new(AstNode::Name(field_name.clone()))],
3385 &JValue::Array(inner_arr.clone()),
3386 )?;
3387 match nested_result {
3389 JValue::Array(nested) => {
3390 result.extend(nested.iter().cloned());
3392 }
3393 JValue::Null => {} other => result.push(other),
3395 }
3396 }
3397 _ => {} }
3399 }
3400
3401 if result.is_empty() {
3405 Ok(JValue::Null)
3406 } else if result.len() == 1 {
3407 Ok(result.into_iter().next().unwrap())
3408 } else {
3409 Ok(JValue::array(result))
3410 }
3411 } }
3413 _ => Ok(JValue::Null),
3414 };
3415 }
3416 }
3417
3418 if steps.len() == 2
3421 && steps[0].stages.is_empty()
3422 && steps[1].stages.is_empty()
3423 {
3424 if let (AstNode::Variable(var_name), AstNode::Name(field_name)) =
3425 (&steps[0].node, &steps[1].node)
3426 {
3427 if !var_name.is_empty() {
3428 if let Some(value) = self.context.lookup(var_name) {
3429 match value {
3430 JValue::Object(obj) => {
3431 return Ok(obj.get(field_name).cloned().unwrap_or(JValue::Null));
3432 }
3433 JValue::Array(arr) => {
3434 let mut result = Vec::with_capacity(arr.len());
3436 for item in arr.iter() {
3437 if let JValue::Object(obj) = item {
3438 if let Some(val) = obj.get(field_name) {
3439 if !val.is_null() {
3440 match val {
3441 JValue::Array(inner) => {
3442 result.extend(inner.iter().cloned());
3443 }
3444 other => result.push(other.clone()),
3445 }
3446 }
3447 }
3448 }
3449 }
3450 return match result.len() {
3451 0 => Ok(JValue::Null),
3452 1 => Ok(result.pop().unwrap()),
3453 _ => Ok(JValue::array(result)),
3454 };
3455 }
3456 _ => {} }
3458 }
3459 }
3460 }
3461 }
3462
3463 let mut did_array_mapping = false;
3465
3466 let mut current: JValue = match &steps[0].node {
3468 AstNode::Wildcard => {
3469 match data {
3471 JValue::Object(obj) => {
3472 let mut result = Vec::new();
3473 for value in obj.values() {
3474 match value {
3476 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3477 _ => result.push(value.clone()),
3478 }
3479 }
3480 JValue::array(result)
3481 }
3482 JValue::Array(arr) => JValue::Array(arr.clone()),
3483 _ => JValue::Null,
3484 }
3485 }
3486 AstNode::Descendant => {
3487 let descendants = self.collect_descendants(data);
3489 JValue::array(descendants)
3490 }
3491 AstNode::ParentVariable(name) => {
3492 let parent_data = self.context.get_parent().ok_or_else(|| {
3494 EvaluatorError::ReferenceError("Parent context not available".to_string())
3495 })?;
3496
3497 if name.is_empty() {
3498 parent_data.clone()
3500 } else {
3501 match parent_data {
3503 JValue::Object(obj) => obj.get(name).cloned().unwrap_or(JValue::Null),
3504 _ => JValue::Null,
3505 }
3506 }
3507 }
3508 AstNode::Name(field_name) => {
3509 let stages = &steps[0].stages;
3511
3512 match data {
3513 JValue::Object(obj) => {
3514 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3515 if !stages.is_empty() {
3517 self.apply_stages(val, stages)?
3518 } else {
3519 val
3520 }
3521 }
3522 JValue::Array(arr) => {
3523 let mut result = Vec::new();
3525 for item in arr.iter() {
3526 match item {
3527 JValue::Object(obj) => {
3528 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3529 if !val.is_null() {
3530 if !stages.is_empty() {
3531 let processed_val = self.apply_stages(val, stages)?;
3533 match processed_val {
3535 JValue::Array(arr) => {
3536 result.extend(arr.iter().cloned())
3537 }
3538 JValue::Null => {} other => result.push(other), }
3541 } else {
3542 match val {
3544 JValue::Array(arr) => {
3545 result.extend(arr.iter().cloned())
3546 }
3547 other => result.push(other),
3548 }
3549 }
3550 }
3551 }
3552 JValue::Array(inner_arr) => {
3553 let nested_result = self.evaluate_path(
3555 &[steps[0].clone()],
3556 &JValue::Array(inner_arr.clone()),
3557 )?;
3558 match nested_result {
3559 JValue::Array(nested) => {
3560 result.extend(nested.iter().cloned())
3561 }
3562 JValue::Null => {} other => result.push(other),
3564 }
3565 }
3566 _ => {} }
3568 }
3569 JValue::array(result)
3570 }
3571 JValue::Null => JValue::Null,
3572 _ => JValue::Undefined,
3574 }
3575 }
3576 AstNode::String(string_literal) => {
3577 let stages = &steps[0].stages;
3580 let val = JValue::string(string_literal.clone());
3581
3582 if !stages.is_empty() {
3583 let result = self.apply_stages(val, stages)?;
3585 match result {
3588 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
3589 JValue::Array(arr) if arr.is_empty() => JValue::Null,
3590 other => other,
3591 }
3592 } else {
3593 val
3594 }
3595 }
3596 AstNode::Predicate(pred_expr) => {
3597 self.evaluate_predicate(data, pred_expr)?
3599 }
3600 AstNode::IndexBind { .. } => {
3601 self.evaluate_internal(&steps[0].node, data)?
3603 }
3604 _ => {
3605 self.evaluate_path_step(&steps[0].node, data, data)?
3607 }
3608 };
3609
3610 for step in steps[1..].iter() {
3612 if current.is_null() || current.is_undefined() {
3615 return Ok(JValue::Null);
3616 }
3617
3618 let is_tuple_array = if let JValue::Array(arr) = ¤t {
3621 arr.first().is_some_and(|first| {
3622 if let JValue::Object(obj) = first {
3623 obj.get("__tuple__") == Some(&JValue::Bool(true))
3624 } else {
3625 false
3626 }
3627 })
3628 } else {
3629 false
3630 };
3631
3632 let needs_tuple_context_binding = is_tuple_array
3642 && matches!(
3643 &step.node,
3644 AstNode::Object(_) | AstNode::FunctionApplication(_) | AstNode::Variable(_)
3645 );
3646
3647 if needs_tuple_context_binding {
3648 if let JValue::Array(arr) = ¤t {
3649 let mut results = Vec::new();
3650
3651 for tuple in arr.iter() {
3652 if let JValue::Object(tuple_obj) = tuple {
3653 let bindings: Vec<(String, JValue)> = tuple_obj
3655 .iter()
3656 .filter(|(k, _)| k.starts_with('$') && k.len() > 1) .map(|(k, v)| (k[1..].to_string(), v.clone())) .collect();
3659
3660 let saved_bindings: Vec<(String, Option<JValue>)> = bindings
3662 .iter()
3663 .map(|(name, _)| (name.clone(), self.context.lookup(name).cloned()))
3664 .collect();
3665
3666 for (name, value) in &bindings {
3668 self.context.bind(name.clone(), value.clone());
3669 }
3670
3671 let actual_data = tuple_obj.get("@").cloned().unwrap_or(JValue::Null);
3673
3674 let step_result = match &step.node {
3676 AstNode::Variable(_) => {
3677 self.evaluate_internal(&step.node, tuple)?
3679 }
3680 AstNode::Object(_) | AstNode::FunctionApplication(_) => {
3681 self.evaluate_internal(&step.node, &actual_data)?
3683 }
3684 _ => unreachable!(), };
3686
3687 for (name, saved_value) in &saved_bindings {
3689 if let Some(value) = saved_value {
3690 self.context.bind(name.clone(), value.clone());
3691 } else {
3692 self.context.unbind(name);
3693 }
3694 }
3695
3696 if !step_result.is_null() && !step_result.is_undefined() {
3698 if matches!(&step.node, AstNode::Object(_)) {
3701 results.push(step_result);
3702 } else if matches!(step_result, JValue::Array(_)) {
3703 if let JValue::Array(arr) = step_result {
3704 results.extend(arr.iter().cloned());
3705 }
3706 } else {
3707 results.push(step_result);
3708 }
3709 }
3710 }
3711 }
3712
3713 current = JValue::array(results);
3714 continue; }
3716 }
3717
3718 current = match &step.node {
3719 AstNode::Wildcard => {
3720 let stages = &step.stages;
3722 let wildcard_result = match ¤t {
3723 JValue::Object(obj) => {
3724 let mut result = Vec::new();
3725 for value in obj.values() {
3726 match value {
3728 JValue::Array(arr) => result.extend(arr.iter().cloned()),
3729 _ => result.push(value.clone()),
3730 }
3731 }
3732 JValue::array(result)
3733 }
3734 JValue::Array(arr) => {
3735 let mut all_values = Vec::new();
3737 for item in arr.iter() {
3738 match item {
3739 JValue::Object(obj) => {
3740 for value in obj.values() {
3741 match value {
3743 JValue::Array(arr) => {
3744 all_values.extend(arr.iter().cloned())
3745 }
3746 _ => all_values.push(value.clone()),
3747 }
3748 }
3749 }
3750 JValue::Array(inner) => {
3751 all_values.extend(inner.iter().cloned());
3752 }
3753 _ => {}
3754 }
3755 }
3756 JValue::array(all_values)
3757 }
3758 _ => JValue::Null,
3759 };
3760
3761 if !stages.is_empty() {
3763 self.apply_stages(wildcard_result, stages)?
3764 } else {
3765 wildcard_result
3766 }
3767 }
3768 AstNode::Descendant => {
3769 match ¤t {
3771 JValue::Array(arr) => {
3772 let mut all_descendants = Vec::new();
3774 for item in arr.iter() {
3775 all_descendants.extend(self.collect_descendants(item));
3776 }
3777 JValue::array(all_descendants)
3778 }
3779 _ => {
3780 let descendants = self.collect_descendants(¤t);
3782 JValue::array(descendants)
3783 }
3784 }
3785 }
3786 AstNode::Name(field_name) => {
3787 let stages = &step.stages;
3789
3790 match ¤t {
3791 JValue::Object(obj) => {
3792 did_array_mapping = false;
3797 let val = obj.get(field_name).cloned().unwrap_or(JValue::Null);
3798 if !stages.is_empty() {
3800 self.apply_stages(val, stages)?
3801 } else {
3802 val
3803 }
3804 }
3805 JValue::Array(arr) => {
3806 did_array_mapping = true; let has_tuples = arr.first().is_some_and(|item| {
3814 matches!(item, JValue::Object(obj) if obj.get("__tuple__") == Some(&JValue::Bool(true)))
3815 });
3816
3817 if !has_tuples && stages.is_empty() {
3818 let mut result = Vec::with_capacity(arr.len());
3819 for item in arr.iter() {
3820 match item {
3821 JValue::Object(obj) => {
3822 if let Some(val) = obj.get(field_name) {
3823 if !val.is_null() {
3824 match val {
3825 JValue::Array(arr_val) => {
3826 result.extend(arr_val.iter().cloned())
3827 }
3828 other => result.push(other.clone()),
3829 }
3830 }
3831 }
3832 }
3833 JValue::Array(_) => {
3834 let nested_result =
3835 self.evaluate_path(&[step.clone()], item)?;
3836 match nested_result {
3837 JValue::Array(nested) => {
3838 result.extend(nested.iter().cloned())
3839 }
3840 JValue::Null => {}
3841 other => result.push(other),
3842 }
3843 }
3844 _ => {}
3845 }
3846 }
3847 JValue::array(result)
3848 } else {
3849 let mut result = Vec::new();
3851
3852 for item in arr.iter() {
3853 match item {
3854 JValue::Object(obj) => {
3855 let (actual_obj, tuple_bindings) =
3857 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
3858 if let Some(JValue::Object(inner)) = obj.get("@") {
3860 let bindings: Vec<(String, JValue)> = obj
3862 .iter()
3863 .filter(|(k, _)| k.starts_with('$'))
3864 .map(|(k, v)| (k.clone(), v.clone()))
3865 .collect();
3866 (inner.clone(), Some(bindings))
3867 } else {
3868 continue; }
3870 } else {
3871 (obj.clone(), None)
3872 };
3873
3874 let val = actual_obj
3875 .get(field_name)
3876 .cloned()
3877 .unwrap_or(JValue::Null);
3878
3879 if !val.is_null() {
3880 let wrap_in_tuple = |v: JValue, bindings: &Option<Vec<(String, JValue)>>| -> JValue {
3882 if let Some(b) = bindings {
3883 let mut wrapper = IndexMap::new();
3884 wrapper.insert("@".to_string(), v);
3885 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
3886 for (k, val) in b {
3887 wrapper.insert(k.clone(), val.clone());
3888 }
3889 JValue::object(wrapper)
3890 } else {
3891 v
3892 }
3893 };
3894
3895 if !stages.is_empty() {
3896 let processed_val =
3898 self.apply_stages(val, stages)?;
3899 match processed_val {
3901 JValue::Array(arr) => {
3902 for item in arr.iter() {
3903 result.push(wrap_in_tuple(
3904 item.clone(),
3905 &tuple_bindings,
3906 ));
3907 }
3908 }
3909 JValue::Null => {} other => result.push(wrap_in_tuple(
3911 other,
3912 &tuple_bindings,
3913 )),
3914 }
3915 } else {
3916 match val {
3919 JValue::Array(arr) => {
3920 for item in arr.iter() {
3921 result.push(wrap_in_tuple(
3922 item.clone(),
3923 &tuple_bindings,
3924 ));
3925 }
3926 }
3927 other => result.push(wrap_in_tuple(
3928 other,
3929 &tuple_bindings,
3930 )),
3931 }
3932 }
3933 }
3934 }
3935 JValue::Array(_) => {
3936 let nested_result =
3938 self.evaluate_path(&[step.clone()], item)?;
3939 match nested_result {
3940 JValue::Array(nested) => {
3941 result.extend(nested.iter().cloned())
3942 }
3943 JValue::Null => {}
3944 other => result.push(other),
3945 }
3946 }
3947 _ => {}
3948 }
3949 }
3950
3951 JValue::array(result)
3952 }
3953 }
3954 JValue::Null => JValue::Null,
3955 _ => JValue::Undefined,
3957 }
3958 }
3959 AstNode::String(string_literal) => {
3960 let stages = &step.stages;
3962 let val = JValue::string(string_literal.clone());
3963
3964 if !stages.is_empty() {
3965 let result = self.apply_stages(val, stages)?;
3967 match result {
3969 JValue::Array(arr) if arr.len() == 1 => arr[0].clone(),
3970 JValue::Array(arr) if arr.is_empty() => JValue::Null,
3971 other => other,
3972 }
3973 } else {
3974 val
3975 }
3976 }
3977 AstNode::Predicate(pred_expr) => {
3978 self.evaluate_predicate(¤t, pred_expr)?
3980 }
3981 AstNode::ArrayGroup(elements) => {
3982 match ¤t {
3985 JValue::Array(arr) => {
3986 let mut result = Vec::new();
3987 for item in arr.iter() {
3988 let mut group_values = Vec::new();
3990 for element in elements {
3991 let value = self.evaluate_internal(element, item)?;
3992 let should_preserve_array = matches!(
3995 element,
3996 AstNode::Array(_) | AstNode::ArrayGroup(_)
3997 );
3998
3999 if should_preserve_array {
4000 group_values.push(value);
4002 } else {
4003 match value {
4005 JValue::Array(arr) => {
4006 group_values.extend(arr.iter().cloned())
4007 }
4008 other => group_values.push(other),
4009 }
4010 }
4011 }
4012 result.push(JValue::array(group_values));
4014 }
4015 JValue::array(result)
4016 }
4017 _ => {
4018 let mut result = Vec::new();
4020 for element in elements {
4021 let value = self.evaluate_internal(element, ¤t)?;
4022 result.push(value);
4023 }
4024 JValue::array(result)
4025 }
4026 }
4027 }
4028 AstNode::FunctionApplication(expr) => {
4029 match ¤t {
4033 JValue::Array(arr) => {
4034 let mapped: Vec<JValue> =
4038 if let Some(compiled) = try_compile_expr(expr) {
4039 let shape = arr.first().and_then(build_shape_cache);
4040 let mut result = Vec::with_capacity(arr.len());
4041 for item in arr.iter() {
4042 let value = if let Some(ref s) = shape {
4043 eval_compiled_shaped(&compiled, item, None, s)?
4044 } else {
4045 eval_compiled(&compiled, item, None)?
4046 };
4047 if !value.is_null() && !value.is_undefined() {
4048 result.push(value);
4049 }
4050 }
4051 result
4052 } else {
4053 let mut result = Vec::new();
4054 for item in arr.iter() {
4055 let saved_dollar = self.context.lookup("$").cloned();
4057
4058 self.context.bind("$".to_string(), item.clone());
4060
4061 let value = self.evaluate_internal(expr, item)?;
4063
4064 if let Some(saved) = saved_dollar {
4066 self.context.bind("$".to_string(), saved);
4067 } else {
4068 self.context.unbind("$");
4069 }
4070
4071 if !value.is_null() && !value.is_undefined() {
4073 result.push(value);
4074 }
4075 }
4076 result
4077 };
4078 JValue::array(mapped)
4081 }
4082 _ => {
4083 let saved_dollar = self.context.lookup("$").cloned();
4085 self.context.bind("$".to_string(), current.clone());
4086
4087 let value = self.evaluate_internal(expr, ¤t)?;
4088
4089 if let Some(saved) = saved_dollar {
4090 self.context.bind("$".to_string(), saved);
4091 } else {
4092 self.context.unbind("$");
4093 }
4094
4095 value
4096 }
4097 }
4098 }
4099 AstNode::Sort { terms, .. } => {
4100 self.evaluate_sort(¤t, terms)?
4102 }
4103 AstNode::IndexBind { variable, .. } => {
4104 match ¤t {
4107 JValue::Array(arr) => {
4108 let mut result = Vec::new();
4109 for (idx, item) in arr.iter().enumerate() {
4110 let mut wrapper = IndexMap::new();
4111 wrapper.insert("@".to_string(), item.clone());
4112 wrapper
4113 .insert(format!("${}", variable), JValue::Number(idx as f64));
4114 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4115 result.push(JValue::object(wrapper));
4116 }
4117 JValue::array(result)
4118 }
4119 other => {
4120 let mut wrapper = IndexMap::new();
4122 wrapper.insert("@".to_string(), other.clone());
4123 wrapper.insert(format!("${}", variable), JValue::from(0i64));
4124 wrapper.insert("__tuple__".to_string(), JValue::Bool(true));
4125 JValue::object(wrapper)
4126 }
4127 }
4128 }
4129 _ => self.evaluate_path_step(&step.node, ¤t, data)?,
4131 };
4132 }
4133
4134 let has_explicit_array_keep = steps.iter().any(|step| {
4142 if let AstNode::Predicate(pred) = &step.node {
4144 if matches!(**pred, AstNode::Boolean(true)) {
4145 return true;
4146 }
4147 }
4148 step.stages.iter().any(|stage| {
4150 let crate::ast::Stage::Filter(pred) = stage;
4151 matches!(**pred, AstNode::Boolean(true))
4152 })
4153 });
4154
4155 let should_unwrap = !has_explicit_array_keep
4165 && (steps.iter().any(|step| !step.stages.is_empty()) || did_array_mapping);
4166
4167 let result = match ¤t {
4168 JValue::Array(arr) if arr.is_empty() => JValue::Null,
4170 JValue::Array(arr) if arr.len() == 1 && should_unwrap => arr[0].clone(),
4172 _ => current,
4174 };
4175
4176 Ok(result)
4177 }
4178
4179 fn evaluate_path_step(
4181 &mut self,
4182 step: &AstNode,
4183 current: &JValue,
4184 original_data: &JValue,
4185 ) -> Result<JValue, EvaluatorError> {
4186 if matches!(current, JValue::Array(_)) && matches!(step, AstNode::Object(_)) {
4189 match (current, step) {
4190 (JValue::Array(arr), AstNode::Object(pairs)) => {
4191 if let Some(compiled) = try_compile_expr(&AstNode::Object(pairs.clone())) {
4193 let shape = arr.first().and_then(build_shape_cache);
4194 let mut mapped = Vec::with_capacity(arr.len());
4195 for item in arr.iter() {
4196 let result = if let Some(ref s) = shape {
4197 eval_compiled_shaped(&compiled, item, None, s)?
4198 } else {
4199 eval_compiled(&compiled, item, None)?
4200 };
4201 if !result.is_undefined() {
4202 mapped.push(result);
4203 }
4204 }
4205 return Ok(JValue::array(mapped));
4206 }
4207 let mapped: Result<Vec<JValue>, EvaluatorError> = arr
4209 .iter()
4210 .map(|item| self.evaluate_internal(step, item))
4211 .collect();
4212 Ok(JValue::array(mapped?))
4213 }
4214 _ => unreachable!(),
4215 }
4216 } else {
4217 if let AstNode::Variable(name) = step {
4220 if name.is_empty() {
4221 if let JValue::Array(arr) = current {
4223 return Ok(JValue::Array(arr.clone()));
4225 } else {
4226 return Ok(current.clone());
4228 }
4229 }
4230 }
4231
4232 if matches!(step, AstNode::Variable(_)) {
4236 if let JValue::Array(arr) = current {
4237 let is_tuple_array = arr.first().is_some_and(|first| {
4239 if let JValue::Object(obj) = first {
4240 obj.get("__tuple__") == Some(&JValue::Bool(true))
4241 } else {
4242 false
4243 }
4244 });
4245
4246 if is_tuple_array {
4247 let mut results = Vec::new();
4249 for tuple in arr.iter() {
4250 let val = self.evaluate_internal(step, tuple)?;
4253 if !val.is_null() && !val.is_undefined() {
4254 results.push(val);
4255 }
4256 }
4257 return Ok(JValue::array(results));
4258 }
4259 }
4260 }
4261
4262 if matches!(
4271 step,
4272 AstNode::Binary { .. }
4273 | AstNode::Function { .. }
4274 | AstNode::Variable(_)
4275 | AstNode::ParentVariable(_)
4276 | AstNode::Array(_)
4277 | AstNode::Object(_)
4278 | AstNode::Sort { .. }
4279 | AstNode::Block(_)
4280 ) {
4281 return self.evaluate_internal(step, original_data);
4283 }
4284
4285 let step_value = self.evaluate_internal(step, original_data)?;
4287 Ok(match (current, &step_value) {
4288 (JValue::Object(obj), JValue::String(key)) => {
4289 obj.get(&**key).cloned().unwrap_or(JValue::Null)
4290 }
4291 (JValue::Array(arr), JValue::Number(n)) => {
4292 let index = *n as i64;
4293 let len = arr.len() as i64;
4294
4295 let actual_idx = if index < 0 { len + index } else { index };
4297
4298 if actual_idx < 0 || actual_idx >= len {
4299 JValue::Null
4300 } else {
4301 arr[actual_idx as usize].clone()
4302 }
4303 }
4304 _ => JValue::Null,
4305 })
4306 }
4307 }
4308
4309 fn evaluate_binary_op(
4311 &mut self,
4312 op: crate::ast::BinaryOp,
4313 lhs: &AstNode,
4314 rhs: &AstNode,
4315 data: &JValue,
4316 ) -> Result<JValue, EvaluatorError> {
4317 use crate::ast::BinaryOp;
4318
4319 if op == BinaryOp::Coalesce {
4323 return match self.evaluate_internal(lhs, data) {
4325 Ok(value) => {
4326 if matches!(lhs, AstNode::Null) {
4329 Ok(value)
4330 }
4331 else if value.is_null()
4333 && (matches!(lhs, AstNode::Path { .. })
4334 || matches!(lhs, AstNode::String(_))
4335 || matches!(lhs, AstNode::Variable(_)))
4336 {
4337 self.evaluate_internal(rhs, data)
4338 } else {
4339 Ok(value)
4340 }
4341 }
4342 Err(_) => {
4343 self.evaluate_internal(rhs, data)
4345 }
4346 };
4347 }
4348
4349 if op == BinaryOp::Default {
4352 let left = self.evaluate_internal(lhs, data)?;
4353 if self.is_truthy_for_default(&left) {
4354 return Ok(left);
4355 }
4356 return self.evaluate_internal(rhs, data);
4357 }
4358
4359 if op == BinaryOp::ChainPipe {
4363 if let AstNode::Regex { pattern, flags } = rhs {
4365 let lhs_value = self.evaluate_internal(lhs, data)?;
4367 return match lhs_value {
4369 JValue::String(s) => {
4370 let case_insensitive = flags.contains('i');
4372 let regex_pattern = if case_insensitive {
4373 format!("(?i){}", pattern)
4374 } else {
4375 pattern.clone()
4376 };
4377 match regex::Regex::new(®ex_pattern) {
4378 Ok(re) => {
4379 if let Some(m) = re.find(&s) {
4380 let mut result = IndexMap::new();
4382 result.insert(
4383 "match".to_string(),
4384 JValue::string(m.as_str().to_string()),
4385 );
4386 result.insert(
4387 "start".to_string(),
4388 JValue::Number(m.start() as f64),
4389 );
4390 result
4391 .insert("end".to_string(), JValue::Number(m.end() as f64));
4392
4393 let mut groups = Vec::new();
4395 for cap in re.captures_iter(&s).take(1) {
4396 for i in 1..cap.len() {
4397 if let Some(c) = cap.get(i) {
4398 groups.push(JValue::string(c.as_str().to_string()));
4399 }
4400 }
4401 }
4402 if !groups.is_empty() {
4403 result.insert("groups".to_string(), JValue::array(groups));
4404 }
4405
4406 Ok(JValue::object(result))
4407 } else {
4408 Ok(JValue::Null)
4409 }
4410 }
4411 Err(e) => Err(EvaluatorError::EvaluationError(format!(
4412 "Invalid regex: {}",
4413 e
4414 ))),
4415 }
4416 }
4417 JValue::Null => Ok(JValue::Null),
4418 _ => Err(EvaluatorError::TypeError(
4419 "Left side of ~> /regex/ must be a string".to_string(),
4420 )),
4421 };
4422 }
4423
4424 let lhs_value_for_check = self.evaluate_internal(lhs, data)?;
4427 if lhs_value_for_check.is_undefined() || lhs_value_for_check.is_null() {
4428 return Ok(JValue::Undefined);
4429 }
4430
4431 match rhs {
4433 AstNode::Function {
4434 name,
4435 args,
4436 is_builtin,
4437 } => {
4438 let has_placeholder =
4441 args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
4442
4443 if has_placeholder {
4444 let lhs_value = self.evaluate_internal(lhs, data)?;
4446 let mut filled_args = Vec::new();
4447 let mut lhs_used = false;
4448
4449 for arg in args.iter() {
4450 if matches!(arg, AstNode::Placeholder) && !lhs_used {
4451 let temp_name = format!("__pipe_arg_{}", filled_args.len());
4454 self.context.bind(temp_name.clone(), lhs_value.clone());
4455 filled_args.push(AstNode::Variable(temp_name));
4456 lhs_used = true;
4457 } else {
4458 filled_args.push(arg.clone());
4459 }
4460 }
4461
4462 let result =
4464 self.evaluate_function_call(name, &filled_args, *is_builtin, data);
4465
4466 for (i, arg) in args.iter().enumerate() {
4468 if matches!(arg, AstNode::Placeholder) {
4469 self.context.unbind(&format!("__pipe_arg_{}", i));
4470 }
4471 }
4472
4473 return result.map(|v| self.unwrap_singleton(v));
4475 } else {
4476 let mut all_args = vec![lhs.clone()];
4478 all_args.extend_from_slice(args);
4479 return self
4481 .evaluate_function_call(name, &all_args, *is_builtin, data)
4482 .map(|v| self.unwrap_singleton(v));
4483 }
4484 }
4485 AstNode::Variable(var_name) => {
4486 let all_args = vec![lhs.clone()];
4489 return self
4491 .evaluate_function_call(var_name, &all_args, true, data)
4492 .map(|v| self.unwrap_singleton(v));
4493 }
4494 AstNode::Binary {
4495 op: BinaryOp::ChainPipe,
4496 ..
4497 } => {
4498 let lhs_value = self.evaluate_internal(lhs, data)?;
4501 return self.evaluate_internal(rhs, &lhs_value);
4502 }
4503 AstNode::Transform { .. } => {
4504 let lhs_value = self.evaluate_internal(lhs, data)?;
4507
4508 let saved_binding = self.context.lookup("$").cloned();
4510 self.context.bind("$".to_string(), lhs_value.clone());
4511
4512 let result = self.evaluate_internal(rhs, data);
4513
4514 if let Some(saved) = saved_binding {
4516 self.context.bind("$".to_string(), saved);
4517 } else {
4518 self.context.unbind("$");
4519 }
4520
4521 return result.map(|v| self.unwrap_singleton(v));
4523 }
4524 AstNode::Lambda {
4525 params,
4526 body,
4527 signature,
4528 thunk,
4529 } => {
4530 let lhs_value = self.evaluate_internal(lhs, data)?;
4532 return self
4534 .invoke_lambda(params, body, signature.as_ref(), &[lhs_value], data, *thunk)
4535 .map(|v| self.unwrap_singleton(v));
4536 }
4537 AstNode::Path { steps } => {
4538 if let Some(first_step) = steps.first() {
4541 match &first_step.node {
4542 AstNode::Function {
4543 name,
4544 args,
4545 is_builtin,
4546 } => {
4547 let mut all_args = vec![lhs.clone()];
4549 all_args.extend_from_slice(args);
4550
4551 let mut result = self.evaluate_function_call(
4553 name,
4554 &all_args,
4555 *is_builtin,
4556 data,
4557 )?;
4558
4559 for stage in &first_step.stages {
4561 match stage {
4562 Stage::Filter(filter_expr) => {
4563 result = self.evaluate_predicate_as_stage(
4564 &result,
4565 filter_expr,
4566 )?;
4567 }
4568 }
4569 }
4570
4571 if steps.len() > 1 {
4573 let remaining_path = AstNode::Path {
4574 steps: steps[1..].to_vec(),
4575 };
4576 result = self.evaluate_internal(&remaining_path, &result)?;
4577 }
4578
4579 if !first_step.stages.is_empty() || steps.len() > 1 {
4582 return Ok(result);
4583 } else {
4584 return Ok(self.unwrap_singleton(result));
4585 }
4586 }
4587 AstNode::Variable(var_name) => {
4588 let all_args = vec![lhs.clone()];
4590 let mut result =
4591 self.evaluate_function_call(var_name, &all_args, true, data)?;
4592
4593 for stage in &first_step.stages {
4595 match stage {
4596 Stage::Filter(filter_expr) => {
4597 result = self.evaluate_predicate_as_stage(
4598 &result,
4599 filter_expr,
4600 )?;
4601 }
4602 }
4603 }
4604
4605 if steps.len() > 1 {
4607 let remaining_path = AstNode::Path {
4608 steps: steps[1..].to_vec(),
4609 };
4610 result = self.evaluate_internal(&remaining_path, &result)?;
4611 }
4612
4613 if !first_step.stages.is_empty() || steps.len() > 1 {
4616 return Ok(result);
4617 } else {
4618 return Ok(self.unwrap_singleton(result));
4619 }
4620 }
4621 _ => {
4622 let lhs_value = self.evaluate_internal(lhs, data)?;
4624 return self
4625 .evaluate_internal(rhs, &lhs_value)
4626 .map(|v| self.unwrap_singleton(v));
4627 }
4628 }
4629 }
4630
4631 let lhs_value = self.evaluate_internal(lhs, data)?;
4633 return self
4634 .evaluate_internal(rhs, &lhs_value)
4635 .map(|v| self.unwrap_singleton(v));
4636 }
4637 _ => {
4638 return Err(EvaluatorError::TypeError(
4639 "Right side of ~> must be a function call or function reference"
4640 .to_string(),
4641 ));
4642 }
4643 }
4644 }
4645
4646 if op == BinaryOp::ColonEqual {
4648 let var_name = match lhs {
4650 AstNode::Variable(name) => name.clone(),
4651 _ => {
4652 return Err(EvaluatorError::TypeError(
4653 "Left side of := must be a variable".to_string(),
4654 ))
4655 }
4656 };
4657
4658 if let AstNode::Lambda {
4660 params,
4661 body,
4662 signature,
4663 thunk,
4664 } = rhs
4665 {
4666 let captured_env = self.capture_environment_for(body, params);
4669 let compiled_body = if !thunk {
4670 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
4671 try_compile_expr_with_allowed_vars(body, &var_refs)
4672 } else {
4673 None
4674 };
4675 let stored_lambda = StoredLambda {
4676 params: params.clone(),
4677 body: (**body).clone(),
4678 compiled_body,
4679 signature: signature.clone(),
4680 captured_env,
4681 captured_data: Some(data.clone()),
4682 thunk: *thunk,
4683 };
4684 let lambda_params = stored_lambda.params.clone();
4685 let lambda_sig = stored_lambda.signature.clone();
4686 self.context.bind_lambda(var_name.clone(), stored_lambda);
4687
4688 let lambda_repr = JValue::lambda(
4690 var_name.as_str(),
4691 lambda_params,
4692 Some(var_name.clone()),
4693 lambda_sig,
4694 );
4695 return Ok(lambda_repr);
4696 }
4697
4698 if let AstNode::Binary {
4704 op: BinaryOp::ChainPipe,
4705 lhs: chain_lhs,
4706 rhs: chain_rhs,
4707 } = rhs
4708 {
4709 let is_function_composition = match chain_lhs.as_ref() {
4712 AstNode::Variable(name)
4714 if self.is_builtin_function(name)
4715 || self.context.lookup_lambda(name).is_some() =>
4716 {
4717 true
4718 }
4719 AstNode::Binary {
4721 op: BinaryOp::ChainPipe,
4722 ..
4723 } => true,
4724 AstNode::Function { args, .. }
4727 if args.iter().any(|a| matches!(a, AstNode::Placeholder)) =>
4728 {
4729 true
4730 }
4731 _ => false,
4733 };
4734
4735 if is_function_composition {
4736 let param_name = "$".to_string();
4740
4741 let first_pipe = AstNode::Binary {
4743 op: BinaryOp::ChainPipe,
4744 lhs: Box::new(AstNode::Variable(param_name.clone())),
4745 rhs: chain_lhs.clone(),
4746 };
4747
4748 let composed_body = AstNode::Binary {
4750 op: BinaryOp::ChainPipe,
4751 lhs: Box::new(first_pipe),
4752 rhs: chain_rhs.clone(),
4753 };
4754
4755 let stored_lambda = StoredLambda {
4756 params: vec![param_name],
4757 body: composed_body,
4758 compiled_body: None, signature: None,
4760 captured_env: self.capture_current_environment(),
4761 captured_data: Some(data.clone()),
4762 thunk: false,
4763 };
4764 self.context.bind_lambda(var_name.clone(), stored_lambda);
4765
4766 let lambda_repr = JValue::lambda(
4768 var_name.as_str(),
4769 vec!["$".to_string()],
4770 Some(var_name.clone()),
4771 None::<String>,
4772 );
4773 return Ok(lambda_repr);
4774 }
4775 }
4777
4778 let value = self.evaluate_internal(rhs, data)?;
4780
4781 if let Some(stored) = self.lookup_lambda_from_value(&value) {
4783 self.context.bind_lambda(var_name.clone(), stored);
4784 }
4785
4786 self.context.bind(var_name, value.clone());
4788 return Ok(value);
4789 }
4790
4791 if op == BinaryOp::In {
4794 let left = self.evaluate_internal(lhs, data)?;
4795
4796 if matches!(left, JValue::Array(_)) {
4798 let right_result = self.evaluate_internal(rhs, data);
4800
4801 if let Ok(JValue::Number(_)) = right_result {
4802 return self.array_index(&left, &right_result.unwrap());
4804 } else {
4805 return self.array_filter(lhs, rhs, &left, data);
4808 }
4809 }
4810 }
4811
4812 if op == BinaryOp::And {
4814 let left = self.evaluate_internal(lhs, data)?;
4815 if !self.is_truthy(&left) {
4816 return Ok(JValue::Bool(false));
4818 }
4819 let right = self.evaluate_internal(rhs, data)?;
4820 return Ok(JValue::Bool(self.is_truthy(&right)));
4821 }
4822
4823 if op == BinaryOp::Or {
4824 let left = self.evaluate_internal(lhs, data)?;
4825 if self.is_truthy(&left) {
4826 return Ok(JValue::Bool(true));
4828 }
4829 let right = self.evaluate_internal(rhs, data)?;
4830 return Ok(JValue::Bool(self.is_truthy(&right)));
4831 }
4832
4833 let left_is_explicit_null = matches!(lhs, AstNode::Null);
4835 let right_is_explicit_null = matches!(rhs, AstNode::Null);
4836
4837 let left = self.evaluate_internal(lhs, data)?;
4839 let right = self.evaluate_internal(rhs, data)?;
4840
4841 match op {
4842 BinaryOp::Add => self.add(&left, &right, left_is_explicit_null, right_is_explicit_null),
4843 BinaryOp::Subtract => {
4844 self.subtract(&left, &right, left_is_explicit_null, right_is_explicit_null)
4845 }
4846 BinaryOp::Multiply => {
4847 self.multiply(&left, &right, left_is_explicit_null, right_is_explicit_null)
4848 }
4849 BinaryOp::Divide => {
4850 self.divide(&left, &right, left_is_explicit_null, right_is_explicit_null)
4851 }
4852 BinaryOp::Modulo => {
4853 self.modulo(&left, &right, left_is_explicit_null, right_is_explicit_null)
4854 }
4855
4856 BinaryOp::Equal => Ok(JValue::Bool(self.equals(&left, &right))),
4857 BinaryOp::NotEqual => Ok(JValue::Bool(!self.equals(&left, &right))),
4858 BinaryOp::LessThan => {
4859 self.less_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
4860 }
4861 BinaryOp::LessThanOrEqual => self.less_than_or_equal(
4862 &left,
4863 &right,
4864 left_is_explicit_null,
4865 right_is_explicit_null,
4866 ),
4867 BinaryOp::GreaterThan => {
4868 self.greater_than(&left, &right, left_is_explicit_null, right_is_explicit_null)
4869 }
4870 BinaryOp::GreaterThanOrEqual => self.greater_than_or_equal(
4871 &left,
4872 &right,
4873 left_is_explicit_null,
4874 right_is_explicit_null,
4875 ),
4876
4877 BinaryOp::And | BinaryOp::Or => unreachable!(),
4879
4880 BinaryOp::Concatenate => self.concatenate(&left, &right),
4881 BinaryOp::Range => self.range(&left, &right),
4882 BinaryOp::In => self.in_operator(&left, &right),
4883
4884 BinaryOp::ColonEqual | BinaryOp::Coalesce | BinaryOp::Default | BinaryOp::ChainPipe => {
4886 unreachable!()
4887 }
4888 }
4889 }
4890
4891 fn evaluate_unary_op(
4893 &mut self,
4894 op: crate::ast::UnaryOp,
4895 operand: &AstNode,
4896 data: &JValue,
4897 ) -> Result<JValue, EvaluatorError> {
4898 use crate::ast::UnaryOp;
4899
4900 let value = self.evaluate_internal(operand, data)?;
4901
4902 match op {
4903 UnaryOp::Negate => match value {
4904 JValue::Null => Ok(JValue::Null),
4906 JValue::Number(n) => Ok(JValue::Number(-n)),
4907 _ => Err(EvaluatorError::TypeError(
4908 "D1002: Cannot negate non-number value".to_string(),
4909 )),
4910 },
4911 UnaryOp::Not => Ok(JValue::Bool(!self.is_truthy(&value))),
4912 }
4913 }
4914
4915 fn try_fused_aggregate(
4922 &mut self,
4923 name: &str,
4924 arg: &AstNode,
4925 data: &JValue,
4926 ) -> Result<Option<JValue>, EvaluatorError> {
4927 if !matches!(name, "sum" | "max" | "min" | "average") {
4929 return Ok(None);
4930 }
4931
4932 let AstNode::Path { steps } = arg else {
4934 return Ok(None);
4935 };
4936
4937 if steps.len() != 2 {
4940 return Ok(None);
4941 }
4942
4943 let field_step = &steps[1];
4945 if !field_step.stages.is_empty() {
4946 return Ok(None);
4947 }
4948 let AstNode::Name(extract_field) = &field_step.node else {
4949 return Ok(None);
4950 };
4951
4952 let arr_step = &steps[0];
4954 let AstNode::Name(arr_name) = &arr_step.node else {
4955 return Ok(None);
4956 };
4957
4958 let arr = match data {
4960 JValue::Object(obj) => match obj.get(arr_name) {
4961 Some(JValue::Array(arr)) => arr,
4962 _ => return Ok(None),
4963 },
4964 _ => return Ok(None),
4965 };
4966
4967 let filter_compiled = match arr_step.stages.as_slice() {
4969 [] => None,
4970 [Stage::Filter(pred)] => try_compile_expr(pred),
4971 _ => return Ok(None),
4972 };
4973 if !arr_step.stages.is_empty() && filter_compiled.is_none() {
4975 return Ok(None);
4976 }
4977
4978 let shape = arr.first().and_then(build_shape_cache);
4980
4981 let mut total = 0.0f64;
4983 let mut count = 0usize;
4984 let mut max_val = f64::NEG_INFINITY;
4985 let mut min_val = f64::INFINITY;
4986 let mut has_any = false;
4987
4988 for item in arr.iter() {
4989 if let Some(ref compiled) = filter_compiled {
4991 let result = if let Some(ref s) = shape {
4992 eval_compiled_shaped(compiled, item, None, s)?
4993 } else {
4994 eval_compiled(compiled, item, None)?
4995 };
4996 if !compiled_is_truthy(&result) {
4997 continue;
4998 }
4999 }
5000
5001 let val = match item {
5003 JValue::Object(obj) => match obj.get(extract_field) {
5004 Some(JValue::Number(n)) => *n,
5005 Some(_) | None => continue, },
5007 _ => continue,
5008 };
5009
5010 has_any = true;
5011 match name {
5012 "sum" => total += val,
5013 "max" => max_val = max_val.max(val),
5014 "min" => min_val = min_val.min(val),
5015 "average" => { total += val; count += 1; }
5016 _ => unreachable!(),
5017 }
5018 }
5019
5020 if !has_any {
5021 return Ok(Some(match name {
5022 "sum" => JValue::from(0i64),
5023 "average" | "max" | "min" => JValue::Null,
5024 _ => unreachable!(),
5025 }));
5026 }
5027
5028 Ok(Some(match name {
5029 "sum" => JValue::Number(total),
5030 "max" => JValue::Number(max_val),
5031 "min" => JValue::Number(min_val),
5032 "average" => JValue::Number(total / count as f64),
5033 _ => unreachable!(),
5034 }))
5035 }
5036
5037 fn evaluate_function_call(
5039 &mut self,
5040 name: &str,
5041 args: &[AstNode],
5042 is_builtin: bool,
5043 data: &JValue,
5044 ) -> Result<JValue, EvaluatorError> {
5045 use crate::functions;
5046
5047 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
5049 if has_placeholder {
5050 return self.create_partial_application(name, args, is_builtin, data);
5051 }
5052
5053 if let Some(value) = self.context.lookup(name).cloned() {
5060 if let Some(stored_lambda) = self.lookup_lambda_from_value(&value) {
5061 let mut evaluated_args = Vec::with_capacity(args.len());
5062 for arg in args {
5063 evaluated_args.push(self.evaluate_internal(arg, data)?);
5064 }
5065 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5066 }
5067 if let JValue::Builtin { name: builtin_name } = &value {
5068 let mut evaluated_args = Vec::with_capacity(args.len());
5070 for arg in args {
5071 evaluated_args.push(self.evaluate_internal(arg, data)?);
5072 }
5073 return self.call_builtin_with_values(builtin_name, &evaluated_args);
5074 }
5075 }
5076
5077 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
5080 let mut evaluated_args = Vec::with_capacity(args.len());
5081 for arg in args {
5082 evaluated_args.push(self.evaluate_internal(arg, data)?);
5083 }
5084 return self.invoke_stored_lambda(&stored_lambda, &evaluated_args, data);
5085 }
5086
5087 if !is_builtin && name != "__lambda__" {
5090 return Err(EvaluatorError::ReferenceError(format!(
5091 "Unknown function: {}",
5092 name
5093 )));
5094 }
5095
5096 if name == "exists" && args.len() == 1 {
5099 let arg = &args[0];
5100
5101 if matches!(arg, AstNode::Null) {
5103 return Ok(JValue::Bool(true)); }
5105
5106 if let AstNode::Variable(var_name) = arg {
5108 if self.is_builtin_function(var_name) {
5109 return Ok(JValue::Bool(true)); }
5111
5112 if self.context.lookup_lambda(var_name).is_some() {
5114 return Ok(JValue::Bool(true)); }
5116
5117 if let Some(val) = self.context.lookup(var_name) {
5119 if val.is_undefined() {
5121 return Ok(JValue::Bool(false));
5122 }
5123 return Ok(JValue::Bool(true)); } else {
5125 return Ok(JValue::Bool(false)); }
5127 }
5128
5129 let value = self.evaluate_internal(arg, data)?;
5131 return Ok(JValue::Bool(
5132 !value.is_null() && !value.is_undefined(),
5133 ));
5134 }
5135
5136 for arg in args {
5139 if let AstNode::Variable(var_name) = arg {
5141 if !var_name.is_empty()
5143 && !self.is_builtin_function(var_name)
5144 && self.context.lookup(var_name).is_none()
5145 {
5146 if propagates_undefined(name) {
5148 return Ok(JValue::Null); }
5150 }
5151 }
5152 if let AstNode::Name(field_name) = arg {
5154 let field_exists =
5155 matches!(data, JValue::Object(obj) if obj.contains_key(field_name));
5156 if !field_exists && propagates_undefined(name) {
5157 return Ok(JValue::Null);
5158 }
5159 }
5160 if let AstNode::Path { steps } = arg {
5165 if let Ok(JValue::Null) = self.evaluate_internal(arg, data) {
5172 if steps.len() == 1 {
5175 let field_name = match &steps[0].node {
5177 AstNode::Name(n) => Some(n.as_str()),
5178 AstNode::String(s) => Some(s.as_str()),
5179 _ => None,
5180 };
5181 if let Some(field) = field_name {
5182 match data {
5183 JValue::Object(obj) => {
5184 if !obj.contains_key(field) {
5185 if propagates_undefined(name) {
5187 return Ok(JValue::Null);
5188 }
5189 }
5190 }
5192 JValue::Null => {
5193 if propagates_undefined(name) {
5195 return Ok(JValue::Null);
5196 }
5197 }
5198 _ => {}
5199 }
5200 }
5201 }
5202 else if steps.len() > 1 {
5204 let mut current = data;
5206 let mut failed_due_to_missing_field = false;
5207
5208 for (i, step) in steps.iter().enumerate() {
5209 if let AstNode::Name(field_name) = &step.node {
5210 match current {
5211 JValue::Object(obj) => {
5212 if let Some(val) = obj.get(field_name) {
5213 current = val;
5214 } else {
5215 failed_due_to_missing_field = true;
5217 break;
5218 }
5219 }
5220 JValue::Array(_) => {
5221 break;
5223 }
5224 JValue::Null => {
5225 if i > 0 {
5227 failed_due_to_missing_field = false;
5229 }
5230 break;
5231 }
5232 _ => break,
5233 }
5234 }
5235 }
5236
5237 if failed_due_to_missing_field && propagates_undefined(name) {
5238 return Ok(JValue::Null);
5239 }
5240 }
5241 }
5242 }
5243 }
5244
5245 if args.len() == 1 {
5248 if let Some(result) = self.try_fused_aggregate(name, &args[0], data)? {
5249 return Ok(result);
5250 }
5251 }
5252
5253 let mut evaluated_args = Vec::with_capacity(args.len());
5254 for arg in args {
5255 evaluated_args.push(self.evaluate_internal(arg, data)?);
5256 }
5257
5258 let context_functions_zero_arg = ["string", "number", "boolean", "uppercase", "lowercase"];
5263 let context_functions_missing_first = [
5264 "substringBefore",
5265 "substringAfter",
5266 "contains",
5267 "split",
5268 "replace",
5269 ];
5270
5271 if evaluated_args.is_empty() && context_functions_zero_arg.contains(&name) {
5272 evaluated_args.push(data.clone());
5274 } else if evaluated_args.len() == 1 && context_functions_missing_first.contains(&name) {
5275 if matches!(data, JValue::String(_)) {
5279 evaluated_args.insert(0, data.clone());
5280 }
5281 }
5282
5283 if name == "string"
5286 && args.is_empty()
5287 && !evaluated_args.is_empty()
5288 && evaluated_args[0].is_null()
5289 {
5290 return Ok(JValue::Null);
5292 }
5293
5294 match name {
5295 "string" => {
5296 if evaluated_args.len() > 2 {
5297 return Err(EvaluatorError::EvaluationError(
5298 "string() takes at most 2 arguments".to_string(),
5299 ));
5300 }
5301
5302 let prettify = if evaluated_args.len() == 2 {
5303 match &evaluated_args[1] {
5304 JValue::Bool(b) => Some(*b),
5305 _ => {
5306 return Err(EvaluatorError::TypeError(
5307 "string() prettify parameter must be a boolean".to_string(),
5308 ))
5309 }
5310 }
5311 } else {
5312 None
5313 };
5314
5315 Ok(functions::string::string(&evaluated_args[0], prettify)?)
5316 }
5317 "length" => {
5318 if evaluated_args.len() != 1 {
5319 return Err(EvaluatorError::EvaluationError(
5320 "length() requires exactly 1 argument".to_string(),
5321 ));
5322 }
5323 match &evaluated_args[0] {
5324 JValue::String(s) => Ok(functions::string::length(s)?),
5325 _ => Err(EvaluatorError::TypeError(
5326 "T0410: Argument 1 of function length does not match function signature"
5327 .to_string(),
5328 )),
5329 }
5330 }
5331 "uppercase" => {
5332 if evaluated_args.len() != 1 {
5333 return Err(EvaluatorError::EvaluationError(
5334 "uppercase() requires exactly 1 argument".to_string(),
5335 ));
5336 }
5337 match &evaluated_args[0] {
5338 JValue::String(s) => Ok(functions::string::uppercase(s)?),
5339 _ => Err(EvaluatorError::TypeError(
5340 "T0410: Argument 1 of function uppercase does not match function signature"
5341 .to_string(),
5342 )),
5343 }
5344 }
5345 "lowercase" => {
5346 if evaluated_args.len() != 1 {
5347 return Err(EvaluatorError::EvaluationError(
5348 "lowercase() requires exactly 1 argument".to_string(),
5349 ));
5350 }
5351 match &evaluated_args[0] {
5352 JValue::String(s) => Ok(functions::string::lowercase(s)?),
5353 _ => Err(EvaluatorError::TypeError(
5354 "T0410: Argument 1 of function lowercase does not match function signature"
5355 .to_string(),
5356 )),
5357 }
5358 }
5359 "number" => {
5360 if evaluated_args.is_empty() {
5361 return Err(EvaluatorError::EvaluationError(
5362 "number() requires at least 1 argument".to_string(),
5363 ));
5364 }
5365 if evaluated_args.len() > 1 {
5366 return Err(EvaluatorError::TypeError(
5367 "T0410: Argument 2 of function number does not match function signature"
5368 .to_string(),
5369 ));
5370 }
5371 Ok(functions::numeric::number(&evaluated_args[0])?)
5372 }
5373 "sum" => {
5374 if evaluated_args.len() != 1 {
5375 return Err(EvaluatorError::EvaluationError(
5376 "sum() requires exactly 1 argument".to_string(),
5377 ));
5378 }
5379 if evaluated_args[0].is_undefined() {
5381 return Ok(JValue::Undefined);
5382 }
5383 match &evaluated_args[0] {
5384 JValue::Null => Ok(JValue::Null),
5385 JValue::Array(arr) => {
5386 Ok(aggregation::sum(arr)?)
5388 }
5389 JValue::Number(n) => Ok(JValue::Number(*n)),
5391 other => Ok(functions::numeric::sum(&[other.clone()])?),
5392 }
5393 }
5394 "count" => {
5395 if evaluated_args.len() != 1 {
5396 return Err(EvaluatorError::EvaluationError(
5397 "count() requires exactly 1 argument".to_string(),
5398 ));
5399 }
5400 if evaluated_args[0].is_undefined() {
5402 return Ok(JValue::from(0i64));
5403 }
5404 match &evaluated_args[0] {
5405 JValue::Null => Ok(JValue::from(0i64)), JValue::Array(arr) => Ok(functions::array::count(arr)?),
5407 _ => Ok(JValue::from(1i64)), }
5409 }
5410 "substring" => {
5411 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5412 return Err(EvaluatorError::EvaluationError(
5413 "substring() requires 2 or 3 arguments".to_string(),
5414 ));
5415 }
5416 match (&evaluated_args[0], &evaluated_args[1]) {
5417 (JValue::String(s), JValue::Number(start)) => {
5418 let length = if evaluated_args.len() == 3 {
5419 match &evaluated_args[2] {
5420 JValue::Number(l) => Some(*l as i64),
5421 _ => return Err(EvaluatorError::TypeError(
5422 "T0410: Argument 3 of function substring does not match function signature".to_string(),
5423 )),
5424 }
5425 } else {
5426 None
5427 };
5428 Ok(functions::string::substring(s, *start as i64, length)?)
5429 }
5430 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5431 "T0410: Argument 2 of function substring does not match function signature"
5432 .to_string(),
5433 )),
5434 _ => Err(EvaluatorError::TypeError(
5435 "T0410: Argument 1 of function substring does not match function signature"
5436 .to_string(),
5437 )),
5438 }
5439 }
5440 "substringBefore" => {
5441 if evaluated_args.len() != 2 {
5442 return Err(EvaluatorError::TypeError(
5443 "T0411: Context value is not a compatible type with argument 2 of function substringBefore".to_string(),
5444 ));
5445 }
5446 match (&evaluated_args[0], &evaluated_args[1]) {
5447 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_before(s, sep)?),
5448 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5449 "T0410: Argument 2 of function substringBefore does not match function signature".to_string(),
5450 )),
5451 _ => Err(EvaluatorError::TypeError(
5452 "T0410: Argument 1 of function substringBefore does not match function signature".to_string(),
5453 )),
5454 }
5455 }
5456 "substringAfter" => {
5457 if evaluated_args.len() != 2 {
5458 return Err(EvaluatorError::TypeError(
5459 "T0411: Context value is not a compatible type with argument 2 of function substringAfter".to_string(),
5460 ));
5461 }
5462 match (&evaluated_args[0], &evaluated_args[1]) {
5463 (JValue::String(s), JValue::String(sep)) => Ok(functions::string::substring_after(s, sep)?),
5464 (JValue::String(_), _) => Err(EvaluatorError::TypeError(
5465 "T0410: Argument 2 of function substringAfter does not match function signature".to_string(),
5466 )),
5467 _ => Err(EvaluatorError::TypeError(
5468 "T0410: Argument 1 of function substringAfter does not match function signature".to_string(),
5469 )),
5470 }
5471 }
5472 "pad" => {
5473 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
5474 return Err(EvaluatorError::EvaluationError(
5475 "pad() requires 2 or 3 arguments".to_string(),
5476 ));
5477 }
5478
5479 let string = match &evaluated_args[0] {
5481 JValue::String(s) => s.clone(),
5482 JValue::Null => return Ok(JValue::Null),
5483 _ => {
5484 return Err(EvaluatorError::TypeError(
5485 "pad() first argument must be a string".to_string(),
5486 ))
5487 }
5488 };
5489
5490 let width = match &evaluated_args.get(1) {
5492 Some(JValue::Number(n)) => *n as i32,
5493 _ => {
5494 return Err(EvaluatorError::TypeError(
5495 "pad() second argument must be a number".to_string(),
5496 ))
5497 }
5498 };
5499
5500 let pad_string = match evaluated_args.get(2) {
5502 Some(JValue::String(s)) if !s.is_empty() => s.clone(),
5503 _ => Rc::from(" "),
5504 };
5505
5506 let abs_width = width.unsigned_abs() as usize;
5507 let char_count = string.chars().count();
5509
5510 if char_count >= abs_width {
5511 return Ok(JValue::string(string));
5513 }
5514
5515 let padding_needed = abs_width - char_count;
5516
5517 let pad_chars: Vec<char> = pad_string.chars().collect();
5518 let mut padding = String::with_capacity(padding_needed);
5519 for i in 0..padding_needed {
5520 padding.push(pad_chars[i % pad_chars.len()]);
5521 }
5522
5523 let result = if width < 0 {
5524 format!("{}{}", padding, string)
5526 } else {
5527 format!("{}{}", string, padding)
5529 };
5530
5531 Ok(JValue::string(result))
5532 }
5533
5534 "trim" => {
5535 if evaluated_args.is_empty() {
5536 return Ok(JValue::Null); }
5538 if evaluated_args.len() != 1 {
5539 return Err(EvaluatorError::EvaluationError(
5540 "trim() requires at most 1 argument".to_string(),
5541 ));
5542 }
5543 match &evaluated_args[0] {
5544 JValue::Null => Ok(JValue::Null),
5545 JValue::String(s) => Ok(functions::string::trim(s)?),
5546 _ => Err(EvaluatorError::TypeError(
5547 "trim() requires a string argument".to_string(),
5548 )),
5549 }
5550 }
5551 "contains" => {
5552 if evaluated_args.len() != 2 {
5553 return Err(EvaluatorError::EvaluationError(
5554 "contains() requires exactly 2 arguments".to_string(),
5555 ));
5556 }
5557 if evaluated_args[0].is_null() {
5558 return Ok(JValue::Null);
5559 }
5560 match &evaluated_args[0] {
5561 JValue::String(s) => Ok(functions::string::contains(s, &evaluated_args[1])?),
5562 _ => Err(EvaluatorError::TypeError(
5563 "contains() requires a string as the first argument".to_string(),
5564 )),
5565 }
5566 }
5567 "split" => {
5568 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
5569 return Err(EvaluatorError::EvaluationError(
5570 "split() requires 2 or 3 arguments".to_string(),
5571 ));
5572 }
5573 if evaluated_args[0].is_null() {
5574 return Ok(JValue::Null);
5575 }
5576 match &evaluated_args[0] {
5577 JValue::String(s) => {
5578 let limit = if evaluated_args.len() == 3 {
5579 match &evaluated_args[2] {
5580 JValue::Number(n) => {
5581 let f = *n;
5582 if f < 0.0 {
5584 return Err(EvaluatorError::EvaluationError(
5585 "D3020: Third argument of split function must be a positive number".to_string(),
5586 ));
5587 }
5588 Some(f.floor() as usize)
5590 }
5591 _ => {
5592 return Err(EvaluatorError::TypeError(
5593 "split() limit must be a number".to_string(),
5594 ))
5595 }
5596 }
5597 } else {
5598 None
5599 };
5600 Ok(functions::string::split(s, &evaluated_args[1], limit)?)
5601 }
5602 _ => Err(EvaluatorError::TypeError(
5603 "split() requires a string as the first argument".to_string(),
5604 )),
5605 }
5606 }
5607 "join" => {
5608 if evaluated_args.is_empty() {
5611 return Err(EvaluatorError::TypeError(
5612 "T0410: Argument 1 of function $join does not match function signature"
5613 .to_string(),
5614 ));
5615 }
5616 if evaluated_args[0].is_null() {
5617 return Ok(JValue::Null);
5618 }
5619
5620 use crate::signature::Signature;
5623
5624 let signature = Signature::parse("<a<s>s?:s>").map_err(|e| {
5625 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
5626 })?;
5627
5628 let coerced_args = match signature.validate_and_coerce(&evaluated_args) {
5629 Ok(args) => args,
5630 Err(crate::signature::SignatureError::UndefinedArgument) => {
5631 let sig_first_arg = Signature::parse("<a<s>:a<s>>").map_err(|e| {
5634 EvaluatorError::EvaluationError(format!("Invalid signature: {}", e))
5635 })?;
5636
5637 match sig_first_arg.validate_and_coerce(&evaluated_args[0..1]) {
5638 Ok(args) => args,
5639 Err(crate::signature::SignatureError::ArrayTypeMismatch {
5640 index,
5641 expected,
5642 }) => {
5643 return Err(EvaluatorError::TypeError(format!(
5644 "T0412: Argument {} of function $join must be an array of {}",
5645 index, expected
5646 )));
5647 }
5648 Err(e) => {
5649 return Err(EvaluatorError::TypeError(format!(
5650 "Signature validation failed: {}",
5651 e
5652 )));
5653 }
5654 }
5655 }
5656 Err(crate::signature::SignatureError::ArgumentTypeMismatch {
5657 index,
5658 expected,
5659 }) => {
5660 return Err(EvaluatorError::TypeError(
5661 format!("T0410: Argument {} of function $join does not match function signature (expected {})", index, expected)
5662 ));
5663 }
5664 Err(crate::signature::SignatureError::ArrayTypeMismatch {
5665 index,
5666 expected,
5667 }) => {
5668 return Err(EvaluatorError::TypeError(format!(
5669 "T0412: Argument {} of function $join must be an array of {}",
5670 index, expected
5671 )));
5672 }
5673 Err(e) => {
5674 return Err(EvaluatorError::TypeError(format!(
5675 "Signature validation failed: {}",
5676 e
5677 )));
5678 }
5679 };
5680
5681 match &coerced_args[0] {
5683 JValue::Array(arr) => {
5684 let separator = if coerced_args.len() == 2 {
5685 match &coerced_args[1] {
5686 JValue::String(s) => Some(&**s),
5687 JValue::Null => None, _ => None, }
5690 } else {
5691 None };
5693 Ok(functions::string::join(arr, separator)?)
5694 }
5695 JValue::Null => Ok(JValue::Null),
5696 _ => unreachable!("Signature validation should ensure array type"),
5697 }
5698 }
5699 "replace" => {
5700 if evaluated_args.len() < 3 || evaluated_args.len() > 4 {
5701 return Err(EvaluatorError::EvaluationError(
5702 "replace() requires 3 or 4 arguments".to_string(),
5703 ));
5704 }
5705 if evaluated_args[0].is_null() {
5706 return Ok(JValue::Null);
5707 }
5708
5709 let replacement_is_lambda = matches!(
5711 evaluated_args[2],
5712 JValue::Lambda { .. } | JValue::Builtin { .. }
5713 );
5714
5715 if replacement_is_lambda {
5716 return self.replace_with_lambda(
5718 &evaluated_args[0],
5719 &evaluated_args[1],
5720 &evaluated_args[2],
5721 if evaluated_args.len() == 4 {
5722 Some(&evaluated_args[3])
5723 } else {
5724 None
5725 },
5726 data,
5727 );
5728 }
5729
5730 match (&evaluated_args[0], &evaluated_args[2]) {
5732 (JValue::String(s), JValue::String(replacement)) => {
5733 let limit = if evaluated_args.len() == 4 {
5734 match &evaluated_args[3] {
5735 JValue::Number(n) => {
5736 let lim_f64 = *n;
5737 if lim_f64 < 0.0 {
5738 return Err(EvaluatorError::EvaluationError(format!(
5739 "D3011: Limit must be non-negative, got {}",
5740 lim_f64
5741 )));
5742 }
5743 Some(lim_f64 as usize)
5744 }
5745 _ => {
5746 return Err(EvaluatorError::TypeError(
5747 "replace() limit must be a number".to_string(),
5748 ))
5749 }
5750 }
5751 } else {
5752 None
5753 };
5754 Ok(functions::string::replace(
5755 s,
5756 &evaluated_args[1],
5757 replacement,
5758 limit,
5759 )?)
5760 }
5761 _ => Err(EvaluatorError::TypeError(
5762 "replace() requires string arguments".to_string(),
5763 )),
5764 }
5765 }
5766 "match" => {
5767 if evaluated_args.is_empty() || evaluated_args.len() > 3 {
5770 return Err(EvaluatorError::EvaluationError(
5771 "match() requires 1 to 3 arguments".to_string(),
5772 ));
5773 }
5774 if evaluated_args[0].is_null() {
5775 return Ok(JValue::Null);
5776 }
5777
5778 let s = match &evaluated_args[0] {
5779 JValue::String(s) => s.clone(),
5780 _ => {
5781 return Err(EvaluatorError::TypeError(
5782 "match() first argument must be a string".to_string(),
5783 ))
5784 }
5785 };
5786
5787 let limit = if evaluated_args.len() == 3 {
5789 match &evaluated_args[2] {
5790 JValue::Number(n) => Some(*n as usize),
5791 JValue::Null => None,
5792 _ => {
5793 return Err(EvaluatorError::TypeError(
5794 "match() limit must be a number".to_string(),
5795 ))
5796 }
5797 }
5798 } else {
5799 None
5800 };
5801
5802 let pattern_value = evaluated_args.get(1);
5804 let is_custom_matcher = pattern_value.is_some_and(|val| {
5805 matches!(val, JValue::Lambda { .. } | JValue::Builtin { .. })
5806 });
5807
5808 if is_custom_matcher {
5809 return self.match_with_custom_matcher(&s, &args[1], limit, data);
5812 }
5813
5814 let (pattern, flags) = match pattern_value {
5816 Some(val) => crate::functions::string::extract_regex(val).ok_or_else(|| {
5817 EvaluatorError::TypeError(
5818 "match() second argument must be a regex pattern or matcher function"
5819 .to_string(),
5820 )
5821 })?,
5822 None => (".*".to_string(), "".to_string()),
5823 };
5824
5825 let is_global = flags.contains('g');
5827 let regex_pattern = if flags.contains('i') {
5828 format!("(?i){}", pattern)
5829 } else {
5830 pattern.clone()
5831 };
5832
5833 let re = regex::Regex::new(®ex_pattern).map_err(|e| {
5834 EvaluatorError::EvaluationError(format!("Invalid regex pattern: {}", e))
5835 })?;
5836
5837 let mut results = Vec::new();
5838 let mut count = 0;
5839
5840 for caps in re.captures_iter(&s) {
5841 if let Some(lim) = limit {
5842 if count >= lim {
5843 break;
5844 }
5845 }
5846
5847 let full_match = caps.get(0).unwrap();
5848 let mut match_obj = IndexMap::new();
5849 match_obj.insert(
5850 "match".to_string(),
5851 JValue::string(full_match.as_str().to_string()),
5852 );
5853 match_obj.insert(
5854 "index".to_string(),
5855 JValue::Number(full_match.start() as f64),
5856 );
5857
5858 let mut groups: Vec<JValue> = Vec::new();
5860 for i in 1..caps.len() {
5861 if let Some(group) = caps.get(i) {
5862 groups.push(JValue::string(group.as_str().to_string()));
5863 } else {
5864 groups.push(JValue::Null);
5865 }
5866 }
5867 if !groups.is_empty() {
5868 match_obj.insert("groups".to_string(), JValue::array(groups));
5869 }
5870
5871 results.push(JValue::object(match_obj));
5872 count += 1;
5873
5874 if !is_global {
5876 break;
5877 }
5878 }
5879
5880 if results.is_empty() {
5881 Ok(JValue::Null)
5882 } else if results.len() == 1 && !is_global {
5883 Ok(results.into_iter().next().unwrap())
5885 } else {
5886 Ok(JValue::array(results))
5887 }
5888 }
5889 "max" => {
5890 if evaluated_args.len() != 1 {
5891 return Err(EvaluatorError::EvaluationError(
5892 "max() requires exactly 1 argument".to_string(),
5893 ));
5894 }
5895 if evaluated_args[0].is_undefined() {
5897 return Ok(JValue::Undefined);
5898 }
5899 match &evaluated_args[0] {
5900 JValue::Null => Ok(JValue::Null),
5901 JValue::Array(arr) => {
5902 Ok(aggregation::max(arr)?)
5904 }
5905 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
5907 "max() requires an array or number argument".to_string(),
5908 )),
5909 }
5910 }
5911 "min" => {
5912 if evaluated_args.len() != 1 {
5913 return Err(EvaluatorError::EvaluationError(
5914 "min() requires exactly 1 argument".to_string(),
5915 ));
5916 }
5917 if evaluated_args[0].is_undefined() {
5919 return Ok(JValue::Undefined);
5920 }
5921 match &evaluated_args[0] {
5922 JValue::Null => Ok(JValue::Null),
5923 JValue::Array(arr) => {
5924 Ok(aggregation::min(arr)?)
5926 }
5927 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
5929 "min() requires an array or number argument".to_string(),
5930 )),
5931 }
5932 }
5933 "average" => {
5934 if evaluated_args.len() != 1 {
5935 return Err(EvaluatorError::EvaluationError(
5936 "average() requires exactly 1 argument".to_string(),
5937 ));
5938 }
5939 if evaluated_args[0].is_undefined() {
5941 return Ok(JValue::Undefined);
5942 }
5943 match &evaluated_args[0] {
5944 JValue::Null => Ok(JValue::Null),
5945 JValue::Array(arr) => {
5946 Ok(aggregation::average(arr)?)
5948 }
5949 JValue::Number(_) => Ok(evaluated_args[0].clone()), _ => Err(EvaluatorError::TypeError(
5951 "average() requires an array or number argument".to_string(),
5952 )),
5953 }
5954 }
5955 "abs" => {
5956 if evaluated_args.len() != 1 {
5957 return Err(EvaluatorError::EvaluationError(
5958 "abs() requires exactly 1 argument".to_string(),
5959 ));
5960 }
5961 match &evaluated_args[0] {
5962 JValue::Null => Ok(JValue::Null),
5963 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
5964 _ => Err(EvaluatorError::TypeError(
5965 "abs() requires a number argument".to_string(),
5966 )),
5967 }
5968 }
5969 "floor" => {
5970 if evaluated_args.len() != 1 {
5971 return Err(EvaluatorError::EvaluationError(
5972 "floor() requires exactly 1 argument".to_string(),
5973 ));
5974 }
5975 match &evaluated_args[0] {
5976 JValue::Null => Ok(JValue::Null),
5977 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
5978 _ => Err(EvaluatorError::TypeError(
5979 "floor() requires a number argument".to_string(),
5980 )),
5981 }
5982 }
5983 "ceil" => {
5984 if evaluated_args.len() != 1 {
5985 return Err(EvaluatorError::EvaluationError(
5986 "ceil() requires exactly 1 argument".to_string(),
5987 ));
5988 }
5989 match &evaluated_args[0] {
5990 JValue::Null => Ok(JValue::Null),
5991 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
5992 _ => Err(EvaluatorError::TypeError(
5993 "ceil() requires a number argument".to_string(),
5994 )),
5995 }
5996 }
5997 "round" => {
5998 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
5999 return Err(EvaluatorError::EvaluationError(
6000 "round() requires 1 or 2 arguments".to_string(),
6001 ));
6002 }
6003 match &evaluated_args[0] {
6004 JValue::Null => Ok(JValue::Null),
6005 JValue::Number(n) => {
6006 let precision = if evaluated_args.len() == 2 {
6007 match &evaluated_args[1] {
6008 JValue::Number(p) => Some(*p as i32),
6009 _ => {
6010 return Err(EvaluatorError::TypeError(
6011 "round() precision must be a number".to_string(),
6012 ))
6013 }
6014 }
6015 } else {
6016 None
6017 };
6018 Ok(functions::numeric::round(*n, precision)?)
6019 }
6020 _ => Err(EvaluatorError::TypeError(
6021 "round() requires a number argument".to_string(),
6022 )),
6023 }
6024 }
6025 "sqrt" => {
6026 if evaluated_args.len() != 1 {
6027 return Err(EvaluatorError::EvaluationError(
6028 "sqrt() requires exactly 1 argument".to_string(),
6029 ));
6030 }
6031 match &evaluated_args[0] {
6032 JValue::Null => Ok(JValue::Null),
6033 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
6034 _ => Err(EvaluatorError::TypeError(
6035 "sqrt() requires a number argument".to_string(),
6036 )),
6037 }
6038 }
6039 "power" => {
6040 if evaluated_args.len() != 2 {
6041 return Err(EvaluatorError::EvaluationError(
6042 "power() requires exactly 2 arguments".to_string(),
6043 ));
6044 }
6045 if evaluated_args[0].is_null() {
6046 return Ok(JValue::Null);
6047 }
6048 match (&evaluated_args[0], &evaluated_args[1]) {
6049 (JValue::Number(base), JValue::Number(exp)) => {
6050 Ok(functions::numeric::power(*base, *exp)?)
6051 }
6052 _ => Err(EvaluatorError::TypeError(
6053 "power() requires number arguments".to_string(),
6054 )),
6055 }
6056 }
6057 "formatNumber" => {
6058 if evaluated_args.len() < 2 || evaluated_args.len() > 3 {
6059 return Err(EvaluatorError::EvaluationError(
6060 "formatNumber() requires 2 or 3 arguments".to_string(),
6061 ));
6062 }
6063 if evaluated_args[0].is_null() {
6064 return Ok(JValue::Null);
6065 }
6066 match (&evaluated_args[0], &evaluated_args[1]) {
6067 (JValue::Number(num), JValue::String(picture)) => {
6068 let options = if evaluated_args.len() == 3 {
6069 Some(&evaluated_args[2])
6070 } else {
6071 None
6072 };
6073 Ok(functions::numeric::format_number(*num, picture, options)?)
6074 }
6075 _ => Err(EvaluatorError::TypeError(
6076 "formatNumber() requires a number and a string".to_string(),
6077 )),
6078 }
6079 }
6080 "formatBase" => {
6081 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6082 return Err(EvaluatorError::EvaluationError(
6083 "formatBase() requires 1 or 2 arguments".to_string(),
6084 ));
6085 }
6086 if evaluated_args[0].is_null() {
6088 return Ok(JValue::Null);
6089 }
6090 match &evaluated_args[0] {
6091 JValue::Number(num) => {
6092 let radix = if evaluated_args.len() == 2 {
6093 match &evaluated_args[1] {
6094 JValue::Number(r) => Some(r.trunc() as i64),
6095 _ => {
6096 return Err(EvaluatorError::TypeError(
6097 "formatBase() radix must be a number".to_string(),
6098 ))
6099 }
6100 }
6101 } else {
6102 None
6103 };
6104 Ok(functions::numeric::format_base(*num, radix)?)
6105 }
6106 _ => Err(EvaluatorError::TypeError(
6107 "formatBase() requires a number".to_string(),
6108 )),
6109 }
6110 }
6111 "append" => {
6112 if evaluated_args.len() != 2 {
6113 return Err(EvaluatorError::EvaluationError(
6114 "append() requires exactly 2 arguments".to_string(),
6115 ));
6116 }
6117 let first = &evaluated_args[0];
6119 let second = &evaluated_args[1];
6120
6121 if second.is_null() {
6123 return Ok(first.clone());
6124 }
6125
6126 if first.is_null() {
6128 return Ok(second.clone());
6129 }
6130
6131 let arr = match first {
6133 JValue::Array(a) => a.to_vec(),
6134 other => vec![other.clone()], };
6136
6137 Ok(functions::array::append(&arr, second)?)
6138 }
6139 "reverse" => {
6140 if evaluated_args.len() != 1 {
6141 return Err(EvaluatorError::EvaluationError(
6142 "reverse() requires exactly 1 argument".to_string(),
6143 ));
6144 }
6145 match &evaluated_args[0] {
6146 JValue::Null => Ok(JValue::Null), JValue::Array(arr) => Ok(functions::array::reverse(arr)?),
6148 _ => Err(EvaluatorError::TypeError(
6149 "reverse() requires an array argument".to_string(),
6150 )),
6151 }
6152 }
6153 "shuffle" => {
6154 if evaluated_args.len() != 1 {
6155 return Err(EvaluatorError::EvaluationError(
6156 "shuffle() requires exactly 1 argument".to_string(),
6157 ));
6158 }
6159 if evaluated_args[0].is_null() {
6160 return Ok(JValue::Null);
6161 }
6162 match &evaluated_args[0] {
6163 JValue::Array(arr) => Ok(functions::array::shuffle(arr)?),
6164 _ => Err(EvaluatorError::TypeError(
6165 "shuffle() requires an array argument".to_string(),
6166 )),
6167 }
6168 }
6169
6170 "sift" => {
6171 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6173 return Err(EvaluatorError::EvaluationError(
6174 "sift() requires 1 or 2 arguments".to_string(),
6175 ));
6176 }
6177
6178 let func_arg = if evaluated_args.len() == 1 {
6180 &args[0]
6181 } else {
6182 &args[1]
6183 };
6184
6185 let param_count = self.get_callback_param_count(func_arg);
6187
6188 let sift_object = |evaluator: &mut Self,
6190 obj: &IndexMap<String, JValue>,
6191 func_node: &AstNode,
6192 context_data: &JValue,
6193 param_count: usize|
6194 -> Result<JValue, EvaluatorError> {
6195 let obj_value = if param_count >= 3 {
6197 Some(JValue::object(obj.clone()))
6198 } else {
6199 None
6200 };
6201
6202 let mut result = IndexMap::new();
6203 for (key, value) in obj.iter() {
6204 let call_args = match param_count {
6206 1 => vec![value.clone()],
6207 2 => vec![value.clone(), JValue::string(key.clone())],
6208 _ => vec![
6209 value.clone(),
6210 JValue::string(key.clone()),
6211 obj_value.as_ref().unwrap().clone(),
6212 ],
6213 };
6214
6215 let pred_result =
6216 evaluator.apply_function(func_node, &call_args, context_data)?;
6217 if evaluator.is_truthy(&pred_result) {
6218 result.insert(key.clone(), value.clone());
6219 }
6220 }
6221 if result.is_empty() {
6223 Ok(JValue::Undefined)
6224 } else {
6225 Ok(JValue::object(result))
6226 }
6227 };
6228
6229 if evaluated_args.len() == 1 {
6231 match data {
6233 JValue::Object(o) => sift_object(self, o, &args[0], data, param_count),
6234 JValue::Array(arr) => {
6235 let mut results = Vec::new();
6237 for item in arr.iter() {
6238 if let JValue::Object(o) = item {
6239 let sifted = sift_object(self, o, &args[0], item, param_count)?;
6240 if !sifted.is_undefined() {
6242 results.push(sifted);
6243 }
6244 }
6245 }
6246 Ok(JValue::array(results))
6247 }
6248 JValue::Null => Ok(JValue::Null),
6249 _ => Ok(JValue::Undefined),
6250 }
6251 } else {
6252 match &evaluated_args[0] {
6254 JValue::Object(o) => sift_object(self, o, &args[1], data, param_count),
6255 JValue::Null => Ok(JValue::Null),
6256 _ => Err(EvaluatorError::TypeError(
6257 "sift() first argument must be an object".to_string(),
6258 )),
6259 }
6260 }
6261 }
6262
6263 "zip" => {
6264 if evaluated_args.is_empty() {
6265 return Err(EvaluatorError::EvaluationError(
6266 "zip() requires at least 1 argument".to_string(),
6267 ));
6268 }
6269
6270 let mut arrays: Vec<Vec<JValue>> = Vec::with_capacity(evaluated_args.len());
6273 for arg in &evaluated_args {
6274 match arg {
6275 JValue::Array(arr) => {
6276 if arr.is_empty() {
6277 return Ok(JValue::array(vec![]));
6279 }
6280 arrays.push(arr.to_vec());
6281 }
6282 JValue::Null => {
6283 return Ok(JValue::array(vec![]));
6285 }
6286 other => {
6287 arrays.push(vec![other.clone()]);
6289 }
6290 }
6291 }
6292
6293 if arrays.is_empty() {
6294 return Ok(JValue::array(vec![]));
6295 }
6296
6297 let min_len = arrays.iter().map(|a| a.len()).min().unwrap_or(0);
6299
6300 let mut result = Vec::with_capacity(min_len);
6302 for i in 0..min_len {
6303 let mut tuple = Vec::with_capacity(arrays.len());
6304 for array in &arrays {
6305 tuple.push(array[i].clone());
6306 }
6307 result.push(JValue::array(tuple));
6308 }
6309
6310 Ok(JValue::array(result))
6311 }
6312
6313 "sort" => {
6314 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
6315 return Err(EvaluatorError::EvaluationError(
6316 "sort() requires 1 or 2 arguments".to_string(),
6317 ));
6318 }
6319
6320 let array_value = &evaluated_args[0];
6322
6323 if array_value.is_null() {
6325 return Ok(JValue::Null);
6326 }
6327
6328 let mut arr = match array_value {
6329 JValue::Array(arr) => arr.to_vec(),
6330 other => vec![other.clone()],
6331 };
6332
6333 if args.len() == 2 {
6334 self.merge_sort_with_comparator(&mut arr, &args[1], data)?;
6337 Ok(JValue::array(arr))
6338 } else {
6339 Ok(functions::array::sort(&arr)?)
6341 }
6342 }
6343 "distinct" => {
6344 if evaluated_args.len() != 1 {
6345 return Err(EvaluatorError::EvaluationError(
6346 "distinct() requires exactly 1 argument".to_string(),
6347 ));
6348 }
6349 match &evaluated_args[0] {
6350 JValue::Array(arr) => Ok(functions::array::distinct(arr)?),
6351 _ => Err(EvaluatorError::TypeError(
6352 "distinct() requires an array argument".to_string(),
6353 )),
6354 }
6355 }
6356 "exists" => {
6357 if evaluated_args.len() != 1 {
6358 return Err(EvaluatorError::EvaluationError(
6359 "exists() requires exactly 1 argument".to_string(),
6360 ));
6361 }
6362 Ok(functions::array::exists(&evaluated_args[0])?)
6363 }
6364 "keys" => {
6365 if evaluated_args.len() != 1 {
6366 return Err(EvaluatorError::EvaluationError(
6367 "keys() requires exactly 1 argument".to_string(),
6368 ));
6369 }
6370
6371 let unwrap_single = |keys: Vec<JValue>| -> JValue {
6373 if keys.len() == 1 {
6374 keys.into_iter().next().unwrap()
6375 } else {
6376 JValue::array(keys)
6377 }
6378 };
6379
6380 match &evaluated_args[0] {
6381 JValue::Null => Ok(JValue::Null),
6382 JValue::Lambda { .. } | JValue::Builtin { .. } => Ok(JValue::Null),
6383 JValue::Object(obj) => {
6384 if obj.is_empty() {
6386 Ok(JValue::Null)
6387 } else {
6388 let keys: Vec<JValue> =
6389 obj.keys().map(|k| JValue::string(k.clone())).collect();
6390 Ok(unwrap_single(keys))
6391 }
6392 }
6393 JValue::Array(arr) => {
6394 let mut all_keys = Vec::new();
6396 for item in arr.iter() {
6397 if matches!(item, JValue::Lambda { .. } | JValue::Builtin { .. }) {
6399 continue;
6400 }
6401 if let JValue::Object(obj) = item {
6402 for key in obj.keys() {
6403 if !all_keys.contains(&JValue::string(key.clone())) {
6404 all_keys.push(JValue::string(key.clone()));
6405 }
6406 }
6407 }
6408 }
6409 if all_keys.is_empty() {
6410 Ok(JValue::Null)
6411 } else {
6412 Ok(unwrap_single(all_keys))
6413 }
6414 }
6415 _ => Ok(JValue::Null),
6417 }
6418 }
6419 "lookup" => {
6420 if evaluated_args.len() != 2 {
6421 return Err(EvaluatorError::EvaluationError(
6422 "lookup() requires exactly 2 arguments".to_string(),
6423 ));
6424 }
6425 if evaluated_args[0].is_null() {
6426 return Ok(JValue::Null);
6427 }
6428
6429 let key = match &evaluated_args[1] {
6430 JValue::String(k) => &**k,
6431 _ => {
6432 return Err(EvaluatorError::TypeError(
6433 "lookup() requires a string key".to_string(),
6434 ))
6435 }
6436 };
6437
6438 fn lookup_recursive(val: &JValue, key: &str) -> Vec<JValue> {
6440 match val {
6441 JValue::Array(arr) => {
6442 let mut results = Vec::new();
6443 for item in arr.iter() {
6444 let nested = lookup_recursive(item, key);
6445 results.extend(nested.iter().cloned());
6446 }
6447 results
6448 }
6449 JValue::Object(obj) => {
6450 if let Some(v) = obj.get(key) {
6451 vec![v.clone()]
6452 } else {
6453 vec![]
6454 }
6455 }
6456 _ => vec![],
6457 }
6458 }
6459
6460 let results = lookup_recursive(&evaluated_args[0], key);
6461 if results.is_empty() {
6462 Ok(JValue::Null)
6463 } else if results.len() == 1 {
6464 Ok(results[0].clone())
6465 } else {
6466 Ok(JValue::array(results))
6467 }
6468 }
6469 "spread" => {
6470 if evaluated_args.len() != 1 {
6471 return Err(EvaluatorError::EvaluationError(
6472 "spread() requires exactly 1 argument".to_string(),
6473 ));
6474 }
6475 match &evaluated_args[0] {
6476 JValue::Null => Ok(JValue::Null),
6477 JValue::Lambda { .. } | JValue::Builtin { .. } => Ok(JValue::Undefined),
6478 JValue::Object(obj) => Ok(functions::object::spread(obj)?),
6479 JValue::Array(arr) => {
6480 let mut result = Vec::new();
6482 for item in arr.iter() {
6483 match item {
6484 JValue::Lambda { .. } | JValue::Builtin { .. } => {
6485 continue;
6487 }
6488 JValue::Object(obj) => {
6489 let spread_result = functions::object::spread(obj)?;
6490 if let JValue::Array(spread_items) = spread_result {
6491 result.extend(spread_items.iter().cloned());
6492 } else {
6493 result.push(spread_result);
6494 }
6495 }
6496 other => result.push(other.clone()),
6498 }
6499 }
6500 Ok(JValue::array(result))
6501 }
6502 other => Ok(other.clone()),
6504 }
6505 }
6506 "merge" => {
6507 if evaluated_args.is_empty() {
6508 return Err(EvaluatorError::EvaluationError(
6509 "merge() requires at least 1 argument".to_string(),
6510 ));
6511 }
6512 if evaluated_args.len() == 1 {
6515 match &evaluated_args[0] {
6516 JValue::Array(arr) => Ok(functions::object::merge(arr)?),
6517 JValue::Null => Ok(JValue::Null), JValue::Object(_) => {
6519 Ok(evaluated_args[0].clone())
6521 }
6522 _ => Err(EvaluatorError::TypeError(
6523 "merge() requires objects or an array of objects".to_string(),
6524 )),
6525 }
6526 } else {
6527 Ok(functions::object::merge(&evaluated_args)?)
6528 }
6529 }
6530
6531 "map" => {
6532 if args.len() != 2 {
6533 return Err(EvaluatorError::EvaluationError(
6534 "map() requires exactly 2 arguments".to_string(),
6535 ));
6536 }
6537
6538 let array = self.evaluate_internal(&args[0], data)?;
6540
6541 match array {
6542 JValue::Array(arr) => {
6543 let param_count = self.get_callback_param_count(&args[1]);
6545
6546 if param_count == 1 {
6548 if let AstNode::Lambda {
6549 params, body, signature: None, thunk: false,
6550 } = &args[1]
6551 {
6552 let var_refs: Vec<&str> =
6553 params.iter().map(|s| s.as_str()).collect();
6554 if let Some(compiled) =
6555 try_compile_expr_with_allowed_vars(body, &var_refs)
6556 {
6557 let param_name = params[0].as_str();
6558 let mut result = Vec::with_capacity(arr.len());
6559 let mut vars = HashMap::new();
6560 for item in arr.iter() {
6561 vars.insert(param_name, item);
6562 let mapped = eval_compiled(&compiled, data, Some(&vars))?;
6563 if !mapped.is_undefined() {
6564 result.push(mapped);
6565 }
6566 }
6567 return Ok(JValue::array(result));
6568 }
6569 }
6570 if let AstNode::Variable(var_name) = &args[1] {
6572 if let Some(stored) = self.context.lookup_lambda(var_name) {
6573 if let Some(ref ce) = stored.compiled_body.clone() {
6574 let param_name = stored.params[0].clone();
6575 let captured_data = stored.captured_data.clone();
6576 let captured_env_clone = stored.captured_env.clone();
6577 let ce_clone = ce.clone();
6578 if !captured_env_clone
6579 .values()
6580 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
6581 {
6582 let call_data =
6583 captured_data.as_ref().unwrap_or(data);
6584 let mut result = Vec::with_capacity(arr.len());
6585 let mut vars: HashMap<&str, &JValue> =
6586 captured_env_clone
6587 .iter()
6588 .map(|(k, v)| (k.as_str(), v))
6589 .collect();
6590 for item in arr.iter() {
6591 vars.insert(param_name.as_str(), item);
6592 let mapped = eval_compiled(
6593 &ce_clone,
6594 call_data,
6595 Some(&vars),
6596 )?;
6597 if !mapped.is_undefined() {
6598 result.push(mapped);
6599 }
6600 }
6601 return Ok(JValue::array(result));
6602 }
6603 }
6604 }
6605 }
6606 }
6607
6608 let arr_value = if param_count >= 3 {
6610 Some(JValue::Array(arr.clone()))
6611 } else {
6612 None
6613 };
6614
6615 let mut result = Vec::with_capacity(arr.len());
6616 for (index, item) in arr.iter().enumerate() {
6617 let call_args = match param_count {
6619 1 => vec![item.clone()],
6620 2 => vec![item.clone(), JValue::Number(index as f64)],
6621 _ => vec![
6622 item.clone(),
6623 JValue::Number(index as f64),
6624 arr_value.as_ref().unwrap().clone(),
6625 ],
6626 };
6627
6628 let mapped = self.apply_function(&args[1], &call_args, data)?;
6629 if !mapped.is_undefined() {
6632 result.push(mapped);
6633 }
6634 }
6635 Ok(JValue::array(result))
6636 }
6637 JValue::Null => Ok(JValue::Null),
6638 _ => Err(EvaluatorError::TypeError(
6639 "map() first argument must be an array".to_string(),
6640 )),
6641 }
6642 }
6643
6644 "filter" => {
6645 if args.len() != 2 {
6646 return Err(EvaluatorError::EvaluationError(
6647 "filter() requires exactly 2 arguments".to_string(),
6648 ));
6649 }
6650
6651 let array = self.evaluate_internal(&args[0], data)?;
6653
6654 if array.is_undefined() {
6656 return Ok(JValue::Undefined);
6657 }
6658
6659 if array.is_null() {
6661 return Ok(JValue::Undefined);
6662 }
6663
6664 let single_holder;
6668 let (items, was_single_value): (&[JValue], bool) = match &array {
6669 JValue::Array(arr) => (arr.as_slice(), false),
6670 _ => { single_holder = [array]; (&single_holder[..], true) }
6671 };
6672
6673 let param_count = self.get_callback_param_count(&args[1]);
6675
6676 if param_count == 1 {
6678 if let AstNode::Lambda {
6679 params, body, signature: None, thunk: false,
6680 } = &args[1]
6681 {
6682 let var_refs: Vec<&str> =
6683 params.iter().map(|s| s.as_str()).collect();
6684 if let Some(compiled) =
6685 try_compile_expr_with_allowed_vars(body, &var_refs)
6686 {
6687 let param_name = params[0].as_str();
6688 let mut result = Vec::with_capacity(items.len() / 2);
6689 let mut vars = HashMap::new();
6690 for item in items.iter() {
6691 vars.insert(param_name, item);
6692 let pred_result = eval_compiled(&compiled, data, Some(&vars))?;
6693 if compiled_is_truthy(&pred_result) {
6694 result.push(item.clone());
6695 }
6696 }
6697 if was_single_value {
6698 if result.len() == 1 {
6699 return Ok(result.remove(0));
6700 } else if result.is_empty() {
6701 return Ok(JValue::Undefined);
6702 }
6703 }
6704 return Ok(JValue::array(result));
6705 }
6706 }
6707 if let AstNode::Variable(var_name) = &args[1] {
6709 if let Some(stored) = self.context.lookup_lambda(var_name) {
6710 if let Some(ref ce) = stored.compiled_body.clone() {
6711 let param_name = stored.params[0].clone();
6712 let captured_data = stored.captured_data.clone();
6713 let captured_env_clone = stored.captured_env.clone();
6714 let ce_clone = ce.clone();
6715 if !captured_env_clone
6716 .values()
6717 .any(|v| matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. }))
6718 {
6719 let call_data = captured_data.as_ref().unwrap_or(data);
6720 let mut result = Vec::with_capacity(items.len() / 2);
6721 let mut vars: HashMap<&str, &JValue> = captured_env_clone
6722 .iter()
6723 .map(|(k, v)| (k.as_str(), v))
6724 .collect();
6725 for item in items.iter() {
6726 vars.insert(param_name.as_str(), item);
6727 let pred_result =
6728 eval_compiled(&ce_clone, call_data, Some(&vars))?;
6729 if compiled_is_truthy(&pred_result) {
6730 result.push(item.clone());
6731 }
6732 }
6733 if was_single_value {
6734 if result.len() == 1 {
6735 return Ok(result.remove(0));
6736 } else if result.is_empty() {
6737 return Ok(JValue::Undefined);
6738 }
6739 }
6740 return Ok(JValue::array(result));
6741 }
6742 }
6743 }
6744 }
6745 }
6746
6747 let arr_value = if param_count >= 3 {
6749 Some(JValue::array(items.to_vec()))
6750 } else {
6751 None
6752 };
6753
6754 let mut result = Vec::with_capacity(items.len() / 2);
6755
6756 for (index, item) in items.iter().enumerate() {
6757 let call_args = match param_count {
6759 1 => vec![item.clone()],
6760 2 => vec![item.clone(), JValue::Number(index as f64)],
6761 _ => vec![
6762 item.clone(),
6763 JValue::Number(index as f64),
6764 arr_value.as_ref().unwrap().clone(),
6765 ],
6766 };
6767
6768 let predicate_result = self.apply_function(&args[1], &call_args, data)?;
6769 if self.is_truthy(&predicate_result) {
6770 result.push(item.clone());
6771 }
6772 }
6773
6774 if was_single_value {
6777 if result.len() == 1 {
6778 return Ok(result.remove(0));
6779 } else if result.is_empty() {
6780 return Ok(JValue::Undefined);
6781 }
6782 }
6783
6784 Ok(JValue::array(result))
6785 }
6786
6787 "reduce" => {
6788 if args.len() < 2 || args.len() > 3 {
6789 return Err(EvaluatorError::EvaluationError(
6790 "reduce() requires 2 or 3 arguments".to_string(),
6791 ));
6792 }
6793
6794 if let AstNode::Lambda { params, .. } = &args[1] {
6796 if params.len() < 2 {
6797 return Err(EvaluatorError::EvaluationError(
6798 "D3050: The second argument of reduce must be a function with at least two arguments".to_string(),
6799 ));
6800 }
6801 } else if let AstNode::Function { name, .. } = &args[1] {
6802 let _ = name; }
6806
6807 let array = self.evaluate_internal(&args[0], data)?;
6809
6810 let single_holder;
6813 let items: &[JValue] = match &array {
6814 JValue::Array(arr) => arr.as_slice(),
6815 JValue::Null => return Ok(JValue::Null),
6816 _ => { single_holder = [array]; &single_holder[..] }
6817 };
6818
6819 if items.is_empty() {
6820 return if args.len() == 3 {
6822 self.evaluate_internal(&args[2], data)
6823 } else {
6824 Ok(JValue::Null)
6825 };
6826 }
6827
6828 let mut accumulator = if args.len() == 3 {
6830 self.evaluate_internal(&args[2], data)?
6831 } else {
6832 items[0].clone()
6833 };
6834
6835 let start_idx = if args.len() == 3 { 0 } else { 1 };
6836
6837 let param_count = self.get_callback_param_count(&args[1]);
6839
6840 if param_count == 2 {
6842 if let AstNode::Lambda {
6843 params, body, signature: None, thunk: false,
6844 } = &args[1]
6845 {
6846 let var_refs: Vec<&str> =
6847 params.iter().map(|s| s.as_str()).collect();
6848 if let Some(compiled) =
6849 try_compile_expr_with_allowed_vars(body, &var_refs)
6850 {
6851 let acc_name = params[0].as_str();
6852 let item_name = params[1].as_str();
6853 for item in items[start_idx..].iter() {
6854 let vars: HashMap<&str, &JValue> = HashMap::from([
6855 (acc_name, &accumulator),
6856 (item_name, item),
6857 ]);
6858 accumulator = eval_compiled(&compiled, data, Some(&vars))?;
6859 }
6860 return Ok(accumulator);
6861 }
6862 }
6863 if let AstNode::Variable(var_name) = &args[1] {
6865 if let Some(stored) = self.context.lookup_lambda(var_name) {
6866 if stored.params.len() == 2 {
6867 if let Some(ref ce) = stored.compiled_body.clone() {
6868 let acc_param = stored.params[0].clone();
6869 let item_param = stored.params[1].clone();
6870 let captured_data = stored.captured_data.clone();
6871 let captured_env_clone = stored.captured_env.clone();
6872 let ce_clone = ce.clone();
6873 if !captured_env_clone.values().any(|v| {
6874 matches!(v, JValue::Lambda { .. } | JValue::Builtin { .. })
6875 }) {
6876 let call_data =
6877 captured_data.as_ref().unwrap_or(data);
6878 for item in items[start_idx..].iter() {
6879 let mut vars: HashMap<&str, &JValue> =
6880 captured_env_clone
6881 .iter()
6882 .map(|(k, v)| (k.as_str(), v))
6883 .collect();
6884 vars.insert(acc_param.as_str(), &accumulator);
6885 vars.insert(item_param.as_str(), item);
6886 let new_acc =
6889 eval_compiled(&ce_clone, call_data, Some(&vars))?;
6890 drop(vars);
6891 accumulator = new_acc;
6892 }
6893 return Ok(accumulator);
6894 }
6895 }
6896 }
6897 }
6898 }
6899 }
6900
6901 let arr_value = if param_count >= 4 {
6903 Some(JValue::array(items.to_vec()))
6904 } else {
6905 None
6906 };
6907
6908 for (idx, item) in items[start_idx..].iter().enumerate() {
6910 let actual_idx = start_idx + idx;
6913
6914 let call_args = match param_count {
6916 2 => vec![accumulator.clone(), item.clone()],
6917 3 => vec![
6918 accumulator.clone(),
6919 item.clone(),
6920 JValue::Number(actual_idx as f64),
6921 ],
6922 _ => vec![
6923 accumulator.clone(),
6924 item.clone(),
6925 JValue::Number(actual_idx as f64),
6926 arr_value.as_ref().unwrap().clone(),
6927 ],
6928 };
6929
6930 accumulator = self.apply_function(&args[1], &call_args, data)?;
6931 }
6932
6933 Ok(accumulator)
6934 }
6935
6936 "single" => {
6937 if args.is_empty() || args.len() > 2 {
6938 return Err(EvaluatorError::EvaluationError(
6939 "single() requires 1 or 2 arguments".to_string(),
6940 ));
6941 }
6942
6943 let array = self.evaluate_internal(&args[0], data)?;
6945
6946 let arr = match array {
6948 JValue::Array(arr) => arr.to_vec(),
6949 JValue::Null => return Ok(JValue::Null),
6950 other => vec![other],
6951 };
6952
6953 if args.len() == 1 {
6954 match arr.len() {
6956 0 => Err(EvaluatorError::EvaluationError(
6957 "single() argument is empty".to_string(),
6958 )),
6959 1 => Ok(arr.into_iter().next().unwrap()),
6960 count => Err(EvaluatorError::EvaluationError(format!(
6961 "single() argument has {} values (expected exactly 1)",
6962 count
6963 ))),
6964 }
6965 } else {
6966 let arr_value = JValue::array(arr.clone());
6968 let mut matches = Vec::new();
6969 for (index, item) in arr.into_iter().enumerate() {
6970 let predicate_result = self.apply_function(
6972 &args[1],
6973 &[
6974 item.clone(),
6975 JValue::Number(index as f64),
6976 arr_value.clone(),
6977 ],
6978 data,
6979 )?;
6980 if self.is_truthy(&predicate_result) {
6981 matches.push(item);
6982 }
6983 }
6984
6985 match matches.len() {
6986 0 => Err(EvaluatorError::EvaluationError(
6987 "single() predicate matches no values".to_string(),
6988 )),
6989 1 => Ok(matches.into_iter().next().unwrap()),
6990 count => Err(EvaluatorError::EvaluationError(format!(
6991 "single() predicate matches {} values (expected exactly 1)",
6992 count
6993 ))),
6994 }
6995 }
6996 }
6997
6998 "each" => {
6999 if args.is_empty() || args.len() > 2 {
7002 return Err(EvaluatorError::EvaluationError(
7003 "each() requires 1 or 2 arguments".to_string(),
7004 ));
7005 }
7006
7007 let (obj_value, func_arg) = if args.len() == 1 {
7009 (data.clone(), &args[0])
7011 } else {
7012 (self.evaluate_internal(&args[0], data)?, &args[1])
7014 };
7015
7016 let param_count = self.get_callback_param_count(func_arg);
7018
7019 match obj_value {
7020 JValue::Object(obj) => {
7021 let mut result = Vec::new();
7022 for (key, value) in obj.iter() {
7023 let call_args = match param_count {
7026 1 => vec![value.clone()],
7027 _ => vec![value.clone(), JValue::string(key.clone())],
7028 };
7029
7030 let fn_result = self.apply_function(func_arg, &call_args, data)?;
7031 if !fn_result.is_null() && !fn_result.is_undefined() {
7033 result.push(fn_result);
7034 }
7035 }
7036 Ok(JValue::array(result))
7037 }
7038 JValue::Null => Ok(JValue::Null),
7039 _ => Err(EvaluatorError::TypeError(
7040 "each() first argument must be an object".to_string(),
7041 )),
7042 }
7043 }
7044
7045 "not" => {
7046 if evaluated_args.len() != 1 {
7047 return Err(EvaluatorError::EvaluationError(
7048 "not() requires exactly 1 argument".to_string(),
7049 ));
7050 }
7051 Ok(JValue::Bool(!self.is_truthy(&evaluated_args[0])))
7054 }
7055 "boolean" => {
7056 if evaluated_args.len() != 1 {
7057 return Err(EvaluatorError::EvaluationError(
7058 "boolean() requires exactly 1 argument".to_string(),
7059 ));
7060 }
7061 Ok(functions::boolean::boolean(&evaluated_args[0])?)
7062 }
7063 "type" => {
7064 if evaluated_args.len() != 1 {
7065 return Err(EvaluatorError::EvaluationError(
7066 "type() requires exactly 1 argument".to_string(),
7067 ));
7068 }
7069 match &evaluated_args[0] {
7073 JValue::Null => Ok(JValue::string("null")),
7074 JValue::Bool(_) => Ok(JValue::string("boolean")),
7075 JValue::Number(_) => Ok(JValue::string("number")),
7076 JValue::String(_) => Ok(JValue::string("string")),
7077 JValue::Array(_) => Ok(JValue::string("array")),
7078 JValue::Object(_) => Ok(JValue::string("object")),
7079 JValue::Undefined => Ok(JValue::Undefined),
7080 JValue::Lambda { .. } | JValue::Builtin { .. } => {
7081 Ok(JValue::string("function"))
7082 }
7083 JValue::Regex { .. } => Ok(JValue::string("regex")),
7084 }
7085 }
7086
7087 "base64encode" => {
7088 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7089 return Ok(JValue::Null);
7090 }
7091 if evaluated_args.len() != 1 {
7092 return Err(EvaluatorError::EvaluationError(
7093 "base64encode() requires exactly 1 argument".to_string(),
7094 ));
7095 }
7096 match &evaluated_args[0] {
7097 JValue::String(s) => Ok(functions::encoding::base64encode(s)?),
7098 _ => Err(EvaluatorError::TypeError(
7099 "base64encode() requires a string argument".to_string(),
7100 )),
7101 }
7102 }
7103 "base64decode" => {
7104 if evaluated_args.is_empty() || evaluated_args[0].is_null() {
7105 return Ok(JValue::Null);
7106 }
7107 if evaluated_args.len() != 1 {
7108 return Err(EvaluatorError::EvaluationError(
7109 "base64decode() requires exactly 1 argument".to_string(),
7110 ));
7111 }
7112 match &evaluated_args[0] {
7113 JValue::String(s) => Ok(functions::encoding::base64decode(s)?),
7114 _ => Err(EvaluatorError::TypeError(
7115 "base64decode() requires a string argument".to_string(),
7116 )),
7117 }
7118 }
7119 "encodeUrlComponent" => {
7120 if evaluated_args.len() != 1 {
7121 return Err(EvaluatorError::EvaluationError(
7122 "encodeUrlComponent() requires exactly 1 argument".to_string(),
7123 ));
7124 }
7125 if evaluated_args[0].is_null() {
7126 return Ok(JValue::Null);
7127 }
7128 match &evaluated_args[0] {
7129 JValue::String(s) => Ok(functions::encoding::encode_url_component(s)?),
7130 _ => Err(EvaluatorError::TypeError(
7131 "encodeUrlComponent() requires a string argument".to_string(),
7132 )),
7133 }
7134 }
7135 "decodeUrlComponent" => {
7136 if evaluated_args.len() != 1 {
7137 return Err(EvaluatorError::EvaluationError(
7138 "decodeUrlComponent() requires exactly 1 argument".to_string(),
7139 ));
7140 }
7141 if evaluated_args[0].is_null() {
7142 return Ok(JValue::Null);
7143 }
7144 match &evaluated_args[0] {
7145 JValue::String(s) => Ok(functions::encoding::decode_url_component(s)?),
7146 _ => Err(EvaluatorError::TypeError(
7147 "decodeUrlComponent() requires a string argument".to_string(),
7148 )),
7149 }
7150 }
7151 "encodeUrl" => {
7152 if evaluated_args.len() != 1 {
7153 return Err(EvaluatorError::EvaluationError(
7154 "encodeUrl() requires exactly 1 argument".to_string(),
7155 ));
7156 }
7157 if evaluated_args[0].is_null() {
7158 return Ok(JValue::Null);
7159 }
7160 match &evaluated_args[0] {
7161 JValue::String(s) => Ok(functions::encoding::encode_url(s)?),
7162 _ => Err(EvaluatorError::TypeError(
7163 "encodeUrl() requires a string argument".to_string(),
7164 )),
7165 }
7166 }
7167 "decodeUrl" => {
7168 if evaluated_args.len() != 1 {
7169 return Err(EvaluatorError::EvaluationError(
7170 "decodeUrl() requires exactly 1 argument".to_string(),
7171 ));
7172 }
7173 if evaluated_args[0].is_null() {
7174 return Ok(JValue::Null);
7175 }
7176 match &evaluated_args[0] {
7177 JValue::String(s) => Ok(functions::encoding::decode_url(s)?),
7178 _ => Err(EvaluatorError::TypeError(
7179 "decodeUrl() requires a string argument".to_string(),
7180 )),
7181 }
7182 }
7183
7184 "error" => {
7185 if evaluated_args.is_empty() {
7187 return Err(EvaluatorError::EvaluationError(
7189 "D3137: $error() function evaluated".to_string(),
7190 ));
7191 }
7192
7193 match &evaluated_args[0] {
7194 JValue::String(s) => {
7195 Err(EvaluatorError::EvaluationError(format!("D3137: {}", s)))
7196 }
7197 _ => Err(EvaluatorError::TypeError(
7198 "T0410: Argument 1 of function error does not match function signature"
7199 .to_string(),
7200 )),
7201 }
7202 }
7203 "assert" => {
7204 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7206 return Err(EvaluatorError::EvaluationError(
7207 "assert() requires 1 or 2 arguments".to_string(),
7208 ));
7209 }
7210
7211 let condition = match &evaluated_args[0] {
7213 JValue::Bool(b) => *b,
7214 _ => {
7215 return Err(EvaluatorError::TypeError(
7216 "T0410: Argument 1 of function $assert does not match function signature".to_string(),
7217 ));
7218 }
7219 };
7220
7221 if !condition {
7222 let message = if evaluated_args.len() == 2 {
7223 match &evaluated_args[1] {
7224 JValue::String(s) => s.clone(),
7225 _ => Rc::from("$assert() statement failed"),
7226 }
7227 } else {
7228 Rc::from("$assert() statement failed")
7229 };
7230 return Err(EvaluatorError::EvaluationError(format!(
7231 "D3141: {}",
7232 message
7233 )));
7234 }
7235
7236 Ok(JValue::Null)
7237 }
7238
7239 "eval" => {
7240 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7242 return Err(EvaluatorError::EvaluationError(
7243 "T0410: Argument 1 of function $eval must be a string".to_string(),
7244 ));
7245 }
7246
7247 if evaluated_args[0].is_null() {
7249 return Ok(JValue::Null);
7250 }
7251
7252 let expr_str = match &evaluated_args[0] {
7254 JValue::String(s) => &**s,
7255 _ => {
7256 return Err(EvaluatorError::EvaluationError(
7257 "T0410: Argument 1 of function $eval must be a string".to_string(),
7258 ));
7259 }
7260 };
7261
7262 let parsed_ast = match parser::parse(expr_str) {
7264 Ok(ast) => ast,
7265 Err(e) => {
7266 return Err(EvaluatorError::EvaluationError(format!(
7268 "D3120: The expression passed to $eval cannot be parsed: {}",
7269 e
7270 )));
7271 }
7272 };
7273
7274 let eval_context = if evaluated_args.len() == 2 {
7276 &evaluated_args[1]
7277 } else {
7278 data
7279 };
7280
7281 match self.evaluate_internal(&parsed_ast, eval_context) {
7283 Ok(result) => Ok(result),
7284 Err(e) => {
7285 let err_msg = e.to_string();
7287 if err_msg.starts_with("D3121") || err_msg.contains("Unknown function") {
7288 Err(EvaluatorError::EvaluationError(format!(
7289 "D3121: {}",
7290 err_msg
7291 )))
7292 } else {
7293 Err(e)
7294 }
7295 }
7296 }
7297 }
7298
7299 "now" => {
7300 if !evaluated_args.is_empty() {
7301 return Err(EvaluatorError::EvaluationError(
7302 "now() takes no arguments".to_string(),
7303 ));
7304 }
7305 Ok(crate::datetime::now())
7306 }
7307
7308 "millis" => {
7309 if !evaluated_args.is_empty() {
7310 return Err(EvaluatorError::EvaluationError(
7311 "millis() takes no arguments".to_string(),
7312 ));
7313 }
7314 Ok(crate::datetime::millis())
7315 }
7316
7317 "toMillis" => {
7318 if evaluated_args.is_empty() || evaluated_args.len() > 2 {
7319 return Err(EvaluatorError::EvaluationError(
7320 "toMillis() requires 1 or 2 arguments".to_string(),
7321 ));
7322 }
7323
7324 match &evaluated_args[0] {
7325 JValue::String(s) => {
7326 if evaluated_args.len() == 2 {
7328 match &evaluated_args[1] {
7329 JValue::String(picture) => {
7330 Ok(crate::datetime::to_millis_with_picture(s, picture)?)
7332 }
7333 JValue::Null => Ok(JValue::Null),
7334 _ => Err(EvaluatorError::TypeError(
7335 "toMillis() second argument must be a string".to_string(),
7336 )),
7337 }
7338 } else {
7339 Ok(crate::datetime::to_millis(s)?)
7341 }
7342 }
7343 JValue::Null => Ok(JValue::Null),
7344 _ => Err(EvaluatorError::TypeError(
7345 "toMillis() requires a string argument".to_string(),
7346 )),
7347 }
7348 }
7349
7350 "fromMillis" => {
7351 if evaluated_args.len() != 1 {
7352 return Err(EvaluatorError::EvaluationError(
7353 "fromMillis() requires exactly 1 argument".to_string(),
7354 ));
7355 }
7356
7357 match &evaluated_args[0] {
7358 JValue::Number(n) => {
7359 let millis = (if n.fract() == 0.0 {
7360 Ok(*n as i64)
7361 } else {
7362 Err(())
7363 })
7364 .map_err(|_| {
7365 EvaluatorError::TypeError(
7366 "fromMillis() requires an integer".to_string(),
7367 )
7368 })?;
7369 Ok(crate::datetime::from_millis(millis)?)
7370 }
7371 JValue::Null => Ok(JValue::Null),
7372 _ => Err(EvaluatorError::TypeError(
7373 "fromMillis() requires a number argument".to_string(),
7374 )),
7375 }
7376 }
7377
7378 _ => Err(EvaluatorError::ReferenceError(format!(
7379 "Unknown function: {}",
7380 name
7381 ))),
7382 }
7383 }
7384
7385 fn apply_function(
7391 &mut self,
7392 func_node: &AstNode,
7393 values: &[JValue],
7394 data: &JValue,
7395 ) -> Result<JValue, EvaluatorError> {
7396 match func_node {
7397 AstNode::Lambda {
7398 params,
7399 body,
7400 signature,
7401 thunk,
7402 } => {
7403 self.invoke_lambda(params, body, signature.as_ref(), values, data, *thunk)
7405 }
7406 AstNode::Function {
7407 name,
7408 args,
7409 is_builtin,
7410 } => {
7411 let has_placeholder = args.iter().any(|arg| matches!(arg, AstNode::Placeholder));
7413
7414 if has_placeholder {
7415 let partial_lambda =
7417 self.create_partial_application(name, args, *is_builtin, data)?;
7418
7419 if let Some(stored) = self.lookup_lambda_from_value(&partial_lambda) {
7421 return self.invoke_stored_lambda(&stored, values, data);
7422 }
7423 Err(EvaluatorError::EvaluationError(
7424 "Failed to apply partial application".to_string(),
7425 ))
7426 } else {
7427 let result = self.evaluate_internal(func_node, data)?;
7430
7431 if let Some(stored) = self.lookup_lambda_from_value(&result) {
7433 return self.invoke_stored_lambda(&stored, values, data);
7434 }
7435
7436 Ok(result)
7438 }
7439 }
7440 AstNode::Variable(var_name) => {
7441 if let Some(stored_lambda) = self.context.lookup_lambda(var_name).cloned() {
7443 self.invoke_stored_lambda(&stored_lambda, values, data)
7444 } else if let Some(value) = self.context.lookup(var_name).cloned() {
7445 if let Some(stored) = self.lookup_lambda_from_value(&value) {
7448 return self.invoke_stored_lambda(&stored, values, data);
7449 }
7450 if values.is_empty() {
7452 self.evaluate_internal(func_node, data)
7453 } else {
7454 self.evaluate_internal(func_node, &values[0])
7455 }
7456 } else if self.is_builtin_function(var_name) {
7457 self.call_builtin_with_values(var_name, values)
7460 } else {
7461 if values.is_empty() {
7463 self.evaluate_internal(func_node, data)
7464 } else {
7465 self.evaluate_internal(func_node, &values[0])
7466 }
7467 }
7468 }
7469 _ => {
7470 if values.is_empty() {
7472 self.evaluate_internal(func_node, data)
7473 } else {
7474 self.evaluate_internal(func_node, &values[0])
7475 }
7476 }
7477 }
7478 }
7479
7480 fn execute_transform(
7482 &mut self,
7483 location: &AstNode,
7484 update: &AstNode,
7485 delete: Option<&AstNode>,
7486 _original_data: &JValue,
7487 ) -> Result<JValue, EvaluatorError> {
7488 let input = self
7490 .context
7491 .lookup("$")
7492 .ok_or_else(|| {
7493 EvaluatorError::EvaluationError("Transform requires $ binding".to_string())
7494 })?
7495 .clone();
7496
7497 let located_objects = self.evaluate_internal(location, &input)?;
7499
7500 let targets: Vec<JValue> = match located_objects {
7502 JValue::Array(arr) => arr.to_vec(),
7503 JValue::Object(_) => vec![located_objects],
7504 JValue::Null => Vec::new(),
7505 other => vec![other],
7506 };
7507
7508 let delete_fields: Vec<String> = if let Some(delete_node) = delete {
7514 let delete_val = self.evaluate_internal(delete_node, &input)?;
7515 match delete_val {
7516 JValue::Array(arr) => arr
7517 .iter()
7518 .filter_map(|v| match v {
7519 JValue::String(s) => Some(s.to_string()),
7520 _ => None,
7521 })
7522 .collect(),
7523 JValue::String(s) => vec![s.to_string()],
7524 JValue::Null => Vec::new(), _ => {
7526 return Err(EvaluatorError::EvaluationError(
7528 "T2012: The third argument of the transform operator must be an array of strings".to_string()
7529 ));
7530 }
7531 }
7532 } else {
7533 Vec::new()
7534 };
7535
7536 fn apply_transform_deep(
7538 evaluator: &mut Evaluator,
7539 value: &JValue,
7540 targets: &[JValue],
7541 update: &AstNode,
7542 delete_fields: &[String],
7543 ) -> Result<JValue, EvaluatorError> {
7544 if targets.iter().any(|t| t == value) {
7547 if let JValue::Object(map_rc) = value.clone() {
7549 let mut map = (*map_rc).clone();
7550 let update_val = evaluator.evaluate_internal(update, value)?;
7551 match update_val {
7553 JValue::Object(update_map) => {
7554 for (key, val) in update_map.iter() {
7555 map.insert(key.clone(), val.clone());
7556 }
7557 }
7558 JValue::Null => {
7559 }
7561 _ => {
7562 return Err(EvaluatorError::EvaluationError(
7563 "T2011: The second argument of the transform operator must evaluate to an object".to_string()
7564 ));
7565 }
7566 }
7567 for field in delete_fields {
7568 map.shift_remove(field);
7569 }
7570 return Ok(JValue::object(map));
7571 }
7572 return Ok(value.clone());
7573 }
7574
7575 match value {
7577 JValue::Object(map) => {
7578 let mut new_map = IndexMap::new();
7579 for (k, v) in map.iter() {
7580 new_map.insert(
7581 k.clone(),
7582 apply_transform_deep(evaluator, v, targets, update, delete_fields)?,
7583 );
7584 }
7585 Ok(JValue::object(new_map))
7586 }
7587 JValue::Array(arr) => {
7588 let mut new_arr = Vec::new();
7589 for item in arr.iter() {
7590 new_arr.push(apply_transform_deep(
7591 evaluator,
7592 item,
7593 targets,
7594 update,
7595 delete_fields,
7596 )?);
7597 }
7598 Ok(JValue::array(new_arr))
7599 }
7600 _ => Ok(value.clone()),
7601 }
7602 }
7603
7604 apply_transform_deep(self, &input, &targets, update, &delete_fields)
7606 }
7607
7608 fn invoke_lambda(
7610 &mut self,
7611 params: &[String],
7612 body: &AstNode,
7613 signature: Option<&String>,
7614 values: &[JValue],
7615 data: &JValue,
7616 thunk: bool,
7617 ) -> Result<JValue, EvaluatorError> {
7618 self.invoke_lambda_with_env(params, body, signature, values, data, None, None, thunk)
7619 }
7620
7621 fn invoke_lambda_with_env(
7623 &mut self,
7624 params: &[String],
7625 body: &AstNode,
7626 signature: Option<&String>,
7627 values: &[JValue],
7628 data: &JValue,
7629 captured_env: Option<&HashMap<String, JValue>>,
7630 captured_data: Option<&JValue>,
7631 thunk: bool,
7632 ) -> Result<JValue, EvaluatorError> {
7633 if thunk {
7635 let stored = StoredLambda {
7636 params: params.to_vec(),
7637 body: body.clone(),
7638 compiled_body: None, signature: signature.cloned(),
7640 captured_env: captured_env.cloned().unwrap_or_default(),
7641 captured_data: captured_data.cloned(),
7642 thunk,
7643 };
7644 return self.invoke_lambda_with_tco(&stored, values, data);
7645 }
7646
7647 self.context.push_scope();
7650
7651 if let Some(env) = captured_env {
7653 for (name, value) in env {
7654 self.context.bind(name.clone(), value.clone());
7655 }
7656 }
7657
7658 if let Some(sig_str) = signature {
7659 let coerced_values = match crate::signature::Signature::parse(sig_str) {
7661 Ok(sig) => {
7662 match sig.validate_and_coerce(values) {
7663 Ok(coerced) => coerced,
7664 Err(e) => {
7665 self.context.pop_scope();
7666 match e {
7667 crate::signature::SignatureError::UndefinedArgument => {
7668 return Ok(JValue::Null);
7669 }
7670 crate::signature::SignatureError::ArgumentTypeMismatch {
7671 index,
7672 expected,
7673 } => {
7674 return Err(EvaluatorError::TypeError(
7675 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
7676 ));
7677 }
7678 crate::signature::SignatureError::ArrayTypeMismatch {
7679 index,
7680 expected,
7681 } => {
7682 return Err(EvaluatorError::TypeError(format!(
7683 "T0412: Argument {} of function must be an array of {}",
7684 index, expected
7685 )));
7686 }
7687 _ => {
7688 return Err(EvaluatorError::TypeError(format!(
7689 "Signature validation failed: {}",
7690 e
7691 )));
7692 }
7693 }
7694 }
7695 }
7696 }
7697 Err(e) => {
7698 self.context.pop_scope();
7699 return Err(EvaluatorError::EvaluationError(format!(
7700 "Invalid signature: {}",
7701 e
7702 )));
7703 }
7704 };
7705 for (i, param) in params.iter().enumerate() {
7707 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Undefined);
7708 self.context.bind(param.clone(), value);
7709 }
7710 } else {
7711 for (i, param) in params.iter().enumerate() {
7713 let value = values.get(i).cloned().unwrap_or(JValue::Undefined);
7714 self.context.bind(param.clone(), value);
7715 }
7716 }
7717
7718 if let AstNode::String(body_str) = body {
7720 if body_str.starts_with("__partial_call:") {
7721 let parts: Vec<&str> = body_str.split(':').collect();
7723 if parts.len() >= 4 {
7724 let func_name = parts[1];
7725 let is_builtin = parts[2] == "true";
7726 let total_args: usize = parts[3].parse().unwrap_or(0);
7727
7728 let placeholder_positions: Vec<usize> = if let Some(env) = captured_env {
7730 if let Some(JValue::Array(positions)) = env.get("__placeholder_positions") {
7731 positions
7732 .iter()
7733 .filter_map(|v| v.as_f64().map(|n| n as usize))
7734 .collect()
7735 } else {
7736 vec![]
7737 }
7738 } else {
7739 vec![]
7740 };
7741
7742 let mut full_args: Vec<JValue> = vec![JValue::Null; total_args];
7744
7745 if let Some(env) = captured_env {
7747 for (key, value) in env {
7748 if key.starts_with("__bound_arg_") {
7749 if let Ok(pos) = key[12..].parse::<usize>() {
7750 if pos < total_args {
7751 full_args[pos] = value.clone();
7752 }
7753 }
7754 }
7755 }
7756 }
7757
7758 for (i, &pos) in placeholder_positions.iter().enumerate() {
7760 if pos < total_args {
7761 let value = values.get(i).cloned().unwrap_or(JValue::Null);
7762 full_args[pos] = value;
7763 }
7764 }
7765
7766 self.context.pop_scope();
7768 self.context.push_scope();
7769
7770 let mut temp_args: Vec<AstNode> = Vec::new();
7772 for (i, value) in full_args.iter().enumerate() {
7773 let temp_name = format!("__temp_arg_{}", i);
7774 self.context.bind(temp_name.clone(), value.clone());
7775 temp_args.push(AstNode::Variable(temp_name));
7776 }
7777
7778 let result =
7780 self.evaluate_function_call(func_name, &temp_args, is_builtin, data);
7781
7782 self.context.pop_scope();
7784
7785 return result;
7786 }
7787 }
7788 }
7789
7790 let body_data = captured_data.unwrap_or(data);
7793 let result = self.evaluate_internal(body, body_data)?;
7794
7795 let is_scalar = matches!(&result,
7798 JValue::Number(_) | JValue::Bool(_) | JValue::String(_)
7799 | JValue::Null | JValue::Undefined);
7800 if is_scalar {
7801 self.context.pop_scope();
7802 } else {
7803 let lambdas_to_keep = self.extract_lambda_ids(&result);
7804 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
7805 }
7806
7807 Ok(result)
7808 }
7809
7810 fn invoke_lambda_with_tco(
7814 &mut self,
7815 stored_lambda: &StoredLambda,
7816 initial_args: &[JValue],
7817 data: &JValue,
7818 ) -> Result<JValue, EvaluatorError> {
7819 let mut current_lambda = stored_lambda.clone();
7820 let mut current_args = initial_args.to_vec();
7821 let mut current_data = data.clone();
7822
7823 const MAX_TCO_ITERATIONS: usize = 100_000;
7826 let mut iterations = 0;
7827
7828 self.context.push_scope();
7832
7833 let result = loop {
7835 iterations += 1;
7836 if iterations > MAX_TCO_ITERATIONS {
7837 self.context.pop_scope();
7838 return Err(EvaluatorError::EvaluationError(
7839 "U1001: Stack overflow - maximum recursion depth (500) exceeded".to_string(),
7840 ));
7841 }
7842
7843 let result =
7845 self.invoke_lambda_body_for_tco(¤t_lambda, ¤t_args, ¤t_data)?;
7846
7847 match result {
7848 LambdaResult::JValue(v) => break v,
7849 LambdaResult::TailCall { lambda, args, data } => {
7850 current_lambda = *lambda;
7852 current_args = args;
7853 current_data = data;
7854 }
7855 }
7856 };
7857
7858 let lambdas_to_keep = self.extract_lambda_ids(&result);
7860 self.context.pop_scope_preserving_lambdas(&lambdas_to_keep);
7861
7862 Ok(result)
7863 }
7864
7865 fn invoke_lambda_body_for_tco(
7870 &mut self,
7871 lambda: &StoredLambda,
7872 values: &[JValue],
7873 data: &JValue,
7874 ) -> Result<LambdaResult, EvaluatorError> {
7875 let coerced_values = if let Some(sig_str) = &lambda.signature {
7877 match crate::signature::Signature::parse(sig_str) {
7878 Ok(sig) => match sig.validate_and_coerce(values) {
7879 Ok(coerced) => coerced,
7880 Err(e) => match e {
7881 crate::signature::SignatureError::UndefinedArgument => {
7882 return Ok(LambdaResult::JValue(JValue::Null));
7883 }
7884 crate::signature::SignatureError::ArgumentTypeMismatch {
7885 index,
7886 expected,
7887 } => {
7888 return Err(EvaluatorError::TypeError(
7889 format!("T0410: Argument {} of function does not match function signature (expected {})", index, expected)
7890 ));
7891 }
7892 crate::signature::SignatureError::ArrayTypeMismatch { index, expected } => {
7893 return Err(EvaluatorError::TypeError(format!(
7894 "T0412: Argument {} of function must be an array of {}",
7895 index, expected
7896 )));
7897 }
7898 _ => {
7899 return Err(EvaluatorError::TypeError(format!(
7900 "Signature validation failed: {}",
7901 e
7902 )));
7903 }
7904 },
7905 },
7906 Err(e) => {
7907 return Err(EvaluatorError::EvaluationError(format!(
7908 "Invalid signature: {}",
7909 e
7910 )));
7911 }
7912 }
7913 } else {
7914 values.to_vec()
7915 };
7916
7917 for (name, value) in &lambda.captured_env {
7920 self.context.bind(name.clone(), value.clone());
7921 }
7922
7923 for (i, param) in lambda.params.iter().enumerate() {
7925 let value = coerced_values.get(i).cloned().unwrap_or(JValue::Null);
7926 self.context.bind(param.clone(), value);
7927 }
7928
7929 let body_data = lambda.captured_data.as_ref().unwrap_or(data);
7931 self.evaluate_for_tco(&lambda.body, body_data)
7932 }
7933
7934 fn evaluate_for_tco(
7937 &mut self,
7938 node: &AstNode,
7939 data: &JValue,
7940 ) -> Result<LambdaResult, EvaluatorError> {
7941 match node {
7942 AstNode::Conditional {
7944 condition,
7945 then_branch,
7946 else_branch,
7947 } => {
7948 let cond_value = self.evaluate_internal(condition, data)?;
7949 let is_truthy = self.is_truthy(&cond_value);
7950
7951 if is_truthy {
7952 self.evaluate_for_tco(then_branch, data)
7953 } else if let Some(else_expr) = else_branch {
7954 self.evaluate_for_tco(else_expr, data)
7955 } else {
7956 Ok(LambdaResult::JValue(JValue::Null))
7957 }
7958 }
7959
7960 AstNode::Block(exprs) => {
7962 if exprs.is_empty() {
7963 return Ok(LambdaResult::JValue(JValue::Null));
7964 }
7965
7966 let mut result = JValue::Null;
7968 for (i, expr) in exprs.iter().enumerate() {
7969 if i == exprs.len() - 1 {
7970 return self.evaluate_for_tco(expr, data);
7972 } else {
7973 result = self.evaluate_internal(expr, data)?;
7974 }
7975 }
7976 Ok(LambdaResult::JValue(result))
7977 }
7978
7979 AstNode::Binary {
7981 op: BinaryOp::ColonEqual,
7982 lhs,
7983 rhs,
7984 } => {
7985 let var_name = match lhs.as_ref() {
7987 AstNode::Variable(name) => name.clone(),
7988 _ => {
7989 let result = self.evaluate_internal(node, data)?;
7991 return Ok(LambdaResult::JValue(result));
7992 }
7993 };
7994
7995 if let AstNode::Lambda {
7997 params,
7998 body,
7999 signature,
8000 thunk,
8001 } = rhs.as_ref()
8002 {
8003 let captured_env = self.capture_environment_for(body, params);
8004 let compiled_body = if !thunk {
8005 let var_refs: Vec<&str> = params.iter().map(|s| s.as_str()).collect();
8006 try_compile_expr_with_allowed_vars(body, &var_refs)
8007 } else {
8008 None
8009 };
8010 let stored_lambda = StoredLambda {
8011 params: params.clone(),
8012 body: (**body).clone(),
8013 compiled_body,
8014 signature: signature.clone(),
8015 captured_env,
8016 captured_data: Some(data.clone()),
8017 thunk: *thunk,
8018 };
8019 self.context.bind_lambda(var_name, stored_lambda);
8020 let lambda_repr =
8021 JValue::lambda("anon", params.clone(), None::<String>, None::<String>);
8022 return Ok(LambdaResult::JValue(lambda_repr));
8023 }
8024
8025 let value = self.evaluate_internal(rhs, data)?;
8027 self.context.bind(var_name, value.clone());
8028 Ok(LambdaResult::JValue(value))
8029 }
8030
8031 AstNode::Function { name, args, .. } => {
8033 if let Some(stored_lambda) = self.context.lookup_lambda(name).cloned() {
8035 if stored_lambda.thunk {
8036 let mut evaluated_args = Vec::with_capacity(args.len());
8037 for arg in args {
8038 evaluated_args.push(self.evaluate_internal(arg, data)?);
8039 }
8040 return Ok(LambdaResult::TailCall {
8041 lambda: Box::new(stored_lambda),
8042 args: evaluated_args,
8043 data: data.clone(),
8044 });
8045 }
8046 }
8047 let result = self.evaluate_internal(node, data)?;
8049 Ok(LambdaResult::JValue(result))
8050 }
8051
8052 AstNode::Call { procedure, args } => {
8054 let callable = self.evaluate_internal(procedure, data)?;
8056
8057 if let JValue::Lambda { lambda_id, .. } = &callable {
8059 if let Some(stored_lambda) = self.context.lookup_lambda(lambda_id).cloned() {
8060 if stored_lambda.thunk {
8061 let mut evaluated_args = Vec::with_capacity(args.len());
8062 for arg in args {
8063 evaluated_args.push(self.evaluate_internal(arg, data)?);
8064 }
8065 return Ok(LambdaResult::TailCall {
8066 lambda: Box::new(stored_lambda),
8067 args: evaluated_args,
8068 data: data.clone(),
8069 });
8070 }
8071 }
8072 }
8073 let result = self.evaluate_internal(node, data)?;
8075 Ok(LambdaResult::JValue(result))
8076 }
8077
8078 AstNode::Variable(_) => {
8081 let result = self.evaluate_internal(node, data)?;
8082 Ok(LambdaResult::JValue(result))
8083 }
8084
8085 _ => {
8087 let result = self.evaluate_internal(node, data)?;
8088 Ok(LambdaResult::JValue(result))
8089 }
8090 }
8091 }
8092
8093 fn match_with_custom_matcher(
8100 &mut self,
8101 str_value: &str,
8102 matcher_node: &AstNode,
8103 limit: Option<usize>,
8104 data: &JValue,
8105 ) -> Result<JValue, EvaluatorError> {
8106 let mut results = Vec::new();
8107 let mut count = 0;
8108
8109 let str_val = JValue::string(str_value.to_string());
8111 let mut current_match = self.apply_function(matcher_node, &[str_val], data)?;
8112
8113 while !current_match.is_undefined() && !current_match.is_null() {
8115 if let Some(lim) = limit {
8117 if count >= lim {
8118 break;
8119 }
8120 }
8121
8122 if let JValue::Object(ref match_obj) = current_match {
8124 let has_match = match_obj.contains_key("match");
8126 let has_start = match_obj.contains_key("start");
8127 let has_end = match_obj.contains_key("end");
8128 let has_groups = match_obj.contains_key("groups");
8129 let has_next = match_obj.contains_key("next");
8130
8131 if !has_match && !has_start && !has_end && !has_groups && !has_next {
8132 return Err(EvaluatorError::EvaluationError(
8134 "T1010: The matcher function did not return the correct object structure"
8135 .to_string(),
8136 ));
8137 }
8138
8139 let mut result_obj = IndexMap::new();
8141
8142 if let Some(match_val) = match_obj.get("match") {
8143 result_obj.insert("match".to_string(), match_val.clone());
8144 }
8145
8146 if let Some(start_val) = match_obj.get("start") {
8147 result_obj.insert("index".to_string(), start_val.clone());
8148 }
8149
8150 if let Some(groups_val) = match_obj.get("groups") {
8151 result_obj.insert("groups".to_string(), groups_val.clone());
8152 }
8153
8154 results.push(JValue::object(result_obj));
8155 count += 1;
8156
8157 if let Some(next_func) = match_obj.get("next") {
8159 if let Some(stored) = self.lookup_lambda_from_value(next_func) {
8160 current_match = self.invoke_stored_lambda(&stored, &[], data)?;
8161 continue;
8162 }
8163 }
8164
8165 break;
8167 } else {
8168 break;
8170 }
8171 }
8172
8173 if results.is_empty() {
8175 Ok(JValue::Undefined)
8176 } else {
8177 Ok(JValue::array(results))
8178 }
8179 }
8180
8181 fn replace_with_lambda(
8186 &mut self,
8187 str_value: &JValue,
8188 pattern_value: &JValue,
8189 lambda_value: &JValue,
8190 limit_value: Option<&JValue>,
8191 data: &JValue,
8192 ) -> Result<JValue, EvaluatorError> {
8193 let s = match str_value {
8195 JValue::String(s) => &**s,
8196 _ => {
8197 return Err(EvaluatorError::TypeError(
8198 "replace() requires string arguments".to_string(),
8199 ))
8200 }
8201 };
8202
8203 let (pattern, flags) =
8205 crate::functions::string::extract_regex(pattern_value).ok_or_else(|| {
8206 EvaluatorError::TypeError(
8207 "replace() pattern must be a regex when using lambda replacement".to_string(),
8208 )
8209 })?;
8210
8211 let re = crate::functions::string::build_regex(&pattern, &flags)?;
8213
8214 let limit = if let Some(lim_val) = limit_value {
8216 match lim_val {
8217 JValue::Number(n) => {
8218 let lim_f64 = *n;
8219 if lim_f64 < 0.0 {
8220 return Err(EvaluatorError::EvaluationError(format!(
8221 "D3011: Limit must be non-negative, got {}",
8222 lim_f64
8223 )));
8224 }
8225 Some(lim_f64 as usize)
8226 }
8227 _ => {
8228 return Err(EvaluatorError::TypeError(
8229 "replace() limit must be a number".to_string(),
8230 ))
8231 }
8232 }
8233 } else {
8234 None
8235 };
8236
8237 let mut result = String::new();
8239 let mut last_end = 0;
8240 let mut count = 0;
8241
8242 for cap in re.captures_iter(s) {
8243 if let Some(lim) = limit {
8245 if count >= lim {
8246 break;
8247 }
8248 }
8249
8250 let m = cap.get(0).unwrap();
8251 let match_start = m.start();
8252 let match_end = m.end();
8253 let match_str = m.as_str();
8254
8255 result.push_str(&s[last_end..match_start]);
8257
8258 let groups: Vec<JValue> = (1..cap.len())
8260 .map(|i| {
8261 cap.get(i)
8262 .map(|m| JValue::string(m.as_str().to_string()))
8263 .unwrap_or(JValue::Null)
8264 })
8265 .collect();
8266
8267 let mut match_map = IndexMap::new();
8268 match_map.insert("match".to_string(), JValue::string(match_str));
8269 match_map.insert("start".to_string(), JValue::Number(match_start as f64));
8270 match_map.insert("end".to_string(), JValue::Number(match_end as f64));
8271 match_map.insert("groups".to_string(), JValue::array(groups));
8272 let match_obj = JValue::object(match_map);
8273
8274 let stored_lambda = self.lookup_lambda_from_value(lambda_value).ok_or_else(|| {
8276 EvaluatorError::TypeError("Replacement must be a lambda function".to_string())
8277 })?;
8278 let lambda_result = self.invoke_stored_lambda(&stored_lambda, &[match_obj], data)?;
8279 let replacement_str = match lambda_result {
8280 JValue::String(s) => s,
8281 _ => {
8282 return Err(EvaluatorError::TypeError(format!(
8283 "D3012: Replacement function must return a string, got {:?}",
8284 lambda_result
8285 )))
8286 }
8287 };
8288
8289 result.push_str(&replacement_str);
8291
8292 last_end = match_end;
8293 count += 1;
8294 }
8295
8296 result.push_str(&s[last_end..]);
8298
8299 Ok(JValue::string(result))
8300 }
8301
8302 fn capture_current_environment(&self) -> HashMap<String, JValue> {
8304 self.context.all_bindings()
8305 }
8306
8307 fn capture_environment_for(
8310 &self,
8311 body: &AstNode,
8312 params: &[String],
8313 ) -> HashMap<String, JValue> {
8314 let free_vars = Self::collect_free_variables(body, params);
8315 if free_vars.is_empty() {
8316 return HashMap::new();
8317 }
8318 let mut result = HashMap::new();
8319 for var_name in &free_vars {
8320 if let Some(value) = self.context.lookup(var_name) {
8321 result.insert(var_name.clone(), value.clone());
8322 }
8323 }
8324 result
8325 }
8326
8327 fn collect_free_variables(body: &AstNode, params: &[String]) -> HashSet<String> {
8330 let mut free_vars = HashSet::new();
8331 let bound: HashSet<&str> = params.iter().map(|s| s.as_str()).collect();
8332 Self::collect_free_vars_walk(body, &bound, &mut free_vars);
8333 free_vars
8334 }
8335
8336 fn collect_free_vars_walk(node: &AstNode, bound: &HashSet<&str>, free: &mut HashSet<String>) {
8337 match node {
8338 AstNode::Variable(name) => {
8339 if !bound.contains(name.as_str()) {
8340 free.insert(name.clone());
8341 }
8342 }
8343 AstNode::Function { name, args, .. } => {
8344 if !bound.contains(name.as_str()) {
8346 free.insert(name.clone());
8347 }
8348 for arg in args {
8349 Self::collect_free_vars_walk(arg, bound, free);
8350 }
8351 }
8352 AstNode::Lambda { params, body, .. } => {
8353 let mut inner_bound = bound.clone();
8355 for p in params {
8356 inner_bound.insert(p.as_str());
8357 }
8358 Self::collect_free_vars_walk(body, &inner_bound, free);
8359 }
8360 AstNode::Binary { op, lhs, rhs } => {
8361 Self::collect_free_vars_walk(lhs, bound, free);
8362 Self::collect_free_vars_walk(rhs, bound, free);
8363 let _ = op;
8366 }
8367 AstNode::Unary { operand, .. } => {
8368 Self::collect_free_vars_walk(operand, bound, free);
8369 }
8370 AstNode::Path { steps } => {
8371 for step in steps {
8372 Self::collect_free_vars_walk(&step.node, bound, free);
8373 for stage in &step.stages {
8374 match stage {
8375 Stage::Filter(expr) => Self::collect_free_vars_walk(expr, bound, free),
8376 }
8377 }
8378 }
8379 }
8380 AstNode::Call { procedure, args } => {
8381 Self::collect_free_vars_walk(procedure, bound, free);
8382 for arg in args {
8383 Self::collect_free_vars_walk(arg, bound, free);
8384 }
8385 }
8386 AstNode::Conditional {
8387 condition,
8388 then_branch,
8389 else_branch,
8390 } => {
8391 Self::collect_free_vars_walk(condition, bound, free);
8392 Self::collect_free_vars_walk(then_branch, bound, free);
8393 if let Some(else_expr) = else_branch {
8394 Self::collect_free_vars_walk(else_expr, bound, free);
8395 }
8396 }
8397 AstNode::Block(exprs) => {
8398 let mut block_bound = bound.clone();
8399 for expr in exprs {
8400 Self::collect_free_vars_walk(expr, &block_bound, free);
8401 if let AstNode::Binary {
8403 op: BinaryOp::ColonEqual,
8404 lhs,
8405 ..
8406 } = expr
8407 {
8408 if let AstNode::Variable(var_name) = lhs.as_ref() {
8409 block_bound.insert(var_name.as_str());
8410 }
8411 }
8412 }
8413 }
8414 AstNode::Array(exprs) | AstNode::ArrayGroup(exprs) => {
8415 for expr in exprs {
8416 Self::collect_free_vars_walk(expr, bound, free);
8417 }
8418 }
8419 AstNode::Object(pairs) => {
8420 for (key, value) in pairs {
8421 Self::collect_free_vars_walk(key, bound, free);
8422 Self::collect_free_vars_walk(value, bound, free);
8423 }
8424 }
8425 AstNode::ObjectTransform { input, pattern } => {
8426 Self::collect_free_vars_walk(input, bound, free);
8427 for (key, value) in pattern {
8428 Self::collect_free_vars_walk(key, bound, free);
8429 Self::collect_free_vars_walk(value, bound, free);
8430 }
8431 }
8432 AstNode::Predicate(expr) | AstNode::FunctionApplication(expr) => {
8433 Self::collect_free_vars_walk(expr, bound, free);
8434 }
8435 AstNode::Sort { input, terms } => {
8436 Self::collect_free_vars_walk(input, bound, free);
8437 for (expr, _) in terms {
8438 Self::collect_free_vars_walk(expr, bound, free);
8439 }
8440 }
8441 AstNode::IndexBind { input, .. } => {
8442 Self::collect_free_vars_walk(input, bound, free);
8443 }
8444 AstNode::Transform {
8445 location,
8446 update,
8447 delete,
8448 } => {
8449 Self::collect_free_vars_walk(location, bound, free);
8450 Self::collect_free_vars_walk(update, bound, free);
8451 if let Some(del) = delete {
8452 Self::collect_free_vars_walk(del, bound, free);
8453 }
8454 }
8455 AstNode::String(_)
8457 | AstNode::Name(_)
8458 | AstNode::Number(_)
8459 | AstNode::Boolean(_)
8460 | AstNode::Null
8461 | AstNode::Undefined
8462 | AstNode::Placeholder
8463 | AstNode::Regex { .. }
8464 | AstNode::Wildcard
8465 | AstNode::Descendant
8466 | AstNode::ParentVariable(_) => {}
8467 }
8468 }
8469
8470 fn is_builtin_function(&self, name: &str) -> bool {
8472 matches!(
8473 name,
8474 "string" | "length" | "substring" | "substringBefore" | "substringAfter" |
8476 "uppercase" | "lowercase" | "trim" | "pad" | "contains" | "split" |
8477 "join" | "match" | "replace" | "eval" | "base64encode" | "base64decode" |
8478 "encodeUrlComponent" | "encodeUrl" | "decodeUrlComponent" | "decodeUrl" |
8479
8480 "number" | "abs" | "floor" | "ceil" | "round" | "power" | "sqrt" |
8482 "random" | "formatNumber" | "formatBase" | "formatInteger" | "parseInteger" |
8483
8484 "sum" | "max" | "min" | "average" |
8486
8487 "boolean" | "not" | "exists" |
8489
8490 "count" | "append" | "sort" | "reverse" | "shuffle" | "distinct" | "zip" |
8492
8493 "keys" | "lookup" | "spread" | "merge" | "sift" | "each" | "error" | "assert" | "type" |
8495
8496 "map" | "filter" | "reduce" | "singletonArray" |
8498
8499 "now" | "millis" | "fromMillis" | "toMillis"
8501 )
8502 }
8503
8504 fn call_builtin_with_values(
8507 &mut self,
8508 name: &str,
8509 values: &[JValue],
8510 ) -> Result<JValue, EvaluatorError> {
8511 use crate::functions;
8512
8513 if values.is_empty() {
8514 return Err(EvaluatorError::EvaluationError(format!(
8515 "{}() requires at least 1 argument",
8516 name
8517 )));
8518 }
8519
8520 let arg = &values[0];
8521
8522 match name {
8523 "string" => Ok(functions::string::string(arg, None)?),
8524 "number" => Ok(functions::numeric::number(arg)?),
8525 "boolean" => Ok(functions::boolean::boolean(arg)?),
8526 "not" => {
8527 let b = functions::boolean::boolean(arg)?;
8528 match b {
8529 JValue::Bool(val) => Ok(JValue::Bool(!val)),
8530 _ => Err(EvaluatorError::TypeError(
8531 "not() requires a boolean".to_string(),
8532 )),
8533 }
8534 }
8535 "exists" => Ok(JValue::Bool(!arg.is_null())),
8536 "abs" => match arg {
8537 JValue::Number(n) => Ok(functions::numeric::abs(*n)?),
8538 _ => Err(EvaluatorError::TypeError(
8539 "abs() requires a number argument".to_string(),
8540 )),
8541 },
8542 "floor" => match arg {
8543 JValue::Number(n) => Ok(functions::numeric::floor(*n)?),
8544 _ => Err(EvaluatorError::TypeError(
8545 "floor() requires a number argument".to_string(),
8546 )),
8547 },
8548 "ceil" => match arg {
8549 JValue::Number(n) => Ok(functions::numeric::ceil(*n)?),
8550 _ => Err(EvaluatorError::TypeError(
8551 "ceil() requires a number argument".to_string(),
8552 )),
8553 },
8554 "round" => match arg {
8555 JValue::Number(n) => Ok(functions::numeric::round(*n, None)?),
8556 _ => Err(EvaluatorError::TypeError(
8557 "round() requires a number argument".to_string(),
8558 )),
8559 },
8560 "sqrt" => match arg {
8561 JValue::Number(n) => Ok(functions::numeric::sqrt(*n)?),
8562 _ => Err(EvaluatorError::TypeError(
8563 "sqrt() requires a number argument".to_string(),
8564 )),
8565 },
8566 "uppercase" => match arg {
8567 JValue::String(s) => Ok(JValue::string(s.to_uppercase())),
8568 JValue::Null => Ok(JValue::Null),
8569 _ => Err(EvaluatorError::TypeError(
8570 "uppercase() requires a string argument".to_string(),
8571 )),
8572 },
8573 "lowercase" => match arg {
8574 JValue::String(s) => Ok(JValue::string(s.to_lowercase())),
8575 JValue::Null => Ok(JValue::Null),
8576 _ => Err(EvaluatorError::TypeError(
8577 "lowercase() requires a string argument".to_string(),
8578 )),
8579 },
8580 "trim" => match arg {
8581 JValue::String(s) => Ok(JValue::string(s.trim().to_string())),
8582 JValue::Null => Ok(JValue::Null),
8583 _ => Err(EvaluatorError::TypeError(
8584 "trim() requires a string argument".to_string(),
8585 )),
8586 },
8587 "length" => match arg {
8588 JValue::String(s) => Ok(JValue::Number(s.chars().count() as f64)),
8589 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
8590 JValue::Null => Ok(JValue::Null),
8591 _ => Err(EvaluatorError::TypeError(
8592 "length() requires a string or array argument".to_string(),
8593 )),
8594 },
8595 "sum" => match arg {
8596 JValue::Array(arr) => {
8597 let mut total = 0.0;
8598 for item in arr.iter() {
8599 match item {
8600 JValue::Number(n) => {
8601 total += *n;
8602 }
8603 _ => {
8604 return Err(EvaluatorError::TypeError(
8605 "sum() requires all array elements to be numbers".to_string(),
8606 ));
8607 }
8608 }
8609 }
8610 Ok(JValue::Number(total))
8611 }
8612 JValue::Number(n) => Ok(JValue::Number(*n)),
8613 JValue::Null => Ok(JValue::Null),
8614 _ => Err(EvaluatorError::TypeError(
8615 "sum() requires an array of numbers".to_string(),
8616 )),
8617 },
8618 "count" => {
8619 match arg {
8620 JValue::Array(arr) => Ok(JValue::Number(arr.len() as f64)),
8621 JValue::Null => Ok(JValue::Number(0.0)),
8622 _ => Ok(JValue::Number(1.0)), }
8624 }
8625 "max" => match arg {
8626 JValue::Array(arr) => {
8627 let mut max_val: Option<f64> = None;
8628 for item in arr.iter() {
8629 if let JValue::Number(n) = item {
8630 let f = *n;
8631 max_val = Some(max_val.map_or(f, |m| m.max(f)));
8632 }
8633 }
8634 max_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
8635 }
8636 JValue::Number(n) => Ok(JValue::Number(*n)),
8637 JValue::Null => Ok(JValue::Null),
8638 _ => Err(EvaluatorError::TypeError(
8639 "max() requires an array of numbers".to_string(),
8640 )),
8641 },
8642 "min" => match arg {
8643 JValue::Array(arr) => {
8644 let mut min_val: Option<f64> = None;
8645 for item in arr.iter() {
8646 if let JValue::Number(n) = item {
8647 let f = *n;
8648 min_val = Some(min_val.map_or(f, |m| m.min(f)));
8649 }
8650 }
8651 min_val.map_or(Ok(JValue::Null), |m| Ok(JValue::Number(m)))
8652 }
8653 JValue::Number(n) => Ok(JValue::Number(*n)),
8654 JValue::Null => Ok(JValue::Null),
8655 _ => Err(EvaluatorError::TypeError(
8656 "min() requires an array of numbers".to_string(),
8657 )),
8658 },
8659 "average" => match arg {
8660 JValue::Array(arr) => {
8661 let nums: Vec<f64> = arr.iter().filter_map(|v| v.as_f64()).collect();
8662 if nums.is_empty() {
8663 Ok(JValue::Null)
8664 } else {
8665 let avg = nums.iter().sum::<f64>() / nums.len() as f64;
8666 Ok(JValue::Number(avg))
8667 }
8668 }
8669 JValue::Number(n) => Ok(JValue::Number(*n)),
8670 JValue::Null => Ok(JValue::Null),
8671 _ => Err(EvaluatorError::TypeError(
8672 "average() requires an array of numbers".to_string(),
8673 )),
8674 },
8675 "append" => {
8676 if values.len() < 2 {
8678 return Err(EvaluatorError::EvaluationError(
8679 "append() requires 2 arguments".to_string(),
8680 ));
8681 }
8682 let first = &values[0];
8683 let second = &values[1];
8684
8685 let mut result = match first {
8687 JValue::Array(arr) => arr.to_vec(),
8688 JValue::Null => vec![],
8689 other => vec![other.clone()],
8690 };
8691
8692 match second {
8694 JValue::Array(arr) => result.extend(arr.iter().cloned()),
8695 JValue::Null => {}
8696 other => result.push(other.clone()),
8697 }
8698
8699 Ok(JValue::array(result))
8700 }
8701 "reverse" => match arg {
8702 JValue::Array(arr) => {
8703 let mut reversed = arr.to_vec();
8704 reversed.reverse();
8705 Ok(JValue::array(reversed))
8706 }
8707 JValue::Null => Ok(JValue::Null),
8708 _ => Err(EvaluatorError::TypeError(
8709 "reverse() requires an array".to_string(),
8710 )),
8711 },
8712 "keys" => match arg {
8713 JValue::Object(obj) => {
8714 let keys: Vec<JValue> = obj.keys().map(|k| JValue::string(k.clone())).collect();
8715 Ok(JValue::array(keys))
8716 }
8717 JValue::Null => Ok(JValue::Null),
8718 _ => Err(EvaluatorError::TypeError(
8719 "keys() requires an object".to_string(),
8720 )),
8721 },
8722
8723 _ => Err(EvaluatorError::ReferenceError(format!(
8725 "Built-in function {} cannot be called with values directly",
8726 name
8727 ))),
8728 }
8729 }
8730
8731 fn collect_descendants(&self, value: &JValue) -> Vec<JValue> {
8733 let mut descendants = Vec::new();
8734
8735 match value {
8736 JValue::Null => {
8737 return descendants;
8739 }
8740 JValue::Object(obj) => {
8741 descendants.push(value.clone());
8743
8744 for val in obj.values() {
8745 descendants.extend(self.collect_descendants(val));
8747 }
8748 }
8749 JValue::Array(arr) => {
8750 for val in arr.iter() {
8753 descendants.extend(self.collect_descendants(val));
8755 }
8756 }
8757 _ => {
8758 descendants.push(value.clone());
8760 }
8761 }
8762
8763 descendants
8764 }
8765
8766 fn evaluate_predicate(
8768 &mut self,
8769 current: &JValue,
8770 predicate: &AstNode,
8771 ) -> Result<JValue, EvaluatorError> {
8772 if matches!(predicate, AstNode::Boolean(true)) {
8775 return match current {
8776 JValue::Array(arr) => Ok(JValue::Array(arr.clone())),
8777 JValue::Null => Ok(JValue::Null),
8778 other => Ok(JValue::array(vec![other.clone()])),
8779 };
8780 }
8781
8782 match current {
8783 JValue::Array(_arr) => {
8784 if let AstNode::Number(n) = predicate {
8788 return self.array_index(current, &JValue::Number(*n));
8790 }
8791
8792 if Self::is_filter_predicate(predicate) {
8795 if let Some(compiled) = try_compile_expr(predicate) {
8797 let shape = _arr.first().and_then(build_shape_cache);
8798 let mut filtered = Vec::with_capacity(_arr.len());
8799 for item in _arr.iter() {
8800 let result = if let Some(ref s) = shape {
8801 eval_compiled_shaped(&compiled, item, None, s)?
8802 } else {
8803 eval_compiled(&compiled, item, None)?
8804 };
8805 if compiled_is_truthy(&result) {
8806 filtered.push(item.clone());
8807 }
8808 }
8809 return Ok(JValue::array(filtered));
8810 }
8811 let mut filtered = Vec::new();
8813 for item in _arr.iter() {
8814 let item_result = self.evaluate_internal(predicate, item)?;
8815 if self.is_truthy(&item_result) {
8816 filtered.push(item.clone());
8817 }
8818 }
8819 return Ok(JValue::array(filtered));
8820 }
8821
8822 match self.evaluate_internal(predicate, current) {
8826 Ok(JValue::Number(_)) => {
8827 let pred_result = self.evaluate_internal(predicate, current)?;
8829 return self.array_index(current, &pred_result);
8830 }
8831 Ok(JValue::Array(indices)) => {
8832 let has_non_numeric =
8835 indices.iter().any(|v| !matches!(v, JValue::Number(_)));
8836
8837 if has_non_numeric {
8838 return Ok(current.clone());
8840 }
8841
8842 let arr_len = _arr.len() as i64;
8844 let mut resolved_indices: Vec<i64> = indices
8845 .iter()
8846 .filter_map(|v| {
8847 if let JValue::Number(n) = v {
8848 let idx = *n as i64;
8849 let actual_idx = if idx < 0 { arr_len + idx } else { idx };
8851 if actual_idx >= 0 && actual_idx < arr_len {
8853 Some(actual_idx)
8854 } else {
8855 None
8856 }
8857 } else {
8858 None
8859 }
8860 })
8861 .collect();
8862
8863 resolved_indices.sort();
8865 resolved_indices.dedup();
8866
8867 let result: Vec<JValue> = resolved_indices
8869 .iter()
8870 .map(|&idx| _arr[idx as usize].clone())
8871 .collect();
8872
8873 return Ok(JValue::array(result));
8874 }
8875 Ok(_) => {
8876 }
8879 Err(_) => {
8880 }
8883 }
8884
8885 if let Some(compiled) = try_compile_expr(predicate) {
8887 let shape = _arr.first().and_then(build_shape_cache);
8888 let mut filtered = Vec::with_capacity(_arr.len());
8889 for item in _arr.iter() {
8890 let result = if let Some(ref s) = shape {
8891 eval_compiled_shaped(&compiled, item, None, s)?
8892 } else {
8893 eval_compiled(&compiled, item, None)?
8894 };
8895 if compiled_is_truthy(&result) {
8896 filtered.push(item.clone());
8897 }
8898 }
8899 return Ok(JValue::array(filtered));
8900 }
8901
8902 let mut filtered = Vec::new();
8904 for item in _arr.iter() {
8905 let item_result = self.evaluate_internal(predicate, item)?;
8906
8907 if self.is_truthy(&item_result) {
8909 filtered.push(item.clone());
8910 }
8911 }
8912
8913 Ok(JValue::array(filtered))
8914 }
8915 JValue::Object(obj) => {
8916 let pred_result = self.evaluate_internal(predicate, current)?;
8920
8921 if let JValue::String(key) = &pred_result {
8923 return Ok(obj.get(&**key).cloned().unwrap_or(JValue::Null));
8924 }
8925
8926 if self.is_truthy(&pred_result) {
8929 Ok(current.clone())
8930 } else {
8931 Ok(JValue::Undefined)
8932 }
8933 }
8934 _ => {
8935 if let AstNode::Number(n) = predicate {
8941 let idx = n.floor() as i64;
8943 if idx == 0 || idx == -1 {
8944 return Ok(current.clone());
8945 } else {
8946 return Ok(JValue::Undefined);
8947 }
8948 }
8949
8950 let pred_result = self.evaluate_internal(predicate, current)?;
8952
8953 if let JValue::Number(n) = &pred_result {
8954 let idx = n.floor() as i64;
8956 if idx == 0 || idx == -1 {
8957 return Ok(current.clone());
8958 } else {
8959 return Ok(JValue::Undefined);
8960 }
8961 }
8962
8963 if self.is_truthy(&pred_result) {
8967 Ok(current.clone())
8968 } else {
8969 Ok(JValue::Undefined)
8971 }
8972 }
8973 }
8974 }
8975
8976 fn evaluate_sort_term(
8979 &mut self,
8980 term_expr: &AstNode,
8981 element: &JValue,
8982 ) -> Result<JValue, EvaluatorError> {
8983 let actual_element = if let JValue::Object(obj) = element {
8985 if obj.get("__tuple__") == Some(&JValue::Bool(true)) {
8986 obj.get("@").cloned().unwrap_or(JValue::Null)
8987 } else {
8988 element.clone()
8989 }
8990 } else {
8991 element.clone()
8992 };
8993
8994 if let AstNode::Path { steps } = term_expr {
8996 if steps.len() == 1 && steps[0].stages.is_empty() {
8997 if let AstNode::Name(field_name) = &steps[0].node {
8998 if let JValue::Object(obj) = &actual_element {
9000 return match obj.get(field_name) {
9001 Some(val) => Ok(val.clone()), None => Ok(JValue::Undefined), };
9004 } else {
9005 return Ok(JValue::Undefined);
9007 }
9008 }
9009 }
9010 }
9011
9012 let result = self.evaluate_internal(term_expr, element)?;
9015
9016 if result.is_null() {
9021 return Ok(JValue::Undefined);
9022 }
9023
9024 Ok(result)
9025 }
9026
9027 fn evaluate_sort(
9029 &mut self,
9030 data: &JValue,
9031 terms: &[(AstNode, bool)],
9032 ) -> Result<JValue, EvaluatorError> {
9033 if data.is_null() {
9035 return Ok(JValue::Null);
9036 }
9037
9038 let array = match data {
9040 JValue::Array(arr) => arr.clone(),
9041 other => return Ok(other.clone()),
9042 };
9043
9044 if array.is_empty() {
9046 return Ok(JValue::Array(array));
9047 }
9048
9049 let mut indexed_array: Vec<(usize, Vec<JValue>)> = Vec::new();
9051
9052 for (idx, element) in array.iter().enumerate() {
9053 let mut sort_keys = Vec::new();
9054
9055 for (term_expr, _ascending) in terms {
9057 let saved_dollar = self.context.lookup("$").cloned();
9059
9060 self.context.bind("$".to_string(), element.clone());
9062
9063 let sort_value = self.evaluate_sort_term(term_expr, element)?;
9065
9066 if let Some(val) = saved_dollar {
9068 self.context.bind("$".to_string(), val);
9069 } else {
9070 self.context.unbind("$");
9071 }
9072
9073 sort_keys.push(sort_value);
9074 }
9075
9076 indexed_array.push((idx, sort_keys));
9077 }
9078
9079 for term_idx in 0..terms.len() {
9083 let mut first_valid_type: Option<&str> = None;
9084
9085 for (_idx, sort_keys) in &indexed_array {
9086 let sort_value = &sort_keys[term_idx];
9087
9088 if sort_value.is_undefined() {
9090 continue;
9091 }
9092
9093 let value_type = match sort_value {
9096 JValue::Number(_) => "number",
9097 JValue::String(_) => "string",
9098 JValue::Bool(_) => "boolean",
9099 JValue::Array(_) => "array",
9100 JValue::Object(_) => "object", JValue::Null => "null", _ => "unknown",
9103 };
9104
9105 if value_type != "number" && value_type != "string" {
9108 return Err(EvaluatorError::TypeError("T2008: The expressions within an order-by clause must evaluate to numeric or string values".to_string()));
9109 }
9110
9111 if let Some(first_type) = first_valid_type {
9113 if first_type != value_type {
9114 return Err(EvaluatorError::TypeError(format!(
9115 "T2007: Type mismatch when comparing values in order-by clause: {} and {}",
9116 first_type, value_type
9117 )));
9118 }
9119 } else {
9120 first_valid_type = Some(value_type);
9121 }
9122 }
9123 }
9124
9125 indexed_array.sort_by(|a, b| {
9127 for (i, (_term_expr, ascending)) in terms.iter().enumerate() {
9129 let left = &a.1[i];
9130 let right = &b.1[i];
9131
9132 let cmp = self.compare_values(left, right);
9133
9134 if cmp != std::cmp::Ordering::Equal {
9135 return if *ascending { cmp } else { cmp.reverse() };
9136 }
9137 }
9138
9139 a.0.cmp(&b.0)
9141 });
9142
9143 let sorted: Vec<JValue> = indexed_array
9145 .iter()
9146 .map(|(idx, _)| array[*idx].clone())
9147 .collect();
9148
9149 Ok(JValue::array(sorted))
9150 }
9151
9152 fn compare_values(&self, left: &JValue, right: &JValue) -> Ordering {
9154 let left_undef = left.is_undefined();
9156 let right_undef = right.is_undefined();
9157
9158 if left_undef && right_undef {
9159 return Ordering::Equal;
9160 }
9161 if left_undef {
9162 return Ordering::Greater; }
9164 if right_undef {
9165 return Ordering::Less;
9166 }
9167
9168 match (left, right) {
9169 (JValue::Null, JValue::Null) => Ordering::Equal,
9171 (JValue::Null, _) => Ordering::Greater,
9172 (_, JValue::Null) => Ordering::Less,
9173
9174 (JValue::Number(a), JValue::Number(b)) => {
9176 let a_f64 = *a;
9177 let b_f64 = *b;
9178 a_f64.partial_cmp(&b_f64).unwrap_or(Ordering::Equal)
9179 }
9180
9181 (JValue::String(a), JValue::String(b)) => a.cmp(b),
9183
9184 (JValue::Bool(a), JValue::Bool(b)) => a.cmp(b),
9186
9187 (JValue::Array(a), JValue::Array(b)) => {
9189 for (a_elem, b_elem) in a.iter().zip(b.iter()) {
9190 let cmp = self.compare_values(a_elem, b_elem);
9191 if cmp != Ordering::Equal {
9192 return cmp;
9193 }
9194 }
9195 a.len().cmp(&b.len())
9196 }
9197
9198 (JValue::Bool(_), JValue::Number(_)) => Ordering::Less,
9201 (JValue::Bool(_), JValue::String(_)) => Ordering::Less,
9202 (JValue::Bool(_), JValue::Array(_)) => Ordering::Less,
9203 (JValue::Bool(_), JValue::Object(_)) => Ordering::Less,
9204
9205 (JValue::Number(_), JValue::Bool(_)) => Ordering::Greater,
9206 (JValue::Number(_), JValue::String(_)) => Ordering::Less,
9207 (JValue::Number(_), JValue::Array(_)) => Ordering::Less,
9208 (JValue::Number(_), JValue::Object(_)) => Ordering::Less,
9209
9210 (JValue::String(_), JValue::Bool(_)) => Ordering::Greater,
9211 (JValue::String(_), JValue::Number(_)) => Ordering::Greater,
9212 (JValue::String(_), JValue::Array(_)) => Ordering::Less,
9213 (JValue::String(_), JValue::Object(_)) => Ordering::Less,
9214
9215 (JValue::Array(_), JValue::Bool(_)) => Ordering::Greater,
9216 (JValue::Array(_), JValue::Number(_)) => Ordering::Greater,
9217 (JValue::Array(_), JValue::String(_)) => Ordering::Greater,
9218 (JValue::Array(_), JValue::Object(_)) => Ordering::Less,
9219
9220 (JValue::Object(_), _) => Ordering::Greater,
9221 _ => Ordering::Equal,
9222 }
9223 }
9224
9225 fn is_truthy(&self, value: &JValue) -> bool {
9227 match value {
9228 JValue::Null | JValue::Undefined => false,
9229 JValue::Bool(b) => *b,
9230 JValue::Number(n) => *n != 0.0,
9231 JValue::String(s) => !s.is_empty(),
9232 JValue::Array(arr) => !arr.is_empty(),
9233 JValue::Object(obj) => !obj.is_empty(),
9234 _ => false,
9235 }
9236 }
9237
9238 fn is_truthy_for_default(&self, value: &JValue) -> bool {
9244 match value {
9245 JValue::Lambda { .. } | JValue::Builtin { .. } => false,
9247 JValue::Array(arr) => {
9249 if arr.is_empty() {
9250 return false;
9251 }
9252 arr.iter().any(|elem| self.is_truthy(elem))
9254 }
9255 _ => self.is_truthy(value),
9257 }
9258 }
9259
9260 fn unwrap_singleton(&self, value: JValue) -> JValue {
9263 match value {
9264 JValue::Array(ref arr) if arr.len() == 1 => arr[0].clone(),
9265 _ => value,
9266 }
9267 }
9268
9269 fn extract_lambda_ids(&self, value: &JValue) -> Vec<String> {
9273 match value {
9275 JValue::Number(_) | JValue::Bool(_) | JValue::String(_)
9276 | JValue::Null | JValue::Undefined | JValue::Regex { .. }
9277 | JValue::Builtin { .. } => return Vec::new(),
9278 _ => {}
9279 }
9280 let mut ids = Vec::new();
9281 self.collect_lambda_ids(value, &mut ids);
9282 ids
9283 }
9284
9285 fn collect_lambda_ids(&self, value: &JValue, ids: &mut Vec<String>) {
9286 match value {
9287 JValue::Lambda { lambda_id, .. } => {
9288 let id_str = lambda_id.to_string();
9289 if !ids.contains(&id_str) {
9290 ids.push(id_str);
9291 if let Some(stored) = self.context.lookup_lambda(lambda_id) {
9296 let env_values: Vec<JValue> =
9297 stored.captured_env.values().cloned().collect();
9298 for env_value in &env_values {
9299 self.collect_lambda_ids(env_value, ids);
9300 }
9301 }
9302 }
9303 }
9304 JValue::Object(map) => {
9305 for v in map.values() {
9307 self.collect_lambda_ids(v, ids);
9308 }
9309 }
9310 JValue::Array(arr) => {
9311 for v in arr.iter() {
9313 self.collect_lambda_ids(v, ids);
9314 }
9315 }
9316 _ => {}
9317 }
9318 }
9319
9320 fn equals(&self, left: &JValue, right: &JValue) -> bool {
9322 crate::functions::array::values_equal(left, right)
9323 }
9324
9325 fn add(
9327 &self,
9328 left: &JValue,
9329 right: &JValue,
9330 left_is_explicit_null: bool,
9331 right_is_explicit_null: bool,
9332 ) -> Result<JValue, EvaluatorError> {
9333 match (left, right) {
9334 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a + *b)),
9335 (JValue::Null, JValue::Number(_)) if left_is_explicit_null => {
9337 Err(EvaluatorError::TypeError(
9338 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9339 ))
9340 }
9341 (JValue::Number(_), JValue::Null) if right_is_explicit_null => {
9342 Err(EvaluatorError::TypeError(
9343 "T2002: The right side of the + operator must evaluate to a number".to_string(),
9344 ))
9345 }
9346 (JValue::Null, JValue::Null) if left_is_explicit_null || right_is_explicit_null => {
9347 Err(EvaluatorError::TypeError(
9348 "T2002: The left side of the + operator must evaluate to a number".to_string(),
9349 ))
9350 }
9351 (JValue::Null, JValue::Number(_)) | (JValue::Number(_), JValue::Null) => {
9353 Ok(JValue::Null)
9354 }
9355 (JValue::Bool(_), _) => Err(EvaluatorError::TypeError(
9357 "T2001: The left side of the '+' operator must evaluate to a number or a string"
9358 .to_string(),
9359 )),
9360 (_, JValue::Bool(_)) => Err(EvaluatorError::TypeError(
9361 "T2001: The right side of the '+' operator must evaluate to a number or a string"
9362 .to_string(),
9363 )),
9364 (JValue::Null, JValue::Null) => Ok(JValue::Null),
9366 _ => Err(EvaluatorError::TypeError(format!(
9367 "Cannot add {:?} and {:?}",
9368 left, right
9369 ))),
9370 }
9371 }
9372
9373 fn subtract(
9375 &self,
9376 left: &JValue,
9377 right: &JValue,
9378 left_is_explicit_null: bool,
9379 right_is_explicit_null: bool,
9380 ) -> Result<JValue, EvaluatorError> {
9381 match (left, right) {
9382 (JValue::Number(a), JValue::Number(b)) => Ok(JValue::Number(*a - *b)),
9383 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9385 "T2002: The left side of the - operator must evaluate to a number".to_string(),
9386 )),
9387 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9388 "T2002: The right side of the - operator must evaluate to a number".to_string(),
9389 )),
9390 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9392 _ => Err(EvaluatorError::TypeError(format!(
9393 "Cannot subtract {:?} and {:?}",
9394 left, right
9395 ))),
9396 }
9397 }
9398
9399 fn multiply(
9401 &self,
9402 left: &JValue,
9403 right: &JValue,
9404 left_is_explicit_null: bool,
9405 right_is_explicit_null: bool,
9406 ) -> Result<JValue, EvaluatorError> {
9407 match (left, right) {
9408 (JValue::Number(a), JValue::Number(b)) => {
9409 let result = *a * *b;
9410 if result.is_infinite() {
9412 return Err(EvaluatorError::EvaluationError(
9413 "D1001: Number out of range".to_string(),
9414 ));
9415 }
9416 Ok(JValue::Number(result))
9417 }
9418 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9420 "T2002: The left side of the * operator must evaluate to a number".to_string(),
9421 )),
9422 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9423 "T2002: The right side of the * operator must evaluate to a number".to_string(),
9424 )),
9425 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9427 _ => Err(EvaluatorError::TypeError(format!(
9428 "Cannot multiply {:?} and {:?}",
9429 left, right
9430 ))),
9431 }
9432 }
9433
9434 fn divide(
9436 &self,
9437 left: &JValue,
9438 right: &JValue,
9439 left_is_explicit_null: bool,
9440 right_is_explicit_null: bool,
9441 ) -> Result<JValue, EvaluatorError> {
9442 match (left, right) {
9443 (JValue::Number(a), JValue::Number(b)) => {
9444 let denominator = *b;
9445 if denominator == 0.0 {
9446 return Err(EvaluatorError::EvaluationError(
9447 "Division by zero".to_string(),
9448 ));
9449 }
9450 Ok(JValue::Number(*a / denominator))
9451 }
9452 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9454 "T2002: The left side of the / operator must evaluate to a number".to_string(),
9455 )),
9456 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9457 "T2002: The right side of the / operator must evaluate to a number".to_string(),
9458 )),
9459 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9461 _ => Err(EvaluatorError::TypeError(format!(
9462 "Cannot divide {:?} and {:?}",
9463 left, right
9464 ))),
9465 }
9466 }
9467
9468 fn modulo(
9470 &self,
9471 left: &JValue,
9472 right: &JValue,
9473 left_is_explicit_null: bool,
9474 right_is_explicit_null: bool,
9475 ) -> Result<JValue, EvaluatorError> {
9476 match (left, right) {
9477 (JValue::Number(a), JValue::Number(b)) => {
9478 let denominator = *b;
9479 if denominator == 0.0 {
9480 return Err(EvaluatorError::EvaluationError(
9481 "Division by zero".to_string(),
9482 ));
9483 }
9484 Ok(JValue::Number(*a % denominator))
9485 }
9486 (JValue::Null, _) if left_is_explicit_null => Err(EvaluatorError::TypeError(
9488 "T2002: The left side of the % operator must evaluate to a number".to_string(),
9489 )),
9490 (_, JValue::Null) if right_is_explicit_null => Err(EvaluatorError::TypeError(
9491 "T2002: The right side of the % operator must evaluate to a number".to_string(),
9492 )),
9493 (JValue::Null, _) | (_, JValue::Null) => Ok(JValue::Null),
9495 _ => Err(EvaluatorError::TypeError(format!(
9496 "Cannot compute modulo of {:?} and {:?}",
9497 left, right
9498 ))),
9499 }
9500 }
9501
9502 fn type_name(value: &JValue) -> &'static str {
9504 match value {
9505 JValue::Null => "null",
9506 JValue::Bool(_) => "boolean",
9507 JValue::Number(_) => "number",
9508 JValue::String(_) => "string",
9509 JValue::Array(_) => "array",
9510 JValue::Object(_) => "object",
9511 _ => "unknown",
9512 }
9513 }
9514
9515 fn ordered_compare(
9521 &self,
9522 left: &JValue,
9523 right: &JValue,
9524 left_is_explicit_null: bool,
9525 right_is_explicit_null: bool,
9526 op_symbol: &str,
9527 compare_nums: fn(f64, f64) -> bool,
9528 compare_strs: fn(&str, &str) -> bool,
9529 ) -> Result<JValue, EvaluatorError> {
9530 match (left, right) {
9531 (JValue::Number(a), JValue::Number(b)) => {
9532 Ok(JValue::Bool(compare_nums(*a, *b)))
9533 }
9534 (JValue::String(a), JValue::String(b)) => Ok(JValue::Bool(compare_strs(a, b))),
9535 (JValue::Null, JValue::Null) => Ok(JValue::Null),
9537 (JValue::Null, _) if left_is_explicit_null => {
9539 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9540 }
9541 (_, JValue::Null) if right_is_explicit_null => {
9542 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9543 }
9544 (JValue::Bool(_), JValue::Null) | (JValue::Null, JValue::Bool(_)) => {
9546 Err(EvaluatorError::EvaluationError("T2010: Type mismatch in comparison".to_string()))
9547 }
9548 (JValue::Number(_), JValue::Null) | (JValue::Null, JValue::Number(_)) |
9550 (JValue::String(_), JValue::Null) | (JValue::Null, JValue::String(_)) => {
9551 Ok(JValue::Null)
9552 }
9553 (JValue::String(_), JValue::Number(_)) | (JValue::Number(_), JValue::String(_)) => {
9555 Err(EvaluatorError::EvaluationError(format!(
9556 "T2009: The expressions on either side of operator \"{}\" must be of the same data type",
9557 op_symbol
9558 )))
9559 }
9560 (JValue::Bool(_), _) | (_, JValue::Bool(_)) => {
9562 Err(EvaluatorError::EvaluationError(format!(
9563 "T2010: Cannot compare {} and {}",
9564 Self::type_name(left), Self::type_name(right)
9565 )))
9566 }
9567 _ => Err(EvaluatorError::EvaluationError(format!(
9569 "T2010: Cannot compare {} and {}",
9570 Self::type_name(left), Self::type_name(right)
9571 ))),
9572 }
9573 }
9574
9575 fn less_than(
9577 &self,
9578 left: &JValue,
9579 right: &JValue,
9580 left_is_explicit_null: bool,
9581 right_is_explicit_null: bool,
9582 ) -> Result<JValue, EvaluatorError> {
9583 self.ordered_compare(
9584 left,
9585 right,
9586 left_is_explicit_null,
9587 right_is_explicit_null,
9588 "<",
9589 |a, b| a < b,
9590 |a, b| a < b,
9591 )
9592 }
9593
9594 fn less_than_or_equal(
9596 &self,
9597 left: &JValue,
9598 right: &JValue,
9599 left_is_explicit_null: bool,
9600 right_is_explicit_null: bool,
9601 ) -> Result<JValue, EvaluatorError> {
9602 self.ordered_compare(
9603 left,
9604 right,
9605 left_is_explicit_null,
9606 right_is_explicit_null,
9607 "<=",
9608 |a, b| a <= b,
9609 |a, b| a <= b,
9610 )
9611 }
9612
9613 fn greater_than(
9615 &self,
9616 left: &JValue,
9617 right: &JValue,
9618 left_is_explicit_null: bool,
9619 right_is_explicit_null: bool,
9620 ) -> Result<JValue, EvaluatorError> {
9621 self.ordered_compare(
9622 left,
9623 right,
9624 left_is_explicit_null,
9625 right_is_explicit_null,
9626 ">",
9627 |a, b| a > b,
9628 |a, b| a > b,
9629 )
9630 }
9631
9632 fn greater_than_or_equal(
9634 &self,
9635 left: &JValue,
9636 right: &JValue,
9637 left_is_explicit_null: bool,
9638 right_is_explicit_null: bool,
9639 ) -> Result<JValue, EvaluatorError> {
9640 self.ordered_compare(
9641 left,
9642 right,
9643 left_is_explicit_null,
9644 right_is_explicit_null,
9645 ">=",
9646 |a, b| a >= b,
9647 |a, b| a >= b,
9648 )
9649 }
9650
9651 fn value_to_concat_string(value: &JValue) -> Result<String, EvaluatorError> {
9653 match value {
9654 JValue::String(s) => Ok(s.to_string()),
9655 JValue::Null => Ok(String::new()),
9656 JValue::Number(_) | JValue::Bool(_) | JValue::Array(_) | JValue::Object(_) => {
9657 match crate::functions::string::string(value, None) {
9658 Ok(JValue::String(s)) => Ok(s.to_string()),
9659 Ok(JValue::Null) => Ok(String::new()),
9660 _ => Err(EvaluatorError::TypeError(
9661 "Cannot concatenate complex types".to_string(),
9662 )),
9663 }
9664 }
9665 _ => Ok(String::new()),
9666 }
9667 }
9668
9669 fn concatenate(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
9671 let left_str = Self::value_to_concat_string(left)?;
9672 let right_str = Self::value_to_concat_string(right)?;
9673 Ok(JValue::string(format!("{}{}", left_str, right_str)))
9674 }
9675
9676 fn range(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
9678 let start_f64 = match left {
9680 JValue::Number(n) => Some(*n),
9681 JValue::Null => None,
9682 _ => {
9683 return Err(EvaluatorError::EvaluationError(
9684 "T2003: Left operand of range operator must be a number".to_string(),
9685 ));
9686 }
9687 };
9688
9689 if let Some(val) = start_f64 {
9691 if val.fract() != 0.0 {
9692 return Err(EvaluatorError::EvaluationError(
9693 "T2003: Left operand of range operator must be an integer".to_string(),
9694 ));
9695 }
9696 }
9697
9698 let end_f64 = match right {
9700 JValue::Number(n) => Some(*n),
9701 JValue::Null => None,
9702 _ => {
9703 return Err(EvaluatorError::EvaluationError(
9704 "T2004: Right operand of range operator must be a number".to_string(),
9705 ));
9706 }
9707 };
9708
9709 if let Some(val) = end_f64 {
9711 if val.fract() != 0.0 {
9712 return Err(EvaluatorError::EvaluationError(
9713 "T2004: Right operand of range operator must be an integer".to_string(),
9714 ));
9715 }
9716 }
9717
9718 if start_f64.is_none() || end_f64.is_none() {
9720 return Ok(JValue::array(vec![]));
9721 }
9722
9723 let start = start_f64.unwrap() as i64;
9724 let end = end_f64.unwrap() as i64;
9725
9726 let size = if start <= end {
9728 (end - start + 1) as usize
9729 } else {
9730 0
9731 };
9732 if size > 10_000_000 {
9733 return Err(EvaluatorError::EvaluationError(
9734 "D2014: Range operator results in too many elements (> 10,000,000)".to_string(),
9735 ));
9736 }
9737
9738 let mut result = Vec::with_capacity(size);
9739 if start <= end {
9740 for i in start..=end {
9741 result.push(JValue::Number(i as f64));
9742 }
9743 }
9744 Ok(JValue::array(result))
9746 }
9747
9748 fn array_index(&self, array: &JValue, index: &JValue) -> Result<JValue, EvaluatorError> {
9751 match (array, index) {
9752 (JValue::Array(arr), JValue::Number(n)) => {
9753 let idx = *n as i64;
9754 let len = arr.len() as i64;
9755
9756 let actual_idx = if idx < 0 { len + idx } else { idx };
9758
9759 if actual_idx < 0 || actual_idx >= len {
9760 Ok(JValue::Null)
9761 } else {
9762 Ok(arr[actual_idx as usize].clone())
9763 }
9764 }
9765 _ => Err(EvaluatorError::TypeError(
9766 "Array indexing requires array and number".to_string(),
9767 )),
9768 }
9769 }
9770
9771 fn array_filter(
9774 &mut self,
9775 _lhs_node: &AstNode,
9776 rhs_node: &AstNode,
9777 array: &JValue,
9778 _original_data: &JValue,
9779 ) -> Result<JValue, EvaluatorError> {
9780 match array {
9781 JValue::Array(arr) => {
9782 let mut filtered = Vec::with_capacity(arr.len() / 2);
9784
9785 for item in arr.iter() {
9786 let predicate_result = self.evaluate_internal(rhs_node, item)?;
9789
9790 if self.is_truthy(&predicate_result) {
9792 filtered.push(item.clone());
9793 }
9794 }
9795
9796 Ok(JValue::array(filtered))
9797 }
9798 _ => Err(EvaluatorError::TypeError(
9799 "Array filtering requires an array".to_string(),
9800 )),
9801 }
9802 }
9803
9804 fn in_operator(&self, left: &JValue, right: &JValue) -> Result<JValue, EvaluatorError> {
9805 if left.is_null() || right.is_null() {
9808 return Ok(JValue::Bool(false));
9809 }
9810
9811 match right {
9812 JValue::Array(arr) => Ok(JValue::Bool(arr.iter().any(|v| self.equals(left, v)))),
9813 JValue::Object(obj) => {
9814 if let JValue::String(key) = left {
9815 Ok(JValue::Bool(obj.contains_key(&**key)))
9816 } else {
9817 Ok(JValue::Bool(false))
9818 }
9819 }
9820 other => Ok(JValue::Bool(self.equals(left, other))),
9823 }
9824 }
9825
9826 fn create_partial_application(
9830 &mut self,
9831 name: &str,
9832 args: &[AstNode],
9833 is_builtin: bool,
9834 data: &JValue,
9835 ) -> Result<JValue, EvaluatorError> {
9836 let is_lambda = self.context.lookup_lambda(name).is_some()
9838 || (self
9839 .context
9840 .lookup(name)
9841 .map(|v| matches!(v, JValue::Lambda { .. }))
9842 .unwrap_or(false));
9843
9844 if !is_lambda && !is_builtin {
9847 if self.is_builtin_function(name) {
9849 return Err(EvaluatorError::EvaluationError(format!(
9850 "T1007: Attempted to partially apply a non-function. Did you mean ${}?",
9851 name
9852 )));
9853 }
9854 return Err(EvaluatorError::EvaluationError(
9855 "T1008: Attempted to partially apply a non-function".to_string(),
9856 ));
9857 }
9858
9859 let mut bound_args: Vec<(usize, JValue)> = Vec::new();
9861 let mut placeholder_positions: Vec<usize> = Vec::new();
9862
9863 for (i, arg) in args.iter().enumerate() {
9864 if matches!(arg, AstNode::Placeholder) {
9865 placeholder_positions.push(i);
9866 } else {
9867 let value = self.evaluate_internal(arg, data)?;
9868 bound_args.push((i, value));
9869 }
9870 }
9871
9872 let param_names: Vec<String> = placeholder_positions
9874 .iter()
9875 .enumerate()
9876 .map(|(i, _)| format!("__p{}", i))
9877 .collect();
9878
9879 let partial_id = format!(
9882 "__partial_{}_{}_{}",
9883 name,
9884 placeholder_positions.len(),
9885 bound_args.len()
9886 );
9887
9888 let stored_lambda = StoredLambda {
9891 params: param_names.clone(),
9892 body: AstNode::String(format!(
9893 "__partial_call:{}:{}:{}",
9894 name,
9895 is_builtin,
9896 args.len()
9897 )),
9898 compiled_body: None, signature: None,
9900 captured_env: {
9901 let mut env = self.capture_current_environment();
9902 for (pos, value) in &bound_args {
9904 env.insert(format!("__bound_arg_{}", pos), value.clone());
9905 }
9906 env.insert(
9908 "__placeholder_positions".to_string(),
9909 JValue::array(
9910 placeholder_positions
9911 .iter()
9912 .map(|p| JValue::Number(*p as f64))
9913 .collect::<Vec<_>>(),
9914 ),
9915 );
9916 env.insert(
9918 "__total_args".to_string(),
9919 JValue::Number(args.len() as f64),
9920 );
9921 env
9922 },
9923 captured_data: Some(data.clone()),
9924 thunk: false,
9925 };
9926
9927 self.context.bind_lambda(partial_id.clone(), stored_lambda);
9928
9929 let lambda_obj = JValue::lambda(
9931 partial_id.as_str(),
9932 param_names,
9933 Some(name.to_string()),
9934 None::<String>,
9935 );
9936
9937 Ok(lambda_obj)
9938 }
9939}
9940
9941impl Default for Evaluator {
9942 fn default() -> Self {
9943 Self::new()
9944 }
9945}
9946
9947#[cfg(test)]
9948mod tests {
9949 use super::*;
9950 use crate::ast::{BinaryOp, UnaryOp};
9951
9952 #[test]
9953 fn test_evaluate_literals() {
9954 let mut evaluator = Evaluator::new();
9955 let data = JValue::Null;
9956
9957 let result = evaluator
9959 .evaluate(&AstNode::string("hello"), &data)
9960 .unwrap();
9961 assert_eq!(result, JValue::string("hello"));
9962
9963 let result = evaluator.evaluate(&AstNode::number(42.0), &data).unwrap();
9965 assert_eq!(result, JValue::from(42i64));
9966
9967 let result = evaluator.evaluate(&AstNode::boolean(true), &data).unwrap();
9969 assert_eq!(result, JValue::Bool(true));
9970
9971 let result = evaluator.evaluate(&AstNode::null(), &data).unwrap();
9973 assert_eq!(result, JValue::Null);
9974 }
9975
9976 #[test]
9977 fn test_evaluate_variables() {
9978 let mut evaluator = Evaluator::new();
9979 let data = JValue::Null;
9980
9981 evaluator
9983 .context
9984 .bind("x".to_string(), JValue::from(100i64));
9985
9986 let result = evaluator.evaluate(&AstNode::variable("x"), &data).unwrap();
9988 assert_eq!(result, JValue::from(100i64));
9989
9990 let result = evaluator
9992 .evaluate(&AstNode::variable("undefined"), &data)
9993 .unwrap();
9994 assert_eq!(result, JValue::Null);
9995 }
9996
9997 #[test]
9998 fn test_evaluate_path() {
9999 let mut evaluator = Evaluator::new();
10000 let data = JValue::from(serde_json::json!({
10001 "foo": {
10002 "bar": {
10003 "baz": 42
10004 }
10005 }
10006 }));
10007 let path = AstNode::Path {
10009 steps: vec![PathStep::new(AstNode::Name("foo".to_string()))],
10010 };
10011 let result = evaluator.evaluate(&path, &data).unwrap();
10012 assert_eq!(
10013 result,
10014 JValue::from(serde_json::json!({"bar": {"baz": 42}}))
10015 );
10016
10017 let path = AstNode::Path {
10019 steps: vec![
10020 PathStep::new(AstNode::Name("foo".to_string())),
10021 PathStep::new(AstNode::Name("bar".to_string())),
10022 PathStep::new(AstNode::Name("baz".to_string())),
10023 ],
10024 };
10025 let result = evaluator.evaluate(&path, &data).unwrap();
10026 assert_eq!(result, JValue::from(42i64));
10027
10028 let path = AstNode::Path {
10030 steps: vec![PathStep::new(AstNode::Name("missing".to_string()))],
10031 };
10032 let result = evaluator.evaluate(&path, &data).unwrap();
10033 assert_eq!(result, JValue::Null);
10034 }
10035
10036 #[test]
10037 fn test_arithmetic_operations() {
10038 let mut evaluator = Evaluator::new();
10039 let data = JValue::Null;
10040
10041 let expr = AstNode::Binary {
10043 op: BinaryOp::Add,
10044 lhs: Box::new(AstNode::number(10.0)),
10045 rhs: Box::new(AstNode::number(5.0)),
10046 };
10047 let result = evaluator.evaluate(&expr, &data).unwrap();
10048 assert_eq!(result, JValue::Number(15.0));
10049
10050 let expr = AstNode::Binary {
10052 op: BinaryOp::Subtract,
10053 lhs: Box::new(AstNode::number(10.0)),
10054 rhs: Box::new(AstNode::number(5.0)),
10055 };
10056 let result = evaluator.evaluate(&expr, &data).unwrap();
10057 assert_eq!(result, JValue::Number(5.0));
10058
10059 let expr = AstNode::Binary {
10061 op: BinaryOp::Multiply,
10062 lhs: Box::new(AstNode::number(10.0)),
10063 rhs: Box::new(AstNode::number(5.0)),
10064 };
10065 let result = evaluator.evaluate(&expr, &data).unwrap();
10066 assert_eq!(result, JValue::Number(50.0));
10067
10068 let expr = AstNode::Binary {
10070 op: BinaryOp::Divide,
10071 lhs: Box::new(AstNode::number(10.0)),
10072 rhs: Box::new(AstNode::number(5.0)),
10073 };
10074 let result = evaluator.evaluate(&expr, &data).unwrap();
10075 assert_eq!(result, JValue::Number(2.0));
10076
10077 let expr = AstNode::Binary {
10079 op: BinaryOp::Modulo,
10080 lhs: Box::new(AstNode::number(10.0)),
10081 rhs: Box::new(AstNode::number(3.0)),
10082 };
10083 let result = evaluator.evaluate(&expr, &data).unwrap();
10084 assert_eq!(result, JValue::Number(1.0));
10085 }
10086
10087 #[test]
10088 fn test_division_by_zero() {
10089 let mut evaluator = Evaluator::new();
10090 let data = JValue::Null;
10091
10092 let expr = AstNode::Binary {
10093 op: BinaryOp::Divide,
10094 lhs: Box::new(AstNode::number(10.0)),
10095 rhs: Box::new(AstNode::number(0.0)),
10096 };
10097 let result = evaluator.evaluate(&expr, &data);
10098 assert!(result.is_err());
10099 }
10100
10101 #[test]
10102 fn test_comparison_operations() {
10103 let mut evaluator = Evaluator::new();
10104 let data = JValue::Null;
10105
10106 let expr = AstNode::Binary {
10108 op: BinaryOp::Equal,
10109 lhs: Box::new(AstNode::number(5.0)),
10110 rhs: Box::new(AstNode::number(5.0)),
10111 };
10112 assert_eq!(
10113 evaluator.evaluate(&expr, &data).unwrap(),
10114 JValue::Bool(true)
10115 );
10116
10117 let expr = AstNode::Binary {
10119 op: BinaryOp::NotEqual,
10120 lhs: Box::new(AstNode::number(5.0)),
10121 rhs: Box::new(AstNode::number(3.0)),
10122 };
10123 assert_eq!(
10124 evaluator.evaluate(&expr, &data).unwrap(),
10125 JValue::Bool(true)
10126 );
10127
10128 let expr = AstNode::Binary {
10130 op: BinaryOp::LessThan,
10131 lhs: Box::new(AstNode::number(3.0)),
10132 rhs: Box::new(AstNode::number(5.0)),
10133 };
10134 assert_eq!(
10135 evaluator.evaluate(&expr, &data).unwrap(),
10136 JValue::Bool(true)
10137 );
10138
10139 let expr = AstNode::Binary {
10141 op: BinaryOp::GreaterThan,
10142 lhs: Box::new(AstNode::number(5.0)),
10143 rhs: Box::new(AstNode::number(3.0)),
10144 };
10145 assert_eq!(
10146 evaluator.evaluate(&expr, &data).unwrap(),
10147 JValue::Bool(true)
10148 );
10149 }
10150
10151 #[test]
10152 fn test_logical_operations() {
10153 let mut evaluator = Evaluator::new();
10154 let data = JValue::Null;
10155
10156 let expr = AstNode::Binary {
10158 op: BinaryOp::And,
10159 lhs: Box::new(AstNode::boolean(true)),
10160 rhs: Box::new(AstNode::boolean(true)),
10161 };
10162 assert_eq!(
10163 evaluator.evaluate(&expr, &data).unwrap(),
10164 JValue::Bool(true)
10165 );
10166
10167 let expr = AstNode::Binary {
10169 op: BinaryOp::And,
10170 lhs: Box::new(AstNode::boolean(false)),
10171 rhs: Box::new(AstNode::boolean(true)),
10172 };
10173 assert_eq!(
10174 evaluator.evaluate(&expr, &data).unwrap(),
10175 JValue::Bool(false)
10176 );
10177
10178 let expr = AstNode::Binary {
10180 op: BinaryOp::Or,
10181 lhs: Box::new(AstNode::boolean(true)),
10182 rhs: Box::new(AstNode::boolean(false)),
10183 };
10184 assert_eq!(
10185 evaluator.evaluate(&expr, &data).unwrap(),
10186 JValue::Bool(true)
10187 );
10188
10189 let expr = AstNode::Binary {
10191 op: BinaryOp::Or,
10192 lhs: Box::new(AstNode::boolean(false)),
10193 rhs: Box::new(AstNode::boolean(false)),
10194 };
10195 assert_eq!(
10196 evaluator.evaluate(&expr, &data).unwrap(),
10197 JValue::Bool(false)
10198 );
10199 }
10200
10201 #[test]
10202 fn test_string_concatenation() {
10203 let mut evaluator = Evaluator::new();
10204 let data = JValue::Null;
10205
10206 let expr = AstNode::Binary {
10207 op: BinaryOp::Concatenate,
10208 lhs: Box::new(AstNode::string("Hello")),
10209 rhs: Box::new(AstNode::string(" World")),
10210 };
10211 let result = evaluator.evaluate(&expr, &data).unwrap();
10212 assert_eq!(result, JValue::string("Hello World"));
10213 }
10214
10215 #[test]
10216 fn test_range_operator() {
10217 let mut evaluator = Evaluator::new();
10218 let data = JValue::Null;
10219
10220 let expr = AstNode::Binary {
10222 op: BinaryOp::Range,
10223 lhs: Box::new(AstNode::number(1.0)),
10224 rhs: Box::new(AstNode::number(5.0)),
10225 };
10226 let result = evaluator.evaluate(&expr, &data).unwrap();
10227 assert_eq!(
10228 result,
10229 JValue::array(vec![
10230 JValue::Number(1.0),
10231 JValue::Number(2.0),
10232 JValue::Number(3.0),
10233 JValue::Number(4.0),
10234 JValue::Number(5.0)
10235 ])
10236 );
10237
10238 let expr = AstNode::Binary {
10240 op: BinaryOp::Range,
10241 lhs: Box::new(AstNode::number(5.0)),
10242 rhs: Box::new(AstNode::number(1.0)),
10243 };
10244 let result = evaluator.evaluate(&expr, &data).unwrap();
10245 assert_eq!(result, JValue::array(vec![]));
10246 }
10247
10248 #[test]
10249 fn test_in_operator() {
10250 let mut evaluator = Evaluator::new();
10251 let data = JValue::Null;
10252
10253 let expr = AstNode::Binary {
10255 op: BinaryOp::In,
10256 lhs: Box::new(AstNode::number(3.0)),
10257 rhs: Box::new(AstNode::Array(vec![
10258 AstNode::number(1.0),
10259 AstNode::number(2.0),
10260 AstNode::number(3.0),
10261 ])),
10262 };
10263 let result = evaluator.evaluate(&expr, &data).unwrap();
10264 assert_eq!(result, JValue::Bool(true));
10265
10266 let expr = AstNode::Binary {
10268 op: BinaryOp::In,
10269 lhs: Box::new(AstNode::number(5.0)),
10270 rhs: Box::new(AstNode::Array(vec![
10271 AstNode::number(1.0),
10272 AstNode::number(2.0),
10273 AstNode::number(3.0),
10274 ])),
10275 };
10276 let result = evaluator.evaluate(&expr, &data).unwrap();
10277 assert_eq!(result, JValue::Bool(false));
10278 }
10279
10280 #[test]
10281 fn test_unary_operations() {
10282 let mut evaluator = Evaluator::new();
10283 let data = JValue::Null;
10284
10285 let expr = AstNode::Unary {
10287 op: UnaryOp::Negate,
10288 operand: Box::new(AstNode::number(5.0)),
10289 };
10290 let result = evaluator.evaluate(&expr, &data).unwrap();
10291 assert_eq!(result, JValue::Number(-5.0));
10292
10293 let expr = AstNode::Unary {
10295 op: UnaryOp::Not,
10296 operand: Box::new(AstNode::boolean(true)),
10297 };
10298 let result = evaluator.evaluate(&expr, &data).unwrap();
10299 assert_eq!(result, JValue::Bool(false));
10300 }
10301
10302 #[test]
10303 fn test_array_construction() {
10304 let mut evaluator = Evaluator::new();
10305 let data = JValue::Null;
10306
10307 let expr = AstNode::Array(vec![
10308 AstNode::number(1.0),
10309 AstNode::number(2.0),
10310 AstNode::number(3.0),
10311 ]);
10312 let result = evaluator.evaluate(&expr, &data).unwrap();
10313 assert_eq!(result, JValue::from(serde_json::json!([1, 2, 3])));
10315 }
10316
10317 #[test]
10318 fn test_object_construction() {
10319 let mut evaluator = Evaluator::new();
10320 let data = JValue::Null;
10321
10322 let expr = AstNode::Object(vec![
10323 (AstNode::string("name"), AstNode::string("Alice")),
10324 (AstNode::string("age"), AstNode::number(30.0)),
10325 ]);
10326 let result = evaluator.evaluate(&expr, &data).unwrap();
10327 let mut expected = IndexMap::new();
10329 expected.insert("name".to_string(), JValue::string("Alice"));
10330 expected.insert("age".to_string(), JValue::Number(30.0));
10331 assert_eq!(result, JValue::object(expected));
10332 }
10333
10334 #[test]
10335 fn test_conditional() {
10336 let mut evaluator = Evaluator::new();
10337 let data = JValue::Null;
10338
10339 let expr = AstNode::Conditional {
10341 condition: Box::new(AstNode::boolean(true)),
10342 then_branch: Box::new(AstNode::string("yes")),
10343 else_branch: Some(Box::new(AstNode::string("no"))),
10344 };
10345 let result = evaluator.evaluate(&expr, &data).unwrap();
10346 assert_eq!(result, JValue::string("yes"));
10347
10348 let expr = AstNode::Conditional {
10350 condition: Box::new(AstNode::boolean(false)),
10351 then_branch: Box::new(AstNode::string("yes")),
10352 else_branch: Some(Box::new(AstNode::string("no"))),
10353 };
10354 let result = evaluator.evaluate(&expr, &data).unwrap();
10355 assert_eq!(result, JValue::string("no"));
10356
10357 let expr = AstNode::Conditional {
10359 condition: Box::new(AstNode::boolean(false)),
10360 then_branch: Box::new(AstNode::string("yes")),
10361 else_branch: None,
10362 };
10363 let result = evaluator.evaluate(&expr, &data).unwrap();
10364 assert_eq!(result, JValue::Undefined);
10365 }
10366
10367 #[test]
10368 fn test_block_expression() {
10369 let mut evaluator = Evaluator::new();
10370 let data = JValue::Null;
10371
10372 let expr = AstNode::Block(vec![
10373 AstNode::number(1.0),
10374 AstNode::number(2.0),
10375 AstNode::number(3.0),
10376 ]);
10377 let result = evaluator.evaluate(&expr, &data).unwrap();
10378 assert_eq!(result, JValue::from(3i64));
10380 }
10381
10382 #[test]
10383 fn test_function_calls() {
10384 let mut evaluator = Evaluator::new();
10385 let data = JValue::Null;
10386
10387 let expr = AstNode::Function {
10389 name: "uppercase".to_string(),
10390 args: vec![AstNode::string("hello")],
10391 is_builtin: true,
10392 };
10393 let result = evaluator.evaluate(&expr, &data).unwrap();
10394 assert_eq!(result, JValue::string("HELLO"));
10395
10396 let expr = AstNode::Function {
10398 name: "lowercase".to_string(),
10399 args: vec![AstNode::string("HELLO")],
10400 is_builtin: true,
10401 };
10402 let result = evaluator.evaluate(&expr, &data).unwrap();
10403 assert_eq!(result, JValue::string("hello"));
10404
10405 let expr = AstNode::Function {
10407 name: "length".to_string(),
10408 args: vec![AstNode::string("hello")],
10409 is_builtin: true,
10410 };
10411 let result = evaluator.evaluate(&expr, &data).unwrap();
10412 assert_eq!(result, JValue::from(5i64));
10413
10414 let expr = AstNode::Function {
10416 name: "sum".to_string(),
10417 args: vec![AstNode::Array(vec![
10418 AstNode::number(1.0),
10419 AstNode::number(2.0),
10420 AstNode::number(3.0),
10421 ])],
10422 is_builtin: true,
10423 };
10424 let result = evaluator.evaluate(&expr, &data).unwrap();
10425 assert_eq!(result, JValue::Number(6.0));
10426
10427 let expr = AstNode::Function {
10429 name: "count".to_string(),
10430 args: vec![AstNode::Array(vec![
10431 AstNode::number(1.0),
10432 AstNode::number(2.0),
10433 AstNode::number(3.0),
10434 ])],
10435 is_builtin: true,
10436 };
10437 let result = evaluator.evaluate(&expr, &data).unwrap();
10438 assert_eq!(result, JValue::from(3i64));
10439 }
10440
10441 #[test]
10442 fn test_complex_nested_data() {
10443 let mut evaluator = Evaluator::new();
10444 let data = JValue::from(serde_json::json!({
10445 "users": [
10446 {"name": "Alice", "age": 30},
10447 {"name": "Bob", "age": 25},
10448 {"name": "Charlie", "age": 35}
10449 ],
10450 "metadata": {
10451 "total": 3,
10452 "version": "1.0"
10453 }
10454 }));
10455 let path = AstNode::Path {
10457 steps: vec![
10458 PathStep::new(AstNode::Name("metadata".to_string())),
10459 PathStep::new(AstNode::Name("version".to_string())),
10460 ],
10461 };
10462 let result = evaluator.evaluate(&path, &data).unwrap();
10463 assert_eq!(result, JValue::string("1.0"));
10464 }
10465
10466 #[test]
10467 fn test_error_handling() {
10468 let mut evaluator = Evaluator::new();
10469 let data = JValue::Null;
10470
10471 let expr = AstNode::Binary {
10473 op: BinaryOp::Add,
10474 lhs: Box::new(AstNode::string("hello")),
10475 rhs: Box::new(AstNode::number(5.0)),
10476 };
10477 let result = evaluator.evaluate(&expr, &data);
10478 assert!(result.is_err());
10479
10480 let expr = AstNode::Function {
10482 name: "undefined_function".to_string(),
10483 args: vec![],
10484 is_builtin: false,
10485 };
10486 let result = evaluator.evaluate(&expr, &data);
10487 assert!(result.is_err());
10488 }
10489
10490 #[test]
10491 fn test_truthiness() {
10492 let evaluator = Evaluator::new();
10493
10494 assert!(!evaluator.is_truthy(&JValue::Null));
10495 assert!(!evaluator.is_truthy(&JValue::Bool(false)));
10496 assert!(evaluator.is_truthy(&JValue::Bool(true)));
10497 assert!(!evaluator.is_truthy(&JValue::from(0i64)));
10498 assert!(evaluator.is_truthy(&JValue::from(1i64)));
10499 assert!(!evaluator.is_truthy(&JValue::string("")));
10500 assert!(evaluator.is_truthy(&JValue::string("hello")));
10501 assert!(!evaluator.is_truthy(&JValue::array(vec![])));
10502 assert!(evaluator.is_truthy(&JValue::from(serde_json::json!([1, 2, 3]))));
10503 }
10504
10505 #[test]
10506 fn test_integration_with_parser() {
10507 use crate::parser::parse;
10508
10509 let mut evaluator = Evaluator::new();
10510 let data = JValue::from(serde_json::json!({
10511 "price": 10,
10512 "quantity": 5
10513 }));
10514 let ast = parse("price").unwrap();
10516 let result = evaluator.evaluate(&ast, &data).unwrap();
10517 assert_eq!(result, JValue::from(10i64));
10518
10519 let ast = parse("price * quantity").unwrap();
10521 let result = evaluator.evaluate(&ast, &data).unwrap();
10522 assert_eq!(result, JValue::Number(50.0));
10524
10525 let ast = parse("price > 5").unwrap();
10527 let result = evaluator.evaluate(&ast, &data).unwrap();
10528 assert_eq!(result, JValue::Bool(true));
10529 }
10530
10531 #[test]
10532 fn test_evaluate_dollar_function_uppercase() {
10533 use crate::parser::parse;
10534
10535 let mut evaluator = Evaluator::new();
10536 let ast = parse(r#"$uppercase("hello")"#).unwrap();
10537 let empty = JValue::object(IndexMap::new());
10538 let result = evaluator.evaluate(&ast, &empty).unwrap();
10539 assert_eq!(result, JValue::string("HELLO"));
10540 }
10541
10542 #[test]
10543 fn test_evaluate_dollar_function_sum() {
10544 use crate::parser::parse;
10545
10546 let mut evaluator = Evaluator::new();
10547 let ast = parse("$sum([1, 2, 3, 4, 5])").unwrap();
10548 let empty = JValue::object(IndexMap::new());
10549 let result = evaluator.evaluate(&ast, &empty).unwrap();
10550 assert_eq!(result, JValue::Number(15.0));
10551 }
10552
10553 #[test]
10554 fn test_evaluate_nested_dollar_functions() {
10555 use crate::parser::parse;
10556
10557 let mut evaluator = Evaluator::new();
10558 let ast = parse(r#"$length($lowercase("HELLO"))"#).unwrap();
10559 let empty = JValue::object(IndexMap::new());
10560 let result = evaluator.evaluate(&ast, &empty).unwrap();
10561 assert_eq!(result, JValue::Number(5.0));
10563 }
10564
10565 #[test]
10566 fn test_array_mapping() {
10567 use crate::parser::parse;
10568
10569 let mut evaluator = Evaluator::new();
10570 let data: JValue = serde_json::from_str(
10571 r#"{
10572 "products": [
10573 {"id": 1, "name": "Laptop", "price": 999.99},
10574 {"id": 2, "name": "Mouse", "price": 29.99},
10575 {"id": 3, "name": "Keyboard", "price": 79.99}
10576 ]
10577 }"#,
10578 )
10579 .map(|v: serde_json::Value| JValue::from(v))
10580 .unwrap();
10581
10582 let ast = parse("products.name").unwrap();
10584 let result = evaluator.evaluate(&ast, &data).unwrap();
10585 assert_eq!(
10586 result,
10587 JValue::array(vec![
10588 JValue::string("Laptop"),
10589 JValue::string("Mouse"),
10590 JValue::string("Keyboard")
10591 ])
10592 );
10593
10594 let ast = parse("products.price").unwrap();
10596 let result = evaluator.evaluate(&ast, &data).unwrap();
10597 assert_eq!(
10598 result,
10599 JValue::array(vec![
10600 JValue::Number(999.99),
10601 JValue::Number(29.99),
10602 JValue::Number(79.99)
10603 ])
10604 );
10605
10606 let ast = parse("$sum(products.price)").unwrap();
10608 let result = evaluator.evaluate(&ast, &data).unwrap();
10609 assert_eq!(result, JValue::Number(1109.97));
10610 }
10611
10612 #[test]
10613 fn test_empty_brackets() {
10614 use crate::parser::parse;
10615
10616 let mut evaluator = Evaluator::new();
10617
10618 let data: JValue = JValue::from(serde_json::json!({"foo": "bar"}));
10620 let ast = parse("foo[]").unwrap();
10621 let result = evaluator.evaluate(&ast, &data).unwrap();
10622 assert_eq!(
10623 result,
10624 JValue::array(vec![JValue::string("bar")]),
10625 "Empty brackets should wrap value in array"
10626 );
10627
10628 let data2: JValue = JValue::from(serde_json::json!({"arr": [1, 2, 3]}));
10630 let ast2 = parse("arr[]").unwrap();
10631 let result2 = evaluator.evaluate(&ast2, &data2).unwrap();
10632 assert_eq!(
10633 result2,
10634 JValue::array(vec![
10635 JValue::Number(1.0),
10636 JValue::Number(2.0),
10637 JValue::Number(3.0)
10638 ]),
10639 "Empty brackets should preserve array"
10640 );
10641 }
10642}