1#[cfg(test)]
3mod late_binding_tests {
4 use crate::environment::Environment;
5 use crate::expressions::evaluate_pairs;
6 use crate::heap::Heap;
7 use crate::parser::{Rule, get_pairs};
8 use crate::values::Value;
9 use std::cell::RefCell;
10 use std::rc::Rc;
11
12 fn parse_and_evaluate(
13 code: &str,
14 heap: Option<Rc<RefCell<Heap>>>,
15 bindings: Option<Rc<Environment>>,
16 ) -> Result<Value, crate::error::RuntimeError> {
17 let pairs = get_pairs(code).map_err(|e| crate::error::RuntimeError::new(e.to_string()))?;
18
19 let heap = heap.unwrap_or_else(|| Rc::new(RefCell::new(Heap::new())));
20 let bindings = bindings.unwrap_or_else(|| Rc::new(Environment::new()));
21
22 let mut result = Value::Null;
23 for pair in pairs {
24 match pair.as_rule() {
25 Rule::statement => {
26 if let Some(inner_pair) = pair.into_inner().next() {
27 match inner_pair.as_rule() {
28 Rule::expression | Rule::assignment => {
29 result = evaluate_pairs(
30 inner_pair.into_inner(),
31 Rc::clone(&heap),
32 Rc::clone(&bindings),
33 0,
34 code,
35 )?;
36 }
37 _ => {}
38 }
39 }
40 }
41 Rule::EOI => {}
42 _ => {}
43 }
44 }
45 Ok(result)
46 }
47
48 #[test]
49 fn test_late_binding_simple() {
50 let heap = Rc::new(RefCell::new(Heap::new()));
52 let bindings = Rc::new(Environment::new());
53
54 let _ = parse_and_evaluate(
56 "f = x => g(x)",
57 Some(Rc::clone(&heap)),
58 Some(Rc::clone(&bindings)),
59 )
60 .unwrap();
61
62 let _ = parse_and_evaluate(
64 "g = x => x * 2",
65 Some(Rc::clone(&heap)),
66 Some(Rc::clone(&bindings)),
67 )
68 .unwrap();
69
70 let result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
72
73 assert_eq!(result, Value::Number(10.0));
74 }
75
76 #[test]
77 fn test_late_binding_with_closure() {
78 let heap = Rc::new(RefCell::new(Heap::new()));
80 let bindings = Rc::new(Environment::new());
81
82 let _ = parse_and_evaluate("y = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
84 .unwrap();
85
86 let _ = parse_and_evaluate(
88 "f = x => x + y",
89 Some(Rc::clone(&heap)),
90 Some(Rc::clone(&bindings)),
91 )
92 .unwrap();
93
94 let _ = parse_and_evaluate(
96 "y2 = 20",
97 Some(Rc::clone(&heap)),
98 Some(Rc::clone(&bindings)),
99 )
100 .unwrap();
101
102 let result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
104
105 assert_eq!(result, Value::Number(15.0)); }
107
108 #[test]
109 fn test_late_binding_chain() {
110 let heap = Rc::new(RefCell::new(Heap::new()));
112 let bindings = Rc::new(Environment::new());
113
114 let _ = parse_and_evaluate(
116 "f = x => g(x) + 1",
117 Some(Rc::clone(&heap)),
118 Some(Rc::clone(&bindings)),
119 )
120 .unwrap();
121
122 let _ = parse_and_evaluate(
124 "g = x => h(x) * 2",
125 Some(Rc::clone(&heap)),
126 Some(Rc::clone(&bindings)),
127 )
128 .unwrap();
129
130 let _ = parse_and_evaluate(
132 "h = x => x + 3",
133 Some(Rc::clone(&heap)),
134 Some(Rc::clone(&bindings)),
135 )
136 .unwrap();
137
138 let result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
140
141 assert_eq!(result, Value::Number(17.0));
142 }
143
144 #[test]
145 fn test_late_binding_with_recursion() {
146 let heap = Rc::new(RefCell::new(Heap::new()));
148 let bindings = Rc::new(Environment::new());
149
150 let _ = parse_and_evaluate(
152 "factorial = n => if n <= 1 then 1 else n * factorial(n - 1)",
153 Some(Rc::clone(&heap)),
154 Some(Rc::clone(&bindings)),
155 )
156 .unwrap();
157
158 let result = parse_and_evaluate("factorial(5)", Some(heap), Some(bindings)).unwrap();
160
161 assert_eq!(result, Value::Number(120.0));
162 }
163
164 #[test]
165 fn test_late_binding_undefined_still_fails() {
166 let heap = Rc::new(RefCell::new(Heap::new()));
168 let bindings = Rc::new(Environment::new());
169
170 let _ = parse_and_evaluate(
172 "f = x => h(x)",
173 Some(Rc::clone(&heap)),
174 Some(Rc::clone(&bindings)),
175 )
176 .unwrap();
177
178 let result = parse_and_evaluate("f(5)", Some(heap), Some(bindings));
180
181 assert!(result.is_err());
182 assert!(
183 result
184 .unwrap_err()
185 .to_string()
186 .contains("unknown identifier: h")
187 );
188 }
189
190 #[test]
191 fn test_late_binding_with_redefinition() {
192 let heap = Rc::new(RefCell::new(Heap::new()));
195 let bindings = Rc::new(Environment::new());
196
197 let _ = parse_and_evaluate(
199 "f = x => g(x)",
200 Some(Rc::clone(&heap)),
201 Some(Rc::clone(&bindings)),
202 )
203 .unwrap();
204
205 let _ = parse_and_evaluate(
207 "g = x => x * 2",
208 Some(Rc::clone(&heap)),
209 Some(Rc::clone(&bindings)),
210 )
211 .unwrap();
212
213 let result1 =
215 parse_and_evaluate("f(5)", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
216 assert_eq!(result1, Value::Number(10.0));
217
218 let new_heap = Rc::new(RefCell::new(Heap::new()));
220 let new_bindings = Rc::new(Environment::new());
221
222 let _ = parse_and_evaluate(
224 "f = x => g(x)",
225 Some(Rc::clone(&new_heap)),
226 Some(Rc::clone(&new_bindings)),
227 )
228 .unwrap();
229
230 let _ = parse_and_evaluate(
232 "g = x => x * 3",
233 Some(Rc::clone(&new_heap)),
234 Some(Rc::clone(&new_bindings)),
235 )
236 .unwrap();
237
238 let result2 = parse_and_evaluate("f(5)", Some(new_heap), Some(new_bindings)).unwrap();
240 assert_eq!(result2, Value::Number(15.0));
241 }
242
243 #[test]
244 fn test_argument_shadows_late_binding() {
245 let heap = Rc::new(RefCell::new(Heap::new()));
247 let bindings = Rc::new(Environment::new());
248
249 let _ = parse_and_evaluate(
251 "g = 100",
252 Some(Rc::clone(&heap)),
253 Some(Rc::clone(&bindings)),
254 )
255 .unwrap();
256
257 let _ = parse_and_evaluate(
259 "f = g => g * 2",
260 Some(Rc::clone(&heap)),
261 Some(Rc::clone(&bindings)),
262 )
263 .unwrap();
264
265 let result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
267
268 assert_eq!(result, Value::Number(10.0));
269 }
270}
271
272#[cfg(test)]
274mod input_reference_tests {
275 use crate::environment::Environment;
276 use crate::expressions::evaluate_pairs;
277 use crate::heap::{Heap, HeapPointer};
278 use crate::parser::{Rule, get_pairs};
279 use crate::values::Value;
280 use indexmap::IndexMap;
281 use std::cell::RefCell;
282 use std::rc::Rc;
283
284 fn parse_and_evaluate(
285 code: &str,
286 heap: Option<Rc<RefCell<Heap>>>,
287 bindings: Option<Rc<Environment>>,
288 ) -> Result<Value, crate::error::RuntimeError> {
289 let pairs = get_pairs(code).map_err(|e| crate::error::RuntimeError::new(e.to_string()))?;
290
291 let heap = heap.unwrap_or_else(|| Rc::new(RefCell::new(Heap::new())));
292 let bindings = bindings.unwrap_or_else(|| Rc::new(Environment::new()));
293
294 let mut result = Value::Null;
295 for pair in pairs {
296 match pair.as_rule() {
297 Rule::statement => {
298 if let Some(inner_pair) = pair.into_inner().next() {
299 match inner_pair.as_rule() {
300 Rule::expression | Rule::assignment => {
301 result = evaluate_pairs(
302 inner_pair.into_inner(),
303 Rc::clone(&heap),
304 Rc::clone(&bindings),
305 0,
306 code,
307 )?;
308 }
309 _ => {}
310 }
311 }
312 }
313 Rule::EOI => {}
314 _ => {}
315 }
316 }
317 Ok(result)
318 }
319
320 fn setup_inputs(heap: &Rc<RefCell<Heap>>, bindings: &Rc<Environment>) {
321 let mut inputs_map = IndexMap::new();
323 inputs_map.insert("x".to_string(), Value::Number(42.0));
324 inputs_map.insert("y".to_string(), Value::Number(10.0));
325 inputs_map.insert(
326 "name".to_string(),
327 heap.borrow_mut().insert_string("Alice".to_string()),
328 );
329
330 let inputs_value = heap.borrow_mut().insert_record(inputs_map);
331 bindings.insert("inputs".to_string(), inputs_value);
332 }
333
334 #[test]
335 fn test_input_reference_simple() {
336 let heap = Rc::new(RefCell::new(Heap::new()));
337 let bindings = Rc::new(Environment::new());
338 setup_inputs(&heap, &bindings);
339
340 let result = parse_and_evaluate("#x", Some(heap), Some(bindings)).unwrap();
341 assert_eq!(result, Value::Number(42.0));
342 }
343
344 #[test]
345 fn test_input_reference_string() {
346 let heap = Rc::new(RefCell::new(Heap::new()));
347 let bindings = Rc::new(Environment::new());
348 setup_inputs(&heap, &bindings);
349
350 let result = parse_and_evaluate("#name", Some(Rc::clone(&heap)), Some(bindings)).unwrap();
351
352 if let Value::String(s) = result {
353 let borrowed_heap = heap.borrow();
354 let name = s.reify(&borrowed_heap).as_string().unwrap();
355 assert_eq!(name, "Alice");
356 } else {
357 panic!("Expected string value");
358 }
359 }
360
361 #[test]
362 fn test_input_reference_in_expression() {
363 let heap = Rc::new(RefCell::new(Heap::new()));
364 let bindings = Rc::new(Environment::new());
365 setup_inputs(&heap, &bindings);
366
367 let result = parse_and_evaluate("#x + #y", Some(heap), Some(bindings)).unwrap();
368 assert_eq!(result, Value::Number(52.0));
369 }
370
371 #[test]
372 fn test_input_reference_equivalence() {
373 let heap = Rc::new(RefCell::new(Heap::new()));
375 let bindings = Rc::new(Environment::new());
376 setup_inputs(&heap, &bindings);
377
378 let result1 =
379 parse_and_evaluate("#x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
380 let result2 = parse_and_evaluate("inputs.x", Some(heap), Some(bindings)).unwrap();
381
382 assert_eq!(result1, result2);
383 }
384
385 #[test]
386 fn test_input_reference_in_function() {
387 let heap = Rc::new(RefCell::new(Heap::new()));
388 let bindings = Rc::new(Environment::new());
389 setup_inputs(&heap, &bindings);
390
391 parse_and_evaluate(
393 "double_x = () => #x * 2",
394 Some(Rc::clone(&heap)),
395 Some(Rc::clone(&bindings)),
396 )
397 .unwrap();
398
399 let result = parse_and_evaluate("double_x()", Some(heap), Some(bindings)).unwrap();
400 assert_eq!(result, Value::Number(84.0));
401 }
402
403 #[test]
404 fn test_input_reference_multiple_uses() {
405 let heap = Rc::new(RefCell::new(Heap::new()));
406 let bindings = Rc::new(Environment::new());
407 setup_inputs(&heap, &bindings);
408
409 let result = parse_and_evaluate("#x + #x * #y", Some(heap), Some(bindings)).unwrap();
410 assert_eq!(result, Value::Number(462.0));
412 }
413
414 #[test]
415 fn test_input_reference_missing_field() {
416 let heap = Rc::new(RefCell::new(Heap::new()));
418 let bindings = Rc::new(Environment::new());
419 setup_inputs(&heap, &bindings);
420
421 let result = parse_and_evaluate("#missing", Some(heap), Some(bindings)).unwrap();
422 assert_eq!(result, Value::Null);
423 }
424
425 #[test]
426 fn test_input_reference_no_inputs() {
427 let heap = Rc::new(RefCell::new(Heap::new()));
428 let bindings = Rc::new(Environment::new());
429 let result = parse_and_evaluate("#x", Some(heap), Some(bindings));
432
433 assert!(result.is_err());
434 assert!(result.unwrap_err().to_string().contains("inputs not found"));
435 }
436
437 #[test]
438 fn test_input_reference_in_conditional() {
439 let heap = Rc::new(RefCell::new(Heap::new()));
440 let bindings = Rc::new(Environment::new());
441 setup_inputs(&heap, &bindings);
442
443 let result = parse_and_evaluate(
444 "if #x > 40 then #x + 10 else #y",
445 Some(heap),
446 Some(bindings),
447 )
448 .unwrap();
449
450 assert_eq!(result, Value::Number(52.0)); }
452
453 #[test]
454 fn test_input_reference_with_underscore() {
455 let heap = Rc::new(RefCell::new(Heap::new()));
456 let bindings = Rc::new(Environment::new());
457
458 let mut inputs_map = IndexMap::new();
460 inputs_map.insert("my_value".to_string(), Value::Number(123.0));
461 let inputs_value = heap.borrow_mut().insert_record(inputs_map);
462 bindings.insert("inputs".to_string(), inputs_value);
463
464 let result = parse_and_evaluate("#my_value", Some(heap), Some(bindings)).unwrap();
465 assert_eq!(result, Value::Number(123.0));
466 }
467
468 #[test]
469 fn test_multiline_evaluation_with_inputs() {
470 let heap = Rc::new(RefCell::new(Heap::new()));
471 let bindings = Rc::new(Environment::new());
472 setup_inputs(&heap, &bindings);
473
474 let code = "a = #x * 2\nb = #y + 10\na + b";
476 let result = parse_and_evaluate(code, Some(heap), Some(bindings)).unwrap();
477 assert_eq!(result, Value::Number(104.0));
479 }
480
481 #[test]
482 fn test_multiline_evaluation_with_comments() {
483 let heap = Rc::new(RefCell::new(Heap::new()));
484 let bindings = Rc::new(Environment::new());
485 setup_inputs(&heap, &bindings);
486
487 let code = "// Calculate doubled value\na = #x * 2\n// Calculate offset\nb = #y + 10\n// Return sum\na + b";
489 let result = parse_and_evaluate(code, Some(heap), Some(bindings)).unwrap();
490 assert_eq!(result, Value::Number(104.0));
492 }
493
494 #[test]
495 fn test_multiline_with_blank_lines_and_comments() {
496 let heap = Rc::new(RefCell::new(Heap::new()));
497 let bindings = Rc::new(Environment::new());
498 setup_inputs(&heap, &bindings);
499
500 let code = "x = #x * 2\ny = #y + 10\n\n// hi\nx + y";
502 let result = parse_and_evaluate(code, Some(heap), Some(bindings)).unwrap();
503 assert_eq!(result, Value::Number(104.0));
505 }
506
507 #[test]
508 fn test_comment_only_lines() {
509 let heap = Rc::new(RefCell::new(Heap::new()));
510 let bindings = Rc::new(Environment::new());
511
512 let code = "// First comment\n// Second comment\n42";
514 let result = parse_and_evaluate(code, Some(heap), Some(bindings)).unwrap();
515 assert_eq!(result, Value::Number(42.0));
516 }
517}
518
519#[cfg(test)]
521mod lambda_error_context_tests {
522 use crate::environment::Environment;
523 use crate::expressions::evaluate_pairs;
524 use crate::heap::Heap;
525 use crate::parser::{Rule, get_pairs};
526 use crate::values::Value;
527 use std::cell::RefCell;
528 use std::rc::Rc;
529
530 fn parse_and_evaluate(
531 code: &str,
532 heap: Option<Rc<RefCell<Heap>>>,
533 bindings: Option<Rc<Environment>>,
534 ) -> Result<Value, crate::error::RuntimeError> {
535 let pairs = get_pairs(code).map_err(|e| crate::error::RuntimeError::new(e.to_string()))?;
536
537 let heap = heap.unwrap_or_else(|| Rc::new(RefCell::new(Heap::new())));
538 let bindings = bindings.unwrap_or_else(|| Rc::new(Environment::new()));
539
540 let mut result = Value::Null;
541 for pair in pairs {
542 match pair.as_rule() {
543 Rule::statement => {
544 if let Some(inner_pair) = pair.into_inner().next() {
545 match inner_pair.as_rule() {
546 Rule::expression | Rule::assignment => {
547 result = evaluate_pairs(
548 inner_pair.into_inner(),
549 Rc::clone(&heap),
550 Rc::clone(&bindings),
551 0,
552 code,
553 )?;
554 }
555 _ => {}
556 }
557 }
558 }
559 Rule::EOI => {}
560 _ => {}
561 }
562 }
563 Ok(result)
564 }
565
566 #[test]
567 fn test_lambda_error_preserves_original_source() {
568 let heap = Rc::new(RefCell::new(Heap::new()));
571 let bindings = Rc::new(Environment::new());
572
573 let lambda_definition = "f = x => x + undefined_var";
575 parse_and_evaluate(
576 lambda_definition,
577 Some(Rc::clone(&heap)),
578 Some(Rc::clone(&bindings)),
579 )
580 .unwrap();
581
582 let call_code = "f(5)";
584 let result = parse_and_evaluate(call_code, Some(heap), Some(bindings));
585
586 assert!(result.is_err());
588
589 let error = result.unwrap_err();
590 let error_msg = error.to_string();
591
592 assert!(error_msg.contains("undefined_var"));
594 }
595
596 #[test]
597 fn test_lambda_error_with_multiple_calls() {
598 let heap = Rc::new(RefCell::new(Heap::new()));
600 let bindings = Rc::new(Environment::new());
601
602 let lambda_def = "divide = (a, b) => a / b";
604 parse_and_evaluate(
605 lambda_def,
606 Some(Rc::clone(&heap)),
607 Some(Rc::clone(&bindings)),
608 )
609 .unwrap();
610
611 let call1 = "divide(10, 2)";
613 let result1 = parse_and_evaluate(call1, Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
614 assert!(result1.is_ok());
615 assert_eq!(result1.unwrap(), Value::Number(5.0));
616
617 let call2 = "divide(10, 0)";
619 let result2 = parse_and_evaluate(call2, Some(heap), Some(bindings));
620 assert!(result2.is_ok());
621 }
622
623 #[test]
624 fn test_nested_lambda_error_reporting() {
625 let heap = Rc::new(RefCell::new(Heap::new()));
627 let bindings = Rc::new(Environment::new());
628
629 let outer_lambda = "outer = x => (y => x + y + undefined_nested)";
631 parse_and_evaluate(
632 outer_lambda,
633 Some(Rc::clone(&heap)),
634 Some(Rc::clone(&bindings)),
635 )
636 .unwrap();
637
638 let get_inner = "inner = outer(5)";
640 parse_and_evaluate(
641 get_inner,
642 Some(Rc::clone(&heap)),
643 Some(Rc::clone(&bindings)),
644 )
645 .unwrap();
646
647 let call_inner = "inner(10)";
649 let result = parse_and_evaluate(call_inner, Some(heap), Some(bindings));
650
651 assert!(result.is_err());
652 let error = result.unwrap_err();
653 assert!(error.to_string().contains("undefined_nested"));
654 }
655}
656
657#[cfg(test)]
660mod lambda_parameter_isolation_tests {
661 use crate::environment::Environment;
662 use crate::expressions::evaluate_pairs;
663 use crate::heap::Heap;
664 use crate::parser::{Rule, get_pairs};
665 use crate::values::{SerializableValue, Value};
666 use std::cell::RefCell;
667 use std::rc::Rc;
668
669 fn parse_and_evaluate(
670 code: &str,
671 heap: Option<Rc<RefCell<Heap>>>,
672 bindings: Option<Rc<Environment>>,
673 ) -> Result<Value, crate::error::RuntimeError> {
674 let pairs = get_pairs(code).map_err(|e| crate::error::RuntimeError::new(e.to_string()))?;
675
676 let heap = heap.unwrap_or_else(|| Rc::new(RefCell::new(Heap::new())));
677 let bindings = bindings.unwrap_or_else(|| Rc::new(Environment::new()));
678
679 let mut result = Value::Null;
680 for pair in pairs {
681 match pair.as_rule() {
682 Rule::statement => {
683 if let Some(inner_pair) = pair.into_inner().next() {
684 match inner_pair.as_rule() {
685 Rule::expression | Rule::assignment => {
686 result = evaluate_pairs(
687 inner_pair.into_inner(),
688 Rc::clone(&heap),
689 Rc::clone(&bindings),
690 0,
691 code,
692 )?;
693 }
694 _ => {}
695 }
696 }
697 }
698 Rule::EOI => {}
699 _ => {}
700 }
701 }
702 Ok(result)
703 }
704
705 #[test]
706 fn test_lambda_parameters_not_captured_from_outer_scope() {
707 let heap = Rc::new(RefCell::new(Heap::new()));
710 let bindings = Rc::new(Environment::new());
711
712 parse_and_evaluate("x = 42", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
714
715 parse_and_evaluate(
717 "f = x => x + 1",
718 Some(Rc::clone(&heap)),
719 Some(Rc::clone(&bindings)),
720 )
721 .unwrap();
722
723 let f_value = bindings.get("f").unwrap();
725 {
726 let heap_ref = heap.borrow();
727 let lambda_def = f_value.as_lambda(&heap_ref).unwrap();
728
729 assert!(
731 !lambda_def.scope.contains_key("x"),
732 "Lambda should not capture its own parameter 'x' from outer scope"
733 );
734 }
735
736 let result = parse_and_evaluate("f(10)", Some(heap), Some(bindings)).unwrap();
738 assert_eq!(result, Value::Number(11.0));
739 }
740
741 #[test]
742 fn test_lambda_multiple_parameters_not_captured() {
743 let heap = Rc::new(RefCell::new(Heap::new()));
745 let bindings = Rc::new(Environment::new());
746
747 parse_and_evaluate("a = 1", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
749 parse_and_evaluate("b = 2", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
750
751 parse_and_evaluate(
753 "add = (a, b) => a + b",
754 Some(Rc::clone(&heap)),
755 Some(Rc::clone(&bindings)),
756 )
757 .unwrap();
758
759 let add_value = bindings.get("add").unwrap();
761 {
762 let heap_ref = heap.borrow();
763 let lambda_def = add_value.as_lambda(&heap_ref).unwrap();
764
765 assert!(
767 !lambda_def.scope.contains_key("a"),
768 "Lambda should not capture its parameter 'a'"
769 );
770 assert!(
771 !lambda_def.scope.contains_key("b"),
772 "Lambda should not capture its parameter 'b'"
773 );
774 }
775
776 let result = parse_and_evaluate("add(10, 20)", Some(heap), Some(bindings)).unwrap();
778 assert_eq!(result, Value::Number(30.0));
779 }
780
781 #[test]
782 fn test_nested_lambda_parameters_not_captured() {
783 let heap = Rc::new(RefCell::new(Heap::new()));
785 let bindings = Rc::new(Environment::new());
786
787 parse_and_evaluate(
789 "x = 100",
790 Some(Rc::clone(&heap)),
791 Some(Rc::clone(&bindings)),
792 )
793 .unwrap();
794
795 parse_and_evaluate(
797 "outer = x => (y => x + y)",
798 Some(Rc::clone(&heap)),
799 Some(Rc::clone(&bindings)),
800 )
801 .unwrap();
802
803 let outer_value = bindings.get("outer").unwrap();
805 {
806 let heap_ref = heap.borrow();
807 let outer_lambda = outer_value.as_lambda(&heap_ref).unwrap();
808
809 assert!(
811 !outer_lambda.scope.contains_key("x"),
812 "Outer lambda should not capture its own parameter 'x'"
813 );
814 }
815
816 parse_and_evaluate(
818 "inner = outer(5)",
819 Some(Rc::clone(&heap)),
820 Some(Rc::clone(&bindings)),
821 )
822 .unwrap();
823
824 let result = parse_and_evaluate("inner(3)", Some(heap), Some(bindings)).unwrap();
826 assert_eq!(result, Value::Number(8.0));
827 }
828
829 #[test]
830 fn test_lambda_serialization_preserves_parameter_names() {
831 let heap = Rc::new(RefCell::new(Heap::new()));
833 let bindings = Rc::new(Environment::new());
834
835 parse_and_evaluate("x = 42", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
837
838 parse_and_evaluate(
840 "increment = x => x + 1",
841 Some(Rc::clone(&heap)),
842 Some(Rc::clone(&bindings)),
843 )
844 .unwrap();
845
846 let increment_value = bindings.get("increment").unwrap();
848 let serialized = increment_value
849 .to_serializable_value(&heap.borrow())
850 .unwrap();
851
852 if let SerializableValue::Lambda(lambda_def) = serialized {
854 assert!(
855 lambda_def.body.contains("x"),
856 "Serialized lambda body should contain parameter name 'x', got: {}",
857 lambda_def.body
858 );
859 assert!(
860 !lambda_def.body.contains("42"),
861 "Serialized lambda body should not contain value '42', got: {}",
862 lambda_def.body
863 );
864 assert_eq!(
865 lambda_def.body, "x + 1",
866 "Serialized lambda body should be 'x + 1', got: {}",
867 lambda_def.body
868 );
869 } else {
870 panic!("Expected Lambda, got: {:?}", serialized);
871 }
872 }
873
874 #[test]
875 fn test_curried_function_captures_correctly() {
876 let heap = Rc::new(RefCell::new(Heap::new()));
878 let bindings = Rc::new(Environment::new());
879
880 parse_and_evaluate(
882 "curry_add = x => y => x + y",
883 Some(Rc::clone(&heap)),
884 Some(Rc::clone(&bindings)),
885 )
886 .unwrap();
887
888 parse_and_evaluate(
890 "add_five = curry_add(5)",
891 Some(Rc::clone(&heap)),
892 Some(Rc::clone(&bindings)),
893 )
894 .unwrap();
895
896 let add_five_value = bindings.get("add_five").unwrap();
898 {
899 let heap_ref = heap.borrow();
900 let add_five_lambda = add_five_value.as_lambda(&heap_ref).unwrap();
901
902 assert!(
904 add_five_lambda.scope.contains_key("x"),
905 "add_five should capture 'x' from outer scope"
906 );
907 assert_eq!(
908 add_five_lambda.scope.get("x").unwrap().as_number().unwrap(),
909 5.0
910 );
911
912 assert!(
914 !add_five_lambda.scope.contains_key("y"),
915 "add_five should not capture its own parameter 'y'"
916 );
917 }
918
919 let serialized = add_five_value
921 .to_serializable_value(&heap.borrow())
922 .unwrap();
923
924 if let SerializableValue::Lambda(lambda_def) = serialized {
925 assert_eq!(
927 lambda_def.body, "5 + y",
928 "Serialized body should be '5 + y', got: {}",
929 lambda_def.body
930 );
931 } else {
932 panic!("Expected Lambda, got: {:?}", serialized);
933 }
934 }
935}
936
937#[cfg(test)]
939mod where_operator_tests {
940 use crate::environment::Environment;
941 use crate::expressions::evaluate_pairs;
942 use crate::heap::{Heap, HeapPointer};
943 use crate::parser::{Rule, get_pairs};
944 use crate::values::Value;
945 use std::cell::RefCell;
946 use std::rc::Rc;
947
948 fn parse_and_evaluate(
949 code: &str,
950 ) -> Result<(Value, Rc<RefCell<Heap>>), crate::error::RuntimeError> {
951 let pairs = get_pairs(code).map_err(|e| crate::error::RuntimeError::new(e.to_string()))?;
952
953 let heap = Rc::new(RefCell::new(Heap::new()));
954 let bindings = Rc::new(Environment::new());
955
956 let mut result = Value::Null;
957 for pair in pairs {
958 match pair.as_rule() {
959 Rule::statement => {
960 if let Some(inner_pair) = pair.into_inner().next() {
961 match inner_pair.as_rule() {
962 Rule::expression | Rule::assignment => {
963 result = evaluate_pairs(
964 inner_pair.into_inner(),
965 Rc::clone(&heap),
966 Rc::clone(&bindings),
967 0,
968 code,
969 )?;
970 }
971 _ => {}
972 }
973 }
974 }
975 Rule::EOI => {}
976 _ => {}
977 }
978 }
979 Ok((result, heap))
980 }
981
982 #[test]
983 fn test_where_basic_filtering() {
984 let (result, heap) = parse_and_evaluate("[1, 2, 3, 4, 5] where x => x > 3").unwrap();
985 let borrowed_heap = heap.borrow();
986 let list = result.as_list(&borrowed_heap).unwrap();
987 assert_eq!(list.len(), 2);
988 assert_eq!(list[0], Value::Number(4.0));
989 assert_eq!(list[1], Value::Number(5.0));
990 }
991
992 #[test]
993 fn test_where_with_index() {
994 let (result, heap) =
995 parse_and_evaluate("[10, 20, 30, 40] where (val, idx) => idx > 0").unwrap();
996 let borrowed_heap = heap.borrow();
997 let list = result.as_list(&borrowed_heap).unwrap();
998 assert_eq!(list.len(), 3);
999 assert_eq!(list[0], Value::Number(20.0));
1000 assert_eq!(list[1], Value::Number(30.0));
1001 assert_eq!(list[2], Value::Number(40.0));
1002 }
1003
1004 #[test]
1005 fn test_where_empty_list() {
1006 let (result, heap) = parse_and_evaluate("[] where x => x > 3").unwrap();
1007 let borrowed_heap = heap.borrow();
1008 let list = result.as_list(&borrowed_heap).unwrap();
1009 assert_eq!(list.len(), 0);
1010 }
1011
1012 #[test]
1013 fn test_where_all_filtered_out() {
1014 let (result, heap) = parse_and_evaluate("[1, 2, 3] where x => x > 10").unwrap();
1015 let borrowed_heap = heap.borrow();
1016 let list = result.as_list(&borrowed_heap).unwrap();
1017 assert_eq!(list.len(), 0);
1018 }
1019
1020 #[test]
1021 fn test_where_none_filtered_out() {
1022 let (result, heap) = parse_and_evaluate("[1, 2, 3] where x => true").unwrap();
1023 let borrowed_heap = heap.borrow();
1024 let list = result.as_list(&borrowed_heap).unwrap();
1025 assert_eq!(list.len(), 3);
1026 assert_eq!(list[0], Value::Number(1.0));
1027 assert_eq!(list[1], Value::Number(2.0));
1028 assert_eq!(list[2], Value::Number(3.0));
1029 }
1030
1031 #[test]
1032 fn test_where_scalar_error() {
1033 let result = parse_and_evaluate("5 where x => x > 3");
1034 assert!(result.is_err());
1035 let error_msg = result.unwrap_err().to_string();
1036 assert!(error_msg.contains("where operator requires a list on the left side"));
1037 }
1038
1039 #[test]
1040 fn test_where_non_function_error() {
1041 let result = parse_and_evaluate("[1, 2, 3] where 5");
1042 assert!(result.is_err());
1043 let error_msg = result.unwrap_err().to_string();
1044 assert!(error_msg.contains("can't call a non-function"));
1045 }
1046
1047 #[test]
1048 fn test_where_non_boolean_return_error() {
1049 let result = parse_and_evaluate("[1, 2, 3] where x => x");
1050 assert!(result.is_err());
1051 let error_msg = result.unwrap_err().to_string();
1052 assert!(error_msg.contains("expected a boolean"));
1053 }
1054
1055 #[test]
1056 fn test_where_with_string_comparison() {
1057 let heap = Rc::new(RefCell::new(Heap::new()));
1058 let pairs = crate::parser::get_pairs(
1059 "[\"apple\", \"banana\", \"cherry\"] where s => s == \"banana\"",
1060 )
1061 .unwrap();
1062 let bindings = Rc::new(Environment::new());
1063
1064 let mut result = Value::Null;
1065 for pair in pairs {
1066 if let crate::parser::Rule::statement = pair.as_rule() {
1067 if let Some(inner_pair) = pair.into_inner().next() {
1068 if let crate::parser::Rule::expression = inner_pair.as_rule() {
1069 result = crate::expressions::evaluate_pairs(
1070 inner_pair.into_inner(),
1071 Rc::clone(&heap),
1072 Rc::clone(&bindings),
1073 0,
1074 "[\"apple\", \"banana\", \"cherry\"] where s => s == \"banana\"",
1075 )
1076 .unwrap();
1077 }
1078 }
1079 }
1080 }
1081
1082 let borrowed_heap = heap.borrow();
1083 let list = result.as_list(&borrowed_heap).unwrap();
1084 assert_eq!(list.len(), 1);
1085
1086 if let Value::String(s) = list[0] {
1087 let string_value = s.reify(&borrowed_heap).as_string().unwrap();
1088 assert_eq!(string_value, "banana");
1089 } else {
1090 panic!("Expected string value");
1091 }
1092 }
1093
1094 #[test]
1095 fn test_where_complex_predicate() {
1096 let (result, heap) =
1097 parse_and_evaluate("[1, 2, 3, 4, 5, 6] where x => x % 2 == 0").unwrap();
1098 let borrowed_heap = heap.borrow();
1099 let list = result.as_list(&borrowed_heap).unwrap();
1100 assert_eq!(list.len(), 3);
1101 assert_eq!(list[0], Value::Number(2.0));
1102 assert_eq!(list[1], Value::Number(4.0));
1103 assert_eq!(list[2], Value::Number(6.0));
1104 }
1105
1106 #[test]
1107 fn test_where_with_built_in_function() {
1108 let (result, heap) = parse_and_evaluate("[1, -2, 3, -4, 5] where x => x > 0").unwrap();
1109 let borrowed_heap = heap.borrow();
1110 let list = result.as_list(&borrowed_heap).unwrap();
1111 assert_eq!(list.len(), 3);
1112 assert_eq!(list[0], Value::Number(1.0));
1113 assert_eq!(list[1], Value::Number(3.0));
1114 assert_eq!(list[2], Value::Number(5.0));
1115 }
1116}
1117
1118#[cfg(test)]
1120mod flat_chaining_tests {
1121 use crate::environment::Environment;
1122 use crate::expressions::evaluate_pairs;
1123 use crate::heap::Heap;
1124 use crate::parser::{Rule, get_pairs};
1125 use crate::values::Value;
1126 use std::cell::RefCell;
1127 use std::rc::Rc;
1128
1129 fn parse_and_evaluate(
1130 code: &str,
1131 ) -> Result<(Value, Rc<RefCell<Heap>>), crate::error::RuntimeError> {
1132 let pairs = get_pairs(code).map_err(|e| crate::error::RuntimeError::new(e.to_string()))?;
1133
1134 let heap = Rc::new(RefCell::new(Heap::new()));
1135 let bindings = Rc::new(Environment::new());
1136
1137 let mut result = Value::Null;
1138 for pair in pairs {
1139 match pair.as_rule() {
1140 Rule::statement => {
1141 if let Some(inner_pair) = pair.into_inner().next() {
1142 match inner_pair.as_rule() {
1143 Rule::expression | Rule::assignment => {
1144 result = evaluate_pairs(
1145 inner_pair.into_inner(),
1146 Rc::clone(&heap),
1147 Rc::clone(&bindings),
1148 0,
1149 code,
1150 )?;
1151 }
1152 _ => {}
1153 }
1154 }
1155 }
1156 Rule::EOI => {}
1157 _ => {}
1158 }
1159 }
1160 Ok((result, heap))
1161 }
1162
1163 #[test]
1164 fn test_via_then_where() {
1165 let (result, heap) =
1166 parse_and_evaluate("[1, 2, 3, 4, 5] via x => x * 2 where y => y > 5").unwrap();
1167 let borrowed_heap = heap.borrow();
1168 let list = result.as_list(&borrowed_heap).unwrap();
1169 assert_eq!(list.len(), 3);
1170 assert_eq!(list[0], Value::Number(6.0));
1171 assert_eq!(list[1], Value::Number(8.0));
1172 assert_eq!(list[2], Value::Number(10.0));
1173 }
1174
1175 #[test]
1176 fn test_where_then_via() {
1177 let (result, heap) =
1178 parse_and_evaluate("[1, 2, 3, 4, 5] where x => x > 3 via y => y * 10").unwrap();
1179 let borrowed_heap = heap.borrow();
1180 let list = result.as_list(&borrowed_heap).unwrap();
1181 assert_eq!(list.len(), 2);
1182 assert_eq!(list[0], Value::Number(40.0));
1183 assert_eq!(list[1], Value::Number(50.0));
1184 }
1185
1186 #[test]
1187 fn test_via_where_via_chain() {
1188 let (result, heap) =
1189 parse_and_evaluate("[1, 2, 3, 4, 5, 6] via x => x * 2 where y => y > 5 via z => z + 1")
1190 .unwrap();
1191 let borrowed_heap = heap.borrow();
1192 let list = result.as_list(&borrowed_heap).unwrap();
1193 assert_eq!(list.len(), 4);
1194 assert_eq!(list[0], Value::Number(7.0));
1195 assert_eq!(list[1], Value::Number(9.0));
1196 assert_eq!(list[2], Value::Number(11.0));
1197 assert_eq!(list[3], Value::Number(13.0));
1198 }
1199
1200 #[test]
1201 fn test_via_where_into() {
1202 let (result, _heap) =
1203 parse_and_evaluate("[1, 2, 3, 4, 5] via x => x * 2 where x => x > 5 into sum").unwrap();
1204 assert_eq!(result, Value::Number(24.0));
1205 }
1206
1207 #[test]
1208 fn test_where_with_index_then_via() {
1209 let (result, heap) =
1210 parse_and_evaluate("[10, 20, 30, 40] where (val, idx) => idx > 0 via x => x / 10")
1211 .unwrap();
1212 let borrowed_heap = heap.borrow();
1213 let list = result.as_list(&borrowed_heap).unwrap();
1214 assert_eq!(list.len(), 3);
1215 assert_eq!(list[0], Value::Number(2.0));
1216 assert_eq!(list[1], Value::Number(3.0));
1217 assert_eq!(list[2], Value::Number(4.0));
1218 }
1219
1220 #[test]
1221 fn test_nested_via_requires_parens() {
1222 let (result, heap) =
1224 parse_and_evaluate("[[1, 2], [3, 4]] via row => (row via x => x * 10)").unwrap();
1225 let borrowed_heap = heap.borrow();
1226 let outer_list = result.as_list(&borrowed_heap).unwrap();
1227 assert_eq!(outer_list.len(), 2);
1228
1229 let first_row = outer_list[0].as_list(&borrowed_heap).unwrap();
1230 assert_eq!(first_row[0], Value::Number(10.0));
1231 assert_eq!(first_row[1], Value::Number(20.0));
1232
1233 let second_row = outer_list[1].as_list(&borrowed_heap).unwrap();
1234 assert_eq!(second_row[0], Value::Number(30.0));
1235 assert_eq!(second_row[1], Value::Number(40.0));
1236 }
1237
1238 #[test]
1239 fn test_complex_chain_with_different_param_names() {
1240 let (result, heap) = parse_and_evaluate(
1241 "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] where a => a % 2 == 0 via b => b * 3 where c => c > 10"
1242 ).unwrap();
1243 let borrowed_heap = heap.borrow();
1244 let list = result.as_list(&borrowed_heap).unwrap();
1245 assert_eq!(list.len(), 4);
1246 assert_eq!(list[0], Value::Number(12.0)); assert_eq!(list[1], Value::Number(18.0)); assert_eq!(list[2], Value::Number(24.0)); assert_eq!(list[3], Value::Number(30.0)); }
1251}
1252
1253#[cfg(test)]
1255mod via_into_error_message_tests {
1256 use crate::environment::Environment;
1257 use crate::expressions::evaluate_pairs;
1258 use crate::heap::Heap;
1259 use crate::parser::{Rule, get_pairs};
1260 use crate::values::Value;
1261 use std::cell::RefCell;
1262 use std::rc::Rc;
1263
1264 fn parse_and_evaluate(code: &str) -> Result<Value, crate::error::RuntimeError> {
1265 let pairs = get_pairs(code).map_err(|e| crate::error::RuntimeError::new(e.to_string()))?;
1266
1267 let heap = Rc::new(RefCell::new(Heap::new()));
1268 let bindings = Rc::new(Environment::new());
1269
1270 let mut result = Value::Null;
1271 for pair in pairs {
1272 match pair.as_rule() {
1273 Rule::statement => {
1274 if let Some(inner_pair) = pair.into_inner().next() {
1275 match inner_pair.as_rule() {
1276 Rule::expression | Rule::assignment => {
1277 result = evaluate_pairs(
1278 inner_pair.into_inner(),
1279 Rc::clone(&heap),
1280 Rc::clone(&bindings),
1281 0,
1282 code,
1283 )?;
1284 }
1285 _ => {}
1286 }
1287 }
1288 }
1289 Rule::EOI => {}
1290 _ => {}
1291 }
1292 }
1293 Ok(result)
1294 }
1295
1296 #[test]
1297 fn test_via_callback_error_message_with_list() {
1298 let result = parse_and_evaluate("[1, 2, null] via x => x + 1");
1300 assert!(result.is_err());
1301 let error_msg = result.unwrap_err().to_string();
1302 assert!(
1303 error_msg.contains("in \"via\" callback"),
1304 "Error should mention 'via callback', got: {}",
1305 error_msg
1306 );
1307 assert!(
1308 !error_msg.contains("in built-in function \"map\""),
1309 "Error should not mention 'built-in function map', got: {}",
1310 error_msg
1311 );
1312 }
1313
1314 #[test]
1315 fn test_via_callback_error_message_scalar() {
1316 let result = parse_and_evaluate("null via x => x + 1");
1318 assert!(result.is_err());
1319 let error_msg = result.unwrap_err().to_string();
1320 assert!(
1321 error_msg.contains("in \"via\" callback"),
1322 "Error should mention 'via callback', got: {}",
1323 error_msg
1324 );
1325 }
1326
1327 #[test]
1328 fn test_via_callback_error_message_list_to_list() {
1329 let result = parse_and_evaluate("[1, 2] via [x => x + 1, x => x + null]");
1331 assert!(result.is_err());
1332 let error_msg = result.unwrap_err().to_string();
1333 assert!(
1334 error_msg.contains("in \"via\" callback"),
1335 "Error should mention 'via callback', got: {}",
1336 error_msg
1337 );
1338 }
1339
1340 #[test]
1341 fn test_via_callback_error_includes_function_context() {
1342 let result = parse_and_evaluate("[1, 2, null] via x => x + 1");
1344 assert!(result.is_err());
1345 let error_msg = result.unwrap_err().to_string();
1346 assert!(
1347 error_msg.contains("in anonymous function"),
1348 "Error should mention 'anonymous function', got: {}",
1349 error_msg
1350 );
1351 }
1352
1353 #[test]
1354 fn test_via_callback_error_with_named_function() {
1355 let result = parse_and_evaluate("add_one = x => x + 1\n[1, 2, null] via add_one");
1357 assert!(result.is_err());
1358 let error_msg = result.unwrap_err().to_string();
1359 assert!(
1360 error_msg.contains("in \"via\" callback"),
1361 "Error should mention 'via callback', got: {}",
1362 error_msg
1363 );
1364 assert!(
1365 error_msg.contains("function \"add_one\""),
1366 "Error should mention function name, got: {}",
1367 error_msg
1368 );
1369 }
1370
1371 #[test]
1372 fn test_via_callback_error_with_index_parameter() {
1373 let result = parse_and_evaluate("[1, 2, null] via (x, idx) => x + idx");
1375 assert!(result.is_err());
1376 let error_msg = result.unwrap_err().to_string();
1377 assert!(
1378 error_msg.contains("in \"via\" callback"),
1379 "Error should mention 'via callback', got: {}",
1380 error_msg
1381 );
1382 }
1383
1384 #[test]
1385 fn test_via_with_index_parameter_works() {
1386 let (result, heap) = {
1388 let pairs = crate::parser::get_pairs("[10, 20, 30] via (x, idx) => x + idx").unwrap();
1389 let heap = Rc::new(RefCell::new(crate::heap::Heap::new()));
1390 let bindings = Rc::new(Environment::new());
1391 let mut result = Value::Null;
1392 for pair in pairs {
1393 if let crate::parser::Rule::statement = pair.as_rule() {
1394 if let Some(inner_pair) = pair.into_inner().next() {
1395 result = crate::expressions::evaluate_pairs(
1396 inner_pair.into_inner(),
1397 Rc::clone(&heap),
1398 Rc::clone(&bindings),
1399 0,
1400 "[10, 20, 30] via (x, idx) => x + idx",
1401 )
1402 .unwrap();
1403 }
1404 }
1405 }
1406 (result, heap)
1407 };
1408 let borrowed_heap = heap.borrow();
1409 let list = result.as_list(&borrowed_heap).unwrap();
1410 assert_eq!(list.len(), 3);
1411 assert_eq!(list[0], Value::Number(10.0)); assert_eq!(list[1], Value::Number(21.0)); assert_eq!(list[2], Value::Number(32.0)); }
1415
1416 #[test]
1417 fn test_into_callback_error_message_with_list() {
1418 let result = parse_and_evaluate("[1, 2, 3] into list => list + \"oops\"");
1420 assert!(result.is_err());
1421 let error_msg = result.unwrap_err().to_string();
1422 assert!(
1423 error_msg.contains("in \"into\" callback"),
1424 "Error should mention 'into callback', got: {}",
1425 error_msg
1426 );
1427 }
1428
1429 #[test]
1430 fn test_into_callback_error_message_scalar() {
1431 let result = parse_and_evaluate("5 into x => x + \"oops\"");
1433 assert!(result.is_err());
1434 let error_msg = result.unwrap_err().to_string();
1435 assert!(
1436 error_msg.contains("in \"into\" callback"),
1437 "Error should mention 'into callback', got: {}",
1438 error_msg
1439 );
1440 }
1441
1442 #[test]
1443 fn test_into_callback_error_includes_function_context() {
1444 let result = parse_and_evaluate("[1, 2, 3] into list => list + \"oops\"");
1446 assert!(result.is_err());
1447 let error_msg = result.unwrap_err().to_string();
1448 assert!(
1449 error_msg.contains("in anonymous function"),
1450 "Error should mention 'anonymous function', got: {}",
1451 error_msg
1452 );
1453 }
1454
1455 #[test]
1456 fn test_into_callback_error_with_named_function() {
1457 let result = parse_and_evaluate("bad_fn = list => list + \"oops\"\n[1, 2, 3] into bad_fn");
1459 assert!(result.is_err());
1460 let error_msg = result.unwrap_err().to_string();
1461 assert!(
1462 error_msg.contains("in \"into\" callback"),
1463 "Error should mention 'into callback', got: {}",
1464 error_msg
1465 );
1466 assert!(
1467 error_msg.contains("function \"bad_fn\""),
1468 "Error should mention function name, got: {}",
1469 error_msg
1470 );
1471 }
1472
1473 #[test]
1474 fn test_where_callback_error_message_with_list() {
1475 let result = parse_and_evaluate("[1, 2, null] where x => x + 1 > 0");
1478 assert!(result.is_err());
1479 let error_msg = result.unwrap_err().to_string();
1480 assert!(
1481 error_msg.contains("in \"where\" callback"),
1482 "Error should mention 'where callback', got: {}",
1483 error_msg
1484 );
1485 assert!(
1486 !error_msg.contains("in built-in function \"filter\""),
1487 "Error should not mention 'built-in function filter', got: {}",
1488 error_msg
1489 );
1490 }
1491
1492 #[test]
1493 fn test_where_callback_error_includes_function_context() {
1494 let result = parse_and_evaluate("[1, 2, null] where x => x + 1 > 0");
1496 assert!(result.is_err());
1497 let error_msg = result.unwrap_err().to_string();
1498 assert!(
1499 error_msg.contains("in anonymous function"),
1500 "Error should mention 'anonymous function', got: {}",
1501 error_msg
1502 );
1503 }
1504
1505 #[test]
1506 fn test_where_callback_error_with_named_function() {
1507 let result =
1509 parse_and_evaluate("is_positive = x => x + 1 > 0\n[1, 2, null] where is_positive");
1510 assert!(result.is_err());
1511 let error_msg = result.unwrap_err().to_string();
1512 assert!(
1513 error_msg.contains("in \"where\" callback"),
1514 "Error should mention 'where callback', got: {}",
1515 error_msg
1516 );
1517 assert!(
1518 error_msg.contains("function \"is_positive\""),
1519 "Error should mention function name, got: {}",
1520 error_msg
1521 );
1522 }
1523
1524 #[test]
1525 fn test_where_callback_error_with_index_parameter() {
1526 let result = parse_and_evaluate("[1, 2, null] where (x, idx) => x + 1 > idx");
1528 assert!(result.is_err());
1529 let error_msg = result.unwrap_err().to_string();
1530 assert!(
1531 error_msg.contains("in \"where\" callback"),
1532 "Error should mention 'where callback', got: {}",
1533 error_msg
1534 );
1535 }
1536
1537 #[test]
1538 fn test_where_callback_error_on_non_boolean_return() {
1539 let result = parse_and_evaluate("[1, 2, 3] where x => x * 2");
1541 assert!(result.is_err());
1542 let error_msg = result.unwrap_err().to_string();
1543 assert!(
1544 error_msg.contains("in \"where\" callback"),
1545 "Error should mention 'where callback', got: {}",
1546 error_msg
1547 );
1548 }
1549}