Skip to main content

bock_core/
iterator.rs

1//! Iterator protocol methods and combinators.
2//!
3//! Registers methods on `TypeTag::Iterator` for lazy iteration:
4//! `next`, `map`, `filter`, `take`, `skip`, `enumerate`, `zip`, `chain`, `collect`.
5//!
6//! Also registers `iter()` on List, Set, Map, and Range to create iterators.
7
8use bock_interp::{
9    BuiltinRegistry, IteratorKind, IteratorNext, IteratorValue, RuntimeError, TypeTag, Value,
10};
11
12/// Register all iterator-related methods.
13pub fn register(registry: &mut BuiltinRegistry) {
14    // ── Creating iterators from collections ──────────────────────────────
15    registry.register(TypeTag::List, "iter", list_iter);
16    registry.register(TypeTag::Set, "iter", set_iter);
17    registry.register(TypeTag::Map, "iter", map_iter);
18    registry.register(TypeTag::Range, "iter", range_iter);
19
20    // ── Iterator methods ─────────────────────────────────────────────────
21    registry.register(TypeTag::Iterator, "next", iter_next);
22    registry.register(TypeTag::Iterator, "map", iter_map);
23    registry.register(TypeTag::Iterator, "filter", iter_filter);
24    registry.register(TypeTag::Iterator, "take", iter_take);
25    registry.register(TypeTag::Iterator, "skip", iter_skip);
26    registry.register(TypeTag::Iterator, "enumerate", iter_enumerate);
27    registry.register(TypeTag::Iterator, "zip", iter_zip);
28    registry.register(TypeTag::Iterator, "chain", iter_chain);
29    registry.register(TypeTag::Iterator, "collect", iter_collect);
30}
31
32// ─── Helpers ──────────────────────────────────────────────────────────────────
33
34fn expect_iterator(args: &[Value], method: &str) -> Result<IteratorValue, RuntimeError> {
35    match args.first() {
36        Some(Value::Iterator(it)) => Ok(it.clone()),
37        Some(other) => Err(RuntimeError::TypeError(format!(
38            "Iterator.{method} expects Iterator, got {other}"
39        ))),
40        None => Err(RuntimeError::ArityMismatch {
41            expected: 1,
42            got: 0,
43        }),
44    }
45}
46
47fn expect_int(args: &[Value], pos: usize, method: &str) -> Result<i64, RuntimeError> {
48    match args.get(pos) {
49        Some(Value::Int(v)) => Ok(*v),
50        Some(other) => Err(RuntimeError::TypeError(format!(
51            "Iterator.{method} expects Int, got {other}"
52        ))),
53        None => Err(RuntimeError::ArityMismatch {
54            expected: pos + 1,
55            got: args.len(),
56        }),
57    }
58}
59
60fn expect_fn_value(
61    args: &[Value],
62    pos: usize,
63    method: &str,
64) -> Result<bock_interp::FnValue, RuntimeError> {
65    match args.get(pos) {
66        Some(Value::Function(f)) => Ok(f.clone()),
67        Some(other) => Err(RuntimeError::TypeError(format!(
68            "Iterator.{method} expects Function, got {other}"
69        ))),
70        None => Err(RuntimeError::ArityMismatch {
71            expected: pos + 1,
72            got: args.len(),
73        }),
74    }
75}
76
77// ─── Creating iterators ───────────────────────────────────────────────────────
78
79fn list_iter(args: &[Value]) -> Result<Value, RuntimeError> {
80    match args.first() {
81        Some(Value::List(items)) => Ok(Value::Iterator(IteratorValue::new(IteratorKind::List {
82            items: items.clone(),
83            pos: 0,
84        }))),
85        Some(other) => Err(RuntimeError::TypeError(format!(
86            "List.iter expects List, got {other}"
87        ))),
88        None => Err(RuntimeError::ArityMismatch {
89            expected: 1,
90            got: 0,
91        }),
92    }
93}
94
95fn set_iter(args: &[Value]) -> Result<Value, RuntimeError> {
96    match args.first() {
97        Some(Value::Set(set)) => {
98            let items: Vec<Value> = set.iter().cloned().collect();
99            Ok(Value::Iterator(IteratorValue::new(IteratorKind::Set {
100                items,
101                pos: 0,
102            })))
103        }
104        Some(other) => Err(RuntimeError::TypeError(format!(
105            "Set.iter expects Set, got {other}"
106        ))),
107        None => Err(RuntimeError::ArityMismatch {
108            expected: 1,
109            got: 0,
110        }),
111    }
112}
113
114fn map_iter(args: &[Value]) -> Result<Value, RuntimeError> {
115    match args.first() {
116        Some(Value::Map(map)) => {
117            let items: Vec<(Value, Value)> =
118                map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
119            Ok(Value::Iterator(IteratorValue::new(
120                IteratorKind::MapEntries { items, pos: 0 },
121            )))
122        }
123        Some(other) => Err(RuntimeError::TypeError(format!(
124            "Map.iter expects Map, got {other}"
125        ))),
126        None => Err(RuntimeError::ArityMismatch {
127            expected: 1,
128            got: 0,
129        }),
130    }
131}
132
133fn range_iter(args: &[Value]) -> Result<Value, RuntimeError> {
134    match args.first() {
135        Some(Value::Range {
136            start,
137            end,
138            inclusive,
139            step,
140        }) => Ok(Value::Iterator(IteratorValue::new(IteratorKind::Range {
141            current: *start,
142            end: *end,
143            inclusive: *inclusive,
144            step: *step,
145        }))),
146        Some(other) => Err(RuntimeError::TypeError(format!(
147            "Range.iter expects Range, got {other}"
148        ))),
149        None => Err(RuntimeError::ArityMismatch {
150            expected: 1,
151            got: 0,
152        }),
153    }
154}
155
156// ─── Iterator methods ─────────────────────────────────────────────────────────
157
158fn iter_next(args: &[Value]) -> Result<Value, RuntimeError> {
159    let it = expect_iterator(args, "next")?;
160    let mut kind = it.kind.lock().unwrap();
161    match kind.next() {
162        IteratorNext::Some(val) => Ok(Value::Optional(Some(Box::new(val)))),
163        IteratorNext::Done => Ok(Value::Optional(None)),
164        IteratorNext::NeedsMapCallback { .. } | IteratorNext::NeedsFilterCallback { .. } => {
165            Err(RuntimeError::TypeError(
166                "Iterator.next on map/filter combinator requires interpreter support \
167                 (not available from builtin dispatch)"
168                    .to_string(),
169            ))
170        }
171    }
172}
173
174fn iter_map(args: &[Value]) -> Result<Value, RuntimeError> {
175    let it = expect_iterator(args, "map")?;
176    let func = expect_fn_value(args, 1, "map")?;
177    Ok(Value::Iterator(IteratorValue::new(IteratorKind::Map {
178        source: it.kind.clone(),
179        func,
180    })))
181}
182
183fn iter_filter(args: &[Value]) -> Result<Value, RuntimeError> {
184    let it = expect_iterator(args, "filter")?;
185    let func = expect_fn_value(args, 1, "filter")?;
186    Ok(Value::Iterator(IteratorValue::new(IteratorKind::Filter {
187        source: it.kind.clone(),
188        pred: func,
189    })))
190}
191
192fn iter_take(args: &[Value]) -> Result<Value, RuntimeError> {
193    let it = expect_iterator(args, "take")?;
194    let n = expect_int(args, 1, "take")?;
195    let n = n.max(0) as usize;
196    Ok(Value::Iterator(IteratorValue::new(IteratorKind::Take {
197        source: it.kind.clone(),
198        remaining: n,
199    })))
200}
201
202fn iter_skip(args: &[Value]) -> Result<Value, RuntimeError> {
203    let it = expect_iterator(args, "skip")?;
204    let n = expect_int(args, 1, "skip")?;
205    let n = n.max(0) as usize;
206    Ok(Value::Iterator(IteratorValue::new(IteratorKind::Skip {
207        source: it.kind.clone(),
208        to_skip: n,
209        skipped: false,
210    })))
211}
212
213fn iter_enumerate(args: &[Value]) -> Result<Value, RuntimeError> {
214    let it = expect_iterator(args, "enumerate")?;
215    Ok(Value::Iterator(IteratorValue::new(
216        IteratorKind::Enumerate {
217            source: it.kind.clone(),
218            index: 0,
219        },
220    )))
221}
222
223fn iter_zip(args: &[Value]) -> Result<Value, RuntimeError> {
224    let it = expect_iterator(args, "zip")?;
225    let other = match args.get(1) {
226        Some(Value::Iterator(other)) => other.clone(),
227        Some(other) => {
228            return Err(RuntimeError::TypeError(format!(
229                "Iterator.zip expects Iterator, got {other}"
230            )))
231        }
232        None => {
233            return Err(RuntimeError::ArityMismatch {
234                expected: 2,
235                got: 1,
236            })
237        }
238    };
239    Ok(Value::Iterator(IteratorValue::new(IteratorKind::Zip {
240        a: it.kind.clone(),
241        b: other.kind.clone(),
242    })))
243}
244
245fn iter_chain(args: &[Value]) -> Result<Value, RuntimeError> {
246    let it = expect_iterator(args, "chain")?;
247    let other = match args.get(1) {
248        Some(Value::Iterator(other)) => other.clone(),
249        Some(other) => {
250            return Err(RuntimeError::TypeError(format!(
251                "Iterator.chain expects Iterator, got {other}"
252            )))
253        }
254        None => {
255            return Err(RuntimeError::ArityMismatch {
256                expected: 2,
257                got: 1,
258            })
259        }
260    };
261    Ok(Value::Iterator(IteratorValue::new(IteratorKind::Chain {
262        a: it.kind.clone(),
263        b: other.kind.clone(),
264        first_done: false,
265    })))
266}
267
268fn iter_collect(args: &[Value]) -> Result<Value, RuntimeError> {
269    let it = expect_iterator(args, "collect")?;
270    let mut result = Vec::new();
271    loop {
272        let mut kind = it.kind.lock().unwrap();
273        match kind.next() {
274            IteratorNext::Some(val) => result.push(val),
275            IteratorNext::Done => break,
276            IteratorNext::NeedsMapCallback { .. } | IteratorNext::NeedsFilterCallback { .. } => {
277                return Err(RuntimeError::TypeError(
278                    "Iterator.collect on map/filter combinator requires interpreter support \
279                     (not available from builtin dispatch)"
280                        .to_string(),
281                ));
282            }
283        }
284    }
285    Ok(Value::List(result))
286}
287
288// ─── Tests ────────────────────────────────────────────────────────────────────
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293    use std::collections::{BTreeMap, BTreeSet};
294
295    fn reg() -> BuiltinRegistry {
296        let mut r = BuiltinRegistry::new();
297        register(&mut r);
298        r
299    }
300
301    fn list(vals: &[i64]) -> Value {
302        Value::List(vals.iter().map(|&v| Value::Int(v)).collect())
303    }
304
305    fn range(start: i64, end: i64, inclusive: bool) -> Value {
306        Value::Range {
307            start,
308            end,
309            inclusive,
310            step: 1,
311        }
312    }
313
314    // ── iter() creation ───────────────────────────────────────────────────
315
316    #[test]
317    fn list_iter_creates_iterator() {
318        let r = reg();
319        let result = r.call(TypeTag::List, "iter", &[list(&[1, 2, 3])]);
320        assert!(matches!(result.unwrap().unwrap(), Value::Iterator(_)));
321    }
322
323    #[test]
324    fn range_iter_creates_iterator() {
325        let r = reg();
326        let result = r.call(TypeTag::Range, "iter", &[range(0, 3, false)]);
327        assert!(matches!(result.unwrap().unwrap(), Value::Iterator(_)));
328    }
329
330    #[test]
331    fn set_iter_creates_iterator() {
332        let r = reg();
333        let mut set = BTreeSet::new();
334        set.insert(Value::Int(1));
335        set.insert(Value::Int(2));
336        let result = r.call(TypeTag::Set, "iter", &[Value::Set(set)]);
337        assert!(matches!(result.unwrap().unwrap(), Value::Iterator(_)));
338    }
339
340    #[test]
341    fn map_iter_creates_iterator() {
342        let r = reg();
343        let mut map = BTreeMap::new();
344        map.insert(Value::Int(1), Value::Int(10));
345        let result = r.call(TypeTag::Map, "iter", &[Value::Map(map)]);
346        assert!(matches!(result.unwrap().unwrap(), Value::Iterator(_)));
347    }
348
349    // ── next() ────────────────────────────────────────────────────────────
350
351    #[test]
352    fn next_returns_elements_then_none() {
353        let r = reg();
354        let iter_val = r
355            .call(TypeTag::List, "iter", &[list(&[10, 20])])
356            .unwrap()
357            .unwrap();
358
359        let first = r
360            .call(TypeTag::Iterator, "next", &[iter_val.clone()])
361            .unwrap()
362            .unwrap();
363        assert_eq!(first, Value::Optional(Some(Box::new(Value::Int(10)))));
364
365        let second = r
366            .call(TypeTag::Iterator, "next", &[iter_val.clone()])
367            .unwrap()
368            .unwrap();
369        assert_eq!(second, Value::Optional(Some(Box::new(Value::Int(20)))));
370
371        let third = r
372            .call(TypeTag::Iterator, "next", &[iter_val])
373            .unwrap()
374            .unwrap();
375        assert_eq!(third, Value::Optional(None));
376    }
377
378    #[test]
379    fn range_iter_produces_values() {
380        let r = reg();
381        let iter_val = r
382            .call(TypeTag::Range, "iter", &[range(1, 4, false)])
383            .unwrap()
384            .unwrap();
385
386        let mut results = Vec::new();
387        loop {
388            let v = r
389                .call(TypeTag::Iterator, "next", &[iter_val.clone()])
390                .unwrap()
391                .unwrap();
392            match v {
393                Value::Optional(Some(val)) => results.push(*val),
394                Value::Optional(None) => break,
395                _ => panic!("unexpected"),
396            }
397        }
398        assert_eq!(results, vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
399    }
400
401    #[test]
402    fn range_inclusive_iter() {
403        let r = reg();
404        let iter_val = r
405            .call(TypeTag::Range, "iter", &[range(1, 3, true)])
406            .unwrap()
407            .unwrap();
408
409        let mut results = Vec::new();
410        loop {
411            let v = r
412                .call(TypeTag::Iterator, "next", &[iter_val.clone()])
413                .unwrap()
414                .unwrap();
415            match v {
416                Value::Optional(Some(val)) => results.push(*val),
417                Value::Optional(None) => break,
418                _ => panic!("unexpected"),
419            }
420        }
421        assert_eq!(results, vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
422    }
423
424    // ── collect() ─────────────────────────────────────────────────────────
425
426    #[test]
427    fn collect_drains_into_list() {
428        let r = reg();
429        let iter_val = r
430            .call(TypeTag::List, "iter", &[list(&[1, 2, 3])])
431            .unwrap()
432            .unwrap();
433        let collected = r
434            .call(TypeTag::Iterator, "collect", &[iter_val])
435            .unwrap()
436            .unwrap();
437        assert_eq!(collected, list(&[1, 2, 3]));
438    }
439
440    #[test]
441    fn collect_range_iter() {
442        let r = reg();
443        let iter_val = r
444            .call(TypeTag::Range, "iter", &[range(0, 3, false)])
445            .unwrap()
446            .unwrap();
447        let collected = r
448            .call(TypeTag::Iterator, "collect", &[iter_val])
449            .unwrap()
450            .unwrap();
451        assert_eq!(collected, list(&[0, 1, 2]));
452    }
453
454    // ── take() ────────────────────────────────────────────────────────────
455
456    #[test]
457    fn take_limits_elements() {
458        let r = reg();
459        let iter_val = r
460            .call(TypeTag::List, "iter", &[list(&[1, 2, 3, 4, 5])])
461            .unwrap()
462            .unwrap();
463        let taken = r
464            .call(TypeTag::Iterator, "take", &[iter_val, Value::Int(3)])
465            .unwrap()
466            .unwrap();
467        let collected = r
468            .call(TypeTag::Iterator, "collect", &[taken])
469            .unwrap()
470            .unwrap();
471        assert_eq!(collected, list(&[1, 2, 3]));
472    }
473
474    #[test]
475    fn take_zero() {
476        let r = reg();
477        let iter_val = r
478            .call(TypeTag::List, "iter", &[list(&[1, 2])])
479            .unwrap()
480            .unwrap();
481        let taken = r
482            .call(TypeTag::Iterator, "take", &[iter_val, Value::Int(0)])
483            .unwrap()
484            .unwrap();
485        let collected = r
486            .call(TypeTag::Iterator, "collect", &[taken])
487            .unwrap()
488            .unwrap();
489        assert_eq!(collected, list(&[]));
490    }
491
492    // ── skip() ────────────────────────────────────────────────────────────
493
494    #[test]
495    fn skip_elements() {
496        let r = reg();
497        let iter_val = r
498            .call(TypeTag::List, "iter", &[list(&[1, 2, 3, 4, 5])])
499            .unwrap()
500            .unwrap();
501        let skipped = r
502            .call(TypeTag::Iterator, "skip", &[iter_val, Value::Int(2)])
503            .unwrap()
504            .unwrap();
505        let collected = r
506            .call(TypeTag::Iterator, "collect", &[skipped])
507            .unwrap()
508            .unwrap();
509        assert_eq!(collected, list(&[3, 4, 5]));
510    }
511
512    #[test]
513    fn skip_all() {
514        let r = reg();
515        let iter_val = r
516            .call(TypeTag::List, "iter", &[list(&[1, 2])])
517            .unwrap()
518            .unwrap();
519        let skipped = r
520            .call(TypeTag::Iterator, "skip", &[iter_val, Value::Int(10)])
521            .unwrap()
522            .unwrap();
523        let collected = r
524            .call(TypeTag::Iterator, "collect", &[skipped])
525            .unwrap()
526            .unwrap();
527        assert_eq!(collected, list(&[]));
528    }
529
530    // ── enumerate() ───────────────────────────────────────────────────────
531
532    #[test]
533    fn enumerate_adds_index() {
534        let r = reg();
535        let iter_val = r
536            .call(TypeTag::List, "iter", &[list(&[10, 20, 30])])
537            .unwrap()
538            .unwrap();
539        let enumerated = r
540            .call(TypeTag::Iterator, "enumerate", &[iter_val])
541            .unwrap()
542            .unwrap();
543        let collected = r
544            .call(TypeTag::Iterator, "collect", &[enumerated])
545            .unwrap()
546            .unwrap();
547        let expected = Value::List(vec![
548            Value::Tuple(vec![Value::Int(0), Value::Int(10)]),
549            Value::Tuple(vec![Value::Int(1), Value::Int(20)]),
550            Value::Tuple(vec![Value::Int(2), Value::Int(30)]),
551        ]);
552        assert_eq!(collected, expected);
553    }
554
555    // ── zip() ─────────────────────────────────────────────────────────────
556
557    #[test]
558    fn zip_two_iterators() {
559        let r = reg();
560        let iter_a = r
561            .call(TypeTag::List, "iter", &[list(&[1, 2, 3])])
562            .unwrap()
563            .unwrap();
564        let iter_b = r
565            .call(TypeTag::List, "iter", &[list(&[10, 20])])
566            .unwrap()
567            .unwrap();
568        let zipped = r
569            .call(TypeTag::Iterator, "zip", &[iter_a, iter_b])
570            .unwrap()
571            .unwrap();
572        let collected = r
573            .call(TypeTag::Iterator, "collect", &[zipped])
574            .unwrap()
575            .unwrap();
576        let expected = Value::List(vec![
577            Value::Tuple(vec![Value::Int(1), Value::Int(10)]),
578            Value::Tuple(vec![Value::Int(2), Value::Int(20)]),
579        ]);
580        assert_eq!(collected, expected);
581    }
582
583    // ── chain() ───────────────────────────────────────────────────────────
584
585    #[test]
586    fn chain_two_iterators() {
587        let r = reg();
588        let iter_a = r
589            .call(TypeTag::List, "iter", &[list(&[1, 2])])
590            .unwrap()
591            .unwrap();
592        let iter_b = r
593            .call(TypeTag::List, "iter", &[list(&[3, 4])])
594            .unwrap()
595            .unwrap();
596        let chained = r
597            .call(TypeTag::Iterator, "chain", &[iter_a, iter_b])
598            .unwrap()
599            .unwrap();
600        let collected = r
601            .call(TypeTag::Iterator, "collect", &[chained])
602            .unwrap()
603            .unwrap();
604        assert_eq!(collected, list(&[1, 2, 3, 4]));
605    }
606
607    // ── Lazy evaluation verification ──────────────────────────────────────
608
609    #[test]
610    fn take_is_lazy_does_not_consume_all() {
611        // Create iterator, take 2, then collect. The remaining elements
612        // in the source should still be accessible.
613        let it = IteratorValue::new(IteratorKind::List {
614            items: vec![Value::Int(1), Value::Int(2), Value::Int(3), Value::Int(4)],
615            pos: 0,
616        });
617        let take_kind = IteratorKind::Take {
618            source: it.kind.clone(),
619            remaining: 2,
620        };
621        let take_it = IteratorValue::new(take_kind);
622
623        // Collect the take iterator
624        let r = reg();
625        let collected = r
626            .call(TypeTag::Iterator, "collect", &[Value::Iterator(take_it)])
627            .unwrap()
628            .unwrap();
629        assert_eq!(collected, list(&[1, 2]));
630
631        // The source iterator should be at position 2, not fully consumed
632        let remaining = r
633            .call(TypeTag::Iterator, "next", &[Value::Iterator(it)])
634            .unwrap()
635            .unwrap();
636        assert_eq!(remaining, Value::Optional(Some(Box::new(Value::Int(3)))));
637    }
638
639    // ── Combinator chaining ───────────────────────────────────────────────
640
641    #[test]
642    fn skip_then_take() {
643        let r = reg();
644        let iter_val = r
645            .call(TypeTag::List, "iter", &[list(&[1, 2, 3, 4, 5])])
646            .unwrap()
647            .unwrap();
648        let skipped = r
649            .call(TypeTag::Iterator, "skip", &[iter_val, Value::Int(1)])
650            .unwrap()
651            .unwrap();
652        let taken = r
653            .call(TypeTag::Iterator, "take", &[skipped, Value::Int(2)])
654            .unwrap()
655            .unwrap();
656        let collected = r
657            .call(TypeTag::Iterator, "collect", &[taken])
658            .unwrap()
659            .unwrap();
660        assert_eq!(collected, list(&[2, 3]));
661    }
662
663    #[test]
664    fn enumerate_then_take() {
665        let r = reg();
666        let iter_val = r
667            .call(TypeTag::List, "iter", &[list(&[10, 20, 30])])
668            .unwrap()
669            .unwrap();
670        let enumerated = r
671            .call(TypeTag::Iterator, "enumerate", &[iter_val])
672            .unwrap()
673            .unwrap();
674        let taken = r
675            .call(TypeTag::Iterator, "take", &[enumerated, Value::Int(2)])
676            .unwrap()
677            .unwrap();
678        let collected = r
679            .call(TypeTag::Iterator, "collect", &[taken])
680            .unwrap()
681            .unwrap();
682        let expected = Value::List(vec![
683            Value::Tuple(vec![Value::Int(0), Value::Int(10)]),
684            Value::Tuple(vec![Value::Int(1), Value::Int(20)]),
685        ]);
686        assert_eq!(collected, expected);
687    }
688
689    // ── map/filter create lazy iterators (need interpreter for next) ─────
690
691    #[test]
692    fn map_creates_lazy_iterator() {
693        let r = reg();
694        let iter_val = r
695            .call(TypeTag::List, "iter", &[list(&[1, 2])])
696            .unwrap()
697            .unwrap();
698        let func = Value::Function(bock_interp::FnValue::new_named("double"));
699        let mapped = r
700            .call(TypeTag::Iterator, "map", &[iter_val, func])
701            .unwrap()
702            .unwrap();
703        // The map iterator is created lazily
704        assert!(matches!(mapped, Value::Iterator(_)));
705    }
706
707    #[test]
708    fn filter_creates_lazy_iterator() {
709        let r = reg();
710        let iter_val = r
711            .call(TypeTag::List, "iter", &[list(&[1, 2])])
712            .unwrap()
713            .unwrap();
714        let pred = Value::Function(bock_interp::FnValue::new_named("is_even"));
715        let filtered = r
716            .call(TypeTag::Iterator, "filter", &[iter_val, pred])
717            .unwrap()
718            .unwrap();
719        assert!(matches!(filtered, Value::Iterator(_)));
720    }
721
722    // ── Map entries iteration ─────────────────────────────────────────────
723
724    #[test]
725    fn map_entries_as_tuples() {
726        let r = reg();
727        let mut map = BTreeMap::new();
728        map.insert(Value::Int(1), Value::Int(10));
729        map.insert(Value::Int(2), Value::Int(20));
730        let iter_val = r
731            .call(TypeTag::Map, "iter", &[Value::Map(map)])
732            .unwrap()
733            .unwrap();
734        let collected = r
735            .call(TypeTag::Iterator, "collect", &[iter_val])
736            .unwrap()
737            .unwrap();
738        let expected = Value::List(vec![
739            Value::Tuple(vec![Value::Int(1), Value::Int(10)]),
740            Value::Tuple(vec![Value::Int(2), Value::Int(20)]),
741        ]);
742        assert_eq!(collected, expected);
743    }
744
745    // ── Set iteration ─────────────────────────────────────────────────────
746
747    #[test]
748    fn set_iter_collects_sorted() {
749        let r = reg();
750        let mut set = BTreeSet::new();
751        set.insert(Value::Int(3));
752        set.insert(Value::Int(1));
753        set.insert(Value::Int(2));
754        let iter_val = r
755            .call(TypeTag::Set, "iter", &[Value::Set(set)])
756            .unwrap()
757            .unwrap();
758        let collected = r
759            .call(TypeTag::Iterator, "collect", &[iter_val])
760            .unwrap()
761            .unwrap();
762        assert_eq!(collected, list(&[1, 2, 3]));
763    }
764
765    // ── Error cases ───────────────────────────────────────────────────────
766
767    #[test]
768    fn map_requires_function() {
769        let r = reg();
770        let iter_val = r
771            .call(TypeTag::List, "iter", &[list(&[1])])
772            .unwrap()
773            .unwrap();
774        let result = r.call(TypeTag::Iterator, "map", &[iter_val, Value::Int(0)]);
775        assert!(matches!(result.unwrap(), Err(RuntimeError::TypeError(_))));
776    }
777
778    #[test]
779    fn filter_requires_function() {
780        let r = reg();
781        let iter_val = r
782            .call(TypeTag::List, "iter", &[list(&[1])])
783            .unwrap()
784            .unwrap();
785        let result = r.call(TypeTag::Iterator, "filter", &[iter_val, Value::Int(0)]);
786        assert!(matches!(result.unwrap(), Err(RuntimeError::TypeError(_))));
787    }
788
789    #[test]
790    fn zip_requires_iterator() {
791        let r = reg();
792        let iter_val = r
793            .call(TypeTag::List, "iter", &[list(&[1])])
794            .unwrap()
795            .unwrap();
796        let result = r.call(TypeTag::Iterator, "zip", &[iter_val, Value::Int(0)]);
797        assert!(matches!(result.unwrap(), Err(RuntimeError::TypeError(_))));
798    }
799
800    #[test]
801    fn chain_requires_iterator() {
802        let r = reg();
803        let iter_val = r
804            .call(TypeTag::List, "iter", &[list(&[1])])
805            .unwrap()
806            .unwrap();
807        let result = r.call(TypeTag::Iterator, "chain", &[iter_val, Value::Int(0)]);
808        assert!(matches!(result.unwrap(), Err(RuntimeError::TypeError(_))));
809    }
810}