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