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 mapped_list = l_vec
743                        .iter()
744                        .zip(&r_vec)
745                        .map(|(l, r)| Ok(Bool(l.equals(r, &heap.borrow())?)))
746                        .collect::<Result<Vec<Value>>>()?;
747                    Ok(heap.borrow_mut().insert_list(mapped_list))
748                }
749                BinaryOp::NotEqual => {
750                    let mapped_list = l_vec
751                        .iter()
752                        .zip(&r_vec)
753                        .map(|(l, r)| Ok(Bool(!l.equals(r, &heap.borrow())?)))
754                        .collect::<Result<Vec<Value>>>()?;
755                    Ok(heap.borrow_mut().insert_list(mapped_list))
756                }
757                BinaryOp::Less => {
758                    let mapped_list = l_vec
759                        .iter()
760                        .zip(&r_vec)
761                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
762                            Some(std::cmp::Ordering::Less) => Ok(Bool(true)),
763                            _ => Ok(Bool(false)),
764                        })
765                        .collect::<Result<Vec<Value>>>()?;
766                    Ok(heap.borrow_mut().insert_list(mapped_list))
767                }
768                BinaryOp::LessEq => {
769                    let mapped_list = l_vec
770                        .iter()
771                        .zip(&r_vec)
772                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
773                            Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal) => {
774                                Ok(Bool(true))
775                            }
776                            _ => Ok(Bool(false)),
777                        })
778                        .collect::<Result<Vec<Value>>>()?;
779                    Ok(heap.borrow_mut().insert_list(mapped_list))
780                }
781                BinaryOp::Greater => {
782                    let mapped_list = l_vec
783                        .iter()
784                        .zip(&r_vec)
785                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
786                            Some(std::cmp::Ordering::Greater) => Ok(Bool(true)),
787                            _ => Ok(Bool(false)),
788                        })
789                        .collect::<Result<Vec<Value>>>()?;
790                    Ok(heap.borrow_mut().insert_list(mapped_list))
791                }
792                BinaryOp::GreaterEq => {
793                    let mapped_list = l_vec
794                        .iter()
795                        .zip(&r_vec)
796                        .map(|(l, r)| match l.compare(r, &heap.borrow())? {
797                            Some(std::cmp::Ordering::Greater) | Some(std::cmp::Ordering::Equal) => {
798                                Ok(Bool(true))
799                            }
800                            _ => Ok(Bool(false)),
801                        })
802                        .collect::<Result<Vec<Value>>>()?;
803                    Ok(heap.borrow_mut().insert_list(mapped_list))
804                }
805                BinaryOp::And | BinaryOp::NaturalAnd => {
806                    let mapped_list = l_vec
807                        .iter()
808                        .zip(&r_vec)
809                        .map(|(l, r)| Ok(Bool(l.as_bool()? && r.as_bool()?)))
810                        .collect::<Result<Vec<Value>>>()?;
811                    Ok(heap.borrow_mut().insert_list(mapped_list))
812                }
813                BinaryOp::Or | BinaryOp::NaturalOr => {
814                    let mapped_list = l_vec
815                        .iter()
816                        .zip(&r_vec)
817                        .map(|(l, r)| Ok(Bool(l.as_bool()? || r.as_bool()?)))
818                        .collect::<Result<Vec<Value>>>()?;
819                    Ok(heap.borrow_mut().insert_list(mapped_list))
820                }
821                BinaryOp::Add => {
822                    let mapped_list = l_vec
823                        .iter()
824                        .zip(&r_vec)
825                        .map(|(l, r)| match (l, r) {
826                            (Value::String(_), Value::String(_)) => {
827                                let (l_str, r_str) = {
828                                    (
829                                        l.as_string(&heap.borrow())?.to_string(),
830                                        r.as_string(&heap.borrow())?.to_string(),
831                                    )
832                                };
833                                Ok(heap
834                                    .borrow_mut()
835                                    .insert_string(format!("{}{}", l_str, r_str)))
836                            }
837                            (Value::Number(_), Value::Number(_)) => {
838                                Ok(Number(l.as_number()? + r.as_number()?))
839                            }
840                            _ => Err(anyhow!("can't add {} and {}", l.get_type(), r.get_type())),
841                        })
842                        .collect::<Result<Vec<Value>>>()?;
843                    Ok(heap.borrow_mut().insert_list(mapped_list))
844                }
845                BinaryOp::Subtract => {
846                    let mapped_list = l_vec
847                        .iter()
848                        .zip(&r_vec)
849                        .map(|(l, r)| Ok(Number(l.as_number()? - r.as_number()?)))
850                        .collect::<Result<Vec<Value>>>()?;
851                    Ok(heap.borrow_mut().insert_list(mapped_list))
852                }
853                BinaryOp::Multiply => {
854                    let mapped_list = l_vec
855                        .iter()
856                        .zip(&r_vec)
857                        .map(|(l, r)| Ok(Number(l.as_number()? * r.as_number()?)))
858                        .collect::<Result<Vec<Value>>>()?;
859                    Ok(heap.borrow_mut().insert_list(mapped_list))
860                }
861                BinaryOp::Divide => {
862                    let mapped_list = l_vec
863                        .iter()
864                        .zip(&r_vec)
865                        .map(|(l, r)| Ok(Number(l.as_number()? / r.as_number()?)))
866                        .collect::<Result<Vec<Value>>>()?;
867                    Ok(heap.borrow_mut().insert_list(mapped_list))
868                }
869                BinaryOp::Modulo => {
870                    let mapped_list = l_vec
871                        .iter()
872                        .zip(&r_vec)
873                        .map(|(l, r)| Ok(Number(l.as_number()? % r.as_number()?)))
874                        .collect::<Result<Vec<Value>>>()?;
875                    Ok(heap.borrow_mut().insert_list(mapped_list))
876                }
877                BinaryOp::Power => {
878                    let mapped_list = l_vec
879                        .iter()
880                        .zip(&r_vec)
881                        .map(|(l, r)| Ok(Number(l.as_number()?.powf(r.as_number()?))))
882                        .collect::<Result<Vec<Value>>>()?;
883                    Ok(heap.borrow_mut().insert_list(mapped_list))
884                }
885                BinaryOp::Coalesce => {
886                    let mapped_list = l_vec
887                        .iter()
888                        .zip(&r_vec)
889                        .map(|(l, r)| if *l == Value::Null { Ok(*r) } else { Ok(*l) })
890                        .collect::<Result<Vec<Value>>>()?;
891                    Ok(heap.borrow_mut().insert_list(mapped_list))
892                }
893                BinaryOp::Via => {
894                    let mapped_list = l_vec
895                        .iter()
896                        .zip(&r_vec)
897                        .map(|(l, r)| {
898                            if !r.is_lambda() && !r.is_built_in() {
899                                return Err(anyhow!(
900                                    "right-hand iterable contains non-function {}",
901                                    r.stringify_internal(&heap.borrow())
902                                ));
903                            }
904
905                            let def = {
906                                let borrowed_heap = &heap.borrow();
907                                get_function_def(r, borrowed_heap).ok_or_else(|| {
908                                    anyhow!(
909                                        "unknown function: {}",
910                                        r.stringify_internal(borrowed_heap)
911                                    )
912                                })?
913                            };
914
915                            def.call(
916                                *r,
917                                vec![*l],
918                                Rc::clone(&heap),
919                                Rc::clone(&bindings),
920                                call_depth,
921                            )
922                        })
923                        .collect::<Result<Vec<Value>>>()?;
924                    Ok(heap.borrow_mut().insert_list(mapped_list))
925                }
926                BinaryOp::Into => {
927                    // This should never be reached due to early check above
928                    unreachable!("Into operator should not reach list-to-list evaluation")
929                }
930                BinaryOp::DotEqual
931                | BinaryOp::DotNotEqual
932                | BinaryOp::DotLess
933                | BinaryOp::DotLessEq
934                | BinaryOp::DotGreater
935                | BinaryOp::DotGreaterEq => {
936                    // Dot operators are handled before this match, so this should never be reached
937                    unreachable!("Dot operators should be handled before list broadcasting")
938                }
939            }
940        }
941        (Value::List(list), scalar) | (scalar, Value::List(list)) => {
942            let (l_vec, is_list_first) = {
943                let borrowed_heap = &heap.borrow();
944                if matches!(lhs, Value::List(_)) {
945                    (list.reify(borrowed_heap).as_list()?.clone(), true)
946                } else {
947                    (list.reify(borrowed_heap).as_list()?.clone(), false)
948                }
949            };
950
951            match op {
952                BinaryOp::Equal => {
953                    let mapped_list = l_vec
954                        .iter()
955                        .map(|v| Ok(Bool(v.equals(&scalar, &heap.borrow())?)))
956                        .collect::<Result<Vec<Value>>>()?;
957                    Ok(heap.borrow_mut().insert_list(mapped_list))
958                }
959                BinaryOp::NotEqual => {
960                    let mapped_list = l_vec
961                        .iter()
962                        .map(|v| Ok(Bool(!v.equals(&scalar, &heap.borrow())?)))
963                        .collect::<Result<Vec<Value>>>()?;
964                    Ok(heap.borrow_mut().insert_list(mapped_list))
965                }
966                BinaryOp::Less => {
967                    let mapped_list = l_vec
968                        .iter()
969                        .map(|v| {
970                            let ordering = if is_list_first {
971                                v.compare(&scalar, &heap.borrow())?
972                            } else {
973                                scalar.compare(v, &heap.borrow())?
974                            };
975                            match ordering {
976                                Some(std::cmp::Ordering::Less) => Ok(Bool(true)),
977                                _ => Ok(Bool(false)),
978                            }
979                        })
980                        .collect::<Result<Vec<Value>>>()?;
981                    Ok(heap.borrow_mut().insert_list(mapped_list))
982                }
983                BinaryOp::LessEq => {
984                    let mapped_list = l_vec
985                        .iter()
986                        .map(|v| {
987                            let ordering = if is_list_first {
988                                v.compare(&scalar, &heap.borrow())?
989                            } else {
990                                scalar.compare(v, &heap.borrow())?
991                            };
992                            match ordering {
993                                Some(std::cmp::Ordering::Less)
994                                | Some(std::cmp::Ordering::Equal) => Ok(Bool(true)),
995                                _ => Ok(Bool(false)),
996                            }
997                        })
998                        .collect::<Result<Vec<Value>>>()?;
999                    Ok(heap.borrow_mut().insert_list(mapped_list))
1000                }
1001                BinaryOp::Greater => {
1002                    let mapped_list = l_vec
1003                        .iter()
1004                        .map(|v| {
1005                            let ordering = if is_list_first {
1006                                v.compare(&scalar, &heap.borrow())?
1007                            } else {
1008                                scalar.compare(v, &heap.borrow())?
1009                            };
1010                            match ordering {
1011                                Some(std::cmp::Ordering::Greater) => Ok(Bool(true)),
1012                                _ => Ok(Bool(false)),
1013                            }
1014                        })
1015                        .collect::<Result<Vec<Value>>>()?;
1016                    Ok(heap.borrow_mut().insert_list(mapped_list))
1017                }
1018                BinaryOp::GreaterEq => {
1019                    let mapped_list = l_vec
1020                        .iter()
1021                        .map(|v| {
1022                            let ordering = if is_list_first {
1023                                v.compare(&scalar, &heap.borrow())?
1024                            } else {
1025                                scalar.compare(v, &heap.borrow())?
1026                            };
1027                            match ordering {
1028                                Some(std::cmp::Ordering::Greater)
1029                                | Some(std::cmp::Ordering::Equal) => Ok(Bool(true)),
1030                                _ => Ok(Bool(false)),
1031                            }
1032                        })
1033                        .collect::<Result<Vec<Value>>>()?;
1034                    Ok(heap.borrow_mut().insert_list(mapped_list))
1035                }
1036                BinaryOp::And | BinaryOp::NaturalAnd => {
1037                    let mapped_list = if is_list_first {
1038                        l_vec
1039                            .iter()
1040                            .map(|v| Ok(Bool(v.as_bool()? && scalar.as_bool()?)))
1041                            .collect::<Result<Vec<Value>>>()?
1042                    } else {
1043                        l_vec
1044                            .iter()
1045                            .map(|v| Ok(Bool(scalar.as_bool()? && v.as_bool()?)))
1046                            .collect::<Result<Vec<Value>>>()?
1047                    };
1048                    Ok(heap.borrow_mut().insert_list(mapped_list))
1049                }
1050                BinaryOp::Or | BinaryOp::NaturalOr => {
1051                    let mapped_list = if is_list_first {
1052                        l_vec
1053                            .iter()
1054                            .map(|v| Ok(Bool(v.as_bool()? || scalar.as_bool()?)))
1055                            .collect::<Result<Vec<Value>>>()?
1056                    } else {
1057                        l_vec
1058                            .iter()
1059                            .map(|v| Ok(Bool(scalar.as_bool()? || v.as_bool()?)))
1060                            .collect::<Result<Vec<Value>>>()?
1061                    };
1062                    Ok(heap.borrow_mut().insert_list(mapped_list))
1063                }
1064                BinaryOp::Add => {
1065                    let mapped_list = if is_list_first {
1066                        l_vec
1067                            .iter()
1068                            .map(|v| match (v, &scalar) {
1069                                (Value::String(_), Value::String(_)) => {
1070                                    let (v_str, s_str) = {
1071                                        (
1072                                            v.as_string(&heap.borrow())?.to_string(),
1073                                            scalar.as_string(&heap.borrow())?.to_string(),
1074                                        )
1075                                    };
1076                                    Ok(heap
1077                                        .borrow_mut()
1078                                        .insert_string(format!("{}{}", v_str, s_str)))
1079                                }
1080                                (Value::Number(_), Value::Number(_)) => {
1081                                    Ok(Number(v.as_number()? + scalar.as_number()?))
1082                                }
1083                                _ => Err(anyhow!(
1084                                    "can't add {} and {}",
1085                                    v.get_type(),
1086                                    scalar.get_type()
1087                                )),
1088                            })
1089                            .collect::<Result<Vec<Value>>>()?
1090                    } else {
1091                        l_vec
1092                            .iter()
1093                            .map(|v| match (&scalar, v) {
1094                                (Value::String(_), Value::String(_)) => {
1095                                    let (s_str, v_str) = {
1096                                        (
1097                                            scalar.as_string(&heap.borrow())?.to_string(),
1098                                            v.as_string(&heap.borrow())?.to_string(),
1099                                        )
1100                                    };
1101                                    Ok(heap
1102                                        .borrow_mut()
1103                                        .insert_string(format!("{}{}", s_str, v_str)))
1104                                }
1105                                (Value::Number(_), Value::Number(_)) => {
1106                                    Ok(Number(scalar.as_number()? + v.as_number()?))
1107                                }
1108                                _ => Err(anyhow!(
1109                                    "can't add {} and {}",
1110                                    scalar.get_type(),
1111                                    v.get_type()
1112                                )),
1113                            })
1114                            .collect::<Result<Vec<Value>>>()?
1115                    };
1116                    Ok(heap.borrow_mut().insert_list(mapped_list))
1117                }
1118                BinaryOp::Subtract => {
1119                    let mapped_list = if is_list_first {
1120                        l_vec
1121                            .iter()
1122                            .map(|v| Ok(Number(v.as_number()? - scalar.as_number()?)))
1123                            .collect::<Result<Vec<Value>>>()?
1124                    } else {
1125                        l_vec
1126                            .iter()
1127                            .map(|v| Ok(Number(scalar.as_number()? - v.as_number()?)))
1128                            .collect::<Result<Vec<Value>>>()?
1129                    };
1130                    Ok(heap.borrow_mut().insert_list(mapped_list))
1131                }
1132                BinaryOp::Multiply => {
1133                    let mapped_list = l_vec
1134                        .iter()
1135                        .map(|v| Ok(Number(v.as_number()? * scalar.as_number()?)))
1136                        .collect::<Result<Vec<Value>>>()?;
1137                    Ok(heap.borrow_mut().insert_list(mapped_list))
1138                }
1139                BinaryOp::Divide => {
1140                    let mapped_list = if is_list_first {
1141                        l_vec
1142                            .iter()
1143                            .map(|v| Ok(Number(v.as_number()? / scalar.as_number()?)))
1144                            .collect::<Result<Vec<Value>>>()?
1145                    } else {
1146                        l_vec
1147                            .iter()
1148                            .map(|v| Ok(Number(scalar.as_number()? / v.as_number()?)))
1149                            .collect::<Result<Vec<Value>>>()?
1150                    };
1151                    Ok(heap.borrow_mut().insert_list(mapped_list))
1152                }
1153                BinaryOp::Modulo => {
1154                    let mapped_list = if is_list_first {
1155                        l_vec
1156                            .iter()
1157                            .map(|v| Ok(Number(v.as_number()? % scalar.as_number()?)))
1158                            .collect::<Result<Vec<Value>>>()?
1159                    } else {
1160                        l_vec
1161                            .iter()
1162                            .map(|v| Ok(Number(scalar.as_number()? % v.as_number()?)))
1163                            .collect::<Result<Vec<Value>>>()?
1164                    };
1165                    Ok(heap.borrow_mut().insert_list(mapped_list))
1166                }
1167                BinaryOp::Power => {
1168                    let mapped_list = if is_list_first {
1169                        l_vec
1170                            .iter()
1171                            .map(|v| Ok(Number(v.as_number()?.powf(scalar.as_number()?))))
1172                            .collect::<Result<Vec<Value>>>()?
1173                    } else {
1174                        l_vec
1175                            .iter()
1176                            .map(|v| Ok(Number(scalar.as_number()?.powf(v.as_number()?))))
1177                            .collect::<Result<Vec<Value>>>()?
1178                    };
1179                    Ok(heap.borrow_mut().insert_list(mapped_list))
1180                }
1181                BinaryOp::Coalesce => {
1182                    let mapped_list = if is_list_first {
1183                        l_vec
1184                            .iter()
1185                            .map(|v| {
1186                                if *v == Value::Null {
1187                                    Ok(scalar)
1188                                } else {
1189                                    Ok(*v)
1190                                }
1191                            })
1192                            .collect::<Result<Vec<Value>>>()?
1193                    } else {
1194                        l_vec
1195                            .iter()
1196                            .map(|v| {
1197                                if scalar == Value::Null {
1198                                    Ok(*v)
1199                                } else {
1200                                    Ok(scalar)
1201                                }
1202                            })
1203                            .collect::<Result<Vec<Value>>>()?
1204                    };
1205                    Ok(heap.borrow_mut().insert_list(mapped_list))
1206                }
1207                BinaryOp::Via => {
1208                    if is_list_first {
1209                        if !scalar.is_callable() {
1210                            return Err(anyhow!(
1211                                "can't call a non-function: {}",
1212                                scalar.stringify_internal(&heap.borrow())
1213                            ));
1214                        }
1215
1216                        let list = heap.borrow_mut().insert_list(l_vec.clone());
1217
1218                        let call_result = get_built_in_function_def_by_ident("map").unwrap().call(
1219                            scalar,
1220                            vec![list, scalar],
1221                            Rc::clone(&heap),
1222                            Rc::clone(&bindings),
1223                            call_depth,
1224                        )?;
1225
1226                        let mapped_list = { call_result.as_list(&heap.borrow())?.clone() };
1227
1228                        Ok(heap.borrow_mut().insert_list(mapped_list))
1229                    } else {
1230                        Err(anyhow!("map operator requires function on right side"))
1231                    }
1232                }
1233                BinaryOp::Into => {
1234                    if is_list_first {
1235                        if !scalar.is_callable() {
1236                            return Err(anyhow!(
1237                                "can't call a non-function: {}",
1238                                scalar.stringify_internal(&heap.borrow())
1239                            ));
1240                        }
1241
1242                        let list = heap.borrow_mut().insert_list(l_vec.clone());
1243
1244                        let def = {
1245                            let borrowed_heap = &heap.borrow();
1246                            get_function_def(&scalar, borrowed_heap).ok_or_else(|| {
1247                                anyhow!(
1248                                    "unknown function: {}",
1249                                    scalar.stringify_internal(borrowed_heap)
1250                                )
1251                            })?
1252                        };
1253
1254                        def.call(
1255                            scalar,
1256                            vec![list],
1257                            Rc::clone(&heap),
1258                            Rc::clone(&bindings),
1259                            call_depth,
1260                        )
1261                    } else {
1262                        Err(anyhow!("'into' operator requires function on right side"))
1263                    }
1264                }
1265                BinaryOp::DotEqual
1266                | BinaryOp::DotNotEqual
1267                | BinaryOp::DotLess
1268                | BinaryOp::DotLessEq
1269                | BinaryOp::DotGreater
1270                | BinaryOp::DotGreaterEq => {
1271                    // Dot operators are handled before this match, so this should never be reached
1272                    unreachable!("Dot operators should be handled before list broadcasting")
1273                }
1274            }
1275        }
1276        (lhs, rhs) => match op {
1277            BinaryOp::Equal => Ok(Bool(lhs.equals(&rhs, &heap.borrow())?)),
1278            BinaryOp::NotEqual => Ok(Bool(!lhs.equals(&rhs, &heap.borrow())?)),
1279            BinaryOp::Less => match lhs.compare(&rhs, &heap.borrow())? {
1280                Some(std::cmp::Ordering::Less) => Ok(Bool(true)),
1281                _ => Ok(Bool(false)),
1282            },
1283            BinaryOp::LessEq => match lhs.compare(&rhs, &heap.borrow())? {
1284                Some(std::cmp::Ordering::Less) | Some(std::cmp::Ordering::Equal) => Ok(Bool(true)),
1285                _ => Ok(Bool(false)),
1286            },
1287            BinaryOp::Greater => match lhs.compare(&rhs, &heap.borrow())? {
1288                Some(std::cmp::Ordering::Greater) => Ok(Bool(true)),
1289                _ => Ok(Bool(false)),
1290            },
1291            BinaryOp::GreaterEq => match lhs.compare(&rhs, &heap.borrow())? {
1292                Some(std::cmp::Ordering::Greater) | Some(std::cmp::Ordering::Equal) => {
1293                    Ok(Bool(true))
1294                }
1295                _ => Ok(Bool(false)),
1296            },
1297            BinaryOp::And | BinaryOp::NaturalAnd => Ok(Bool(lhs.as_bool()? && rhs.as_bool()?)),
1298            BinaryOp::Or | BinaryOp::NaturalOr => Ok(Bool(lhs.as_bool()? || rhs.as_bool()?)),
1299            BinaryOp::Add => {
1300                if lhs.is_string() {
1301                    let (l_str, r_str) = {
1302                        (
1303                            lhs.as_string(&heap.borrow())?.to_string(),
1304                            rhs.as_string(&heap.borrow())?.to_string(),
1305                        )
1306                    };
1307
1308                    return Ok(heap
1309                        .borrow_mut()
1310                        .insert_string(format!("{}{}", l_str, r_str)));
1311                }
1312
1313                Ok(Number(lhs.as_number()? + rhs.as_number()?))
1314            }
1315            BinaryOp::Subtract => Ok(Number(lhs.as_number()? - rhs.as_number()?)),
1316            BinaryOp::Multiply => Ok(Number(lhs.as_number()? * rhs.as_number()?)),
1317            BinaryOp::Divide => Ok(Number(lhs.as_number()? / rhs.as_number()?)),
1318            BinaryOp::Modulo => Ok(Number(lhs.as_number()? % rhs.as_number()?)),
1319            BinaryOp::Power => Ok(Number(lhs.as_number()?.powf(rhs.as_number()?))),
1320            BinaryOp::Coalesce => {
1321                if lhs == Value::Null {
1322                    Ok(rhs)
1323                } else {
1324                    Ok(lhs)
1325                }
1326            }
1327            BinaryOp::Via => {
1328                if !rhs.is_callable() {
1329                    return Err(anyhow!(
1330                        "can't call a non-function ({} is of type {})",
1331                        rhs.stringify_internal(&heap.borrow()),
1332                        rhs.get_type()
1333                    ));
1334                }
1335
1336                let def = { get_function_def(&rhs, &heap.borrow()) };
1337
1338                if def.is_none() {
1339                    return Err(anyhow!(
1340                        "unknown function: {}",
1341                        rhs.stringify_internal(&heap.borrow())
1342                    ));
1343                }
1344
1345                def.unwrap().call(
1346                    rhs,
1347                    vec![lhs],
1348                    Rc::clone(&heap),
1349                    Rc::clone(&bindings),
1350                    call_depth,
1351                )
1352            }
1353            BinaryOp::Into => {
1354                if !rhs.is_callable() {
1355                    return Err(anyhow!(
1356                        "can't call a non-function ({} is of type {})",
1357                        rhs.stringify_internal(&heap.borrow()),
1358                        rhs.get_type()
1359                    ));
1360                }
1361
1362                let def = { get_function_def(&rhs, &heap.borrow()) };
1363
1364                if def.is_none() {
1365                    return Err(anyhow!(
1366                        "unknown function: {}",
1367                        rhs.stringify_internal(&heap.borrow())
1368                    ));
1369                }
1370
1371                def.unwrap().call(
1372                    rhs,
1373                    vec![lhs],
1374                    Rc::clone(&heap),
1375                    Rc::clone(&bindings),
1376                    call_depth,
1377                )
1378            }
1379            BinaryOp::DotEqual
1380            | BinaryOp::DotNotEqual
1381            | BinaryOp::DotLess
1382            | BinaryOp::DotLessEq
1383            | BinaryOp::DotGreater
1384            | BinaryOp::DotGreaterEq => {
1385                // Dot operators are handled before this match, so this should never be reached
1386                unreachable!("Dot operators should be handled before generic value matching")
1387            }
1388        },
1389    }
1390}
1391
1392// Convert Pest pairs to our AST
1393pub fn pairs_to_expr(pairs: Pairs<Rule>) -> Result<Expr> {
1394    PRATT
1395        .map_primary(|primary| match primary.as_rule() {
1396            Rule::number => Ok(Expr::Number(
1397                primary
1398                    .as_str()
1399                    .replace("_", "")
1400                    .parse::<f64>()
1401                    .map_err(anyhow::Error::from)?,
1402            )),
1403            Rule::list => {
1404                let list_pairs = primary.into_inner();
1405                let exprs = list_pairs
1406                    .into_iter()
1407                    .map(|pair| pairs_to_expr(pair.into_inner()))
1408                    .collect::<Result<Vec<Expr>>>()?;
1409                Ok(Expr::List(exprs))
1410            }
1411            Rule::record => {
1412                let record_pairs = primary.into_inner();
1413                let mut entries = Vec::new();
1414
1415                for pair in record_pairs {
1416                    match pair.as_rule() {
1417                        Rule::record_pair => {
1418                            let mut inner_pairs = pair.into_inner();
1419                            let key_pair = inner_pairs.next().unwrap();
1420                            let key = match key_pair.as_rule() {
1421                                Rule::record_key_static => {
1422                                    let inner_key_pair = key_pair.into_inner().next().unwrap();
1423                                    match inner_key_pair.as_rule() {
1424                                        Rule::identifier => {
1425                                            RecordKey::Static(inner_key_pair.as_str().to_string())
1426                                        }
1427                                        Rule::string => RecordKey::Static(
1428                                            inner_key_pair.into_inner().as_str().to_string(),
1429                                        ),
1430                                        _ => unreachable!(),
1431                                    }
1432                                }
1433                                Rule::record_key_dynamic => RecordKey::Dynamic(Box::new(
1434                                    pairs_to_expr(key_pair.into_inner())?,
1435                                )),
1436                                _ => unreachable!(),
1437                            };
1438
1439                            let value = pairs_to_expr(inner_pairs.next().unwrap().into_inner())?;
1440                            entries.push(RecordEntry { key, value });
1441                        }
1442                        Rule::record_shorthand => {
1443                            let ident = pair.into_inner().next().unwrap().as_str().to_string();
1444                            entries.push(RecordEntry {
1445                                key: RecordKey::Shorthand(ident),
1446                                value: Expr::Null, // Will be resolved during evaluation
1447                            });
1448                        }
1449                        Rule::spread_expression => {
1450                            let spread_expr = pairs_to_expr(pair.into_inner())?;
1451                            entries.push(RecordEntry {
1452                                key: RecordKey::Spread(Box::new(spread_expr)),
1453                                value: Expr::Null, // Will be resolved during evaluation
1454                            });
1455                        }
1456                        _ => {}
1457                    }
1458                }
1459
1460                Ok(Expr::Record(entries))
1461            }
1462            Rule::bool => {
1463                let bool_str = primary.as_str();
1464                match bool_str {
1465                    "true" => Ok(Expr::Bool(true)),
1466                    "false" => Ok(Expr::Bool(false)),
1467                    _ => unreachable!(),
1468                }
1469            }
1470            Rule::null => Ok(Expr::Null),
1471            Rule::string => {
1472                let contents = primary.into_inner().as_str().to_string();
1473                Ok(Expr::String(contents))
1474            }
1475            Rule::assignment => {
1476                let mut inner_pairs = primary.into_inner();
1477                let ident = inner_pairs.next().unwrap().as_str().to_string();
1478                let value = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1479                Ok(Expr::Assignment { ident, value })
1480            }
1481            Rule::lambda => {
1482                let mut inner_pairs = primary.into_inner();
1483                let arg_list = inner_pairs.next().unwrap();
1484                let body_pairs = inner_pairs.next().unwrap();
1485
1486                let mut args = Vec::new();
1487                for arg_pair in arg_list.into_inner() {
1488                    match arg_pair.as_rule() {
1489                        Rule::required_arg => {
1490                            args.push(LambdaArg::Required(
1491                                arg_pair.into_inner().as_str().to_string(),
1492                            ));
1493                        }
1494                        Rule::optional_arg => {
1495                            args.push(LambdaArg::Optional(
1496                                arg_pair.into_inner().as_str().to_string(),
1497                            ));
1498                        }
1499                        Rule::rest_arg => {
1500                            args.push(LambdaArg::Rest(arg_pair.into_inner().as_str().to_string()));
1501                        }
1502                        _ => {}
1503                    }
1504                }
1505
1506                let body = Box::new(pairs_to_expr(body_pairs.into_inner())?);
1507                Ok(Expr::Lambda { args, body })
1508            }
1509            Rule::conditional => {
1510                let mut inner_pairs = primary.into_inner();
1511                let condition = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1512                let then_expr = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1513                let else_expr = Box::new(pairs_to_expr(inner_pairs.next().unwrap().into_inner())?);
1514                Ok(Expr::Conditional {
1515                    condition,
1516                    then_expr,
1517                    else_expr,
1518                })
1519            }
1520            Rule::do_block => {
1521                let inner_pairs = primary.into_inner();
1522                let mut statements = Vec::new();
1523                let mut return_expr = Box::new(Expr::Null);
1524
1525                for pair in inner_pairs {
1526                    match pair.as_rule() {
1527                        Rule::do_statement => {
1528                            if let Some(inner) = pair.into_inner().next() {
1529                                if inner.as_rule() == Rule::expression {
1530                                    statements.push(DoStatement::Expression(pairs_to_expr(
1531                                        inner.into_inner(),
1532                                    )?));
1533                                } else if inner.as_rule() == Rule::comment {
1534                                    statements
1535                                        .push(DoStatement::Comment(inner.as_str().to_string()));
1536                                }
1537                            }
1538                        }
1539                        Rule::return_statement => {
1540                            let return_expr_pair = pair.into_inner().next().unwrap();
1541                            return_expr = Box::new(pairs_to_expr(return_expr_pair.into_inner())?);
1542                        }
1543                        _ => {}
1544                    }
1545                }
1546
1547                Ok(Expr::DoBlock {
1548                    statements,
1549                    return_expr,
1550                })
1551            }
1552            Rule::identifier => {
1553                let ident = primary.as_str();
1554
1555                // Check if it's a built-in function
1556                if let Some(built_in) = BuiltInFunction::from_ident(ident) {
1557                    Ok(Expr::BuiltIn(built_in))
1558                } else {
1559                    Ok(Expr::Identifier(ident.to_string()))
1560                }
1561            }
1562            Rule::expression => pairs_to_expr(primary.into_inner()),
1563            _ => unreachable!("{}", primary.as_str()),
1564        })
1565        .map_prefix(|op, rhs| match op.as_rule() {
1566            Rule::negation => Ok(Expr::UnaryOp {
1567                op: UnaryOp::Negate,
1568                expr: Box::new(rhs?),
1569            }),
1570            Rule::spread_operator => Ok(Expr::Spread(Box::new(rhs?))),
1571            Rule::invert | Rule::natural_not => Ok(Expr::UnaryOp {
1572                op: UnaryOp::Not,
1573                expr: Box::new(rhs?),
1574            }),
1575            _ => unreachable!(),
1576        })
1577        .map_postfix(|lhs, op| match op.as_rule() {
1578            Rule::factorial => Ok(Expr::PostfixOp {
1579                op: PostfixOp::Factorial,
1580                expr: Box::new(lhs?),
1581            }),
1582            Rule::access => {
1583                let index_expr = pairs_to_expr(op.into_inner())?;
1584                Ok(Expr::Access {
1585                    expr: Box::new(lhs?),
1586                    index: Box::new(index_expr),
1587                })
1588            }
1589            Rule::dot_access => {
1590                let field = op.into_inner().as_str().to_string();
1591                Ok(Expr::DotAccess {
1592                    expr: Box::new(lhs?),
1593                    field,
1594                })
1595            }
1596            Rule::call_list => {
1597                let call_list = op.into_inner();
1598                let args = call_list
1599                    .into_iter()
1600                    .map(|arg| pairs_to_expr(arg.into_inner()))
1601                    .collect::<Result<Vec<Expr>>>()?;
1602                Ok(Expr::Call {
1603                    func: Box::new(lhs?),
1604                    args,
1605                })
1606            }
1607            _ => unreachable!(),
1608        })
1609        .map_infix(|lhs, op, rhs| {
1610            let op = match op.as_rule() {
1611                Rule::add => BinaryOp::Add,
1612                Rule::subtract => BinaryOp::Subtract,
1613                Rule::multiply => BinaryOp::Multiply,
1614                Rule::divide => BinaryOp::Divide,
1615                Rule::modulo => BinaryOp::Modulo,
1616                Rule::power => BinaryOp::Power,
1617                Rule::equal => BinaryOp::Equal,
1618                Rule::not_equal => BinaryOp::NotEqual,
1619                Rule::less => BinaryOp::Less,
1620                Rule::less_eq => BinaryOp::LessEq,
1621                Rule::greater => BinaryOp::Greater,
1622                Rule::greater_eq => BinaryOp::GreaterEq,
1623                Rule::dot_equal => BinaryOp::DotEqual,
1624                Rule::dot_not_equal => BinaryOp::DotNotEqual,
1625                Rule::dot_less => BinaryOp::DotLess,
1626                Rule::dot_less_eq => BinaryOp::DotLessEq,
1627                Rule::dot_greater => BinaryOp::DotGreater,
1628                Rule::dot_greater_eq => BinaryOp::DotGreaterEq,
1629                Rule::and => BinaryOp::And,
1630                Rule::natural_and => BinaryOp::NaturalAnd,
1631                Rule::or => BinaryOp::Or,
1632                Rule::natural_or => BinaryOp::NaturalOr,
1633                Rule::via => BinaryOp::Via,
1634                Rule::into => BinaryOp::Into,
1635                Rule::coalesce => BinaryOp::Coalesce,
1636                _ => unreachable!(),
1637            };
1638            Ok(Expr::BinaryOp {
1639                op,
1640                left: Box::new(lhs?),
1641                right: Box::new(rhs?),
1642            })
1643        })
1644        .parse(pairs)
1645}
1646
1647#[cfg(test)]
1648mod tests {
1649    use super::*;
1650    use crate::{heap::LambdaPointer, parser::get_pairs};
1651
1652    fn parse_and_evaluate(
1653        input: &str,
1654        heap: Option<Rc<RefCell<Heap>>>,
1655        bindings: Option<Rc<RefCell<HashMap<String, Value>>>>,
1656    ) -> Result<Value> {
1657        let binding = input.to_string();
1658        let mut pairs = get_pairs(&binding).unwrap();
1659        let expr = pairs.next().unwrap().into_inner();
1660        evaluate_pairs(
1661            expr,
1662            heap.unwrap_or(Rc::new(RefCell::new(Heap::new()))),
1663            bindings.unwrap_or(Rc::new(RefCell::new(HashMap::new()))),
1664            0,
1665        )
1666    }
1667
1668    #[test]
1669    fn addition_of_integers() {
1670        let result = parse_and_evaluate("5 + 2", None, None).unwrap();
1671        assert_eq!(result, Value::Number(7.0));
1672    }
1673
1674    #[test]
1675    fn exponentiation_of_two_integers() {
1676        let result = parse_and_evaluate("2 ^ 3", None, None).unwrap();
1677        assert_eq!(result, Value::Number(8.0));
1678    }
1679
1680    #[test]
1681    fn multiplication_of_integers() {
1682        let result = parse_and_evaluate("8 * 4", None, None).unwrap();
1683        assert_eq!(result, Value::Number(32.0));
1684    }
1685
1686    #[test]
1687    fn division_with_integer_resulting_in_decimal() {
1688        let result = parse_and_evaluate("9 / 2", None, None).unwrap();
1689        assert_eq!(result, Value::Number(4.5));
1690    }
1691
1692    #[test]
1693    fn addition_with_nested_expression() {
1694        let result = parse_and_evaluate("5 + (2 * 4)", None, None).unwrap();
1695        assert_eq!(result, Value::Number(13.0));
1696    }
1697
1698    #[test]
1699    fn grouping_and_multiplication_in_expression() {
1700        let result = parse_and_evaluate("(3 + 2) * 2", None, None).unwrap();
1701        assert_eq!(result, Value::Number(10.0));
1702    }
1703
1704    #[test]
1705    fn mixed_operations_with_decimal_and_precedence() {
1706        let result = parse_and_evaluate("6.5 / 2 + 4 * 2", None, None).unwrap();
1707        assert_eq!(result, Value::Number(11.25));
1708    }
1709
1710    #[test]
1711    fn exponentiation_with_nested_expression() {
1712        let result = parse_and_evaluate("2 ^ (1 + 2)", None, None).unwrap();
1713        assert_eq!(result, Value::Number(8.0));
1714    }
1715
1716    #[test]
1717    fn complex_expression_with_decimals() {
1718        let result = parse_and_evaluate("7.5 - 3.25 + 2 * (8 / 4)", None, None).unwrap();
1719        assert_eq!(result, Value::Number(8.25));
1720    }
1721
1722    #[test]
1723    fn subtraction_with_decimal_result() {
1724        let result = parse_and_evaluate("10.75 - 3.5", None, None).unwrap();
1725        assert_eq!(result, Value::Number(7.25));
1726    }
1727
1728    #[test]
1729    fn multiplication_of_two_decimals() {
1730        let result = parse_and_evaluate("3.5 * 2.0", None, None).unwrap();
1731        assert_eq!(result, Value::Number(7.0));
1732    }
1733
1734    #[test]
1735    fn division_of_two_decimals() {
1736        let result = parse_and_evaluate("7.5 / 2.5", None, None).unwrap();
1737        assert_eq!(result, Value::Number(3.0));
1738    }
1739
1740    #[test]
1741    fn boolean_and() {
1742        let result = parse_and_evaluate("true and false", None, None).unwrap();
1743        assert_eq!(result, Value::Bool(false));
1744    }
1745
1746    #[test]
1747    fn boolean_and_alt() {
1748        let result = parse_and_evaluate("true && false", None, None).unwrap();
1749        assert_eq!(result, Value::Bool(false));
1750    }
1751
1752    #[test]
1753    fn boolean_or() {
1754        let result = parse_and_evaluate("true or false", None, None).unwrap();
1755        assert_eq!(result, Value::Bool(true));
1756    }
1757
1758    #[test]
1759    fn boolean_or_alt() {
1760        let result = parse_and_evaluate("true || false", None, None).unwrap();
1761        assert_eq!(result, Value::Bool(true));
1762    }
1763
1764    #[test]
1765    fn boolean_and_with_nested_expression() {
1766        let result = parse_and_evaluate("true and (false or true)", None, None).unwrap();
1767        assert_eq!(result, Value::Bool(true));
1768    }
1769
1770    #[test]
1771    fn boolean_and_with_nested_expression_alt() {
1772        let result = parse_and_evaluate("true && (false || true)", None, None).unwrap();
1773        assert_eq!(result, Value::Bool(true));
1774    }
1775
1776    #[test]
1777    fn boolean_or_with_nested_expression() {
1778        let result = parse_and_evaluate("true or (false and true)", None, None).unwrap();
1779        assert_eq!(result, Value::Bool(true));
1780    }
1781
1782    #[test]
1783    fn boolean_or_with_nested_expression_alt() {
1784        let result = parse_and_evaluate("true || (false && true)", None, None).unwrap();
1785        assert_eq!(result, Value::Bool(true));
1786    }
1787
1788    #[test]
1789    fn logical_not() {
1790        let result = parse_and_evaluate("!true", None, None).unwrap();
1791        assert_eq!(result, Value::Bool(false));
1792    }
1793
1794    #[test]
1795    fn logical_not_with_nested_expression() {
1796        let result = parse_and_evaluate("!(true and false)", None, None).unwrap();
1797        assert_eq!(result, Value::Bool(true));
1798    }
1799
1800    #[test]
1801    fn equality_of_two_integers() {
1802        let result = parse_and_evaluate("5 == 5", None, None).unwrap();
1803        assert_eq!(result, Value::Bool(true));
1804    }
1805
1806    #[test]
1807    fn inequality_of_two_integers() {
1808        let result = parse_and_evaluate("5 != 5", None, None).unwrap();
1809        assert_eq!(result, Value::Bool(false));
1810    }
1811
1812    #[test]
1813    fn less_than_comparison() {
1814        let result = parse_and_evaluate("5 < 10", None, None).unwrap();
1815        assert_eq!(result, Value::Bool(true));
1816    }
1817
1818    #[test]
1819    fn less_than_or_equal_comparison() {
1820        let result = parse_and_evaluate("5 <= 5", None, None).unwrap();
1821        assert_eq!(result, Value::Bool(true));
1822    }
1823
1824    #[test]
1825    fn greater_than_comparison() {
1826        let result = parse_and_evaluate("10 > 5", None, None).unwrap();
1827        assert_eq!(result, Value::Bool(true));
1828    }
1829
1830    #[test]
1831    fn greater_than_or_equal_comparison() {
1832        let result = parse_and_evaluate("5 >= 5", None, None).unwrap();
1833        assert_eq!(result, Value::Bool(true));
1834    }
1835
1836    #[test]
1837    fn equality_of_two_lists() {
1838        // List == List performs element-wise comparison
1839        let heap = Rc::new(RefCell::new(Heap::new()));
1840        let result =
1841            parse_and_evaluate("[1, 2, 3] == [1, 2, 3]", Some(heap.clone()), None).unwrap();
1842        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
1843        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
1844    }
1845
1846    #[test]
1847    fn inequality_of_two_lists() {
1848        // List != List performs element-wise comparison
1849        let heap = Rc::new(RefCell::new(Heap::new()));
1850        let result =
1851            parse_and_evaluate("[1, 2, 3] != [2, 3, 4]", Some(heap.clone()), None).unwrap();
1852        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
1853        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
1854    }
1855
1856    #[test]
1857    fn conditional_expression_with_true_condition() {
1858        let result = parse_and_evaluate("if true then 5 else 10", None, None).unwrap();
1859        assert_eq!(result, Value::Number(5.0));
1860    }
1861
1862    #[test]
1863    fn conditional_expression_with_false_condition() {
1864        let result = parse_and_evaluate("if false then 5 else 10", None, None).unwrap();
1865        assert_eq!(result, Value::Number(10.0));
1866    }
1867
1868    #[test]
1869    fn factorial_of_integer() {
1870        let result = parse_and_evaluate("5!", None, None).unwrap();
1871        assert_eq!(result, Value::Number(120.0));
1872    }
1873
1874    #[test]
1875    fn factorial_of_zero() {
1876        let result = parse_and_evaluate("0!", None, None).unwrap();
1877        assert_eq!(result, Value::Number(1.0));
1878    }
1879
1880    #[test]
1881    fn factorial_of_negative_integer() {
1882        let result = parse_and_evaluate("(-5)!", None, None);
1883        assert!(result.is_err());
1884    }
1885
1886    #[test]
1887    fn factorial_of_decimal() {
1888        let result = parse_and_evaluate("5.5!", None, None);
1889        assert!(result.is_err());
1890    }
1891
1892    #[test]
1893    fn string_concatenation() {
1894        let heap = Rc::new(RefCell::new(Heap::new()));
1895        let result =
1896            parse_and_evaluate("\"hello\" + \"world\"", Some(Rc::clone(&heap)), None).unwrap();
1897
1898        assert!(matches!(result, Value::String(_)));
1899        assert_eq!(result.as_string(&heap.borrow()).unwrap(), "helloworld");
1900    }
1901
1902    #[test]
1903    fn string_concatenation_with_integer() {
1904        let result = parse_and_evaluate("\"hello\" + 5", None, None);
1905        assert!(result.is_err());
1906    }
1907
1908    #[test]
1909    fn variable_assignment() {
1910        let bindings = Rc::new(RefCell::new(HashMap::new()));
1911        let result = parse_and_evaluate("x = 5", None, Some(Rc::clone(&bindings))).unwrap();
1912
1913        assert_eq!(result, Value::Number(5.0));
1914        assert_eq!(bindings.borrow().get("x").unwrap(), &Value::Number(5.0));
1915    }
1916
1917    #[test]
1918    fn variable_assignment_with_expression() {
1919        let bindings = Rc::new(RefCell::new(HashMap::new()));
1920        let result = parse_and_evaluate("x = 5 + 2", None, Some(Rc::clone(&bindings))).unwrap();
1921        assert_eq!(result, Value::Number(7.0));
1922        assert_eq!(bindings.borrow().get("x").unwrap(), &Value::Number(7.0));
1923    }
1924
1925    #[test]
1926    fn variable_assignment_with_lambda() {
1927        let heap = Rc::new(RefCell::new(Heap::new()));
1928        let bindings = Rc::new(RefCell::new(HashMap::new()));
1929        let result = parse_and_evaluate(
1930            "f = x => x + 1",
1931            Some(Rc::clone(&heap)),
1932            Some(Rc::clone(&bindings)),
1933        )
1934        .unwrap();
1935
1936        {
1937            let heap_borrow = heap.borrow();
1938            let lambda_def = result.as_lambda(&heap_borrow).unwrap();
1939            assert_eq!(lambda_def.name, Some("f".to_string()));
1940            assert_eq!(lambda_def.args, vec![LambdaArg::Required("x".to_string())]);
1941            assert_eq!(lambda_def.scope, HashMap::new());
1942            // The body should be a BinaryOp(Add) with Identifier("x") and Number(1)
1943            match &lambda_def.body {
1944                Expr::BinaryOp {
1945                    op: BinaryOp::Add,
1946                    left,
1947                    right,
1948                } => match (left.as_ref(), right.as_ref()) {
1949                    (Expr::Identifier(x), Expr::Number(n)) => {
1950                        assert_eq!(x, "x");
1951                        assert_eq!(*n, 1.0);
1952                    }
1953                    _ => panic!("Expected Identifier('x') + Number(1)"),
1954                },
1955                _ => panic!("Expected BinaryOp(Add)"),
1956            }
1957        }
1958        assert_eq!(
1959            bindings.borrow().get("f").unwrap(),
1960            &Value::Lambda(LambdaPointer::new(1))
1961        );
1962    }
1963
1964    #[test]
1965    fn variable_assignment_with_lambda_and_call() {
1966        let heap = Rc::new(RefCell::new(Heap::new()));
1967        let bindings = Rc::new(RefCell::new(HashMap::new()));
1968        let _ = parse_and_evaluate(
1969            "f = x => x + 1",
1970            Some(Rc::clone(&heap)),
1971            Some(Rc::clone(&bindings)),
1972        )
1973        .unwrap();
1974        let result =
1975            parse_and_evaluate("f(5)", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings))).unwrap();
1976
1977        assert_eq!(result, Value::Number(6.0));
1978    }
1979
1980    #[test]
1981    fn variable_assignment_with_lambda_and_call_with_multiple_args() {
1982        let heap = Rc::new(RefCell::new(Heap::new()));
1983        let bindings = Rc::new(RefCell::new(HashMap::new()));
1984        let _ = parse_and_evaluate(
1985            "f = (x, y) => x + y",
1986            Some(Rc::clone(&heap)),
1987            Some(Rc::clone(&bindings)),
1988        )
1989        .unwrap();
1990        let result = parse_and_evaluate(
1991            "f(5, 2)",
1992            Some(Rc::clone(&heap)),
1993            Some(Rc::clone(&bindings)),
1994        )
1995        .unwrap();
1996
1997        assert_eq!(result, Value::Number(7.0));
1998    }
1999
2000    #[test]
2001    fn variable_assignment_with_lambda_and_call_with_multiple_args_and_expression() {
2002        let heap = Rc::new(RefCell::new(Heap::new()));
2003        let bindings = Rc::new(RefCell::new(HashMap::new()));
2004        let _ = parse_and_evaluate(
2005            "f = (x, y) => x + y",
2006            Some(Rc::clone(&heap)),
2007            Some(Rc::clone(&bindings)),
2008        )
2009        .unwrap();
2010        let result = parse_and_evaluate(
2011            "f(5, 2) + 3",
2012            Some(Rc::clone(&heap)),
2013            Some(Rc::clone(&bindings)),
2014        )
2015        .unwrap();
2016
2017        assert_eq!(result, Value::Number(10.0));
2018    }
2019
2020    #[test]
2021    fn coalesce_operator_with_null() {
2022        let result = parse_and_evaluate("null ?? 5", None, None).unwrap();
2023        assert_eq!(result, Value::Number(5.0));
2024    }
2025
2026    #[test]
2027    fn coalesce_operator_with_non_null() {
2028        let result = parse_and_evaluate("3 ?? 10", None, None).unwrap();
2029        assert_eq!(result, Value::Number(3.0));
2030    }
2031
2032    // Element-wise operations tests
2033    #[test]
2034    fn list_elementwise_addition() {
2035        let heap = Rc::new(RefCell::new(Heap::new()));
2036        let result =
2037            parse_and_evaluate("[1, 2, 3] + [4, 5, 6]", Some(Rc::clone(&heap)), None).unwrap();
2038        let expected = vec![Value::Number(5.0), Value::Number(7.0), Value::Number(9.0)];
2039        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2040    }
2041
2042    #[test]
2043    fn list_elementwise_subtraction() {
2044        let heap = Rc::new(RefCell::new(Heap::new()));
2045        let result =
2046            parse_and_evaluate("[5, 7, 9] - [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
2047        let expected = vec![Value::Number(4.0), Value::Number(5.0), Value::Number(6.0)];
2048        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2049    }
2050
2051    #[test]
2052    fn list_elementwise_multiplication() {
2053        let heap = Rc::new(RefCell::new(Heap::new()));
2054        let result =
2055            parse_and_evaluate("[2, 3, 4] * [5, 6, 7]", Some(Rc::clone(&heap)), None).unwrap();
2056        let expected = vec![
2057            Value::Number(10.0),
2058            Value::Number(18.0),
2059            Value::Number(28.0),
2060        ];
2061        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2062    }
2063
2064    #[test]
2065    fn list_elementwise_division() {
2066        let heap = Rc::new(RefCell::new(Heap::new()));
2067        let result =
2068            parse_and_evaluate("[10, 20, 30] / [2, 4, 5]", Some(Rc::clone(&heap)), None).unwrap();
2069        let expected = vec![Value::Number(5.0), Value::Number(5.0), Value::Number(6.0)];
2070        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2071    }
2072
2073    #[test]
2074    fn list_elementwise_power() {
2075        let heap = Rc::new(RefCell::new(Heap::new()));
2076        let result =
2077            parse_and_evaluate("[2, 3, 4] ^ [2, 2, 2]", Some(Rc::clone(&heap)), None).unwrap();
2078        let expected = vec![Value::Number(4.0), Value::Number(9.0), Value::Number(16.0)];
2079        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2080    }
2081
2082    #[test]
2083    fn list_elementwise_modulo() {
2084        let heap = Rc::new(RefCell::new(Heap::new()));
2085        let result =
2086            parse_and_evaluate("[10, 11, 12] % [3, 3, 3]", Some(Rc::clone(&heap)), None).unwrap();
2087        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(0.0)];
2088        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2089    }
2090
2091    #[test]
2092    fn list_elementwise_different_lengths_error() {
2093        let result = parse_and_evaluate("[1, 2, 3] + [4, 5]", None, None);
2094        assert!(result.is_err());
2095        assert!(
2096            result
2097                .unwrap_err()
2098                .to_string()
2099                .contains("left- and right-hand-side lists must be the same length")
2100        );
2101    }
2102
2103    // Broadcast operations tests
2104    #[test]
2105    fn list_scalar_addition() {
2106        let heap = Rc::new(RefCell::new(Heap::new()));
2107        let result = parse_and_evaluate("[1, 2, 3] + 10", Some(Rc::clone(&heap)), None).unwrap();
2108        let expected = vec![
2109            Value::Number(11.0),
2110            Value::Number(12.0),
2111            Value::Number(13.0),
2112        ];
2113        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2114    }
2115
2116    #[test]
2117    fn list_scalar_subtraction() {
2118        let heap = Rc::new(RefCell::new(Heap::new()));
2119        let result = parse_and_evaluate("[10, 20, 30] - 5", Some(Rc::clone(&heap)), None).unwrap();
2120        let expected = vec![Value::Number(5.0), Value::Number(15.0), Value::Number(25.0)];
2121        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2122    }
2123
2124    #[test]
2125    fn list_scalar_multiplication() {
2126        let heap = Rc::new(RefCell::new(Heap::new()));
2127        let result = parse_and_evaluate("[2, 3, 4] * 2", Some(Rc::clone(&heap)), None).unwrap();
2128        let expected = vec![Value::Number(4.0), Value::Number(6.0), Value::Number(8.0)];
2129        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2130    }
2131
2132    #[test]
2133    fn list_scalar_division() {
2134        let heap = Rc::new(RefCell::new(Heap::new()));
2135        let result = parse_and_evaluate("[10, 20, 30] / 10", Some(Rc::clone(&heap)), None).unwrap();
2136        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(3.0)];
2137        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2138    }
2139
2140    #[test]
2141    fn list_scalar_power() {
2142        let heap = Rc::new(RefCell::new(Heap::new()));
2143        let result = parse_and_evaluate("[2, 3, 4] ^ 2", Some(Rc::clone(&heap)), None).unwrap();
2144        let expected = vec![Value::Number(4.0), Value::Number(9.0), Value::Number(16.0)];
2145        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2146    }
2147
2148    #[test]
2149    fn list_scalar_modulo() {
2150        let heap = Rc::new(RefCell::new(Heap::new()));
2151        let result = parse_and_evaluate("[10, 11, 12] % 3", Some(Rc::clone(&heap)), None).unwrap();
2152        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(0.0)];
2153        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2154    }
2155
2156    #[test]
2157    fn list_with_operator() {
2158        let heap = Rc::new(RefCell::new(Heap::new()));
2159        let result =
2160            parse_and_evaluate("[1, 2, 3] via (x => x * x)", Some(Rc::clone(&heap)), None).unwrap();
2161        let expected = vec![Value::Number(1.0), Value::Number(4.0), Value::Number(9.0)];
2162        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2163    }
2164
2165    #[test]
2166    fn list_with_builtin_function() {
2167        let heap = Rc::new(RefCell::new(Heap::new()));
2168        let result =
2169            parse_and_evaluate("[4, 9, 16] via sqrt", Some(Rc::clone(&heap)), None).unwrap();
2170        let expected = vec![Value::Number(2.0), Value::Number(3.0), Value::Number(4.0)];
2171        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2172    }
2173
2174    #[test]
2175    fn into_operator_with_list() {
2176        let heap = Rc::new(RefCell::new(Heap::new()));
2177        let result =
2178            parse_and_evaluate("['hello', 'world'] into head", Some(Rc::clone(&heap)), None)
2179                .unwrap();
2180        assert_eq!(result.as_string(&heap.borrow()).unwrap(), "hello");
2181    }
2182
2183    #[test]
2184    fn into_operator_with_string() {
2185        let heap = Rc::new(RefCell::new(Heap::new()));
2186        let result = parse_and_evaluate("'hello' into head", Some(Rc::clone(&heap)), None).unwrap();
2187        assert_eq!(result.as_string(&heap.borrow()).unwrap(), "h");
2188    }
2189
2190    #[test]
2191    fn into_vs_via_difference() {
2192        let heap = Rc::new(RefCell::new(Heap::new()));
2193
2194        // via should apply to each element
2195        let via_result =
2196            parse_and_evaluate("['hello', 'world'] via head", Some(Rc::clone(&heap)), None)
2197                .unwrap();
2198        let borrowed_heap = heap.borrow();
2199        let via_list = via_result.as_list(&borrowed_heap).unwrap();
2200        assert_eq!(via_list.len(), 2);
2201        assert_eq!(via_list[0].as_string(&borrowed_heap).unwrap(), "h");
2202        assert_eq!(via_list[1].as_string(&borrowed_heap).unwrap(), "w");
2203        drop(borrowed_heap);
2204
2205        // into should apply to the whole list
2206        let into_result =
2207            parse_and_evaluate("['hello', 'world'] into head", Some(Rc::clone(&heap)), None)
2208                .unwrap();
2209        assert_eq!(into_result.as_string(&heap.borrow()).unwrap(), "hello");
2210    }
2211
2212    #[test]
2213    fn into_operator_with_aggregation() {
2214        let heap = Rc::new(RefCell::new(Heap::new()));
2215        let result =
2216            parse_and_evaluate("[1, 2, 3, 4, 5] into sum", Some(Rc::clone(&heap)), None).unwrap();
2217        assert_eq!(result.as_number().unwrap(), 15.0);
2218    }
2219
2220    #[test]
2221    fn into_operator_rejects_lists() {
2222        let heap = Rc::new(RefCell::new(Heap::new()));
2223
2224        // Test with multiple functions in list
2225        let result = parse_and_evaluate(
2226            "[1, 2, 3] into [sqrt, sqrt, sqrt]",
2227            Some(Rc::clone(&heap)),
2228            None,
2229        );
2230        assert!(result.is_err());
2231        assert!(
2232            result
2233                .unwrap_err()
2234                .to_string()
2235                .contains("'into' operator requires a function on the right side, not a list")
2236        );
2237
2238        // Test with single function in list
2239        let result = parse_and_evaluate("[1, 2, 3] into [sqrt]", Some(Rc::clone(&heap)), None);
2240        assert!(result.is_err());
2241        assert!(
2242            result
2243                .unwrap_err()
2244                .to_string()
2245                .contains("'into' operator requires a function on the right side, not a list")
2246        );
2247    }
2248
2249    #[test]
2250    fn list_elementwise_boolean_and() {
2251        let heap = Rc::new(RefCell::new(Heap::new()));
2252        let result = parse_and_evaluate(
2253            "[true, true, false] && [true, false, true]",
2254            Some(Rc::clone(&heap)),
2255            None,
2256        )
2257        .unwrap();
2258        let expected = vec![Value::Bool(true), Value::Bool(false), Value::Bool(false)];
2259        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2260    }
2261
2262    #[test]
2263    fn list_elementwise_boolean_or() {
2264        let heap = Rc::new(RefCell::new(Heap::new()));
2265        let result = parse_and_evaluate(
2266            "[true, false, false] || [false, true, false]",
2267            Some(Rc::clone(&heap)),
2268            None,
2269        )
2270        .unwrap();
2271        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(false)];
2272        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2273    }
2274
2275    #[test]
2276    fn list_elementwise_coalesce() {
2277        let heap = Rc::new(RefCell::new(Heap::new()));
2278        let result = parse_and_evaluate(
2279            "[null, 2, null] ?? [1, null, 3]",
2280            Some(Rc::clone(&heap)),
2281            None,
2282        )
2283        .unwrap();
2284        let expected = vec![Value::Number(1.0), Value::Number(2.0), Value::Number(3.0)];
2285        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2286    }
2287
2288    #[test]
2289    fn list_scalar_coalesce() {
2290        let heap = Rc::new(RefCell::new(Heap::new()));
2291        let result =
2292            parse_and_evaluate("[null, 2, null] ?? 5", Some(Rc::clone(&heap)), None).unwrap();
2293        let expected = vec![Value::Number(5.0), Value::Number(2.0), Value::Number(5.0)];
2294        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2295    }
2296
2297    #[test]
2298    fn list_elementwise_string_concat() {
2299        let heap = Rc::new(RefCell::new(Heap::new()));
2300        let result = parse_and_evaluate(
2301            "[\"a\", \"b\", \"c\"] + [\"1\", \"2\", \"3\"]",
2302            Some(Rc::clone(&heap)),
2303            None,
2304        )
2305        .unwrap();
2306        let borrowed_heap = heap.borrow();
2307        let result_list = result.as_list(&borrowed_heap).unwrap();
2308        assert_eq!(result_list.len(), 3);
2309        assert_eq!(result_list[0].as_string(&borrowed_heap).unwrap(), "a1");
2310        assert_eq!(result_list[1].as_string(&borrowed_heap).unwrap(), "b2");
2311        assert_eq!(result_list[2].as_string(&borrowed_heap).unwrap(), "c3");
2312    }
2313
2314    #[test]
2315    fn list_scalar_string_concat() {
2316        let heap = Rc::new(RefCell::new(Heap::new()));
2317        let result = parse_and_evaluate(
2318            "[\"a\", \"b\", \"c\"] + \"!\"",
2319            Some(Rc::clone(&heap)),
2320            None,
2321        )
2322        .unwrap();
2323        let borrowed_heap = heap.borrow();
2324        let result_list = result.as_list(&borrowed_heap).unwrap();
2325        assert_eq!(result_list.len(), 3);
2326        assert_eq!(result_list[0].as_string(&borrowed_heap).unwrap(), "a!");
2327        assert_eq!(result_list[1].as_string(&borrowed_heap).unwrap(), "b!");
2328        assert_eq!(result_list[2].as_string(&borrowed_heap).unwrap(), "c!");
2329    }
2330
2331    #[test]
2332    fn nested_list_operations() {
2333        let heap = Rc::new(RefCell::new(Heap::new()));
2334        let result =
2335            parse_and_evaluate("([1, 2, 3] + [4, 5, 6]) * 2", Some(Rc::clone(&heap)), None)
2336                .unwrap();
2337        let expected = vec![
2338            Value::Number(10.0),
2339            Value::Number(14.0),
2340            Value::Number(18.0),
2341        ];
2342        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2343    }
2344
2345    #[test]
2346    fn list_comparison_all_equal() {
2347        let heap = Rc::new(RefCell::new(Heap::new()));
2348        let result = parse_and_evaluate("[1, 1, 1] == 1", Some(heap.clone()), None).unwrap();
2349        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
2350        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2351    }
2352
2353    #[test]
2354    fn list_comparison_not_all_equal() {
2355        let heap = Rc::new(RefCell::new(Heap::new()));
2356        let result = parse_and_evaluate("[1, 2, 1] == 1", Some(heap.clone()), None).unwrap();
2357        let expected = vec![Value::Bool(true), Value::Bool(false), Value::Bool(true)];
2358        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2359    }
2360
2361    #[test]
2362    fn list_comparison_all_less() {
2363        let heap = Rc::new(RefCell::new(Heap::new()));
2364        let result = parse_and_evaluate("[1, 2, 3] < 5", Some(heap.clone()), None).unwrap();
2365        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
2366        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2367    }
2368
2369    #[test]
2370    fn list_comparison_not_all_less() {
2371        let heap = Rc::new(RefCell::new(Heap::new()));
2372        let result = parse_and_evaluate("[1, 6, 3] < 5", Some(heap.clone()), None).unwrap();
2373        let expected = vec![Value::Bool(true), Value::Bool(false), Value::Bool(true)];
2374        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2375    }
2376
2377    #[test]
2378    fn not_operator_basic() {
2379        let result = parse_and_evaluate("not true", None, None).unwrap();
2380        assert_eq!(result, Value::Bool(false));
2381    }
2382
2383    #[test]
2384    fn not_operator_with_false() {
2385        let result = parse_and_evaluate("not false", None, None).unwrap();
2386        assert_eq!(result, Value::Bool(true));
2387    }
2388
2389    #[test]
2390    fn not_operator_with_expression() {
2391        let result = parse_and_evaluate("not (5 > 10)", None, None).unwrap();
2392        assert_eq!(result, Value::Bool(true));
2393    }
2394
2395    #[test]
2396    fn not_operator_double_negation() {
2397        let result = parse_and_evaluate("not not true", None, None).unwrap();
2398        assert_eq!(result, Value::Bool(true));
2399    }
2400
2401    #[test]
2402    fn not_operator_with_and() {
2403        let result = parse_and_evaluate("not (true and false)", None, None).unwrap();
2404        assert_eq!(result, Value::Bool(true));
2405    }
2406
2407    #[test]
2408    fn not_operator_with_or() {
2409        let result = parse_and_evaluate("not (false or false)", None, None).unwrap();
2410        assert_eq!(result, Value::Bool(true));
2411    }
2412
2413    #[test]
2414    fn not_operator_precedence() {
2415        let result = parse_and_evaluate("not true and false", None, None).unwrap();
2416        assert_eq!(result, Value::Bool(false));
2417    }
2418
2419    #[test]
2420    fn not_operator_comparison_with_invert() {
2421        let not_result = parse_and_evaluate("not true", None, None).unwrap();
2422        let invert_result = parse_and_evaluate("!true", None, None).unwrap();
2423        assert_eq!(not_result, invert_result);
2424    }
2425
2426    #[test]
2427    fn variable_immutability_prevents_reassignment() {
2428        let heap = Rc::new(RefCell::new(Heap::new()));
2429        let bindings = Rc::new(RefCell::new(HashMap::new()));
2430
2431        // First assignment should succeed
2432        let result1 =
2433            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2434        assert!(result1.is_ok());
2435        assert_eq!(result1.unwrap(), Value::Number(5.0));
2436
2437        // Second assignment to same variable should fail
2438        let result2 =
2439            parse_and_evaluate("x = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2440        assert!(result2.is_err());
2441        assert!(
2442            result2
2443                .unwrap_err()
2444                .to_string()
2445                .contains("x is already defined, and cannot be reassigned")
2446        );
2447
2448        // Original value should remain unchanged
2449        let result3 = parse_and_evaluate("x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2450        assert!(result3.is_ok());
2451        assert_eq!(result3.unwrap(), Value::Number(5.0));
2452    }
2453
2454    #[test]
2455    fn variable_immutability_allows_shadowing_in_nested_scopes() {
2456        let heap = Rc::new(RefCell::new(Heap::new()));
2457        let bindings = Rc::new(RefCell::new(HashMap::new()));
2458
2459        // Assign x in outer scope
2460        let result1 =
2461            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2462        assert!(result1.is_ok());
2463
2464        // Shadowing in lambda should work (creates new scope)
2465        let result2 = parse_and_evaluate(
2466            "f = (x) => x * 2",
2467            Some(Rc::clone(&heap)),
2468            Some(Rc::clone(&bindings)),
2469        );
2470        assert!(result2.is_ok());
2471
2472        // Call lambda with different value
2473        let result3 =
2474            parse_and_evaluate("f(10)", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2475        assert!(result3.is_ok());
2476        assert_eq!(result3.unwrap(), Value::Number(20.0));
2477
2478        // Original x should be unchanged
2479        let result4 = parse_and_evaluate("x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2480        assert!(result4.is_ok());
2481        assert_eq!(result4.unwrap(), Value::Number(5.0));
2482    }
2483
2484    #[test]
2485    fn variable_immutability_in_do_blocks() {
2486        let heap = Rc::new(RefCell::new(Heap::new()));
2487        let bindings = Rc::new(RefCell::new(HashMap::new()));
2488
2489        // Assign x in outer scope
2490        let result1 =
2491            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2492        assert!(result1.is_ok());
2493
2494        // Do blocks can now shadow outer variables (changed behavior)
2495        let do_block = r#"do {
2496            x = 10
2497            y = x * 2
2498            return y
2499        }"#;
2500        let result2 =
2501            parse_and_evaluate(do_block, Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2502        assert!(result2.is_ok());
2503        assert_eq!(result2.unwrap(), Value::Number(20.0)); // 10 * 2 = 20
2504
2505        // But new variables in do block should work
2506        let do_block2 = r#"do {
2507            z = x * 3
2508            return z
2509        }"#;
2510        let result3 = parse_and_evaluate(
2511            do_block2,
2512            Some(Rc::clone(&heap)),
2513            Some(Rc::clone(&bindings)),
2514        );
2515        assert!(result3.is_ok());
2516        assert_eq!(result3.unwrap(), Value::Number(15.0));
2517
2518        // Original x should be unchanged
2519        let result4 = parse_and_evaluate("x", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2520        assert!(result4.is_ok());
2521        assert_eq!(result4.unwrap(), Value::Number(5.0));
2522    }
2523
2524    #[test]
2525    fn variable_immutability_different_variables() {
2526        let heap = Rc::new(RefCell::new(Heap::new()));
2527        let bindings = Rc::new(RefCell::new(HashMap::new()));
2528
2529        // Multiple different variables should work
2530        let result1 =
2531            parse_and_evaluate("x = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2532        assert!(result1.is_ok());
2533
2534        let result2 =
2535            parse_and_evaluate("y = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)));
2536        assert!(result2.is_ok());
2537
2538        let result3 = parse_and_evaluate(
2539            "z = x + y",
2540            Some(Rc::clone(&heap)),
2541            Some(Rc::clone(&bindings)),
2542        );
2543        assert!(result3.is_ok());
2544        assert_eq!(result3.unwrap(), Value::Number(15.0));
2545    }
2546
2547    #[test]
2548    fn variable_immutability_with_complex_types() {
2549        let heap = Rc::new(RefCell::new(Heap::new()));
2550        let bindings = Rc::new(RefCell::new(HashMap::new()));
2551
2552        // List assignment
2553        let result1 = parse_and_evaluate(
2554            "list = [1, 2, 3]",
2555            Some(Rc::clone(&heap)),
2556            Some(Rc::clone(&bindings)),
2557        );
2558        assert!(result1.is_ok());
2559
2560        // Reassignment should fail
2561        let result2 = parse_and_evaluate(
2562            "list = [4, 5, 6]",
2563            Some(Rc::clone(&heap)),
2564            Some(Rc::clone(&bindings)),
2565        );
2566        assert!(result2.is_err());
2567        assert!(
2568            result2
2569                .unwrap_err()
2570                .to_string()
2571                .contains("list is already defined, and cannot be reassigned")
2572        );
2573
2574        // Record assignment
2575        let result3 = parse_and_evaluate(
2576            "rec = {x: 1, y: 2}",
2577            Some(Rc::clone(&heap)),
2578            Some(Rc::clone(&bindings)),
2579        );
2580        assert!(result3.is_ok());
2581
2582        // Reassignment should fail
2583        let result4 = parse_and_evaluate(
2584            "rec = {x: 3, y: 4}",
2585            Some(Rc::clone(&heap)),
2586            Some(Rc::clone(&bindings)),
2587        );
2588        assert!(result4.is_err());
2589        assert!(
2590            result4
2591                .unwrap_err()
2592                .to_string()
2593                .contains("rec is already defined, and cannot be reassigned")
2594        );
2595    }
2596
2597    #[test]
2598    fn equality_comparison_numbers() {
2599        let result = parse_and_evaluate("5e2 == 5e2", None, None).unwrap();
2600        assert_eq!(result, Value::Bool(true));
2601
2602        let result = parse_and_evaluate("42 == 42", None, None).unwrap();
2603        assert_eq!(result, Value::Bool(true));
2604
2605        let result = parse_and_evaluate("42 == 43", None, None).unwrap();
2606        assert_eq!(result, Value::Bool(false));
2607    }
2608
2609    #[test]
2610    fn equality_comparison_strings() {
2611        let result = parse_and_evaluate(r#""hey" == "hey""#, None, None).unwrap();
2612        assert_eq!(result, Value::Bool(true));
2613
2614        let result = parse_and_evaluate(r#""hello" == "world""#, None, None).unwrap();
2615        assert_eq!(result, Value::Bool(false));
2616
2617        let result = parse_and_evaluate(r#""" == """#, None, None).unwrap();
2618        assert_eq!(result, Value::Bool(true));
2619    }
2620
2621    #[test]
2622    fn equality_comparison_lists() {
2623        let heap = Rc::new(RefCell::new(Heap::new()));
2624        let result = parse_and_evaluate("[1,2,3] == [1,2,3]", Some(heap.clone()), None).unwrap();
2625        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
2626        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2627
2628        let heap2 = Rc::new(RefCell::new(Heap::new()));
2629        let result = parse_and_evaluate("[1,2,3] == [1,2,4]", Some(heap2.clone()), None).unwrap();
2630        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(false)];
2631        assert_eq!(result.as_list(&heap2.borrow()).unwrap(), &expected);
2632
2633        // Note: Lists of different lengths cannot be compared with broadcasting syntax
2634        // They are compared directly as values
2635
2636        // String lists - element-wise comparison
2637        let heap3 = Rc::new(RefCell::new(Heap::new()));
2638        let result =
2639            parse_and_evaluate(r#"["a", "b"] == ["a", "b"]"#, Some(heap3.clone()), None).unwrap();
2640        let expected = vec![Value::Bool(true), Value::Bool(true)];
2641        assert_eq!(result.as_list(&heap3.borrow()).unwrap(), &expected);
2642
2643        // Nested lists - each inner list compared as a whole
2644        let heap4 = Rc::new(RefCell::new(Heap::new()));
2645        let result =
2646            parse_and_evaluate("[[1,2,3]] == [[1,2,3]]", Some(heap4.clone()), None).unwrap();
2647        let expected = vec![Value::Bool(true)];
2648        assert_eq!(result.as_list(&heap4.borrow()).unwrap(), &expected);
2649
2650        let heap5 = Rc::new(RefCell::new(Heap::new()));
2651        let result = parse_and_evaluate(
2652            "[[1,2,3], [4,5]] == [[1,2,3], [4,5]]",
2653            Some(heap5.clone()),
2654            None,
2655        )
2656        .unwrap();
2657        let expected = vec![Value::Bool(true), Value::Bool(true)];
2658        assert_eq!(result.as_list(&heap5.borrow()).unwrap(), &expected);
2659    }
2660
2661    #[test]
2662    fn equality_comparison_records() {
2663        let result = parse_and_evaluate("{ hey: 1 } == { hey: 1 }", None, None).unwrap();
2664        assert_eq!(result, Value::Bool(true));
2665
2666        let result = parse_and_evaluate("{ hey: 1 } == { hey: 2 }", None, None).unwrap();
2667        assert_eq!(result, Value::Bool(false));
2668
2669        let result = parse_and_evaluate("{ hey: 1 } == { hello: 1 }", None, None).unwrap();
2670        assert_eq!(result, Value::Bool(false));
2671
2672        // Multiple fields
2673        let result = parse_and_evaluate("{ a: 1, b: 2 } == { a: 1, b: 2 }", None, None).unwrap();
2674        assert_eq!(result, Value::Bool(true));
2675
2676        // Order shouldn't matter for records
2677        let result = parse_and_evaluate("{ a: 1, b: 2 } == { b: 2, a: 1 }", None, None).unwrap();
2678        assert_eq!(result, Value::Bool(true));
2679
2680        // Nested records
2681        let result = parse_and_evaluate("{ x: { y: 1 } } == { x: { y: 1 } }", None, None).unwrap();
2682        assert_eq!(result, Value::Bool(true));
2683    }
2684
2685    #[test]
2686    fn equality_comparison_lambdas() {
2687        let heap = Rc::new(RefCell::new(Heap::new()));
2688        let bindings = Rc::new(RefCell::new(HashMap::new()));
2689
2690        // Lambdas with same structure are equal
2691        parse_and_evaluate(
2692            "f1 = (x) => x + 2",
2693            Some(Rc::clone(&heap)),
2694            Some(Rc::clone(&bindings)),
2695        )
2696        .unwrap();
2697        parse_and_evaluate(
2698            "f2 = (x) => x + 2",
2699            Some(Rc::clone(&heap)),
2700            Some(Rc::clone(&bindings)),
2701        )
2702        .unwrap();
2703        let result = parse_and_evaluate(
2704            "f1 == f2",
2705            Some(Rc::clone(&heap)),
2706            Some(Rc::clone(&bindings)),
2707        )
2708        .unwrap();
2709        assert_eq!(result, Value::Bool(true));
2710
2711        // Same lambda reference is equal to itself
2712        let result = parse_and_evaluate(
2713            "f1 == f1",
2714            Some(Rc::clone(&heap)),
2715            Some(Rc::clone(&bindings)),
2716        )
2717        .unwrap();
2718        assert_eq!(result, Value::Bool(true));
2719
2720        // Assigned lambda is equal
2721        parse_and_evaluate(
2722            "f3 = f1",
2723            Some(Rc::clone(&heap)),
2724            Some(Rc::clone(&bindings)),
2725        )
2726        .unwrap();
2727        let result = parse_and_evaluate(
2728            "f1 == f3",
2729            Some(Rc::clone(&heap)),
2730            Some(Rc::clone(&bindings)),
2731        )
2732        .unwrap();
2733        assert_eq!(result, Value::Bool(true));
2734
2735        // Different bodies are not equal
2736        parse_and_evaluate(
2737            "f4 = (x) => x + 3",
2738            Some(Rc::clone(&heap)),
2739            Some(Rc::clone(&bindings)),
2740        )
2741        .unwrap();
2742        let result = parse_and_evaluate(
2743            "f1 == f4",
2744            Some(Rc::clone(&heap)),
2745            Some(Rc::clone(&bindings)),
2746        )
2747        .unwrap();
2748        assert_eq!(result, Value::Bool(false));
2749
2750        // Different parameter names are not equal
2751        parse_and_evaluate(
2752            "f5 = (y) => y + 2",
2753            Some(Rc::clone(&heap)),
2754            Some(Rc::clone(&bindings)),
2755        )
2756        .unwrap();
2757        let result = parse_and_evaluate(
2758            "f1 == f5",
2759            Some(Rc::clone(&heap)),
2760            Some(Rc::clone(&bindings)),
2761        )
2762        .unwrap();
2763        assert_eq!(result, Value::Bool(false));
2764
2765        // Different arities are not equal
2766        parse_and_evaluate(
2767            "f6 = (x, y) => x + y",
2768            Some(Rc::clone(&heap)),
2769            Some(Rc::clone(&bindings)),
2770        )
2771        .unwrap();
2772        let result = parse_and_evaluate(
2773            "f1 == f6",
2774            Some(Rc::clone(&heap)),
2775            Some(Rc::clone(&bindings)),
2776        )
2777        .unwrap();
2778        assert_eq!(result, Value::Bool(false));
2779    }
2780
2781    #[test]
2782    fn equality_comparison_mixed_types() {
2783        // Different types are never equal
2784        let result = parse_and_evaluate("1 == \"1\"", None, None).unwrap();
2785        assert_eq!(result, Value::Bool(false));
2786
2787        let result = parse_and_evaluate("true == 1", None, None).unwrap();
2788        assert_eq!(result, Value::Bool(false));
2789
2790        // Note: [1] == 1 uses broadcasting and returns [true]
2791        let heap = Rc::new(RefCell::new(Heap::new()));
2792        let result = parse_and_evaluate("[1] == 1", Some(heap.clone()), None).unwrap();
2793        let expected = vec![Value::Bool(true)];
2794        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2795
2796        let result = parse_and_evaluate("null == 0", None, None).unwrap();
2797        assert_eq!(result, Value::Bool(false));
2798    }
2799
2800    #[test]
2801    fn comparison_operators_strings() {
2802        // Lexicographic comparison
2803        let result = parse_and_evaluate(r#""apple" < "banana""#, None, None).unwrap();
2804        assert_eq!(result, Value::Bool(true));
2805
2806        let result = parse_and_evaluate(r#""zebra" > "apple""#, None, None).unwrap();
2807        assert_eq!(result, Value::Bool(true));
2808
2809        let result = parse_and_evaluate(r#""hello" <= "hello""#, None, None).unwrap();
2810        assert_eq!(result, Value::Bool(true));
2811    }
2812
2813    #[test]
2814    fn comparison_operators_lists() {
2815        // List comparisons use element-wise broadcasting
2816        // [1, 2] < [2, 3] means [1 < 2, 2 < 3] = [true, true]
2817        let heap = Rc::new(RefCell::new(Heap::new()));
2818        let result = parse_and_evaluate("[1, 2] < [2, 3]", Some(heap.clone()), None).unwrap();
2819        let expected = vec![Value::Bool(true), Value::Bool(true)];
2820        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
2821
2822        // [1, 2] < [1, 3] means [1 < 1, 2 < 3] = [false, true]
2823        let heap2 = Rc::new(RefCell::new(Heap::new()));
2824        let result = parse_and_evaluate("[1, 2] < [1, 3]", Some(heap2.clone()), None).unwrap();
2825        let expected = vec![Value::Bool(false), Value::Bool(true)];
2826        assert_eq!(result.as_list(&heap2.borrow()).unwrap(), &expected);
2827
2828        // [2, 1] > [1, 0] means [2 > 1, 1 > 0] = [true, true]
2829        let heap3 = Rc::new(RefCell::new(Heap::new()));
2830        let result = parse_and_evaluate("[2, 1] > [1, 0]", Some(heap3.clone()), None).unwrap();
2831        let expected = vec![Value::Bool(true), Value::Bool(true)];
2832        assert_eq!(result.as_list(&heap3.borrow()).unwrap(), &expected);
2833    }
2834
2835    #[test]
2836    fn late_binding_unbound_variable_succeeds() {
2837        // Functions now use late binding - definition succeeds
2838        let heap = Rc::new(RefCell::new(Heap::new()));
2839        let bindings = Rc::new(RefCell::new(HashMap::new()));
2840        let result = parse_and_evaluate("f = x => x + y", Some(heap), Some(bindings));
2841        // Definition should succeed (error only on call if y not bound)
2842        assert!(result.is_ok());
2843    }
2844
2845    #[test]
2846    fn early_binding_bound_variable_succeeds() {
2847        // Should succeed - y is bound before function definition
2848        let heap = Rc::new(RefCell::new(Heap::new()));
2849        let bindings = Rc::new(RefCell::new(HashMap::new()));
2850
2851        // First bind y
2852        let _ = parse_and_evaluate("y = 5", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
2853            .unwrap();
2854
2855        // Then define function that uses y
2856        let result = parse_and_evaluate(
2857            "g = x => x + y",
2858            Some(Rc::clone(&heap)),
2859            Some(Rc::clone(&bindings)),
2860        );
2861        assert!(result.is_ok());
2862
2863        // Verify the function works
2864        let call_result = parse_and_evaluate("g(2)", Some(heap), Some(bindings)).unwrap();
2865        assert_eq!(call_result, Value::Number(7.0));
2866    }
2867
2868    #[test]
2869    fn early_binding_builtin_function_succeeds() {
2870        // Should succeed - using built-in functions
2871        let heap = Rc::new(RefCell::new(Heap::new()));
2872        let bindings = Rc::new(RefCell::new(HashMap::new()));
2873        let result = parse_and_evaluate(
2874            "h = x => sin(x)",
2875            Some(Rc::clone(&heap)),
2876            Some(Rc::clone(&bindings)),
2877        );
2878        assert!(result.is_ok());
2879
2880        // Verify the function works
2881        let call_result = parse_and_evaluate("h(0)", Some(heap), Some(bindings)).unwrap();
2882        assert_eq!(call_result, Value::Number(0.0));
2883    }
2884
2885    #[test]
2886    fn late_binding_nested_function_succeeds() {
2887        // Should succeed with late binding - nested function can reference z defined later
2888        let heap = Rc::new(RefCell::new(Heap::new()));
2889        let bindings = Rc::new(RefCell::new(HashMap::new()));
2890
2891        // Define outer function with nested inner that references z
2892        let result = parse_and_evaluate(
2893            "outer = x => do { inner = y => y + z; return inner(x) }",
2894            Some(Rc::clone(&heap)),
2895            Some(Rc::clone(&bindings)),
2896        );
2897        assert!(result.is_ok());
2898
2899        // Define z
2900        let _ = parse_and_evaluate(
2901            "z = 100",
2902            Some(Rc::clone(&heap)),
2903            Some(Rc::clone(&bindings)),
2904        )
2905        .unwrap();
2906
2907        // Call outer(5) should work: 5 + 100 = 105
2908        let call_result = parse_and_evaluate("outer(5)", Some(heap), Some(bindings)).unwrap();
2909        assert_eq!(call_result, Value::Number(105.0));
2910    }
2911
2912    #[test]
2913    fn early_binding_all_variables_bound() {
2914        // Should succeed - all variables bound
2915        let heap = Rc::new(RefCell::new(Heap::new()));
2916        let bindings = Rc::new(RefCell::new(HashMap::new()));
2917
2918        // Bind a and b
2919        let _ = parse_and_evaluate("a = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
2920            .unwrap();
2921        let _ = parse_and_evaluate("b = 20", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
2922            .unwrap();
2923
2924        // Define function using a and b
2925        let result = parse_and_evaluate(
2926            "func = x => (x + a) * b",
2927            Some(Rc::clone(&heap)),
2928            Some(Rc::clone(&bindings)),
2929        );
2930        assert!(result.is_ok());
2931
2932        // Verify the function works
2933        let call_result = parse_and_evaluate("func(5)", Some(heap), Some(bindings)).unwrap();
2934        assert_eq!(call_result, Value::Number(300.0));
2935    }
2936
2937    #[test]
2938    fn early_binding_parameter_shadows_outer() {
2939        // Should succeed - lambda parameter shadows outer variable
2940        let heap = Rc::new(RefCell::new(Heap::new()));
2941        let bindings = Rc::new(RefCell::new(HashMap::new()));
2942
2943        // Bind outer x
2944        let _ = parse_and_evaluate(
2945            "x = 100",
2946            Some(Rc::clone(&heap)),
2947            Some(Rc::clone(&bindings)),
2948        )
2949        .unwrap();
2950
2951        // Define function with parameter x that shadows outer x
2952        let result = parse_and_evaluate(
2953            "shadow = x => x * 2",
2954            Some(Rc::clone(&heap)),
2955            Some(Rc::clone(&bindings)),
2956        );
2957        assert!(result.is_ok());
2958
2959        // Verify the function uses parameter, not outer variable
2960        let call_result = parse_and_evaluate("shadow(5)", Some(heap), Some(bindings)).unwrap();
2961        assert_eq!(call_result, Value::Number(10.0));
2962    }
2963
2964    #[test]
2965    fn early_binding_do_block_internal_binding() {
2966        // Should succeed - variable bound inside lambda's do block
2967        let heap = Rc::new(RefCell::new(Heap::new()));
2968        let bindings = Rc::new(RefCell::new(HashMap::new()));
2969
2970        // Define function with do block that binds y internally
2971        let result = parse_and_evaluate(
2972            "f = x => do { y = 5; return x + y }",
2973            Some(Rc::clone(&heap)),
2974            Some(Rc::clone(&bindings)),
2975        );
2976        assert!(result.is_ok());
2977
2978        // Verify the function works
2979        let call_result = parse_and_evaluate("f(10)", Some(heap), Some(bindings)).unwrap();
2980        assert_eq!(call_result, Value::Number(15.0));
2981    }
2982
2983    #[test]
2984    fn early_binding_do_block_nested_scope() {
2985        // Should succeed - variable bound in do block before inner function definition
2986        let heap = Rc::new(RefCell::new(Heap::new()));
2987        let bindings = Rc::new(RefCell::new(HashMap::new()));
2988
2989        let result = parse_and_evaluate(
2990            "later_func = do { c = 30; f = x => x + c; return f(10) }",
2991            Some(Rc::clone(&heap)),
2992            Some(Rc::clone(&bindings)),
2993        );
2994        assert!(result.is_ok());
2995
2996        // Verify the result
2997        let later_func_result =
2998            parse_and_evaluate("later_func", Some(heap), Some(bindings)).unwrap();
2999        assert_eq!(later_func_result, Value::Number(40.0));
3000    }
3001
3002    #[test]
3003    fn late_binding_do_block_forward_reference_succeeds() {
3004        // Should succeed with late binding - do block variables are available
3005        let heap = Rc::new(RefCell::new(Heap::new()));
3006        let bindings = Rc::new(RefCell::new(HashMap::new()));
3007
3008        let result = parse_and_evaluate(
3009            "g = x => do { f = y => y + z; z = 10; return f(x) }",
3010            Some(Rc::clone(&heap)),
3011            Some(Rc::clone(&bindings)),
3012        );
3013        assert!(result.is_ok());
3014
3015        // Call g(5) should work: 5 + 10 = 15
3016        let call_result = parse_and_evaluate("g(5)", Some(heap), Some(bindings)).unwrap();
3017        assert_eq!(call_result, Value::Number(15.0));
3018    }
3019
3020    #[test]
3021    fn early_binding_do_block_multiple_assignments() {
3022        // Should succeed - multiple assignments in do block
3023        let heap = Rc::new(RefCell::new(Heap::new()));
3024        let bindings = Rc::new(RefCell::new(HashMap::new()));
3025
3026        let result = parse_and_evaluate(
3027            "calc = x => do { a = 2; b = 3; c = 4; return x * a + b * c }",
3028            Some(Rc::clone(&heap)),
3029            Some(Rc::clone(&bindings)),
3030        );
3031        assert!(result.is_ok());
3032
3033        // Verify the function works: 5 * 2 + 3 * 4 = 10 + 12 = 22
3034        let call_result = parse_and_evaluate("calc(5)", Some(heap), Some(bindings)).unwrap();
3035        assert_eq!(call_result, Value::Number(22.0));
3036    }
3037
3038    #[test]
3039    fn late_binding_multiple_unbound_variables_succeeds() {
3040        // Should succeed with late binding - function can reference y and z defined later
3041        let heap = Rc::new(RefCell::new(Heap::new()));
3042        let bindings = Rc::new(RefCell::new(HashMap::new()));
3043
3044        let result = parse_and_evaluate(
3045            "f = x => x + y + z",
3046            Some(Rc::clone(&heap)),
3047            Some(Rc::clone(&bindings)),
3048        );
3049        assert!(result.is_ok());
3050
3051        // Define y and z
3052        let _ = parse_and_evaluate("y = 10", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
3053            .unwrap();
3054        let _ = parse_and_evaluate("z = 20", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
3055            .unwrap();
3056
3057        // Call f(5) should work: 5 + 10 + 20 = 35
3058        let call_result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
3059        assert_eq!(call_result, Value::Number(35.0));
3060    }
3061
3062    #[test]
3063    fn late_binding_with_optional_args_succeeds() {
3064        // Should succeed with late binding - function with optional args can reference z defined later
3065        let heap = Rc::new(RefCell::new(Heap::new()));
3066        let bindings = Rc::new(RefCell::new(HashMap::new()));
3067
3068        let result = parse_and_evaluate(
3069            "f = (x, y?) => x + z",
3070            Some(Rc::clone(&heap)),
3071            Some(Rc::clone(&bindings)),
3072        );
3073        assert!(result.is_ok());
3074
3075        // Define z
3076        let _ = parse_and_evaluate(
3077            "z = 100",
3078            Some(Rc::clone(&heap)),
3079            Some(Rc::clone(&bindings)),
3080        )
3081        .unwrap();
3082
3083        // Call f(5) should work: 5 + 100 = 105
3084        let call_result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
3085        assert_eq!(call_result, Value::Number(105.0));
3086    }
3087
3088    #[test]
3089    fn late_binding_with_rest_args_succeeds() {
3090        // Should succeed with late binding - function with rest args can reference y defined later
3091        let heap = Rc::new(RefCell::new(Heap::new()));
3092        let bindings = Rc::new(RefCell::new(HashMap::new()));
3093
3094        let result = parse_and_evaluate(
3095            "f = (x, ...rest) => x + y",
3096            Some(Rc::clone(&heap)),
3097            Some(Rc::clone(&bindings)),
3098        );
3099        assert!(result.is_ok());
3100
3101        // Define y
3102        let _ = parse_and_evaluate("y = 50", Some(Rc::clone(&heap)), Some(Rc::clone(&bindings)))
3103            .unwrap();
3104
3105        // Call f(5) should work: 5 + 50 = 55
3106        let call_result = parse_and_evaluate("f(5)", Some(heap), Some(bindings)).unwrap();
3107        assert_eq!(call_result, Value::Number(55.0));
3108    }
3109
3110    #[test]
3111    fn recursion_self_reference_allowed() {
3112        // Should succeed - recursive functions can reference themselves
3113        let heap = Rc::new(RefCell::new(Heap::new()));
3114        let bindings = Rc::new(RefCell::new(HashMap::new()));
3115
3116        // Define recursive factorial
3117        let result = parse_and_evaluate(
3118            "factorial = n => if n <= 1 then 1 else n * factorial(n - 1)",
3119            Some(Rc::clone(&heap)),
3120            Some(Rc::clone(&bindings)),
3121        );
3122        assert!(result.is_ok());
3123
3124        // Test it works
3125        let call_result = parse_and_evaluate("factorial(5)", Some(heap), Some(bindings)).unwrap();
3126        assert_eq!(call_result, Value::Number(120.0));
3127    }
3128
3129    #[test]
3130    fn recursion_fibonacci() {
3131        // Should succeed - another recursive function example
3132        let heap = Rc::new(RefCell::new(Heap::new()));
3133        let bindings = Rc::new(RefCell::new(HashMap::new()));
3134
3135        // Define recursive fibonacci
3136        let result = parse_and_evaluate(
3137            "fib = n => if n <= 1 then n else fib(n - 1) + fib(n - 2)",
3138            Some(Rc::clone(&heap)),
3139            Some(Rc::clone(&bindings)),
3140        );
3141        assert!(result.is_ok());
3142
3143        // Test it works - fib(6) = 8
3144        let call_result = parse_and_evaluate("fib(6)", Some(heap), Some(bindings)).unwrap();
3145        assert_eq!(call_result, Value::Number(8.0));
3146    }
3147
3148    #[test]
3149    fn recursion_in_do_block() {
3150        // Should succeed - recursion defined in do block
3151        let heap = Rc::new(RefCell::new(Heap::new()));
3152        let bindings = Rc::new(RefCell::new(HashMap::new()));
3153
3154        let result = parse_and_evaluate(
3155            "result = do { factorial = n => if n <= 1 then 1 else n * factorial(n - 1); return factorial(4) }",
3156            Some(Rc::clone(&heap)),
3157            Some(Rc::clone(&bindings)),
3158        );
3159        assert!(result.is_ok());
3160
3161        // Verify the result
3162        let result_val = parse_and_evaluate("result", Some(heap), Some(bindings)).unwrap();
3163        assert_eq!(result_val, Value::Number(24.0));
3164    }
3165
3166    #[test]
3167    fn mutual_recursion_succeeds() {
3168        // Should succeed with late binding - mutual recursion now works
3169        let heap = Rc::new(RefCell::new(Heap::new()));
3170        let bindings = Rc::new(RefCell::new(HashMap::new()));
3171
3172        // Define isEven that references isOdd
3173        let result = parse_and_evaluate(
3174            "isEven = n => if n == 0 then true else isOdd(n - 1)",
3175            Some(Rc::clone(&heap)),
3176            Some(Rc::clone(&bindings)),
3177        );
3178        assert!(result.is_ok());
3179
3180        // Define isOdd that references isEven
3181        let _ = parse_and_evaluate(
3182            "isOdd = n => if n == 0 then false else isEven(n - 1)",
3183            Some(Rc::clone(&heap)),
3184            Some(Rc::clone(&bindings)),
3185        )
3186        .unwrap();
3187
3188        // Test mutual recursion
3189        let result1 = parse_and_evaluate(
3190            "isEven(4)",
3191            Some(Rc::clone(&heap)),
3192            Some(Rc::clone(&bindings)),
3193        )
3194        .unwrap();
3195        assert_eq!(result1, Value::Bool(true));
3196
3197        let result2 = parse_and_evaluate(
3198            "isEven(5)",
3199            Some(Rc::clone(&heap)),
3200            Some(Rc::clone(&bindings)),
3201        )
3202        .unwrap();
3203        assert_eq!(result2, Value::Bool(false));
3204
3205        let result3 = parse_and_evaluate(
3206            "isOdd(3)",
3207            Some(Rc::clone(&heap)),
3208            Some(Rc::clone(&bindings)),
3209        )
3210        .unwrap();
3211        assert_eq!(result3, Value::Bool(true));
3212
3213        let result4 = parse_and_evaluate("isOdd(6)", Some(heap), Some(bindings)).unwrap();
3214        assert_eq!(result4, Value::Bool(false));
3215    }
3216
3217    #[test]
3218    fn recursion_only_for_lambdas() {
3219        // Non-lambda assignments should not get special treatment
3220        let heap = Rc::new(RefCell::new(Heap::new()));
3221        let bindings = Rc::new(RefCell::new(HashMap::new()));
3222
3223        // This should fail because x is not bound
3224        let result = parse_and_evaluate("y = x + 1", Some(heap), Some(bindings));
3225        assert!(result.is_err());
3226    }
3227
3228    // Tests for dot-prefixed comparison operators (non-broadcasting)
3229    #[test]
3230    fn dot_equal_lists() {
3231        let heap = Rc::new(RefCell::new(Heap::new()));
3232
3233        // Lists with same values should be equal
3234        let result =
3235            parse_and_evaluate("[1, 2, 3] .== [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3236        assert_eq!(result, Value::Bool(true));
3237
3238        // Different lists should not be equal
3239        let result =
3240            parse_and_evaluate("[1, 2, 3] .== [1, 2, 4]", Some(Rc::clone(&heap)), None).unwrap();
3241        assert_eq!(result, Value::Bool(false));
3242
3243        // Different lengths should not be equal
3244        let result =
3245            parse_and_evaluate("[1, 2] .== [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3246        assert_eq!(result, Value::Bool(false));
3247
3248        // List vs scalar should not be equal
3249        let result = parse_and_evaluate("[1, 2, 3] .== 1", Some(Rc::clone(&heap)), None).unwrap();
3250        assert_eq!(result, Value::Bool(false));
3251
3252        // Empty lists should be equal
3253        let result = parse_and_evaluate("[] .== []", Some(Rc::clone(&heap)), None).unwrap();
3254        assert_eq!(result, Value::Bool(true));
3255
3256        // Nested lists
3257        let result =
3258            parse_and_evaluate("[[1, 2], [3, 4]] .== [[1, 2], [3, 4]]", Some(heap), None).unwrap();
3259        assert_eq!(result, Value::Bool(true));
3260    }
3261
3262    #[test]
3263    fn dot_not_equal_lists() {
3264        let heap = Rc::new(RefCell::new(Heap::new()));
3265
3266        let result =
3267            parse_and_evaluate("[1, 2, 3] .!= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3268        assert_eq!(result, Value::Bool(false));
3269
3270        let result =
3271            parse_and_evaluate("[1, 2, 3] .!= [1, 2, 4]", Some(Rc::clone(&heap)), None).unwrap();
3272        assert_eq!(result, Value::Bool(true));
3273
3274        let result = parse_and_evaluate("[1, 2, 3] .!= 1", Some(heap), None).unwrap();
3275        assert_eq!(result, Value::Bool(true));
3276    }
3277
3278    #[test]
3279    fn dot_less_lists() {
3280        let heap = Rc::new(RefCell::new(Heap::new()));
3281
3282        // Lexicographic comparison
3283        let result =
3284            parse_and_evaluate("[1, 2, 2] .< [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3285        assert_eq!(result, Value::Bool(true));
3286
3287        let result =
3288            parse_and_evaluate("[1, 2] .< [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3289        assert_eq!(result, Value::Bool(true));
3290
3291        let result = parse_and_evaluate("[] .< [1]", Some(Rc::clone(&heap)), None).unwrap();
3292        assert_eq!(result, Value::Bool(true));
3293
3294        let result = parse_and_evaluate("[2] .< [1, 9, 9]", Some(Rc::clone(&heap)), None).unwrap();
3295        assert_eq!(result, Value::Bool(false));
3296
3297        let result = parse_and_evaluate("[1, 2, 3] .< [1, 2, 3]", Some(heap), None).unwrap();
3298        assert_eq!(result, Value::Bool(false));
3299    }
3300
3301    #[test]
3302    fn dot_less_eq_lists() {
3303        let heap = Rc::new(RefCell::new(Heap::new()));
3304
3305        let result =
3306            parse_and_evaluate("[1, 2, 2] .<= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3307        assert_eq!(result, Value::Bool(true));
3308
3309        let result =
3310            parse_and_evaluate("[1, 2, 3] .<= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3311        assert_eq!(result, Value::Bool(true));
3312
3313        let result = parse_and_evaluate("[1, 2, 4] .<= [1, 2, 3]", Some(heap), None).unwrap();
3314        assert_eq!(result, Value::Bool(false));
3315    }
3316
3317    #[test]
3318    fn dot_greater_lists() {
3319        let heap = Rc::new(RefCell::new(Heap::new()));
3320
3321        let result =
3322            parse_and_evaluate("[1, 2, 3] .> [1, 2, 2]", Some(Rc::clone(&heap)), None).unwrap();
3323        assert_eq!(result, Value::Bool(true));
3324
3325        let result = parse_and_evaluate("[2] .> [1, 9, 9]", Some(Rc::clone(&heap)), None).unwrap();
3326        assert_eq!(result, Value::Bool(true));
3327
3328        let result = parse_and_evaluate("[1] .> []", Some(Rc::clone(&heap)), None).unwrap();
3329        assert_eq!(result, Value::Bool(true));
3330
3331        let result = parse_and_evaluate("[1, 2, 3] .> [1, 2, 3]", Some(heap), None).unwrap();
3332        assert_eq!(result, Value::Bool(false));
3333    }
3334
3335    #[test]
3336    fn dot_greater_eq_lists() {
3337        let heap = Rc::new(RefCell::new(Heap::new()));
3338
3339        let result =
3340            parse_and_evaluate("[1, 2, 3] .>= [1, 2, 2]", Some(Rc::clone(&heap)), None).unwrap();
3341        assert_eq!(result, Value::Bool(true));
3342
3343        let result =
3344            parse_and_evaluate("[1, 2, 3] .>= [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3345        assert_eq!(result, Value::Bool(true));
3346
3347        let result = parse_and_evaluate("[1, 2, 2] .>= [1, 2, 3]", Some(heap), None).unwrap();
3348        assert_eq!(result, Value::Bool(false));
3349    }
3350
3351    #[test]
3352    fn dot_operators_with_scalars() {
3353        // Dot operators should work normally with scalars
3354        let result = parse_and_evaluate("5 .== 5", None, None).unwrap();
3355        assert_eq!(result, Value::Bool(true));
3356
3357        let result = parse_and_evaluate("5 .!= 3", None, None).unwrap();
3358        assert_eq!(result, Value::Bool(true));
3359
3360        let result = parse_and_evaluate("3 .< 5", None, None).unwrap();
3361        assert_eq!(result, Value::Bool(true));
3362
3363        let result = parse_and_evaluate("5 .> 3", None, None).unwrap();
3364        assert_eq!(result, Value::Bool(true));
3365    }
3366
3367    #[test]
3368    fn dot_equal_vs_regular_equal_with_broadcasting() {
3369        let heap = Rc::new(RefCell::new(Heap::new()));
3370
3371        // Regular == with broadcasting: each element compared individually
3372        let result =
3373            parse_and_evaluate("[true, true, true] == true", Some(Rc::clone(&heap)), None).unwrap();
3374        let expected = vec![Value::Bool(true), Value::Bool(true), Value::Bool(true)];
3375        assert_eq!(result.as_list(&heap.borrow()).unwrap(), &expected);
3376
3377        // Dot operator without broadcasting: list doesn't equal scalar
3378        let result =
3379            parse_and_evaluate("[true, true, true] .== true", Some(Rc::clone(&heap)), None)
3380                .unwrap();
3381        assert_eq!(result, Value::Bool(false));
3382
3383        // Dot operator comparing whole lists
3384        let result = parse_and_evaluate(
3385            "[true, true, true] .== [true, true, true]",
3386            Some(heap),
3387            None,
3388        )
3389        .unwrap();
3390        assert_eq!(result, Value::Bool(true));
3391    }
3392
3393    #[test]
3394    fn dot_operators_mixed_types() {
3395        let heap = Rc::new(RefCell::new(Heap::new()));
3396
3397        // String vs list should not be equal
3398        let result =
3399            parse_and_evaluate("\"hello\" .== [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3400        assert_eq!(result, Value::Bool(false));
3401
3402        // Number vs list comparison returns false (no natural ordering)
3403        let result = parse_and_evaluate("5 .< [1, 2, 3]", Some(Rc::clone(&heap)), None).unwrap();
3404        assert_eq!(result, Value::Bool(false));
3405
3406        // String comparison works
3407        let result = parse_and_evaluate("\"abc\" .< \"def\"", Some(heap), None).unwrap();
3408        assert_eq!(result, Value::Bool(true));
3409    }
3410}