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