blots_core/
functions.rs

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