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