blots_core/
expressions.rs

1use crate::{
2    ast::{BinaryOp, DoStatement, Expr, PostfixOp, RecordEntry, RecordKey, UnaryOp},
3    functions::{
4        BuiltInFunction, get_built_in_function_def_by_ident, get_function_def, is_built_in_function,
5    },
6    heap::{Heap, HeapPointer, HeapValue, IterablePointer, RecordPointer},
7    parser::Rule,
8    values::{
9        LambdaArg, LambdaDef,
10        Value::{self, Bool, List, Number, Spread},
11    },
12};
13use anyhow::{Result, anyhow};
14use indexmap::IndexMap;
15use pest::{
16    iterators::Pairs,
17    pratt_parser::{Assoc, Op, PrattParser},
18};
19use std::{
20    cell::RefCell,
21    collections::{HashMap, HashSet},
22    rc::Rc,
23    sync::LazyLock,
24};
25
26static PRATT: LazyLock<PrattParser<Rule>> = LazyLock::new(|| {
27    PrattParser::new()
28        .op(Op::infix(Rule::and, Assoc::Left)
29            | Op::infix(Rule::natural_and, Assoc::Left)
30            | Op::infix(Rule::or, Assoc::Left)
31            | Op::infix(Rule::natural_or, Assoc::Left)
32            | Op::infix(Rule::via, Assoc::Left)
33            | Op::infix(Rule::into, Assoc::Left))
34        .op(Op::infix(Rule::equal, Assoc::Left)
35            | Op::infix(Rule::not_equal, Assoc::Left)
36            | Op::infix(Rule::less, Assoc::Left)
37            | Op::infix(Rule::less_eq, Assoc::Left)
38            | Op::infix(Rule::greater, Assoc::Left)
39            | Op::infix(Rule::greater_eq, Assoc::Left)
40            | Op::infix(Rule::dot_equal, Assoc::Left)
41            | Op::infix(Rule::dot_not_equal, Assoc::Left)
42            | Op::infix(Rule::dot_less, Assoc::Left)
43            | Op::infix(Rule::dot_less_eq, Assoc::Left)
44            | Op::infix(Rule::dot_greater, Assoc::Left)
45            | Op::infix(Rule::dot_greater_eq, Assoc::Left))
46        .op(Op::infix(Rule::add, Assoc::Left) | Op::infix(Rule::subtract, Assoc::Left))
47        .op(Op::infix(Rule::multiply, Assoc::Left)
48            | Op::infix(Rule::divide, Assoc::Left)
49            | Op::infix(Rule::modulo, Assoc::Left))
50        .op(Op::infix(Rule::power, Assoc::Right) | Op::infix(Rule::coalesce, Assoc::Left))
51        .op(Op::prefix(Rule::negation)
52            | Op::prefix(Rule::spread_operator)
53            | Op::prefix(Rule::invert)
54            | Op::prefix(Rule::natural_not))
55        .op(Op::postfix(Rule::factorial))
56        .op(Op::postfix(Rule::access)
57            | Op::postfix(Rule::dot_access)
58            | Op::postfix(Rule::call_list))
59});
60
61/// Parse pairs into AST and evaluate
62pub fn evaluate_pairs(
63    pairs: Pairs<Rule>,
64    heap: Rc<RefCell<Heap>>,
65    bindings: Rc<RefCell<HashMap<String, Value>>>,
66    call_depth: usize,
67) -> Result<Value> {
68    // First parse to AST
69    let ast = pairs_to_expr(pairs)?;
70
71    // Then evaluate the AST
72    evaluate_ast(&ast, heap, bindings, call_depth)
73}
74
75// Evaluate an AST expression
76/// Helper function to flatten spread values into a vector
77fn flatten_spread_value(
78    spread_ptr: &IterablePointer,
79    heap: &Rc<RefCell<Heap>>,
80) -> Result<Vec<Value>> {
81    match spread_ptr {
82        IterablePointer::List(list_ptr) => {
83            let borrowed_heap = heap.borrow();
84            Ok(list_ptr.reify(&borrowed_heap).as_list()?.clone())
85        }
86        IterablePointer::String(string_ptr) => {
87            let string = {
88                let borrowed_heap = heap.borrow();
89                string_ptr.reify(&borrowed_heap).as_string()?.to_string()
90            };
91            // Convert string to list of character values
92            Ok(string
93                .chars()
94                .map(|c| heap.borrow_mut().insert_string(c.to_string()))
95                .collect())
96        }
97        IterablePointer::Record(record_ptr) => {
98            let entries = {
99                let borrowed_heap = heap.borrow();
100                record_ptr.reify(&borrowed_heap).as_record()?.clone()
101            };
102            // Convert record to list of [key, value] pairs
103            Ok(entries
104                .into_iter()
105                .map(|(k, v)| {
106                    let key = heap.borrow_mut().insert_string(k);
107                    heap.borrow_mut().insert_list(vec![key, v])
108                })
109                .collect())
110        }
111    }
112}
113
114// Helper function for evaluating expressions inside do blocks with shadowing allowed
115fn evaluate_do_block_expr(
116    expr: &Expr,
117    heap: Rc<RefCell<Heap>>,
118    bindings: Rc<RefCell<HashMap<String, Value>>>,
119    call_depth: usize,
120) -> Result<Value> {
121    // For assignments in do blocks, skip the immutability check
122    if let Expr::Assignment { ident, value } = expr {
123        // Still check for keywords
124        if matches!(
125            ident.as_str(),
126            "return" | "if" | "then" | "else" | "do" | "true" | "false" | "null" | "output"
127        ) {
128            return Err(anyhow!("{} is a keyword, and cannot be reassigned", ident));
129        }
130
131        // Evaluate the value (allow shadowing - no check for existing binding)
132        let val = evaluate_ast(value, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
133
134        // Set lambda name if assigning a lambda
135        if let Value::Lambda(lambda_ptr) = val {
136            let mut borrowed_heap = heap.borrow_mut();
137            if let Some(HeapValue::Lambda(lambda_def)) = borrowed_heap.get_mut(lambda_ptr.index()) {
138                lambda_def.name = Some(ident.clone());
139            }
140        }
141
142        bindings.borrow_mut().insert(ident.clone(), val);
143        return Ok(val);
144    }
145
146    // For all other expressions, use the regular evaluate_ast
147    evaluate_ast(expr, heap, bindings, call_depth)
148}
149
150pub fn evaluate_ast(
151    expr: &Expr,
152    heap: Rc<RefCell<Heap>>,
153    bindings: Rc<RefCell<HashMap<String, Value>>>,
154    call_depth: usize,
155) -> Result<Value> {
156    match expr {
157        Expr::Number(n) => Ok(Number(*n)),
158        Expr::String(s) => Ok(heap.borrow_mut().insert_string(s.clone())),
159        Expr::Bool(b) => Ok(Bool(*b)),
160        Expr::Null => Ok(Value::Null),
161        Expr::Identifier(ident) => match ident.as_str() {
162            "infinity" => Ok(Number(f64::INFINITY)),
163            "inf" => Ok(Number(f64::INFINITY)),
164            "constants" => Ok(Value::Record(RecordPointer::new(0))),
165            _ => bindings
166                .borrow()
167                .get(ident)
168                .copied()
169                .ok_or(anyhow!("unknown identifier: {}", ident)),
170        },
171        Expr::InputReference(field) => {
172            // Desugar #field to inputs.field
173            let inputs_value = bindings
174                .borrow()
175                .get("inputs")
176                .copied()
177                .ok_or(anyhow!("inputs not found (required for #{} syntax)", field))?;
178
179            match inputs_value {
180                Value::Record(record_ptr) => {
181                    let borrowed_heap = heap.borrow();
182                    let record = record_ptr.reify(&borrowed_heap).as_record()?;
183                    // Return null if field doesn't exist (consistent with inputs.field behavior)
184                    Ok(record.get(field).copied().unwrap_or(Value::Null))
185                }
186                _ => Err(anyhow!("inputs must be a record to use #{} syntax", field)),
187            }
188        }
189        Expr::BuiltIn(built_in) => Ok(Value::BuiltIn(*built_in)),
190        Expr::List(exprs) => {
191            let values = exprs
192                .iter()
193                .map(|e| evaluate_ast(e, Rc::clone(&heap), Rc::clone(&bindings), call_depth))
194                .collect::<Result<Vec<Value>>>()?;
195
196            // Flatten spreads
197            let mut flattened = Vec::new();
198
199            for value in values {
200                match value {
201                    Value::Spread(spread_ptr) => {
202                        let spread_values = flatten_spread_value(&spread_ptr, &heap)?;
203                        flattened.extend(spread_values);
204                    }
205                    _ => flattened.push(value),
206                }
207            }
208
209            Ok(heap.borrow_mut().insert_list(flattened))
210        }
211        Expr::Record(entries) => {
212            let mut record = IndexMap::new();
213
214            for entry in entries {
215                match &entry.key {
216                    RecordKey::Static(key) => {
217                        let value = evaluate_ast(
218                            &entry.value,
219                            Rc::clone(&heap),
220                            Rc::clone(&bindings),
221                            call_depth,
222                        )?;
223                        record.insert(key.clone(), value);
224                    }
225                    RecordKey::Dynamic(key_expr) => {
226                        let key_value = evaluate_ast(
227                            key_expr,
228                            Rc::clone(&heap),
229                            Rc::clone(&bindings),
230                            call_depth,
231                        )?;
232                        let key = key_value.as_string(&heap.borrow())?.to_string();
233                        let value = evaluate_ast(
234                            &entry.value,
235                            Rc::clone(&heap),
236                            Rc::clone(&bindings),
237                            call_depth,
238                        )?;
239                        record.insert(key, value);
240                    }
241                    RecordKey::Shorthand(ident) => {
242                        let value = bindings
243                            .borrow()
244                            .get(ident)
245                            .copied()
246                            .ok_or(anyhow!("unknown identifier: {}", ident))?;
247                        record.insert(ident.clone(), value);
248                    }
249                    RecordKey::Spread(spread_expr) => {
250                        let spread_value = evaluate_ast(
251                            spread_expr,
252                            Rc::clone(&heap),
253                            Rc::clone(&bindings),
254                            call_depth,
255                        )?;
256
257                        if let Spread(iterable) = spread_value {
258                            match iterable {
259                                IterablePointer::List(pointer) => {
260                                    let borrowed_heap = &heap.borrow();
261                                    pointer
262                                        .reify(borrowed_heap)
263                                        .as_list()?
264                                        .iter()
265                                        .enumerate()
266                                        .for_each(|(i, value)| {
267                                            record.insert(i.to_string(), *value);
268                                        });
269                                }
270                                IterablePointer::String(pointer) => {
271                                    let s = {
272                                        let borrowed_heap = &heap.borrow();
273                                        pointer.reify(borrowed_heap).as_string()?.to_string()
274                                    };
275
276                                    s.chars().enumerate().for_each(|(i, c)| {
277                                        record.insert(
278                                            i.to_string(),
279                                            heap.borrow_mut().insert_string(c.to_string()),
280                                        );
281                                    });
282                                }
283                                IterablePointer::Record(pointer) => {
284                                    let borrowed_heap = &heap.borrow();
285                                    let spread_record = pointer.reify(borrowed_heap).as_record()?;
286
287                                    for (k, v) in spread_record {
288                                        record.insert(k.clone(), *v);
289                                    }
290                                }
291                            }
292                        }
293                    }
294                }
295            }
296
297            Ok(heap.borrow_mut().insert_record(record))
298        }
299        Expr::Lambda { args, body } => {
300            // Capture variables used in the lambda body
301            let mut captured_scope = HashMap::new();
302            let mut referenced_vars = Vec::new();
303            collect_free_variables(body, &mut referenced_vars, &mut HashSet::new());
304
305            let current_bindings = bindings.borrow();
306            for var in referenced_vars {
307                if current_bindings.contains_key(&var) && !is_built_in_function(&var) {
308                    captured_scope.insert(var.clone(), current_bindings[&var]);
309                }
310            }
311
312            let lambda = heap.borrow_mut().insert_lambda(LambdaDef {
313                name: None,
314                args: args.clone(),
315                body: (**body).clone(),
316                scope: captured_scope,
317            });
318
319            Ok(lambda)
320        }
321        Expr::Assignment { ident, value } => {
322            if is_built_in_function(ident) {
323                return Err(anyhow!(
324                    "{} is the name of a built-in function, and cannot be reassigned",
325                    ident
326                ));
327            }
328
329            if ident == "constants"
330                || ident == "if"
331                || ident == "then"
332                || ident == "else"
333                || ident == "true"
334                || ident == "false"
335                || ident == "null"
336                || ident == "inputs"
337                || ident == "and"
338                || ident == "or"
339            {
340                return Err(anyhow!("{} is a keyword, and cannot be reassigned", ident));
341            }
342
343            // Check if the variable already exists (immutability check)
344            if bindings.borrow().contains_key(ident) {
345                return Err(anyhow!(
346                    "{} is already defined, and cannot be reassigned",
347                    ident
348                ));
349            }
350
351            let val = evaluate_ast(value, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
352
353            // Set lambda name if assigning a lambda
354            if let Value::Lambda(lambda_ptr) = val {
355                let mut borrowed_heap = heap.borrow_mut();
356                if let Some(HeapValue::Lambda(lambda_def)) =
357                    borrowed_heap.get_mut(lambda_ptr.index())
358                {
359                    lambda_def.name = Some(ident.clone());
360                }
361            }
362
363            bindings.borrow_mut().insert(ident.clone(), val);
364            Ok(val)
365        }
366        Expr::Conditional {
367            condition,
368            then_expr,
369            else_expr,
370        } => {
371            let cond_val = evaluate_ast(
372                condition,
373                Rc::clone(&heap),
374                Rc::clone(&bindings),
375                call_depth,
376            )?;
377
378            if cond_val.as_bool()? {
379                evaluate_ast(then_expr, heap, bindings, call_depth)
380            } else {
381                evaluate_ast(else_expr, heap, bindings, call_depth)
382            }
383        }
384        Expr::DoBlock {
385            statements,
386            return_expr,
387        } => {
388            // Create new scope from current bindings
389            let new_bindings = bindings.borrow().clone();
390            let block_bindings = Rc::new(RefCell::new(new_bindings));
391
392            // Execute statements with shadowing allowed
393            for stmt in statements {
394                match stmt {
395                    DoStatement::Expression(expr) => {
396                        evaluate_do_block_expr(
397                            expr,
398                            Rc::clone(&heap),
399                            Rc::clone(&block_bindings),
400                            call_depth,
401                        )?;
402                    }
403                    DoStatement::Comment(_) => {} // Skip comments
404                }
405            }
406
407            // Return the final expression
408            evaluate_do_block_expr(return_expr, heap, block_bindings, call_depth)
409        }
410        Expr::Call { func, args } => {
411            let func_val = evaluate_ast(func, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
412            let arg_vals_raw = args
413                .iter()
414                .map(|arg| evaluate_ast(arg, Rc::clone(&heap), Rc::clone(&bindings), call_depth))
415                .collect::<Result<Vec<_>>>()?;
416
417            // Flatten any spread arguments
418            let mut arg_vals = Vec::new();
419
420            for value in arg_vals_raw {
421                match value {
422                    Value::Spread(spread_ptr) => {
423                        let spread_values = flatten_spread_value(&spread_ptr, &heap)?;
424                        arg_vals.extend(spread_values);
425                    }
426                    _ => arg_vals.push(value),
427                }
428            }
429
430            if !func_val.is_lambda() && !func_val.is_built_in() {
431                return Err(anyhow!(
432                    "can't call a non-function: {}",
433                    func_val.stringify_internal(&heap.borrow())
434                ));
435            }
436
437            let def = {
438                let borrowed_heap = &heap.borrow();
439                get_function_def(&func_val, borrowed_heap).ok_or_else(|| {
440                    anyhow!(
441                        "unknown function: {}",
442                        func_val.stringify_internal(borrowed_heap)
443                    )
444                })?
445            };
446
447            def.call(func_val, arg_vals, heap, bindings, call_depth)
448        }
449        Expr::Access { expr, index } => {
450            let val = evaluate_ast(expr, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
451            let idx_val = evaluate_ast(index, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
452
453            match val {
454                Value::Record(record) => {
455                    let borrowed_heap = &heap.borrow();
456                    let record = record.reify(borrowed_heap).as_record()?;
457                    let key = idx_val.as_string(borrowed_heap)?;
458                    Ok(record.get(key).copied().unwrap_or(Value::Null))
459                }
460                Value::List(list) => {
461                    let borrowed_heap = &heap.borrow();
462                    let list = list.reify(borrowed_heap).as_list()?;
463                    let index = usize::try_from(idx_val.as_number()? as u64)?;
464                    Ok(list.get(index).copied().unwrap_or(Value::Null))
465                }
466                Value::String(string) => {
467                    let string = string.reify(&heap.borrow()).as_string()?.to_string();
468                    let index = usize::try_from(idx_val.as_number()? as u64)?;
469
470                    Ok(string
471                        .chars()
472                        .nth(index)
473                        .map(|c| heap.borrow_mut().insert_string(c.to_string()))
474                        .unwrap_or(Value::Null))
475                }
476                _ => Err(anyhow!(
477                    "expected a record, list, string, but got a {}",
478                    val.get_type()
479                )),
480            }
481        }
482        Expr::DotAccess { expr, field } => {
483            let val = evaluate_ast(expr, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
484            let borrowed_heap = &heap.borrow();
485
486            match val {
487                Value::Record(record) => {
488                    let record = record.reify(borrowed_heap).as_record()?;
489                    Ok(record.get(field).copied().unwrap_or(Value::Null))
490                }
491                _ => Err(anyhow!("expected a record, but got a {}", val.get_type())),
492            }
493        }
494        Expr::BinaryOp { op, left, right } => {
495            evaluate_binary_op_ast(*op, left, right, heap, bindings, call_depth)
496        }
497        Expr::UnaryOp { op, expr } => {
498            let val = evaluate_ast(expr, heap, bindings, call_depth)?;
499
500            match op {
501                UnaryOp::Negate => Ok(Number(-val.as_number()?)),
502                UnaryOp::Not | UnaryOp::Invert => Ok(Bool(!val.as_bool()?)),
503            }
504        }
505        Expr::PostfixOp { op, expr } => {
506            let val = evaluate_ast(expr, heap, bindings, call_depth)?;
507
508            match op {
509                PostfixOp::Factorial => {
510                    let n = val.as_number()?;
511                    if n >= 0.0 && n == (n as u64) as f64 {
512                        Ok(Number(
513                            (1..(n as u64) + 1).map(|x| x as f64).product::<f64>(),
514                        ))
515                    } else {
516                        Err(anyhow!("factorial only works on non-negative integers"))
517                    }
518                }
519            }
520        }
521        Expr::Spread(expr) => {
522            let val = evaluate_ast(expr, heap, bindings, call_depth)?;
523
524            match val {
525                List(pointer) => Ok(Spread(IterablePointer::List(pointer))),
526                Value::String(pointer) => Ok(Spread(IterablePointer::String(pointer))),
527                Value::Record(pointer) => Ok(Spread(IterablePointer::Record(pointer))),
528                _ => Err(anyhow!("expected a list, record, or string")),
529            }
530        }
531    }
532}
533
534// Helper function to collect free variables in an expression
535fn collect_free_variables(expr: &Expr, vars: &mut Vec<String>, bound: &mut HashSet<String>) {
536    match expr {
537        Expr::Identifier(name) => {
538            if !bound.contains(name) && name != "infinity" && name != "inf" && name != "constants" {
539                vars.push(name.clone());
540            }
541        }
542        Expr::Lambda { args, body } => {
543            let mut new_bound = bound.clone();
544            for arg in args {
545                new_bound.insert(arg.get_name().to_string());
546            }
547            collect_free_variables(body, vars, &mut new_bound);
548        }
549        Expr::BinaryOp { left, right, .. } => {
550            collect_free_variables(left, vars, bound);
551            collect_free_variables(right, vars, bound);
552        }
553        Expr::UnaryOp { expr, .. } | Expr::PostfixOp { expr, .. } | Expr::Spread(expr) => {
554            collect_free_variables(expr, vars, bound);
555        }
556        Expr::Call { func, args } => {
557            collect_free_variables(func, vars, bound);
558            for arg in args {
559                collect_free_variables(arg, vars, bound);
560            }
561        }
562        Expr::Access { expr, index } => {
563            collect_free_variables(expr, vars, bound);
564            collect_free_variables(index, vars, bound);
565        }
566        Expr::DotAccess { expr, .. } => {
567            collect_free_variables(expr, vars, bound);
568        }
569        Expr::Conditional {
570            condition,
571            then_expr,
572            else_expr,
573        } => {
574            collect_free_variables(condition, vars, bound);
575            collect_free_variables(then_expr, vars, bound);
576            collect_free_variables(else_expr, vars, bound);
577        }
578        Expr::Assignment { value, .. } => {
579            collect_free_variables(value, vars, bound);
580        }
581        Expr::List(exprs) => {
582            for expr in exprs {
583                collect_free_variables(expr, vars, bound);
584            }
585        }
586        Expr::Record(entries) => {
587            for entry in entries {
588                match &entry.key {
589                    RecordKey::Dynamic(expr) | RecordKey::Spread(expr) => {
590                        collect_free_variables(expr, vars, bound);
591                    }
592                    _ => {}
593                }
594                if !matches!(entry.key, RecordKey::Shorthand(_) | RecordKey::Spread(_)) {
595                    collect_free_variables(&entry.value, vars, bound);
596                }
597            }
598        }
599        Expr::DoBlock {
600            statements,
601            return_expr,
602        } => {
603            // Create a new scope for the do block
604            let mut block_bound = bound.clone();
605
606            for stmt in statements {
607                if let DoStatement::Expression(expr) = stmt {
608                    // Check if this is an assignment and add the variable to block scope
609                    if let Expr::Assignment { ident, value } = expr {
610                        // First collect free variables from the value
611                        collect_free_variables(value, vars, &mut block_bound);
612                        // Then add the assigned variable to the block's bound set
613                        block_bound.insert(ident.clone());
614                    } else {
615                        collect_free_variables(expr, vars, &mut block_bound);
616                    }
617                }
618            }
619            collect_free_variables(return_expr, vars, &mut block_bound);
620        }
621        _ => {}
622    }
623}
624
625// Validate that a value (especially lambdas) is portable for output
626pub fn validate_portable_value(
627    value: &Value,
628    heap: &Heap,
629    bindings: &HashMap<String, Value>,
630) -> Result<()> {
631    match value {
632        Value::Lambda(lambda_ptr) => {
633            // Get the lambda definition
634            if let Some(HeapValue::Lambda(lambda_def)) = heap.get(lambda_ptr.index()) {
635                // Collect free variables from the lambda body
636                let mut free_vars = Vec::new();
637                let mut bound_vars = HashSet::new();
638
639                // Add lambda arguments to bound variables
640                for arg in &lambda_def.args {
641                    bound_vars.insert(arg.get_name().to_string());
642                }
643
644                // Also add variables from the captured scope as bound
645                for key in lambda_def.scope.keys() {
646                    bound_vars.insert(key.clone());
647                }
648
649                collect_free_variables(&lambda_def.body, &mut free_vars, &mut bound_vars);
650
651                // Check that all free variables are either:
652                // 1. In the captured scope (already captured when function was defined)
653                // 2. Built-in functions
654                // 3. Self-reference (for recursion) - if the lambda has a name
655                // 4. Currently bound in the environment (for late-bound functions)
656                for var in &free_vars {
657                    let is_self_reference = lambda_def.name.as_ref() == Some(var);
658
659                    if !lambda_def.scope.contains_key(var)
660                        && !is_built_in_function(var)
661                        && !is_self_reference
662                        && !bindings.contains_key(var)
663                    {
664                        return Err(anyhow!(
665                            "Function contains unbound variable \"{}\". Output functions must be self-contained.",
666                            var
667                        ));
668                    }
669                }
670            }
671            Ok(())
672        }
673        Value::List(list_ptr) => {
674            // Recursively validate list elements
675            let list = list_ptr.reify(heap).as_list()?;
676            for item in list {
677                validate_portable_value(item, heap, bindings)?;
678            }
679            Ok(())
680        }
681        Value::Record(record_ptr) => {
682            // Recursively validate record values
683            let record = record_ptr.reify(heap).as_record()?;
684            for value in record.values() {
685                validate_portable_value(value, heap, bindings)?;
686            }
687            Ok(())
688        }
689        // Other value types are always portable (including built-in functions)
690        _ => Ok(()),
691    }
692}
693
694// Evaluate binary operations on AST
695fn evaluate_binary_op_ast(
696    op: BinaryOp,
697    left: &Expr,
698    right: &Expr,
699    heap: Rc<RefCell<Heap>>,
700    bindings: Rc<RefCell<HashMap<String, Value>>>,
701    call_depth: usize,
702) -> Result<Value> {
703    let lhs = evaluate_ast(left, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
704    let rhs = evaluate_ast(right, Rc::clone(&heap), Rc::clone(&bindings), call_depth)?;
705
706    // Handle dot operators first - they never broadcast
707    match op {
708        BinaryOp::DotEqual => return Ok(Bool(lhs.equals(&rhs, &heap.borrow())?)),
709        BinaryOp::DotNotEqual => return Ok(Bool(!lhs.equals(&rhs, &heap.borrow())?)),
710        BinaryOp::DotLess => {
711            return match lhs.compare(&rhs, &heap.borrow())? {
712                Some(std::cmp::Ordering::Less) => Ok(Bool(true)),
713                _ => Ok(Bool(false)),
714            };
715        }
716        BinaryOp::DotLessEq => {
717            return match lhs.compare(&rhs, &heap.borrow())? {
718                Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal) => Ok(Bool(true)),
719                _ => Ok(Bool(false)),
720            };
721        }
722        BinaryOp::DotGreater => {
723            return match lhs.compare(&rhs, &heap.borrow())? {
724                Some(std::cmp::Ordering::Greater) => Ok(Bool(true)),
725                _ => Ok(Bool(false)),
726            };
727        }
728        BinaryOp::DotGreaterEq => {
729            return match lhs.compare(&rhs, &heap.borrow())? {
730                Some(std::cmp::Ordering::Greater) | Some(std::cmp::Ordering::Equal) => {
731                    Ok(Bool(true))
732                }
733                _ => Ok(Bool(false)),
734            };
735        }
736        _ => {} // Continue to regular operator handling
737    }
738
739    match (lhs, rhs) {
740        (_, Value::List(_)) if op == BinaryOp::Into => Err(anyhow!(
741            "'into' operator requires a function on the right side, not a list"
742        )),
743        (Value::List(list_l), Value::List(list_r)) => {
744            let (l_vec, r_vec) = {
745                let borrowed_heap = &heap.borrow();
746                (
747                    list_l.reify(borrowed_heap).as_list()?.clone(),
748                    list_r.reify(borrowed_heap).as_list()?.clone(),
749                )
750            };
751
752            if l_vec.len() != r_vec.len() {
753                return Err(anyhow!(
754                    "left- and right-hand-side lists must be the same length"
755                ));
756            }
757
758            match op {
759                BinaryOp::Equal => {
760                    let mapped_list = l_vec
761                        .iter()
762                        .zip(&r_vec)
763                        .map(|(l, r)| Ok(Bool(l.equals(r, &heap.borrow())?)))
764                        .collect::<Result<Vec<Value>>>()?;
765                    Ok(heap.borrow_mut().insert_list(mapped_list))
766                }
767                BinaryOp::NotEqual => {
768                    let mapped_list = l_vec
769                        .iter()
770                        .zip(&r_vec)
771                        .map(|(l, r)| Ok(Bool(!l.equals(r, &heap.borrow())?)))
772                        .collect::<Result<Vec<Value>>>()?;
773                    Ok(heap.borrow_mut().insert_list(mapped_list))
774                }
775                BinaryOp::Less => {
776                    let mapped_list = l_vec
777                        .iter()
778                        .zip(&r_vec)
779                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
780                            Some(std::cmp::Ordering::Less) => Ok(Bool(true)),
781                            _ => Ok(Bool(false)),
782                        })
783                        .collect::<Result<Vec<Value>>>()?;
784                    Ok(heap.borrow_mut().insert_list(mapped_list))
785                }
786                BinaryOp::LessEq => {
787                    let mapped_list = l_vec
788                        .iter()
789                        .zip(&r_vec)
790                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
791                            Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal) => {
792                                Ok(Bool(true))
793                            }
794                            _ => Ok(Bool(false)),
795                        })
796                        .collect::<Result<Vec<Value>>>()?;
797                    Ok(heap.borrow_mut().insert_list(mapped_list))
798                }
799                BinaryOp::Greater => {
800                    let mapped_list = l_vec
801                        .iter()
802                        .zip(&r_vec)
803                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
804                            Some(std::cmp::Ordering::Greater) => Ok(Bool(true)),
805                            _ => Ok(Bool(false)),
806                        })
807                        .collect::<Result<Vec<Value>>>()?;
808                    Ok(heap.borrow_mut().insert_list(mapped_list))
809                }
810                BinaryOp::GreaterEq => {
811                    let mapped_list = l_vec
812                        .iter()
813                        .zip(&r_vec)
814                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
815                            Some(std::cmp::Ordering::Greater) | Some(std::cmp::Ordering::Equal) => {
816                                Ok(Bool(true))
817                            }
818                            _ => Ok(Bool(false)),
819                        })
820                        .collect::<Result<Vec<Value>>>()?;
821                    Ok(heap.borrow_mut().insert_list(mapped_list))
822                }
823                BinaryOp::And | BinaryOp::NaturalAnd => {
824                    let mapped_list = l_vec
825                        .iter()
826                        .zip(&r_vec)
827                        .map(|(l, r)| Ok(Bool(l.as_bool()? && r.as_bool()?)))
828                        .collect::<Result<Vec<Value>>>()?;
829                    Ok(heap.borrow_mut().insert_list(mapped_list))
830                }
831                BinaryOp::Or | BinaryOp::NaturalOr => {
832                    let mapped_list = l_vec
833                        .iter()
834                        .zip(&r_vec)
835                        .map(|(l, r)| Ok(Bool(l.as_bool()? || r.as_bool()?)))
836                        .collect::<Result<Vec<Value>>>()?;
837                    Ok(heap.borrow_mut().insert_list(mapped_list))
838                }
839                BinaryOp::Add => {
840                    let mapped_list = l_vec
841                        .iter()
842                        .zip(&r_vec)
843                        .map(|(l, r)| match (l, r) {
844                            (Value::String(_), Value::String(_)) => {
845                                let (l_str, r_str) = {
846                                    (
847                                        l.as_string(&heap.borrow())?.to_string(),
848                                        r.as_string(&heap.borrow())?.to_string(),
849                                    )
850                                };
851                                Ok(heap
852                                    .borrow_mut()
853                                    .insert_string(format!("{}{}", l_str, r_str)))
854                            }
855                            (Value::Number(_), Value::Number(_)) => {
856                                Ok(Number(l.as_number()? + r.as_number()?))
857                            }
858                            _ => Err(anyhow!("can't add {} and {}", l.get_type(), r.get_type())),
859                        })
860                        .collect::<Result<Vec<Value>>>()?;
861                    Ok(heap.borrow_mut().insert_list(mapped_list))
862                }
863                BinaryOp::Subtract => {
864                    let mapped_list = l_vec
865                        .iter()
866                        .zip(&r_vec)
867                        .map(|(l, r)| Ok(Number(l.as_number()? - r.as_number()?)))
868                        .collect::<Result<Vec<Value>>>()?;
869                    Ok(heap.borrow_mut().insert_list(mapped_list))
870                }
871                BinaryOp::Multiply => {
872                    let mapped_list = l_vec
873                        .iter()
874                        .zip(&r_vec)
875                        .map(|(l, r)| Ok(Number(l.as_number()? * r.as_number()?)))
876                        .collect::<Result<Vec<Value>>>()?;
877                    Ok(heap.borrow_mut().insert_list(mapped_list))
878                }
879                BinaryOp::Divide => {
880                    let mapped_list = l_vec
881                        .iter()
882                        .zip(&r_vec)
883                        .map(|(l, r)| Ok(Number(l.as_number()? / r.as_number()?)))
884                        .collect::<Result<Vec<Value>>>()?;
885                    Ok(heap.borrow_mut().insert_list(mapped_list))
886                }
887                BinaryOp::Modulo => {
888                    let mapped_list = l_vec
889                        .iter()
890                        .zip(&r_vec)
891                        .map(|(l, r)| Ok(Number(l.as_number()? % r.as_number()?)))
892                        .collect::<Result<Vec<Value>>>()?;
893                    Ok(heap.borrow_mut().insert_list(mapped_list))
894                }
895                BinaryOp::Power => {
896                    let mapped_list = l_vec
897                        .iter()
898                        .zip(&r_vec)
899                        .map(|(l, r)| Ok(Number(l.as_number()?.powf(r.as_number()?))))
900                        .collect::<Result<Vec<Value>>>()?;
901                    Ok(heap.borrow_mut().insert_list(mapped_list))
902                }
903                BinaryOp::Coalesce => {
904                    let mapped_list = l_vec
905                        .iter()
906                        .zip(&r_vec)
907                        .map(|(l, r)| if *l == Value::Null { Ok(*r) } else { Ok(*l) })
908                        .collect::<Result<Vec<Value>>>()?;
909                    Ok(heap.borrow_mut().insert_list(mapped_list))
910                }
911                BinaryOp::Via => {
912                    let mapped_list = l_vec
913                        .iter()
914                        .zip(&r_vec)
915                        .map(|(l, r)| {
916                            if !r.is_lambda() && !r.is_built_in() {
917                                return Err(anyhow!(
918                                    "right-hand iterable contains non-function {}",
919                                    r.stringify_internal(&heap.borrow())
920                                ));
921                            }
922
923                            let def = {
924                                let borrowed_heap = &heap.borrow();
925                                get_function_def(r, borrowed_heap).ok_or_else(|| {
926                                    anyhow!(
927                                        "unknown function: {}",
928                                        r.stringify_internal(borrowed_heap)
929                                    )
930                                })?
931                            };
932
933                            def.call(
934                                *r,
935                                vec![*l],
936                                Rc::clone(&heap),
937                                Rc::clone(&bindings),
938                                call_depth,
939                            )
940                        })
941                        .collect::<Result<Vec<Value>>>()?;
942                    Ok(heap.borrow_mut().insert_list(mapped_list))
943                }
944                BinaryOp::Into => {
945                    // This should never be reached due to early check above
946                    unreachable!("Into operator should not reach list-to-list evaluation")
947                }
948                BinaryOp::DotEqual
949                | BinaryOp::DotNotEqual
950                | BinaryOp::DotLess
951                | BinaryOp::DotLessEq
952                | BinaryOp::DotGreater
953                | BinaryOp::DotGreaterEq => {
954                    // Dot operators are handled before this match, so this should never be reached
955                    unreachable!("Dot operators should be handled before list broadcasting")
956                }
957            }
958        }
959        (Value::List(list), scalar) | (scalar, Value::List(list)) => {
960            let (l_vec, is_list_first) = {
961                let borrowed_heap = &heap.borrow();
962                if matches!(lhs, Value::List(_)) {
963                    (list.reify(borrowed_heap).as_list()?.clone(), true)
964                } else {
965                    (list.reify(borrowed_heap).as_list()?.clone(), false)
966                }
967            };
968
969            match op {
970                BinaryOp::Equal => {
971                    let mapped_list = l_vec
972                        .iter()
973                        .map(|v| Ok(Bool(v.equals(&scalar, &heap.borrow())?)))
974                        .collect::<Result<Vec<Value>>>()?;
975                    Ok(heap.borrow_mut().insert_list(mapped_list))
976                }
977                BinaryOp::NotEqual => {
978                    let mapped_list = l_vec
979                        .iter()
980                        .map(|v| Ok(Bool(!v.equals(&scalar, &heap.borrow())?)))
981                        .collect::<Result<Vec<Value>>>()?;
982                    Ok(heap.borrow_mut().insert_list(mapped_list))
983                }
984                BinaryOp::Less => {
985                    let mapped_list = l_vec
986                        .iter()
987                        .map(|v| {
988                            let ordering = if is_list_first {
989                                v.compare(&scalar, &heap.borrow())?
990                            } else {
991                                scalar.compare(v, &heap.borrow())?
992                            };
993                            match ordering {
994                                Some(std::cmp::Ordering::Less) => Ok(Bool(true)),
995                                _ => Ok(Bool(false)),
996                            }
997                        })
998                        .collect::<Result<Vec<Value>>>()?;
999                    Ok(heap.borrow_mut().insert_list(mapped_list))
1000                }
1001                BinaryOp::LessEq => {
1002                    let mapped_list = l_vec
1003                        .iter()
1004                        .map(|v| {
1005                            let ordering = if is_list_first {
1006                                v.compare(&scalar, &heap.borrow())?
1007                            } else {
1008                                scalar.compare(v, &heap.borrow())?
1009                            };
1010                            match ordering {
1011                                Some(std::cmp::Ordering::Less)
1012                                | Some(std::cmp::Ordering::Equal) => Ok(Bool(true)),
1013                                _ => Ok(Bool(false)),
1014                            }
1015                        })
1016                        .collect::<Result<Vec<Value>>>()?;
1017                    Ok(heap.borrow_mut().insert_list(mapped_list))
1018                }
1019                BinaryOp::Greater => {
1020                    let mapped_list = l_vec
1021                        .iter()
1022                        .map(|v| {
1023                            let ordering = if is_list_first {
1024                                v.compare(&scalar, &heap.borrow())?
1025                            } else {
1026                                scalar.compare(v, &heap.borrow())?
1027                            };
1028                            match ordering {
1029                                Some(std::cmp::Ordering::Greater) => Ok(Bool(true)),
1030                                _ => Ok(Bool(false)),
1031                            }
1032                        })
1033                        .collect::<Result<Vec<Value>>>()?;
1034                    Ok(heap.borrow_mut().insert_list(mapped_list))
1035                }
1036                BinaryOp::GreaterEq => {
1037                    let mapped_list = l_vec
1038                        .iter()
1039                        .map(|v| {
1040                            let ordering = if is_list_first {
1041                                v.compare(&scalar, &heap.borrow())?
1042                            } else {
1043                                scalar.compare(v, &heap.borrow())?
1044                            };
1045                            match ordering {
1046                                Some(std::cmp::Ordering::Greater)
1047                                | Some(std::cmp::Ordering::Equal) => Ok(Bool(true)),
1048                                _ => Ok(Bool(false)),
1049                            }
1050                        })
1051                        .collect::<Result<Vec<Value>>>()?;
1052                    Ok(heap.borrow_mut().insert_list(mapped_list))
1053                }
1054                BinaryOp::And | BinaryOp::NaturalAnd => {
1055                    let mapped_list = if is_list_first {
1056                        l_vec
1057                            .iter()
1058                            .map(|v| Ok(Bool(v.as_bool()? && scalar.as_bool()?)))
1059                            .collect::<Result<Vec<Value>>>()?
1060                    } else {
1061                        l_vec
1062                            .iter()
1063                            .map(|v| Ok(Bool(scalar.as_bool()? && v.as_bool()?)))
1064                            .collect::<Result<Vec<Value>>>()?
1065                    };
1066                    Ok(heap.borrow_mut().insert_list(mapped_list))
1067                }
1068                BinaryOp::Or | BinaryOp::NaturalOr => {
1069                    let mapped_list = if is_list_first {
1070                        l_vec
1071                            .iter()
1072                            .map(|v| Ok(Bool(v.as_bool()? || scalar.as_bool()?)))
1073                            .collect::<Result<Vec<Value>>>()?
1074                    } else {
1075                        l_vec
1076                            .iter()
1077                            .map(|v| Ok(Bool(scalar.as_bool()? || v.as_bool()?)))
1078                            .collect::<Result<Vec<Value>>>()?
1079                    };
1080                    Ok(heap.borrow_mut().insert_list(mapped_list))
1081                }
1082                BinaryOp::Add => {
1083                    let mapped_list = if is_list_first {
1084                        l_vec
1085                            .iter()
1086                            .map(|v| match (v, &scalar) {
1087                                (Value::String(_), Value::String(_)) => {
1088                                    let (v_str, s_str) = {
1089                                        (
1090                                            v.as_string(&heap.borrow())?.to_string(),
1091                                            scalar.as_string(&heap.borrow())?.to_string(),
1092                                        )
1093                                    };
1094                                    Ok(heap
1095                                        .borrow_mut()
1096                                        .insert_string(format!("{}{}", v_str, s_str)))
1097                                }
1098                                (Value::Number(_), Value::Number(_)) => {
1099                                    Ok(Number(v.as_number()? + scalar.as_number()?))
1100                                }
1101                                _ => Err(anyhow!(
1102                                    "can't add {} and {}",
1103                                    v.get_type(),
1104                                    scalar.get_type()
1105                                )),
1106                            })
1107                            .collect::<Result<Vec<Value>>>()?
1108                    } else {
1109                        l_vec
1110                            .iter()
1111                            .map(|v| match (&scalar, v) {
1112                                (Value::String(_), Value::String(_)) => {
1113                                    let (s_str, v_str) = {
1114                                        (
1115                                            scalar.as_string(&heap.borrow())?.to_string(),
1116                                            v.as_string(&heap.borrow())?.to_string(),
1117                                        )
1118                                    };
1119                                    Ok(heap
1120                                        .borrow_mut()
1121                                        .insert_string(format!("{}{}", s_str, v_str)))
1122                                }
1123                                (Value::Number(_), Value::Number(_)) => {
1124                                    Ok(Number(scalar.as_number()? + v.as_number()?))
1125                                }
1126                                _ => Err(anyhow!(
1127                                    "can't add {} and {}",
1128                                    scalar.get_type(),
1129                                    v.get_type()
1130                                )),
1131                            })
1132                            .collect::<Result<Vec<Value>>>()?
1133                    };
1134                    Ok(heap.borrow_mut().insert_list(mapped_list))
1135                }
1136                BinaryOp::Subtract => {
1137                    let mapped_list = if is_list_first {
1138                        l_vec
1139                            .iter()
1140                            .map(|v| Ok(Number(v.as_number()? - scalar.as_number()?)))
1141                            .collect::<Result<Vec<Value>>>()?
1142                    } else {
1143                        l_vec
1144                            .iter()
1145                            .map(|v| Ok(Number(scalar.as_number()? - v.as_number()?)))
1146                            .collect::<Result<Vec<Value>>>()?
1147                    };
1148                    Ok(heap.borrow_mut().insert_list(mapped_list))
1149                }
1150                BinaryOp::Multiply => {
1151                    let mapped_list = l_vec
1152                        .iter()
1153                        .map(|v| Ok(Number(v.as_number()? * scalar.as_number()?)))
1154                        .collect::<Result<Vec<Value>>>()?;
1155                    Ok(heap.borrow_mut().insert_list(mapped_list))
1156                }
1157                BinaryOp::Divide => {
1158                    let mapped_list = if is_list_first {
1159                        l_vec
1160                            .iter()
1161                            .map(|v| Ok(Number(v.as_number()? / scalar.as_number()?)))
1162                            .collect::<Result<Vec<Value>>>()?
1163                    } else {
1164                        l_vec
1165                            .iter()
1166                            .map(|v| Ok(Number(scalar.as_number()? / v.as_number()?)))
1167                            .collect::<Result<Vec<Value>>>()?
1168                    };
1169                    Ok(heap.borrow_mut().insert_list(mapped_list))
1170                }
1171                BinaryOp::Modulo => {
1172                    let mapped_list = if is_list_first {
1173                        l_vec
1174                            .iter()
1175                            .map(|v| Ok(Number(v.as_number()? % scalar.as_number()?)))
1176                            .collect::<Result<Vec<Value>>>()?
1177                    } else {
1178                        l_vec
1179                            .iter()
1180                            .map(|v| Ok(Number(scalar.as_number()? % v.as_number()?)))
1181                            .collect::<Result<Vec<Value>>>()?
1182                    };
1183                    Ok(heap.borrow_mut().insert_list(mapped_list))
1184                }
1185                BinaryOp::Power => {
1186                    let mapped_list = if is_list_first {
1187                        l_vec
1188                            .iter()
1189                            .map(|v| Ok(Number(v.as_number()?.powf(scalar.as_number()?))))
1190                            .collect::<Result<Vec<Value>>>()?
1191                    } else {
1192                        l_vec
1193                            .iter()
1194                            .map(|v| Ok(Number(scalar.as_number()?.powf(v.as_number()?))))
1195                            .collect::<Result<Vec<Value>>>()?
1196                    };
1197                    Ok(heap.borrow_mut().insert_list(mapped_list))
1198                }
1199                BinaryOp::Coalesce => {
1200                    let mapped_list = if is_list_first {
1201                        l_vec
1202                            .iter()
1203                            .map(|v| {
1204                                if *v == Value::Null {
1205                                    Ok(scalar)
1206                                } else {
1207                                    Ok(*v)
1208                                }
1209                            })
1210                            .collect::<Result<Vec<Value>>>()?
1211                    } else {
1212                        l_vec
1213                            .iter()
1214                            .map(|v| {
1215                                if scalar == Value::Null {
1216                                    Ok(*v)
1217                                } else {
1218                                    Ok(scalar)
1219                                }
1220                            })
1221                            .collect::<Result<Vec<Value>>>()?
1222                    };
1223                    Ok(heap.borrow_mut().insert_list(mapped_list))
1224                }
1225                BinaryOp::Via => {
1226                    if is_list_first {
1227                        if !scalar.is_callable() {
1228                            return Err(anyhow!(
1229                                "can't call a non-function: {}",
1230                                scalar.stringify_internal(&heap.borrow())
1231                            ));
1232                        }
1233
1234                        let list = heap.borrow_mut().insert_list(l_vec.clone());
1235
1236                        let call_result = get_built_in_function_def_by_ident("map").unwrap().call(
1237                            scalar,
1238                            vec![list, scalar],
1239                            Rc::clone(&heap),
1240                            Rc::clone(&bindings),
1241                            call_depth,
1242                        )?;
1243
1244                        let mapped_list = { call_result.as_list(&heap.borrow())?.clone() };
1245
1246                        Ok(heap.borrow_mut().insert_list(mapped_list))
1247                    } else {
1248                        Err(anyhow!("map operator requires function on right side"))
1249                    }
1250                }
1251                BinaryOp::Into => {
1252                    if is_list_first {
1253                        if !scalar.is_callable() {
1254                            return Err(anyhow!(
1255                                "can't call a non-function: {}",
1256                                scalar.stringify_internal(&heap.borrow())
1257                            ));
1258                        }
1259
1260                        let list = heap.borrow_mut().insert_list(l_vec.clone());
1261
1262                        let def = {
1263                            let borrowed_heap = &heap.borrow();
1264                            get_function_def(&scalar, borrowed_heap).ok_or_else(|| {
1265                                anyhow!(
1266                                    "unknown function: {}",
1267                                    scalar.stringify_internal(borrowed_heap)
1268                                )
1269                            })?
1270                        };
1271
1272                        def.call(
1273                            scalar,
1274                            vec![list],
1275                            Rc::clone(&heap),
1276                            Rc::clone(&bindings),
1277                            call_depth,
1278                        )
1279                    } else {
1280                        Err(anyhow!("'into' operator requires function on right side"))
1281                    }
1282                }
1283                BinaryOp::DotEqual
1284                | BinaryOp::DotNotEqual
1285                | BinaryOp::DotLess
1286                | BinaryOp::DotLessEq
1287                | BinaryOp::DotGreater
1288                | BinaryOp::DotGreaterEq => {
1289                    // Dot operators are handled before this match, so this should never be reached
1290                    unreachable!("Dot operators should be handled before list broadcasting")
1291                }
1292            }
1293        }
1294        (lhs, rhs) => match op {
1295            BinaryOp::Equal => Ok(Bool(lhs.equals(&rhs, &heap.borrow())?)),
1296            BinaryOp::NotEqual => Ok(Bool(!lhs.equals(&rhs, &heap.borrow())?)),
1297            BinaryOp::Less => match lhs.compare(&rhs, &heap.borrow())? {
1298                Some(std::cmp::Ordering::Less) => Ok(Bool(true)),
1299                _ => Ok(Bool(false)),
1300            },
1301            BinaryOp::LessEq => match lhs.compare(&rhs, &heap.borrow())? {
1302                Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal) => Ok(Bool(true)),
1303                _ => Ok(Bool(false)),
1304            },
1305            BinaryOp::Greater => match lhs.compare(&rhs, &heap.borrow())? {
1306                Some(std::cmp::Ordering::Greater) => Ok(Bool(true)),
1307                _ => Ok(Bool(false)),
1308            },
1309            BinaryOp::GreaterEq => match lhs.compare(&rhs, &heap.borrow())? {
1310                Some(std::cmp::Ordering::Greater) | Some(std::cmp::Ordering::Equal) => {
1311                    Ok(Bool(true))
1312                }
1313                _ => Ok(Bool(false)),
1314            },
1315            BinaryOp::And | BinaryOp::NaturalAnd => Ok(Bool(lhs.as_bool()? && rhs.as_bool()?)),
1316            BinaryOp::Or | BinaryOp::NaturalOr => Ok(Bool(lhs.as_bool()? || rhs.as_bool()?)),
1317            BinaryOp::Add => {
1318                if lhs.is_string() {
1319                    let (l_str, r_str) = {
1320                        (
1321                            lhs.as_string(&heap.borrow())?.to_string(),
1322                            rhs.as_string(&heap.borrow())?.to_string(),
1323                        )
1324                    };
1325
1326                    return Ok(heap
1327                        .borrow_mut()
1328                        .insert_string(format!("{}{}", l_str, r_str)));
1329                }
1330
1331                Ok(Number(lhs.as_number()? + rhs.as_number()?))
1332            }
1333            BinaryOp::Subtract => Ok(Number(lhs.as_number()? - rhs.as_number()?)),
1334            BinaryOp::Multiply => Ok(Number(lhs.as_number()? * rhs.as_number()?)),
1335            BinaryOp::Divide => Ok(Number(lhs.as_number()? / rhs.as_number()?)),
1336            BinaryOp::Modulo => Ok(Number(lhs.as_number()? % rhs.as_number()?)),
1337            BinaryOp::Power => Ok(Number(lhs.as_number()?.powf(rhs.as_number()?))),
1338            BinaryOp::Coalesce => {
1339                if lhs == Value::Null {
1340                    Ok(rhs)
1341                } else {
1342                    Ok(lhs)
1343                }
1344            }
1345            BinaryOp::Via => {
1346                if !rhs.is_callable() {
1347                    return Err(anyhow!(
1348                        "can't call a non-function ({} is of type {})",
1349                        rhs.stringify_internal(&heap.borrow()),
1350                        rhs.get_type()
1351                    ));
1352                }
1353
1354                let def = { get_function_def(&rhs, &heap.borrow()) };
1355
1356                if def.is_none() {
1357                    return Err(anyhow!(
1358                        "unknown function: {}",
1359                        rhs.stringify_internal(&heap.borrow())
1360                    ));
1361                }
1362
1363                def.unwrap().call(
1364                    rhs,
1365                    vec![lhs],
1366                    Rc::clone(&heap),
1367                    Rc::clone(&bindings),
1368                    call_depth,
1369                )
1370            }
1371            BinaryOp::Into => {
1372                if !rhs.is_callable() {
1373                    return Err(anyhow!(
1374                        "can't call a non-function ({} is of type {})",
1375                        rhs.stringify_internal(&heap.borrow()),
1376                        rhs.get_type()
1377                    ));
1378                }
1379
1380                let def = { get_function_def(&rhs, &heap.borrow()) };
1381
1382                if def.is_none() {
1383                    return Err(anyhow!(
1384                        "unknown function: {}",
1385                        rhs.stringify_internal(&heap.borrow())
1386                    ));
1387                }
1388
1389                def.unwrap().call(
1390                    rhs,
1391                    vec![lhs],
1392                    Rc::clone(&heap),
1393                    Rc::clone(&bindings),
1394                    call_depth,
1395                )
1396            }
1397            BinaryOp::DotEqual
1398            | BinaryOp::DotNotEqual
1399            | BinaryOp::DotLess
1400            | BinaryOp::DotLessEq
1401            | BinaryOp::DotGreater
1402            | BinaryOp::DotGreaterEq => {
1403                // Dot operators are handled before this match, so this should never be reached
1404                unreachable!("Dot operators should be handled before generic value matching")
1405            }
1406        },
1407    }
1408}
1409
1410// Convert Pest pairs to our AST
1411pub fn pairs_to_expr(pairs: Pairs<Rule>) -> Result<Expr> {
1412    PRATT
1413        .map_primary(|primary| match primary.as_rule() {
1414            Rule::number => Ok(Expr::Number(
1415                primary
1416                    .as_str()
1417                    .replace("_", "")
1418                    .parse::<f64>()
1419                    .map_err(anyhow::Error::from)?,
1420            )),
1421            Rule::list => {
1422                let list_pairs = primary.into_inner();
1423                let exprs = list_pairs
1424                    .into_iter()
1425                    .map(|pair| pairs_to_expr(pair.into_inner()))
1426                    .collect::<Result<Vec<Expr>>>()?;
1427                Ok(Expr::List(exprs))
1428            }
1429            Rule::record => {
1430                let record_pairs = primary.into_inner();
1431                let mut entries = Vec::new();
1432
1433                for pair in record_pairs {
1434                    match pair.as_rule() {
1435                        Rule::record_pair => {
1436                            let mut inner_pairs = pair.into_inner();
1437                            let key_pair = inner_pairs.next().unwrap();
1438                            let key = match key_pair.as_rule() {
1439                                Rule::record_key_static => {
1440                                    let inner_key_pair = key_pair.into_inner().next().unwrap();
1441                                    match inner_key_pair.as_rule() {
1442                                        Rule::identifier => {
1443                                            RecordKey::Static(inner_key_pair.as_str().to_string())
1444                                        }
1445                                        Rule::string => RecordKey::Static(
1446                                            inner_key_pair.into_inner().as_str().to_string(),
1447                                        ),
1448                                        _ => unreachable!(),
1449                                    }
1450                                }
1451                                Rule::record_key_dynamic => RecordKey::Dynamic(Box::new(
1452                                    pairs_to_expr(key_pair.into_inner())?,
1453                                )),
1454                                _ => unreachable!(),
1455                            };
1456
1457                            let value = pairs_to_expr(inner_pairs.next().unwrap().into_inner())?;
1458                            entries.push(RecordEntry { key, value });
1459                        }
1460                        Rule::record_shorthand => {
1461                            let ident = pair.into_inner().next().unwrap().as_str().to_string();
1462                            entries.push(RecordEntry {
1463                                key: RecordKey::Shorthand(ident),
1464                                value: Expr::Null, // Will be resolved during evaluation
1465                            });
1466                        }
1467                        Rule::spread_expression => {
1468                            let spread_expr = pairs_to_expr(pair.into_inner())?;
1469                            entries.push(RecordEntry {
1470                                key: RecordKey::Spread(Box::new(spread_expr)),
1471                                value: Expr::Null, // Will be resolved during evaluation
1472                            });
1473                        }
1474                        _ => {}
1475                    }
1476                }
1477
1478                Ok(Expr::Record(entries))
1479            }
1480            Rule::bool => {
1481                let bool_str = primary.as_str();
1482                match bool_str {
1483                    "true" => Ok(Expr::Bool(true)),
1484                    "false" => Ok(Expr::Bool(false)),
1485                    _ => unreachable!(),
1486                }
1487            }
1488            Rule::null => Ok(Expr::Null),
1489            Rule::string => {
1490                let contents = primary.into_inner().as_str().to_string();
1491                Ok(Expr::String(contents))
1492            }
1493            Rule::assignment => {
1494                let mut inner_pairs = primary.into_inner();
1495                let ident = inner_pairs.next().unwrap().as_str().to_string();
1496                let value = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1497                Ok(Expr::Assignment { ident, value })
1498            }
1499            Rule::lambda => {
1500                let mut inner_pairs = primary.into_inner();
1501                let arg_list = inner_pairs.next().unwrap();
1502                let body_pairs = inner_pairs.next().unwrap();
1503
1504                let mut args = Vec::new();
1505                for arg_pair in arg_list.into_inner() {
1506                    match arg_pair.as_rule() {
1507                        Rule::required_arg => {
1508                            args.push(LambdaArg::Required(
1509                                arg_pair.into_inner().as_str().to_string(),
1510                            ));
1511                        }
1512                        Rule::optional_arg => {
1513                            args.push(LambdaArg::Optional(
1514                                arg_pair.into_inner().as_str().to_string(),
1515                            ));
1516                        }
1517                        Rule::rest_arg => {
1518                            args.push(LambdaArg::Rest(arg_pair.into_inner().as_str().to_string()));
1519                        }
1520                        _ => {}
1521                    }
1522                }
1523
1524                let body = Box::new(pairs_to_expr(body_pairs.into_inner())?);
1525                Ok(Expr::Lambda { args, body })
1526            }
1527            Rule::conditional => {
1528                let mut inner_pairs = primary.into_inner();
1529                let condition = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1530                let then_expr = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1531                let else_expr = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1532                Ok(Expr::Conditional {
1533                    condition,
1534                    then_expr,
1535                    else_expr,
1536                })
1537            }
1538            Rule::do_block => {
1539                let inner_pairs = primary.into_inner();
1540                let mut statements = Vec::new();
1541                let mut return_expr = Box::new(Expr::Null);
1542
1543                for pair in inner_pairs {
1544                    match pair.as_rule() {
1545                        Rule::do_statement => {
1546                            if let Some(inner) = pair.into_inner().next() {
1547                                if inner.as_rule() == Rule::expression {
1548                                    statements.push(DoStatement::Expression(pairs_to_expr(
1549                                        inner.into_inner(),
1550                                    )?));
1551                                } else if inner.as_rule() == Rule::comment {
1552                                    statements
1553                                        .push(DoStatement::Comment(inner.as_str().to_string()));
1554                                }
1555                            }
1556                        }
1557                        Rule::return_statement => {
1558                            let return_expr_pair = pair.into_inner().next().unwrap();
1559                            return_expr = Box::new(pairs_to_expr(return_expr_pair.into_inner())?);
1560                        }
1561                        _ => {}
1562                    }
1563                }
1564
1565                Ok(Expr::DoBlock {
1566                    statements,
1567                    return_expr,
1568                })
1569            }
1570            Rule::identifier => {
1571                let ident = primary.as_str();
1572
1573                // Check if it's a built-in function
1574                if let Some(built_in) = BuiltInFunction::from_ident(ident) {
1575                    Ok(Expr::BuiltIn(built_in))
1576                } else {
1577                    Ok(Expr::Identifier(ident.to_string()))
1578                }
1579            }
1580            Rule::input_reference => {
1581                // Strip the leading '#' and create an InputReference
1582                let field = primary.as_str()[1..].to_string();
1583                Ok(Expr::InputReference(field))
1584            }
1585            Rule::expression => pairs_to_expr(primary.into_inner()),
1586            _ => unreachable!("{}", primary.as_str()),
1587        })
1588        .map_prefix(|op, rhs| match op.as_rule() {
1589            Rule::negation => Ok(Expr::UnaryOp {
1590                op: UnaryOp::Negate,
1591                expr: Box::new(rhs?),
1592            }),
1593            Rule::spread_operator => Ok(Expr::Spread(Box::new(rhs?))),
1594            Rule::invert | Rule::natural_not => Ok(Expr::UnaryOp {
1595                op: UnaryOp::Not,
1596                expr: Box::new(rhs?),
1597            }),
1598            _ => unreachable!(),
1599        })
1600        .map_postfix(|lhs, op| match op.as_rule() {
1601            Rule::factorial => Ok(Expr::PostfixOp {
1602                op: PostfixOp::Factorial,
1603                expr: Box::new(lhs?),
1604            }),
1605            Rule::access => {
1606                let index_expr = pairs_to_expr(op.into_inner())?;
1607                Ok(Expr::Access {
1608                    expr: Box::new(lhs?),
1609                    index: Box::new(index_expr),
1610                })
1611            }
1612            Rule::dot_access => {
1613                let field = op.into_inner().as_str().to_string();
1614                Ok(Expr::DotAccess {
1615                    expr: Box::new(lhs?),
1616                    field,
1617                })
1618            }
1619            Rule::call_list => {
1620                let call_list = op.into_inner();
1621                let args = call_list
1622                    .into_iter()
1623                    .map(|arg| pairs_to_expr(arg.into_inner()))
1624                    .collect::<Result<Vec<Expr>>>()?;
1625                Ok(Expr::Call {
1626                    func: Box::new(lhs?),
1627                    args,
1628                })
1629            }
1630            _ => unreachable!(),
1631        })
1632        .map_infix(|lhs, op, rhs| {
1633            let op = match op.as_rule() {
1634                Rule::add => BinaryOp::Add,
1635                Rule::subtract => BinaryOp::Subtract,
1636                Rule::multiply => BinaryOp::Multiply,
1637                Rule::divide => BinaryOp::Divide,
1638                Rule::modulo => BinaryOp::Modulo,
1639                Rule::power => BinaryOp::Power,
1640                Rule::equal => BinaryOp::Equal,
1641                Rule::not_equal => BinaryOp::NotEqual,
1642                Rule::less => BinaryOp::Less,
1643                Rule::less_eq => BinaryOp::LessEq,
1644                Rule::greater => BinaryOp::Greater,
1645                Rule::greater_eq => BinaryOp::GreaterEq,
1646                Rule::dot_equal => BinaryOp::DotEqual,
1647                Rule::dot_not_equal => BinaryOp::DotNotEqual,
1648                Rule::dot_less => BinaryOp::DotLess,
1649                Rule::dot_less_eq => BinaryOp::DotLessEq,
1650                Rule::dot_greater => BinaryOp::DotGreater,
1651                Rule::dot_greater_eq => BinaryOp::DotGreaterEq,
1652                Rule::and => BinaryOp::And,
1653                Rule::natural_and => BinaryOp::NaturalAnd,
1654                Rule::or => BinaryOp::Or,
1655                Rule::natural_or => BinaryOp::NaturalOr,
1656                Rule::via => BinaryOp::Via,
1657                Rule::into => BinaryOp::Into,
1658                Rule::coalesce => BinaryOp::Coalesce,
1659                _ => unreachable!(),
1660            };
1661            Ok(Expr::BinaryOp {
1662                op,
1663                left: Box::new(lhs?),
1664                right: Box::new(rhs?),
1665            })
1666        })
1667        .parse(pairs)
1668}
1669
1670#[cfg(test)]
1671mod tests {
1672    use super::*;
1673    use crate::{heap::LambdaPointer, parser::get_pairs};
1674
1675    fn parse_and_evaluate(
1676        input: &str,
1677        heap: Option<Rc<RefCell<Heap>>>,
1678        bindings: Option<Rc<RefCell<HashMap<String, Value>>>>,
1679    ) -> Result<Value> {
1680        let binding = input.to_string();
1681        let mut pairs = get_pairs(&binding).unwrap();
1682        let expr = pairs.next().unwrap().into_inner();
1683        evaluate_pairs(
1684            expr,
1685            heap.unwrap_or(Rc::new(RefCell::new(Heap::new()))),
1686            bindings.unwrap_or(Rc::new(RefCell::new(HashMap::new()))),
1687            0,
1688        )
1689    }
1690
1691    #[test]
1692    fn addition_of_integers() {
1693        let result = parse_and_evaluate("5 + 2", None, None).unwrap();
1694        assert_eq!(result, Value::Number(7.0));
1695    }
1696
1697    #[test]
1698    fn exponentiation_of_two_integers() {
1699        let result = parse_and_evaluate("2 ^ 3", None, None).unwrap();
1700        assert_eq!(result, Value::Number(8.0));
1701    }
1702
1703    #[test]
1704    fn multiplication_of_integers() {
1705        let result = parse_and_evaluate("8 * 4", None, None).unwrap();
1706        assert_eq!(result, Value::Number(32.0));
1707    }
1708
1709    #[test]
1710    fn division_with_integer_resulting_in_decimal() {
1711        let result = parse_and_evaluate("9 / 2", None, None).unwrap();
1712        assert_eq!(result, Value::Number(4.5));
1713    }
1714
1715    #[test]
1716    fn addition_with_nested_expression() {
1717        let result = parse_and_evaluate("5 + (2 * 4)", None, None).unwrap();
1718        assert_eq!(result, Value::Number(13.0));
1719    }
1720
1721    #[test]
1722    fn grouping_and_multiplication_in_expression() {
1723        let result = parse_and_evaluate("(3 + 2) * 2", None, None).unwrap();
1724        assert_eq!(result, Value::Number(10.0));
1725    }
1726
1727    #[test]
1728    fn mixed_operations_with_decimal_and_precedence() {
1729        let result = parse_and_evaluate("6.5 / 2 + 4 * 2", None, None).unwrap();
1730        assert_eq!(result, Value::Number(11.25));
1731    }
1732
1733    #[test]
1734    fn exponentiation_with_nested_expression() {
1735        let result = parse_and_evaluate("2 ^ (1 + 2)", None, None).unwrap();
1736        assert_eq!(result, Value::Number(8.0));
1737    }
1738
1739    #[test]
1740    fn complex_expression_with_decimals() {
1741        let result = parse_and_evaluate("7.5 - 3.25 + 2 * (8 / 4)", None, None).unwrap();
1742        assert_eq!(result, Value::Number(8.25));
1743    }
1744
1745    #[test]
1746    fn subtraction_with_decimal_result() {
1747        let result = parse_and_evaluate("10.75 - 3.5", None, None).unwrap();
1748        assert_eq!(result, Value::Number(7.25));
1749    }
1750
1751    #[test]
1752    fn multiplication_of_two_decimals() {
1753        let result = parse_and_evaluate("3.5 * 2.0", None, None).unwrap();
1754        assert_eq!(result, Value::Number(7.0));
1755    }
1756
1757    #[test]
1758    fn division_of_two_decimals() {
1759        let result = parse_and_evaluate("7.5 / 2.5", None, None).unwrap();
1760        assert_eq!(result, Value::Number(3.0));
1761    }
1762
1763    #[test]
1764    fn boolean_and() {
1765        let result = parse_and_evaluate("true and false", None, None).unwrap();
1766        assert_eq!(result, Value::Bool(false));
1767    }
1768
1769    #[test]
1770    fn boolean_and_alt() {
1771        let result = parse_and_evaluate("true && false", None, None).unwrap();
1772        assert_eq!(result, Value::Bool(false));
1773    }
1774
1775    #[test]
1776    fn boolean_or() {
1777        let result = parse_and_evaluate("true or false", None, None).unwrap();
1778        assert_eq!(result, Value::Bool(true));
1779    }
1780
1781    #[test]
1782    fn boolean_or_alt() {
1783        let result = parse_and_evaluate("true || false", None, None).unwrap();
1784        assert_eq!(result, Value::Bool(true));
1785    }
1786
1787    #[test]
1788    fn boolean_and_with_nested_expression() {
1789        let result = parse_and_evaluate("true and (false or true)", None, None).unwrap();
1790        assert_eq!(result, Value::Bool(true));
1791    }
1792
1793    #[test]
1794    fn boolean_and_with_nested_expression_alt() {
1795        let result = parse_and_evaluate("true && (false || true)", None, None).unwrap();
1796        assert_eq!(result, Value::Bool(true));
1797    }
1798
1799    #[test]
1800    fn boolean_or_with_nested_expression() {
1801        let result = parse_and_evaluate("true or (false and true)", None, None).unwrap();
1802        assert_eq!(result, Value::Bool(true));
1803    }
1804
1805    #[test]
1806    fn boolean_or_with_nested_expression_alt() {
1807        let result = parse_and_evaluate("true || (false && true)", None, None).unwrap();
1808        assert_eq!(result, Value::Bool(true));
1809    }
1810
1811    #[test]
1812    fn logical_not() {
1813        let result = parse_and_evaluate("!true", None, None).unwrap();
1814        assert_eq!(result, Value::Bool(false));
1815    }
1816
1817    #[test]
1818    fn logical_not_with_nested_expression() {
1819        let result = parse_and_evaluate("!(true and false)", None, None).unwrap();
1820        assert_eq!(result, Value::Bool(true));
1821    }
1822
1823    #[test]
1824    fn equality_of_two_integers() {
1825        let result = parse_and_evaluate("5 == 5", None, None).unwrap();
1826        assert_eq!(result, Value::Bool(true));
1827    }
1828
1829    #[test]
1830    fn inequality_of_two_integers() {
1831        let result = parse_and_evaluate("5 != 5", None, None).unwrap();
1832        assert_eq!(result, Value::Bool(false));
1833    }
1834
1835    #[test]
1836    fn less_than_comparison() {
1837        let result = parse_and_evaluate("5 < 10", None, None).unwrap();
1838        assert_eq!(result, Value::Bool(true));
1839    }
1840
1841    #[test]
1842    fn less_than_or_equal_comparison() {
1843        let result = parse_and_evaluate("5 <= 5", None, None).unwrap();
1844        assert_eq!(result, Value::Bool(true));
1845    }
1846
1847    #[test]
1848    fn greater_than_comparison() {
1849        let result = parse_and_evaluate("10 > 5", None, None).unwrap();
1850        assert_eq!(result, Value::Bool(true));
1851    }
1852
1853    #[test]
1854    fn greater_than_or_equal_comparison() {
1855        let result = parse_and_evaluate("5 >= 5", None, None).unwrap();
1856        assert_eq!(result, Value::Bool(true));
1857    }
1858
1859    #[test]
1860    fn equality_of_two_lists() {
1861        // List == List performs element-wise comparison
1862        let heap = Rc::new(RefCell::new(Heap::new()));
1863        let result =
1864            parse_and_evaluate("[1, 2, 3] == [1, 2, 3]", Some(heap.clone()), None).unwrap();
1865        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
1866        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
1867    }
1868
1869    #[test]
1870    fn inequality_of_two_lists() {
1871        // List != List performs element-wise comparison
1872        let heap = Rc::new(RefCell::new(Heap::new()));
1873        let result =
1874            parse_and_evaluate("[1, 2, 3] != [2, 3, 4]", Some(heap.clone()), None).unwrap();
1875        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
1876        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
1877    }
1878
1879    #[test]
1880    fn conditional_expression_with_true_condition() {
1881        let result = parse_and_evaluate("if true then 5 else 10", None, None).unwrap();
1882        assert_eq!(result, Value::Number(5.0));
1883    }
1884
1885    #[test]
1886    fn conditional_expression_with_false_condition() {
1887        let result = parse_and_evaluate("if false then 5 else 10", None, None).unwrap();
1888        assert_eq!(result, Value::Number(10.0));
1889    }
1890
1891    #[test]
1892    fn factorial_of_integer() {
1893        let result = parse_and_evaluate("5!", None, None).unwrap();
1894        assert_eq!(result, Value::Number(120.0));
1895    }
1896
1897    #[test]
1898    fn factorial_of_zero() {
1899        let result = parse_and_evaluate("0!", None, None).unwrap();
1900        assert_eq!(result, Value::Number(1.0));
1901    }
1902
1903    #[test]
1904    fn factorial_of_negative_integer() {
1905        let result = parse_and_evaluate("(-5)!", None, None);
1906        assert!(result.is_err());
1907    }
1908
1909    #[test]
1910    fn factorial_of_decimal() {
1911        let result = parse_and_evaluate("5.5!", None, None);
1912        assert!(result.is_err());
1913    }
1914
1915    #[test]
1916    fn string_concatenation() {
1917        let heap = Rc::new(RefCell::new(Heap::new()));
1918        let result =
1919            parse_and_evaluate("\"hello\" + \"world\"", Some(Rc::clone(&heap)), None).unwrap();
1920
1921        assert!(matches!(result, Value::String(_)));
1922        assert_eq!(result.as_string(&heap.borrow()).unwrap(), "helloworld");
1923    }
1924
1925    #[test]
1926    fn string_concatenation_with_integer() {
1927        let result = parse_and_evaluate("\"hello\" + 5", None, None);
1928        assert!(result.is_err());
1929    }
1930
1931    #[test]
1932    fn variable_assignment() {
1933        let bindings = Rc::new(RefCell::new(HashMap::new()));
1934        let result = parse_and_evaluate("x = 5", None, Some(Rc::clone(&bindings))).unwrap();
1935
1936        assert_eq!(result, Value::Number(5.0));
1937        assert_eq!(bindings.borrow().get("x").unwrap(), &Value::Number(5.0));
1938    }
1939
1940    #[test]
1941    fn variable_assignment_with_expression() {
1942        let bindings = Rc::new(RefCell::new(HashMap::new()));
1943        let result = parse_and_evaluate("x = 5 + 2", None, Some(Rc::clone(&bindings))).unwrap();
1944        assert_eq!(result, Value::Number(7.0));
1945        assert_eq!(bindings.borrow().get("x").unwrap(), &Value::Number(7.0));
1946    }
1947
1948    #[test]
1949    fn variable_assignment_with_lambda() {
1950        let heap = Rc::new(RefCell::new(Heap::new()));
1951        let bindings = Rc::new(RefCell::new(HashMap::new()));
1952        let result = parse_and_evaluate(
1953            "f = x => x + 1",
1954            Some(Rc::clone(&heap)),
1955            Some(Rc::clone(&bindings)),
1956        )
1957        .unwrap();
1958
1959        {
1960            let heap_borrow = heap.borrow();
1961            let lambda_def = result.as_lambda(&heap_borrow).unwrap();
1962            assert_eq!(lambda_def.name, Some("f".to_string()));
1963            assert_eq!(lambda_def.args, vec![LambdaArg::Required("x".to_string())]);
1964            assert_eq!(lambda_def.scope, HashMap::new());
1965            // The body should be a BinaryOp(Add) with Identifier("x") and Number(1)
1966            match &lambda_def.body {
1967                Expr::BinaryOp {
1968                    op: BinaryOp::Add,
1969                    left,
1970                    right,
1971                } => match (left.as_ref(), right.as_ref()) {
1972                    (Expr::Identifier(x), Expr::Number(n)) => {
1973                        assert_eq!(x, "x");
1974                        assert_eq!(*n, 1.0);
1975                    }
1976                    _ => panic!("Expected Identifier('x') + Number(1)"),
1977                },
1978                _ => panic!("Expected BinaryOp(Add)"),
1979            }
1980        }
1981        assert_eq!(
1982            bindings.borrow().get("f").unwrap(),
1983            &Value::Lambda(LambdaPointer::new(1))
1984        );
1985    }
1986
1987    #[test]
1988    fn variable_assignment_with_lambda_and_call() {
1989        let heap = Rc::new(RefCell::new(Heap::new()));
1990        let bindings = Rc::new(RefCell::new(HashMap::new()));
1991        let _ = parse_and_evaluate(
1992            "f = x => x + 1",
1993            Some(Rc::clone(&heap)),
1994            Some(Rc::clone(&bindings)),
1995        )
1996        .unwrap();
1997        let result =
1998            parse_and_evaluate("f(5)", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
1999
2000        assert_eq!(result, Value::Number(6.0));
2001    }
2002
2003    #[test]
2004    fn variable_assignment_with_lambda_and_call_with_multiple_args() {
2005        let heap = Rc::new(RefCell::new(Heap::new()));
2006        let bindings = Rc::new(RefCell::new(HashMap::new()));
2007        let _ = parse_and_evaluate(
2008            "f = (x, y) => x + y",
2009            Some(Rc::clone(&heap)),
2010            Some(Rc::clone(&bindings)),
2011        )
2012        .unwrap();
2013        let result = parse_and_evaluate(
2014            "f(5, 2)",
2015            Some(Rc::clone(&heap)),
2016            Some(Rc::clone(&bindings)),
2017        )
2018        .unwrap();
2019
2020        assert_eq!(result, Value::Number(7.0));
2021    }
2022
2023    #[test]
2024    fn variable_assignment_with_lambda_and_call_with_multiple_args_and_expression() {
2025        let heap = Rc::new(RefCell::new(Heap::new()));
2026        let bindings = Rc::new(RefCell::new(HashMap::new()));
2027        let _ = parse_and_evaluate(
2028            "f = (x, y) => x + y",
2029            Some(Rc::clone(&heap)),
2030            Some(Rc::clone(&bindings)),
2031        )
2032        .unwrap();
2033        let result = parse_and_evaluate(
2034            "f(5, 2) + 3",
2035            Some(Rc::clone(&heap)),
2036            Some(Rc::clone(&bindings)),
2037        )
2038        .unwrap();
2039
2040        assert_eq!(result, Value::Number(10.0));
2041    }
2042
2043    #[test]
2044    fn coalesce_operator_with_null() {
2045        let result = parse_and_evaluate("null ?? 5", None, None).unwrap();
2046        assert_eq!(result, Value::Number(5.0));
2047    }
2048
2049    #[test]
2050    fn coalesce_operator_with_non_null() {
2051        let result = parse_and_evaluate("3 ?? 10", None, None).unwrap();
2052        assert_eq!(result, Value::Number(3.0));
2053    }
2054
2055    // Element-wise operations tests
2056    #[test]
2057    fn list_elementwise_addition() {
2058        let heap = Rc::new(RefCell::new(Heap::new()));
2059        let result =
2060            parse_and_evaluate("[1, 2, 3] + [4, 5, 6]", Some(Rc::clone(&heap)), None).unwrap();
2061        let expected = vec![Value::Number(5.0), Value::Number(7.0), Value::Number(9.0)];
2062        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2063    }
2064
2065    #[test]
2066    fn list_elementwise_subtraction() {
2067        let heap = Rc::new(RefCell::new(Heap::new()));
2068        let result =
2069            parse_and_evaluate("[5, 7, 9] - [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
2070        let expected = vec![Value::Number(4.0), Value::Number(5.0), Value::Number(6.0)];
2071        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2072    }
2073
2074    #[test]
2075    fn list_elementwise_multiplication() {
2076        let heap = Rc::new(RefCell::new(Heap::new()));
2077        let result =
2078            parse_and_evaluate("[2, 3, 4] * [5, 6, 7]", Some(Rc::clone(&heap)), None).unwrap();
2079        let expected = vec![
2080            Value::Number(10.0),
2081            Value::Number(18.0),
2082            Value::Number(28.0),
2083        ];
2084        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2085    }
2086
2087    #[test]
2088    fn list_elementwise_division() {
2089        let heap = Rc::new(RefCell::new(Heap::new()));
2090        let result =
2091            parse_and_evaluate("[10, 20, 30] / [2, 4, 5]", Some(Rc::clone(&heap)), None).unwrap();
2092        let expected = vec![Value::Number(5.0), Value::Number(5.0), Value::Number(6.0)];
2093        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2094    }
2095
2096    #[test]
2097    fn list_elementwise_power() {
2098        let heap = Rc::new(RefCell::new(Heap::new()));
2099        let result =
2100            parse_and_evaluate("[2, 3, 4] ^ [2, 2, 2]", Some(Rc::clone(&heap)), None).unwrap();
2101        let expected = vec![Value::Number(4.0), Value::Number(9.0), Value::Number(16.0)];
2102        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2103    }
2104
2105    #[test]
2106    fn list_elementwise_modulo() {
2107        let heap = Rc::new(RefCell::new(Heap::new()));
2108        let result =
2109            parse_and_evaluate("[10, 11, 12] % [3, 3, 3]", Some(Rc::clone(&heap)), None).unwrap();
2110        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(0.0)];
2111        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2112    }
2113
2114    #[test]
2115    fn list_elementwise_different_lengths_error() {
2116        let result = parse_and_evaluate("[1, 2, 3] + [4, 5]", None, None);
2117        assert!(result.is_err());
2118        assert!(
2119            result
2120                .unwrap_err()
2121                .to_string()
2122                .contains("left- and right-hand-side lists must be the same length")
2123        );
2124    }
2125
2126    // Broadcast operations tests
2127    #[test]
2128    fn list_scalar_addition() {
2129        let heap = Rc::new(RefCell::new(Heap::new()));
2130        let result = parse_and_evaluate("[1, 2, 3] + 10", Some(Rc::clone(&heap)), None).unwrap();
2131        let expected = vec![
2132            Value::Number(11.0),
2133            Value::Number(12.0),
2134            Value::Number(13.0),
2135        ];
2136        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2137    }
2138
2139    #[test]
2140    fn list_scalar_subtraction() {
2141        let heap = Rc::new(RefCell::new(Heap::new()));
2142        let result = parse_and_evaluate("[10, 20, 30] - 5", Some(Rc::clone(&heap)), None).unwrap();
2143        let expected = vec![Value::Number(5.0), Value::Number(15.0), Value::Number(25.0)];
2144        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2145    }
2146
2147    #[test]
2148    fn list_scalar_multiplication() {
2149        let heap = Rc::new(RefCell::new(Heap::new()));
2150        let result = parse_and_evaluate("[2, 3, 4] * 2", Some(Rc::clone(&heap)), None).unwrap();
2151        let expected = vec![Value::Number(4.0), Value::Number(6.0), Value::Number(8.0)];
2152        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2153    }
2154
2155    #[test]
2156    fn list_scalar_division() {
2157        let heap = Rc::new(RefCell::new(Heap::new()));
2158        let result = parse_and_evaluate("[10, 20, 30] / 10", Some(Rc::clone(&heap)), None).unwrap();
2159        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(3.0)];
2160        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2161    }
2162
2163    #[test]
2164    fn list_scalar_power() {
2165        let heap = Rc::new(RefCell::new(Heap::new()));
2166        let result = parse_and_evaluate("[2, 3, 4] ^ 2", Some(Rc::clone(&heap)), None).unwrap();
2167        let expected = vec![Value::Number(4.0), Value::Number(9.0), Value::Number(16.0)];
2168        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2169    }
2170
2171    #[test]
2172    fn list_scalar_modulo() {
2173        let heap = Rc::new(RefCell::new(Heap::new()));
2174        let result = parse_and_evaluate("[10, 11, 12] % 3", Some(Rc::clone(&heap)), None).unwrap();
2175        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(0.0)];
2176        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2177    }
2178
2179    #[test]
2180    fn list_with_operator() {
2181        let heap = Rc::new(RefCell::new(Heap::new()));
2182        let result =
2183            parse_and_evaluate("[1, 2, 3] via (x => x * x)", Some(Rc::clone(&heap)), None).unwrap();
2184        let expected = vec![Value::Number(1.0), Value::Number(4.0), Value::Number(9.0)];
2185        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2186    }
2187
2188    #[test]
2189    fn list_with_builtin_function() {
2190        let heap = Rc::new(RefCell::new(Heap::new()));
2191        let result =
2192            parse_and_evaluate("[4, 9, 16] via sqrt", Some(Rc::clone(&heap)), None).unwrap();
2193        let expected = vec![Value::Number(2.0), Value::Number(3.0), Value::Number(4.0)];
2194        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2195    }
2196
2197    #[test]
2198    fn into_operator_with_list() {
2199        let heap = Rc::new(RefCell::new(Heap::new()));
2200        let result =
2201            parse_and_evaluate("['hello', 'world'] into head", Some(Rc::clone(&heap)), None)
2202                .unwrap();
2203        assert_eq!(result.as_string(&heap.borrow()).unwrap(), "hello");
2204    }
2205
2206    #[test]
2207    fn into_operator_with_string() {
2208        let heap = Rc::new(RefCell::new(Heap::new()));
2209        let result = parse_and_evaluate("'hello' into head", Some(Rc::clone(&heap)), None).unwrap();
2210        assert_eq!(result.as_string(&heap.borrow()).unwrap(), "h");
2211    }
2212
2213    #[test]
2214    fn into_vs_via_difference() {
2215        let heap = Rc::new(RefCell::new(Heap::new()));
2216
2217        // via should apply to each element
2218        let via_result =
2219            parse_and_evaluate("['hello', 'world'] via head", Some(Rc::clone(&heap)), None)
2220                .unwrap();
2221        let borrowed_heap = heap.borrow();
2222        let via_list = via_result.as_list(&borrowed_heap).unwrap();
2223        assert_eq!(via_list.len(), 2);
2224        assert_eq!(via_list[0].as_string(&borrowed_heap).unwrap(), "h");
2225        assert_eq!(via_list[1].as_string(&borrowed_heap).unwrap(), "w");
2226        drop(borrowed_heap);
2227
2228        // into should apply to the whole list
2229        let into_result =
2230            parse_and_evaluate("['hello', 'world'] into head", Some(Rc::clone(&heap)), None)
2231                .unwrap();
2232        assert_eq!(into_result.as_string(&heap.borrow()).unwrap(), "hello");
2233    }
2234
2235    #[test]
2236    fn into_operator_with_aggregation() {
2237        let heap = Rc::new(RefCell::new(Heap::new()));
2238        let result =
2239            parse_and_evaluate("[1, 2, 3, 4, 5] into sum", Some(Rc::clone(&heap)), None).unwrap();
2240        assert_eq!(result.as_number().unwrap(), 15.0);
2241    }
2242
2243    #[test]
2244    fn into_operator_rejects_lists() {
2245        let heap = Rc::new(RefCell::new(Heap::new()));
2246
2247        // Test with multiple functions in list
2248        let result = parse_and_evaluate(
2249            "[1, 2, 3] into [sqrt, sqrt, sqrt]",
2250            Some(Rc::clone(&heap)),
2251            None,
2252        );
2253        assert!(result.is_err());
2254        assert!(
2255            result
2256                .unwrap_err()
2257                .to_string()
2258                .contains("'into' operator requires a function on the right side, not a list")
2259        );
2260
2261        // Test with single function in list
2262        let result = parse_and_evaluate("[1, 2, 3] into [sqrt]", Some(Rc::clone(&heap)), None);
2263        assert!(result.is_err());
2264        assert!(
2265            result
2266                .unwrap_err()
2267                .to_string()
2268                .contains("'into' operator requires a function on the right side, not a list")
2269        );
2270    }
2271
2272    #[test]
2273    fn list_elementwise_boolean_and() {
2274        let heap = Rc::new(RefCell::new(Heap::new()));
2275        let result = parse_and_evaluate(
2276            "[true, true, false] && [true, false, true]",
2277            Some(Rc::clone(&heap)),
2278            None,
2279        )
2280        .unwrap();
2281        let expected = vec![Value::Bool(true), Value::Bool(false), Value::Bool(false)];
2282        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2283    }
2284
2285    #[test]
2286    fn list_elementwise_boolean_or() {
2287        let heap = Rc::new(RefCell::new(Heap::new()));
2288        let result = parse_and_evaluate(
2289            "[true, false, false] || [false, true, false]",
2290            Some(Rc::clone(&heap)),
2291            None,
2292        )
2293        .unwrap();
2294        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(false)];
2295        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2296    }
2297
2298    #[test]
2299    fn list_elementwise_coalesce() {
2300        let heap = Rc::new(RefCell::new(Heap::new()));
2301        let result = parse_and_evaluate(
2302            "[null, 2, null] ?? [1, null, 3]",
2303            Some(Rc::clone(&heap)),
2304            None,
2305        )
2306        .unwrap();
2307        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(3.0)];
2308        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2309    }
2310
2311    #[test]
2312    fn list_scalar_coalesce() {
2313        let heap = Rc::new(RefCell::new(Heap::new()));
2314        let result =
2315            parse_and_evaluate("[null, 2, null] ?? 5", Some(Rc::clone(&heap)), None).unwrap();
2316        let expected = vec![Value::Number(5.0), Value::Number(2.0), Value::Number(5.0)];
2317        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2318    }
2319
2320    #[test]
2321    fn list_elementwise_string_concat() {
2322        let heap = Rc::new(RefCell::new(Heap::new()));
2323        let result = parse_and_evaluate(
2324            "[\"a\", \"b\", \"c\"] + [\"1\", \"2\", \"3\"]",
2325            Some(Rc::clone(&heap)),
2326            None,
2327        )
2328        .unwrap();
2329        let borrowed_heap = heap.borrow();
2330        let result_list = result.as_list(&borrowed_heap).unwrap();
2331        assert_eq!(result_list.len(), 3);
2332        assert_eq!(result_list[0].as_string(&borrowed_heap).unwrap(), "a1");
2333        assert_eq!(result_list[1].as_string(&borrowed_heap).unwrap(), "b2");
2334        assert_eq!(result_list[2].as_string(&borrowed_heap).unwrap(), "c3");
2335    }
2336
2337    #[test]
2338    fn list_scalar_string_concat() {
2339        let heap = Rc::new(RefCell::new(Heap::new()));
2340        let result = parse_and_evaluate(
2341            "[\"a\", \"b\", \"c\"] + \"!\"",
2342            Some(Rc::clone(&heap)),
2343            None,
2344        )
2345        .unwrap();
2346        let borrowed_heap = heap.borrow();
2347        let result_list = result.as_list(&borrowed_heap).unwrap();
2348        assert_eq!(result_list.len(), 3);
2349        assert_eq!(result_list[0].as_string(&borrowed_heap).unwrap(), "a!");
2350        assert_eq!(result_list[1].as_string(&borrowed_heap).unwrap(), "b!");
2351        assert_eq!(result_list[2].as_string(&borrowed_heap).unwrap(), "c!");
2352    }
2353
2354    #[test]
2355    fn nested_list_operations() {
2356        let heap = Rc::new(RefCell::new(Heap::new()));
2357        let result =
2358            parse_and_evaluate("([1, 2, 3] + [4, 5, 6]) * 2", Some(Rc::clone(&heap)), None)
2359                .unwrap();
2360        let expected = vec![
2361            Value::Number(10.0),
2362            Value::Number(14.0),
2363            Value::Number(18.0),
2364        ];
2365        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2366    }
2367
2368    #[test]
2369    fn list_comparison_all_equal() {
2370        let heap = Rc::new(RefCell::new(Heap::new()));
2371        let result = parse_and_evaluate("[1, 1, 1] == 1", Some(heap.clone()), None).unwrap();
2372        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
2373        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2374    }
2375
2376    #[test]
2377    fn list_comparison_not_all_equal() {
2378        let heap = Rc::new(RefCell::new(Heap::new()));
2379        let result = parse_and_evaluate("[1, 2, 1] == 1", Some(heap.clone()), None).unwrap();
2380        let expected = vec![Value::Bool(true), Value::Bool(false), Value::Bool(true)];
2381        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2382    }
2383
2384    #[test]
2385    fn list_comparison_all_less() {
2386        let heap = Rc::new(RefCell::new(Heap::new()));
2387        let result = parse_and_evaluate("[1, 2, 3] < 5", Some(heap.clone()), None).unwrap();
2388        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
2389        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2390    }
2391
2392    #[test]
2393    fn list_comparison_not_all_less() {
2394        let heap = Rc::new(RefCell::new(Heap::new()));
2395        let result = parse_and_evaluate("[1, 6, 3] < 5", Some(heap.clone()), None).unwrap();
2396        let expected = vec![Value::Bool(true), Value::Bool(false), Value::Bool(true)];
2397        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2398    }
2399
2400    #[test]
2401    fn not_operator_basic() {
2402        let result = parse_and_evaluate("not true", None, None).unwrap();
2403        assert_eq!(result, Value::Bool(false));
2404    }
2405
2406    #[test]
2407    fn not_operator_with_false() {
2408        let result = parse_and_evaluate("not false", None, None).unwrap();
2409        assert_eq!(result, Value::Bool(true));
2410    }
2411
2412    #[test]
2413    fn not_operator_with_expression() {
2414        let result = parse_and_evaluate("not (5 > 10)", None, None).unwrap();
2415        assert_eq!(result, Value::Bool(true));
2416    }
2417
2418    #[test]
2419    fn not_operator_double_negation() {
2420        let result = parse_and_evaluate("not not true", None, None).unwrap();
2421        assert_eq!(result, Value::Bool(true));
2422    }
2423
2424    #[test]
2425    fn not_operator_with_and() {
2426        let result = parse_and_evaluate("not (true and false)", None, None).unwrap();
2427        assert_eq!(result, Value::Bool(true));
2428    }
2429
2430    #[test]
2431    fn not_operator_with_or() {
2432        let result = parse_and_evaluate("not (false or false)", None, None).unwrap();
2433        assert_eq!(result, Value::Bool(true));
2434    }
2435
2436    #[test]
2437    fn not_operator_precedence() {
2438        let result = parse_and_evaluate("not true and false", None, None).unwrap();
2439        assert_eq!(result, Value::Bool(false));
2440    }
2441
2442    #[test]
2443    fn not_operator_comparison_with_invert() {
2444        let not_result = parse_and_evaluate("not true", None, None).unwrap();
2445        let invert_result = parse_and_evaluate("!true", None, None).unwrap();
2446        assert_eq!(not_result, invert_result);
2447    }
2448
2449    #[test]
2450    fn variable_immutability_prevents_reassignment() {
2451        let heap = Rc::new(RefCell::new(Heap::new()));
2452        let bindings = Rc::new(RefCell::new(HashMap::new()));
2453
2454        // First assignment should succeed
2455        let result1 =
2456            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2457        assert!(result1.is_ok());
2458        assert_eq!(result1.unwrap(), Value::Number(5.0));
2459
2460        // Second assignment to same variable should fail
2461        let result2 =
2462            parse_and_evaluate("x = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2463        assert!(result2.is_err());
2464        assert!(
2465            result2
2466                .unwrap_err()
2467                .to_string()
2468                .contains("x is already defined, and cannot be reassigned")
2469        );
2470
2471        // Original value should remain unchanged
2472        let result3 = parse_and_evaluate("x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2473        assert!(result3.is_ok());
2474        assert_eq!(result3.unwrap(), Value::Number(5.0));
2475    }
2476
2477    #[test]
2478    fn variable_immutability_allows_shadowing_in_nested_scopes() {
2479        let heap = Rc::new(RefCell::new(Heap::new()));
2480        let bindings = Rc::new(RefCell::new(HashMap::new()));
2481
2482        // Assign x in outer scope
2483        let result1 =
2484            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2485        assert!(result1.is_ok());
2486
2487        // Shadowing in lambda should work (creates new scope)
2488        let result2 = parse_and_evaluate(
2489            "f = (x) => x * 2",
2490            Some(Rc::clone(&heap)),
2491            Some(Rc::clone(&bindings)),
2492        );
2493        assert!(result2.is_ok());
2494
2495        // Call lambda with different value
2496        let result3 =
2497            parse_and_evaluate("f(10)", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2498        assert!(result3.is_ok());
2499        assert_eq!(result3.unwrap(), Value::Number(20.0));
2500
2501        // Original x should be unchanged
2502        let result4 = parse_and_evaluate("x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2503        assert!(result4.is_ok());
2504        assert_eq!(result4.unwrap(), Value::Number(5.0));
2505    }
2506
2507    #[test]
2508    fn variable_immutability_in_do_blocks() {
2509        let heap = Rc::new(RefCell::new(Heap::new()));
2510        let bindings = Rc::new(RefCell::new(HashMap::new()));
2511
2512        // Assign x in outer scope
2513        let result1 =
2514            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2515        assert!(result1.is_ok());
2516
2517        // Do blocks can now shadow outer variables (changed behavior)
2518        let do_block = r#"do {
2519            x = 10
2520            y = x * 2
2521            return y
2522        }"#;
2523        let result2 =
2524            parse_and_evaluate(do_block, Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2525        assert!(result2.is_ok());
2526        assert_eq!(result2.unwrap(), Value::Number(20.0)); // 10 * 2 = 20
2527
2528        // But new variables in do block should work
2529        let do_block2 = r#"do {
2530            z = x * 3
2531            return z
2532        }"#;
2533        let result3 = parse_and_evaluate(
2534            do_block2,
2535            Some(Rc::clone(&heap)),
2536            Some(Rc::clone(&bindings)),
2537        );
2538        assert!(result3.is_ok());
2539        assert_eq!(result3.unwrap(), Value::Number(15.0));
2540
2541        // Original x should be unchanged
2542        let result4 = parse_and_evaluate("x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2543        assert!(result4.is_ok());
2544        assert_eq!(result4.unwrap(), Value::Number(5.0));
2545    }
2546
2547    #[test]
2548    fn variable_immutability_different_variables() {
2549        let heap = Rc::new(RefCell::new(Heap::new()));
2550        let bindings = Rc::new(RefCell::new(HashMap::new()));
2551
2552        // Multiple different variables should work
2553        let result1 =
2554            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2555        assert!(result1.is_ok());
2556
2557        let result2 =
2558            parse_and_evaluate("y = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2559        assert!(result2.is_ok());
2560
2561        let result3 = parse_and_evaluate(
2562            "z = x + y",
2563            Some(Rc::clone(&heap)),
2564            Some(Rc::clone(&bindings)),
2565        );
2566        assert!(result3.is_ok());
2567        assert_eq!(result3.unwrap(), Value::Number(15.0));
2568    }
2569
2570    #[test]
2571    fn variable_immutability_with_complex_types() {
2572        let heap = Rc::new(RefCell::new(Heap::new()));
2573        let bindings = Rc::new(RefCell::new(HashMap::new()));
2574
2575        // List assignment
2576        let result1 = parse_and_evaluate(
2577            "list = [1, 2, 3]",
2578            Some(Rc::clone(&heap)),
2579            Some(Rc::clone(&bindings)),
2580        );
2581        assert!(result1.is_ok());
2582
2583        // Reassignment should fail
2584        let result2 = parse_and_evaluate(
2585            "list = [4, 5, 6]",
2586            Some(Rc::clone(&heap)),
2587            Some(Rc::clone(&bindings)),
2588        );
2589        assert!(result2.is_err());
2590        assert!(
2591            result2
2592                .unwrap_err()
2593                .to_string()
2594                .contains("list is already defined, and cannot be reassigned")
2595        );
2596
2597        // Record assignment
2598        let result3 = parse_and_evaluate(
2599            "rec = {x: 1, y: 2}",
2600            Some(Rc::clone(&heap)),
2601            Some(Rc::clone(&bindings)),
2602        );
2603        assert!(result3.is_ok());
2604
2605        // Reassignment should fail
2606        let result4 = parse_and_evaluate(
2607            "rec = {x: 3, y: 4}",
2608            Some(Rc::clone(&heap)),
2609            Some(Rc::clone(&bindings)),
2610        );
2611        assert!(result4.is_err());
2612        assert!(
2613            result4
2614                .unwrap_err()
2615                .to_string()
2616                .contains("rec is already defined, and cannot be reassigned")
2617        );
2618    }
2619
2620    #[test]
2621    fn equality_comparison_numbers() {
2622        let result = parse_and_evaluate("5e2 == 5e2", None, None).unwrap();
2623        assert_eq!(result, Value::Bool(true));
2624
2625        let result = parse_and_evaluate("42 == 42", None, None).unwrap();
2626        assert_eq!(result, Value::Bool(true));
2627
2628        let result = parse_and_evaluate("42 == 43", None, None).unwrap();
2629        assert_eq!(result, Value::Bool(false));
2630    }
2631
2632    #[test]
2633    fn equality_comparison_strings() {
2634        let result = parse_and_evaluate(r#""hey" == "hey""#, None, None).unwrap();
2635        assert_eq!(result, Value::Bool(true));
2636
2637        let result = parse_and_evaluate(r#""hello" == "world""#, None, None).unwrap();
2638        assert_eq!(result, Value::Bool(false));
2639
2640        let result = parse_and_evaluate(r#""" == """#, None, None).unwrap();
2641        assert_eq!(result, Value::Bool(true));
2642    }
2643
2644    #[test]
2645    fn equality_comparison_lists() {
2646        let heap = Rc::new(RefCell::new(Heap::new()));
2647        let result = parse_and_evaluate("[1,2,3] == [1,2,3]", Some(heap.clone()), None).unwrap();
2648        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
2649        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2650
2651        let heap2 = Rc::new(RefCell::new(Heap::new()));
2652        let result = parse_and_evaluate("[1,2,3] == [1,2,4]", Some(heap2.clone()), None).unwrap();
2653        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(false)];
2654        assert_eq!(result.as_list(&heap2.borrow()).unwrap(), &expected);
2655
2656        // Note: Lists of different lengths cannot be compared with broadcasting syntax
2657        // They are compared directly as values
2658
2659        // String lists - element-wise comparison
2660        let heap3 = Rc::new(RefCell::new(Heap::new()));
2661        let result =
2662            parse_and_evaluate(r#"["a", "b"] == ["a", "b"]"#, Some(heap3.clone()), None).unwrap();
2663        let expected = vec![Value::Bool(true), Value::Bool(true)];
2664        assert_eq!(result.as_list(&heap3.borrow()).unwrap(), &expected);
2665
2666        // Nested lists - each inner list compared as a whole
2667        let heap4 = Rc::new(RefCell::new(Heap::new()));
2668        let result =
2669            parse_and_evaluate("[[1,2,3]] == [[1,2,3]]", Some(heap4.clone()), None).unwrap();
2670        let expected = vec![Value::Bool(true)];
2671        assert_eq!(result.as_list(&heap4.borrow()).unwrap(), &expected);
2672
2673        let heap5 = Rc::new(RefCell::new(Heap::new()));
2674        let result = parse_and_evaluate(
2675            "[[1,2,3], [4,5]] == [[1,2,3], [4,5]]",
2676            Some(heap5.clone()),
2677            None,
2678        )
2679        .unwrap();
2680        let expected = vec![Value::Bool(true), Value::Bool(true)];
2681        assert_eq!(result.as_list(&heap5.borrow()).unwrap(), &expected);
2682    }
2683
2684    #[test]
2685    fn equality_comparison_records() {
2686        let result = parse_and_evaluate("{ hey: 1 } == { hey: 1 }", None, None).unwrap();
2687        assert_eq!(result, Value::Bool(true));
2688
2689        let result = parse_and_evaluate("{ hey: 1 } == { hey: 2 }", None, None).unwrap();
2690        assert_eq!(result, Value::Bool(false));
2691
2692        let result = parse_and_evaluate("{ hey: 1 } == { hello: 1 }", None, None).unwrap();
2693        assert_eq!(result, Value::Bool(false));
2694
2695        // Multiple fields
2696        let result = parse_and_evaluate("{ a: 1, b: 2 } == { a: 1, b: 2 }", None, None).unwrap();
2697        assert_eq!(result, Value::Bool(true));
2698
2699        // Order shouldn't matter for records
2700        let result = parse_and_evaluate("{ a: 1, b: 2 } == { b: 2, a: 1 }", None, None).unwrap();
2701        assert_eq!(result, Value::Bool(true));
2702
2703        // Nested records
2704        let result = parse_and_evaluate("{ x: { y: 1 } } == { x: { y: 1 } }", None, None).unwrap();
2705        assert_eq!(result, Value::Bool(true));
2706    }
2707
2708    #[test]
2709    fn equality_comparison_lambdas() {
2710        let heap = Rc::new(RefCell::new(Heap::new()));
2711        let bindings = Rc::new(RefCell::new(HashMap::new()));
2712
2713        // Lambdas with same structure are equal
2714        parse_and_evaluate(
2715            "f1 = (x) => x + 2",
2716            Some(Rc::clone(&heap)),
2717            Some(Rc::clone(&bindings)),
2718        )
2719        .unwrap();
2720        parse_and_evaluate(
2721            "f2 = (x) => x + 2",
2722            Some(Rc::clone(&heap)),
2723            Some(Rc::clone(&bindings)),
2724        )
2725        .unwrap();
2726        let result = parse_and_evaluate(
2727            "f1 == f2",
2728            Some(Rc::clone(&heap)),
2729            Some(Rc::clone(&bindings)),
2730        )
2731        .unwrap();
2732        assert_eq!(result, Value::Bool(true));
2733
2734        // Same lambda reference is equal to itself
2735        let result = parse_and_evaluate(
2736            "f1 == f1",
2737            Some(Rc::clone(&heap)),
2738            Some(Rc::clone(&bindings)),
2739        )
2740        .unwrap();
2741        assert_eq!(result, Value::Bool(true));
2742
2743        // Assigned lambda is equal
2744        parse_and_evaluate(
2745            "f3 = f1",
2746            Some(Rc::clone(&heap)),
2747            Some(Rc::clone(&bindings)),
2748        )
2749        .unwrap();
2750        let result = parse_and_evaluate(
2751            "f1 == f3",
2752            Some(Rc::clone(&heap)),
2753            Some(Rc::clone(&bindings)),
2754        )
2755        .unwrap();
2756        assert_eq!(result, Value::Bool(true));
2757
2758        // Different bodies are not equal
2759        parse_and_evaluate(
2760            "f4 = (x) => x + 3",
2761            Some(Rc::clone(&heap)),
2762            Some(Rc::clone(&bindings)),
2763        )
2764        .unwrap();
2765        let result = parse_and_evaluate(
2766            "f1 == f4",
2767            Some(Rc::clone(&heap)),
2768            Some(Rc::clone(&bindings)),
2769        )
2770        .unwrap();
2771        assert_eq!(result, Value::Bool(false));
2772
2773        // Different parameter names are not equal
2774        parse_and_evaluate(
2775            "f5 = (y) => y + 2",
2776            Some(Rc::clone(&heap)),
2777            Some(Rc::clone(&bindings)),
2778        )
2779        .unwrap();
2780        let result = parse_and_evaluate(
2781            "f1 == f5",
2782            Some(Rc::clone(&heap)),
2783            Some(Rc::clone(&bindings)),
2784        )
2785        .unwrap();
2786        assert_eq!(result, Value::Bool(false));
2787
2788        // Different arities are not equal
2789        parse_and_evaluate(
2790            "f6 = (x, y) => x + y",
2791            Some(Rc::clone(&heap)),
2792            Some(Rc::clone(&bindings)),
2793        )
2794        .unwrap();
2795        let result = parse_and_evaluate(
2796            "f1 == f6",
2797            Some(Rc::clone(&heap)),
2798            Some(Rc::clone(&bindings)),
2799        )
2800        .unwrap();
2801        assert_eq!(result, Value::Bool(false));
2802    }
2803
2804    #[test]
2805    fn equality_comparison_mixed_types() {
2806        // Different types are never equal
2807        let result = parse_and_evaluate("1 == \"1\"", None, None).unwrap();
2808        assert_eq!(result, Value::Bool(false));
2809
2810        let result = parse_and_evaluate("true == 1", None, None).unwrap();
2811        assert_eq!(result, Value::Bool(false));
2812
2813        // Note: [1] == 1 uses broadcasting and returns [true]
2814        let heap = Rc::new(RefCell::new(Heap::new()));
2815        let result = parse_and_evaluate("[1] == 1", Some(heap.clone()), None).unwrap();
2816        let expected = vec![Value::Bool(true)];
2817        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2818
2819        let result = parse_and_evaluate("null == 0", None, None).unwrap();
2820        assert_eq!(result, Value::Bool(false));
2821    }
2822
2823    #[test]
2824    fn comparison_operators_strings() {
2825        // Lexicographic comparison
2826        let result = parse_and_evaluate(r#""apple" < "banana""#, None, None).unwrap();
2827        assert_eq!(result, Value::Bool(true));
2828
2829        let result = parse_and_evaluate(r#""zebra" > "apple""#, None, None).unwrap();
2830        assert_eq!(result, Value::Bool(true));
2831
2832        let result = parse_and_evaluate(r#""hello" <= "hello""#, None, None).unwrap();
2833        assert_eq!(result, Value::Bool(true));
2834    }
2835
2836    #[test]
2837    fn comparison_operators_lists() {
2838        // List comparisons use element-wise broadcasting
2839        // [1, 2] < [2, 3] means [1 < 2, 2 < 3] = [true, true]
2840        let heap = Rc::new(RefCell::new(Heap::new()));
2841        let result = parse_and_evaluate("[1, 2] < [2, 3]", Some(heap.clone()), None).unwrap();
2842        let expected = vec![Value::Bool(true), Value::Bool(true)];
2843        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2844
2845        // [1, 2] < [1, 3] means [1 < 1, 2 < 3] = [false, true]
2846        let heap2 = Rc::new(RefCell::new(Heap::new()));
2847        let result = parse_and_evaluate("[1, 2] < [1, 3]", Some(heap2.clone()), None).unwrap();
2848        let expected = vec![Value::Bool(false), Value::Bool(true)];
2849        assert_eq!(result.as_list(&heap2.borrow()).unwrap(), &expected);
2850
2851        // [2, 1] > [1, 0] means [2 > 1, 1 > 0] = [true, true]
2852        let heap3 = Rc::new(RefCell::new(Heap::new()));
2853        let result = parse_and_evaluate("[2, 1] > [1, 0]", Some(heap3.clone()), None).unwrap();
2854        let expected = vec![Value::Bool(true), Value::Bool(true)];
2855        assert_eq!(result.as_list(&heap3.borrow()).unwrap(), &expected);
2856    }
2857
2858    #[test]
2859    fn late_binding_unbound_variable_succeeds() {
2860        // Functions now use late binding - definition succeeds
2861        let heap = Rc::new(RefCell::new(Heap::new()));
2862        let bindings = Rc::new(RefCell::new(HashMap::new()));
2863        let result = parse_and_evaluate("f = x => x + y", Some(heap), Some(bindings));
2864        // Definition should succeed (error only on call if y not bound)
2865        assert!(result.is_ok());
2866    }
2867
2868    #[test]
2869    fn early_binding_bound_variable_succeeds() {
2870        // Should succeed - y is bound before function definition
2871        let heap = Rc::new(RefCell::new(Heap::new()));
2872        let bindings = Rc::new(RefCell::new(HashMap::new()));
2873
2874        // First bind y
2875        let _ = parse_and_evaluate("y = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
2876            .unwrap();
2877
2878        // Then define function that uses y
2879        let result = parse_and_evaluate(
2880            "g = x => x + y",
2881            Some(Rc::clone(&heap)),
2882            Some(Rc::clone(&bindings)),
2883        );
2884        assert!(result.is_ok());
2885
2886        // Verify the function works
2887        let call_result = parse_and_evaluate("g(2)", Some(heap), Some(bindings)).unwrap();
2888        assert_eq!(call_result, Value::Number(7.0));
2889    }
2890
2891    #[test]
2892    fn early_binding_builtin_function_succeeds() {
2893        // Should succeed - using built-in functions
2894        let heap = Rc::new(RefCell::new(Heap::new()));
2895        let bindings = Rc::new(RefCell::new(HashMap::new()));
2896        let result = parse_and_evaluate(
2897            "h = x => sin(x)",
2898            Some(Rc::clone(&heap)),
2899            Some(Rc::clone(&bindings)),
2900        );
2901        assert!(result.is_ok());
2902
2903        // Verify the function works
2904        let call_result = parse_and_evaluate("h(0)", Some(heap), Some(bindings)).unwrap();
2905        assert_eq!(call_result, Value::Number(0.0));
2906    }
2907
2908    #[test]
2909    fn late_binding_nested_function_succeeds() {
2910        // Should succeed with late binding - nested function can reference z defined later
2911        let heap = Rc::new(RefCell::new(Heap::new()));
2912        let bindings = Rc::new(RefCell::new(HashMap::new()));
2913
2914        // Define outer function with nested inner that references z
2915        let result = parse_and_evaluate(
2916            "outer = x => do { inner = y => y + z; return inner(x) }",
2917            Some(Rc::clone(&heap)),
2918            Some(Rc::clone(&bindings)),
2919        );
2920        assert!(result.is_ok());
2921
2922        // Define z
2923        let _ = parse_and_evaluate(
2924            "z = 100",
2925            Some(Rc::clone(&heap)),
2926            Some(Rc::clone(&bindings)),
2927        )
2928        .unwrap();
2929
2930        // Call outer(5) should work: 5 + 100 = 105
2931        let call_result = parse_and_evaluate("outer(5)", Some(heap), Some(bindings)).unwrap();
2932        assert_eq!(call_result, Value::Number(105.0));
2933    }
2934
2935    #[test]
2936    fn early_binding_all_variables_bound() {
2937        // Should succeed - all variables bound
2938        let heap = Rc::new(RefCell::new(Heap::new()));
2939        let bindings = Rc::new(RefCell::new(HashMap::new()));
2940
2941        // Bind a and b
2942        let _ = parse_and_evaluate("a = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
2943            .unwrap();
2944        let _ = parse_and_evaluate("b = 20", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
2945            .unwrap();
2946
2947        // Define function using a and b
2948        let result = parse_and_evaluate(
2949            "func = x => (x + a) * b",
2950            Some(Rc::clone(&heap)),
2951            Some(Rc::clone(&bindings)),
2952        );
2953        assert!(result.is_ok());
2954
2955        // Verify the function works
2956        let call_result = parse_and_evaluate("func(5)", Some(heap), Some(bindings)).unwrap();
2957        assert_eq!(call_result, Value::Number(300.0));
2958    }
2959
2960    #[test]
2961    fn early_binding_parameter_shadows_outer() {
2962        // Should succeed - lambda parameter shadows outer variable
2963        let heap = Rc::new(RefCell::new(Heap::new()));
2964        let bindings = Rc::new(RefCell::new(HashMap::new()));
2965
2966        // Bind outer x
2967        let _ = parse_and_evaluate(
2968            "x = 100",
2969            Some(Rc::clone(&heap)),
2970            Some(Rc::clone(&bindings)),
2971        )
2972        .unwrap();
2973
2974        // Define function with parameter x that shadows outer x
2975        let result = parse_and_evaluate(
2976            "shadow = x => x * 2",
2977            Some(Rc::clone(&heap)),
2978            Some(Rc::clone(&bindings)),
2979        );
2980        assert!(result.is_ok());
2981
2982        // Verify the function uses parameter, not outer variable
2983        let call_result = parse_and_evaluate("shadow(5)", Some(heap), Some(bindings)).unwrap();
2984        assert_eq!(call_result, Value::Number(10.0));
2985    }
2986
2987    #[test]
2988    fn early_binding_do_block_internal_binding() {
2989        // Should succeed - variable bound inside lambda's do block
2990        let heap = Rc::new(RefCell::new(Heap::new()));
2991        let bindings = Rc::new(RefCell::new(HashMap::new()));
2992
2993        // Define function with do block that binds y internally
2994        let result = parse_and_evaluate(
2995            "f = x => do { y = 5; return x + y }",
2996            Some(Rc::clone(&heap)),
2997            Some(Rc::clone(&bindings)),
2998        );
2999        assert!(result.is_ok());
3000
3001        // Verify the function works
3002        let call_result = parse_and_evaluate("f(10)", Some(heap), Some(bindings)).unwrap();
3003        assert_eq!(call_result, Value::Number(15.0));
3004    }
3005
3006    #[test]
3007    fn early_binding_do_block_nested_scope() {
3008        // Should succeed - variable bound in do block before inner function definition
3009        let heap = Rc::new(RefCell::new(Heap::new()));
3010        let bindings = Rc::new(RefCell::new(HashMap::new()));
3011
3012        let result = parse_and_evaluate(
3013            "later_func = do { c = 30; f = x => x + c; return f(10) }",
3014            Some(Rc::clone(&heap)),
3015            Some(Rc::clone(&bindings)),
3016        );
3017        assert!(result.is_ok());
3018
3019        // Verify the result
3020        let later_func_result =
3021            parse_and_evaluate("later_func", Some(heap), Some(bindings)).unwrap();
3022        assert_eq!(later_func_result, Value::Number(40.0));
3023    }
3024
3025    #[test]
3026    fn late_binding_do_block_forward_reference_succeeds() {
3027        // Should succeed with late binding - do block variables are available
3028        let heap = Rc::new(RefCell::new(Heap::new()));
3029        let bindings = Rc::new(RefCell::new(HashMap::new()));
3030
3031        let result = parse_and_evaluate(
3032            "g = x => do { f = y => y + z; z = 10; return f(x) }",
3033            Some(Rc::clone(&heap)),
3034            Some(Rc::clone(&bindings)),
3035        );
3036        assert!(result.is_ok());
3037
3038        // Call g(5) should work: 5 + 10 = 15
3039        let call_result = parse_and_evaluate("g(5)", Some(heap), Some(bindings)).unwrap();
3040        assert_eq!(call_result, Value::Number(15.0));
3041    }
3042
3043    #[test]
3044    fn early_binding_do_block_multiple_assignments() {
3045        // Should succeed - multiple assignments in do block
3046        let heap = Rc::new(RefCell::new(Heap::new()));
3047        let bindings = Rc::new(RefCell::new(HashMap::new()));
3048
3049        let result = parse_and_evaluate(
3050            "calc = x => do { a = 2; b = 3; c = 4; return x * a + b * c }",
3051            Some(Rc::clone(&heap)),
3052            Some(Rc::clone(&bindings)),
3053        );
3054        assert!(result.is_ok());
3055
3056        // Verify the function works: 5 * 2 + 3 * 4 = 10 + 12 = 22
3057        let call_result = parse_and_evaluate("calc(5)", Some(heap), Some(bindings)).unwrap();
3058        assert_eq!(call_result, Value::Number(22.0));
3059    }
3060
3061    #[test]
3062    fn late_binding_multiple_unbound_variables_succeeds() {
3063        // Should succeed with late binding - function can reference y and z defined later
3064        let heap = Rc::new(RefCell::new(Heap::new()));
3065        let bindings = Rc::new(RefCell::new(HashMap::new()));
3066
3067        let result = parse_and_evaluate(
3068            "f = x => x + y + z",
3069            Some(Rc::clone(&heap)),
3070            Some(Rc::clone(&bindings)),
3071        );
3072        assert!(result.is_ok());
3073
3074        // Define y and z
3075        let _ = parse_and_evaluate("y = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
3076            .unwrap();
3077        let _ = parse_and_evaluate("z = 20", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
3078            .unwrap();
3079
3080        // Call f(5) should work: 5 + 10 + 20 = 35
3081        let call_result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
3082        assert_eq!(call_result, Value::Number(35.0));
3083    }
3084
3085    #[test]
3086    fn late_binding_with_optional_args_succeeds() {
3087        // Should succeed with late binding - function with optional args can reference z defined later
3088        let heap = Rc::new(RefCell::new(Heap::new()));
3089        let bindings = Rc::new(RefCell::new(HashMap::new()));
3090
3091        let result = parse_and_evaluate(
3092            "f = (x, y?) => x + z",
3093            Some(Rc::clone(&heap)),
3094            Some(Rc::clone(&bindings)),
3095        );
3096        assert!(result.is_ok());
3097
3098        // Define z
3099        let _ = parse_and_evaluate(
3100            "z = 100",
3101            Some(Rc::clone(&heap)),
3102            Some(Rc::clone(&bindings)),
3103        )
3104        .unwrap();
3105
3106        // Call f(5) should work: 5 + 100 = 105
3107        let call_result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
3108        assert_eq!(call_result, Value::Number(105.0));
3109    }
3110
3111    #[test]
3112    fn late_binding_with_rest_args_succeeds() {
3113        // Should succeed with late binding - function with rest args can reference y defined later
3114        let heap = Rc::new(RefCell::new(Heap::new()));
3115        let bindings = Rc::new(RefCell::new(HashMap::new()));
3116
3117        let result = parse_and_evaluate(
3118            "f = (x, ...rest) => x + y",
3119            Some(Rc::clone(&heap)),
3120            Some(Rc::clone(&bindings)),
3121        );
3122        assert!(result.is_ok());
3123
3124        // Define y
3125        let _ = parse_and_evaluate("y = 50", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
3126            .unwrap();
3127
3128        // Call f(5) should work: 5 + 50 = 55
3129        let call_result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
3130        assert_eq!(call_result, Value::Number(55.0));
3131    }
3132
3133    #[test]
3134    fn recursion_self_reference_allowed() {
3135        // Should succeed - recursive functions can reference themselves
3136        let heap = Rc::new(RefCell::new(Heap::new()));
3137        let bindings = Rc::new(RefCell::new(HashMap::new()));
3138
3139        // Define recursive factorial
3140        let result = parse_and_evaluate(
3141            "factorial = n => if n <= 1 then 1 else n * factorial(n - 1)",
3142            Some(Rc::clone(&heap)),
3143            Some(Rc::clone(&bindings)),
3144        );
3145        assert!(result.is_ok());
3146
3147        // Test it works
3148        let call_result = parse_and_evaluate("factorial(5)", Some(heap), Some(bindings)).unwrap();
3149        assert_eq!(call_result, Value::Number(120.0));
3150    }
3151
3152    #[test]
3153    fn recursion_fibonacci() {
3154        // Should succeed - another recursive function example
3155        let heap = Rc::new(RefCell::new(Heap::new()));
3156        let bindings = Rc::new(RefCell::new(HashMap::new()));
3157
3158        // Define recursive fibonacci
3159        let result = parse_and_evaluate(
3160            "fib = n => if n <= 1 then n else fib(n - 1) + fib(n - 2)",
3161            Some(Rc::clone(&heap)),
3162            Some(Rc::clone(&bindings)),
3163        );
3164        assert!(result.is_ok());
3165
3166        // Test it works - fib(6) = 8
3167        let call_result = parse_and_evaluate("fib(6)", Some(heap), Some(bindings)).unwrap();
3168        assert_eq!(call_result, Value::Number(8.0));
3169    }
3170
3171    #[test]
3172    fn recursion_in_do_block() {
3173        // Should succeed - recursion defined in do block
3174        let heap = Rc::new(RefCell::new(Heap::new()));
3175        let bindings = Rc::new(RefCell::new(HashMap::new()));
3176
3177        let result = parse_and_evaluate(
3178            "result = do { factorial = n => if n <= 1 then 1 else n * factorial(n - 1); return factorial(4) }",
3179            Some(Rc::clone(&heap)),
3180            Some(Rc::clone(&bindings)),
3181        );
3182        assert!(result.is_ok());
3183
3184        // Verify the result
3185        let result_val = parse_and_evaluate("result", Some(heap), Some(bindings)).unwrap();
3186        assert_eq!(result_val, Value::Number(24.0));
3187    }
3188
3189    #[test]
3190    fn mutual_recursion_succeeds() {
3191        // Should succeed with late binding - mutual recursion now works
3192        let heap = Rc::new(RefCell::new(Heap::new()));
3193        let bindings = Rc::new(RefCell::new(HashMap::new()));
3194
3195        // Define isEven that references isOdd
3196        let result = parse_and_evaluate(
3197            "isEven = n => if n == 0 then true else isOdd(n - 1)",
3198            Some(Rc::clone(&heap)),
3199            Some(Rc::clone(&bindings)),
3200        );
3201        assert!(result.is_ok());
3202
3203        // Define isOdd that references isEven
3204        let _ = parse_and_evaluate(
3205            "isOdd = n => if n == 0 then false else isEven(n - 1)",
3206            Some(Rc::clone(&heap)),
3207            Some(Rc::clone(&bindings)),
3208        )
3209        .unwrap();
3210
3211        // Test mutual recursion
3212        let result1 = parse_and_evaluate(
3213            "isEven(4)",
3214            Some(Rc::clone(&heap)),
3215            Some(Rc::clone(&bindings)),
3216        )
3217        .unwrap();
3218        assert_eq!(result1, Value::Bool(true));
3219
3220        let result2 = parse_and_evaluate(
3221            "isEven(5)",
3222            Some(Rc::clone(&heap)),
3223            Some(Rc::clone(&bindings)),
3224        )
3225        .unwrap();
3226        assert_eq!(result2, Value::Bool(false));
3227
3228        let result3 = parse_and_evaluate(
3229            "isOdd(3)",
3230            Some(Rc::clone(&heap)),
3231            Some(Rc::clone(&bindings)),
3232        )
3233        .unwrap();
3234        assert_eq!(result3, Value::Bool(true));
3235
3236        let result4 = parse_and_evaluate("isOdd(6)", Some(heap), Some(bindings)).unwrap();
3237        assert_eq!(result4, Value::Bool(false));
3238    }
3239
3240    #[test]
3241    fn recursion_only_for_lambdas() {
3242        // Non-lambda assignments should not get special treatment
3243        let heap = Rc::new(RefCell::new(Heap::new()));
3244        let bindings = Rc::new(RefCell::new(HashMap::new()));
3245
3246        // This should fail because x is not bound
3247        let result = parse_and_evaluate("y = x + 1", Some(heap), Some(bindings));
3248        assert!(result.is_err());
3249    }
3250
3251    // Tests for dot-prefixed comparison operators (non-broadcasting)
3252    #[test]
3253    fn dot_equal_lists() {
3254        let heap = Rc::new(RefCell::new(Heap::new()));
3255
3256        // Lists with same values should be equal
3257        let result =
3258            parse_and_evaluate("[1, 2, 3] .== [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3259        assert_eq!(result, Value::Bool(true));
3260
3261        // Different lists should not be equal
3262        let result =
3263            parse_and_evaluate("[1, 2, 3] .== [1, 2, 4]", Some(Rc::clone(&heap)), None).unwrap();
3264        assert_eq!(result, Value::Bool(false));
3265
3266        // Different lengths should not be equal
3267        let result =
3268            parse_and_evaluate("[1, 2] .== [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3269        assert_eq!(result, Value::Bool(false));
3270
3271        // List vs scalar should not be equal
3272        let result = parse_and_evaluate("[1, 2, 3] .== 1", Some(Rc::clone(&heap)), None).unwrap();
3273        assert_eq!(result, Value::Bool(false));
3274
3275        // Empty lists should be equal
3276        let result = parse_and_evaluate("[] .== []", Some(Rc::clone(&heap)), None).unwrap();
3277        assert_eq!(result, Value::Bool(true));
3278
3279        // Nested lists
3280        let result =
3281            parse_and_evaluate("[[1, 2], [3, 4]] .== [[1, 2], [3, 4]]", Some(heap), None).unwrap();
3282        assert_eq!(result, Value::Bool(true));
3283    }
3284
3285    #[test]
3286    fn dot_not_equal_lists() {
3287        let heap = Rc::new(RefCell::new(Heap::new()));
3288
3289        let result =
3290            parse_and_evaluate("[1, 2, 3] .!= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3291        assert_eq!(result, Value::Bool(false));
3292
3293        let result =
3294            parse_and_evaluate("[1, 2, 3] .!= [1, 2, 4]", Some(Rc::clone(&heap)), None).unwrap();
3295        assert_eq!(result, Value::Bool(true));
3296
3297        let result = parse_and_evaluate("[1, 2, 3] .!= 1", Some(heap), None).unwrap();
3298        assert_eq!(result, Value::Bool(true));
3299    }
3300
3301    #[test]
3302    fn dot_less_lists() {
3303        let heap = Rc::new(RefCell::new(Heap::new()));
3304
3305        // Lexicographic comparison
3306        let result =
3307            parse_and_evaluate("[1, 2, 2] .< [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3308        assert_eq!(result, Value::Bool(true));
3309
3310        let result =
3311            parse_and_evaluate("[1, 2] .< [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3312        assert_eq!(result, Value::Bool(true));
3313
3314        let result = parse_and_evaluate("[] .< [1]", Some(Rc::clone(&heap)), None).unwrap();
3315        assert_eq!(result, Value::Bool(true));
3316
3317        let result = parse_and_evaluate("[2] .< [1, 9, 9]", Some(Rc::clone(&heap)), None).unwrap();
3318        assert_eq!(result, Value::Bool(false));
3319
3320        let result = parse_and_evaluate("[1, 2, 3] .< [1, 2, 3]", Some(heap), None).unwrap();
3321        assert_eq!(result, Value::Bool(false));
3322    }
3323
3324    #[test]
3325    fn dot_less_eq_lists() {
3326        let heap = Rc::new(RefCell::new(Heap::new()));
3327
3328        let result =
3329            parse_and_evaluate("[1, 2, 2] .<= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3330        assert_eq!(result, Value::Bool(true));
3331
3332        let result =
3333            parse_and_evaluate("[1, 2, 3] .<= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3334        assert_eq!(result, Value::Bool(true));
3335
3336        let result = parse_and_evaluate("[1, 2, 4] .<= [1, 2, 3]", Some(heap), None).unwrap();
3337        assert_eq!(result, Value::Bool(false));
3338    }
3339
3340    #[test]
3341    fn dot_greater_lists() {
3342        let heap = Rc::new(RefCell::new(Heap::new()));
3343
3344        let result =
3345            parse_and_evaluate("[1, 2, 3] .> [1, 2, 2]", Some(Rc::clone(&heap)), None).unwrap();
3346        assert_eq!(result, Value::Bool(true));
3347
3348        let result = parse_and_evaluate("[2] .> [1, 9, 9]", Some(Rc::clone(&heap)), None).unwrap();
3349        assert_eq!(result, Value::Bool(true));
3350
3351        let result = parse_and_evaluate("[1] .> []", Some(Rc::clone(&heap)), None).unwrap();
3352        assert_eq!(result, Value::Bool(true));
3353
3354        let result = parse_and_evaluate("[1, 2, 3] .> [1, 2, 3]", Some(heap), None).unwrap();
3355        assert_eq!(result, Value::Bool(false));
3356    }
3357
3358    #[test]
3359    fn dot_greater_eq_lists() {
3360        let heap = Rc::new(RefCell::new(Heap::new()));
3361
3362        let result =
3363            parse_and_evaluate("[1, 2, 3] .>= [1, 2, 2]", Some(Rc::clone(&heap)), None).unwrap();
3364        assert_eq!(result, Value::Bool(true));
3365
3366        let result =
3367            parse_and_evaluate("[1, 2, 3] .>= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3368        assert_eq!(result, Value::Bool(true));
3369
3370        let result = parse_and_evaluate("[1, 2, 2] .>= [1, 2, 3]", Some(heap), None).unwrap();
3371        assert_eq!(result, Value::Bool(false));
3372    }
3373
3374    #[test]
3375    fn dot_operators_with_scalars() {
3376        // Dot operators should work normally with scalars
3377        let result = parse_and_evaluate("5 .== 5", None, None).unwrap();
3378        assert_eq!(result, Value::Bool(true));
3379
3380        let result = parse_and_evaluate("5 .!= 3", None, None).unwrap();
3381        assert_eq!(result, Value::Bool(true));
3382
3383        let result = parse_and_evaluate("3 .< 5", None, None).unwrap();
3384        assert_eq!(result, Value::Bool(true));
3385
3386        let result = parse_and_evaluate("5 .> 3", None, None).unwrap();
3387        assert_eq!(result, Value::Bool(true));
3388    }
3389
3390    #[test]
3391    fn dot_equal_vs_regular_equal_with_broadcasting() {
3392        let heap = Rc::new(RefCell::new(Heap::new()));
3393
3394        // Regular == with broadcasting: each element compared individually
3395        let result =
3396            parse_and_evaluate("[true, true, true] == true", Some(Rc::clone(&heap)), None).unwrap();
3397        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
3398        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
3399
3400        // Dot operator without broadcasting: list doesn't equal scalar
3401        let result =
3402            parse_and_evaluate("[true, true, true] .== true", Some(Rc::clone(&heap)), None)
3403                .unwrap();
3404        assert_eq!(result, Value::Bool(false));
3405
3406        // Dot operator comparing whole lists
3407        let result = parse_and_evaluate(
3408            "[true, true, true] .== [true, true, true]",
3409            Some(heap),
3410            None,
3411        )
3412        .unwrap();
3413        assert_eq!(result, Value::Bool(true));
3414    }
3415
3416    #[test]
3417    fn dot_operators_mixed_types() {
3418        let heap = Rc::new(RefCell::new(Heap::new()));
3419
3420        // String vs list should not be equal
3421        let result =
3422            parse_and_evaluate("\"hello\" .== [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3423        assert_eq!(result, Value::Bool(false));
3424
3425        // Number vs list comparison returns false (no natural ordering)
3426        let result = parse_and_evaluate("5 .< [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3427        assert_eq!(result, Value::Bool(false));
3428
3429        // String comparison works
3430        let result = parse_and_evaluate("\"abc\" .< \"def\"", Some(heap), None).unwrap();
3431        assert_eq!(result, Value::Bool(true));
3432    }
3433}