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