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
20pub 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 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 Min,
47 Max,
48 Avg,
49 Sum,
50 Prod,
51 Median,
52 Percentile,
53
54 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 Map,
71 Reduce,
72 Filter,
73 Every,
74 Some,
75
76 Split,
78 Join,
79 Replace,
80 Trim,
81 Uppercase,
82 Lowercase,
83 Includes,
84 Format,
85
86 Typeof,
88 Arity,
89
90 Keys,
92 Values,
93 Entries,
94
95 ToString,
97 ToNumber,
98 ToBool,
99
100 #[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 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 Self::Round => FunctionArity::Between(1, 2),
265
266 Self::Min | Self::Max | Self::Avg | Self::Sum | Self::Prod | Self::Median => {
268 FunctionArity::AtLeast(1)
269 }
270
271 Self::Range => FunctionArity::Between(1, 2),
273
274 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 Self::Map | Self::Filter | Self::Every | Self::Some | Self::SortBy => {
289 FunctionArity::Exact(2)
290 }
291 Self::Reduce => FunctionArity::Exact(3),
292
293 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 Self::Typeof | Self::Arity => FunctionArity::Exact(1),
306
307 Self::Keys | Self::Values | Self::Entries => FunctionArity::Exact(1),
309
310 #[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 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 Self::Min => {
355 let nums = if args.len() == 1 {
356 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 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 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 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 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 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 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 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 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 Self::ToString => match args[0] {
943 Value::String(_) => Ok(args[0]), _ => {
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]), 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]), 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 #[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 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 Self::Map => {
999 let func = &args[1];
1000 let list = {
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 drop(func_def);
1006 list
1007 };
1008
1009 let mut mapped_list = vec![];
1010 for item in list {
1011 let func_def = get_function_def(func, &heap.borrow())
1012 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1013 let result = func_def.call(
1014 Value::Null,
1015 vec![item],
1016 Rc::clone(&heap),
1017 Rc::clone(&bindings),
1018 call_depth + 1,
1019 )?;
1020 mapped_list.push(result);
1021 }
1022
1023 Ok(heap.borrow_mut().insert_list(mapped_list))
1024 }
1025
1026 Self::Filter => {
1027 let func = &args[1];
1028 let list = {
1029 let borrowed_heap = &heap.borrow();
1030 args[0].as_list(borrowed_heap)?.clone()
1031 };
1032
1033 let mut filtered_list = vec![];
1034 for item in list {
1035 let func_def = get_function_def(func, &heap.borrow())
1036 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1037 let result = func_def.call(
1038 Value::Null,
1039 vec![item],
1040 Rc::clone(&heap),
1041 Rc::clone(&bindings),
1042 call_depth + 1,
1043 )?;
1044 if result.as_bool()? {
1045 filtered_list.push(item);
1046 }
1047 }
1048
1049 Ok(heap.borrow_mut().insert_list(filtered_list))
1050 }
1051
1052 Self::Reduce => {
1053 let func = &args[1];
1054 let initial = args[2];
1055 let list = {
1056 let borrowed_heap = &heap.borrow();
1057 args[0].as_list(borrowed_heap)?.clone()
1058 };
1059
1060 let mut accumulator = initial;
1061 for item in list {
1062 let func_def = get_function_def(func, &heap.borrow())
1063 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1064 accumulator = func_def.call(
1065 Value::Null,
1066 vec![accumulator, item],
1067 Rc::clone(&heap),
1068 Rc::clone(&bindings),
1069 call_depth + 1,
1070 )?;
1071 }
1072
1073 Ok(accumulator)
1074 }
1075
1076 Self::Every => {
1077 let func = &args[1];
1078 let list = {
1079 let borrowed_heap = &heap.borrow();
1080 args[0].as_list(borrowed_heap)?.clone()
1081 };
1082
1083 for item in list {
1084 let func_def = get_function_def(func, &heap.borrow())
1085 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1086 let result = func_def.call(
1087 Value::Null,
1088 vec![item],
1089 Rc::clone(&heap),
1090 Rc::clone(&bindings),
1091 call_depth + 1,
1092 )?;
1093 if !result.as_bool()? {
1094 return Ok(Value::Bool(false));
1095 }
1096 }
1097
1098 Ok(Value::Bool(true))
1099 }
1100
1101 Self::Some => {
1102 let func = &args[1];
1103 let list = {
1104 let borrowed_heap = &heap.borrow();
1105 args[0].as_list(borrowed_heap)?.clone()
1106 };
1107
1108 for item in list {
1109 let func_def = get_function_def(func, &heap.borrow())
1110 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1111 let result = func_def.call(
1112 Value::Null,
1113 vec![item],
1114 Rc::clone(&heap),
1115 Rc::clone(&bindings),
1116 call_depth + 1,
1117 )?;
1118 if result.as_bool()? {
1119 return Ok(Value::Bool(true));
1120 }
1121 }
1122
1123 Ok(Value::Bool(false))
1124 }
1125
1126 Self::SortBy => {
1127 let func = &args[1];
1128 let mut list = {
1129 let borrowed_heap = &heap.borrow();
1130 args[0].as_list(borrowed_heap)?.clone()
1131 };
1132
1133 list.sort_by(|a, b| {
1134 let func_def_a = get_function_def(func, &heap.borrow());
1135 let func_def_b = get_function_def(func, &heap.borrow());
1136
1137 match (func_def_a, func_def_b) {
1138 (Some(fd_a), Some(fd_b)) => {
1139 let result_a = fd_a.call(
1140 Value::Null,
1141 vec![*a],
1142 Rc::clone(&heap),
1143 Rc::clone(&bindings),
1144 call_depth + 1,
1145 );
1146 let result_b = fd_b.call(
1147 Value::Null,
1148 vec![*b],
1149 Rc::clone(&heap),
1150 Rc::clone(&bindings),
1151 call_depth + 1,
1152 );
1153
1154 match (result_a, result_b) {
1155 (Ok(val_a), Ok(val_b)) => val_a
1156 .compare(&val_b, &heap.borrow())
1157 .unwrap_or(None)
1158 .unwrap_or(std::cmp::Ordering::Equal),
1159 _ => std::cmp::Ordering::Equal,
1160 }
1161 }
1162 _ => std::cmp::Ordering::Equal,
1163 }
1164 });
1165
1166 Ok(heap.borrow_mut().insert_list(list))
1167 }
1168 }
1169 }
1170
1171 pub fn all() -> Vec<Self> {
1172 vec![
1173 Self::Sqrt,
1174 Self::Sin,
1175 Self::Cos,
1176 Self::Tan,
1177 Self::Asin,
1178 Self::Acos,
1179 Self::Atan,
1180 Self::Log,
1181 Self::Log10,
1182 Self::Exp,
1183 Self::Abs,
1184 Self::Floor,
1185 Self::Ceil,
1186 Self::Round,
1187 Self::Trunc,
1188 Self::Min,
1189 Self::Max,
1190 Self::Avg,
1191 Self::Sum,
1192 Self::Prod,
1193 Self::Median,
1194 Self::Percentile,
1195 Self::Range,
1196 Self::Len,
1197 Self::Head,
1198 Self::Tail,
1199 Self::Slice,
1200 Self::Concat,
1201 Self::Dot,
1202 Self::Unique,
1203 Self::Sort,
1204 Self::SortBy,
1205 Self::Reverse,
1206 Self::Any,
1207 Self::All,
1208 Self::Map,
1209 Self::Reduce,
1210 Self::Filter,
1211 Self::Every,
1212 Self::Some,
1213 Self::Split,
1214 Self::Join,
1215 Self::Replace,
1216 Self::Trim,
1217 Self::Uppercase,
1218 Self::Lowercase,
1219 Self::ToString,
1220 Self::ToNumber,
1221 Self::ToBool,
1222 Self::Includes,
1223 Self::Format,
1224 Self::Typeof,
1225 Self::Arity,
1226 Self::Keys,
1227 Self::Values,
1228 Self::Entries,
1229 #[cfg(not(target_arch = "wasm32"))]
1230 Self::Print,
1231 #[cfg(not(target_arch = "wasm32"))]
1232 Self::TimeNow,
1233 ]
1234 }
1235
1236 pub fn all_names() -> Vec<&'static str> {
1237 Self::all().iter().map(|f| f.name()).collect()
1238 }
1239}
1240
1241impl FunctionDef {
1242 pub fn get_name(&self) -> String {
1243 match self {
1244 FunctionDef::BuiltIn(built_in) => {
1245 format!("built-in function \"{}\"", built_in.name())
1246 }
1247 FunctionDef::Lambda(LambdaDef { name, .. }) => name
1248 .clone()
1249 .map_or(String::from("anonymous function"), |n| {
1250 format!("function \"{}\"", n)
1251 }),
1252 }
1253 }
1254
1255 pub fn check_arity(&self, arg_count: usize) -> Result<()> {
1256 match self {
1257 FunctionDef::BuiltIn(built_in) => match built_in.arity() {
1258 FunctionArity::Exact(expected) => {
1259 if arg_count == expected {
1260 Ok(())
1261 } else {
1262 Err(anyhow!(
1263 "{} takes exactly {} arguments, but {} were given",
1264 self.get_name(),
1265 expected,
1266 arg_count
1267 ))
1268 }
1269 }
1270 FunctionArity::AtLeast(expected) => {
1271 if arg_count >= expected {
1272 Ok(())
1273 } else {
1274 Err(anyhow!(
1275 "{} takes at least {} arguments, but {} were given",
1276 self.get_name(),
1277 expected,
1278 arg_count
1279 ))
1280 }
1281 }
1282 FunctionArity::Between(min, max) => {
1283 if arg_count >= min && arg_count <= max {
1284 Ok(())
1285 } else {
1286 Err(anyhow!(
1287 "{} takes between {} and {} arguments, but {} were given",
1288 self.get_name(),
1289 min,
1290 max,
1291 arg_count
1292 ))
1293 }
1294 }
1295 },
1296 FunctionDef::Lambda(def) => {
1297 let arity = def.get_arity();
1298
1299 match arity {
1300 FunctionArity::Exact(expected) => {
1301 if arg_count == expected {
1302 Ok(())
1303 } else {
1304 Err(anyhow!(
1305 "{} takes exactly {} arguments, but {} were given",
1306 self.get_name(),
1307 expected,
1308 arg_count
1309 ))
1310 }
1311 }
1312 FunctionArity::AtLeast(expected) => {
1313 if arg_count >= expected {
1314 Ok(())
1315 } else {
1316 Err(anyhow!(
1317 "{} takes at least {} arguments, but {} were given",
1318 self.get_name(),
1319 expected,
1320 arg_count
1321 ))
1322 }
1323 }
1324 FunctionArity::Between(min, max) => {
1325 if arg_count >= min && arg_count <= max {
1326 Ok(())
1327 } else {
1328 Err(anyhow!(
1329 "{} takes between {} and {} arguments, but {} were given",
1330 self.get_name(),
1331 min,
1332 max,
1333 arg_count
1334 ))
1335 }
1336 }
1337 }
1338 }
1339 }
1340 }
1341
1342 pub fn call(
1343 &self,
1344 this_value: Value,
1345 args: Vec<Value>,
1346 heap: Rc<RefCell<Heap>>,
1347 bindings: Rc<RefCell<HashMap<String, Value>>>,
1348 call_depth: usize,
1349 ) -> Result<Value> {
1350 #[cfg(not(target_arch = "wasm32"))]
1351 let start = std::time::Instant::now();
1352
1353 self.check_arity(args.len())?;
1354
1355 if call_depth > 1000 {
1356 return Err(anyhow!(
1357 "in {}: maximum call depth of 1000 exceeded",
1358 self.get_name()
1359 ));
1360 }
1361
1362 match self {
1363 FunctionDef::Lambda(LambdaDef {
1364 name,
1365 args: expected_args,
1366 body,
1367 scope,
1368 }) => {
1369 #[cfg(not(target_arch = "wasm32"))]
1370 let start_var_env = std::time::Instant::now();
1371
1372 let mut new_bindings = bindings.borrow().clone();
1374
1375 for (key, value) in scope {
1377 new_bindings.insert(key.clone(), *value);
1378 }
1379
1380 if let Some(fn_name) = name {
1382 new_bindings.insert(fn_name.clone(), this_value);
1383 }
1384
1385 if let Some(inputs) = new_bindings.get("inputs").copied() {
1387 new_bindings.insert(String::from("inputs"), inputs);
1388 }
1389
1390 for (idx, expected_arg) in expected_args.iter().enumerate() {
1392 match expected_arg {
1393 LambdaArg::Required(arg_name) => {
1394 new_bindings.insert(arg_name.clone(), args[idx]);
1395 }
1396 LambdaArg::Optional(arg_name) => {
1397 new_bindings.insert(
1398 arg_name.clone(),
1399 args.get(idx).copied().unwrap_or(Value::Null),
1400 );
1401 }
1402 LambdaArg::Rest(arg_name) => {
1403 new_bindings.insert(
1404 arg_name.clone(),
1405 heap.borrow_mut()
1406 .insert_list(args.iter().skip(idx).copied().collect()),
1407 );
1408 }
1409 }
1410 }
1411
1412 #[cfg(not(target_arch = "wasm32"))]
1413 let end_var_env = std::time::Instant::now();
1414
1415 let return_value = evaluate_ast(
1416 body,
1417 Rc::clone(&heap),
1418 Rc::new(RefCell::new(new_bindings)),
1419 call_depth + 1,
1420 )
1421 .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1422
1423 #[cfg(not(target_arch = "wasm32"))]
1424 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1425 name: self.get_name(),
1426 start,
1427 end: std::time::Instant::now(),
1428 start_var_env: Some(start_var_env),
1429 end_var_env: Some(end_var_env),
1430 });
1431
1432 return_value
1433 }
1434 FunctionDef::BuiltIn(built_in) => {
1435 let return_value = built_in
1436 .call(args, heap, bindings, call_depth + 1)
1437 .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1438
1439 #[cfg(not(target_arch = "wasm32"))]
1440 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1441 name: self.get_name(),
1442 start,
1443 end: std::time::Instant::now(),
1444 start_var_env: None,
1445 end_var_env: None,
1446 });
1447
1448 return_value
1449 }
1450 }
1451 }
1452}
1453
1454pub fn is_built_in_function(ident: &str) -> bool {
1455 BuiltInFunction::from_ident(ident).is_some()
1456}
1457
1458pub fn get_built_in_function_def_by_ident(ident: &str) -> Option<FunctionDef> {
1459 BuiltInFunction::from_ident(ident).map(FunctionDef::BuiltIn)
1460}
1461
1462pub fn get_built_in_function_idents() -> Vec<&'static str> {
1463 BuiltInFunction::all_names()
1464}
1465
1466pub fn get_function_def(value: &Value, heap: &Heap) -> Option<FunctionDef> {
1467 match value {
1468 Value::Lambda(pointer) => Some(FunctionDef::Lambda(
1469 pointer.reify(heap).as_lambda().ok()?.clone(),
1470 )),
1471 Value::BuiltIn(built_in) => Some(FunctionDef::BuiltIn(*built_in)),
1472 _ => None,
1473 }
1474}
1475
1476#[cfg(test)]
1477mod tests {
1478 use super::*;
1479 use std::cell::RefCell;
1480 use std::collections::HashMap;
1481 use std::rc::Rc;
1482
1483 #[test]
1484 fn test_range_function() {
1485 let heap = Rc::new(RefCell::new(Heap::new()));
1487 let bindings = Rc::new(RefCell::new(HashMap::new()));
1488 let range_fn = BuiltInFunction::Range;
1489
1490 let args = vec![Value::Number(4.0)];
1492 let result = range_fn
1493 .call(args, heap.clone(), bindings.clone(), 0)
1494 .unwrap();
1495
1496 let heap_borrow = heap.borrow();
1497 let list = result.as_list(&heap_borrow).unwrap();
1498 assert_eq!(list.len(), 4);
1499 assert_eq!(list[0], Value::Number(0.0));
1500 assert_eq!(list[3], Value::Number(3.0));
1501 }
1502
1503 #[test]
1504 fn test_range_function_two_args() {
1505 let heap = Rc::new(RefCell::new(Heap::new()));
1507 let bindings = Rc::new(RefCell::new(HashMap::new()));
1508 let range_fn = BuiltInFunction::Range;
1509
1510 let args = vec![Value::Number(4.0), Value::Number(10.0)];
1512 let result = range_fn
1513 .call(args, heap.clone(), bindings.clone(), 0)
1514 .unwrap();
1515
1516 let heap_borrow = heap.borrow();
1517 let list = result.as_list(&heap_borrow).unwrap();
1518 assert_eq!(list.len(), 6);
1519 assert_eq!(list[0], Value::Number(4.0));
1520 assert_eq!(list[5], Value::Number(9.0));
1521 }
1522
1523 #[test]
1524 fn test_round_function_single_arg() {
1525 let heap = Rc::new(RefCell::new(Heap::new()));
1526 let bindings = Rc::new(RefCell::new(HashMap::new()));
1527 let round_fn = BuiltInFunction::Round;
1528
1529 let test_cases = vec![
1531 (42.4, 42.0),
1532 (42.5, 43.0),
1533 (42.6, 43.0),
1534 (-42.4, -42.0),
1535 (-42.5, -43.0),
1536 (-42.6, -43.0),
1537 (0.0, 0.0),
1538 (1.999, 2.0),
1539 (-1.999, -2.0),
1540 ];
1541
1542 for (input, expected) in test_cases {
1543 let args = vec![Value::Number(input)];
1544 let result = round_fn
1545 .call(args, heap.clone(), bindings.clone(), 0)
1546 .unwrap();
1547 assert_eq!(
1548 result,
1549 Value::Number(expected),
1550 "round({}) should be {}",
1551 input,
1552 expected
1553 );
1554 }
1555 }
1556
1557 #[test]
1558 fn test_round_function_with_decimal_places() {
1559 let heap = Rc::new(RefCell::new(Heap::new()));
1560 let bindings = Rc::new(RefCell::new(HashMap::new()));
1561 let round_fn = BuiltInFunction::Round;
1562
1563 let test_cases = vec![
1565 (42.4543, 0.0, 42.0),
1566 (42.4543, 1.0, 42.5),
1567 (42.4543, 2.0, 42.45),
1568 (42.4543, 3.0, 42.454),
1569 (42.4543, 4.0, 42.4543),
1570 (4.14159, 4.0, 4.1416),
1571 (0.005, 2.0, 0.01),
1572 (0.995, 2.0, 1.0),
1573 (9.995, 2.0, 9.99),
1576 (-9.995, 2.0, -9.99),
1577 ];
1578
1579 for (input, places, expected) in test_cases {
1580 let args = vec![Value::Number(input), Value::Number(places)];
1581 let result = round_fn
1582 .call(args, heap.clone(), bindings.clone(), 0)
1583 .unwrap();
1584
1585 if let Value::Number(result_num) = result {
1587 assert!(
1588 (result_num - expected).abs() < 1e-10,
1589 "round({}, {}) = {} should be close to {}",
1590 input,
1591 places,
1592 result_num,
1593 expected
1594 );
1595 } else {
1596 panic!("Expected number result");
1597 }
1598 }
1599 }
1600
1601 #[test]
1602 fn test_round_function_negative_decimal_places() {
1603 let heap = Rc::new(RefCell::new(Heap::new()));
1604 let bindings = Rc::new(RefCell::new(HashMap::new()));
1605 let round_fn = BuiltInFunction::Round;
1606
1607 let test_cases = vec![
1609 (1234.567, -1.0, 1230.0),
1610 (1234.567, -2.0, 1200.0),
1611 (1234.567, -3.0, 1000.0),
1612 (1234.567, -4.0, 0.0),
1613 (5678.9, -1.0, 5680.0),
1614 (5678.9, -2.0, 5700.0),
1615 (5678.9, -3.0, 6000.0),
1616 (-1234.567, -1.0, -1230.0),
1617 (-1234.567, -2.0, -1200.0),
1618 (1500.0, -3.0, 2000.0),
1619 (-1500.0, -3.0, -2000.0),
1620 ];
1621
1622 for (input, places, expected) in test_cases {
1623 let args = vec![Value::Number(input), Value::Number(places)];
1624 let result = round_fn
1625 .call(args, heap.clone(), bindings.clone(), 0)
1626 .unwrap();
1627 assert_eq!(
1628 result,
1629 Value::Number(expected),
1630 "round({}, {}) should be {}",
1631 input,
1632 places,
1633 expected
1634 );
1635 }
1636 }
1637
1638 #[test]
1639 fn test_round_function_edge_cases() {
1640 let heap = Rc::new(RefCell::new(Heap::new()));
1641 let bindings = Rc::new(RefCell::new(HashMap::new()));
1642 let round_fn = BuiltInFunction::Round;
1643
1644 let test_cases = vec![
1646 (f64::INFINITY, 0.0, f64::INFINITY),
1647 (f64::NEG_INFINITY, 0.0, f64::NEG_INFINITY),
1648 (0.0, 5.0, 0.0),
1649 (-0.0, 5.0, -0.0),
1650 (1e-10, 5.0, 0.0),
1651 (1e-10, 15.0, 1e-10),
1652 ];
1653
1654 for (input, places, expected) in test_cases {
1655 let args = vec![Value::Number(input), Value::Number(places)];
1656 let result = round_fn
1657 .call(args, heap.clone(), bindings.clone(), 0)
1658 .unwrap();
1659
1660 if let Value::Number(result_num) = result {
1661 if expected.is_infinite() {
1662 assert!(
1663 result_num.is_infinite()
1664 && result_num.is_sign_positive() == expected.is_sign_positive(),
1665 "round({}, {}) should be {}",
1666 input,
1667 places,
1668 expected
1669 );
1670 } else if expected == 0.0 || expected == -0.0 {
1671 assert!(
1672 result_num.abs() < 1e-10,
1673 "round({}, {}) = {} should be close to 0",
1674 input,
1675 places,
1676 result_num
1677 );
1678 } else {
1679 assert!(
1680 (result_num - expected).abs() < 1e-15,
1681 "round({}, {}) = {} should be close to {}",
1682 input,
1683 places,
1684 result_num,
1685 expected
1686 );
1687 }
1688 } else {
1689 panic!("Expected number result");
1690 }
1691 }
1692 }
1693}