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
67 Map,
69 Reduce,
70 Filter,
71 Every,
72 Some,
73
74 Split,
76 Join,
77 Replace,
78 Trim,
79 Uppercase,
80 Lowercase,
81 ToString,
82 ToNumber,
83 Includes,
84 Format,
85
86 Typeof,
88 Arity,
89
90 Keys,
92 Values,
93 Entries,
94
95 #[cfg(not(target_arch = "wasm32"))]
97 Print,
98 #[cfg(not(target_arch = "wasm32"))]
99 TimeNow,
100}
101
102#[derive(Debug, Clone)]
103pub enum FunctionDef {
104 BuiltIn(BuiltInFunction),
105 Lambda(LambdaDef),
106}
107
108impl BuiltInFunction {
109 pub fn from_ident(ident: &str) -> Option<Self> {
110 match ident {
111 "sqrt" => Some(Self::Sqrt),
112 "sin" => Some(Self::Sin),
113 "cos" => Some(Self::Cos),
114 "tan" => Some(Self::Tan),
115 "asin" => Some(Self::Asin),
116 "acos" => Some(Self::Acos),
117 "atan" => Some(Self::Atan),
118 "log" => Some(Self::Log),
119 "log10" => Some(Self::Log10),
120 "exp" => Some(Self::Exp),
121 "abs" => Some(Self::Abs),
122 "floor" => Some(Self::Floor),
123 "ceil" => Some(Self::Ceil),
124 "round" => Some(Self::Round),
125 "trunc" => Some(Self::Trunc),
126 "min" => Some(Self::Min),
127 "max" => Some(Self::Max),
128 "avg" => Some(Self::Avg),
129 "sum" => Some(Self::Sum),
130 "prod" => Some(Self::Prod),
131 "median" => Some(Self::Median),
132 "percentile" => Some(Self::Percentile),
133 "range" => Some(Self::Range),
134 "len" => Some(Self::Len),
135 "head" => Some(Self::Head),
136 "tail" => Some(Self::Tail),
137 "slice" => Some(Self::Slice),
138 "concat" => Some(Self::Concat),
139 "dot" => Some(Self::Dot),
140 "unique" => Some(Self::Unique),
141 "sort" => Some(Self::Sort),
142 "sort_by" => Some(Self::SortBy),
143 "reverse" => Some(Self::Reverse),
144 "map" => Some(Self::Map),
145 "reduce" => Some(Self::Reduce),
146 "filter" => Some(Self::Filter),
147 "every" => Some(Self::Every),
148 "some" => Some(Self::Some),
149 "split" => Some(Self::Split),
150 "join" => Some(Self::Join),
151 "replace" => Some(Self::Replace),
152 "trim" => Some(Self::Trim),
153 "uppercase" => Some(Self::Uppercase),
154 "lowercase" => Some(Self::Lowercase),
155 "to_string" => Some(Self::ToString),
156 "to_number" => Some(Self::ToNumber),
157 "includes" => Some(Self::Includes),
158 "format" => Some(Self::Format),
159 "typeof" => Some(Self::Typeof),
160 "arity" => Some(Self::Arity),
161 "keys" => Some(Self::Keys),
162 "values" => Some(Self::Values),
163 "entries" => Some(Self::Entries),
164 #[cfg(not(target_arch = "wasm32"))]
165 "print" => Some(Self::Print),
166 #[cfg(not(target_arch = "wasm32"))]
167 "time_now" => Some(Self::TimeNow),
168 _ => None,
169 }
170 }
171
172 pub fn name(&self) -> &'static str {
173 match self {
174 Self::Sqrt => "sqrt",
175 Self::Sin => "sin",
176 Self::Cos => "cos",
177 Self::Tan => "tan",
178 Self::Asin => "asin",
179 Self::Acos => "acos",
180 Self::Atan => "atan",
181 Self::Log => "log",
182 Self::Log10 => "log10",
183 Self::Exp => "exp",
184 Self::Abs => "abs",
185 Self::Floor => "floor",
186 Self::Ceil => "ceil",
187 Self::Round => "round",
188 Self::Trunc => "trunc",
189 Self::Min => "min",
190 Self::Max => "max",
191 Self::Avg => "avg",
192 Self::Sum => "sum",
193 Self::Prod => "prod",
194 Self::Median => "median",
195 Self::Percentile => "percentile",
196 Self::Range => "range",
197 Self::Len => "len",
198 Self::Head => "head",
199 Self::Tail => "tail",
200 Self::Slice => "slice",
201 Self::Concat => "concat",
202 Self::Dot => "dot",
203 Self::Unique => "unique",
204 Self::Sort => "sort",
205 Self::SortBy => "sort_by",
206 Self::Reverse => "reverse",
207 Self::Map => "map",
208 Self::Reduce => "reduce",
209 Self::Filter => "filter",
210 Self::Every => "every",
211 Self::Some => "some",
212 Self::Split => "split",
213 Self::Join => "join",
214 Self::Replace => "replace",
215 Self::Trim => "trim",
216 Self::Uppercase => "uppercase",
217 Self::Lowercase => "lowercase",
218 Self::ToString => "to_string",
219 Self::ToNumber => "to_number",
220 Self::Includes => "includes",
221 Self::Format => "format",
222 Self::Typeof => "typeof",
223 Self::Arity => "arity",
224 Self::Keys => "keys",
225 Self::Values => "values",
226 Self::Entries => "entries",
227 #[cfg(not(target_arch = "wasm32"))]
228 Self::Print => "print",
229 #[cfg(not(target_arch = "wasm32"))]
230 Self::TimeNow => "time_now",
231 }
232 }
233
234 pub fn arity(&self) -> FunctionArity {
235 match self {
236 Self::Sqrt
238 | Self::Sin
239 | Self::Cos
240 | Self::Tan
241 | Self::Asin
242 | Self::Acos
243 | Self::Atan
244 | Self::Log
245 | Self::Log10
246 | Self::Exp
247 | Self::Abs
248 | Self::Floor
249 | Self::Ceil
250 | Self::Trunc => FunctionArity::Exact(1),
251
252 Self::Round => FunctionArity::Between(1, 2),
254
255 Self::Min | Self::Max | Self::Avg | Self::Sum | Self::Prod | Self::Median => {
257 FunctionArity::AtLeast(1)
258 }
259
260 Self::Range => FunctionArity::Between(1, 2),
262
263 Self::Len | Self::Head | Self::Tail | Self::Unique | Self::Sort | Self::Reverse => {
265 FunctionArity::Exact(1)
266 }
267 Self::Slice => FunctionArity::Exact(3),
268 Self::Concat => FunctionArity::AtLeast(2),
269 Self::Dot | Self::Percentile => FunctionArity::Exact(2),
270
271 Self::Map | Self::Filter | Self::Every | Self::Some | Self::SortBy => {
273 FunctionArity::Exact(2)
274 }
275 Self::Reduce => FunctionArity::Exact(3),
276
277 Self::Split | Self::Join | Self::Includes => FunctionArity::Exact(2),
279 Self::Replace => FunctionArity::Exact(3),
280 Self::Trim | Self::Uppercase | Self::Lowercase | Self::ToString | Self::ToNumber => {
281 FunctionArity::Exact(1)
282 }
283 Self::Format => FunctionArity::AtLeast(1),
284
285 Self::Typeof | Self::Arity => FunctionArity::Exact(1),
287
288 Self::Keys | Self::Values | Self::Entries => FunctionArity::Exact(1),
290
291 #[cfg(not(target_arch = "wasm32"))]
293 Self::Print => FunctionArity::AtLeast(1),
294 #[cfg(not(target_arch = "wasm32"))]
295 Self::TimeNow => FunctionArity::Exact(0),
296 }
297 }
298
299 pub fn call(
300 &self,
301 args: Vec<Value>,
302 heap: Rc<RefCell<Heap>>,
303 bindings: Rc<RefCell<HashMap<String, Value>>>,
304 call_depth: usize,
305 ) -> Result<Value> {
306 match self {
307 Self::Sqrt => Ok(Value::Number(args[0].as_number()?.sqrt())),
309 Self::Sin => Ok(Value::Number(args[0].as_number()?.sin())),
310 Self::Cos => Ok(Value::Number(args[0].as_number()?.cos())),
311 Self::Tan => Ok(Value::Number(args[0].as_number()?.tan())),
312 Self::Asin => Ok(Value::Number(args[0].as_number()?.asin())),
313 Self::Acos => Ok(Value::Number(args[0].as_number()?.acos())),
314 Self::Atan => Ok(Value::Number(args[0].as_number()?.atan())),
315 Self::Log => Ok(Value::Number(args[0].as_number()?.ln())),
316 Self::Log10 => Ok(Value::Number(args[0].as_number()?.log10())),
317 Self::Exp => Ok(Value::Number(args[0].as_number()?.exp())),
318 Self::Abs => Ok(Value::Number(args[0].as_number()?.abs())),
319 Self::Floor => Ok(Value::Number(args[0].as_number()?.floor())),
320 Self::Ceil => Ok(Value::Number(args[0].as_number()?.ceil())),
321 Self::Trunc => Ok(Value::Number(args[0].as_number()?.trunc())),
322
323 Self::Round => {
324 let num = args[0].as_number()?;
325 if args.len() == 1 {
326 Ok(Value::Number(num.round()))
327 } else {
328 let decimal_places = args[1].as_number()? as i32;
329 let multiplier = 10_f64.powi(decimal_places);
330 Ok(Value::Number((num * multiplier).round() / multiplier))
331 }
332 }
333
334 Self::Min => {
336 let nums = if args.len() == 1 {
337 match &args[0] {
339 Value::List(_) => {
340 let borrowed_heap = heap.borrow();
341 let list = args[0].as_list(&borrowed_heap)?;
342 list.iter()
343 .map(|a| a.as_number())
344 .collect::<Result<Vec<f64>>>()?
345 }
346 _ => vec![args[0].as_number()?],
347 }
348 } else {
349 args.iter()
350 .map(|a| a.as_number())
351 .collect::<Result<Vec<f64>>>()?
352 };
353
354 if nums.is_empty() {
355 return Err(anyhow!("min requires at least one number"));
356 }
357
358 Ok(Value::Number(
359 nums.iter().copied().fold(f64::INFINITY, f64::min),
360 ))
361 }
362
363 Self::Max => {
364 let nums = if args.len() == 1 {
365 match &args[0] {
367 Value::List(_) => {
368 let borrowed_heap = heap.borrow();
369 let list = args[0].as_list(&borrowed_heap)?;
370 list.iter()
371 .map(|a| a.as_number())
372 .collect::<Result<Vec<f64>>>()?
373 }
374 _ => vec![args[0].as_number()?],
375 }
376 } else {
377 args.iter()
378 .map(|a| a.as_number())
379 .collect::<Result<Vec<f64>>>()?
380 };
381
382 if nums.is_empty() {
383 return Err(anyhow!("max requires at least one number"));
384 }
385
386 Ok(Value::Number(
387 nums.iter().copied().fold(f64::NEG_INFINITY, f64::max),
388 ))
389 }
390
391 Self::Avg => {
392 let nums = if args.len() == 1 {
393 match &args[0] {
394 Value::List(_) => {
395 let borrowed_heap = heap.borrow();
396 let list = args[0].as_list(&borrowed_heap)?;
397 list.iter()
398 .map(|a| a.as_number())
399 .collect::<Result<Vec<f64>>>()?
400 }
401 _ => vec![args[0].as_number()?],
402 }
403 } else {
404 args.iter()
405 .map(|a| a.as_number())
406 .collect::<Result<Vec<f64>>>()?
407 };
408 if nums.is_empty() {
409 return Err(anyhow!("avg requires at least one number"));
410 }
411 Ok(Value::Number(nums.iter().sum::<f64>() / nums.len() as f64))
412 }
413
414 Self::Prod => {
415 let nums = if args.len() == 1 {
416 match &args[0] {
417 Value::List(_) => {
418 let borrowed_heap = heap.borrow();
419 let list = args[0].as_list(&borrowed_heap)?;
420 list.iter()
421 .map(|a| a.as_number())
422 .collect::<Result<Vec<f64>>>()?
423 }
424 _ => vec![args[0].as_number()?],
425 }
426 } else {
427 args.iter()
428 .map(|a| a.as_number())
429 .collect::<Result<Vec<f64>>>()?
430 };
431 if nums.is_empty() {
432 return Err(anyhow!("prod requires at least one number"));
433 }
434 Ok(Value::Number(nums.iter().product()))
435 }
436
437 Self::Range => {
438 let (start, end) = match args[..] {
439 [Value::Number(start)] => (0.0, start),
440 [Value::Number(start), Value::Number(end)] => (start, end),
441 _ => return Err(anyhow!("range requires 1 or 2 numbers")),
442 };
443
444 if start > end {
445 return Err(anyhow!(
446 "range requires start to be less than or equal to end"
447 ));
448 }
449
450 if !f64::is_finite(start) || !f64::is_finite(end) {
451 return Err(anyhow!("range requires finite numbers"));
452 }
453
454 let start_i64 = start as i64;
455 let end_i64 = end as i64;
456 let length = end_i64 - start_i64;
457
458 if length > u32::MAX as i64 {
459 return Err(anyhow!(
460 "list would be longer than the maximum length of {}",
461 u32::MAX
462 ));
463 }
464
465 let values = (start_i64..end_i64)
466 .map(|e| Value::Number(e as f64))
467 .collect();
468 let list = heap.borrow_mut().insert_list(values);
469
470 Ok(list)
471 }
472
473 Self::Sum => {
474 let nums = if args.len() == 1 {
475 match &args[0] {
476 Value::List(_) => {
477 let borrowed_heap = heap.borrow();
478 let list = args[0].as_list(&borrowed_heap)?;
479 list.iter()
480 .map(|a| a.as_number())
481 .collect::<Result<Vec<f64>>>()?
482 }
483 _ => vec![args[0].as_number()?],
484 }
485 } else {
486 args.iter()
487 .map(|a| a.as_number())
488 .collect::<Result<Vec<f64>>>()?
489 };
490 if nums.is_empty() {
491 return Err(anyhow!("sum requires at least one number"));
492 }
493 Ok(Value::Number(nums.iter().sum()))
494 }
495
496 Self::Median => {
498 let mut nums = if args.len() == 1 {
499 match &args[0] {
500 Value::List(_) => {
501 let borrowed_heap = heap.borrow();
502 let list = args[0].as_list(&borrowed_heap)?;
503 list.iter()
504 .map(|a| a.as_number())
505 .collect::<Result<Vec<f64>>>()?
506 }
507 _ => vec![args[0].as_number()?],
508 }
509 } else {
510 args.into_iter()
511 .map(|a| a.as_number())
512 .collect::<Result<Vec<f64>>>()?
513 };
514
515 if nums.is_empty() {
516 return Err(anyhow!("median requires at least one number"));
517 }
518
519 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
520 let len = nums.len();
521 if len % 2 == 0 {
522 Ok(Value::Number((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
523 } else {
524 Ok(Value::Number(nums[len / 2]))
525 }
526 }
527
528 Self::Percentile => {
529 let p = args[1].as_number()?;
530 let heap = &heap.borrow();
531 let list = args[0].as_list(heap)?;
532
533 if !(0.0..=100.0).contains(&p) {
534 return Err(anyhow!("percentile must be between 0 and 100"));
535 }
536
537 let mut nums = list
538 .iter()
539 .map(|a| a.as_number())
540 .collect::<Result<Vec<f64>>>()?;
541
542 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
543 let index = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
544
545 Ok(Value::Number(nums[index]))
546 }
547
548 Self::Len => match &args[0] {
550 Value::List(l) => Ok(Value::Number(
551 l.reify(&heap.borrow()).as_list()?.len() as f64
552 )),
553 Value::String(s) => Ok(Value::Number(
554 s.reify(&heap.borrow()).as_string()?.len() as f64
555 )),
556 _ => Err(anyhow!("argument must be a list or string")),
557 },
558
559 Self::Head => match &args[0] {
560 Value::List(p) => Ok(p
561 .reify(&heap.borrow())
562 .as_list()?
563 .first()
564 .copied()
565 .unwrap_or(Value::Null)),
566 Value::String(p) => {
567 let val = {
568 p.reify(&heap.borrow())
569 .as_string()?
570 .get(0..1)
571 .unwrap_or("")
572 .to_string()
573 };
574
575 Ok(heap.borrow_mut().insert_string(val))
576 }
577 _ => Err(anyhow!("argument must be a list or string")),
578 },
579
580 Self::Tail => match &args[0] {
581 Value::List(p) => {
582 let val = {
583 p.reify(&heap.borrow())
584 .as_list()?
585 .get(1..)
586 .unwrap_or([].as_slice())
587 .to_vec()
588 };
589
590 Ok(heap.borrow_mut().insert_list(val))
591 }
592 Value::String(s) => {
593 let val = {
594 s.reify(&heap.borrow())
595 .as_string()?
596 .get(1..)
597 .unwrap_or("")
598 .to_string()
599 };
600
601 Ok(heap.borrow_mut().insert_string(val))
602 }
603 _ => Err(anyhow!("argument must be a list or string")),
604 },
605
606 Self::Slice => {
607 let start = args[1].as_number()? as usize;
608 let end = args[2].as_number()? as usize;
609
610 match args[0] {
611 Value::List(_) => {
612 let l = {
613 let borrowed_heap = &heap.borrow();
614 args[0].as_list(borrowed_heap)?.clone()
615 };
616
617 l.get(start..end)
618 .map_or(Err(anyhow!("index out of bounds")), |l| {
619 Ok(heap.borrow_mut().insert_list(l.to_vec()))
620 })
621 }
622 Value::String(_) => {
623 let s = {
624 let borrowed_heap = &heap.borrow();
625 args[0].as_string(borrowed_heap)?.to_string()
626 };
627
628 s.get(start..end)
629 .map_or(Err(anyhow!("index out of bounds")), |s| {
630 Ok(heap.borrow_mut().insert_string(s.to_string()))
631 })
632 }
633 _ => Err(anyhow!("argument must be a list or string")),
634 }
635 }
636
637 Self::Concat => {
638 let mut list = vec![];
639
640 for arg in args {
641 match arg {
642 Value::List(p) => list.extend(p.reify(&heap.borrow()).as_list()?.clone()),
643 Value::Spread(IterablePointer::List(p)) => {
644 list.extend(p.reify(&heap.borrow()).as_list()?.clone())
645 }
646 Value::Spread(IterablePointer::String(p)) => {
647 let string = {
648 let borrowed_heap = &heap.borrow();
649 p.reify(borrowed_heap).as_string()?.to_string()
650 };
651
652 list.extend(
653 string
654 .chars()
655 .map(|c| heap.borrow_mut().insert_string(c.to_string())),
656 );
657 }
658 _ => list.push(arg),
659 }
660 }
661
662 Ok(heap.borrow_mut().insert_list(list))
663 }
664
665 Self::Dot => {
666 let (a, b) = {
667 let borrowed_heap = &heap.borrow();
668 (
669 args[0].as_list(borrowed_heap)?.clone(),
670 args[1].as_list(borrowed_heap)?.clone(),
671 )
672 };
673
674 if a.len() != b.len() {
675 return Err(anyhow!(
676 "cannot calculate dot product of lists with different lengths"
677 ));
678 }
679
680 Ok(Value::Number(
681 a.iter()
682 .zip(b.iter())
683 .map(|(a, b)| {
684 let a_num = a.as_number()?;
685 let b_num = b.as_number()?;
686 Ok(a_num * b_num)
687 })
688 .collect::<Result<Vec<f64>>>()?
689 .iter()
690 .sum(),
691 ))
692 }
693
694 Self::Unique => {
695 let list = {
696 let borrowed_heap = &heap.borrow();
697 args[0].as_list(borrowed_heap)?.clone()
698 };
699 let mut unique_list = vec![];
700 let borrowed_heap = heap.borrow();
701
702 for item in list {
703 let mut is_duplicate = false;
704 for existing in &unique_list {
705 if item.equals(existing, &borrowed_heap)? {
706 is_duplicate = true;
707 break;
708 }
709 }
710 if !is_duplicate {
711 unique_list.push(item);
712 }
713 }
714
715 drop(borrowed_heap);
716 Ok(heap.borrow_mut().insert_list(unique_list))
717 }
718
719 Self::Sort => {
720 let mut list = {
721 let borrowed_heap = &heap.borrow();
722 args[0].as_list(borrowed_heap)?.clone()
723 };
724 let borrowed_heap = heap.borrow();
725 list.sort_by(|a, b| {
726 a.compare(b, &borrowed_heap)
727 .unwrap_or(None)
728 .unwrap_or(std::cmp::Ordering::Equal)
729 });
730 drop(borrowed_heap);
731 Ok(heap.borrow_mut().insert_list(list))
732 }
733
734 Self::Reverse => {
735 let mut list = { args[0].as_list(&heap.borrow())?.clone() };
736 list.reverse();
737 Ok(heap.borrow_mut().insert_list(list))
738 }
739
740 Self::Split => {
742 let (s, delimeter) = {
743 let borrowed_heap = &heap.borrow();
744 (
745 args[0].as_string(borrowed_heap)?.to_string(),
746 args[1].as_string(borrowed_heap)?.to_string(),
747 )
748 };
749
750 let list = s
751 .split(&delimeter)
752 .map(|s| heap.borrow_mut().insert_string(s.to_string()))
753 .collect();
754
755 Ok(heap.borrow_mut().insert_list(list))
756 }
757
758 Self::Join => {
759 let joined_string = {
760 let borrowed_heap = &heap.borrow();
761 let delimeter = args[1].as_string(borrowed_heap)?;
762 let list = args[0].as_list(borrowed_heap)?;
763 list.iter()
764 .map(|v| v.stringify_internal(borrowed_heap))
765 .collect::<Vec<String>>()
766 .join(delimeter)
767 };
768
769 Ok(heap.borrow_mut().insert_string(joined_string))
770 }
771
772 Self::Replace => {
773 let string = {
774 let borrowed_heap = heap.borrow();
775 let old = args[1].as_string(&borrowed_heap)?.to_string();
776 let new = args[2].as_string(&borrowed_heap)?.to_string();
777 let s = args[0].as_string(&borrowed_heap)?.to_string();
778 s.replace(&old, &new)
779 };
780
781 Ok(heap.borrow_mut().insert_string(string))
782 }
783
784 Self::Trim => {
785 let string = {
786 let borrowed_heap = heap.borrow();
787 args[0].as_string(&borrowed_heap)?.trim().to_string()
788 };
789
790 Ok(heap.borrow_mut().insert_string(string))
791 }
792
793 Self::Uppercase => {
794 let string = args[0].as_string(&heap.borrow())?.to_uppercase();
795 Ok(heap.borrow_mut().insert_string(string))
796 }
797
798 Self::Lowercase => {
799 let string = args[0].as_string(&heap.borrow())?.to_lowercase();
800 Ok(heap.borrow_mut().insert_string(string))
801 }
802
803 Self::ToString => {
804 let string = args[0].stringify_internal(&heap.borrow());
805 Ok(heap.borrow_mut().insert_string(string))
806 }
807
808 Self::ToNumber => Ok(Value::Number(args[0].as_string(&heap.borrow())?.parse()?)),
809
810 Self::Includes => {
811 let needle = {
812 let borrowed_heap = &heap.borrow();
813 args[1].as_string(borrowed_heap)?.to_string()
814 };
815
816 match &args[0].reify(&heap.borrow())? {
817 ReifiedValue::List(l, _) => Ok(Value::Bool(
818 (*l).iter()
819 .any(|v| v.as_string(&heap.borrow()).unwrap().eq(&needle)),
820 )),
821 ReifiedValue::String(s, _) => Ok(Value::Bool(s.contains(&needle))),
822 _ => Err(anyhow!("second argument must be a list or string")),
823 }
824 }
825
826 Self::Format => {
827 let format_str = {
828 let borrowed_heap = &heap.borrow();
829 args[0]
830 .as_string(borrowed_heap)
831 .map_err(|_| anyhow!("first argument must be a string"))?
832 .to_string()
833 };
834
835 let format_args = {
836 let borrowed_heap = &heap.borrow();
837 &args[1..]
838 .iter()
839 .map(|v| v.stringify_internal(borrowed_heap))
840 .collect::<Vec<String>>()
841 };
842
843 Ok(heap
844 .borrow_mut()
845 .insert_string(format_str.format(format_args)))
846 }
847
848 Self::Typeof => Ok(heap
850 .borrow_mut()
851 .insert_string(args[0].get_type().to_string())),
852
853 Self::Arity => {
854 let arity = match args[0] {
855 Value::Lambda(p) => p.reify(&heap.borrow()).as_lambda()?.get_arity(),
856 Value::BuiltIn(built_in) => built_in.arity(),
857 _ => return Err(anyhow!("argument must be a function or built-in function")),
858 };
859
860 Ok(Value::Number(match arity {
861 FunctionArity::Exact(n) => n as f64,
862 FunctionArity::AtLeast(n) => n as f64,
863 FunctionArity::Between(min, _max) => min as f64,
864 }))
865 }
866
867 Self::Keys => {
869 let record = args[0].as_record(&heap.borrow())?.clone();
870 let keys = {
871 let key_strings = record.keys().cloned().collect::<Vec<String>>();
872 key_strings
873 .iter()
874 .map(|k| heap.borrow_mut().insert_string(k.to_string()))
875 .collect()
876 };
877
878 Ok(heap.borrow_mut().insert_list(keys))
879 }
880
881 Self::Values => {
882 let record = args[0].as_record(&heap.borrow())?.clone();
883 let values = record.values().cloned().collect();
884
885 Ok(heap.borrow_mut().insert_list(values))
886 }
887
888 Self::Entries => {
889 let record = args[0].as_record(&heap.borrow())?.clone();
890 let entries = record
891 .iter()
892 .map(|(k, v)| {
893 let entry = {
894 let mut borrowed_heap = heap.borrow_mut();
895 vec![borrowed_heap.insert_string(k.to_string()), *v]
896 };
897 heap.borrow_mut().insert_list(entry)
898 })
899 .collect();
900
901 Ok(heap.borrow_mut().insert_list(entries))
902 }
903
904 #[cfg(not(target_arch = "wasm32"))]
906 Self::Print => {
907 let borrowed_heap = &heap.borrow();
908
909 let output = if args.len() == 1 {
910 args[0].stringify_internal(borrowed_heap)
911 } else {
912 let format_str = args[0].as_string(borrowed_heap).map_err(|_| {
913 anyhow!("first argument must be a formatting string if multiple arguments are given")
914 })?;
915 let format_args = &args[1..]
916 .iter()
917 .map(|v| v.stringify_internal(borrowed_heap))
918 .collect::<Vec<String>>();
919 format_str.format(format_args)
920 };
921
922 eprintln!("{}", output);
924
925 Ok(Value::Null)
926 }
927
928 #[cfg(not(target_arch = "wasm32"))]
929 Self::TimeNow => Ok(Value::Number(
930 std::time::SystemTime::now()
931 .duration_since(std::time::UNIX_EPOCH)
932 .unwrap()
933 .as_secs_f64(),
934 )),
935
936 Self::Map => {
938 let func = &args[1];
939 let list = {
940 let borrowed_heap = &heap.borrow();
941 let list = args[0].as_list(borrowed_heap)?.clone();
942 let func_def = get_function_def(func, borrowed_heap)
943 .ok_or_else(|| anyhow!("second argument must be a function"))?;
944 drop(func_def);
945 list
946 };
947
948 let mut mapped_list = vec![];
949 for item in list {
950 let func_def = get_function_def(func, &heap.borrow())
951 .ok_or_else(|| anyhow!("second argument must be a function"))?;
952 let result = func_def.call(
953 Value::Null,
954 vec![item],
955 Rc::clone(&heap),
956 Rc::clone(&bindings),
957 call_depth + 1,
958 )?;
959 mapped_list.push(result);
960 }
961
962 Ok(heap.borrow_mut().insert_list(mapped_list))
963 }
964
965 Self::Filter => {
966 let func = &args[1];
967 let list = {
968 let borrowed_heap = &heap.borrow();
969 args[0].as_list(borrowed_heap)?.clone()
970 };
971
972 let mut filtered_list = vec![];
973 for item in list {
974 let func_def = get_function_def(func, &heap.borrow())
975 .ok_or_else(|| anyhow!("second argument must be a function"))?;
976 let result = func_def.call(
977 Value::Null,
978 vec![item],
979 Rc::clone(&heap),
980 Rc::clone(&bindings),
981 call_depth + 1,
982 )?;
983 if result.as_bool()? {
984 filtered_list.push(item);
985 }
986 }
987
988 Ok(heap.borrow_mut().insert_list(filtered_list))
989 }
990
991 Self::Reduce => {
992 let func = &args[1];
993 let initial = args[2];
994 let list = {
995 let borrowed_heap = &heap.borrow();
996 args[0].as_list(borrowed_heap)?.clone()
997 };
998
999 let mut accumulator = initial;
1000 for item in list {
1001 let func_def = get_function_def(func, &heap.borrow())
1002 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1003 accumulator = func_def.call(
1004 Value::Null,
1005 vec![accumulator, item],
1006 Rc::clone(&heap),
1007 Rc::clone(&bindings),
1008 call_depth + 1,
1009 )?;
1010 }
1011
1012 Ok(accumulator)
1013 }
1014
1015 Self::Every => {
1016 let func = &args[1];
1017 let list = {
1018 let borrowed_heap = &heap.borrow();
1019 args[0].as_list(borrowed_heap)?.clone()
1020 };
1021
1022 for item in list {
1023 let func_def = get_function_def(func, &heap.borrow())
1024 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1025 let result = func_def.call(
1026 Value::Null,
1027 vec![item],
1028 Rc::clone(&heap),
1029 Rc::clone(&bindings),
1030 call_depth + 1,
1031 )?;
1032 if !result.as_bool()? {
1033 return Ok(Value::Bool(false));
1034 }
1035 }
1036
1037 Ok(Value::Bool(true))
1038 }
1039
1040 Self::Some => {
1041 let func = &args[1];
1042 let list = {
1043 let borrowed_heap = &heap.borrow();
1044 args[0].as_list(borrowed_heap)?.clone()
1045 };
1046
1047 for item in list {
1048 let func_def = get_function_def(func, &heap.borrow())
1049 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1050 let result = func_def.call(
1051 Value::Null,
1052 vec![item],
1053 Rc::clone(&heap),
1054 Rc::clone(&bindings),
1055 call_depth + 1,
1056 )?;
1057 if result.as_bool()? {
1058 return Ok(Value::Bool(true));
1059 }
1060 }
1061
1062 Ok(Value::Bool(false))
1063 }
1064
1065 Self::SortBy => {
1066 let func = &args[1];
1067 let mut list = {
1068 let borrowed_heap = &heap.borrow();
1069 args[0].as_list(borrowed_heap)?.clone()
1070 };
1071
1072 list.sort_by(|a, b| {
1073 let func_def_a = get_function_def(func, &heap.borrow());
1074 let func_def_b = get_function_def(func, &heap.borrow());
1075
1076 match (func_def_a, func_def_b) {
1077 (Some(fd_a), Some(fd_b)) => {
1078 let result_a = fd_a.call(
1079 Value::Null,
1080 vec![*a],
1081 Rc::clone(&heap),
1082 Rc::clone(&bindings),
1083 call_depth + 1,
1084 );
1085 let result_b = fd_b.call(
1086 Value::Null,
1087 vec![*b],
1088 Rc::clone(&heap),
1089 Rc::clone(&bindings),
1090 call_depth + 1,
1091 );
1092
1093 match (result_a, result_b) {
1094 (Ok(val_a), Ok(val_b)) => val_a
1095 .compare(&val_b, &heap.borrow())
1096 .unwrap_or(None)
1097 .unwrap_or(std::cmp::Ordering::Equal),
1098 _ => std::cmp::Ordering::Equal,
1099 }
1100 }
1101 _ => std::cmp::Ordering::Equal,
1102 }
1103 });
1104
1105 Ok(heap.borrow_mut().insert_list(list))
1106 }
1107 }
1108 }
1109
1110 pub fn all() -> Vec<Self> {
1111 vec![
1112 Self::Sqrt,
1113 Self::Sin,
1114 Self::Cos,
1115 Self::Tan,
1116 Self::Asin,
1117 Self::Acos,
1118 Self::Atan,
1119 Self::Log,
1120 Self::Log10,
1121 Self::Exp,
1122 Self::Abs,
1123 Self::Floor,
1124 Self::Ceil,
1125 Self::Round,
1126 Self::Trunc,
1127 Self::Min,
1128 Self::Max,
1129 Self::Avg,
1130 Self::Sum,
1131 Self::Prod,
1132 Self::Median,
1133 Self::Percentile,
1134 Self::Range,
1135 Self::Len,
1136 Self::Head,
1137 Self::Tail,
1138 Self::Slice,
1139 Self::Concat,
1140 Self::Dot,
1141 Self::Unique,
1142 Self::Sort,
1143 Self::SortBy,
1144 Self::Reverse,
1145 Self::Map,
1146 Self::Reduce,
1147 Self::Filter,
1148 Self::Every,
1149 Self::Some,
1150 Self::Split,
1151 Self::Join,
1152 Self::Replace,
1153 Self::Trim,
1154 Self::Uppercase,
1155 Self::Lowercase,
1156 Self::ToString,
1157 Self::ToNumber,
1158 Self::Includes,
1159 Self::Format,
1160 Self::Typeof,
1161 Self::Arity,
1162 Self::Keys,
1163 Self::Values,
1164 Self::Entries,
1165 #[cfg(not(target_arch = "wasm32"))]
1166 Self::Print,
1167 #[cfg(not(target_arch = "wasm32"))]
1168 Self::TimeNow,
1169 ]
1170 }
1171
1172 pub fn all_names() -> Vec<&'static str> {
1173 Self::all().iter().map(|f| f.name()).collect()
1174 }
1175}
1176
1177impl FunctionDef {
1178 pub fn get_name(&self) -> String {
1179 match self {
1180 FunctionDef::BuiltIn(built_in) => {
1181 format!("built-in function \"{}\"", built_in.name())
1182 }
1183 FunctionDef::Lambda(LambdaDef { name, .. }) => name
1184 .clone()
1185 .map_or(String::from("anonymous function"), |n| {
1186 format!("function \"{}\"", n)
1187 }),
1188 }
1189 }
1190
1191 pub fn check_arity(&self, arg_count: usize) -> Result<()> {
1192 match self {
1193 FunctionDef::BuiltIn(built_in) => match built_in.arity() {
1194 FunctionArity::Exact(expected) => {
1195 if arg_count == expected {
1196 Ok(())
1197 } else {
1198 Err(anyhow!(
1199 "{} takes exactly {} arguments, but {} were given",
1200 self.get_name(),
1201 expected,
1202 arg_count
1203 ))
1204 }
1205 }
1206 FunctionArity::AtLeast(expected) => {
1207 if arg_count >= expected {
1208 Ok(())
1209 } else {
1210 Err(anyhow!(
1211 "{} takes at least {} arguments, but {} were given",
1212 self.get_name(),
1213 expected,
1214 arg_count
1215 ))
1216 }
1217 }
1218 FunctionArity::Between(min, max) => {
1219 if arg_count >= min && arg_count <= max {
1220 Ok(())
1221 } else {
1222 Err(anyhow!(
1223 "{} takes between {} and {} arguments, but {} were given",
1224 self.get_name(),
1225 min,
1226 max,
1227 arg_count
1228 ))
1229 }
1230 }
1231 },
1232 FunctionDef::Lambda(def) => {
1233 let arity = def.get_arity();
1234
1235 match arity {
1236 FunctionArity::Exact(expected) => {
1237 if arg_count == expected {
1238 Ok(())
1239 } else {
1240 Err(anyhow!(
1241 "{} takes exactly {} arguments, but {} were given",
1242 self.get_name(),
1243 expected,
1244 arg_count
1245 ))
1246 }
1247 }
1248 FunctionArity::AtLeast(expected) => {
1249 if arg_count >= expected {
1250 Ok(())
1251 } else {
1252 Err(anyhow!(
1253 "{} takes at least {} arguments, but {} were given",
1254 self.get_name(),
1255 expected,
1256 arg_count
1257 ))
1258 }
1259 }
1260 FunctionArity::Between(min, max) => {
1261 if arg_count >= min && arg_count <= max {
1262 Ok(())
1263 } else {
1264 Err(anyhow!(
1265 "{} takes between {} and {} arguments, but {} were given",
1266 self.get_name(),
1267 min,
1268 max,
1269 arg_count
1270 ))
1271 }
1272 }
1273 }
1274 }
1275 }
1276 }
1277
1278 pub fn call(
1279 &self,
1280 this_value: Value,
1281 args: Vec<Value>,
1282 heap: Rc<RefCell<Heap>>,
1283 bindings: Rc<RefCell<HashMap<String, Value>>>,
1284 call_depth: usize,
1285 ) -> Result<Value> {
1286 #[cfg(not(target_arch = "wasm32"))]
1287 let start = std::time::Instant::now();
1288
1289 self.check_arity(args.len())?;
1290
1291 if call_depth > 1000 {
1292 return Err(anyhow!(
1293 "in {}: maximum call depth of 1000 exceeded",
1294 self.get_name()
1295 ));
1296 }
1297
1298 match self {
1299 FunctionDef::Lambda(LambdaDef {
1300 name,
1301 args: expected_args,
1302 body,
1303 scope,
1304 }) => {
1305 #[cfg(not(target_arch = "wasm32"))]
1306 let start_var_env = std::time::Instant::now();
1307
1308 let mut new_bindings = bindings.borrow().clone();
1310
1311 for (key, value) in scope {
1313 new_bindings.insert(key.clone(), *value);
1314 }
1315
1316 if let Some(fn_name) = name {
1318 new_bindings.insert(fn_name.clone(), this_value);
1319 }
1320
1321 if let Some(inputs) = new_bindings.get("inputs").copied() {
1323 new_bindings.insert(String::from("inputs"), inputs);
1324 }
1325
1326 for (idx, expected_arg) in expected_args.iter().enumerate() {
1328 match expected_arg {
1329 LambdaArg::Required(arg_name) => {
1330 new_bindings.insert(arg_name.clone(), args[idx]);
1331 }
1332 LambdaArg::Optional(arg_name) => {
1333 new_bindings.insert(
1334 arg_name.clone(),
1335 args.get(idx).copied().unwrap_or(Value::Null),
1336 );
1337 }
1338 LambdaArg::Rest(arg_name) => {
1339 new_bindings.insert(
1340 arg_name.clone(),
1341 heap.borrow_mut()
1342 .insert_list(args.iter().skip(idx).copied().collect()),
1343 );
1344 }
1345 }
1346 }
1347
1348 #[cfg(not(target_arch = "wasm32"))]
1349 let end_var_env = std::time::Instant::now();
1350
1351 let return_value = evaluate_ast(
1352 body,
1353 Rc::clone(&heap),
1354 Rc::new(RefCell::new(new_bindings)),
1355 call_depth + 1,
1356 )
1357 .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1358
1359 #[cfg(not(target_arch = "wasm32"))]
1360 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1361 name: self.get_name(),
1362 start,
1363 end: std::time::Instant::now(),
1364 start_var_env: Some(start_var_env),
1365 end_var_env: Some(end_var_env),
1366 });
1367
1368 return_value
1369 }
1370 FunctionDef::BuiltIn(built_in) => {
1371 let return_value = built_in
1372 .call(args, heap, bindings, call_depth + 1)
1373 .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1374
1375 #[cfg(not(target_arch = "wasm32"))]
1376 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1377 name: self.get_name(),
1378 start,
1379 end: std::time::Instant::now(),
1380 start_var_env: None,
1381 end_var_env: None,
1382 });
1383
1384 return_value
1385 }
1386 }
1387 }
1388}
1389
1390pub fn is_built_in_function(ident: &str) -> bool {
1391 BuiltInFunction::from_ident(ident).is_some()
1392}
1393
1394pub fn get_built_in_function_def_by_ident(ident: &str) -> Option<FunctionDef> {
1395 BuiltInFunction::from_ident(ident).map(FunctionDef::BuiltIn)
1396}
1397
1398pub fn get_built_in_function_idents() -> Vec<&'static str> {
1399 BuiltInFunction::all_names()
1400}
1401
1402pub fn get_function_def(value: &Value, heap: &Heap) -> Option<FunctionDef> {
1403 match value {
1404 Value::Lambda(pointer) => Some(FunctionDef::Lambda(
1405 pointer.reify(heap).as_lambda().ok()?.clone(),
1406 )),
1407 Value::BuiltIn(built_in) => Some(FunctionDef::BuiltIn(*built_in)),
1408 _ => None,
1409 }
1410}
1411
1412#[cfg(test)]
1413mod tests {
1414 use super::*;
1415 use std::cell::RefCell;
1416 use std::collections::HashMap;
1417 use std::rc::Rc;
1418
1419 #[test]
1420 fn test_range_function() {
1421 let heap = Rc::new(RefCell::new(Heap::new()));
1423 let bindings = Rc::new(RefCell::new(HashMap::new()));
1424 let range_fn = BuiltInFunction::Range;
1425
1426 let args = vec![Value::Number(4.0)];
1428 let result = range_fn
1429 .call(args, heap.clone(), bindings.clone(), 0)
1430 .unwrap();
1431
1432 let heap_borrow = heap.borrow();
1433 let list = result.as_list(&heap_borrow).unwrap();
1434 assert_eq!(list.len(), 4);
1435 assert_eq!(list[0], Value::Number(0.0));
1436 assert_eq!(list[3], Value::Number(3.0));
1437 }
1438
1439 #[test]
1440 fn test_range_function_two_args() {
1441 let heap = Rc::new(RefCell::new(Heap::new()));
1443 let bindings = Rc::new(RefCell::new(HashMap::new()));
1444 let range_fn = BuiltInFunction::Range;
1445
1446 let args = vec![Value::Number(4.0), Value::Number(10.0)];
1448 let result = range_fn
1449 .call(args, heap.clone(), bindings.clone(), 0)
1450 .unwrap();
1451
1452 let heap_borrow = heap.borrow();
1453 let list = result.as_list(&heap_borrow).unwrap();
1454 assert_eq!(list.len(), 6);
1455 assert_eq!(list[0], Value::Number(4.0));
1456 assert_eq!(list[5], Value::Number(9.0));
1457 }
1458
1459 #[test]
1460 fn test_round_function_single_arg() {
1461 let heap = Rc::new(RefCell::new(Heap::new()));
1462 let bindings = Rc::new(RefCell::new(HashMap::new()));
1463 let round_fn = BuiltInFunction::Round;
1464
1465 let test_cases = vec![
1467 (42.4, 42.0),
1468 (42.5, 43.0),
1469 (42.6, 43.0),
1470 (-42.4, -42.0),
1471 (-42.5, -43.0),
1472 (-42.6, -43.0),
1473 (0.0, 0.0),
1474 (1.999, 2.0),
1475 (-1.999, -2.0),
1476 ];
1477
1478 for (input, expected) in test_cases {
1479 let args = vec![Value::Number(input)];
1480 let result = round_fn
1481 .call(args, heap.clone(), bindings.clone(), 0)
1482 .unwrap();
1483 assert_eq!(
1484 result,
1485 Value::Number(expected),
1486 "round({}) should be {}",
1487 input,
1488 expected
1489 );
1490 }
1491 }
1492
1493 #[test]
1494 fn test_round_function_with_decimal_places() {
1495 let heap = Rc::new(RefCell::new(Heap::new()));
1496 let bindings = Rc::new(RefCell::new(HashMap::new()));
1497 let round_fn = BuiltInFunction::Round;
1498
1499 let test_cases = vec![
1501 (42.4543, 0.0, 42.0),
1502 (42.4543, 1.0, 42.5),
1503 (42.4543, 2.0, 42.45),
1504 (42.4543, 3.0, 42.454),
1505 (42.4543, 4.0, 42.4543),
1506 (4.14159, 4.0, 4.1416),
1507 (0.005, 2.0, 0.01),
1508 (0.995, 2.0, 1.0),
1509 (9.995, 2.0, 9.99),
1512 (-9.995, 2.0, -9.99),
1513 ];
1514
1515 for (input, places, expected) in test_cases {
1516 let args = vec![Value::Number(input), Value::Number(places)];
1517 let result = round_fn
1518 .call(args, heap.clone(), bindings.clone(), 0)
1519 .unwrap();
1520
1521 if let Value::Number(result_num) = result {
1523 assert!(
1524 (result_num - expected).abs() < 1e-10,
1525 "round({}, {}) = {} should be close to {}",
1526 input,
1527 places,
1528 result_num,
1529 expected
1530 );
1531 } else {
1532 panic!("Expected number result");
1533 }
1534 }
1535 }
1536
1537 #[test]
1538 fn test_round_function_negative_decimal_places() {
1539 let heap = Rc::new(RefCell::new(Heap::new()));
1540 let bindings = Rc::new(RefCell::new(HashMap::new()));
1541 let round_fn = BuiltInFunction::Round;
1542
1543 let test_cases = vec![
1545 (1234.567, -1.0, 1230.0),
1546 (1234.567, -2.0, 1200.0),
1547 (1234.567, -3.0, 1000.0),
1548 (1234.567, -4.0, 0.0),
1549 (5678.9, -1.0, 5680.0),
1550 (5678.9, -2.0, 5700.0),
1551 (5678.9, -3.0, 6000.0),
1552 (-1234.567, -1.0, -1230.0),
1553 (-1234.567, -2.0, -1200.0),
1554 (1500.0, -3.0, 2000.0),
1555 (-1500.0, -3.0, -2000.0),
1556 ];
1557
1558 for (input, places, expected) in test_cases {
1559 let args = vec![Value::Number(input), Value::Number(places)];
1560 let result = round_fn
1561 .call(args, heap.clone(), bindings.clone(), 0)
1562 .unwrap();
1563 assert_eq!(
1564 result,
1565 Value::Number(expected),
1566 "round({}, {}) should be {}",
1567 input,
1568 places,
1569 expected
1570 );
1571 }
1572 }
1573
1574 #[test]
1575 fn test_round_function_edge_cases() {
1576 let heap = Rc::new(RefCell::new(Heap::new()));
1577 let bindings = Rc::new(RefCell::new(HashMap::new()));
1578 let round_fn = BuiltInFunction::Round;
1579
1580 let test_cases = vec![
1582 (f64::INFINITY, 0.0, f64::INFINITY),
1583 (f64::NEG_INFINITY, 0.0, f64::NEG_INFINITY),
1584 (0.0, 5.0, 0.0),
1585 (-0.0, 5.0, -0.0),
1586 (1e-10, 5.0, 0.0),
1587 (1e-10, 15.0, 1e-10),
1588 ];
1589
1590 for (input, places, expected) in test_cases {
1591 let args = vec![Value::Number(input), Value::Number(places)];
1592 let result = round_fn
1593 .call(args, heap.clone(), bindings.clone(), 0)
1594 .unwrap();
1595
1596 if let Value::Number(result_num) = result {
1597 if expected.is_infinite() {
1598 assert!(
1599 result_num.is_infinite()
1600 && result_num.is_sign_positive() == expected.is_sign_positive(),
1601 "round({}, {}) should be {}",
1602 input,
1603 places,
1604 expected
1605 );
1606 } else if expected == 0.0 || expected == -0.0 {
1607 assert!(
1608 result_num.abs() < 1e-10,
1609 "round({}, {}) = {} should be close to 0",
1610 input,
1611 places,
1612 result_num
1613 );
1614 } else {
1615 assert!(
1616 (result_num - expected).abs() < 1e-15,
1617 "round({}, {}) = {} should be close to {}",
1618 input,
1619 places,
1620 result_num,
1621 expected
1622 );
1623 }
1624 } else {
1625 panic!("Expected number result");
1626 }
1627 }
1628 }
1629}