1use std::collections::{BTreeMap, BinaryHeap, HashMap, HashSet, VecDeque};
12
13use super::CallArgs;
14use crate::interpreter::value::*;
15use crate::interpreter::BuiltinContext;
16use crate::interpreter::Interpreter;
17use minir_macros::interpreter_builtin;
18
19#[derive(Debug, Clone)]
23pub enum CollectionObject {
24 HashMap(HashMap<String, RValue>),
26 BTreeMap(BTreeMap<String, RValue>),
28 HashSet(HashSet<String>),
30 BinaryHeap(BinaryHeap<OrdF64>),
32 VecDeque(VecDeque<RValue>),
34}
35
36#[derive(Debug, Clone, Copy, PartialEq)]
41pub struct OrdF64(pub f64);
42
43impl Eq for OrdF64 {}
44
45impl PartialOrd for OrdF64 {
46 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
47 Some(self.cmp(other))
48 }
49}
50
51impl Ord for OrdF64 {
52 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
53 self.0.total_cmp(&other.0)
54 }
55}
56
57impl Interpreter {
62 pub(crate) fn add_collection(&self, obj: CollectionObject) -> usize {
64 let mut collections = self.collections.borrow_mut();
65 let id = collections.len();
66 collections.push(obj);
67 id
68 }
69}
70
71fn collection_value(id: usize, class: &str) -> RValue {
77 let mut rv = RVector::from(Vector::Integer(
78 vec![Some(i64::try_from(id).unwrap_or(0))].into(),
79 ));
80 rv.set_attr(
81 "class".to_string(),
82 RValue::vec(Vector::Character(vec![Some(class.to_string())].into())),
83 );
84 RValue::Vector(rv)
85}
86
87fn collection_id(val: &RValue) -> Result<usize, RError> {
89 val.as_vector()
90 .and_then(|v| v.as_integer_scalar())
91 .and_then(|i| usize::try_from(i).ok())
92 .ok_or_else(|| {
93 RError::new(
94 RErrorKind::Argument,
95 "invalid collection handle — expected an integer ID returned by a collection constructor".to_string(),
96 )
97 })
98}
99
100fn require_string(args: &CallArgs, name: &str, pos: usize) -> Result<String, RError> {
102 args.string(name, pos)
103}
104
105#[interpreter_builtin(
116 name = "hashmap",
117 min_args = 0,
118 max_args = 0,
119 namespace = "collections"
120)]
121fn interp_hashmap(
122 _args: &[RValue],
123 _named: &[(String, RValue)],
124 context: &BuiltinContext,
125) -> Result<RValue, RError> {
126 let interp = context.interpreter();
127 let id = interp.add_collection(CollectionObject::HashMap(HashMap::new()));
128 Ok(collection_value(id, "hashmap"))
129}
130
131#[interpreter_builtin(
138 name = "hashmap_set",
139 min_args = 3,
140 max_args = 3,
141 namespace = "collections"
142)]
143fn interp_hashmap_set(
144 args: &[RValue],
145 named: &[(String, RValue)],
146 context: &BuiltinContext,
147) -> Result<RValue, RError> {
148 let call_args = CallArgs::new(args, named);
149 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
150 let key = require_string(&call_args, "key", 1)?;
151 let value = call_args.value("value", 2).cloned().unwrap_or(RValue::Null);
152
153 let interp = context.interpreter();
154 let mut collections = interp.collections.borrow_mut();
155 match collections.get_mut(id) {
156 Some(CollectionObject::HashMap(map)) => {
157 let old = map.insert(key, value);
158 Ok(old.unwrap_or(RValue::Null))
159 }
160 _ => Err(RError::new(
161 RErrorKind::Argument,
162 format!("collection {id} is not a hashmap"),
163 )),
164 }
165}
166
167#[interpreter_builtin(
174 name = "hashmap_get",
175 min_args = 2,
176 max_args = 3,
177 namespace = "collections"
178)]
179fn interp_hashmap_get(
180 args: &[RValue],
181 named: &[(String, RValue)],
182 context: &BuiltinContext,
183) -> Result<RValue, RError> {
184 let call_args = CallArgs::new(args, named);
185 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
186 let key = require_string(&call_args, "key", 1)?;
187 let default = call_args
188 .value("default", 2)
189 .cloned()
190 .unwrap_or(RValue::Null);
191
192 let interp = context.interpreter();
193 let collections = interp.collections.borrow();
194 match collections.get(id) {
195 Some(CollectionObject::HashMap(map)) => Ok(map.get(&key).cloned().unwrap_or(default)),
196 _ => Err(RError::new(
197 RErrorKind::Argument,
198 format!("collection {id} is not a hashmap"),
199 )),
200 }
201}
202
203#[interpreter_builtin(
209 name = "hashmap_has",
210 min_args = 2,
211 max_args = 2,
212 namespace = "collections"
213)]
214fn interp_hashmap_has(
215 args: &[RValue],
216 named: &[(String, RValue)],
217 context: &BuiltinContext,
218) -> Result<RValue, RError> {
219 let call_args = CallArgs::new(args, named);
220 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
221 let key = require_string(&call_args, "key", 1)?;
222
223 let interp = context.interpreter();
224 let collections = interp.collections.borrow();
225 match collections.get(id) {
226 Some(CollectionObject::HashMap(map)) => Ok(RValue::vec(Vector::Logical(
227 vec![Some(map.contains_key(&key))].into(),
228 ))),
229 _ => Err(RError::new(
230 RErrorKind::Argument,
231 format!("collection {id} is not a hashmap"),
232 )),
233 }
234}
235
236#[interpreter_builtin(
242 name = "hashmap_remove",
243 min_args = 2,
244 max_args = 2,
245 namespace = "collections"
246)]
247fn interp_hashmap_remove(
248 args: &[RValue],
249 named: &[(String, RValue)],
250 context: &BuiltinContext,
251) -> Result<RValue, RError> {
252 let call_args = CallArgs::new(args, named);
253 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
254 let key = require_string(&call_args, "key", 1)?;
255
256 let interp = context.interpreter();
257 let mut collections = interp.collections.borrow_mut();
258 match collections.get_mut(id) {
259 Some(CollectionObject::HashMap(map)) => Ok(map.remove(&key).unwrap_or(RValue::Null)),
260 _ => Err(RError::new(
261 RErrorKind::Argument,
262 format!("collection {id} is not a hashmap"),
263 )),
264 }
265}
266
267#[interpreter_builtin(
274 name = "hashmap_keys",
275 min_args = 1,
276 max_args = 1,
277 namespace = "collections"
278)]
279fn interp_hashmap_keys(
280 args: &[RValue],
281 named: &[(String, RValue)],
282 context: &BuiltinContext,
283) -> Result<RValue, RError> {
284 let call_args = CallArgs::new(args, named);
285 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
286
287 let interp = context.interpreter();
288 let collections = interp.collections.borrow();
289 match collections.get(id) {
290 Some(CollectionObject::HashMap(map)) => {
291 let keys: Vec<Option<String>> = map.keys().map(|k| Some(k.clone())).collect();
292 Ok(RValue::vec(Vector::Character(keys.into())))
293 }
294 _ => Err(RError::new(
295 RErrorKind::Argument,
296 format!("collection {id} is not a hashmap"),
297 )),
298 }
299}
300
301#[interpreter_builtin(
308 name = "hashmap_values",
309 min_args = 1,
310 max_args = 1,
311 namespace = "collections"
312)]
313fn interp_hashmap_values(
314 args: &[RValue],
315 named: &[(String, RValue)],
316 context: &BuiltinContext,
317) -> Result<RValue, RError> {
318 let call_args = CallArgs::new(args, named);
319 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
320
321 let interp = context.interpreter();
322 let collections = interp.collections.borrow();
323 match collections.get(id) {
324 Some(CollectionObject::HashMap(map)) => {
325 let values: Vec<(Option<String>, RValue)> =
326 map.values().map(|v| (None, v.clone())).collect();
327 Ok(RValue::List(RList::new(values)))
328 }
329 _ => Err(RError::new(
330 RErrorKind::Argument,
331 format!("collection {id} is not a hashmap"),
332 )),
333 }
334}
335
336#[interpreter_builtin(
341 name = "hashmap_size",
342 min_args = 1,
343 max_args = 1,
344 namespace = "collections"
345)]
346fn interp_hashmap_size(
347 args: &[RValue],
348 named: &[(String, RValue)],
349 context: &BuiltinContext,
350) -> Result<RValue, RError> {
351 let call_args = CallArgs::new(args, named);
352 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
353
354 let interp = context.interpreter();
355 let collections = interp.collections.borrow();
356 match collections.get(id) {
357 Some(CollectionObject::HashMap(map)) => Ok(RValue::vec(Vector::Integer(
358 vec![Some(i64::try_from(map.len()).unwrap_or(i64::MAX))].into(),
359 ))),
360 _ => Err(RError::new(
361 RErrorKind::Argument,
362 format!("collection {id} is not a hashmap"),
363 )),
364 }
365}
366
367#[interpreter_builtin(
374 name = "hashmap_to_list",
375 min_args = 1,
376 max_args = 1,
377 namespace = "collections"
378)]
379fn interp_hashmap_to_list(
380 args: &[RValue],
381 named: &[(String, RValue)],
382 context: &BuiltinContext,
383) -> Result<RValue, RError> {
384 let call_args = CallArgs::new(args, named);
385 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
386
387 let interp = context.interpreter();
388 let collections = interp.collections.borrow();
389 match collections.get(id) {
390 Some(CollectionObject::HashMap(map)) => {
391 let entries: Vec<(Option<String>, RValue)> = map
392 .iter()
393 .map(|(k, v)| (Some(k.clone()), v.clone()))
394 .collect();
395 Ok(RValue::List(RList::new(entries)))
396 }
397 _ => Err(RError::new(
398 RErrorKind::Argument,
399 format!("collection {id} is not a hashmap"),
400 )),
401 }
402}
403
404#[interpreter_builtin(
415 name = "btreemap",
416 min_args = 0,
417 max_args = 0,
418 namespace = "collections"
419)]
420fn interp_btreemap(
421 _args: &[RValue],
422 _named: &[(String, RValue)],
423 context: &BuiltinContext,
424) -> Result<RValue, RError> {
425 let interp = context.interpreter();
426 let id = interp.add_collection(CollectionObject::BTreeMap(BTreeMap::new()));
427 Ok(collection_value(id, "btreemap"))
428}
429
430#[interpreter_builtin(
437 name = "btreemap_set",
438 min_args = 3,
439 max_args = 3,
440 namespace = "collections"
441)]
442fn interp_btreemap_set(
443 args: &[RValue],
444 named: &[(String, RValue)],
445 context: &BuiltinContext,
446) -> Result<RValue, RError> {
447 let call_args = CallArgs::new(args, named);
448 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
449 let key = require_string(&call_args, "key", 1)?;
450 let value = call_args.value("value", 2).cloned().unwrap_or(RValue::Null);
451
452 let interp = context.interpreter();
453 let mut collections = interp.collections.borrow_mut();
454 match collections.get_mut(id) {
455 Some(CollectionObject::BTreeMap(map)) => {
456 let old = map.insert(key, value);
457 Ok(old.unwrap_or(RValue::Null))
458 }
459 _ => Err(RError::new(
460 RErrorKind::Argument,
461 format!("collection {id} is not a btreemap"),
462 )),
463 }
464}
465
466#[interpreter_builtin(
473 name = "btreemap_get",
474 min_args = 2,
475 max_args = 3,
476 namespace = "collections"
477)]
478fn interp_btreemap_get(
479 args: &[RValue],
480 named: &[(String, RValue)],
481 context: &BuiltinContext,
482) -> Result<RValue, RError> {
483 let call_args = CallArgs::new(args, named);
484 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
485 let key = require_string(&call_args, "key", 1)?;
486 let default = call_args
487 .value("default", 2)
488 .cloned()
489 .unwrap_or(RValue::Null);
490
491 let interp = context.interpreter();
492 let collections = interp.collections.borrow();
493 match collections.get(id) {
494 Some(CollectionObject::BTreeMap(map)) => Ok(map.get(&key).cloned().unwrap_or(default)),
495 _ => Err(RError::new(
496 RErrorKind::Argument,
497 format!("collection {id} is not a btreemap"),
498 )),
499 }
500}
501
502#[interpreter_builtin(
508 name = "btreemap_has",
509 min_args = 2,
510 max_args = 2,
511 namespace = "collections"
512)]
513fn interp_btreemap_has(
514 args: &[RValue],
515 named: &[(String, RValue)],
516 context: &BuiltinContext,
517) -> Result<RValue, RError> {
518 let call_args = CallArgs::new(args, named);
519 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
520 let key = require_string(&call_args, "key", 1)?;
521
522 let interp = context.interpreter();
523 let collections = interp.collections.borrow();
524 match collections.get(id) {
525 Some(CollectionObject::BTreeMap(map)) => Ok(RValue::vec(Vector::Logical(
526 vec![Some(map.contains_key(&key))].into(),
527 ))),
528 _ => Err(RError::new(
529 RErrorKind::Argument,
530 format!("collection {id} is not a btreemap"),
531 )),
532 }
533}
534
535#[interpreter_builtin(
541 name = "btreemap_remove",
542 min_args = 2,
543 max_args = 2,
544 namespace = "collections"
545)]
546fn interp_btreemap_remove(
547 args: &[RValue],
548 named: &[(String, RValue)],
549 context: &BuiltinContext,
550) -> Result<RValue, RError> {
551 let call_args = CallArgs::new(args, named);
552 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
553 let key = require_string(&call_args, "key", 1)?;
554
555 let interp = context.interpreter();
556 let mut collections = interp.collections.borrow_mut();
557 match collections.get_mut(id) {
558 Some(CollectionObject::BTreeMap(map)) => Ok(map.remove(&key).unwrap_or(RValue::Null)),
559 _ => Err(RError::new(
560 RErrorKind::Argument,
561 format!("collection {id} is not a btreemap"),
562 )),
563 }
564}
565
566#[interpreter_builtin(
571 name = "btreemap_keys",
572 min_args = 1,
573 max_args = 1,
574 namespace = "collections"
575)]
576fn interp_btreemap_keys(
577 args: &[RValue],
578 named: &[(String, RValue)],
579 context: &BuiltinContext,
580) -> Result<RValue, RError> {
581 let call_args = CallArgs::new(args, named);
582 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
583
584 let interp = context.interpreter();
585 let collections = interp.collections.borrow();
586 match collections.get(id) {
587 Some(CollectionObject::BTreeMap(map)) => {
588 let keys: Vec<Option<String>> = map.keys().map(|k| Some(k.clone())).collect();
589 Ok(RValue::vec(Vector::Character(keys.into())))
590 }
591 _ => Err(RError::new(
592 RErrorKind::Argument,
593 format!("collection {id} is not a btreemap"),
594 )),
595 }
596}
597
598#[interpreter_builtin(
603 name = "btreemap_values",
604 min_args = 1,
605 max_args = 1,
606 namespace = "collections"
607)]
608fn interp_btreemap_values(
609 args: &[RValue],
610 named: &[(String, RValue)],
611 context: &BuiltinContext,
612) -> Result<RValue, RError> {
613 let call_args = CallArgs::new(args, named);
614 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
615
616 let interp = context.interpreter();
617 let collections = interp.collections.borrow();
618 match collections.get(id) {
619 Some(CollectionObject::BTreeMap(map)) => {
620 let values: Vec<(Option<String>, RValue)> =
621 map.values().map(|v| (None, v.clone())).collect();
622 Ok(RValue::List(RList::new(values)))
623 }
624 _ => Err(RError::new(
625 RErrorKind::Argument,
626 format!("collection {id} is not a btreemap"),
627 )),
628 }
629}
630
631#[interpreter_builtin(
636 name = "btreemap_size",
637 min_args = 1,
638 max_args = 1,
639 namespace = "collections"
640)]
641fn interp_btreemap_size(
642 args: &[RValue],
643 named: &[(String, RValue)],
644 context: &BuiltinContext,
645) -> Result<RValue, RError> {
646 let call_args = CallArgs::new(args, named);
647 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
648
649 let interp = context.interpreter();
650 let collections = interp.collections.borrow();
651 match collections.get(id) {
652 Some(CollectionObject::BTreeMap(map)) => Ok(RValue::vec(Vector::Integer(
653 vec![Some(i64::try_from(map.len()).unwrap_or(i64::MAX))].into(),
654 ))),
655 _ => Err(RError::new(
656 RErrorKind::Argument,
657 format!("collection {id} is not a btreemap"),
658 )),
659 }
660}
661
662#[interpreter_builtin(
667 name = "btreemap_to_list",
668 min_args = 1,
669 max_args = 1,
670 namespace = "collections"
671)]
672fn interp_btreemap_to_list(
673 args: &[RValue],
674 named: &[(String, RValue)],
675 context: &BuiltinContext,
676) -> Result<RValue, RError> {
677 let call_args = CallArgs::new(args, named);
678 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
679
680 let interp = context.interpreter();
681 let collections = interp.collections.borrow();
682 match collections.get(id) {
683 Some(CollectionObject::BTreeMap(map)) => {
684 let entries: Vec<(Option<String>, RValue)> = map
685 .iter()
686 .map(|(k, v)| (Some(k.clone()), v.clone()))
687 .collect();
688 Ok(RValue::List(RList::new(entries)))
689 }
690 _ => Err(RError::new(
691 RErrorKind::Argument,
692 format!("collection {id} is not a btreemap"),
693 )),
694 }
695}
696
697#[interpreter_builtin(
708 name = "hashset",
709 min_args = 0,
710 max_args = 0,
711 namespace = "collections"
712)]
713fn interp_hashset(
714 _args: &[RValue],
715 _named: &[(String, RValue)],
716 context: &BuiltinContext,
717) -> Result<RValue, RError> {
718 let interp = context.interpreter();
719 let id = interp.add_collection(CollectionObject::HashSet(HashSet::new()));
720 Ok(collection_value(id, "hashset"))
721}
722
723#[interpreter_builtin(
729 name = "hashset_add",
730 min_args = 2,
731 max_args = 2,
732 namespace = "collections"
733)]
734fn interp_hashset_add(
735 args: &[RValue],
736 named: &[(String, RValue)],
737 context: &BuiltinContext,
738) -> Result<RValue, RError> {
739 let call_args = CallArgs::new(args, named);
740 let id = collection_id(call_args.value("s", 0).unwrap_or(&RValue::Null))?;
741 let value = require_string(&call_args, "value", 1)?;
742
743 let interp = context.interpreter();
744 let mut collections = interp.collections.borrow_mut();
745 match collections.get_mut(id) {
746 Some(CollectionObject::HashSet(set)) => {
747 let was_new = set.insert(value);
748 Ok(RValue::vec(Vector::Logical(vec![Some(was_new)].into())))
749 }
750 _ => Err(RError::new(
751 RErrorKind::Argument,
752 format!("collection {id} is not a hashset"),
753 )),
754 }
755}
756
757#[interpreter_builtin(
763 name = "hashset_has",
764 min_args = 2,
765 max_args = 2,
766 namespace = "collections"
767)]
768fn interp_hashset_has(
769 args: &[RValue],
770 named: &[(String, RValue)],
771 context: &BuiltinContext,
772) -> Result<RValue, RError> {
773 let call_args = CallArgs::new(args, named);
774 let id = collection_id(call_args.value("s", 0).unwrap_or(&RValue::Null))?;
775 let value = require_string(&call_args, "value", 1)?;
776
777 let interp = context.interpreter();
778 let collections = interp.collections.borrow();
779 match collections.get(id) {
780 Some(CollectionObject::HashSet(set)) => Ok(RValue::vec(Vector::Logical(
781 vec![Some(set.contains(&value))].into(),
782 ))),
783 _ => Err(RError::new(
784 RErrorKind::Argument,
785 format!("collection {id} is not a hashset"),
786 )),
787 }
788}
789
790#[interpreter_builtin(
796 name = "hashset_remove",
797 min_args = 2,
798 max_args = 2,
799 namespace = "collections"
800)]
801fn interp_hashset_remove(
802 args: &[RValue],
803 named: &[(String, RValue)],
804 context: &BuiltinContext,
805) -> Result<RValue, RError> {
806 let call_args = CallArgs::new(args, named);
807 let id = collection_id(call_args.value("s", 0).unwrap_or(&RValue::Null))?;
808 let value = require_string(&call_args, "value", 1)?;
809
810 let interp = context.interpreter();
811 let mut collections = interp.collections.borrow_mut();
812 match collections.get_mut(id) {
813 Some(CollectionObject::HashSet(set)) => {
814 let was_present = set.remove(&value);
815 Ok(RValue::vec(Vector::Logical(vec![Some(was_present)].into())))
816 }
817 _ => Err(RError::new(
818 RErrorKind::Argument,
819 format!("collection {id} is not a hashset"),
820 )),
821 }
822}
823
824#[interpreter_builtin(
829 name = "hashset_size",
830 min_args = 1,
831 max_args = 1,
832 namespace = "collections"
833)]
834fn interp_hashset_size(
835 args: &[RValue],
836 named: &[(String, RValue)],
837 context: &BuiltinContext,
838) -> Result<RValue, RError> {
839 let call_args = CallArgs::new(args, named);
840 let id = collection_id(call_args.value("s", 0).unwrap_or(&RValue::Null))?;
841
842 let interp = context.interpreter();
843 let collections = interp.collections.borrow();
844 match collections.get(id) {
845 Some(CollectionObject::HashSet(set)) => Ok(RValue::vec(Vector::Integer(
846 vec![Some(i64::try_from(set.len()).unwrap_or(i64::MAX))].into(),
847 ))),
848 _ => Err(RError::new(
849 RErrorKind::Argument,
850 format!("collection {id} is not a hashset"),
851 )),
852 }
853}
854
855#[interpreter_builtin(
862 name = "hashset_to_vector",
863 min_args = 1,
864 max_args = 1,
865 namespace = "collections"
866)]
867fn interp_hashset_to_vector(
868 args: &[RValue],
869 named: &[(String, RValue)],
870 context: &BuiltinContext,
871) -> Result<RValue, RError> {
872 let call_args = CallArgs::new(args, named);
873 let id = collection_id(call_args.value("s", 0).unwrap_or(&RValue::Null))?;
874
875 let interp = context.interpreter();
876 let collections = interp.collections.borrow();
877 match collections.get(id) {
878 Some(CollectionObject::HashSet(set)) => {
879 let elems: Vec<Option<String>> = set.iter().map(|e| Some(e.clone())).collect();
880 Ok(RValue::vec(Vector::Character(elems.into())))
881 }
882 _ => Err(RError::new(
883 RErrorKind::Argument,
884 format!("collection {id} is not a hashset"),
885 )),
886 }
887}
888
889#[interpreter_builtin(
895 name = "hashset_union",
896 min_args = 2,
897 max_args = 2,
898 namespace = "collections"
899)]
900fn interp_hashset_union(
901 args: &[RValue],
902 named: &[(String, RValue)],
903 context: &BuiltinContext,
904) -> Result<RValue, RError> {
905 let call_args = CallArgs::new(args, named);
906 let id1 = collection_id(call_args.value("s1", 0).unwrap_or(&RValue::Null))?;
907 let id2 = collection_id(call_args.value("s2", 1).unwrap_or(&RValue::Null))?;
908
909 let interp = context.interpreter();
910 let collections = interp.collections.borrow();
911 let set1 = match collections.get(id1) {
912 Some(CollectionObject::HashSet(s)) => s,
913 _ => {
914 return Err(RError::new(
915 RErrorKind::Argument,
916 format!("collection {id1} is not a hashset"),
917 ))
918 }
919 };
920 let set2 = match collections.get(id2) {
921 Some(CollectionObject::HashSet(s)) => s,
922 _ => {
923 return Err(RError::new(
924 RErrorKind::Argument,
925 format!("collection {id2} is not a hashset"),
926 ))
927 }
928 };
929 let result: HashSet<String> = set1.union(set2).cloned().collect();
930 drop(collections);
931 let id = interp.add_collection(CollectionObject::HashSet(result));
932 Ok(collection_value(id, "hashset"))
933}
934
935#[interpreter_builtin(
941 name = "hashset_intersect",
942 min_args = 2,
943 max_args = 2,
944 namespace = "collections"
945)]
946fn interp_hashset_intersect(
947 args: &[RValue],
948 named: &[(String, RValue)],
949 context: &BuiltinContext,
950) -> Result<RValue, RError> {
951 let call_args = CallArgs::new(args, named);
952 let id1 = collection_id(call_args.value("s1", 0).unwrap_or(&RValue::Null))?;
953 let id2 = collection_id(call_args.value("s2", 1).unwrap_or(&RValue::Null))?;
954
955 let interp = context.interpreter();
956 let collections = interp.collections.borrow();
957 let set1 = match collections.get(id1) {
958 Some(CollectionObject::HashSet(s)) => s,
959 _ => {
960 return Err(RError::new(
961 RErrorKind::Argument,
962 format!("collection {id1} is not a hashset"),
963 ))
964 }
965 };
966 let set2 = match collections.get(id2) {
967 Some(CollectionObject::HashSet(s)) => s,
968 _ => {
969 return Err(RError::new(
970 RErrorKind::Argument,
971 format!("collection {id2} is not a hashset"),
972 ))
973 }
974 };
975 let result: HashSet<String> = set1.intersection(set2).cloned().collect();
976 drop(collections);
977 let id = interp.add_collection(CollectionObject::HashSet(result));
978 Ok(collection_value(id, "hashset"))
979}
980
981#[interpreter_builtin(
987 name = "hashset_diff",
988 min_args = 2,
989 max_args = 2,
990 namespace = "collections"
991)]
992fn interp_hashset_diff(
993 args: &[RValue],
994 named: &[(String, RValue)],
995 context: &BuiltinContext,
996) -> Result<RValue, RError> {
997 let call_args = CallArgs::new(args, named);
998 let id1 = collection_id(call_args.value("s1", 0).unwrap_or(&RValue::Null))?;
999 let id2 = collection_id(call_args.value("s2", 1).unwrap_or(&RValue::Null))?;
1000
1001 let interp = context.interpreter();
1002 let collections = interp.collections.borrow();
1003 let set1 = match collections.get(id1) {
1004 Some(CollectionObject::HashSet(s)) => s,
1005 _ => {
1006 return Err(RError::new(
1007 RErrorKind::Argument,
1008 format!("collection {id1} is not a hashset"),
1009 ))
1010 }
1011 };
1012 let set2 = match collections.get(id2) {
1013 Some(CollectionObject::HashSet(s)) => s,
1014 _ => {
1015 return Err(RError::new(
1016 RErrorKind::Argument,
1017 format!("collection {id2} is not a hashset"),
1018 ))
1019 }
1020 };
1021 let result: HashSet<String> = set1.difference(set2).cloned().collect();
1022 drop(collections);
1023 let id = interp.add_collection(CollectionObject::HashSet(result));
1024 Ok(collection_value(id, "hashset"))
1025}
1026
1027#[interpreter_builtin(name = "heap", min_args = 0, max_args = 0, namespace = "collections")]
1038fn interp_heap(
1039 _args: &[RValue],
1040 _named: &[(String, RValue)],
1041 context: &BuiltinContext,
1042) -> Result<RValue, RError> {
1043 let interp = context.interpreter();
1044 let id = interp.add_collection(CollectionObject::BinaryHeap(BinaryHeap::new()));
1045 Ok(collection_value(id, "heap"))
1046}
1047
1048#[interpreter_builtin(
1054 name = "heap_push",
1055 min_args = 2,
1056 max_args = 2,
1057 namespace = "collections"
1058)]
1059fn interp_heap_push(
1060 args: &[RValue],
1061 named: &[(String, RValue)],
1062 context: &BuiltinContext,
1063) -> Result<RValue, RError> {
1064 let call_args = CallArgs::new(args, named);
1065 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
1066 let val = call_args
1067 .value("value", 1)
1068 .and_then(|v| v.as_vector())
1069 .and_then(|v| v.as_double_scalar())
1070 .ok_or_else(|| {
1071 RError::new(
1072 RErrorKind::Argument,
1073 "heap_push() requires a numeric scalar value".to_string(),
1074 )
1075 })?;
1076
1077 let interp = context.interpreter();
1078 let mut collections = interp.collections.borrow_mut();
1079 match collections.get_mut(id) {
1080 Some(CollectionObject::BinaryHeap(heap)) => {
1081 heap.push(OrdF64(val));
1082 Ok(RValue::Null)
1083 }
1084 _ => Err(RError::new(
1085 RErrorKind::Argument,
1086 format!("collection {id} is not a heap"),
1087 )),
1088 }
1089}
1090
1091#[interpreter_builtin(
1098 name = "heap_pop",
1099 min_args = 1,
1100 max_args = 1,
1101 namespace = "collections"
1102)]
1103fn interp_heap_pop(
1104 args: &[RValue],
1105 named: &[(String, RValue)],
1106 context: &BuiltinContext,
1107) -> Result<RValue, RError> {
1108 let call_args = CallArgs::new(args, named);
1109 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
1110
1111 let interp = context.interpreter();
1112 let mut collections = interp.collections.borrow_mut();
1113 match collections.get_mut(id) {
1114 Some(CollectionObject::BinaryHeap(heap)) => match heap.pop() {
1115 Some(OrdF64(val)) => Ok(RValue::vec(Vector::Double(vec![Some(val)].into()))),
1116 None => Ok(RValue::Null),
1117 },
1118 _ => Err(RError::new(
1119 RErrorKind::Argument,
1120 format!("collection {id} is not a heap"),
1121 )),
1122 }
1123}
1124
1125#[interpreter_builtin(
1132 name = "heap_peek",
1133 min_args = 1,
1134 max_args = 1,
1135 namespace = "collections"
1136)]
1137fn interp_heap_peek(
1138 args: &[RValue],
1139 named: &[(String, RValue)],
1140 context: &BuiltinContext,
1141) -> Result<RValue, RError> {
1142 let call_args = CallArgs::new(args, named);
1143 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
1144
1145 let interp = context.interpreter();
1146 let collections = interp.collections.borrow();
1147 match collections.get(id) {
1148 Some(CollectionObject::BinaryHeap(heap)) => match heap.peek() {
1149 Some(OrdF64(val)) => Ok(RValue::vec(Vector::Double(vec![Some(*val)].into()))),
1150 None => Ok(RValue::Null),
1151 },
1152 _ => Err(RError::new(
1153 RErrorKind::Argument,
1154 format!("collection {id} is not a heap"),
1155 )),
1156 }
1157}
1158
1159#[interpreter_builtin(
1164 name = "heap_size",
1165 min_args = 1,
1166 max_args = 1,
1167 namespace = "collections"
1168)]
1169fn interp_heap_size(
1170 args: &[RValue],
1171 named: &[(String, RValue)],
1172 context: &BuiltinContext,
1173) -> Result<RValue, RError> {
1174 let call_args = CallArgs::new(args, named);
1175 let id = collection_id(call_args.value("h", 0).unwrap_or(&RValue::Null))?;
1176
1177 let interp = context.interpreter();
1178 let collections = interp.collections.borrow();
1179 match collections.get(id) {
1180 Some(CollectionObject::BinaryHeap(heap)) => Ok(RValue::vec(Vector::Integer(
1181 vec![Some(i64::try_from(heap.len()).unwrap_or(i64::MAX))].into(),
1182 ))),
1183 _ => Err(RError::new(
1184 RErrorKind::Argument,
1185 format!("collection {id} is not a heap"),
1186 )),
1187 }
1188}
1189
1190#[interpreter_builtin(name = "deque", min_args = 0, max_args = 0, namespace = "collections")]
1201fn interp_deque(
1202 _args: &[RValue],
1203 _named: &[(String, RValue)],
1204 context: &BuiltinContext,
1205) -> Result<RValue, RError> {
1206 let interp = context.interpreter();
1207 let id = interp.add_collection(CollectionObject::VecDeque(VecDeque::new()));
1208 Ok(collection_value(id, "deque"))
1209}
1210
1211#[interpreter_builtin(
1217 name = "deque_push_back",
1218 min_args = 2,
1219 max_args = 2,
1220 namespace = "collections"
1221)]
1222fn interp_deque_push_back(
1223 args: &[RValue],
1224 named: &[(String, RValue)],
1225 context: &BuiltinContext,
1226) -> Result<RValue, RError> {
1227 let call_args = CallArgs::new(args, named);
1228 let id = collection_id(call_args.value("d", 0).unwrap_or(&RValue::Null))?;
1229 let value = call_args.value("value", 1).cloned().unwrap_or(RValue::Null);
1230
1231 let interp = context.interpreter();
1232 let mut collections = interp.collections.borrow_mut();
1233 match collections.get_mut(id) {
1234 Some(CollectionObject::VecDeque(deque)) => {
1235 deque.push_back(value);
1236 Ok(RValue::Null)
1237 }
1238 _ => Err(RError::new(
1239 RErrorKind::Argument,
1240 format!("collection {id} is not a deque"),
1241 )),
1242 }
1243}
1244
1245#[interpreter_builtin(
1251 name = "deque_push_front",
1252 min_args = 2,
1253 max_args = 2,
1254 namespace = "collections"
1255)]
1256fn interp_deque_push_front(
1257 args: &[RValue],
1258 named: &[(String, RValue)],
1259 context: &BuiltinContext,
1260) -> Result<RValue, RError> {
1261 let call_args = CallArgs::new(args, named);
1262 let id = collection_id(call_args.value("d", 0).unwrap_or(&RValue::Null))?;
1263 let value = call_args.value("value", 1).cloned().unwrap_or(RValue::Null);
1264
1265 let interp = context.interpreter();
1266 let mut collections = interp.collections.borrow_mut();
1267 match collections.get_mut(id) {
1268 Some(CollectionObject::VecDeque(deque)) => {
1269 deque.push_front(value);
1270 Ok(RValue::Null)
1271 }
1272 _ => Err(RError::new(
1273 RErrorKind::Argument,
1274 format!("collection {id} is not a deque"),
1275 )),
1276 }
1277}
1278
1279#[interpreter_builtin(
1286 name = "deque_pop_back",
1287 min_args = 1,
1288 max_args = 1,
1289 namespace = "collections"
1290)]
1291fn interp_deque_pop_back(
1292 args: &[RValue],
1293 named: &[(String, RValue)],
1294 context: &BuiltinContext,
1295) -> Result<RValue, RError> {
1296 let call_args = CallArgs::new(args, named);
1297 let id = collection_id(call_args.value("d", 0).unwrap_or(&RValue::Null))?;
1298
1299 let interp = context.interpreter();
1300 let mut collections = interp.collections.borrow_mut();
1301 match collections.get_mut(id) {
1302 Some(CollectionObject::VecDeque(deque)) => Ok(deque.pop_back().unwrap_or(RValue::Null)),
1303 _ => Err(RError::new(
1304 RErrorKind::Argument,
1305 format!("collection {id} is not a deque"),
1306 )),
1307 }
1308}
1309
1310#[interpreter_builtin(
1317 name = "deque_pop_front",
1318 min_args = 1,
1319 max_args = 1,
1320 namespace = "collections"
1321)]
1322fn interp_deque_pop_front(
1323 args: &[RValue],
1324 named: &[(String, RValue)],
1325 context: &BuiltinContext,
1326) -> Result<RValue, RError> {
1327 let call_args = CallArgs::new(args, named);
1328 let id = collection_id(call_args.value("d", 0).unwrap_or(&RValue::Null))?;
1329
1330 let interp = context.interpreter();
1331 let mut collections = interp.collections.borrow_mut();
1332 match collections.get_mut(id) {
1333 Some(CollectionObject::VecDeque(deque)) => Ok(deque.pop_front().unwrap_or(RValue::Null)),
1334 _ => Err(RError::new(
1335 RErrorKind::Argument,
1336 format!("collection {id} is not a deque"),
1337 )),
1338 }
1339}
1340
1341#[interpreter_builtin(
1346 name = "deque_size",
1347 min_args = 1,
1348 max_args = 1,
1349 namespace = "collections"
1350)]
1351fn interp_deque_size(
1352 args: &[RValue],
1353 named: &[(String, RValue)],
1354 context: &BuiltinContext,
1355) -> Result<RValue, RError> {
1356 let call_args = CallArgs::new(args, named);
1357 let id = collection_id(call_args.value("d", 0).unwrap_or(&RValue::Null))?;
1358
1359 let interp = context.interpreter();
1360 let collections = interp.collections.borrow();
1361 match collections.get(id) {
1362 Some(CollectionObject::VecDeque(deque)) => Ok(RValue::vec(Vector::Integer(
1363 vec![Some(i64::try_from(deque.len()).unwrap_or(i64::MAX))].into(),
1364 ))),
1365 _ => Err(RError::new(
1366 RErrorKind::Argument,
1367 format!("collection {id} is not a deque"),
1368 )),
1369 }
1370}
1371
1372#[interpreter_builtin(
1377 name = "deque_to_list",
1378 min_args = 1,
1379 max_args = 1,
1380 namespace = "collections"
1381)]
1382fn interp_deque_to_list(
1383 args: &[RValue],
1384 named: &[(String, RValue)],
1385 context: &BuiltinContext,
1386) -> Result<RValue, RError> {
1387 let call_args = CallArgs::new(args, named);
1388 let id = collection_id(call_args.value("d", 0).unwrap_or(&RValue::Null))?;
1389
1390 let interp = context.interpreter();
1391 let collections = interp.collections.borrow();
1392 match collections.get(id) {
1393 Some(CollectionObject::VecDeque(deque)) => {
1394 let values: Vec<(Option<String>, RValue)> =
1395 deque.iter().map(|v| (None, v.clone())).collect();
1396 Ok(RValue::List(RList::new(values)))
1397 }
1398 _ => Err(RError::new(
1399 RErrorKind::Argument,
1400 format!("collection {id} is not a deque"),
1401 )),
1402 }
1403}
1404
1405