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