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, func_accepts_two_args) = {
1001 let borrowed_heap = &heap.borrow();
1002 let list = args[0].as_list(borrowed_heap)?.clone();
1003 let func_def = get_function_def(func, borrowed_heap)
1004 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1005
1006 let accepts_two = func_def.arity().can_accept(2);
1008
1009 (list, accepts_two)
1010 };
1011
1012 let mut mapped_list = vec![];
1013 for (idx, item) in list.iter().enumerate() {
1014 let func_def = get_function_def(func, &heap.borrow())
1015 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1016
1017 let args = if func_accepts_two_args {
1018 vec![*item, Value::Number(idx as f64)]
1019 } else {
1020 vec![*item]
1021 };
1022
1023 let result = func_def.call(
1024 Value::Null,
1025 args,
1026 Rc::clone(&heap),
1027 Rc::clone(&bindings),
1028 call_depth + 1,
1029 )?;
1030 mapped_list.push(result);
1031 }
1032
1033 Ok(heap.borrow_mut().insert_list(mapped_list))
1034 }
1035
1036 Self::Filter => {
1037 let func = &args[1];
1038 let (list, func_accepts_two_args) = {
1039 let borrowed_heap = &heap.borrow();
1040 let list = args[0].as_list(borrowed_heap)?.clone();
1041 let func_def = get_function_def(func, borrowed_heap)
1042 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1043
1044 let accepts_two = func_def.arity().can_accept(2);
1046
1047 (list, accepts_two)
1048 };
1049
1050 let mut filtered_list = vec![];
1051 for (idx, item) in list.iter().enumerate() {
1052 let func_def = get_function_def(func, &heap.borrow())
1053 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1054
1055 let args = if func_accepts_two_args {
1056 vec![*item, Value::Number(idx as f64)]
1057 } else {
1058 vec![*item]
1059 };
1060
1061 let result = func_def.call(
1062 Value::Null,
1063 args,
1064 Rc::clone(&heap),
1065 Rc::clone(&bindings),
1066 call_depth + 1,
1067 )?;
1068 if result.as_bool()? {
1069 filtered_list.push(*item);
1070 }
1071 }
1072
1073 Ok(heap.borrow_mut().insert_list(filtered_list))
1074 }
1075
1076 Self::Reduce => {
1077 let func = &args[1];
1078 let initial = args[2];
1079 let (list, func_accepts_three_args) = {
1080 let borrowed_heap = &heap.borrow();
1081 let list = args[0].as_list(borrowed_heap)?.clone();
1082 let func_def = get_function_def(func, borrowed_heap)
1083 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1084
1085 let accepts_three = func_def.arity().can_accept(3);
1087
1088 (list, accepts_three)
1089 };
1090
1091 let mut accumulator = initial;
1092 for (idx, item) in list.iter().enumerate() {
1093 let func_def = get_function_def(func, &heap.borrow())
1094 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1095
1096 let args = if func_accepts_three_args {
1097 vec![accumulator, *item, Value::Number(idx as f64)]
1098 } else {
1099 vec![accumulator, *item]
1100 };
1101
1102 accumulator = func_def.call(
1103 Value::Null,
1104 args,
1105 Rc::clone(&heap),
1106 Rc::clone(&bindings),
1107 call_depth + 1,
1108 )?;
1109 }
1110
1111 Ok(accumulator)
1112 }
1113
1114 Self::Every => {
1115 let func = &args[1];
1116 let (list, func_accepts_two_args) = {
1117 let borrowed_heap = &heap.borrow();
1118 let list = args[0].as_list(borrowed_heap)?.clone();
1119 let func_def = get_function_def(func, borrowed_heap)
1120 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1121
1122 let accepts_two = func_def.arity().can_accept(2);
1124
1125 (list, accepts_two)
1126 };
1127
1128 for (idx, item) in list.iter().enumerate() {
1129 let func_def = get_function_def(func, &heap.borrow())
1130 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1131
1132 let args = if func_accepts_two_args {
1133 vec![*item, Value::Number(idx as f64)]
1134 } else {
1135 vec![*item]
1136 };
1137
1138 let result = func_def.call(
1139 Value::Null,
1140 args,
1141 Rc::clone(&heap),
1142 Rc::clone(&bindings),
1143 call_depth + 1,
1144 )?;
1145 if !result.as_bool()? {
1146 return Ok(Value::Bool(false));
1147 }
1148 }
1149
1150 Ok(Value::Bool(true))
1151 }
1152
1153 Self::Some => {
1154 let func = &args[1];
1155 let (list, func_accepts_two_args) = {
1156 let borrowed_heap = &heap.borrow();
1157 let list = args[0].as_list(borrowed_heap)?.clone();
1158 let func_def = get_function_def(func, borrowed_heap)
1159 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1160
1161 let accepts_two = func_def.arity().can_accept(2);
1163
1164 (list, accepts_two)
1165 };
1166
1167 for (idx, item) in list.iter().enumerate() {
1168 let func_def = get_function_def(func, &heap.borrow())
1169 .ok_or_else(|| anyhow!("second argument must be a function"))?;
1170
1171 let args = if func_accepts_two_args {
1172 vec![*item, Value::Number(idx as f64)]
1173 } else {
1174 vec![*item]
1175 };
1176
1177 let result = func_def.call(
1178 Value::Null,
1179 args,
1180 Rc::clone(&heap),
1181 Rc::clone(&bindings),
1182 call_depth + 1,
1183 )?;
1184 if result.as_bool()? {
1185 return Ok(Value::Bool(true));
1186 }
1187 }
1188
1189 Ok(Value::Bool(false))
1190 }
1191
1192 Self::SortBy => {
1193 let func = &args[1];
1194 let mut list = {
1195 let borrowed_heap = &heap.borrow();
1196 args[0].as_list(borrowed_heap)?.clone()
1197 };
1198
1199 list.sort_by(|a, b| {
1200 let func_def_a = get_function_def(func, &heap.borrow());
1201 let func_def_b = get_function_def(func, &heap.borrow());
1202
1203 match (func_def_a, func_def_b) {
1204 (Some(fd_a), Some(fd_b)) => {
1205 let result_a = fd_a.call(
1206 Value::Null,
1207 vec![*a],
1208 Rc::clone(&heap),
1209 Rc::clone(&bindings),
1210 call_depth + 1,
1211 );
1212 let result_b = fd_b.call(
1213 Value::Null,
1214 vec![*b],
1215 Rc::clone(&heap),
1216 Rc::clone(&bindings),
1217 call_depth + 1,
1218 );
1219
1220 match (result_a, result_b) {
1221 (Ok(val_a), Ok(val_b)) => val_a
1222 .compare(&val_b, &heap.borrow())
1223 .unwrap_or(None)
1224 .unwrap_or(std::cmp::Ordering::Equal),
1225 _ => std::cmp::Ordering::Equal,
1226 }
1227 }
1228 _ => std::cmp::Ordering::Equal,
1229 }
1230 });
1231
1232 Ok(heap.borrow_mut().insert_list(list))
1233 }
1234 }
1235 }
1236
1237 pub fn all() -> Vec<Self> {
1238 vec![
1239 Self::Sqrt,
1240 Self::Sin,
1241 Self::Cos,
1242 Self::Tan,
1243 Self::Asin,
1244 Self::Acos,
1245 Self::Atan,
1246 Self::Log,
1247 Self::Log10,
1248 Self::Exp,
1249 Self::Abs,
1250 Self::Floor,
1251 Self::Ceil,
1252 Self::Round,
1253 Self::Trunc,
1254 Self::Min,
1255 Self::Max,
1256 Self::Avg,
1257 Self::Sum,
1258 Self::Prod,
1259 Self::Median,
1260 Self::Percentile,
1261 Self::Range,
1262 Self::Len,
1263 Self::Head,
1264 Self::Tail,
1265 Self::Slice,
1266 Self::Concat,
1267 Self::Dot,
1268 Self::Unique,
1269 Self::Sort,
1270 Self::SortBy,
1271 Self::Reverse,
1272 Self::Any,
1273 Self::All,
1274 Self::Map,
1275 Self::Reduce,
1276 Self::Filter,
1277 Self::Every,
1278 Self::Some,
1279 Self::Split,
1280 Self::Join,
1281 Self::Replace,
1282 Self::Trim,
1283 Self::Uppercase,
1284 Self::Lowercase,
1285 Self::ToString,
1286 Self::ToNumber,
1287 Self::ToBool,
1288 Self::Includes,
1289 Self::Format,
1290 Self::Typeof,
1291 Self::Arity,
1292 Self::Keys,
1293 Self::Values,
1294 Self::Entries,
1295 #[cfg(not(target_arch = "wasm32"))]
1296 Self::Print,
1297 #[cfg(not(target_arch = "wasm32"))]
1298 Self::TimeNow,
1299 ]
1300 }
1301
1302 pub fn all_names() -> Vec<&'static str> {
1303 Self::all().iter().map(|f| f.name()).collect()
1304 }
1305}
1306
1307impl FunctionDef {
1308 pub fn get_name(&self) -> String {
1309 match self {
1310 FunctionDef::BuiltIn(built_in) => {
1311 format!("built-in function \"{}\"", built_in.name())
1312 }
1313 FunctionDef::Lambda(LambdaDef { name, .. }) => name
1314 .clone()
1315 .map_or(String::from("anonymous function"), |n| {
1316 format!("function \"{}\"", n)
1317 }),
1318 }
1319 }
1320
1321 pub fn arity(&self) -> FunctionArity {
1322 match self {
1323 FunctionDef::BuiltIn(built_in) => built_in.arity(),
1324 FunctionDef::Lambda(lambda_def) => lambda_def.get_arity(),
1325 }
1326 }
1327
1328 pub fn check_arity(&self, arg_count: usize) -> Result<()> {
1329 match self {
1330 FunctionDef::BuiltIn(built_in) => match built_in.arity() {
1331 FunctionArity::Exact(expected) => {
1332 if arg_count == expected {
1333 Ok(())
1334 } else {
1335 Err(anyhow!(
1336 "{} takes exactly {} arguments, but {} were given",
1337 self.get_name(),
1338 expected,
1339 arg_count
1340 ))
1341 }
1342 }
1343 FunctionArity::AtLeast(expected) => {
1344 if arg_count >= expected {
1345 Ok(())
1346 } else {
1347 Err(anyhow!(
1348 "{} takes at least {} arguments, but {} were given",
1349 self.get_name(),
1350 expected,
1351 arg_count
1352 ))
1353 }
1354 }
1355 FunctionArity::Between(min, max) => {
1356 if arg_count >= min && arg_count <= max {
1357 Ok(())
1358 } else {
1359 Err(anyhow!(
1360 "{} takes between {} and {} arguments, but {} were given",
1361 self.get_name(),
1362 min,
1363 max,
1364 arg_count
1365 ))
1366 }
1367 }
1368 },
1369 FunctionDef::Lambda(def) => {
1370 let arity = def.get_arity();
1371
1372 match arity {
1373 FunctionArity::Exact(expected) => {
1374 if arg_count == expected {
1375 Ok(())
1376 } else {
1377 Err(anyhow!(
1378 "{} takes exactly {} arguments, but {} were given",
1379 self.get_name(),
1380 expected,
1381 arg_count
1382 ))
1383 }
1384 }
1385 FunctionArity::AtLeast(expected) => {
1386 if arg_count >= expected {
1387 Ok(())
1388 } else {
1389 Err(anyhow!(
1390 "{} takes at least {} arguments, but {} were given",
1391 self.get_name(),
1392 expected,
1393 arg_count
1394 ))
1395 }
1396 }
1397 FunctionArity::Between(min, max) => {
1398 if arg_count >= min && arg_count <= max {
1399 Ok(())
1400 } else {
1401 Err(anyhow!(
1402 "{} takes between {} and {} arguments, but {} were given",
1403 self.get_name(),
1404 min,
1405 max,
1406 arg_count
1407 ))
1408 }
1409 }
1410 }
1411 }
1412 }
1413 }
1414
1415 pub fn call(
1416 &self,
1417 this_value: Value,
1418 args: Vec<Value>,
1419 heap: Rc<RefCell<Heap>>,
1420 bindings: Rc<RefCell<HashMap<String, Value>>>,
1421 call_depth: usize,
1422 ) -> Result<Value> {
1423 #[cfg(not(target_arch = "wasm32"))]
1424 let start = std::time::Instant::now();
1425
1426 self.check_arity(args.len())?;
1427
1428 if call_depth > 1000 {
1429 return Err(anyhow!(
1430 "in {}: maximum call depth of 1000 exceeded",
1431 self.get_name()
1432 ));
1433 }
1434
1435 match self {
1436 FunctionDef::Lambda(LambdaDef {
1437 name,
1438 args: expected_args,
1439 body,
1440 scope,
1441 }) => {
1442 #[cfg(not(target_arch = "wasm32"))]
1443 let start_var_env = std::time::Instant::now();
1444
1445 let mut new_bindings = bindings.borrow().clone();
1447
1448 for (key, value) in scope {
1450 new_bindings.insert(key.clone(), *value);
1451 }
1452
1453 if let Some(fn_name) = name {
1455 new_bindings.insert(fn_name.clone(), this_value);
1456 }
1457
1458 if let Some(inputs) = new_bindings.get("inputs").copied() {
1460 new_bindings.insert(String::from("inputs"), inputs);
1461 }
1462
1463 for (idx, expected_arg) in expected_args.iter().enumerate() {
1465 match expected_arg {
1466 LambdaArg::Required(arg_name) => {
1467 new_bindings.insert(arg_name.clone(), args[idx]);
1468 }
1469 LambdaArg::Optional(arg_name) => {
1470 new_bindings.insert(
1471 arg_name.clone(),
1472 args.get(idx).copied().unwrap_or(Value::Null),
1473 );
1474 }
1475 LambdaArg::Rest(arg_name) => {
1476 new_bindings.insert(
1477 arg_name.clone(),
1478 heap.borrow_mut()
1479 .insert_list(args.iter().skip(idx).copied().collect()),
1480 );
1481 }
1482 }
1483 }
1484
1485 #[cfg(not(target_arch = "wasm32"))]
1486 let end_var_env = std::time::Instant::now();
1487
1488 let return_value = evaluate_ast(
1489 body,
1490 Rc::clone(&heap),
1491 Rc::new(RefCell::new(new_bindings)),
1492 call_depth + 1,
1493 )
1494 .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1495
1496 #[cfg(not(target_arch = "wasm32"))]
1497 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1498 name: self.get_name(),
1499 start,
1500 end: std::time::Instant::now(),
1501 start_var_env: Some(start_var_env),
1502 end_var_env: Some(end_var_env),
1503 });
1504
1505 return_value
1506 }
1507 FunctionDef::BuiltIn(built_in) => {
1508 let return_value = built_in
1509 .call(args, heap, bindings, call_depth + 1)
1510 .map_err(|error| anyhow!("in {}: {}", self.get_name(), error));
1511
1512 #[cfg(not(target_arch = "wasm32"))]
1513 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1514 name: self.get_name(),
1515 start,
1516 end: std::time::Instant::now(),
1517 start_var_env: None,
1518 end_var_env: None,
1519 });
1520
1521 return_value
1522 }
1523 }
1524 }
1525}
1526
1527pub fn is_built_in_function(ident: &str) -> bool {
1528 BuiltInFunction::from_ident(ident).is_some()
1529}
1530
1531pub fn get_built_in_function_def_by_ident(ident: &str) -> Option<FunctionDef> {
1532 BuiltInFunction::from_ident(ident).map(FunctionDef::BuiltIn)
1533}
1534
1535pub fn get_built_in_function_idents() -> Vec<&'static str> {
1536 BuiltInFunction::all_names()
1537}
1538
1539pub fn get_function_def(value: &Value, heap: &Heap) -> Option<FunctionDef> {
1540 match value {
1541 Value::Lambda(pointer) => Some(FunctionDef::Lambda(
1542 pointer.reify(heap).as_lambda().ok()?.clone(),
1543 )),
1544 Value::BuiltIn(built_in) => Some(FunctionDef::BuiltIn(*built_in)),
1545 _ => None,
1546 }
1547}
1548
1549#[cfg(test)]
1550mod tests {
1551 use super::*;
1552 use std::cell::RefCell;
1553 use std::collections::HashMap;
1554 use std::rc::Rc;
1555
1556 #[test]
1557 fn test_range_function() {
1558 let heap = Rc::new(RefCell::new(Heap::new()));
1560 let bindings = Rc::new(RefCell::new(HashMap::new()));
1561 let range_fn = BuiltInFunction::Range;
1562
1563 let args = vec![Value::Number(4.0)];
1565 let result = range_fn
1566 .call(args, heap.clone(), bindings.clone(), 0)
1567 .unwrap();
1568
1569 let heap_borrow = heap.borrow();
1570 let list = result.as_list(&heap_borrow).unwrap();
1571 assert_eq!(list.len(), 4);
1572 assert_eq!(list[0], Value::Number(0.0));
1573 assert_eq!(list[3], Value::Number(3.0));
1574 }
1575
1576 #[test]
1577 fn test_range_function_two_args() {
1578 let heap = Rc::new(RefCell::new(Heap::new()));
1580 let bindings = Rc::new(RefCell::new(HashMap::new()));
1581 let range_fn = BuiltInFunction::Range;
1582
1583 let args = vec![Value::Number(4.0), Value::Number(10.0)];
1585 let result = range_fn
1586 .call(args, heap.clone(), bindings.clone(), 0)
1587 .unwrap();
1588
1589 let heap_borrow = heap.borrow();
1590 let list = result.as_list(&heap_borrow).unwrap();
1591 assert_eq!(list.len(), 6);
1592 assert_eq!(list[0], Value::Number(4.0));
1593 assert_eq!(list[5], Value::Number(9.0));
1594 }
1595
1596 #[test]
1597 fn test_round_function_single_arg() {
1598 let heap = Rc::new(RefCell::new(Heap::new()));
1599 let bindings = Rc::new(RefCell::new(HashMap::new()));
1600 let round_fn = BuiltInFunction::Round;
1601
1602 let test_cases = vec![
1604 (42.4, 42.0),
1605 (42.5, 43.0),
1606 (42.6, 43.0),
1607 (-42.4, -42.0),
1608 (-42.5, -43.0),
1609 (-42.6, -43.0),
1610 (0.0, 0.0),
1611 (1.999, 2.0),
1612 (-1.999, -2.0),
1613 ];
1614
1615 for (input, expected) in test_cases {
1616 let args = vec![Value::Number(input)];
1617 let result = round_fn
1618 .call(args, heap.clone(), bindings.clone(), 0)
1619 .unwrap();
1620 assert_eq!(
1621 result,
1622 Value::Number(expected),
1623 "round({}) should be {}",
1624 input,
1625 expected
1626 );
1627 }
1628 }
1629
1630 #[test]
1631 fn test_round_function_with_decimal_places() {
1632 let heap = Rc::new(RefCell::new(Heap::new()));
1633 let bindings = Rc::new(RefCell::new(HashMap::new()));
1634 let round_fn = BuiltInFunction::Round;
1635
1636 let test_cases = vec![
1638 (42.4543, 0.0, 42.0),
1639 (42.4543, 1.0, 42.5),
1640 (42.4543, 2.0, 42.45),
1641 (42.4543, 3.0, 42.454),
1642 (42.4543, 4.0, 42.4543),
1643 (4.14159, 4.0, 4.1416),
1644 (0.005, 2.0, 0.01),
1645 (0.995, 2.0, 1.0),
1646 (9.995, 2.0, 9.99),
1649 (-9.995, 2.0, -9.99),
1650 ];
1651
1652 for (input, places, expected) in test_cases {
1653 let args = vec![Value::Number(input), Value::Number(places)];
1654 let result = round_fn
1655 .call(args, heap.clone(), bindings.clone(), 0)
1656 .unwrap();
1657
1658 if let Value::Number(result_num) = result {
1660 assert!(
1661 (result_num - expected).abs() < 1e-10,
1662 "round({}, {}) = {} should be close to {}",
1663 input,
1664 places,
1665 result_num,
1666 expected
1667 );
1668 } else {
1669 panic!("Expected number result");
1670 }
1671 }
1672 }
1673
1674 #[test]
1675 fn test_round_function_negative_decimal_places() {
1676 let heap = Rc::new(RefCell::new(Heap::new()));
1677 let bindings = Rc::new(RefCell::new(HashMap::new()));
1678 let round_fn = BuiltInFunction::Round;
1679
1680 let test_cases = vec![
1682 (1234.567, -1.0, 1230.0),
1683 (1234.567, -2.0, 1200.0),
1684 (1234.567, -3.0, 1000.0),
1685 (1234.567, -4.0, 0.0),
1686 (5678.9, -1.0, 5680.0),
1687 (5678.9, -2.0, 5700.0),
1688 (5678.9, -3.0, 6000.0),
1689 (-1234.567, -1.0, -1230.0),
1690 (-1234.567, -2.0, -1200.0),
1691 (1500.0, -3.0, 2000.0),
1692 (-1500.0, -3.0, -2000.0),
1693 ];
1694
1695 for (input, places, expected) in test_cases {
1696 let args = vec![Value::Number(input), Value::Number(places)];
1697 let result = round_fn
1698 .call(args, heap.clone(), bindings.clone(), 0)
1699 .unwrap();
1700 assert_eq!(
1701 result,
1702 Value::Number(expected),
1703 "round({}, {}) should be {}",
1704 input,
1705 places,
1706 expected
1707 );
1708 }
1709 }
1710
1711 #[test]
1712 fn test_round_function_edge_cases() {
1713 let heap = Rc::new(RefCell::new(Heap::new()));
1714 let bindings = Rc::new(RefCell::new(HashMap::new()));
1715 let round_fn = BuiltInFunction::Round;
1716
1717 let test_cases = vec![
1719 (f64::INFINITY, 0.0, f64::INFINITY),
1720 (f64::NEG_INFINITY, 0.0, f64::NEG_INFINITY),
1721 (0.0, 5.0, 0.0),
1722 (-0.0, 5.0, -0.0),
1723 (1e-10, 5.0, 0.0),
1724 (1e-10, 15.0, 1e-10),
1725 ];
1726
1727 for (input, places, expected) in test_cases {
1728 let args = vec![Value::Number(input), Value::Number(places)];
1729 let result = round_fn
1730 .call(args, heap.clone(), bindings.clone(), 0)
1731 .unwrap();
1732
1733 if let Value::Number(result_num) = result {
1734 if expected.is_infinite() {
1735 assert!(
1736 result_num.is_infinite()
1737 && result_num.is_sign_positive() == expected.is_sign_positive(),
1738 "round({}, {}) should be {}",
1739 input,
1740 places,
1741 expected
1742 );
1743 } else if expected == 0.0 || expected == -0.0 {
1744 assert!(
1745 result_num.abs() < 1e-10,
1746 "round({}, {}) = {} should be close to 0",
1747 input,
1748 places,
1749 result_num
1750 );
1751 } else {
1752 assert!(
1753 (result_num - expected).abs() < 1e-15,
1754 "round({}, {}) = {} should be close to {}",
1755 input,
1756 places,
1757 result_num,
1758 expected
1759 );
1760 }
1761 } else {
1762 panic!("Expected number result");
1763 }
1764 }
1765 }
1766
1767 #[test]
1768 fn test_map_with_index() {
1769 use crate::ast::Expr;
1770 use crate::values::LambdaDef;
1771
1772 let heap = Rc::new(RefCell::new(Heap::new()));
1773 let bindings = Rc::new(RefCell::new(HashMap::new()));
1774
1775 let list = heap.borrow_mut().insert_list(vec![
1777 Value::Number(10.0),
1778 Value::Number(20.0),
1779 Value::Number(30.0),
1780 ]);
1781
1782 let lambda = LambdaDef {
1784 name: None,
1785 args: vec![
1786 crate::values::LambdaArg::Required("x".to_string()),
1787 crate::values::LambdaArg::Required("i".to_string()),
1788 ],
1789 body: Expr::BinaryOp {
1790 op: crate::ast::BinaryOp::Add,
1791 left: Box::new(Expr::Identifier("x".to_string())),
1792 right: Box::new(Expr::Identifier("i".to_string())),
1793 },
1794 scope: HashMap::new(),
1795 };
1796 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
1797
1798 let result = BuiltInFunction::Map
1800 .call(
1801 vec![list, lambda_value],
1802 heap.clone(),
1803 bindings.clone(),
1804 0,
1805 )
1806 .unwrap();
1807
1808 let heap_borrow = heap.borrow();
1810 let result_list = result.as_list(&heap_borrow).unwrap();
1811 assert_eq!(result_list.len(), 3);
1812 assert_eq!(result_list[0], Value::Number(10.0)); assert_eq!(result_list[1], Value::Number(21.0)); assert_eq!(result_list[2], Value::Number(32.0)); }
1816
1817 #[test]
1818 fn test_filter_with_index() {
1819 use crate::ast::Expr;
1820 use crate::values::LambdaDef;
1821
1822 let heap = Rc::new(RefCell::new(Heap::new()));
1823 let bindings = Rc::new(RefCell::new(HashMap::new()));
1824
1825 let list = heap.borrow_mut().insert_list(vec![
1827 Value::Number(10.0),
1828 Value::Number(20.0),
1829 Value::Number(30.0),
1830 Value::Number(40.0),
1831 ]);
1832
1833 let lambda = LambdaDef {
1835 name: None,
1836 args: vec![
1837 crate::values::LambdaArg::Required("x".to_string()),
1838 crate::values::LambdaArg::Required("i".to_string()),
1839 ],
1840 body: Expr::BinaryOp {
1841 op: crate::ast::BinaryOp::Greater,
1842 left: Box::new(Expr::Identifier("i".to_string())),
1843 right: Box::new(Expr::Number(1.0)),
1844 },
1845 scope: HashMap::new(),
1846 };
1847 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
1848
1849 let result = BuiltInFunction::Filter
1851 .call(
1852 vec![list, lambda_value],
1853 heap.clone(),
1854 bindings.clone(),
1855 0,
1856 )
1857 .unwrap();
1858
1859 let heap_borrow = heap.borrow();
1861 let result_list = result.as_list(&heap_borrow).unwrap();
1862 assert_eq!(result_list.len(), 2);
1863 assert_eq!(result_list[0], Value::Number(30.0));
1864 assert_eq!(result_list[1], Value::Number(40.0));
1865 }
1866
1867 #[test]
1868 fn test_reduce_with_index() {
1869 use crate::ast::Expr;
1870 use crate::values::LambdaDef;
1871
1872 let heap = Rc::new(RefCell::new(Heap::new()));
1873 let bindings = Rc::new(RefCell::new(HashMap::new()));
1874
1875 let list = heap.borrow_mut().insert_list(vec![
1877 Value::Number(10.0),
1878 Value::Number(20.0),
1879 Value::Number(30.0),
1880 ]);
1881
1882 let lambda = LambdaDef {
1884 name: None,
1885 args: vec![
1886 crate::values::LambdaArg::Required("acc".to_string()),
1887 crate::values::LambdaArg::Required("x".to_string()),
1888 crate::values::LambdaArg::Required("i".to_string()),
1889 ],
1890 body: Expr::BinaryOp {
1891 op: crate::ast::BinaryOp::Add,
1892 left: Box::new(Expr::BinaryOp {
1893 op: crate::ast::BinaryOp::Add,
1894 left: Box::new(Expr::Identifier("acc".to_string())),
1895 right: Box::new(Expr::Identifier("x".to_string())),
1896 }),
1897 right: Box::new(Expr::Identifier("i".to_string())),
1898 },
1899 scope: HashMap::new(),
1900 };
1901 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
1902
1903 let result = BuiltInFunction::Reduce
1905 .call(
1906 vec![list, lambda_value, Value::Number(0.0)],
1907 heap.clone(),
1908 bindings.clone(),
1909 0,
1910 )
1911 .unwrap();
1912
1913 assert_eq!(result, Value::Number(63.0));
1915 }
1916
1917 #[test]
1918 fn test_map_backward_compatible() {
1919 use crate::ast::Expr;
1920 use crate::values::LambdaDef;
1921
1922 let heap = Rc::new(RefCell::new(Heap::new()));
1923 let bindings = Rc::new(RefCell::new(HashMap::new()));
1924
1925 let list = heap.borrow_mut().insert_list(vec![
1927 Value::Number(10.0),
1928 Value::Number(20.0),
1929 Value::Number(30.0),
1930 ]);
1931
1932 let lambda = LambdaDef {
1934 name: None,
1935 args: vec![crate::values::LambdaArg::Required("x".to_string())],
1936 body: Expr::BinaryOp {
1937 op: crate::ast::BinaryOp::Multiply,
1938 left: Box::new(Expr::Identifier("x".to_string())),
1939 right: Box::new(Expr::Number(2.0)),
1940 },
1941 scope: HashMap::new(),
1942 };
1943 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
1944
1945 let result = BuiltInFunction::Map
1947 .call(
1948 vec![list, lambda_value],
1949 heap.clone(),
1950 bindings.clone(),
1951 0,
1952 )
1953 .unwrap();
1954
1955 let heap_borrow = heap.borrow();
1957 let result_list = result.as_list(&heap_borrow).unwrap();
1958 assert_eq!(result_list.len(), 3);
1959 assert_eq!(result_list[0], Value::Number(20.0));
1960 assert_eq!(result_list[1], Value::Number(40.0));
1961 assert_eq!(result_list[2], Value::Number(60.0));
1962 }
1963}