1use crate::{
2 environment::Environment,
3 error::RuntimeError,
4 expressions::evaluate_ast,
5 heap::{Heap, HeapPointer, IterablePointer},
6 units,
7 values::{FunctionArity, LambdaArg, LambdaDef, ReifiedValue, Value},
8};
9use anyhow::Result as AnyhowResult;
10use dyn_fmt::AsStrFormatExt;
11use indexmap::IndexMap;
12use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::LazyLock};
13
14#[cfg(not(target_arch = "wasm32"))]
15use std::sync::Mutex;
16
17#[cfg(not(target_arch = "wasm32"))]
18use crate::stats::FunctionCallStats;
19
20#[cfg(not(target_arch = "wasm32"))]
21pub static FUNCTION_CALLS: LazyLock<Mutex<Vec<FunctionCallStats>>> =
22 LazyLock::new(|| Mutex::new(Vec::new()));
23
24#[cfg(not(target_arch = "wasm32"))]
25pub fn get_function_call_stats() -> Vec<FunctionCallStats> {
26 FUNCTION_CALLS.lock().unwrap().clone()
27}
28
29#[cfg(not(target_arch = "wasm32"))]
30pub fn clear_function_call_stats() {
31 FUNCTION_CALLS.lock().unwrap().clear();
32}
33
34pub static BUILTIN_FUNCTION_NAMES: LazyLock<Vec<&'static str>> =
36 LazyLock::new(BuiltInFunction::all_names);
37
38#[derive(
39 Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
40)]
41pub enum BuiltInFunction {
42 Sqrt,
44 Sin,
45 Cos,
46 Tan,
47 Asin,
48 Acos,
49 Atan,
50 Log,
51 Log10,
52 Exp,
53 Abs,
54 Floor,
55 Ceil,
56 Round,
57 Trunc,
58 Random,
59
60 Min,
62 Max,
63 Avg,
64 Sum,
65 Prod,
66 Median,
67 Percentile,
68
69 Range,
71 Len,
72 Head,
73 Tail,
74 Slice,
75 Concat,
76 Dot,
77 Unique,
78 Sort,
79 SortBy,
80 Reverse,
81 Any,
82 All,
83
84 Map,
86 Reduce,
87 Filter,
88 Every,
89 Some,
90
91 Split,
93 Join,
94 Replace,
95 Trim,
96 Uppercase,
97 Lowercase,
98 Includes,
99 Format,
100
101 Typeof,
103 Arity,
104
105 Keys,
107 Values,
108 Entries,
109
110 GroupBy,
112 CountBy,
113 Flatten,
114 Zip,
115 Chunk,
116
117 ToString,
119 ToNumber,
120 ToBool,
121 Convert,
122
123 Ugt,
125 Ult,
126 Ugte,
127 Ulte,
128
129 #[cfg(not(target_arch = "wasm32"))]
131 Print,
132 #[cfg(not(target_arch = "wasm32"))]
133 TimeNow,
134}
135
136#[derive(Debug, Clone)]
137pub enum FunctionDef {
138 BuiltIn(BuiltInFunction),
139 Lambda(LambdaDef),
140}
141
142impl BuiltInFunction {
143 pub fn from_ident(ident: &str) -> Option<Self> {
144 match ident {
145 "sqrt" => Some(Self::Sqrt),
146 "sin" => Some(Self::Sin),
147 "cos" => Some(Self::Cos),
148 "tan" => Some(Self::Tan),
149 "asin" => Some(Self::Asin),
150 "acos" => Some(Self::Acos),
151 "atan" => Some(Self::Atan),
152 "log" => Some(Self::Log),
153 "log10" => Some(Self::Log10),
154 "exp" => Some(Self::Exp),
155 "abs" => Some(Self::Abs),
156 "floor" => Some(Self::Floor),
157 "ceil" => Some(Self::Ceil),
158 "round" => Some(Self::Round),
159 "trunc" => Some(Self::Trunc),
160 "random" => Some(Self::Random),
161 "min" => Some(Self::Min),
162 "max" => Some(Self::Max),
163 "avg" => Some(Self::Avg),
164 "sum" => Some(Self::Sum),
165 "prod" => Some(Self::Prod),
166 "median" => Some(Self::Median),
167 "percentile" => Some(Self::Percentile),
168 "range" => Some(Self::Range),
169 "any" => Some(Self::Any),
170 "all" => Some(Self::All),
171 "len" => Some(Self::Len),
172 "head" => Some(Self::Head),
173 "tail" => Some(Self::Tail),
174 "slice" => Some(Self::Slice),
175 "concat" => Some(Self::Concat),
176 "dot" => Some(Self::Dot),
177 "unique" => Some(Self::Unique),
178 "sort" => Some(Self::Sort),
179 "sort_by" => Some(Self::SortBy),
180 "reverse" => Some(Self::Reverse),
181 "map" => Some(Self::Map),
182 "reduce" => Some(Self::Reduce),
183 "filter" => Some(Self::Filter),
184 "every" => Some(Self::Every),
185 "some" => Some(Self::Some),
186 "split" => Some(Self::Split),
187 "join" => Some(Self::Join),
188 "replace" => Some(Self::Replace),
189 "trim" => Some(Self::Trim),
190 "uppercase" => Some(Self::Uppercase),
191 "lowercase" => Some(Self::Lowercase),
192 "to_string" => Some(Self::ToString),
193 "to_number" => Some(Self::ToNumber),
194 "to_bool" => Some(Self::ToBool),
195 "convert" => Some(Self::Convert),
196 "includes" => Some(Self::Includes),
197 "format" => Some(Self::Format),
198 "typeof" => Some(Self::Typeof),
199 "arity" => Some(Self::Arity),
200 "keys" => Some(Self::Keys),
201 "values" => Some(Self::Values),
202 "entries" => Some(Self::Entries),
203 "group_by" => Some(Self::GroupBy),
204 "count_by" => Some(Self::CountBy),
205 "flatten" => Some(Self::Flatten),
206 "zip" => Some(Self::Zip),
207 "chunk" => Some(Self::Chunk),
208 "ugt" => Some(Self::Ugt),
209 "ult" => Some(Self::Ult),
210 "ugte" => Some(Self::Ugte),
211 "ulte" => Some(Self::Ulte),
212 #[cfg(not(target_arch = "wasm32"))]
213 "print" => Some(Self::Print),
214 #[cfg(not(target_arch = "wasm32"))]
215 "time_now" => Some(Self::TimeNow),
216 _ => None,
217 }
218 }
219
220 pub fn name(&self) -> &'static str {
221 match self {
222 Self::Sqrt => "sqrt",
223 Self::Sin => "sin",
224 Self::Cos => "cos",
225 Self::Tan => "tan",
226 Self::Asin => "asin",
227 Self::Acos => "acos",
228 Self::Atan => "atan",
229 Self::Log => "log",
230 Self::Log10 => "log10",
231 Self::Exp => "exp",
232 Self::Abs => "abs",
233 Self::Floor => "floor",
234 Self::Ceil => "ceil",
235 Self::Round => "round",
236 Self::Trunc => "trunc",
237 Self::Random => "random",
238 Self::Min => "min",
239 Self::Max => "max",
240 Self::Avg => "avg",
241 Self::Sum => "sum",
242 Self::Prod => "prod",
243 Self::Median => "median",
244 Self::Percentile => "percentile",
245 Self::Range => "range",
246 Self::Any => "any",
247 Self::All => "all",
248 Self::Len => "len",
249 Self::Head => "head",
250 Self::Tail => "tail",
251 Self::Slice => "slice",
252 Self::Concat => "concat",
253 Self::Dot => "dot",
254 Self::Unique => "unique",
255 Self::Sort => "sort",
256 Self::SortBy => "sort_by",
257 Self::Reverse => "reverse",
258 Self::Map => "map",
259 Self::Reduce => "reduce",
260 Self::Filter => "filter",
261 Self::Every => "every",
262 Self::Some => "some",
263 Self::Split => "split",
264 Self::Join => "join",
265 Self::Replace => "replace",
266 Self::Trim => "trim",
267 Self::Uppercase => "uppercase",
268 Self::Lowercase => "lowercase",
269 Self::Includes => "includes",
270 Self::Format => "format",
271 Self::Typeof => "typeof",
272 Self::Arity => "arity",
273 Self::Keys => "keys",
274 Self::Values => "values",
275 Self::Entries => "entries",
276 Self::GroupBy => "group_by",
277 Self::CountBy => "count_by",
278 Self::Flatten => "flatten",
279 Self::Zip => "zip",
280 Self::Chunk => "chunk",
281 Self::ToString => "to_string",
282 Self::ToNumber => "to_number",
283 Self::ToBool => "to_bool",
284 Self::Convert => "convert",
285 Self::Ugt => "ugt",
286 Self::Ult => "ult",
287 Self::Ugte => "ugte",
288 Self::Ulte => "ulte",
289 #[cfg(not(target_arch = "wasm32"))]
290 Self::Print => "print",
291 #[cfg(not(target_arch = "wasm32"))]
292 Self::TimeNow => "time_now",
293 }
294 }
295
296 pub fn arity(&self) -> FunctionArity {
297 match self {
298 Self::Sqrt
300 | Self::Sin
301 | Self::Cos
302 | Self::Tan
303 | Self::Asin
304 | Self::Acos
305 | Self::Atan
306 | Self::Log
307 | Self::Log10
308 | Self::Exp
309 | Self::Abs
310 | Self::Floor
311 | Self::Ceil
312 | Self::Trunc
313 | Self::Random => FunctionArity::Exact(1),
314
315 Self::Round => FunctionArity::Between(1, 2),
317
318 Self::Min | Self::Max | Self::Avg | Self::Sum | Self::Prod | Self::Median => {
320 FunctionArity::AtLeast(1)
321 }
322
323 Self::Range => FunctionArity::Between(1, 2),
325
326 Self::Len
328 | Self::Head
329 | Self::Tail
330 | Self::Unique
331 | Self::Sort
332 | Self::Reverse
333 | Self::Any
334 | Self::All => FunctionArity::Exact(1),
335 Self::Slice => FunctionArity::Exact(3),
336 Self::Concat => FunctionArity::AtLeast(2),
337 Self::Dot | Self::Percentile => FunctionArity::Exact(2),
338
339 Self::Map | Self::Filter | Self::Every | Self::Some | Self::SortBy => {
341 FunctionArity::Exact(2)
342 }
343 Self::Reduce => FunctionArity::Exact(3),
344
345 Self::Split | Self::Join | Self::Includes => FunctionArity::Exact(2),
347 Self::Replace => FunctionArity::Exact(3),
348 Self::Trim
349 | Self::Uppercase
350 | Self::Lowercase
351 | Self::ToString
352 | Self::ToNumber
353 | Self::ToBool => FunctionArity::Exact(1),
354 Self::Convert => FunctionArity::Exact(3),
355 Self::Format => FunctionArity::AtLeast(1),
356
357 Self::Typeof | Self::Arity => FunctionArity::Exact(1),
359
360 Self::Keys | Self::Values | Self::Entries => FunctionArity::Exact(1),
362
363 Self::GroupBy | Self::CountBy => FunctionArity::Exact(2),
365 Self::Flatten => FunctionArity::Exact(1),
366 Self::Zip => FunctionArity::AtLeast(2),
367 Self::Chunk => FunctionArity::Exact(2),
368
369 Self::Ugt | Self::Ult | Self::Ugte | Self::Ulte => FunctionArity::Exact(2),
371
372 #[cfg(not(target_arch = "wasm32"))]
374 Self::Print => FunctionArity::AtLeast(1),
375 #[cfg(not(target_arch = "wasm32"))]
376 Self::TimeNow => FunctionArity::Exact(0),
377 }
378 }
379
380 pub fn call(
381 &self,
382 args: Vec<Value>,
383 heap: Rc<RefCell<Heap>>,
384 bindings: Rc<Environment>,
385 call_depth: usize,
386 source: &str,
387 ) -> Result<Value, RuntimeError> {
388 match self {
389 Self::Sqrt => Ok(Value::Number(args[0].as_number()?.sqrt())),
391 Self::Sin => Ok(Value::Number(args[0].as_number()?.sin())),
392 Self::Cos => Ok(Value::Number(args[0].as_number()?.cos())),
393 Self::Tan => Ok(Value::Number(args[0].as_number()?.tan())),
394 Self::Asin => Ok(Value::Number(args[0].as_number()?.asin())),
395 Self::Acos => Ok(Value::Number(args[0].as_number()?.acos())),
396 Self::Atan => Ok(Value::Number(args[0].as_number()?.atan())),
397 Self::Log => Ok(Value::Number(args[0].as_number()?.ln())),
398 Self::Log10 => Ok(Value::Number(args[0].as_number()?.log10())),
399 Self::Exp => Ok(Value::Number(args[0].as_number()?.exp())),
400 Self::Abs => Ok(Value::Number(args[0].as_number()?.abs())),
401 Self::Floor => Ok(Value::Number(args[0].as_number()?.floor())),
402 Self::Ceil => Ok(Value::Number(args[0].as_number()?.ceil())),
403 Self::Trunc => Ok(Value::Number(args[0].as_number()?.trunc())),
404
405 Self::Random => {
406 let seed = args[0].as_number()? as u64;
407 let mut rng = fastrand::Rng::with_seed(seed);
408 Ok(Value::Number(rng.f64()))
409 }
410
411 Self::Round => {
412 let num = args[0].as_number()?;
413 if args.len() == 1 {
414 Ok(Value::Number(num.round()))
415 } else {
416 let decimal_places = args[1].as_number()? as i32;
417 let multiplier = 10_f64.powi(decimal_places);
418 Ok(Value::Number((num * multiplier).round() / multiplier))
419 }
420 }
421
422 Self::Min => {
424 let nums = if args.len() == 1 {
425 match &args[0] {
427 Value::List(_) => {
428 let borrowed_heap = heap.borrow();
429 let list = args[0].as_list(&borrowed_heap)?;
430 list.iter()
431 .map(|a| a.as_number())
432 .collect::<AnyhowResult<Vec<f64>>>()?
433 }
434 _ => vec![args[0].as_number()?],
435 }
436 } else {
437 args.iter()
438 .map(|a| a.as_number())
439 .collect::<AnyhowResult<Vec<f64>>>()?
440 };
441
442 if nums.is_empty() {
443 return Err(RuntimeError::from("min requires at least one number"));
444 }
445
446 Ok(Value::Number(
447 nums.iter().copied().fold(f64::INFINITY, f64::min),
448 ))
449 }
450
451 Self::Max => {
452 let nums = if args.len() == 1 {
453 match &args[0] {
455 Value::List(_) => {
456 let borrowed_heap = heap.borrow();
457 let list = args[0].as_list(&borrowed_heap)?;
458 list.iter()
459 .map(|a| a.as_number())
460 .collect::<AnyhowResult<Vec<f64>>>()?
461 }
462 _ => vec![args[0].as_number()?],
463 }
464 } else {
465 args.iter()
466 .map(|a| a.as_number())
467 .collect::<AnyhowResult<Vec<f64>>>()?
468 };
469
470 if nums.is_empty() {
471 return Err(RuntimeError::from("max requires at least one number"));
472 }
473
474 Ok(Value::Number(
475 nums.iter().copied().fold(f64::NEG_INFINITY, f64::max),
476 ))
477 }
478
479 Self::Avg => {
480 let nums = if args.len() == 1 {
481 match &args[0] {
482 Value::List(_) => {
483 let borrowed_heap = heap.borrow();
484 let list = args[0].as_list(&borrowed_heap)?;
485 list.iter()
486 .map(|a| a.as_number())
487 .collect::<AnyhowResult<Vec<f64>>>()?
488 }
489 _ => vec![args[0].as_number()?],
490 }
491 } else {
492 args.iter()
493 .map(|a| a.as_number())
494 .collect::<AnyhowResult<Vec<f64>>>()?
495 };
496 if nums.is_empty() {
497 return Err(RuntimeError::from("avg requires at least one number"));
498 }
499 Ok(Value::Number(nums.iter().sum::<f64>() / nums.len() as f64))
500 }
501
502 Self::Prod => {
503 let nums = if args.len() == 1 {
504 match &args[0] {
505 Value::List(_) => {
506 let borrowed_heap = heap.borrow();
507 let list = args[0].as_list(&borrowed_heap)?;
508 list.iter()
509 .map(|a| a.as_number())
510 .collect::<AnyhowResult<Vec<f64>>>()?
511 }
512 _ => vec![args[0].as_number()?],
513 }
514 } else {
515 args.iter()
516 .map(|a| a.as_number())
517 .collect::<AnyhowResult<Vec<f64>>>()?
518 };
519 if nums.is_empty() {
520 return Err(RuntimeError::from("prod requires at least one number"));
521 }
522 Ok(Value::Number(nums.iter().product()))
523 }
524
525 Self::Range => {
526 let (start, end) = match args[..] {
527 [Value::Number(start)] => (0.0, start),
528 [Value::Number(start), Value::Number(end)] => (start, end),
529 _ => return Err(RuntimeError::from("range requires 1 or 2 numbers")),
530 };
531
532 if start > end {
533 return Err(RuntimeError::from(
534 "range requires start to be less than or equal to end"
535 ));
536 }
537
538 if !f64::is_finite(start) || !f64::is_finite(end) {
539 return Err(RuntimeError::from("range requires finite numbers"));
540 }
541
542 let start_i64 = start as i64;
543 let end_i64 = end as i64;
544 let length = end_i64 - start_i64;
545
546 if length > u32::MAX as i64 {
547 return Err(RuntimeError::new(format!(
548 "list would be longer than the maximum length of {}",
549 u32::MAX
550 )));
551 }
552
553 let values = (start_i64..end_i64)
554 .map(|e| Value::Number(e as f64))
555 .collect();
556 let list = heap.borrow_mut().insert_list(values);
557
558 Ok(list)
559 }
560
561 Self::Sum => {
562 let nums = if args.len() == 1 {
563 match &args[0] {
564 Value::List(_) => {
565 let borrowed_heap = heap.borrow();
566 let list = args[0].as_list(&borrowed_heap)?;
567 list.iter()
568 .map(|a| a.as_number())
569 .collect::<AnyhowResult<Vec<f64>>>()?
570 }
571 _ => vec![args[0].as_number()?],
572 }
573 } else {
574 args.iter()
575 .map(|a| a.as_number())
576 .collect::<AnyhowResult<Vec<f64>>>()?
577 };
578 if nums.is_empty() {
579 return Err(RuntimeError::from("sum requires at least one number"));
580 }
581 Ok(Value::Number(nums.iter().sum()))
582 }
583
584 Self::Median => {
586 let mut nums = if args.len() == 1 {
587 match &args[0] {
588 Value::List(_) => {
589 let borrowed_heap = heap.borrow();
590 let list = args[0].as_list(&borrowed_heap)?;
591 list.iter()
592 .map(|a| a.as_number())
593 .collect::<AnyhowResult<Vec<f64>>>()?
594 }
595 _ => vec![args[0].as_number()?],
596 }
597 } else {
598 args.into_iter()
599 .map(|a| a.as_number())
600 .collect::<AnyhowResult<Vec<f64>>>()?
601 };
602
603 if nums.is_empty() {
604 return Err(RuntimeError::from("median requires at least one number"));
605 }
606
607 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
608 let len = nums.len();
609 if len % 2 == 0 {
610 Ok(Value::Number((nums[len / 2 - 1] + nums[len / 2]) / 2.0))
611 } else {
612 Ok(Value::Number(nums[len / 2]))
613 }
614 }
615
616 Self::Percentile => {
617 let p = args[1].as_number()?;
618 let heap = &heap.borrow();
619 let list = args[0].as_list(heap)?;
620
621 if !(0.0..=100.0).contains(&p) {
622 return Err(RuntimeError::from("percentile must be between 0 and 100"));
623 }
624
625 let mut nums = list
626 .iter()
627 .map(|a| a.as_number())
628 .collect::<AnyhowResult<Vec<f64>>>()?;
629
630 nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
631 let index = (p / 100.0 * (nums.len() - 1) as f64).round() as usize;
632
633 Ok(Value::Number(nums[index]))
634 }
635
636 Self::Len => match &args[0] {
638 Value::List(l) => Ok(Value::Number(
639 l.reify(&heap.borrow()).as_list()?.len() as f64
640 )),
641 Value::String(s) => Ok(Value::Number(
642 s.reify(&heap.borrow()).as_string()?.len() as f64
643 )),
644 _ => Err(RuntimeError::from("argument must be a list or string")),
645 },
646
647 Self::Head => match &args[0] {
648 Value::List(p) => Ok(p
649 .reify(&heap.borrow())
650 .as_list()?
651 .first()
652 .copied()
653 .unwrap_or(Value::Null)),
654 Value::String(p) => {
655 let val = {
656 p.reify(&heap.borrow())
657 .as_string()?
658 .get(0..1)
659 .unwrap_or("")
660 .to_string()
661 };
662
663 Ok(heap.borrow_mut().insert_string(val))
664 }
665 _ => Err(RuntimeError::from("argument must be a list or string")),
666 },
667
668 Self::Tail => match &args[0] {
669 Value::List(p) => {
670 let val = {
671 p.reify(&heap.borrow())
672 .as_list()?
673 .get(1..)
674 .unwrap_or([].as_slice())
675 .to_vec()
676 };
677
678 Ok(heap.borrow_mut().insert_list(val))
679 }
680 Value::String(s) => {
681 let val = {
682 s.reify(&heap.borrow())
683 .as_string()?
684 .get(1..)
685 .unwrap_or("")
686 .to_string()
687 };
688
689 Ok(heap.borrow_mut().insert_string(val))
690 }
691 _ => Err(RuntimeError::from("argument must be a list or string")),
692 },
693
694 Self::Slice => {
695 let start = args[1].as_number()? as usize;
696 let end = args[2].as_number()? as usize;
697
698 match args[0] {
699 Value::List(_) => {
700 let slice = {
701 let borrowed_heap = heap.borrow();
702 let list = args[0].as_list(&borrowed_heap)?;
703 list.get(start..end)
704 .ok_or_else(|| RuntimeError::from("index out of bounds"))?
705 .to_vec()
706 };
707
708 Ok(heap.borrow_mut().insert_list(slice))
709 }
710 Value::String(_) => {
711 let s = {
712 let borrowed_heap = &heap.borrow();
713 args[0].as_string(borrowed_heap)?.to_string()
714 };
715
716 s.get(start..end)
717 .map_or(Err(RuntimeError::from("index out of bounds")), |s| {
718 Ok(heap.borrow_mut().insert_string(s.to_string()))
719 })
720 }
721 _ => Err(RuntimeError::from("argument must be a list or string")),
722 }
723 }
724
725 Self::Concat => {
726 let mut list = vec![];
727
728 for arg in args {
729 match arg {
730 Value::List(p) => {
731 let borrowed_heap = heap.borrow();
732 let values = p.reify(&borrowed_heap).as_list()?;
733 list.extend(values.iter().copied());
734 }
735 Value::Spread(IterablePointer::List(p)) => {
736 let borrowed_heap = heap.borrow();
737 let values = p.reify(&borrowed_heap).as_list()?;
738 list.extend(values.iter().copied());
739 }
740 Value::Spread(IterablePointer::String(p)) => {
741 let string = {
742 let borrowed_heap = &heap.borrow();
743 p.reify(borrowed_heap).as_string()?.to_string()
744 };
745
746 list.extend(
747 string
748 .chars()
749 .map(|c| heap.borrow_mut().insert_string(c.to_string())),
750 );
751 }
752 _ => list.push(arg),
753 }
754 }
755
756 Ok(heap.borrow_mut().insert_list(list))
757 }
758
759 Self::Dot => {
760 let borrowed_heap = heap.borrow();
761 let a = args[0].as_list(&borrowed_heap)?;
762 let b = args[1].as_list(&borrowed_heap)?;
763
764 if a.len() != b.len() {
765 return Err(RuntimeError::from(
766 "cannot calculate dot product of lists with different lengths"
767 ));
768 }
769
770 let mut sum = 0.0;
771 for (a, b) in a.iter().zip(b.iter()) {
772 sum += a.as_number()? * b.as_number()?;
773 }
774 Ok(Value::Number(sum))
775 }
776
777 Self::Unique => {
778 let mut unique_list = vec![];
779 let borrowed_heap = heap.borrow();
780 let list = args[0].as_list(&borrowed_heap)?;
781
782 for item in list.iter() {
783 let mut is_duplicate = false;
784 for existing in &unique_list {
785 if item.equals(existing, &borrowed_heap)? {
786 is_duplicate = true;
787 break;
788 }
789 }
790 if !is_duplicate {
791 unique_list.push(*item);
792 }
793 }
794
795 drop(borrowed_heap);
796 Ok(heap.borrow_mut().insert_list(unique_list))
797 }
798
799 Self::Sort => {
800 let mut list = {
801 let borrowed_heap = &heap.borrow();
802 args[0].as_list(borrowed_heap)?.clone()
803 };
804 let borrowed_heap = heap.borrow();
805 list.sort_by(|a, b| {
806 a.compare(b, &borrowed_heap)
807 .unwrap_or(None)
808 .unwrap_or(std::cmp::Ordering::Equal)
809 });
810 drop(borrowed_heap);
811 Ok(heap.borrow_mut().insert_list(list))
812 }
813
814 Self::Reverse => {
815 let mut list = { args[0].as_list(&heap.borrow())?.clone() };
816 list.reverse();
817 Ok(heap.borrow_mut().insert_list(list))
818 }
819
820 Self::Any => {
821 let borrowed_heap = heap.borrow();
822 let list = args[0].as_list(&borrowed_heap)?;
823 Ok(Value::Bool(
824 list.iter().any(|v| v.as_bool().unwrap_or(false)),
825 ))
826 }
827
828 Self::All => {
829 let borrowed_heap = heap.borrow();
830 let list = args[0].as_list(&borrowed_heap)?;
831 Ok(Value::Bool(
832 list.iter().all(|v| v.as_bool().unwrap_or(false)),
833 ))
834 }
835
836 Self::Split => {
838 let (s, delimeter) = {
839 let borrowed_heap = &heap.borrow();
840 (
841 args[0].as_string(borrowed_heap)?.to_string(),
842 args[1].as_string(borrowed_heap)?.to_string(),
843 )
844 };
845
846 let list = s
847 .split(&delimeter)
848 .map(|s| heap.borrow_mut().insert_string(s.to_string()))
849 .collect();
850
851 Ok(heap.borrow_mut().insert_list(list))
852 }
853
854 Self::Join => {
855 let joined_string = {
856 let borrowed_heap = &heap.borrow();
857 let delimeter = args[1].as_string(borrowed_heap)?;
858 let list = args[0].as_list(borrowed_heap)?;
859 list.iter()
860 .map(|v| v.stringify_internal(borrowed_heap))
861 .collect::<Vec<String>>()
862 .join(delimeter)
863 };
864
865 Ok(heap.borrow_mut().insert_string(joined_string))
866 }
867
868 Self::Replace => {
869 let string = {
870 let borrowed_heap = heap.borrow();
871 let old = args[1].as_string(&borrowed_heap)?.to_string();
872 let new = args[2].as_string(&borrowed_heap)?.to_string();
873 let s = args[0].as_string(&borrowed_heap)?.to_string();
874 s.replace(&old, &new)
875 };
876
877 Ok(heap.borrow_mut().insert_string(string))
878 }
879
880 Self::Trim => {
881 let string = {
882 let borrowed_heap = heap.borrow();
883 args[0].as_string(&borrowed_heap)?.trim().to_string()
884 };
885
886 Ok(heap.borrow_mut().insert_string(string))
887 }
888
889 Self::Uppercase => {
890 let string = args[0].as_string(&heap.borrow())?.to_uppercase();
891 Ok(heap.borrow_mut().insert_string(string))
892 }
893
894 Self::Lowercase => {
895 let string = args[0].as_string(&heap.borrow())?.to_lowercase();
896 Ok(heap.borrow_mut().insert_string(string))
897 }
898
899 Self::Includes => {
900 match &args[0].reify(&heap.borrow())? {
901 ReifiedValue::List(l, _) => {
903 let borrowed_heap = heap.borrow();
904 for item in (*l).iter() {
905 if item.equals(&args[1], &borrowed_heap)? {
906 return Ok(Value::Bool(true));
907 }
908 }
909 Ok(Value::Bool(false))
910 }
911 ReifiedValue::String(s, _) => {
913 let needle = {
914 let borrowed_heap = &heap.borrow();
915 args[1]
916 .as_string(borrowed_heap)
917 .map_err(|_| RuntimeError::from("second argument must be a string"))?
918 .to_string()
919 };
920 Ok(Value::Bool(s.contains(&needle)))
921 }
922 _ => Err(RuntimeError::from("first argument must be a list or string")),
923 }
924 }
925
926 Self::Format => {
927 let format_str = {
928 let borrowed_heap = &heap.borrow();
929 args[0]
930 .as_string(borrowed_heap)
931 .map_err(|_| RuntimeError::from("first argument must be a string"))?
932 .to_string()
933 };
934
935 let format_args = {
936 let borrowed_heap = &heap.borrow();
937 &args[1..]
938 .iter()
939 .map(|v| v.stringify_for_display(borrowed_heap))
940 .collect::<Vec<String>>()
941 };
942
943 Ok(heap
944 .borrow_mut()
945 .insert_string(format_str.format(format_args)))
946 }
947
948 Self::Typeof => Ok(heap
950 .borrow_mut()
951 .insert_string(args[0].get_type().to_string())),
952
953 Self::Arity => {
954 let arity = match args[0] {
955 Value::Lambda(p) => p.reify(&heap.borrow()).as_lambda()?.get_arity(),
956 Value::BuiltIn(built_in) => built_in.arity(),
957 _ => return Err(RuntimeError::from("argument must be a function or built-in function")),
958 };
959
960 Ok(Value::Number(match arity {
961 FunctionArity::Exact(n) => n as f64,
962 FunctionArity::AtLeast(n) => n as f64,
963 FunctionArity::Between(min, _max) => min as f64,
964 }))
965 }
966
967 Self::Keys => {
969 let key_strings = {
970 let borrowed_heap = heap.borrow();
971 args[0]
972 .as_record(&borrowed_heap)?
973 .keys()
974 .cloned()
975 .collect::<Vec<String>>()
976 };
977 let mut borrowed_heap = heap.borrow_mut();
978 let keys = key_strings
979 .into_iter()
980 .map(|k| borrowed_heap.insert_string(k))
981 .collect();
982
983 Ok(borrowed_heap.insert_list(keys))
984 }
985
986 Self::Values => {
987 let values = {
988 let borrowed_heap = heap.borrow();
989 args[0]
990 .as_record(&borrowed_heap)?
991 .values()
992 .copied()
993 .collect::<Vec<Value>>()
994 };
995
996 Ok(heap.borrow_mut().insert_list(values))
997 }
998
999 Self::Entries => {
1000 let entries = {
1001 let borrowed_heap = heap.borrow();
1002 args[0]
1003 .as_record(&borrowed_heap)?
1004 .iter()
1005 .map(|(k, v)| (k.clone(), *v))
1006 .collect::<Vec<(String, Value)>>()
1007 };
1008 let mut borrowed_heap = heap.borrow_mut();
1009 let entry_values = entries
1010 .into_iter()
1011 .map(|(k, v)| {
1012 let key = borrowed_heap.insert_string(k);
1013 borrowed_heap.insert_list(vec![key, v])
1014 })
1015 .collect();
1016
1017 Ok(borrowed_heap.insert_list(entry_values))
1018 }
1019
1020 Self::GroupBy => {
1022 let func = &args[1];
1023 let list_ptr = args[0].as_list_pointer()?;
1024 let func_def = {
1025 let borrowed_heap = heap.borrow();
1026 get_function_def(func, &borrowed_heap)
1027 .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1028 };
1029 let list_len = {
1030 let borrowed_heap = heap.borrow();
1031 list_ptr.reify(&borrowed_heap).as_list()?.len()
1032 };
1033
1034 let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
1035 for idx in 0..list_len {
1036 let item = {
1037 let borrowed_heap = heap.borrow();
1038 let list = list_ptr.reify(&borrowed_heap).as_list()?;
1039 list[idx]
1040 };
1041 let key_result = func_def.call(
1042 Value::Null,
1043 vec![item],
1044 Rc::clone(&heap),
1045 Rc::clone(&bindings),
1046 call_depth + 1,
1047 source,
1048 )?;
1049
1050 let key = match key_result {
1051 Value::String(_) => key_result.as_string(&heap.borrow())?.to_string(),
1052 _ => {
1053 return Err(RuntimeError::new(format!(
1054 "group_by key function must return a string, but got a {}",
1055 key_result.get_type()
1056 )));
1057 }
1058 };
1059
1060 groups.entry(key).or_default().push(item);
1061 }
1062
1063 let record: IndexMap<String, Value> = groups
1065 .into_iter()
1066 .map(|(k, v)| (k, heap.borrow_mut().insert_list(v)))
1067 .collect();
1068
1069 Ok(heap.borrow_mut().insert_record(record))
1070 }
1071
1072 Self::CountBy => {
1073 let func = &args[1];
1074 let list_ptr = args[0].as_list_pointer()?;
1075 let func_def = {
1076 let borrowed_heap = heap.borrow();
1077 get_function_def(func, &borrowed_heap)
1078 .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1079 };
1080 let list_len = {
1081 let borrowed_heap = heap.borrow();
1082 list_ptr.reify(&borrowed_heap).as_list()?.len()
1083 };
1084
1085 let mut counts: IndexMap<String, f64> = IndexMap::new();
1086 for idx in 0..list_len {
1087 let item = {
1088 let borrowed_heap = heap.borrow();
1089 let list = list_ptr.reify(&borrowed_heap).as_list()?;
1090 list[idx]
1091 };
1092 let key_result = func_def.call(
1093 Value::Null,
1094 vec![item],
1095 Rc::clone(&heap),
1096 Rc::clone(&bindings),
1097 call_depth + 1,
1098 source,
1099 )?;
1100
1101 let key = match key_result {
1102 Value::String(_) => key_result.as_string(&heap.borrow())?.to_string(),
1103 _ => {
1104 return Err(RuntimeError::new(format!(
1105 "count_by key function must return a string, but got a {}",
1106 key_result.get_type()
1107 )));
1108 }
1109 };
1110
1111 *counts.entry(key).or_insert(0.0) += 1.0;
1112 }
1113
1114 let record: IndexMap<String, Value> = counts
1116 .into_iter()
1117 .map(|(k, v)| (k, Value::Number(v)))
1118 .collect();
1119
1120 Ok(heap.borrow_mut().insert_record(record))
1121 }
1122
1123 Self::Flatten => {
1124 let mut result = vec![];
1125 let borrowed_heap = heap.borrow();
1126 let list = args[0].as_list(&borrowed_heap)?;
1127
1128 for item in list.iter().copied() {
1129 match item {
1130 Value::List(pointer) => {
1131 let inner = pointer.reify(&borrowed_heap).as_list()?;
1132 result.extend(inner.iter().copied());
1133 }
1134 _ => result.push(item),
1135 }
1136 }
1137
1138 drop(borrowed_heap);
1139 Ok(heap.borrow_mut().insert_list(result))
1140 }
1141
1142 Self::Zip => {
1143 let list_ptrs = args
1145 .iter()
1146 .map(|arg| {
1147 arg.as_list_pointer()
1148 .map_err(|_| RuntimeError::from("all arguments to zip must be lists"))
1149 })
1150 .collect::<Result<Vec<_>, RuntimeError>>()?;
1151
1152 let max_len = {
1154 let borrowed_heap = heap.borrow();
1155 let mut max_len = 0;
1156 for ptr in &list_ptrs {
1157 let len = ptr.reify(&borrowed_heap).as_list()?.len();
1158 max_len = max_len.max(len);
1159 }
1160 max_len
1161 };
1162
1163 let mut result = Vec::with_capacity(max_len);
1165 for i in 0..max_len {
1166 let tuple = {
1167 let borrowed_heap = heap.borrow();
1168 let mut tuple = Vec::with_capacity(list_ptrs.len());
1169 for ptr in &list_ptrs {
1170 let list = ptr.reify(&borrowed_heap).as_list()?;
1171 tuple.push(list.get(i).copied().unwrap_or(Value::Null));
1172 }
1173 tuple
1174 };
1175 result.push(heap.borrow_mut().insert_list(tuple));
1176 }
1177
1178 Ok(heap.borrow_mut().insert_list(result))
1179 }
1180
1181 Self::Chunk => {
1182 let n = args[1].as_number()? as usize;
1183
1184 if n == 0 {
1185 return Err(RuntimeError::from("chunk size must be greater than 0"));
1186 }
1187
1188 let chunk_values = {
1189 let borrowed_heap = heap.borrow();
1190 let list = args[0].as_list(&borrowed_heap)?;
1191 list.chunks(n)
1192 .map(|chunk| chunk.to_vec())
1193 .collect::<Vec<_>>()
1194 };
1195 let mut borrowed_heap = heap.borrow_mut();
1196 let chunks = chunk_values
1197 .into_iter()
1198 .map(|chunk| borrowed_heap.insert_list(chunk))
1199 .collect();
1200
1201 Ok(borrowed_heap.insert_list(chunks))
1202 }
1203
1204 Self::ToString => match args[0] {
1206 Value::String(_) => Ok(args[0]), _ => {
1208 let string = args[0].stringify_internal(&heap.borrow());
1209 Ok(heap.borrow_mut().insert_string(string))
1210 }
1211 },
1212
1213 Self::ToNumber => match args[0] {
1214 Value::Number(_) => Ok(args[0]), Value::Bool(b) => Ok(Value::Number(if b { 1.0 } else { 0.0 })),
1216 _ => {
1217 let s = args[0].as_string(&heap.borrow())?.to_string();
1218 let n: f64 = s.parse().map_err(|_| {
1219 RuntimeError::new(format!("cannot convert '{}' to a number", s))
1220 })?;
1221 Ok(Value::Number(n))
1222 }
1223 },
1224
1225 Self::ToBool => match args[0] {
1226 Value::Bool(_) => Ok(args[0]), Value::Number(_) => Ok(Value::Bool(args[0].as_number()? != 0.0)),
1228 _ => Err(RuntimeError::new(format!(
1229 "expected a boolean or number, but got a {}",
1230 args[0].get_type()
1231 ))),
1232 },
1233
1234 Self::Convert => {
1235 let value = args[0].as_number()?;
1236 let from_unit = {
1237 let borrowed_heap = &heap.borrow();
1238 args[1].as_string(borrowed_heap)?.to_string()
1239 };
1240 let to_unit = {
1241 let borrowed_heap = &heap.borrow();
1242 args[2].as_string(borrowed_heap)?.to_string()
1243 };
1244
1245 let result = units::convert(value, &from_unit, &to_unit)?;
1246 Ok(Value::Number(result))
1247 }
1248
1249 #[cfg(not(target_arch = "wasm32"))]
1251 Self::Print => {
1252 let borrowed_heap = &heap.borrow();
1253
1254 let output = if args.len() == 1 {
1255 args[0].stringify_internal(borrowed_heap)
1256 } else {
1257 let format_str = args[0].as_string(borrowed_heap).map_err(|_| {
1258 RuntimeError::from("first argument must be a formatting string if multiple arguments are given")
1259 })?;
1260 let format_args = &args[1..]
1261 .iter()
1262 .map(|v| v.stringify_internal(borrowed_heap))
1263 .collect::<Vec<String>>();
1264 format_str.format(format_args)
1265 };
1266
1267 eprintln!("{}", output);
1269
1270 Ok(Value::Null)
1271 }
1272
1273 #[cfg(not(target_arch = "wasm32"))]
1274 Self::TimeNow => Ok(Value::Number(
1275 std::time::SystemTime::now()
1276 .duration_since(std::time::UNIX_EPOCH)
1277 .unwrap()
1278 .as_secs_f64(),
1279 )),
1280
1281 Self::Map => {
1283 let func = &args[1];
1284 let list_ptr = args[0].as_list_pointer()?;
1285 let func_def = {
1286 let borrowed_heap = heap.borrow();
1287 get_function_def(func, &borrowed_heap)
1288 .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1289 };
1290 let list_len = {
1291 let borrowed_heap = heap.borrow();
1292 list_ptr.reify(&borrowed_heap).as_list()?.len()
1293 };
1294 let func_accepts_two_args = func_def.arity().can_accept(2);
1295
1296 let mut mapped_list = Vec::with_capacity(list_len);
1297 for idx in 0..list_len {
1298 let item = {
1299 let borrowed_heap = heap.borrow();
1300 let list = list_ptr.reify(&borrowed_heap).as_list()?;
1301 list[idx]
1302 };
1303 let args = if func_accepts_two_args {
1304 vec![item, Value::Number(idx as f64)]
1305 } else {
1306 vec![item]
1307 };
1308
1309 let result = func_def.call(
1310 Value::Null,
1311 args,
1312 Rc::clone(&heap),
1313 Rc::clone(&bindings),
1314 call_depth + 1,
1315 source,
1316 )?;
1317 mapped_list.push(result);
1318 }
1319
1320 Ok(heap.borrow_mut().insert_list(mapped_list))
1321 }
1322
1323 Self::Filter => {
1324 let func = &args[1];
1325 let list_ptr = args[0].as_list_pointer()?;
1326 let func_def = {
1327 let borrowed_heap = heap.borrow();
1328 get_function_def(func, &borrowed_heap)
1329 .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1330 };
1331 let list_len = {
1332 let borrowed_heap = heap.borrow();
1333 list_ptr.reify(&borrowed_heap).as_list()?.len()
1334 };
1335 let func_accepts_two_args = func_def.arity().can_accept(2);
1336
1337 let mut filtered_list = vec![];
1338 for idx in 0..list_len {
1339 let item = {
1340 let borrowed_heap = heap.borrow();
1341 let list = list_ptr.reify(&borrowed_heap).as_list()?;
1342 list[idx]
1343 };
1344 let args = if func_accepts_two_args {
1345 vec![item, Value::Number(idx as f64)]
1346 } else {
1347 vec![item]
1348 };
1349
1350 let result = func_def.call(
1351 Value::Null,
1352 args,
1353 Rc::clone(&heap),
1354 Rc::clone(&bindings),
1355 call_depth + 1,
1356 source,
1357 )?;
1358 if result.as_bool()? {
1359 filtered_list.push(item);
1360 }
1361 }
1362
1363 Ok(heap.borrow_mut().insert_list(filtered_list))
1364 }
1365
1366 Self::Reduce => {
1367 let func = &args[1];
1368 let initial = args[2];
1369 let list_ptr = args[0].as_list_pointer()?;
1370 let func_def = {
1371 let borrowed_heap = heap.borrow();
1372 get_function_def(func, &borrowed_heap)
1373 .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1374 };
1375 let list_len = {
1376 let borrowed_heap = heap.borrow();
1377 list_ptr.reify(&borrowed_heap).as_list()?.len()
1378 };
1379 let func_accepts_three_args = func_def.arity().can_accept(3);
1380
1381 let mut accumulator = initial;
1382 for idx in 0..list_len {
1383 let item = {
1384 let borrowed_heap = heap.borrow();
1385 let list = list_ptr.reify(&borrowed_heap).as_list()?;
1386 list[idx]
1387 };
1388 let args = if func_accepts_three_args {
1389 vec![accumulator, item, Value::Number(idx as f64)]
1390 } else {
1391 vec![accumulator, item]
1392 };
1393
1394 accumulator = func_def.call(
1395 Value::Null,
1396 args,
1397 Rc::clone(&heap),
1398 Rc::clone(&bindings),
1399 call_depth + 1,
1400 source,
1401 )?;
1402 }
1403
1404 Ok(accumulator)
1405 }
1406
1407 Self::Every => {
1408 let func = &args[1];
1409 let list_ptr = args[0].as_list_pointer()?;
1410 let func_def = {
1411 let borrowed_heap = heap.borrow();
1412 get_function_def(func, &borrowed_heap)
1413 .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1414 };
1415 let list_len = {
1416 let borrowed_heap = heap.borrow();
1417 list_ptr.reify(&borrowed_heap).as_list()?.len()
1418 };
1419 let func_accepts_two_args = func_def.arity().can_accept(2);
1420
1421 for idx in 0..list_len {
1422 let item = {
1423 let borrowed_heap = heap.borrow();
1424 let list = list_ptr.reify(&borrowed_heap).as_list()?;
1425 list[idx]
1426 };
1427 let args = if func_accepts_two_args {
1428 vec![item, Value::Number(idx as f64)]
1429 } else {
1430 vec![item]
1431 };
1432
1433 let result = func_def.call(
1434 Value::Null,
1435 args,
1436 Rc::clone(&heap),
1437 Rc::clone(&bindings),
1438 call_depth + 1,
1439 source,
1440 )?;
1441 if !result.as_bool()? {
1442 return Ok(Value::Bool(false));
1443 }
1444 }
1445
1446 Ok(Value::Bool(true))
1447 }
1448
1449 Self::Some => {
1450 let func = &args[1];
1451 let list_ptr = args[0].as_list_pointer()?;
1452 let func_def = {
1453 let borrowed_heap = heap.borrow();
1454 get_function_def(func, &borrowed_heap)
1455 .ok_or_else(|| RuntimeError::from("second argument must be a function"))?
1456 };
1457 let list_len = {
1458 let borrowed_heap = heap.borrow();
1459 list_ptr.reify(&borrowed_heap).as_list()?.len()
1460 };
1461 let func_accepts_two_args = func_def.arity().can_accept(2);
1462
1463 for idx in 0..list_len {
1464 let item = {
1465 let borrowed_heap = heap.borrow();
1466 let list = list_ptr.reify(&borrowed_heap).as_list()?;
1467 list[idx]
1468 };
1469 let args = if func_accepts_two_args {
1470 vec![item, Value::Number(idx as f64)]
1471 } else {
1472 vec![item]
1473 };
1474
1475 let result = func_def.call(
1476 Value::Null,
1477 args,
1478 Rc::clone(&heap),
1479 Rc::clone(&bindings),
1480 call_depth + 1,
1481 source,
1482 )?;
1483 if result.as_bool()? {
1484 return Ok(Value::Bool(true));
1485 }
1486 }
1487
1488 Ok(Value::Bool(false))
1489 }
1490
1491 Self::SortBy => {
1492 let func = &args[1];
1493 let mut list = {
1494 let borrowed_heap = &heap.borrow();
1495 args[0].as_list(borrowed_heap)?.clone()
1496 };
1497
1498 list.sort_by(|a, b| {
1499 let func_def = get_function_def(func, &heap.borrow());
1501
1502 match func_def {
1503 Some(fd) => {
1504 let result_a = fd.call(
1505 Value::Null,
1506 vec![*a],
1507 Rc::clone(&heap),
1508 Rc::clone(&bindings),
1509 call_depth + 1,
1510 source,
1511 );
1512 let result_b = fd.call(
1513 Value::Null,
1514 vec![*b],
1515 Rc::clone(&heap),
1516 Rc::clone(&bindings),
1517 call_depth + 1,
1518 source,
1519 );
1520
1521 match (result_a, result_b) {
1522 (Ok(val_a), Ok(val_b)) => val_a
1523 .compare(&val_b, &heap.borrow())
1524 .unwrap_or(None)
1525 .unwrap_or(std::cmp::Ordering::Equal),
1526 _ => std::cmp::Ordering::Equal,
1527 }
1528 }
1529 _ => std::cmp::Ordering::Equal,
1530 }
1531 });
1532
1533 Ok(heap.borrow_mut().insert_list(list))
1534 }
1535
1536 Self::Ugt => match args[0].compare(&args[1], &heap.borrow())? {
1538 Some(std::cmp::Ordering::Greater) => Ok(Value::Bool(true)),
1539 _ => Ok(Value::Bool(false)),
1540 },
1541 Self::Ult => match args[0].compare(&args[1], &heap.borrow())? {
1542 Some(std::cmp::Ordering::Less) => Ok(Value::Bool(true)),
1543 _ => Ok(Value::Bool(false)),
1544 },
1545 Self::Ugte => match args[0].compare(&args[1], &heap.borrow())? {
1546 Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal) => {
1547 Ok(Value::Bool(true))
1548 }
1549 _ => Ok(Value::Bool(false)),
1550 },
1551 Self::Ulte => match args[0].compare(&args[1], &heap.borrow())? {
1552 Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal) => Ok(Value::Bool(true)),
1553 _ => Ok(Value::Bool(false)),
1554 },
1555 }
1556 }
1557
1558 pub fn all() -> Vec<Self> {
1559 vec![
1560 Self::Sqrt,
1561 Self::Sin,
1562 Self::Cos,
1563 Self::Tan,
1564 Self::Asin,
1565 Self::Acos,
1566 Self::Atan,
1567 Self::Log,
1568 Self::Log10,
1569 Self::Exp,
1570 Self::Abs,
1571 Self::Floor,
1572 Self::Ceil,
1573 Self::Round,
1574 Self::Trunc,
1575 Self::Random,
1576 Self::Min,
1577 Self::Max,
1578 Self::Avg,
1579 Self::Sum,
1580 Self::Prod,
1581 Self::Median,
1582 Self::Percentile,
1583 Self::Range,
1584 Self::Len,
1585 Self::Head,
1586 Self::Tail,
1587 Self::Slice,
1588 Self::Concat,
1589 Self::Dot,
1590 Self::Unique,
1591 Self::Sort,
1592 Self::SortBy,
1593 Self::Reverse,
1594 Self::Any,
1595 Self::All,
1596 Self::Map,
1597 Self::Reduce,
1598 Self::Filter,
1599 Self::Every,
1600 Self::Some,
1601 Self::Split,
1602 Self::Join,
1603 Self::Replace,
1604 Self::Trim,
1605 Self::Uppercase,
1606 Self::Lowercase,
1607 Self::ToString,
1608 Self::ToNumber,
1609 Self::ToBool,
1610 Self::Convert,
1611 Self::Includes,
1612 Self::Format,
1613 Self::Typeof,
1614 Self::Arity,
1615 Self::Keys,
1616 Self::Values,
1617 Self::Entries,
1618 Self::GroupBy,
1619 Self::CountBy,
1620 Self::Flatten,
1621 Self::Zip,
1622 Self::Chunk,
1623 Self::Ugt,
1624 Self::Ult,
1625 Self::Ugte,
1626 Self::Ulte,
1627 #[cfg(not(target_arch = "wasm32"))]
1628 Self::Print,
1629 #[cfg(not(target_arch = "wasm32"))]
1630 Self::TimeNow,
1631 ]
1632 }
1633
1634 pub fn all_names() -> Vec<&'static str> {
1635 Self::all().iter().map(|f| f.name()).collect()
1636 }
1637}
1638
1639impl FunctionDef {
1640 pub fn get_name(&self) -> String {
1641 match self {
1642 FunctionDef::BuiltIn(built_in) => {
1643 format!("built-in function \"{}\"", built_in.name())
1644 }
1645 FunctionDef::Lambda(LambdaDef { name, .. }) => name
1646 .clone()
1647 .map_or(String::from("anonymous function"), |n| {
1648 format!("function \"{}\"", n)
1649 }),
1650 }
1651 }
1652
1653 pub fn arity(&self) -> FunctionArity {
1654 match self {
1655 FunctionDef::BuiltIn(built_in) => built_in.arity(),
1656 FunctionDef::Lambda(lambda_def) => lambda_def.get_arity(),
1657 }
1658 }
1659
1660 pub fn check_arity(&self, arg_count: usize) -> Result<(), RuntimeError> {
1661 match self {
1662 FunctionDef::BuiltIn(built_in) => match built_in.arity() {
1663 FunctionArity::Exact(expected) => {
1664 if arg_count == expected {
1665 Ok(())
1666 } else {
1667 Err(RuntimeError::new(format!(
1668 "{} takes exactly {} arguments, but {} were given",
1669 self.get_name(),
1670 expected,
1671 arg_count
1672 )))
1673 }
1674 }
1675 FunctionArity::AtLeast(expected) => {
1676 if arg_count >= expected {
1677 Ok(())
1678 } else {
1679 Err(RuntimeError::new(format!(
1680 "{} takes at least {} arguments, but {} were given",
1681 self.get_name(),
1682 expected,
1683 arg_count
1684 )))
1685 }
1686 }
1687 FunctionArity::Between(min, max) => {
1688 if arg_count >= min && arg_count <= max {
1689 Ok(())
1690 } else {
1691 Err(RuntimeError::new(format!(
1692 "{} takes between {} and {} arguments, but {} were given",
1693 self.get_name(),
1694 min,
1695 max,
1696 arg_count
1697 )))
1698 }
1699 }
1700 },
1701 FunctionDef::Lambda(def) => {
1702 let arity = def.get_arity();
1703
1704 match arity {
1705 FunctionArity::Exact(expected) => {
1706 if arg_count == expected {
1707 Ok(())
1708 } else {
1709 Err(RuntimeError::new(format!(
1710 "{} takes exactly {} arguments, but {} were given",
1711 self.get_name(),
1712 expected,
1713 arg_count
1714 )))
1715 }
1716 }
1717 FunctionArity::AtLeast(expected) => {
1718 if arg_count >= expected {
1719 Ok(())
1720 } else {
1721 Err(RuntimeError::new(format!(
1722 "{} takes at least {} arguments, but {} were given",
1723 self.get_name(),
1724 expected,
1725 arg_count
1726 )))
1727 }
1728 }
1729 FunctionArity::Between(min, max) => {
1730 if arg_count >= min && arg_count <= max {
1731 Ok(())
1732 } else {
1733 Err(RuntimeError::new(format!(
1734 "{} takes between {} and {} arguments, but {} were given",
1735 self.get_name(),
1736 min,
1737 max,
1738 arg_count
1739 )))
1740 }
1741 }
1742 }
1743 }
1744 }
1745 }
1746
1747 pub fn call(
1748 &self,
1749 this_value: Value,
1750 args: Vec<Value>,
1751 heap: Rc<RefCell<Heap>>,
1752 bindings: Rc<Environment>,
1753 call_depth: usize,
1754 source: &str,
1755 ) -> Result<Value, RuntimeError> {
1756 #[cfg(not(target_arch = "wasm32"))]
1757 let start = std::time::Instant::now();
1758
1759 self.check_arity(args.len())?;
1760
1761 if call_depth > 1000 {
1762 return Err(RuntimeError::new(format!(
1763 "in {}: maximum call depth of 1000 exceeded",
1764 self.get_name()
1765 )));
1766 }
1767
1768 match self {
1769 FunctionDef::Lambda(LambdaDef {
1770 name,
1771 args: expected_args,
1772 body,
1773 scope,
1774 source: lambda_source,
1775 }) => {
1776 #[cfg(not(target_arch = "wasm32"))]
1777 let start_var_env = std::time::Instant::now();
1778
1779 let mut local_bindings = HashMap::new();
1781
1782 if let Some(fn_name) = name {
1784 local_bindings.insert(fn_name.clone(), this_value);
1785 }
1786
1787 if let Some(inputs) = bindings.get("inputs") {
1789 local_bindings.insert(String::from("inputs"), inputs);
1790 }
1791
1792 for (idx, expected_arg) in expected_args.iter().enumerate() {
1794 match expected_arg {
1795 LambdaArg::Required(arg_name) => {
1796 local_bindings.insert(arg_name.clone(), args[idx]);
1797 }
1798 LambdaArg::Optional(arg_name) => {
1799 local_bindings.insert(
1800 arg_name.clone(),
1801 args.get(idx).copied().unwrap_or(Value::Null),
1802 );
1803 }
1804 LambdaArg::Rest(arg_name) => {
1805 local_bindings.insert(
1806 arg_name.clone(),
1807 heap.borrow_mut()
1808 .insert_list(args.iter().skip(idx).copied().collect()),
1809 );
1810 }
1811 }
1812 }
1813
1814 #[cfg(not(target_arch = "wasm32"))]
1815 let end_var_env = std::time::Instant::now();
1816
1817 let parent_env = if scope.is_empty() {
1818 Rc::clone(&bindings)
1819 } else {
1820 Rc::new(Environment::extend_shared(
1821 Rc::clone(&bindings),
1822 scope.as_rc(),
1823 ))
1824 };
1825 let new_env = Rc::new(Environment::extend_with(parent_env, local_bindings));
1827
1828 let return_value = evaluate_ast(
1829 body,
1830 Rc::clone(&heap),
1831 new_env,
1832 call_depth + 1,
1833 lambda_source.clone(),
1834 )
1835 .map_err(|error| error.with_function_context(&self.get_name()));
1836
1837 #[cfg(not(target_arch = "wasm32"))]
1838 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1839 name: self.get_name(),
1840 start,
1841 end: std::time::Instant::now(),
1842 start_var_env: Some(start_var_env),
1843 end_var_env: Some(end_var_env),
1844 });
1845
1846 return_value
1847 }
1848 FunctionDef::BuiltIn(built_in) => {
1849 let return_value = built_in
1850 .call(args, heap, bindings, call_depth + 1, source)
1851 .map_err(|error| error.with_function_context(&self.get_name()));
1852
1853 #[cfg(not(target_arch = "wasm32"))]
1854 FUNCTION_CALLS.lock().unwrap().push(FunctionCallStats {
1855 name: self.get_name(),
1856 start,
1857 end: std::time::Instant::now(),
1858 start_var_env: None,
1859 end_var_env: None,
1860 });
1861
1862 return_value
1863 }
1864 }
1865 }
1866}
1867
1868pub fn is_built_in_function(ident: &str) -> bool {
1869 BuiltInFunction::from_ident(ident).is_some()
1870}
1871
1872pub fn get_built_in_function_def_by_ident(ident: &str) -> Option<FunctionDef> {
1873 BuiltInFunction::from_ident(ident).map(FunctionDef::BuiltIn)
1874}
1875
1876pub fn get_built_in_function_idents() -> Vec<&'static str> {
1877 BuiltInFunction::all_names()
1878}
1879
1880pub fn get_function_def(value: &Value, heap: &Heap) -> Option<FunctionDef> {
1881 match value {
1882 Value::Lambda(pointer) => Some(FunctionDef::Lambda(
1883 pointer.reify(heap).as_lambda().ok()?.clone(),
1884 )),
1885 Value::BuiltIn(built_in) => Some(FunctionDef::BuiltIn(*built_in)),
1886 _ => None,
1887 }
1888}
1889
1890#[cfg(test)]
1891mod tests {
1892 use super::*;
1893 use crate::environment::Environment;
1894 use std::cell::RefCell;
1895 use std::rc::Rc;
1896
1897 #[test]
1898 fn test_range_function() {
1899 let heap = Rc::new(RefCell::new(Heap::new()));
1901 let bindings = Rc::new(Environment::new());
1902 let range_fn = BuiltInFunction::Range;
1903
1904 let args = vec![Value::Number(4.0)];
1906 let result = range_fn
1907 .call(args, heap.clone(), bindings.clone(), 0, "")
1908 .unwrap();
1909
1910 let heap_borrow = heap.borrow();
1911 let list = result.as_list(&heap_borrow).unwrap();
1912 assert_eq!(list.len(), 4);
1913 assert_eq!(list[0], Value::Number(0.0));
1914 assert_eq!(list[3], Value::Number(3.0));
1915 }
1916
1917 #[test]
1918 fn test_range_function_two_args() {
1919 let heap = Rc::new(RefCell::new(Heap::new()));
1921 let bindings = Rc::new(Environment::new());
1922 let range_fn = BuiltInFunction::Range;
1923
1924 let args = vec![Value::Number(4.0), Value::Number(10.0)];
1926 let result = range_fn
1927 .call(args, heap.clone(), bindings.clone(), 0, "")
1928 .unwrap();
1929
1930 let heap_borrow = heap.borrow();
1931 let list = result.as_list(&heap_borrow).unwrap();
1932 assert_eq!(list.len(), 6);
1933 assert_eq!(list[0], Value::Number(4.0));
1934 assert_eq!(list[5], Value::Number(9.0));
1935 }
1936
1937 #[test]
1938 fn test_round_function_single_arg() {
1939 let heap = Rc::new(RefCell::new(Heap::new()));
1940 let bindings = Rc::new(Environment::new());
1941 let round_fn = BuiltInFunction::Round;
1942
1943 let test_cases = vec![
1945 (42.4, 42.0),
1946 (42.5, 43.0),
1947 (42.6, 43.0),
1948 (-42.4, -42.0),
1949 (-42.5, -43.0),
1950 (-42.6, -43.0),
1951 (0.0, 0.0),
1952 (1.999, 2.0),
1953 (-1.999, -2.0),
1954 ];
1955
1956 for (input, expected) in test_cases {
1957 let args = vec![Value::Number(input)];
1958 let result = round_fn
1959 .call(args, heap.clone(), bindings.clone(), 0, "")
1960 .unwrap();
1961 assert_eq!(
1962 result,
1963 Value::Number(expected),
1964 "round({}) should be {}",
1965 input,
1966 expected
1967 );
1968 }
1969 }
1970
1971 #[test]
1972 fn test_round_function_with_decimal_places() {
1973 let heap = Rc::new(RefCell::new(Heap::new()));
1974 let bindings = Rc::new(Environment::new());
1975 let round_fn = BuiltInFunction::Round;
1976
1977 let test_cases = vec![
1979 (42.4543, 0.0, 42.0),
1980 (42.4543, 1.0, 42.5),
1981 (42.4543, 2.0, 42.45),
1982 (42.4543, 3.0, 42.454),
1983 (42.4543, 4.0, 42.4543),
1984 (4.14159, 4.0, 4.1416),
1985 (0.005, 2.0, 0.01),
1986 (0.995, 2.0, 1.0),
1987 (9.995, 2.0, 9.99),
1990 (-9.995, 2.0, -9.99),
1991 ];
1992
1993 for (input, places, expected) in test_cases {
1994 let args = vec![Value::Number(input), Value::Number(places)];
1995 let result = round_fn
1996 .call(args, heap.clone(), bindings.clone(), 0, "")
1997 .unwrap();
1998
1999 if let Value::Number(result_num) = result {
2001 assert!(
2002 (result_num - expected).abs() < 1e-10,
2003 "round({}, {}) = {} should be close to {}",
2004 input,
2005 places,
2006 result_num,
2007 expected
2008 );
2009 } else {
2010 panic!("Expected number result");
2011 }
2012 }
2013 }
2014
2015 #[test]
2016 fn test_round_function_negative_decimal_places() {
2017 let heap = Rc::new(RefCell::new(Heap::new()));
2018 let bindings = Rc::new(Environment::new());
2019 let round_fn = BuiltInFunction::Round;
2020
2021 let test_cases = vec![
2023 (1234.567, -1.0, 1230.0),
2024 (1234.567, -2.0, 1200.0),
2025 (1234.567, -3.0, 1000.0),
2026 (1234.567, -4.0, 0.0),
2027 (5678.9, -1.0, 5680.0),
2028 (5678.9, -2.0, 5700.0),
2029 (5678.9, -3.0, 6000.0),
2030 (-1234.567, -1.0, -1230.0),
2031 (-1234.567, -2.0, -1200.0),
2032 (1500.0, -3.0, 2000.0),
2033 (-1500.0, -3.0, -2000.0),
2034 ];
2035
2036 for (input, places, expected) in test_cases {
2037 let args = vec![Value::Number(input), Value::Number(places)];
2038 let result = round_fn
2039 .call(args, heap.clone(), bindings.clone(), 0, "")
2040 .unwrap();
2041 assert_eq!(
2042 result,
2043 Value::Number(expected),
2044 "round({}, {}) should be {}",
2045 input,
2046 places,
2047 expected
2048 );
2049 }
2050 }
2051
2052 #[test]
2053 fn test_round_function_edge_cases() {
2054 let heap = Rc::new(RefCell::new(Heap::new()));
2055 let bindings = Rc::new(Environment::new());
2056 let round_fn = BuiltInFunction::Round;
2057
2058 let test_cases = vec![
2060 (f64::INFINITY, 0.0, f64::INFINITY),
2061 (f64::NEG_INFINITY, 0.0, f64::NEG_INFINITY),
2062 (0.0, 5.0, 0.0),
2063 (-0.0, 5.0, -0.0),
2064 (1e-10, 5.0, 0.0),
2065 (1e-10, 15.0, 1e-10),
2066 ];
2067
2068 for (input, places, expected) in test_cases {
2069 let args = vec![Value::Number(input), Value::Number(places)];
2070 let result = round_fn
2071 .call(args, heap.clone(), bindings.clone(), 0, "")
2072 .unwrap();
2073
2074 if let Value::Number(result_num) = result {
2075 if expected.is_infinite() {
2076 assert!(
2077 result_num.is_infinite()
2078 && result_num.is_sign_positive() == expected.is_sign_positive(),
2079 "round({}, {}) should be {}",
2080 input,
2081 places,
2082 expected
2083 );
2084 } else if expected == 0.0 || expected == -0.0 {
2085 assert!(
2086 result_num.abs() < 1e-10,
2087 "round({}, {}) = {} should be close to 0",
2088 input,
2089 places,
2090 result_num
2091 );
2092 } else {
2093 assert!(
2094 (result_num - expected).abs() < 1e-15,
2095 "round({}, {}) = {} should be close to {}",
2096 input,
2097 places,
2098 result_num,
2099 expected
2100 );
2101 }
2102 } else {
2103 panic!("Expected number result");
2104 }
2105 }
2106 }
2107
2108 #[test]
2109 fn test_random_function_deterministic() {
2110 let heap = Rc::new(RefCell::new(Heap::new()));
2111 let bindings = Rc::new(Environment::new());
2112 let random_fn = BuiltInFunction::Random;
2113
2114 let seed = 42.0;
2116 let args1 = vec![Value::Number(seed)];
2117 let result1 = random_fn
2118 .call(args1, heap.clone(), bindings.clone(), 0, "")
2119 .unwrap();
2120
2121 let args2 = vec![Value::Number(seed)];
2122 let result2 = random_fn
2123 .call(args2, heap.clone(), bindings.clone(), 0, "")
2124 .unwrap();
2125
2126 assert_eq!(result1, result2, "Same seed should produce same result");
2127 }
2128
2129 #[test]
2130 fn test_random_function_different_seeds() {
2131 let heap = Rc::new(RefCell::new(Heap::new()));
2132 let bindings = Rc::new(Environment::new());
2133 let random_fn = BuiltInFunction::Random;
2134
2135 let args1 = vec![Value::Number(42.0)];
2137 let result1 = random_fn
2138 .call(args1, heap.clone(), bindings.clone(), 0, "")
2139 .unwrap();
2140
2141 let args2 = vec![Value::Number(100.0)];
2142 let result2 = random_fn
2143 .call(args2, heap.clone(), bindings.clone(), 0, "")
2144 .unwrap();
2145
2146 assert_ne!(
2147 result1, result2,
2148 "Different seeds should produce different results"
2149 );
2150 }
2151
2152 #[test]
2153 fn test_random_function_range() {
2154 let heap = Rc::new(RefCell::new(Heap::new()));
2155 let bindings = Rc::new(Environment::new());
2156 let random_fn = BuiltInFunction::Random;
2157
2158 for seed in [0.0, 1.0, 42.0, 100.0, 999.0, 12345.0] {
2160 let args = vec![Value::Number(seed)];
2161 let result = random_fn
2162 .call(args, heap.clone(), bindings.clone(), 0, "")
2163 .unwrap();
2164
2165 if let Value::Number(num) = result {
2166 assert!(
2167 (0.0..1.0).contains(&num),
2168 "Random value {} should be in range [0, 1)",
2169 num
2170 );
2171 } else {
2172 panic!("Expected number result");
2173 }
2174 }
2175 }
2176
2177 #[test]
2178 fn test_random_function_negative_seed() {
2179 let heap = Rc::new(RefCell::new(Heap::new()));
2180 let bindings = Rc::new(Environment::new());
2181 let random_fn = BuiltInFunction::Random;
2182
2183 let args = vec![Value::Number(-42.0)];
2185 let result = random_fn
2186 .call(args, heap.clone(), bindings.clone(), 0, "")
2187 .unwrap();
2188
2189 if let Value::Number(num) = result {
2190 assert!(
2191 (0.0..1.0).contains(&num),
2192 "Random value {} should be in range [0, 1)",
2193 num
2194 );
2195 } else {
2196 panic!("Expected number result");
2197 }
2198 }
2199
2200 #[test]
2201 fn test_map_with_index() {
2202 use crate::values::{CapturedScope, LambdaDef};
2203
2204 let heap = Rc::new(RefCell::new(Heap::new()));
2205 let bindings = Rc::new(Environment::new());
2206
2207 let list = heap.borrow_mut().insert_list(vec![
2209 Value::Number(10.0),
2210 Value::Number(20.0),
2211 Value::Number(30.0),
2212 ]);
2213
2214 let lambda = LambdaDef {
2216 name: None,
2217 args: vec![
2218 crate::values::LambdaArg::Required("x".to_string()),
2219 crate::values::LambdaArg::Required("i".to_string()),
2220 ],
2221 body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2222 op: crate::ast::BinaryOp::Add,
2223 left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2224 "x".to_string(),
2225 ))),
2226 right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2227 "i".to_string(),
2228 ))),
2229 }),
2230 scope: CapturedScope::default(),
2231 source: Rc::from(""),
2232 };
2233 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2234
2235 let result = BuiltInFunction::Map
2237 .call(
2238 vec![list, lambda_value],
2239 heap.clone(),
2240 bindings.clone(),
2241 0,
2242 "",
2243 )
2244 .unwrap();
2245
2246 let heap_borrow = heap.borrow();
2248 let result_list = result.as_list(&heap_borrow).unwrap();
2249 assert_eq!(result_list.len(), 3);
2250 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)); }
2254
2255 #[test]
2256 fn test_filter_with_index() {
2257 use crate::values::{CapturedScope, LambdaDef};
2258
2259 let heap = Rc::new(RefCell::new(Heap::new()));
2260 let bindings = Rc::new(Environment::new());
2261
2262 let list = heap.borrow_mut().insert_list(vec![
2264 Value::Number(10.0),
2265 Value::Number(20.0),
2266 Value::Number(30.0),
2267 Value::Number(40.0),
2268 ]);
2269
2270 let lambda = LambdaDef {
2272 name: None,
2273 args: vec![
2274 crate::values::LambdaArg::Required("x".to_string()),
2275 crate::values::LambdaArg::Required("i".to_string()),
2276 ],
2277 body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2278 op: crate::ast::BinaryOp::Greater,
2279 left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2280 "i".to_string(),
2281 ))),
2282 right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Number(1.0))),
2283 }),
2284 scope: CapturedScope::default(),
2285 source: Rc::from(""),
2286 };
2287 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2288
2289 let result = BuiltInFunction::Filter
2291 .call(
2292 vec![list, lambda_value],
2293 heap.clone(),
2294 bindings.clone(),
2295 0,
2296 "",
2297 )
2298 .unwrap();
2299
2300 let heap_borrow = heap.borrow();
2302 let result_list = result.as_list(&heap_borrow).unwrap();
2303 assert_eq!(result_list.len(), 2);
2304 assert_eq!(result_list[0], Value::Number(30.0));
2305 assert_eq!(result_list[1], Value::Number(40.0));
2306 }
2307
2308 #[test]
2309 fn test_reduce_with_index() {
2310 use crate::values::{CapturedScope, LambdaDef};
2311
2312 let heap = Rc::new(RefCell::new(Heap::new()));
2313 let bindings = Rc::new(Environment::new());
2314
2315 let list = heap.borrow_mut().insert_list(vec![
2317 Value::Number(10.0),
2318 Value::Number(20.0),
2319 Value::Number(30.0),
2320 ]);
2321
2322 let lambda = LambdaDef {
2324 name: None,
2325 args: vec![
2326 crate::values::LambdaArg::Required("acc".to_string()),
2327 crate::values::LambdaArg::Required("x".to_string()),
2328 crate::values::LambdaArg::Required("i".to_string()),
2329 ],
2330 body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2331 op: crate::ast::BinaryOp::Add,
2332 left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2333 op: crate::ast::BinaryOp::Add,
2334 left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2335 "acc".to_string(),
2336 ))),
2337 right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2338 "x".to_string(),
2339 ))),
2340 })),
2341 right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2342 "i".to_string(),
2343 ))),
2344 }),
2345 scope: CapturedScope::default(),
2346 source: Rc::from(""),
2347 };
2348 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2349
2350 let result = BuiltInFunction::Reduce
2352 .call(
2353 vec![list, lambda_value, Value::Number(0.0)],
2354 heap.clone(),
2355 bindings.clone(),
2356 0,
2357 "",
2358 )
2359 .unwrap();
2360
2361 assert_eq!(result, Value::Number(63.0));
2363 }
2364
2365 #[test]
2366 fn test_map_backward_compatible() {
2367 use crate::values::{CapturedScope, LambdaDef};
2368
2369 let heap = Rc::new(RefCell::new(Heap::new()));
2370 let bindings = Rc::new(Environment::new());
2371
2372 let list = heap.borrow_mut().insert_list(vec![
2374 Value::Number(10.0),
2375 Value::Number(20.0),
2376 Value::Number(30.0),
2377 ]);
2378
2379 let lambda = LambdaDef {
2381 name: None,
2382 args: vec![crate::values::LambdaArg::Required("x".to_string())],
2383 body: crate::ast::Spanned::dummy(crate::ast::Expr::BinaryOp {
2384 op: crate::ast::BinaryOp::Multiply,
2385 left: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Identifier(
2386 "x".to_string(),
2387 ))),
2388 right: Box::new(crate::ast::Spanned::dummy(crate::ast::Expr::Number(2.0))),
2389 }),
2390 scope: CapturedScope::default(),
2391 source: Rc::from(""),
2392 };
2393 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2394
2395 let result = BuiltInFunction::Map
2397 .call(
2398 vec![list, lambda_value],
2399 heap.clone(),
2400 bindings.clone(),
2401 0,
2402 "",
2403 )
2404 .unwrap();
2405
2406 let heap_borrow = heap.borrow();
2408 let result_list = result.as_list(&heap_borrow).unwrap();
2409 assert_eq!(result_list.len(), 3);
2410 assert_eq!(result_list[0], Value::Number(20.0));
2411 assert_eq!(result_list[1], Value::Number(40.0));
2412 assert_eq!(result_list[2], Value::Number(60.0));
2413 }
2414
2415 #[test]
2416 fn test_convert_length() {
2417 let heap = Rc::new(RefCell::new(Heap::new()));
2418 let bindings = Rc::new(Environment::new());
2419 let convert_fn = BuiltInFunction::Convert;
2420
2421 let from_unit = heap.borrow_mut().insert_string("km".to_string());
2423 let to_unit = heap.borrow_mut().insert_string("m".to_string());
2424 let args = vec![Value::Number(1.0), from_unit, to_unit];
2425 let result = convert_fn
2426 .call(args, heap.clone(), bindings.clone(), 0, "")
2427 .unwrap();
2428 assert_eq!(result, Value::Number(1000.0));
2429
2430 let from_unit = heap.borrow_mut().insert_string("miles".to_string());
2432 let to_unit = heap.borrow_mut().insert_string("km".to_string());
2433 let args = vec![Value::Number(1.0), from_unit, to_unit];
2434 let result = convert_fn
2435 .call(args, heap.clone(), bindings.clone(), 0, "")
2436 .unwrap();
2437 if let Value::Number(n) = result {
2438 assert!((n - 1.609344).abs() < 1e-6);
2439 } else {
2440 panic!("Expected number");
2441 }
2442 }
2443
2444 #[test]
2445 fn test_convert_temperature() {
2446 let heap = Rc::new(RefCell::new(Heap::new()));
2447 let bindings = Rc::new(Environment::new());
2448 let convert_fn = BuiltInFunction::Convert;
2449
2450 let from_unit = heap.borrow_mut().insert_string("celsius".to_string());
2452 let to_unit = heap.borrow_mut().insert_string("fahrenheit".to_string());
2453 let args = vec![Value::Number(0.0), from_unit, to_unit];
2454 let result = convert_fn
2455 .call(args, heap.clone(), bindings.clone(), 0, "")
2456 .unwrap();
2457 if let Value::Number(n) = result {
2458 assert!((n - 32.0).abs() < 1e-10);
2459 }
2460
2461 let from_unit = heap.borrow_mut().insert_string("celsius".to_string());
2463 let to_unit = heap.borrow_mut().insert_string("fahrenheit".to_string());
2464 let args = vec![Value::Number(100.0), from_unit, to_unit];
2465 let result = convert_fn
2466 .call(args, heap.clone(), bindings.clone(), 0, "")
2467 .unwrap();
2468 if let Value::Number(n) = result {
2469 assert!((n - 212.0).abs() < 1e-10);
2470 }
2471 }
2472
2473 #[test]
2474 fn test_convert_mass() {
2475 let heap = Rc::new(RefCell::new(Heap::new()));
2476 let bindings = Rc::new(Environment::new());
2477 let convert_fn = BuiltInFunction::Convert;
2478
2479 let from_unit = heap.borrow_mut().insert_string("kg".to_string());
2481 let to_unit = heap.borrow_mut().insert_string("lbs".to_string());
2482 let args = vec![Value::Number(1.0), from_unit, to_unit];
2483 let result = convert_fn
2484 .call(args, heap.clone(), bindings.clone(), 0, "")
2485 .unwrap();
2486 if let Value::Number(n) = result {
2487 assert!((n - 2.20462).abs() < 0.001);
2488 }
2489 }
2490
2491 #[test]
2492 fn test_convert_information_storage() {
2493 let heap = Rc::new(RefCell::new(Heap::new()));
2494 let bindings = Rc::new(Environment::new());
2495 let convert_fn = BuiltInFunction::Convert;
2496
2497 let from_unit = heap.borrow_mut().insert_string("kibibytes".to_string());
2499 let to_unit = heap.borrow_mut().insert_string("bytes".to_string());
2500 let args = vec![Value::Number(1.0), from_unit, to_unit];
2501 let result = convert_fn
2502 .call(args, heap.clone(), bindings.clone(), 0, "")
2503 .unwrap();
2504 assert_eq!(result, Value::Number(1024.0));
2505 }
2506
2507 #[test]
2508 fn test_convert_same_unit() {
2509 let heap = Rc::new(RefCell::new(Heap::new()));
2510 let bindings = Rc::new(Environment::new());
2511 let convert_fn = BuiltInFunction::Convert;
2512
2513 let from_unit = heap.borrow_mut().insert_string("meters".to_string());
2515 let to_unit = heap.borrow_mut().insert_string("m".to_string());
2516 let args = vec![Value::Number(42.0), from_unit, to_unit];
2517 let result = convert_fn
2518 .call(args, heap.clone(), bindings.clone(), 0, "")
2519 .unwrap();
2520 assert_eq!(result, Value::Number(42.0));
2521 }
2522
2523 #[test]
2524 fn test_convert_incompatible_units() {
2525 let heap = Rc::new(RefCell::new(Heap::new()));
2526 let bindings = Rc::new(Environment::new());
2527 let convert_fn = BuiltInFunction::Convert;
2528
2529 let from_unit = heap.borrow_mut().insert_string("kg".to_string());
2531 let to_unit = heap.borrow_mut().insert_string("meters".to_string());
2532 let args = vec![Value::Number(1.0), from_unit, to_unit];
2533 let result = convert_fn.call(args, heap.clone(), bindings.clone(), 0, "");
2534 assert!(result.is_err());
2535 assert!(result.unwrap_err().to_string().contains("Cannot convert"));
2536 }
2537
2538 #[test]
2539 fn test_convert_unknown_unit() {
2540 let heap = Rc::new(RefCell::new(Heap::new()));
2541 let bindings = Rc::new(Environment::new());
2542 let convert_fn = BuiltInFunction::Convert;
2543
2544 let from_unit = heap.borrow_mut().insert_string("foobar".to_string());
2546 let to_unit = heap.borrow_mut().insert_string("meters".to_string());
2547 let args = vec![Value::Number(1.0), from_unit, to_unit];
2548 let result = convert_fn.call(args, heap.clone(), bindings.clone(), 0, "");
2549 assert!(result.is_err());
2550 assert!(result.unwrap_err().to_string().contains("Unknown unit"));
2551 }
2552
2553 #[test]
2554 fn test_convert_case_insensitive() {
2555 let heap = Rc::new(RefCell::new(Heap::new()));
2556 let bindings = Rc::new(Environment::new());
2557 let convert_fn = BuiltInFunction::Convert;
2558
2559 let from_unit = heap.borrow_mut().insert_string("KM".to_string());
2561 let to_unit = heap.borrow_mut().insert_string("M".to_string());
2562 let args = vec![Value::Number(1.0), from_unit, to_unit];
2563 let result = convert_fn
2564 .call(args, heap.clone(), bindings.clone(), 0, "")
2565 .unwrap();
2566 assert_eq!(result, Value::Number(1000.0));
2567 }
2568
2569 #[test]
2570 fn test_flatten() {
2571 let heap = Rc::new(RefCell::new(Heap::new()));
2572 let bindings = Rc::new(Environment::new());
2573
2574 let inner1 = heap
2576 .borrow_mut()
2577 .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2578 let inner2 = heap
2579 .borrow_mut()
2580 .insert_list(vec![Value::Number(3.0), Value::Number(4.0)]);
2581 let list = heap.borrow_mut().insert_list(vec![inner1, inner2]);
2582
2583 let result = BuiltInFunction::Flatten
2584 .call(vec![list], heap.clone(), bindings.clone(), 0, "")
2585 .unwrap();
2586
2587 let heap_borrow = heap.borrow();
2588 let result_list = result.as_list(&heap_borrow).unwrap();
2589 assert_eq!(result_list.len(), 4);
2590 assert_eq!(result_list[0], Value::Number(1.0));
2591 assert_eq!(result_list[1], Value::Number(2.0));
2592 assert_eq!(result_list[2], Value::Number(3.0));
2593 assert_eq!(result_list[3], Value::Number(4.0));
2594 }
2595
2596 #[test]
2597 fn test_flatten_with_non_list_elements() {
2598 let heap = Rc::new(RefCell::new(Heap::new()));
2599 let bindings = Rc::new(Environment::new());
2600
2601 let inner1 = heap
2603 .borrow_mut()
2604 .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2605 let inner2 = heap
2606 .borrow_mut()
2607 .insert_list(vec![Value::Number(4.0), Value::Number(5.0)]);
2608 let list = heap
2609 .borrow_mut()
2610 .insert_list(vec![inner1, Value::Number(3.0), inner2]);
2611
2612 let result = BuiltInFunction::Flatten
2613 .call(vec![list], heap.clone(), bindings.clone(), 0, "")
2614 .unwrap();
2615
2616 let heap_borrow = heap.borrow();
2617 let result_list = result.as_list(&heap_borrow).unwrap();
2618 assert_eq!(result_list.len(), 5);
2619 assert_eq!(result_list[0], Value::Number(1.0));
2620 assert_eq!(result_list[1], Value::Number(2.0));
2621 assert_eq!(result_list[2], Value::Number(3.0));
2622 assert_eq!(result_list[3], Value::Number(4.0));
2623 assert_eq!(result_list[4], Value::Number(5.0));
2624 }
2625
2626 #[test]
2627 fn test_flatten_one_level_only() {
2628 let heap = Rc::new(RefCell::new(Heap::new()));
2629 let bindings = Rc::new(Environment::new());
2630
2631 let deep_inner = heap
2633 .borrow_mut()
2634 .insert_list(vec![Value::Number(2.0), Value::Number(3.0)]);
2635 let inner1 = heap
2636 .borrow_mut()
2637 .insert_list(vec![Value::Number(1.0), deep_inner]);
2638 let inner2 = heap.borrow_mut().insert_list(vec![Value::Number(4.0)]);
2639 let list = heap.borrow_mut().insert_list(vec![inner1, inner2]);
2640
2641 let result = BuiltInFunction::Flatten
2642 .call(vec![list], heap.clone(), bindings.clone(), 0, "")
2643 .unwrap();
2644
2645 let heap_borrow = heap.borrow();
2646 let result_list = result.as_list(&heap_borrow).unwrap();
2647 assert_eq!(result_list.len(), 3);
2649 assert_eq!(result_list[0], Value::Number(1.0));
2650 assert!(matches!(result_list[1], Value::List(_)));
2652 assert_eq!(result_list[2], Value::Number(4.0));
2653 }
2654
2655 #[test]
2656 fn test_zip_equal_lengths() {
2657 let heap = Rc::new(RefCell::new(Heap::new()));
2658 let bindings = Rc::new(Environment::new());
2659
2660 let list1 = heap
2661 .borrow_mut()
2662 .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2663 let str_a = heap.borrow_mut().insert_string("a".to_string());
2664 let str_b = heap.borrow_mut().insert_string("b".to_string());
2665 let list2 = heap.borrow_mut().insert_list(vec![str_a, str_b]);
2666
2667 let result = BuiltInFunction::Zip
2668 .call(vec![list1, list2], heap.clone(), bindings.clone(), 0, "")
2669 .unwrap();
2670
2671 let heap_borrow = heap.borrow();
2672 let result_list = result.as_list(&heap_borrow).unwrap();
2673 assert_eq!(result_list.len(), 2);
2674
2675 let pair1 = result_list[0].as_list(&heap_borrow).unwrap();
2676 assert_eq!(pair1.len(), 2);
2677 assert_eq!(pair1[0], Value::Number(1.0));
2678
2679 let pair2 = result_list[1].as_list(&heap_borrow).unwrap();
2680 assert_eq!(pair2.len(), 2);
2681 assert_eq!(pair2[0], Value::Number(2.0));
2682 }
2683
2684 #[test]
2685 fn test_zip_unequal_lengths_pads_with_null() {
2686 let heap = Rc::new(RefCell::new(Heap::new()));
2687 let bindings = Rc::new(Environment::new());
2688
2689 let list1 = heap.borrow_mut().insert_list(vec![Value::Number(1.0)]);
2690 let str_a = heap.borrow_mut().insert_string("a".to_string());
2691 let str_b = heap.borrow_mut().insert_string("b".to_string());
2692 let str_c = heap.borrow_mut().insert_string("c".to_string());
2693 let list2 = heap.borrow_mut().insert_list(vec![str_a, str_b, str_c]);
2694
2695 let result = BuiltInFunction::Zip
2696 .call(vec![list1, list2], heap.clone(), bindings.clone(), 0, "")
2697 .unwrap();
2698
2699 let heap_borrow = heap.borrow();
2700 let result_list = result.as_list(&heap_borrow).unwrap();
2701 assert_eq!(result_list.len(), 3); let pair1 = result_list[0].as_list(&heap_borrow).unwrap();
2704 assert_eq!(pair1[0], Value::Number(1.0));
2705
2706 let pair2 = result_list[1].as_list(&heap_borrow).unwrap();
2707 assert_eq!(pair2[0], Value::Null); let pair3 = result_list[2].as_list(&heap_borrow).unwrap();
2710 assert_eq!(pair3[0], Value::Null); }
2712
2713 #[test]
2714 fn test_zip_three_lists() {
2715 let heap = Rc::new(RefCell::new(Heap::new()));
2716 let bindings = Rc::new(Environment::new());
2717
2718 let list1 = heap
2719 .borrow_mut()
2720 .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2721 let str_a = heap.borrow_mut().insert_string("a".to_string());
2722 let str_b = heap.borrow_mut().insert_string("b".to_string());
2723 let list2 = heap.borrow_mut().insert_list(vec![str_a, str_b]);
2724 let list3 = heap
2725 .borrow_mut()
2726 .insert_list(vec![Value::Bool(true), Value::Bool(false)]);
2727
2728 let result = BuiltInFunction::Zip
2729 .call(
2730 vec![list1, list2, list3],
2731 heap.clone(),
2732 bindings.clone(),
2733 0,
2734 "",
2735 )
2736 .unwrap();
2737
2738 let heap_borrow = heap.borrow();
2739 let result_list = result.as_list(&heap_borrow).unwrap();
2740 assert_eq!(result_list.len(), 2);
2741
2742 let tuple1 = result_list[0].as_list(&heap_borrow).unwrap();
2743 assert_eq!(tuple1.len(), 3);
2744 assert_eq!(tuple1[0], Value::Number(1.0));
2745 assert_eq!(tuple1[2], Value::Bool(true));
2746 }
2747
2748 #[test]
2749 fn test_chunk() {
2750 let heap = Rc::new(RefCell::new(Heap::new()));
2751 let bindings = Rc::new(Environment::new());
2752
2753 let list = heap.borrow_mut().insert_list(vec![
2754 Value::Number(1.0),
2755 Value::Number(2.0),
2756 Value::Number(3.0),
2757 Value::Number(4.0),
2758 Value::Number(5.0),
2759 ]);
2760
2761 let result = BuiltInFunction::Chunk
2762 .call(
2763 vec![list, Value::Number(2.0)],
2764 heap.clone(),
2765 bindings.clone(),
2766 0,
2767 "",
2768 )
2769 .unwrap();
2770
2771 let heap_borrow = heap.borrow();
2772 let result_list = result.as_list(&heap_borrow).unwrap();
2773 assert_eq!(result_list.len(), 3);
2774
2775 let chunk1 = result_list[0].as_list(&heap_borrow).unwrap();
2776 assert_eq!(chunk1.len(), 2);
2777 assert_eq!(chunk1[0], Value::Number(1.0));
2778 assert_eq!(chunk1[1], Value::Number(2.0));
2779
2780 let chunk2 = result_list[1].as_list(&heap_borrow).unwrap();
2781 assert_eq!(chunk2.len(), 2);
2782 assert_eq!(chunk2[0], Value::Number(3.0));
2783 assert_eq!(chunk2[1], Value::Number(4.0));
2784
2785 let chunk3 = result_list[2].as_list(&heap_borrow).unwrap();
2786 assert_eq!(chunk3.len(), 1); assert_eq!(chunk3[0], Value::Number(5.0));
2788 }
2789
2790 #[test]
2791 fn test_chunk_zero_size_error() {
2792 let heap = Rc::new(RefCell::new(Heap::new()));
2793 let bindings = Rc::new(Environment::new());
2794
2795 let list = heap
2796 .borrow_mut()
2797 .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2798
2799 let result = BuiltInFunction::Chunk.call(
2800 vec![list, Value::Number(0.0)],
2801 heap.clone(),
2802 bindings.clone(),
2803 0,
2804 "",
2805 );
2806
2807 assert!(result.is_err());
2808 assert!(
2809 result
2810 .unwrap_err()
2811 .to_string()
2812 .contains("chunk size must be greater than 0")
2813 );
2814 }
2815
2816 #[test]
2817 fn test_chunk_empty_list() {
2818 let heap = Rc::new(RefCell::new(Heap::new()));
2819 let bindings = Rc::new(Environment::new());
2820
2821 let list = heap.borrow_mut().insert_list(vec![]);
2822
2823 let result = BuiltInFunction::Chunk
2824 .call(
2825 vec![list, Value::Number(2.0)],
2826 heap.clone(),
2827 bindings.clone(),
2828 0,
2829 "",
2830 )
2831 .unwrap();
2832
2833 let heap_borrow = heap.borrow();
2834 let result_list = result.as_list(&heap_borrow).unwrap();
2835 assert_eq!(result_list.len(), 0);
2836 }
2837
2838 #[test]
2839 fn test_group_by() {
2840 use crate::values::{CapturedScope, LambdaDef};
2841
2842 let heap = Rc::new(RefCell::new(Heap::new()));
2843 let bindings = Rc::new(Environment::new());
2844
2845 let str_a1 = heap.borrow_mut().insert_string("a".to_string());
2847 let str_b = heap.borrow_mut().insert_string("b".to_string());
2848 let str_a2 = heap.borrow_mut().insert_string("a".to_string());
2849 let simple_list = heap.borrow_mut().insert_list(vec![str_a1, str_b, str_a2]);
2850
2851 let lambda = LambdaDef {
2853 name: None,
2854 args: vec![crate::values::LambdaArg::Required("x".to_string())],
2855 body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2856 scope: CapturedScope::default(),
2857 source: Rc::from(""),
2858 };
2859 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2860
2861 let result = BuiltInFunction::GroupBy
2862 .call(
2863 vec![simple_list, lambda_value],
2864 heap.clone(),
2865 bindings.clone(),
2866 0,
2867 "",
2868 )
2869 .unwrap();
2870
2871 let heap_borrow = heap.borrow();
2872 let record = result.as_record(&heap_borrow).unwrap();
2873 assert_eq!(record.len(), 2); assert!(record.contains_key("a"));
2875 assert!(record.contains_key("b"));
2876
2877 let group_a = record.get("a").unwrap().as_list(&heap_borrow).unwrap();
2878 assert_eq!(group_a.len(), 2); let group_b = record.get("b").unwrap().as_list(&heap_borrow).unwrap();
2881 assert_eq!(group_b.len(), 1); }
2883
2884 #[test]
2885 fn test_group_by_non_string_key_error() {
2886 use crate::values::{CapturedScope, LambdaDef};
2887
2888 let heap = Rc::new(RefCell::new(Heap::new()));
2889 let bindings = Rc::new(Environment::new());
2890
2891 let list = heap
2892 .borrow_mut()
2893 .insert_list(vec![Value::Number(1.0), Value::Number(2.0)]);
2894
2895 let lambda = LambdaDef {
2897 name: None,
2898 args: vec![crate::values::LambdaArg::Required("x".to_string())],
2899 body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2900 scope: CapturedScope::default(),
2901 source: Rc::from(""),
2902 };
2903 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2904
2905 let result = BuiltInFunction::GroupBy.call(
2906 vec![list, lambda_value],
2907 heap.clone(),
2908 bindings.clone(),
2909 0,
2910 "",
2911 );
2912
2913 assert!(result.is_err());
2914 assert!(
2915 result
2916 .unwrap_err()
2917 .to_string()
2918 .contains("must return a string")
2919 );
2920 }
2921
2922 #[test]
2923 fn test_count_by() {
2924 use crate::values::{CapturedScope, LambdaDef};
2925
2926 let heap = Rc::new(RefCell::new(Heap::new()));
2927 let bindings = Rc::new(Environment::new());
2928
2929 let str_a1 = heap.borrow_mut().insert_string("a".to_string());
2931 let str_b = heap.borrow_mut().insert_string("b".to_string());
2932 let str_a2 = heap.borrow_mut().insert_string("a".to_string());
2933 let str_a3 = heap.borrow_mut().insert_string("a".to_string());
2934 let list = heap
2935 .borrow_mut()
2936 .insert_list(vec![str_a1, str_b, str_a2, str_a3]);
2937
2938 let lambda = LambdaDef {
2940 name: None,
2941 args: vec![crate::values::LambdaArg::Required("x".to_string())],
2942 body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2943 scope: CapturedScope::default(),
2944 source: Rc::from(""),
2945 };
2946 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2947
2948 let result = BuiltInFunction::CountBy
2949 .call(
2950 vec![list, lambda_value],
2951 heap.clone(),
2952 bindings.clone(),
2953 0,
2954 "",
2955 )
2956 .unwrap();
2957
2958 let heap_borrow = heap.borrow();
2959 let record = result.as_record(&heap_borrow).unwrap();
2960 assert_eq!(record.len(), 2); assert_eq!(*record.get("a").unwrap(), Value::Number(3.0)); assert_eq!(*record.get("b").unwrap(), Value::Number(1.0)); }
2965
2966 #[test]
2967 fn test_count_by_empty_list() {
2968 use crate::values::{CapturedScope, LambdaDef};
2969
2970 let heap = Rc::new(RefCell::new(Heap::new()));
2971 let bindings = Rc::new(Environment::new());
2972
2973 let list = heap.borrow_mut().insert_list(vec![]);
2974
2975 let lambda = LambdaDef {
2977 name: None,
2978 args: vec![crate::values::LambdaArg::Required("x".to_string())],
2979 body: crate::ast::Spanned::dummy(crate::ast::Expr::Identifier("x".to_string())),
2980 scope: CapturedScope::default(),
2981 source: Rc::from(""),
2982 };
2983 let lambda_value = heap.borrow_mut().insert_lambda(lambda);
2984
2985 let result = BuiltInFunction::CountBy
2986 .call(
2987 vec![list, lambda_value],
2988 heap.clone(),
2989 bindings.clone(),
2990 0,
2991 "",
2992 )
2993 .unwrap();
2994
2995 let heap_borrow = heap.borrow();
2996 let record = result.as_record(&heap_borrow).unwrap();
2997 assert_eq!(record.len(), 0); }
2999
3000 #[test]
3001 fn test_ugt_same_types() {
3002 let heap = Rc::new(RefCell::new(Heap::new()));
3003 let bindings = Rc::new(Environment::new());
3004
3005 let result = BuiltInFunction::Ugt
3007 .call(
3008 vec![Value::Number(5.0), Value::Number(3.0)],
3009 heap.clone(),
3010 bindings.clone(),
3011 0,
3012 "",
3013 )
3014 .unwrap();
3015 assert_eq!(result, Value::Bool(true));
3016
3017 let result = BuiltInFunction::Ugt
3019 .call(
3020 vec![Value::Number(3.0), Value::Number(5.0)],
3021 heap.clone(),
3022 bindings.clone(),
3023 0,
3024 "",
3025 )
3026 .unwrap();
3027 assert_eq!(result, Value::Bool(false));
3028
3029 let result = BuiltInFunction::Ugt
3031 .call(
3032 vec![Value::Number(5.0), Value::Number(5.0)],
3033 heap.clone(),
3034 bindings.clone(),
3035 0,
3036 "",
3037 )
3038 .unwrap();
3039 assert_eq!(result, Value::Bool(false));
3040 }
3041
3042 #[test]
3043 fn test_ult_same_types() {
3044 let heap = Rc::new(RefCell::new(Heap::new()));
3045 let bindings = Rc::new(Environment::new());
3046
3047 let result = BuiltInFunction::Ult
3049 .call(
3050 vec![Value::Number(3.0), Value::Number(5.0)],
3051 heap.clone(),
3052 bindings.clone(),
3053 0,
3054 "",
3055 )
3056 .unwrap();
3057 assert_eq!(result, Value::Bool(true));
3058
3059 let result = BuiltInFunction::Ult
3061 .call(
3062 vec![Value::Number(5.0), Value::Number(3.0)],
3063 heap.clone(),
3064 bindings.clone(),
3065 0,
3066 "",
3067 )
3068 .unwrap();
3069 assert_eq!(result, Value::Bool(false));
3070 }
3071
3072 #[test]
3073 fn test_ugte_same_types() {
3074 let heap = Rc::new(RefCell::new(Heap::new()));
3075 let bindings = Rc::new(Environment::new());
3076
3077 let result = BuiltInFunction::Ugte
3079 .call(
3080 vec![Value::Number(5.0), Value::Number(3.0)],
3081 heap.clone(),
3082 bindings.clone(),
3083 0,
3084 "",
3085 )
3086 .unwrap();
3087 assert_eq!(result, Value::Bool(true));
3088
3089 let result = BuiltInFunction::Ugte
3091 .call(
3092 vec![Value::Number(5.0), Value::Number(5.0)],
3093 heap.clone(),
3094 bindings.clone(),
3095 0,
3096 "",
3097 )
3098 .unwrap();
3099 assert_eq!(result, Value::Bool(true));
3100
3101 let result = BuiltInFunction::Ugte
3103 .call(
3104 vec![Value::Number(3.0), Value::Number(5.0)],
3105 heap.clone(),
3106 bindings.clone(),
3107 0,
3108 "",
3109 )
3110 .unwrap();
3111 assert_eq!(result, Value::Bool(false));
3112 }
3113
3114 #[test]
3115 fn test_ulte_same_types() {
3116 let heap = Rc::new(RefCell::new(Heap::new()));
3117 let bindings = Rc::new(Environment::new());
3118
3119 let result = BuiltInFunction::Ulte
3121 .call(
3122 vec![Value::Number(3.0), Value::Number(5.0)],
3123 heap.clone(),
3124 bindings.clone(),
3125 0,
3126 "",
3127 )
3128 .unwrap();
3129 assert_eq!(result, Value::Bool(true));
3130
3131 let result = BuiltInFunction::Ulte
3133 .call(
3134 vec![Value::Number(5.0), Value::Number(5.0)],
3135 heap.clone(),
3136 bindings.clone(),
3137 0,
3138 "",
3139 )
3140 .unwrap();
3141 assert_eq!(result, Value::Bool(true));
3142
3143 let result = BuiltInFunction::Ulte
3145 .call(
3146 vec![Value::Number(5.0), Value::Number(3.0)],
3147 heap.clone(),
3148 bindings.clone(),
3149 0,
3150 "",
3151 )
3152 .unwrap();
3153 assert_eq!(result, Value::Bool(false));
3154 }
3155
3156 #[test]
3157 fn test_unchecked_comparisons_mixed_types_return_false() {
3158 let heap = Rc::new(RefCell::new(Heap::new()));
3159 let bindings = Rc::new(Environment::new());
3160
3161 let result = BuiltInFunction::Ugt
3163 .call(
3164 vec![Value::Null, Value::Number(0.0)],
3165 heap.clone(),
3166 bindings.clone(),
3167 0,
3168 "",
3169 )
3170 .unwrap();
3171 assert_eq!(result, Value::Bool(false));
3172
3173 let result = BuiltInFunction::Ult
3175 .call(
3176 vec![Value::Null, Value::Number(5.0)],
3177 heap.clone(),
3178 bindings.clone(),
3179 0,
3180 "",
3181 )
3182 .unwrap();
3183 assert_eq!(result, Value::Bool(false));
3184
3185 let result = BuiltInFunction::Ugte
3187 .call(
3188 vec![Value::Null, Value::Number(0.0)],
3189 heap.clone(),
3190 bindings.clone(),
3191 0,
3192 "",
3193 )
3194 .unwrap();
3195 assert_eq!(result, Value::Bool(false));
3196
3197 let result = BuiltInFunction::Ulte
3199 .call(
3200 vec![Value::Null, Value::Number(0.0)],
3201 heap.clone(),
3202 bindings.clone(),
3203 0,
3204 "",
3205 )
3206 .unwrap();
3207 assert_eq!(result, Value::Bool(false));
3208
3209 let str_val = heap.borrow_mut().insert_string("hello".to_string());
3211 let result = BuiltInFunction::Ugt
3212 .call(
3213 vec![str_val, Value::Number(5.0)],
3214 heap.clone(),
3215 bindings.clone(),
3216 0,
3217 "",
3218 )
3219 .unwrap();
3220 assert_eq!(result, Value::Bool(false));
3221
3222 let result = BuiltInFunction::Ugt
3224 .call(
3225 vec![Value::Bool(true), Value::Number(5.0)],
3226 heap.clone(),
3227 bindings.clone(),
3228 0,
3229 "",
3230 )
3231 .unwrap();
3232 assert_eq!(result, Value::Bool(false));
3233
3234 let list = heap.borrow_mut().insert_list(vec![
3236 Value::Number(1.0),
3237 Value::Number(2.0),
3238 Value::Number(3.0),
3239 ]);
3240 let result = BuiltInFunction::Ugt
3241 .call(
3242 vec![list, Value::Number(5.0)],
3243 heap.clone(),
3244 bindings.clone(),
3245 0,
3246 "",
3247 )
3248 .unwrap();
3249 assert_eq!(result, Value::Bool(false));
3250 }
3251}