blots_core/
functions.rs

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