blots_core/
functions.rs

1use crate::{
2    environment::Environment,
3    expressions::evaluate_ast,
4    heap::{Heap, HeapPointer, IterablePointer},
5    units,
6    values::{FunctionArity, LambdaArg, LambdaDef, ReifiedValue, Value},
7};
8use anyhow::{Result, anyhow};
9use dyn_fmt::AsStrFormatExt;
10use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::LazyLock};
11
12#[cfg(not(target_arch = "wasm32"))]
13use std::sync::Mutex;
14
15#[cfg(not(target_arch = "wasm32"))]
16use crate::stats::FunctionCallStats;
17
18#[cfg(not(target_arch = "wasm32"))]
19pub static FUNCTION_CALLS: LazyLock<Mutex<Vec<FunctionCallStats>>> =
20    LazyLock::new(|| Mutex::new(Vec::new()));
21
22#[cfg(not(target_arch = "wasm32"))]
23pub fn get_function_call_stats() -> Vec<FunctionCallStats> {
24    FUNCTION_CALLS.lock().unwrap().clone()
25}
26
27#[cfg(not(target_arch = "wasm32"))]
28pub fn clear_function_call_stats() {
29    FUNCTION_CALLS.lock().unwrap().clear();
30}
31
32// Cache built-in function names since they're static
33pub static BUILTIN_FUNCTION_NAMES: LazyLock<Vec<&'static str>> =
34    LazyLock::new(BuiltInFunction::all_names);
35
36#[derive(
37    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
38)]
39pub enum BuiltInFunction {
40    // Math functions
41    Sqrt,
42    Sin,
43    Cos,
44    Tan,
45    Asin,
46    Acos,
47    Atan,
48    Log,
49    Log10,
50    Exp,
51    Abs,
52    Floor,
53    Ceil,
54    Round,
55    Trunc,
56    Random,
57
58    // Aggregate functions
59    Min,
60    Max,
61    Avg,
62    Sum,
63    Prod,
64    Median,
65    Percentile,
66
67    // List functions
68    Range,
69    Len,
70    Head,
71    Tail,
72    Slice,
73    Concat,
74    Dot,
75    Unique,
76    Sort,
77    SortBy,
78    Reverse,
79    Any,
80    All,
81
82    // Higher-order functions
83    Map,
84    Reduce,
85    Filter,
86    Every,
87    Some,
88
89    // String functions
90    Split,
91    Join,
92    Replace,
93    Trim,
94    Uppercase,
95    Lowercase,
96    Includes,
97    Format,
98
99    // Type functions
100    Typeof,
101    Arity,
102
103    // Record functions
104    Keys,
105    Values,
106    Entries,
107
108    // Conversion functions
109    ToString,
110    ToNumber,
111    ToBool,
112    Convert,
113
114    // Platform-specific functions
115    #[cfg(not(target_arch = "wasm32"))]
116    Print,
117    #[cfg(not(target_arch = "wasm32"))]
118    TimeNow,
119}
120
121#[derive(Debug, Clone)]
122pub enum FunctionDef {
123    BuiltIn(BuiltInFunction),
124    Lambda(LambdaDef),
125}
126
127impl BuiltInFunction {
128    pub fn from_ident(ident: &str) -> Option<Self> {
129        match ident {
130            "sqrt" => Some(Self::Sqrt),
131            "sin" => Some(Self::Sin),
132            "cos" => Some(Self::Cos),
133            "tan" => Some(Self::Tan),
134            "asin" => Some(Self::Asin),
135            "acos" => Some(Self::Acos),
136            "atan" => Some(Self::Atan),
137            "log" => Some(Self::Log),
138            "log10" => Some(Self::Log10),
139            "exp" => Some(Self::Exp),
140            "abs" => Some(Self::Abs),
141            "floor" => Some(Self::Floor),
142            "ceil" => Some(Self::Ceil),
143            "round" => Some(Self::Round),
144            "trunc" => Some(Self::Trunc),
145            "random" => Some(Self::Random),
146            "min" => Some(Self::Min),
147            "max" => Some(Self::Max),
148            "avg" => Some(Self::Avg),
149            "sum" => Some(Self::Sum),
150            "prod" => Some(Self::Prod),
151            "median" => Some(Self::Median),
152            "percentile" => Some(Self::Percentile),
153            "range" => Some(Self::Range),
154            "any" => Some(Self::Any),
155            "all" => Some(Self::All),
156            "len" => Some(Self::Len),
157            "head" => Some(Self::Head),
158            "tail" => Some(Self::Tail),
159            "slice" => Some(Self::Slice),
160            "concat" => Some(Self::Concat),
161            "dot" => Some(Self::Dot),
162            "unique" => Some(Self::Unique),
163            "sort" => Some(Self::Sort),
164            "sort_by" => Some(Self::SortBy),
165            "reverse" => Some(Self::Reverse),
166            "map" => Some(Self::Map),
167            "reduce" => Some(Self::Reduce),
168            "filter" => Some(Self::Filter),
169            "every" => Some(Self::Every),
170            "some" => Some(Self::Some),
171            "split" => Some(Self::Split),
172            "join" => Some(Self::Join),
173            "replace" => Some(Self::Replace),
174            "trim" => Some(Self::Trim),
175            "uppercase" => Some(Self::Uppercase),
176            "lowercase" => Some(Self::Lowercase),
177            "to_string" => Some(Self::ToString),
178            "to_number" => Some(Self::ToNumber),
179            "to_bool" => Some(Self::ToBool),
180            "convert" => Some(Self::Convert),
181            "includes" => Some(Self::Includes),
182            "format" => Some(Self::Format),
183            "typeof" => Some(Self::Typeof),
184            "arity" => Some(Self::Arity),
185            "keys" => Some(Self::Keys),
186            "values" => Some(Self::Values),
187            "entries" => Some(Self::Entries),
188            #[cfg(not(target_arch = "wasm32"))]
189            "print" => Some(Self::Print),
190            #[cfg(not(target_arch = "wasm32"))]
191            "time_now" => Some(Self::TimeNow),
192            _ => None,
193        }
194    }
195
196    pub fn name(&self) -> &'static str {
197        match self {
198            Self::Sqrt => "sqrt",
199            Self::Sin => "sin",
200            Self::Cos => "cos",
201            Self::Tan => "tan",
202            Self::Asin => "asin",
203            Self::Acos => "acos",
204            Self::Atan => "atan",
205            Self::Log => "log",
206            Self::Log10 => "log10",
207            Self::Exp => "exp",
208            Self::Abs => "abs",
209            Self::Floor => "floor",
210            Self::Ceil => "ceil",
211            Self::Round => "round",
212            Self::Trunc => "trunc",
213            Self::Random => "random",
214            Self::Min => "min",
215            Self::Max => "max",
216            Self::Avg => "avg",
217            Self::Sum => "sum",
218            Self::Prod => "prod",
219            Self::Median => "median",
220            Self::Percentile => "percentile",
221            Self::Range => "range",
222            Self::Any => "any",
223            Self::All => "all",
224            Self::Len => "len",
225            Self::Head => "head",
226            Self::Tail => "tail",
227            Self::Slice => "slice",
228            Self::Concat => "concat",
229            Self::Dot => "dot",
230            Self::Unique => "unique",
231            Self::Sort => "sort",
232            Self::SortBy => "sort_by",
233            Self::Reverse => "reverse",
234            Self::Map => "map",
235            Self::Reduce => "reduce",
236            Self::Filter => "filter",
237            Self::Every => "every",
238            Self::Some => "some",
239            Self::Split => "split",
240            Self::Join => "join",
241            Self::Replace => "replace",
242            Self::Trim => "trim",
243            Self::Uppercase => "uppercase",
244            Self::Lowercase => "lowercase",
245            Self::Includes => "includes",
246            Self::Format => "format",
247            Self::Typeof => "typeof",
248            Self::Arity => "arity",
249            Self::Keys => "keys",
250            Self::Values => "values",
251            Self::Entries => "entries",
252            Self::ToString => "to_string",
253            Self::ToNumber => "to_number",
254            Self::ToBool => "to_bool",
255            Self::Convert => "convert",
256            #[cfg(not(target_arch = "wasm32"))]
257            Self::Print => "print",
258            #[cfg(not(target_arch = "wasm32"))]
259            Self::TimeNow => "time_now",
260        }
261    }
262
263    pub fn arity(&self) -> FunctionArity {
264        match self {
265            // Math functions - single argument
266            Self::Sqrt
267            | Self::Sin
268            | Self::Cos
269            | Self::Tan
270            | Self::Asin
271            | Self::Acos
272            | Self::Atan
273            | Self::Log
274            | Self::Log10
275            | Self::Exp
276            | Self::Abs
277            | Self::Floor
278            | Self::Ceil
279            | Self::Trunc
280            | Self::Random => FunctionArity::Exact(1),
281
282            // Round can take 1 or 2 arguments
283            Self::Round => FunctionArity::Between(1, 2),
284
285            // Aggregate functions
286            Self::Min | Self::Max | Self::Avg | Self::Sum | Self::Prod | Self::Median => {
287                FunctionArity::AtLeast(1)
288            }
289
290            // Range can take 1 or 2 arguments
291            Self::Range => FunctionArity::Between(1, 2),
292
293            // List functions
294            Self::Len
295            | Self::Head
296            | Self::Tail
297            | Self::Unique
298            | Self::Sort
299            | Self::Reverse
300            | Self::Any
301            | Self::All => FunctionArity::Exact(1),
302            Self::Slice => FunctionArity::Exact(3),
303            Self::Concat => FunctionArity::AtLeast(2),
304            Self::Dot | Self::Percentile => FunctionArity::Exact(2),
305
306            // Higher-order functions
307            Self::Map | Self::Filter | Self::Every | Self::Some | Self::SortBy => {
308                FunctionArity::Exact(2)
309            }
310            Self::Reduce => FunctionArity::Exact(3),
311
312            // String functions
313            Self::Split | Self::Join | Self::Includes => FunctionArity::Exact(2),
314            Self::Replace => FunctionArity::Exact(3),
315            Self::Trim
316            | Self::Uppercase
317            | Self::Lowercase
318            | Self::ToString
319            | Self::ToNumber
320            | Self::ToBool => FunctionArity::Exact(1),
321            Self::Convert => FunctionArity::Exact(3),
322            Self::Format => FunctionArity::AtLeast(1),
323
324            // Type functions
325            Self::Typeof | Self::Arity => FunctionArity::Exact(1),
326
327            // Record functions
328            Self::Keys | Self::Values | Self::Entries => FunctionArity::Exact(1),
329
330            // Platform-specific functions
331            #[cfg(not(target_arch = "wasm32"))]
332            Self::Print => FunctionArity::AtLeast(1),
333            #[cfg(not(target_arch = "wasm32"))]
334            Self::TimeNow => FunctionArity::Exact(0),
335        }
336    }
337
338    pub fn call(
339        &self,
340        args: Vec<Value>,
341        heap: Rc<RefCell<Heap>>,
342        bindings: Rc<Environment>,
343        call_depth: usize,
344        source: &str,
345    ) -> Result<Value> {
346        match self {
347            // Math functions
348            Self::Sqrt => Ok(Value::Number(args[0].as_number()?.sqrt())),
349            Self::Sin => Ok(Value::Number(args[0].as_number()?.sin())),
350            Self::Cos => Ok(Value::Number(args[0].as_number()?.cos())),
351            Self::Tan => Ok(Value::Number(args[0].as_number()?.tan())),
352            Self::Asin => Ok(Value::Number(args[0].as_number()?.asin())),
353            Self::Acos => Ok(Value::Number(args[0].as_number()?.acos())),
354            Self::Atan => Ok(Value::Number(args[0].as_number()?.atan())),
355            Self::Log => Ok(Value::Number(args[0].as_number()?.ln())),
356            Self::Log10 => Ok(Value::Number(args[0].as_number()?.log10())),
357            Self::Exp => Ok(Value::Number(args[0].as_number()?.exp())),
358            Self::Abs => Ok(Value::Number(args[0].as_number()?.abs())),
359            Self::Floor => Ok(Value::Number(args[0].as_number()?.floor())),
360            Self::Ceil => Ok(Value::Number(args[0].as_number()?.ceil())),
361            Self::Trunc => Ok(Value::Number(args[0].as_number()?.trunc())),
362
363            Self::Random => {
364                let seed = args[0].as_number()? as u64;
365                let mut rng = fastrand::Rng::with_seed(seed);
366                Ok(Value::Number(rng.f64()))
367            }
368
369            Self::Round => {
370                let num = args[0].as_number()?;
371                if args.len() == 1 {
372                    Ok(Value::Number(num.round()))
373                } else {
374                    let decimal_places = args[1].as_number()? as i32;
375                    let multiplier = 10_f64.powi(decimal_places);
376                    Ok(Value::Number((num * multiplier).round() / multiplier))
377                }
378            }
379
380            // Aggregate functions
381            Self::Min => {
382                let nums = if args.len() == 1 {
383                    // Check if single argument is a list
384                    match &args[0] {
385                        Value::List(_) => {
386                            let borrowed_heap = heap.borrow();
387                            let list = args[0].as_list(&borrowed_heap)?;
388                            list.iter()
389                                .map(|a| a.as_number())
390                                .collect::<Result<Vec<f64>>>()?
391                        }
392                        _ => vec![args[0].as_number()?],
393                    }
394                } else {
395                    args.iter()
396                        .map(|a| a.as_number())
397                        .collect::<Result<Vec<f64>>>()?
398                };
399
400                if nums.is_empty() {
401                    return Err(anyhow!("min requires at least one number"));
402                }
403
404                Ok(Value::Number(
405                    nums.iter().copied().fold(f64::INFINITY, f64::min),
406                ))
407            }
408
409            Self::Max => {
410                let nums = if args.len() == 1 {
411                    // Check if single argument is a list
412                    match &args[0] {
413                        Value::List(_) => {
414                            let borrowed_heap = heap.borrow();
415                            let list = args[0].as_list(&borrowed_heap)?;
416                            list.iter()
417                                .map(|a| a.as_number())
418                                .collect::<Result<Vec<f64>>>()?
419                        }
420                        _ => vec![args[0].as_number()?],
421                    }
422                } else {
423                    args.iter()
424                        .map(|a| a.as_number())
425                        .collect::<Result<Vec<f64>>>()?
426                };
427
428                if nums.is_empty() {
429                    return Err(anyhow!("max requires at least one number"));
430                }
431
432                Ok(Value::Number(
433                    nums.iter().copied().fold(f64::NEG_INFINITY, f64::max),
434                ))
435            }
436
437            Self::Avg => {
438                let nums = if args.len() == 1 {
439                    match &args[0] {
440                        Value::List(_) => {
441                            let borrowed_heap = heap.borrow();
442                            let list = args[0].as_list(&borrowed_heap)?;
443                            list.iter()
444                                .map(|a| a.as_number())
445                                .collect::<Result<Vec<f64>>>()?
446                        }
447                        _ => vec![args[0].as_number()?],
448                    }
449                } else {
450                    args.iter()
451                        .map(|a| a.as_number())
452                        .collect::<Result<Vec<f64>>>()?
453                };
454                if nums.is_empty() {
455                    return Err(anyhow!("avg requires at least one number"));
456                }
457                Ok(Value::Number(nums.iter().sum::<f64>() / nums.len() as f64))
458            }
459
460            Self::Prod => {
461                let nums = if args.len() == 1 {
462                    match &args[0] {
463                        Value::List(_) => {
464                            let borrowed_heap = heap.borrow();
465                            let list = args[0].as_list(&borrowed_heap)?;
466                            list.iter()
467                                .map(|a| a.as_number())
468                                .collect::<Result<Vec<f64>>>()?
469                        }
470                        _ => vec![args[0].as_number()?],
471                    }
472                } else {
473                    args.iter()
474                        .map(|a| a.as_number())
475                        .collect::<Result<Vec<f64>>>()?
476                };
477                if nums.is_empty() {
478                    return Err(anyhow!("prod requires at least one number"));
479                }
480                Ok(Value::Number(nums.iter().product()))
481            }
482
483            Self::Range => {
484                let (start, end) = match args[..] {
485                    [Value::Number(start)] => (0.0, start),
486                    [Value::Number(start), Value::Number(end)] => (start, end),
487                    _ => return Err(anyhow!("range requires 1 or 2 numbers")),
488                };
489
490                if start > end {
491                    return Err(anyhow!(
492                        "range requires start to be less than or equal to end"
493                    ));
494                }
495
496                if !f64::is_finite(start) || !f64::is_finite(end) {
497                    return Err(anyhow!("range requires finite numbers"));
498                }
499
500                let start_i64 = start as i64;
501                let end_i64 = end as i64;
502                let length = end_i64 - start_i64;
503
504                if length > u32::MAX as i64 {
505                    return Err(anyhow!(
506                        "list would be longer than the maximum length of {}",
507                        u32::MAX
508                    ));
509                }
510
511                let values = (start_i64..end_i64)
512                    .map(|e| Value::Number(e as f64))
513                    .collect();
514                let list = heap.borrow_mut().insert_list(values);
515
516                Ok(list)
517            }
518
519            Self::Sum => {
520                let nums = if args.len() == 1 {
521                    match &args[0] {
522                        Value::List(_) => {
523                            let borrowed_heap = heap.borrow();
524                            let list = args[0].as_list(&borrowed_heap)?;
525                            list.iter()
526                                .map(|a| a.as_number())
527                                .collect::<Result<Vec<f64>>>()?
528                        }
529                        _ => vec![args[0].as_number()?],
530                    }
531                } else {
532                    args.iter()
533                        .map(|a| a.as_number())
534                        .collect::<Result<Vec<f64>>>()?
535                };
536                if nums.is_empty() {
537                    return Err(anyhow!("sum requires at least one number"));
538                }
539                Ok(Value::Number(nums.iter().sum()))
540            }
541
542            // Remaining aggregate functions
543            Self::Median => {
544                let mut nums = if args.len() == 1 {
545                    match &args[0] {
546                        Value::List(_) => {
547                            let borrowed_heap = heap.borrow();
548                            let list = args[0].as_list(&borrowed_heap)?;
549                            list.iter()
550                                .map(|a| a.as_number())
551                                .collect::<Result<Vec<f64>>>()?
552                        }
553                        _ => vec![args[0].as_number()?],
554                    }
555                } else {
556                    args.into_iter()
557                        .map(|a| a.as_number())
558                        .collect::<Result<Vec<f64>>>()?
559                };
560
561                if nums.is_empty() {
562                    return Err(anyhow!("median requires at least one number"));
563                }
564
565                nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
566                let len = nums.len();
567                if len % 2 == 0 {
568                    Ok(Value::Number((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
569                } else {
570                    Ok(Value::Number(nums[len / 2]))
571                }
572            }
573
574            Self::Percentile => {
575                let p = args[1].as_number()?;
576                let heap = &heap.borrow();
577                let list = args[0].as_list(heap)?;
578
579                if !(0.0..=100.0).contains(&p) {
580                    return Err(anyhow!("percentile must be between 0 and 100"));
581                }
582
583                let mut nums = list
584                    .iter()
585                    .map(|a| a.as_number())
586                    .collect::<Result<Vec<f64>>>()?;
587
588                nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
589                let index = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
590
591                Ok(Value::Number(nums[index]))
592            }
593
594            // List functions
595            Self::Len => match &args[0] {
596                Value::List(l) => Ok(Value::Number(
597                    l.reify(&heap.borrow()).as_list()?.len() as f64
598                )),
599                Value::String(s) => Ok(Value::Number(
600                    s.reify(&heap.borrow()).as_string()?.len() as f64
601                )),
602                _ => Err(anyhow!("argument must be a list or string")),
603            },
604
605            Self::Head => match &args[0] {
606                Value::List(p) => Ok(p
607                    .reify(&heap.borrow())
608                    .as_list()?
609                    .first()
610                    .copied()
611                    .unwrap_or(Value::Null)),
612                Value::String(p) => {
613                    let val = {
614                        p.reify(&heap.borrow())
615                            .as_string()?
616                            .get(0..1)
617                            .unwrap_or("")
618                            .to_string()
619                    };
620
621                    Ok(heap.borrow_mut().insert_string(val))
622                }
623                _ => Err(anyhow!("argument must be a list or string")),
624            },
625
626            Self::Tail => match &args[0] {
627                Value::List(p) => {
628                    let val = {
629                        p.reify(&heap.borrow())
630                            .as_list()?
631                            .get(1..)
632                            .unwrap_or([].as_slice())
633                            .to_vec()
634                    };
635
636                    Ok(heap.borrow_mut().insert_list(val))
637                }
638                Value::String(s) => {
639                    let val = {
640                        s.reify(&heap.borrow())
641                            .as_string()?
642                            .get(1..)
643                            .unwrap_or("")
644                            .to_string()
645                    };
646
647                    Ok(heap.borrow_mut().insert_string(val))
648                }
649                _ => Err(anyhow!("argument must be a list or string")),
650            },
651
652            Self::Slice => {
653                let start = args[1].as_number()? as usize;
654                let end = args[2].as_number()? as usize;
655
656                match args[0] {
657                    Value::List(_) => {
658                        let l = {
659                            let borrowed_heap = &heap.borrow();
660                            args[0].as_list(borrowed_heap)?.clone()
661                        };
662
663                        l.get(start..end)
664                            .map_or(Err(anyhow!("index out of bounds")), |l| {
665                                Ok(heap.borrow_mut().insert_list(l.to_vec()))
666                            })
667                    }
668                    Value::String(_) => {
669                        let s = {
670                            let borrowed_heap = &heap.borrow();
671                            args[0].as_string(borrowed_heap)?.to_string()
672                        };
673
674                        s.get(start..end)
675                            .map_or(Err(anyhow!("index out of bounds")), |s| {
676                                Ok(heap.borrow_mut().insert_string(s.to_string()))
677                            })
678                    }
679                    _ => Err(anyhow!("argument must be a list or string")),
680                }
681            }
682
683            Self::Concat => {
684                let mut list = vec![];
685
686                for arg in args {
687                    match arg {
688                        Value::List(p) => list.extend(p.reify(&heap.borrow()).as_list()?.clone()),
689                        Value::Spread(IterablePointer::List(p)) => {
690                            list.extend(p.reify(&heap.borrow()).as_list()?.clone())
691                        }
692                        Value::Spread(IterablePointer::String(p)) => {
693                            let string = {
694                                let borrowed_heap = &heap.borrow();
695                                p.reify(borrowed_heap).as_string()?.to_string()
696                            };
697
698                            list.extend(
699                                string
700                                    .chars()
701                                    .map(|c| heap.borrow_mut().insert_string(c.to_string())),
702                            );
703                        }
704                        _ => list.push(arg),
705                    }
706                }
707
708                Ok(heap.borrow_mut().insert_list(list))
709            }
710
711            Self::Dot => {
712                let (a, b) = {
713                    let borrowed_heap = &heap.borrow();
714                    (
715                        args[0].as_list(borrowed_heap)?.clone(),
716                        args[1].as_list(borrowed_heap)?.clone(),
717                    )
718                };
719
720                if a.len() != b.len() {
721                    return Err(anyhow!(
722                        "cannot calculate dot product of lists with different lengths"
723                    ));
724                }
725
726                Ok(Value::Number(
727                    a.iter()
728                        .zip(b.iter())
729                        .map(|(a, b)| {
730                            let a_num = a.as_number()?;
731                            let b_num = b.as_number()?;
732                            Ok(a_num * b_num)
733                        })
734                        .collect::<Result<Vec<f64>>>()?
735                        .iter()
736                        .sum(),
737                ))
738            }
739
740            Self::Unique => {
741                let list = {
742                    let borrowed_heap = &heap.borrow();
743                    args[0].as_list(borrowed_heap)?.clone()
744                };
745                let mut unique_list = vec![];
746                let borrowed_heap = heap.borrow();
747
748                for item in list {
749                    let mut is_duplicate = false;
750                    for existing in &unique_list {
751                        if item.equals(existing, &borrowed_heap)? {
752                            is_duplicate = true;
753                            break;
754                        }
755                    }
756                    if !is_duplicate {
757                        unique_list.push(item);
758                    }
759                }
760
761                drop(borrowed_heap);
762                Ok(heap.borrow_mut().insert_list(unique_list))
763            }
764
765            Self::Sort => {
766                let mut list = {
767                    let borrowed_heap = &heap.borrow();
768                    args[0].as_list(borrowed_heap)?.clone()
769                };
770                let borrowed_heap = heap.borrow();
771                list.sort_by(|a, b| {
772                    a.compare(b, &borrowed_heap)
773                        .unwrap_or(None)
774                        .unwrap_or(std::cmp::Ordering::Equal)
775                });
776                drop(borrowed_heap);
777                Ok(heap.borrow_mut().insert_list(list))
778            }
779
780            Self::Reverse => {
781                let mut list = { args[0].as_list(&heap.borrow())?.clone() };
782                list.reverse();
783                Ok(heap.borrow_mut().insert_list(list))
784            }
785
786            Self::Any => {
787                let list = args[0].as_list(&heap.borrow())?.clone();
788                Ok(Value::Bool(
789                    list.iter().any(|v| v.as_bool().unwrap_or(false)),
790                ))
791            }
792
793            Self::All => {
794                let list = args[0].as_list(&heap.borrow())?.clone();
795                Ok(Value::Bool(
796                    list.iter().all(|v| v.as_bool().unwrap_or(false)),
797                ))
798            }
799
800            // String functions
801            Self::Split => {
802                let (s, delimeter) = {
803                    let borrowed_heap = &heap.borrow();
804                    (
805                        args[0].as_string(borrowed_heap)?.to_string(),
806                        args[1].as_string(borrowed_heap)?.to_string(),
807                    )
808                };
809
810                let list = s
811                    .split(&delimeter)
812                    .map(|s| heap.borrow_mut().insert_string(s.to_string()))
813                    .collect();
814
815                Ok(heap.borrow_mut().insert_list(list))
816            }
817
818            Self::Join => {
819                let joined_string = {
820                    let borrowed_heap = &heap.borrow();
821                    let delimeter = args[1].as_string(borrowed_heap)?;
822                    let list = args[0].as_list(borrowed_heap)?;
823                    list.iter()
824                        .map(|v| v.stringify_internal(borrowed_heap))
825                        .collect::<Vec<String>>()
826                        .join(delimeter)
827                };
828
829                Ok(heap.borrow_mut().insert_string(joined_string))
830            }
831
832            Self::Replace => {
833                let string = {
834                    let borrowed_heap = heap.borrow();
835                    let old = args[1].as_string(&borrowed_heap)?.to_string();
836                    let new = args[2].as_string(&borrowed_heap)?.to_string();
837                    let s = args[0].as_string(&borrowed_heap)?.to_string();
838                    s.replace(&old, &new)
839                };
840
841                Ok(heap.borrow_mut().insert_string(string))
842            }
843
844            Self::Trim => {
845                let string = {
846                    let borrowed_heap = heap.borrow();
847                    args[0].as_string(&borrowed_heap)?.trim().to_string()
848                };
849
850                Ok(heap.borrow_mut().insert_string(string))
851            }
852
853            Self::Uppercase => {
854                let string = args[0].as_string(&heap.borrow())?.to_uppercase();
855                Ok(heap.borrow_mut().insert_string(string))
856            }
857
858            Self::Lowercase => {
859                let string = args[0].as_string(&heap.borrow())?.to_lowercase();
860                Ok(heap.borrow_mut().insert_string(string))
861            }
862
863            Self::Includes => {
864                match &args[0].reify(&heap.borrow())? {
865                    // If haystack is a list, check for structural equality with any-type needle
866                    ReifiedValue::List(l, _) => {
867                        let borrowed_heap = heap.borrow();
868                        for item in (*l).iter() {
869                            if item.equals(&args[1], &borrowed_heap)? {
870                                return Ok(Value::Bool(true));
871                            }
872                        }
873                        Ok(Value::Bool(false))
874                    }
875                    // If haystack is a string, require needle to be a string and do substring search
876                    ReifiedValue::String(s, _) => {
877                        let needle = {
878                            let borrowed_heap = &heap.borrow();
879                            args[1]
880                                .as_string(borrowed_heap)
881                                .map_err(|_| anyhow!("second argument must be a string"))?
882                                .to_string()
883                        };
884                        Ok(Value::Bool(s.contains(&needle)))
885                    }
886                    _ => Err(anyhow!("first argument must be a list or string")),
887                }
888            }
889
890            Self::Format => {
891                let format_str = {
892                    let borrowed_heap = &heap.borrow();
893                    args[0]
894                        .as_string(borrowed_heap)
895                        .map_err(|_| anyhow!("first argument must be a string"))?
896                        .to_string()
897                };
898
899                let format_args = {
900                    let borrowed_heap = &heap.borrow();
901                    &args[1..]
902                        .iter()
903                        .map(|v| v.stringify_for_display(borrowed_heap))
904                        .collect::<Vec<String>>()
905                };
906
907                Ok(heap
908                    .borrow_mut()
909                    .insert_string(format_str.format(format_args)))
910            }
911
912            // Type functions
913            Self::Typeof => Ok(heap
914                .borrow_mut()
915                .insert_string(args[0].get_type().to_string())),
916
917            Self::Arity => {
918                let arity = match args[0] {
919                    Value::Lambda(p) => p.reify(&heap.borrow()).as_lambda()?.get_arity(),
920                    Value::BuiltIn(built_in) => built_in.arity(),
921                    _ => return Err(anyhow!("argument must be a function or built-in function")),
922                };
923
924                Ok(Value::Number(match arity {
925                    FunctionArity::Exact(n) => n as f64,
926                    FunctionArity::AtLeast(n) => n as f64,
927                    FunctionArity::Between(min, _max) => min as f64,
928                }))
929            }
930
931            // Record functions
932            Self::Keys => {
933                let record = args[0].as_record(&heap.borrow())?.clone();
934                let keys = {
935                    let key_strings = record.keys().cloned().collect::<Vec<String>>();
936                    key_strings
937                        .iter()
938                        .map(|k| heap.borrow_mut().insert_string(k.to_string()))
939                        .collect()
940                };
941
942                Ok(heap.borrow_mut().insert_list(keys))
943            }
944
945            Self::Values => {
946                let record = args[0].as_record(&heap.borrow())?.clone();
947                let values = record.values().cloned().collect();
948
949                Ok(heap.borrow_mut().insert_list(values))
950            }
951
952            Self::Entries => {
953                let record = args[0].as_record(&heap.borrow())?.clone();
954                let entries = record
955                    .iter()
956                    .map(|(k, v)| {
957                        let entry = {
958                            let mut borrowed_heap = heap.borrow_mut();
959                            vec![borrowed_heap.insert_string(k.to_string()), *v]
960                        };
961                        heap.borrow_mut().insert_list(entry)
962                    })
963                    .collect();
964
965                Ok(heap.borrow_mut().insert_list(entries))
966            }
967
968            // Conversion functions
969            Self::ToString => match args[0] {
970                Value::String(_) => Ok(args[0]), // If it's already a string, just return it
971                _ => {
972                    let string = args[0].stringify_internal(&heap.borrow());
973                    Ok(heap.borrow_mut().insert_string(string))
974                }
975            },
976
977            Self::ToNumber => match args[0] {
978                Value::Number(_) => Ok(args[0]), // If it's already a number, just return it
979                Value::Bool(b) => Ok(Value::Number(if b { 1.0 } else { 0.0 })),
980                _ => Ok(Value::Number(args[0].as_string(&heap.borrow())?.parse()?)),
981            },
982
983            Self::ToBool => match args[0] {
984                Value::Bool(_) => Ok(args[0]), // If it's already a boolean, just return it
985                Value::Number(_) => Ok(Value::Bool(args[0].as_number()? != 0.0)),
986                _ => Err(anyhow!(
987                    "expected a boolean or number, but got a {}",
988                    args[0].get_type()
989                )),
990            },
991
992            Self::Convert => {
993                let value = args[0].as_number()?;
994                let from_unit = {
995                    let borrowed_heap = &heap.borrow();
996                    args[1].as_string(borrowed_heap)?.to_string()
997                };
998                let to_unit = {
999                    let borrowed_heap = &heap.borrow();
1000                    args[2].as_string(borrowed_heap)?.to_string()
1001                };
1002
1003                let result = units::convert(value, &from_unit, &to_unit)?;
1004                Ok(Value::Number(result))
1005            }
1006
1007            // Platform-specific functions
1008            #[cfg(not(target_arch = "wasm32"))]
1009            Self::Print => {
1010                let borrowed_heap = &heap.borrow();
1011
1012                let output = if args.len() == 1 {
1013                    args[0].stringify_internal(borrowed_heap)
1014                } else {
1015                    let format_str = args[0].as_string(borrowed_heap).map_err(|_| {
1016                        anyhow!("first argument must be a formatting string if multiple arguments are given")
1017                    })?;
1018                    let format_args = &args[1..]
1019                        .iter()
1020                        .map(|v| v.stringify_internal(borrowed_heap))
1021                        .collect::<Vec<String>>();
1022                    format_str.format(format_args)
1023                };
1024
1025                // Print to stderr, to not pollute stdout
1026                eprintln!("{}", output);
1027
1028                Ok(Value::Null)
1029            }
1030
1031            #[cfg(not(target_arch = "wasm32"))]
1032            Self::TimeNow => Ok(Value::Number(
1033                std::time::SystemTime::now()
1034                    .duration_since(std::time::UNIX_EPOCH)
1035                    .unwrap()
1036                    .as_secs_f64(),
1037            )),
1038
1039            // Higher-order functions
1040            Self::Map => {
1041                let func = &args[1];
1042                let (list, func_def, func_accepts_two_args) = {
1043                    let borrowed_heap = &heap.borrow();
1044                    let list = args[0].as_list(borrowed_heap)?.clone();
1045                    let func_def = get_function_def(func, borrowed_heap)
1046                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1047
1048                    // Check if the function can accept 2 arguments (element, index)
1049                    let accepts_two = func_def.arity().can_accept(2);
1050
1051                    (list, func_def, accepts_two)
1052                };
1053
1054                let mut mapped_list = vec![];
1055                for (idx, item) in list.iter().enumerate() {
1056                    let args = if func_accepts_two_args {
1057                        vec![*item, Value::Number(idx as f64)]
1058                    } else {
1059                        vec![*item]
1060                    };
1061
1062                    let result = func_def.call(
1063                        Value::Null,
1064                        args,
1065                        Rc::clone(&heap),
1066                        Rc::clone(&bindings),
1067                        call_depth + 1,
1068                        source,
1069                    )?;
1070                    mapped_list.push(result);
1071                }
1072
1073                Ok(heap.borrow_mut().insert_list(mapped_list))
1074            }
1075
1076            Self::Filter => {
1077                let func = &args[1];
1078                let (list, func_def, func_accepts_two_args) = {
1079                    let borrowed_heap = &heap.borrow();
1080                    let list = args[0].as_list(borrowed_heap)?.clone();
1081                    let func_def = get_function_def(func, borrowed_heap)
1082                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1083
1084                    // Check if the function can accept 2 arguments (element, index)
1085                    let accepts_two = func_def.arity().can_accept(2);
1086
1087                    (list, func_def, accepts_two)
1088                };
1089
1090                let mut filtered_list = vec![];
1091                for (idx, item) in list.iter().enumerate() {
1092                    let args = if func_accepts_two_args {
1093                        vec![*item, Value::Number(idx as f64)]
1094                    } else {
1095                        vec![*item]
1096                    };
1097
1098                    let result = func_def.call(
1099                        Value::Null,
1100                        args,
1101                        Rc::clone(&heap),
1102                        Rc::clone(&bindings),
1103                        call_depth + 1,
1104                        source,
1105                    )?;
1106                    if result.as_bool()? {
1107                        filtered_list.push(*item);
1108                    }
1109                }
1110
1111                Ok(heap.borrow_mut().insert_list(filtered_list))
1112            }
1113
1114            Self::Reduce => {
1115                let func = &args[1];
1116                let initial = args[2];
1117                let (list, func_def, func_accepts_three_args) = {
1118                    let borrowed_heap = &heap.borrow();
1119                    let list = args[0].as_list(borrowed_heap)?.clone();
1120                    let func_def = get_function_def(func, borrowed_heap)
1121                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1122
1123                    // Check if the function can accept 3 arguments (accumulator, element, index)
1124                    let accepts_three = func_def.arity().can_accept(3);
1125
1126                    (list, func_def, accepts_three)
1127                };
1128
1129                let mut accumulator = initial;
1130                for (idx, item) in list.iter().enumerate() {
1131                    let args = if func_accepts_three_args {
1132                        vec![accumulator, *item, Value::Number(idx as f64)]
1133                    } else {
1134                        vec![accumulator, *item]
1135                    };
1136
1137                    accumulator = func_def.call(
1138                        Value::Null,
1139                        args,
1140                        Rc::clone(&heap),
1141                        Rc::clone(&bindings),
1142                        call_depth + 1,
1143                        source,
1144                    )?;
1145                }
1146
1147                Ok(accumulator)
1148            }
1149
1150            Self::Every => {
1151                let func = &args[1];
1152                let (list, func_def, func_accepts_two_args) = {
1153                    let borrowed_heap = &heap.borrow();
1154                    let list = args[0].as_list(borrowed_heap)?.clone();
1155                    let func_def = get_function_def(func, borrowed_heap)
1156                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1157
1158                    // Check if the function can accept 2 arguments (element, index)
1159                    let accepts_two = func_def.arity().can_accept(2);
1160
1161                    (list, func_def, accepts_two)
1162                };
1163
1164                for (idx, item) in list.iter().enumerate() {
1165                    let args = if func_accepts_two_args {
1166                        vec![*item, Value::Number(idx as f64)]
1167                    } else {
1168                        vec![*item]
1169                    };
1170
1171                    let result = func_def.call(
1172                        Value::Null,
1173                        args,
1174                        Rc::clone(&heap),
1175                        Rc::clone(&bindings),
1176                        call_depth + 1,
1177                        source,
1178                    )?;
1179                    if !result.as_bool()? {
1180                        return Ok(Value::Bool(false));
1181                    }
1182                }
1183
1184                Ok(Value::Bool(true))
1185            }
1186
1187            Self::Some => {
1188                let func = &args[1];
1189                let (list, func_def, func_accepts_two_args) = {
1190                    let borrowed_heap = &heap.borrow();
1191                    let list = args[0].as_list(borrowed_heap)?.clone();
1192                    let func_def = get_function_def(func, borrowed_heap)
1193                        .ok_or_else(|| anyhow!("second argument must be a function"))?;
1194
1195                    // Check if the function can accept 2 arguments (element, index)
1196                    let accepts_two = func_def.arity().can_accept(2);
1197
1198                    (list, func_def, accepts_two)
1199                };
1200
1201                for (idx, item) in list.iter().enumerate() {
1202                    let args = if func_accepts_two_args {
1203                        vec![*item, Value::Number(idx as f64)]
1204                    } else {
1205                        vec![*item]
1206                    };
1207
1208                    let result = func_def.call(
1209                        Value::Null,
1210                        args,
1211                        Rc::clone(&heap),
1212                        Rc::clone(&bindings),
1213                        call_depth + 1,
1214                        source,
1215                    )?;
1216                    if result.as_bool()? {
1217                        return Ok(Value::Bool(true));
1218                    }
1219                }
1220
1221                Ok(Value::Bool(false))
1222            }
1223
1224            Self::SortBy => {
1225                let func = &args[1];
1226                let mut list = {
1227                    let borrowed_heap = &heap.borrow();
1228                    args[0].as_list(borrowed_heap)?.clone()
1229                };
1230
1231                list.sort_by(|a, b| {
1232                    // Only look up the function once, not twice
1233                    let func_def = get_function_def(func, &heap.borrow());
1234
1235                    match func_def {
1236                        Some(fd) => {
1237                            let result_a = fd.call(
1238                                Value::Null,
1239                                vec![*a],
1240                                Rc::clone(&heap),
1241                                Rc::clone(&bindings),
1242                                call_depth + 1,
1243                                source,
1244                            );
1245                            let result_b = fd.call(
1246                                Value::Null,
1247                                vec![*b],
1248                                Rc::clone(&heap),
1249                                Rc::clone(&bindings),
1250                                call_depth + 1,
1251                                source,
1252                            );
1253
1254                            match (result_a, result_b) {
1255                                (Ok(val_a), Ok(val_b)) => val_a
1256                                    .compare(&val_b, &heap.borrow())
1257                                    .unwrap_or(None)
1258                                    .unwrap_or(std::cmp::Ordering::Equal),
1259                                _ => std::cmp::Ordering::Equal,
1260                            }
1261                        }
1262                        _ => std::cmp::Ordering::Equal,
1263                    }
1264                });
1265
1266                Ok(heap.borrow_mut().insert_list(list))
1267            }
1268        }
1269    }
1270
1271    pub fn all() -> Vec<Self> {
1272        vec![
1273            Self::Sqrt,
1274            Self::Sin,
1275            Self::Cos,
1276            Self::Tan,
1277            Self::Asin,
1278            Self::Acos,
1279            Self::Atan,
1280            Self::Log,
1281            Self::Log10,
1282            Self::Exp,
1283            Self::Abs,
1284            Self::Floor,
1285            Self::Ceil,
1286            Self::Round,
1287            Self::Trunc,
1288            Self::Random,
1289            Self::Min,
1290            Self::Max,
1291            Self::Avg,
1292            Self::Sum,
1293            Self::Prod,
1294            Self::Median,
1295            Self::Percentile,
1296            Self::Range,
1297            Self::Len,
1298            Self::Head,
1299            Self::Tail,
1300            Self::Slice,
1301            Self::Concat,
1302            Self::Dot,
1303            Self::Unique,
1304            Self::Sort,
1305            Self::SortBy,
1306            Self::Reverse,
1307            Self::Any,
1308            Self::All,
1309            Self::Map,
1310            Self::Reduce,
1311            Self::Filter,
1312            Self::Every,
1313            Self::Some,
1314            Self::Split,
1315            Self::Join,
1316            Self::Replace,
1317            Self::Trim,
1318            Self::Uppercase,
1319            Self::Lowercase,
1320            Self::ToString,
1321            Self::ToNumber,
1322            Self::ToBool,
1323            Self::Convert,
1324            Self::Includes,
1325            Self::Format,
1326            Self::Typeof,
1327            Self::Arity,
1328            Self::Keys,
1329            Self::Values,
1330            Self::Entries,
1331            #[cfg(not(target_arch = "wasm32"))]
1332            Self::Print,
1333            #[cfg(not(target_arch = "wasm32"))]
1334            Self::TimeNow,
1335        ]
1336    }
1337
1338    pub fn all_names() -> Vec<&'static str> {
1339        Self::all().iter().map(|f| f.name()).collect()
1340    }
1341}
1342
1343impl FunctionDef {
1344    pub fn get_name(&self) -> String {
1345        match self {
1346            FunctionDef::BuiltIn(built_in) => {
1347                format!("built-in function \"{}\"", built_in.name())
1348            }
1349            FunctionDef::Lambda(LambdaDef { name, .. }) => name
1350                .clone()
1351                .map_or(String::from("anonymous function"), |n| {
1352                    format!("function \"{}\"", n)
1353                }),
1354        }
1355    }
1356
1357    pub fn arity(&self) -> FunctionArity {
1358        match self {
1359            FunctionDef::BuiltIn(built_in) => built_in.arity(),
1360            FunctionDef::Lambda(lambda_def) => lambda_def.get_arity(),
1361        }
1362    }
1363
1364    pub fn check_arity(&self, arg_count: usize) -> Result<()> {
1365        match self {
1366            FunctionDef::BuiltIn(built_in) => match built_in.arity() {
1367                FunctionArity::Exact(expected) => {
1368                    if arg_count == expected {
1369                        Ok(())
1370                    } else {
1371                        Err(anyhow!(
1372                            "{} takes exactly {} arguments, but {} were given",
1373                            self.get_name(),
1374                            expected,
1375                            arg_count
1376                        ))
1377                    }
1378                }
1379                FunctionArity::AtLeast(expected) => {
1380                    if arg_count >= expected {
1381                        Ok(())
1382                    } else {
1383                        Err(anyhow!(
1384                            "{} takes at least {} arguments, but {} were given",
1385                            self.get_name(),
1386                            expected,
1387                            arg_count
1388                        ))
1389                    }
1390                }
1391                FunctionArity::Between(min, max) => {
1392                    if arg_count >= min && arg_count <= max {
1393                        Ok(())
1394                    } else {
1395                        Err(anyhow!(
1396                            "{} takes between {} and {} arguments, but {} were given",
1397                            self.get_name(),
1398                            min,
1399                            max,
1400                            arg_count
1401                        ))
1402                    }
1403                }
1404            },
1405            FunctionDef::Lambda(def) => {
1406                let arity = def.get_arity();
1407
1408                match arity {
1409                    FunctionArity::Exact(expected) => {
1410                        if arg_count == expected {
1411                            Ok(())
1412                        } else {
1413                            Err(anyhow!(
1414                                "{} takes exactly {} arguments, but {} were given",
1415                                self.get_name(),
1416                                expected,
1417                                arg_count
1418                            ))
1419                        }
1420                    }
1421                    FunctionArity::AtLeast(expected) => {
1422                        if arg_count >= expected {
1423                            Ok(())
1424                        } else {
1425                            Err(anyhow!(
1426                                "{} takes at least {} arguments, but {} were given",
1427                                self.get_name(),
1428                                expected,
1429                                arg_count
1430                            ))
1431                        }
1432                    }
1433                    FunctionArity::Between(min, max) => {
1434                        if arg_count >= min && arg_count <= max {
1435                            Ok(())
1436                        } else {
1437                            Err(anyhow!(
1438                                "{} takes between {} and {} arguments, but {} were given",
1439                                self.get_name(),
1440                                min,
1441                                max,
1442                                arg_count
1443                            ))
1444                        }
1445                    }
1446                }
1447            }
1448        }
1449    }
1450
1451    pub fn call(
1452        &self,
1453        this_value: Value,
1454        args: Vec<Value>,
1455        heap: Rc<RefCell<Heap>>,
1456        bindings: Rc<Environment>,
1457        call_depth: usize,
1458        source: &str,
1459    ) -> Result<Value> {
1460        #[cfg(not(target_arch = "wasm32"))]
1461        let start = std::time::Instant::now();
1462
1463        self.check_arity(args.len())?;
1464
1465        if call_depth > 1000 {
1466            return Err(anyhow!(
1467                "in {}: maximum call depth of 1000 exceeded",
1468                self.get_name()
1469            ));
1470        }
1471
1472        match self {
1473            FunctionDef::Lambda(LambdaDef {
1474                name,
1475                args: expected_args,
1476                body,
1477                scope,
1478                source: lambda_source,
1479            }) => {
1480                #[cfg(not(target_arch = "wasm32"))]
1481                let start_var_env = std::time::Instant::now();
1482
1483                // Build local bindings for this call (O(1) - no clone of parent environment!)
1484                let mut local_bindings = HashMap::new();
1485
1486                // Add captured scope (captured variables take precedence over parent env)
1487                for (key, value) in scope {
1488                    local_bindings.insert(key.clone(), *value);
1489                }
1490
1491                // Add self-reference if named
1492                if let Some(fn_name) = name {
1493                    local_bindings.insert(fn_name.clone(), this_value);
1494                }
1495
1496                // Preserve inputs if present in parent
1497                if let Some(inputs) = bindings.get("inputs") {
1498                    local_bindings.insert(String::from("inputs"), inputs);
1499                }
1500
1501                // Add function arguments (highest precedence)
1502                for (idx, expected_arg) in expected_args.iter().enumerate() {
1503                    match expected_arg {
1504                        LambdaArg::Required(arg_name) => {
1505                            local_bindings.insert(arg_name.clone(), args[idx]);
1506                        }
1507                        LambdaArg::Optional(arg_name) => {
1508                            local_bindings.insert(
1509                                arg_name.clone(),
1510                                args.get(idx).copied().unwrap_or(Value::Null),
1511                            );
1512                        }
1513                        LambdaArg::Rest(arg_name) => {
1514                            local_bindings.insert(
1515                                arg_name.clone(),
1516                                heap.borrow_mut()
1517                                    .insert_list(args.iter().skip(idx).copied().collect()),
1518                            );
1519                        }
1520                    }
1521                }
1522
1523                #[cfg(not(target_arch = "wasm32"))]
1524                let end_var_env = std::time::Instant::now();
1525
1526                // Create new environment that extends parent (O(1) instead of O(n) clone!)
1527                let new_env = Rc::new(Environment::extend_with(Rc::clone(&bindings), local_bindings));
1528
1529                let return_value = evaluate_ast(
1530                    body,
1531                    Rc::clone(&heap),
1532                    new_env,
1533                    call_depth + 1,
1534                    lambda_source.clone(),
1535                )
1536                .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1537
1538                #[cfg(not(target_arch = "wasm32"))]
1539                FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1540                    name: self.get_name(),
1541                    start,
1542                    end: std::time::Instant::now(),
1543                    start_var_env: Some(start_var_env),
1544                    end_var_env: Some(end_var_env),
1545                });
1546
1547                return_value
1548            }
1549            FunctionDef::BuiltIn(built_in) => {
1550                let return_value = built_in
1551                    .call(args, heap, bindings, call_depth + 1, source)
1552                    .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1553
1554                #[cfg(not(target_arch = "wasm32"))]
1555                FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1556                    name: self.get_name(),
1557                    start,
1558                    end: std::time::Instant::now(),
1559                    start_var_env: None,
1560                    end_var_env: None,
1561                });
1562
1563                return_value
1564            }
1565        }
1566    }
1567}
1568
1569pub fn is_built_in_function(ident: &str) -> bool {
1570    BuiltInFunction::from_ident(ident).is_some()
1571}
1572
1573pub fn get_built_in_function_def_by_ident(ident: &str) -> Option<FunctionDef> {
1574    BuiltInFunction::from_ident(ident).map(FunctionDef::BuiltIn)
1575}
1576
1577pub fn get_built_in_function_idents() -> Vec<&'static str> {
1578    BuiltInFunction::all_names()
1579}
1580
1581pub fn get_function_def(value: &Value, heap: &Heap) -> Option<FunctionDef> {
1582    match value {
1583        Value::Lambda(pointer) => Some(FunctionDef::Lambda(
1584            pointer.reify(heap).as_lambda().ok()?.clone(),
1585        )),
1586        Value::BuiltIn(built_in) => Some(FunctionDef::BuiltIn(*built_in)),
1587        _ => None,
1588    }
1589}
1590
1591#[cfg(test)]
1592mod tests {
1593    use super::*;
1594    use crate::environment::Environment;
1595    use std::cell::RefCell;
1596    use std::rc::Rc;
1597
1598    #[test]
1599    fn test_range_function() {
1600        // Test single argument
1601        let heap = Rc::new(RefCell::new(Heap::new()));
1602        let bindings = Rc::new(Environment::new());
1603        let range_fn = BuiltInFunction::Range;
1604
1605        // Test range(4) - exclusive, so [0, 1, 2, 3]
1606        let args = vec![Value::Number(4.0)];
1607        let result = range_fn
1608            .call(args, heap.clone(), bindings.clone(), 0, "")
1609            .unwrap();
1610
1611        let heap_borrow = heap.borrow();
1612        let list = result.as_list(&heap_borrow).unwrap();
1613        assert_eq!(list.len(), 4);
1614        assert_eq!(list[0], Value::Number(0.0));
1615        assert_eq!(list[3], Value::Number(3.0));
1616    }
1617
1618    #[test]
1619    fn test_range_function_two_args() {
1620        // Test two arguments
1621        let heap = Rc::new(RefCell::new(Heap::new()));
1622        let bindings = Rc::new(Environment::new());
1623        let range_fn = BuiltInFunction::Range;
1624
1625        // Test range(4, 10) - exclusive, so [4, 5, 6, 7, 8, 9]
1626        let args = vec![Value::Number(4.0), Value::Number(10.0)];
1627        let result = range_fn
1628            .call(args, heap.clone(), bindings.clone(), 0, "")
1629            .unwrap();
1630
1631        let heap_borrow = heap.borrow();
1632        let list = result.as_list(&heap_borrow).unwrap();
1633        assert_eq!(list.len(), 6);
1634        assert_eq!(list[0], Value::Number(4.0));
1635        assert_eq!(list[5], Value::Number(9.0));
1636    }
1637
1638    #[test]
1639    fn test_round_function_single_arg() {
1640        let heap = Rc::new(RefCell::new(Heap::new()));
1641        let bindings = Rc::new(Environment::new());
1642        let round_fn = BuiltInFunction::Round;
1643
1644        // Test basic rounding
1645        let test_cases = vec![
1646            (42.4, 42.0),
1647            (42.5, 43.0),
1648            (42.6, 43.0),
1649            (-42.4, -42.0),
1650            (-42.5, -43.0),
1651            (-42.6, -43.0),
1652            (0.0, 0.0),
1653            (1.999, 2.0),
1654            (-1.999, -2.0),
1655        ];
1656
1657        for (input, expected) in test_cases {
1658            let args = vec![Value::Number(input)];
1659            let result = round_fn
1660                .call(args, heap.clone(), bindings.clone(), 0, "")
1661                .unwrap();
1662            assert_eq!(
1663                result,
1664                Value::Number(expected),
1665                "round({}) should be {}",
1666                input,
1667                expected
1668            );
1669        }
1670    }
1671
1672    #[test]
1673    fn test_round_function_with_decimal_places() {
1674        let heap = Rc::new(RefCell::new(Heap::new()));
1675        let bindings = Rc::new(Environment::new());
1676        let round_fn = BuiltInFunction::Round;
1677
1678        // Test rounding to decimal places
1679        let test_cases = vec![
1680            (42.4543, 0.0, 42.0),
1681            (42.4543, 1.0, 42.5),
1682            (42.4543, 2.0, 42.45),
1683            (42.4543, 3.0, 42.454),
1684            (42.4543, 4.0, 42.4543),
1685            (4.14159, 4.0, 4.1416),
1686            (0.005, 2.0, 0.01),
1687            (0.995, 2.0, 1.0),
1688            // Note: 9.995 has floating point representation issues
1689            // In binary, it's actually slightly less than 9.995
1690            (9.995, 2.0, 9.99),
1691            (-9.995, 2.0, -9.99),
1692        ];
1693
1694        for (input, places, expected) in test_cases {
1695            let args = vec![Value::Number(input), Value::Number(places)];
1696            let result = round_fn
1697                .call(args, heap.clone(), bindings.clone(), 0, "")
1698                .unwrap();
1699
1700            // Use approximate comparison for floating point
1701            if let Value::Number(result_num) = result {
1702                assert!(
1703                    (result_num - expected).abs() < 1e-10,
1704                    "round({}, {}) = {} should be close to {}",
1705                    input,
1706                    places,
1707                    result_num,
1708                    expected
1709                );
1710            } else {
1711                panic!("Expected number result");
1712            }
1713        }
1714    }
1715
1716    #[test]
1717    fn test_round_function_negative_decimal_places() {
1718        let heap = Rc::new(RefCell::new(Heap::new()));
1719        let bindings = Rc::new(Environment::new());
1720        let round_fn = BuiltInFunction::Round;
1721
1722        // Test rounding to tens, hundreds, etc.
1723        let test_cases = vec![
1724            (1234.567, -1.0, 1230.0),
1725            (1234.567, -2.0, 1200.0),
1726            (1234.567, -3.0, 1000.0),
1727            (1234.567, -4.0, 0.0),
1728            (5678.9, -1.0, 5680.0),
1729            (5678.9, -2.0, 5700.0),
1730            (5678.9, -3.0, 6000.0),
1731            (-1234.567, -1.0, -1230.0),
1732            (-1234.567, -2.0, -1200.0),
1733            (1500.0, -3.0, 2000.0),
1734            (-1500.0, -3.0, -2000.0),
1735        ];
1736
1737        for (input, places, expected) in test_cases {
1738            let args = vec![Value::Number(input), Value::Number(places)];
1739            let result = round_fn
1740                .call(args, heap.clone(), bindings.clone(), 0, "")
1741                .unwrap();
1742            assert_eq!(
1743                result,
1744                Value::Number(expected),
1745                "round({}, {}) should be {}",
1746                input,
1747                places,
1748                expected
1749            );
1750        }
1751    }
1752
1753    #[test]
1754    fn test_round_function_edge_cases() {
1755        let heap = Rc::new(RefCell::new(Heap::new()));
1756        let bindings = Rc::new(Environment::new());
1757        let round_fn = BuiltInFunction::Round;
1758
1759        // Test edge cases
1760        let test_cases = vec![
1761            (f64::INFINITY, 0.0, f64::INFINITY),
1762            (f64::NEG_INFINITY, 0.0, f64::NEG_INFINITY),
1763            (0.0, 5.0, 0.0),
1764            (-0.0, 5.0, -0.0),
1765            (1e-10, 5.0, 0.0),
1766            (1e-10, 15.0, 1e-10),
1767        ];
1768
1769        for (input, places, expected) in test_cases {
1770            let args = vec![Value::Number(input), Value::Number(places)];
1771            let result = round_fn
1772                .call(args, heap.clone(), bindings.clone(), 0, "")
1773                .unwrap();
1774
1775            if let Value::Number(result_num) = result {
1776                if expected.is_infinite() {
1777                    assert!(
1778                        result_num.is_infinite()
1779                            && result_num.is_sign_positive() == expected.is_sign_positive(),
1780                        "round({}, {}) should be {}",
1781                        input,
1782                        places,
1783                        expected
1784                    );
1785                } else if expected == 0.0 || expected == -0.0 {
1786                    assert!(
1787                        result_num.abs() < 1e-10,
1788                        "round({}, {}) = {} should be close to 0",
1789                        input,
1790                        places,
1791                        result_num
1792                    );
1793                } else {
1794                    assert!(
1795                        (result_num - expected).abs() < 1e-15,
1796                        "round({}, {}) = {} should be close to {}",
1797                        input,
1798                        places,
1799                        result_num,
1800                        expected
1801                    );
1802                }
1803            } else {
1804                panic!("Expected number result");
1805            }
1806        }
1807    }
1808
1809    #[test]
1810    fn test_random_function_deterministic() {
1811        let heap = Rc::new(RefCell::new(Heap::new()));
1812        let bindings = Rc::new(Environment::new());
1813        let random_fn = BuiltInFunction::Random;
1814
1815        // Test that same seed produces same result
1816        let seed = 42.0;
1817        let args1 = vec![Value::Number(seed)];
1818        let result1 = random_fn
1819            .call(args1, heap.clone(), bindings.clone(), 0, "")
1820            .unwrap();
1821
1822        let args2 = vec![Value::Number(seed)];
1823        let result2 = random_fn
1824            .call(args2, heap.clone(), bindings.clone(), 0, "")
1825            .unwrap();
1826
1827        assert_eq!(result1, result2, "Same seed should produce same result");
1828    }
1829
1830    #[test]
1831    fn test_random_function_different_seeds() {
1832        let heap = Rc::new(RefCell::new(Heap::new()));
1833        let bindings = Rc::new(Environment::new());
1834        let random_fn = BuiltInFunction::Random;
1835
1836        // Test that different seeds produce different results
1837        let args1 = vec![Value::Number(42.0)];
1838        let result1 = random_fn
1839            .call(args1, heap.clone(), bindings.clone(), 0, "")
1840            .unwrap();
1841
1842        let args2 = vec![Value::Number(100.0)];
1843        let result2 = random_fn
1844            .call(args2, heap.clone(), bindings.clone(), 0, "")
1845            .unwrap();
1846
1847        assert_ne!(result1, result2, "Different seeds should produce different results");
1848    }
1849
1850    #[test]
1851    fn test_random_function_range() {
1852        let heap = Rc::new(RefCell::new(Heap::new()));
1853        let bindings = Rc::new(Environment::new());
1854        let random_fn = BuiltInFunction::Random;
1855
1856        // Test that output is in range [0, 1)
1857        for seed in [0.0, 1.0, 42.0, 100.0, 999.0, 12345.0] {
1858            let args = vec![Value::Number(seed)];
1859            let result = random_fn
1860                .call(args, heap.clone(), bindings.clone(), 0, "")
1861                .unwrap();
1862
1863            if let Value::Number(num) = result {
1864                assert!(num >= 0.0 && num < 1.0, "Random value {} should be in range [0, 1)", num);
1865            } else {
1866                panic!("Expected number result");
1867            }
1868        }
1869    }
1870
1871    #[test]
1872    fn test_random_function_negative_seed() {
1873        let heap = Rc::new(RefCell::new(Heap::new()));
1874        let bindings = Rc::new(Environment::new());
1875        let random_fn = BuiltInFunction::Random;
1876
1877        // Test with negative seeds (they get cast to u64)
1878        let args = vec![Value::Number(-42.0)];
1879        let result = random_fn
1880            .call(args, heap.clone(), bindings.clone(), 0, "")
1881            .unwrap();
1882
1883        if let Value::Number(num) = result {
1884            assert!(num >= 0.0 && num < 1.0, "Random value {} should be in range [0, 1)", num);
1885        } else {
1886            panic!("Expected number result");
1887        }
1888    }
1889
1890    #[test]
1891    fn test_map_with_index() {
1892        use crate::values::LambdaDef;
1893
1894        let heap = Rc::new(RefCell::new(Heap::new()));
1895        let bindings = Rc::new(Environment::new());
1896
1897        // Create a list [10, 20, 30]
1898        let list = heap.borrow_mut().insert_list(vec![
1899            Value::Number(10.0),
1900            Value::Number(20.0),
1901            Value::Number(30.0),
1902        ]);
1903
1904        // Create a lambda (x, i) => x + i
1905        let lambda = LambdaDef {
1906            name: None,
1907            args: vec![
1908                crate::values::LambdaArg::Required("x".to_string()),
1909                crate::values::LambdaArg::Required("i".to_string()),
1910            ],
1911            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
1912                op: crate::ast::BinaryOp::Add,
1913                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
1914                    "x".to_string(),
1915                ))),
1916                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
1917                    "i".to_string(),
1918                ))),
1919            }),
1920            scope: HashMap::new(),
1921            source: Rc::from(""),
1922        };
1923        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
1924
1925        // Call map
1926        let result = BuiltInFunction::Map
1927            .call(
1928                vec![list, lambda_value],
1929                heap.clone(),
1930                bindings.clone(),
1931                0,
1932                "",
1933            )
1934            .unwrap();
1935
1936        // Verify result is [10, 21, 32]
1937        let heap_borrow = heap.borrow();
1938        let result_list = result.as_list(&heap_borrow).unwrap();
1939        assert_eq!(result_list.len(), 3);
1940        assert_eq!(result_list[0], Value::Number(10.0)); // 10 + 0
1941        assert_eq!(result_list[1], Value::Number(21.0)); // 20 + 1
1942        assert_eq!(result_list[2], Value::Number(32.0)); // 30 + 2
1943    }
1944
1945    #[test]
1946    fn test_filter_with_index() {
1947        use crate::values::LambdaDef;
1948
1949        let heap = Rc::new(RefCell::new(Heap::new()));
1950        let bindings = Rc::new(Environment::new());
1951
1952        // Create a list [10, 20, 30, 40]
1953        let list = heap.borrow_mut().insert_list(vec![
1954            Value::Number(10.0),
1955            Value::Number(20.0),
1956            Value::Number(30.0),
1957            Value::Number(40.0),
1958        ]);
1959
1960        // Create a lambda (x, i) => i > 1
1961        let lambda = LambdaDef {
1962            name: None,
1963            args: vec![
1964                crate::values::LambdaArg::Required("x".to_string()),
1965                crate::values::LambdaArg::Required("i".to_string()),
1966            ],
1967            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
1968                op: crate::ast::BinaryOp::Greater,
1969                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
1970                    "i".to_string(),
1971                ))),
1972                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Number(1.0))),
1973            }),
1974            scope: HashMap::new(),
1975            source: Rc::from(""),
1976        };
1977        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
1978
1979        // Call filter
1980        let result = BuiltInFunction::Filter
1981            .call(
1982                vec![list, lambda_value],
1983                heap.clone(),
1984                bindings.clone(),
1985                0,
1986                "",
1987            )
1988            .unwrap();
1989
1990        // Verify result is [30, 40] (indices 2 and 3)
1991        let heap_borrow = heap.borrow();
1992        let result_list = result.as_list(&heap_borrow).unwrap();
1993        assert_eq!(result_list.len(), 2);
1994        assert_eq!(result_list[0], Value::Number(30.0));
1995        assert_eq!(result_list[1], Value::Number(40.0));
1996    }
1997
1998    #[test]
1999    fn test_reduce_with_index() {
2000        use crate::values::LambdaDef;
2001
2002        let heap = Rc::new(RefCell::new(Heap::new()));
2003        let bindings = Rc::new(Environment::new());
2004
2005        // Create a list [10, 20, 30]
2006        let list = heap.borrow_mut().insert_list(vec![
2007            Value::Number(10.0),
2008            Value::Number(20.0),
2009            Value::Number(30.0),
2010        ]);
2011
2012        // Create a lambda (acc, x, i) => acc + x + i
2013        let lambda = LambdaDef {
2014            name: None,
2015            args: vec![
2016                crate::values::LambdaArg::Required("acc".to_string()),
2017                crate::values::LambdaArg::Required("x".to_string()),
2018                crate::values::LambdaArg::Required("i".to_string()),
2019            ],
2020            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2021                op: crate::ast::BinaryOp::Add,
2022                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2023                    op: crate::ast::BinaryOp::Add,
2024                    left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2025                        "acc".to_string(),
2026                    ))),
2027                    right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2028                        "x".to_string(),
2029                    ))),
2030                })),
2031                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2032                    "i".to_string(),
2033                ))),
2034            }),
2035            scope: HashMap::new(),
2036            source: Rc::from(""),
2037        };
2038        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2039
2040        // Call reduce with initial value 0
2041        let result = BuiltInFunction::Reduce
2042            .call(
2043                vec![list, lambda_value, Value::Number(0.0)],
2044                heap.clone(),
2045                bindings.clone(),
2046                0,
2047                "",
2048            )
2049            .unwrap();
2050
2051        // Verify result is 63 (0 + 10+0 + 20+1 + 30+2)
2052        assert_eq!(result, Value::Number(63.0));
2053    }
2054
2055    #[test]
2056    fn test_map_backward_compatible() {
2057        use crate::values::LambdaDef;
2058
2059        let heap = Rc::new(RefCell::new(Heap::new()));
2060        let bindings = Rc::new(Environment::new());
2061
2062        // Create a list [10, 20, 30]
2063        let list = heap.borrow_mut().insert_list(vec![
2064            Value::Number(10.0),
2065            Value::Number(20.0),
2066            Value::Number(30.0),
2067        ]);
2068
2069        // Create a lambda x => x * 2 (only one argument)
2070        let lambda = LambdaDef {
2071            name: None,
2072            args: vec![crate::values::LambdaArg::Required("x".to_string())],
2073            body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2074                op: crate::ast::BinaryOp::Multiply,
2075                left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2076                    "x".to_string(),
2077                ))),
2078                right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Number(2.0))),
2079            }),
2080            scope: HashMap::new(),
2081            source: Rc::from(""),
2082        };
2083        let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2084
2085        // Call map
2086        let result = BuiltInFunction::Map
2087            .call(
2088                vec![list, lambda_value],
2089                heap.clone(),
2090                bindings.clone(),
2091                0,
2092                "",
2093            )
2094            .unwrap();
2095
2096        // Verify result is [20, 40, 60] - backward compatible, no index passed
2097        let heap_borrow = heap.borrow();
2098        let result_list = result.as_list(&heap_borrow).unwrap();
2099        assert_eq!(result_list.len(), 3);
2100        assert_eq!(result_list[0], Value::Number(20.0));
2101        assert_eq!(result_list[1], Value::Number(40.0));
2102        assert_eq!(result_list[2], Value::Number(60.0));
2103    }
2104
2105    #[test]
2106    fn test_convert_length() {
2107        let heap = Rc::new(RefCell::new(Heap::new()));
2108        let bindings = Rc::new(Environment::new());
2109        let convert_fn = BuiltInFunction::Convert;
2110
2111        // Test km to m
2112        let from_unit = heap.borrow_mut().insert_string("km".to_string());
2113        let to_unit = heap.borrow_mut().insert_string("m".to_string());
2114        let args = vec![Value::Number(1.0), from_unit, to_unit];
2115        let result = convert_fn
2116            .call(args, heap.clone(), bindings.clone(), 0, "")
2117            .unwrap();
2118        assert_eq!(result, Value::Number(1000.0));
2119
2120        // Test miles to km
2121        let from_unit = heap.borrow_mut().insert_string("miles".to_string());
2122        let to_unit = heap.borrow_mut().insert_string("km".to_string());
2123        let args = vec![Value::Number(1.0), from_unit, to_unit];
2124        let result = convert_fn
2125            .call(args, heap.clone(), bindings.clone(), 0, "")
2126            .unwrap();
2127        if let Value::Number(n) = result {
2128            assert!((n - 1.609344).abs() < 1e-6);
2129        } else {
2130            panic!("Expected number");
2131        }
2132    }
2133
2134    #[test]
2135    fn test_convert_temperature() {
2136        let heap = Rc::new(RefCell::new(Heap::new()));
2137        let bindings = Rc::new(Environment::new());
2138        let convert_fn = BuiltInFunction::Convert;
2139
2140        // Test 0°C to °F (should be 32°F)
2141        let from_unit = heap.borrow_mut().insert_string("celsius".to_string());
2142        let to_unit = heap.borrow_mut().insert_string("fahrenheit".to_string());
2143        let args = vec![Value::Number(0.0), from_unit, to_unit];
2144        let result = convert_fn
2145            .call(args, heap.clone(), bindings.clone(), 0, "")
2146            .unwrap();
2147        if let Value::Number(n) = result {
2148            assert!((n - 32.0).abs() < 1e-10);
2149        }
2150
2151        // Test 100°C to °F (should be 212°F)
2152        let from_unit = heap.borrow_mut().insert_string("celsius".to_string());
2153        let to_unit = heap.borrow_mut().insert_string("fahrenheit".to_string());
2154        let args = vec![Value::Number(100.0), from_unit, to_unit];
2155        let result = convert_fn
2156            .call(args, heap.clone(), bindings.clone(), 0, "")
2157            .unwrap();
2158        if let Value::Number(n) = result {
2159            assert!((n - 212.0).abs() < 1e-10);
2160        }
2161    }
2162
2163    #[test]
2164    fn test_convert_mass() {
2165        let heap = Rc::new(RefCell::new(Heap::new()));
2166        let bindings = Rc::new(Environment::new());
2167        let convert_fn = BuiltInFunction::Convert;
2168
2169        // Test 1 kg to lbs
2170        let from_unit = heap.borrow_mut().insert_string("kg".to_string());
2171        let to_unit = heap.borrow_mut().insert_string("lbs".to_string());
2172        let args = vec![Value::Number(1.0), from_unit, to_unit];
2173        let result = convert_fn
2174            .call(args, heap.clone(), bindings.clone(), 0, "")
2175            .unwrap();
2176        if let Value::Number(n) = result {
2177            assert!((n - 2.20462).abs() < 0.001);
2178        }
2179    }
2180
2181    #[test]
2182    fn test_convert_information_storage() {
2183        let heap = Rc::new(RefCell::new(Heap::new()));
2184        let bindings = Rc::new(Environment::new());
2185        let convert_fn = BuiltInFunction::Convert;
2186
2187        // Test 1 kibibyte to bytes
2188        let from_unit = heap.borrow_mut().insert_string("kibibytes".to_string());
2189        let to_unit = heap.borrow_mut().insert_string("bytes".to_string());
2190        let args = vec![Value::Number(1.0), from_unit, to_unit];
2191        let result = convert_fn
2192            .call(args, heap.clone(), bindings.clone(), 0, "")
2193            .unwrap();
2194        assert_eq!(result, Value::Number(1024.0));
2195    }
2196
2197    #[test]
2198    fn test_convert_same_unit() {
2199        let heap = Rc::new(RefCell::new(Heap::new()));
2200        let bindings = Rc::new(Environment::new());
2201        let convert_fn = BuiltInFunction::Convert;
2202
2203        // Test converting to same unit
2204        let from_unit = heap.borrow_mut().insert_string("meters".to_string());
2205        let to_unit = heap.borrow_mut().insert_string("m".to_string());
2206        let args = vec![Value::Number(42.0), from_unit, to_unit];
2207        let result = convert_fn
2208            .call(args, heap.clone(), bindings.clone(), 0, "")
2209            .unwrap();
2210        assert_eq!(result, Value::Number(42.0));
2211    }
2212
2213    #[test]
2214    fn test_convert_incompatible_units() {
2215        let heap = Rc::new(RefCell::new(Heap::new()));
2216        let bindings = Rc::new(Environment::new());
2217        let convert_fn = BuiltInFunction::Convert;
2218
2219        // Test incompatible units
2220        let from_unit = heap.borrow_mut().insert_string("kg".to_string());
2221        let to_unit = heap.borrow_mut().insert_string("meters".to_string());
2222        let args = vec![Value::Number(1.0), from_unit, to_unit];
2223        let result = convert_fn.call(args, heap.clone(), bindings.clone(), 0, "");
2224        assert!(result.is_err());
2225        assert!(result.unwrap_err().to_string().contains("Cannot convert"));
2226    }
2227
2228    #[test]
2229    fn test_convert_unknown_unit() {
2230        let heap = Rc::new(RefCell::new(Heap::new()));
2231        let bindings = Rc::new(Environment::new());
2232        let convert_fn = BuiltInFunction::Convert;
2233
2234        // Test unknown unit
2235        let from_unit = heap.borrow_mut().insert_string("foobar".to_string());
2236        let to_unit = heap.borrow_mut().insert_string("meters".to_string());
2237        let args = vec![Value::Number(1.0), from_unit, to_unit];
2238        let result = convert_fn.call(args, heap.clone(), bindings.clone(), 0, "");
2239        assert!(result.is_err());
2240        assert!(result.unwrap_err().to_string().contains("Unknown unit"));
2241    }
2242
2243    #[test]
2244    fn test_convert_case_insensitive() {
2245        let heap = Rc::new(RefCell::new(Heap::new()));
2246        let bindings = Rc::new(Environment::new());
2247        let convert_fn = BuiltInFunction::Convert;
2248
2249        // Test case insensitivity
2250        let from_unit = heap.borrow_mut().insert_string("KM".to_string());
2251        let to_unit = heap.borrow_mut().insert_string("M".to_string());
2252        let args = vec![Value::Number(1.0), from_unit, to_unit];
2253        let result = convert_fn
2254            .call(args, heap.clone(), bindings.clone(), 0, "")
2255            .unwrap();
2256        assert_eq!(result, Value::Number(1000.0));
2257    }
2258}