Skip to main content

nanvm_lib/vm/
iterator_record.rs

1use core::iter::FusedIterator;
2
3use crate::{
4    common::{index_iter::IndexIter, sized_index::SizedIndex},
5    vm::{
6        Any, Array, IVm, String, ToAny, ToString,
7        string::code_unit::{is_high_surrogate, is_low_surrogate},
8    },
9};
10
11/// What [`Any::get_iterator`] returns: the values an iterable yields, one at
12/// a time, after ECMAScript's Iterator Record. The spec's record also holds
13/// a `next` method and a `[[Done]]` flag; this one is a Rust [`Iterator`]
14/// and needs neither.
15///
16/// A Rust type, never a VM value: a FunctionalScript module cannot spell an
17/// iterator, so one exists only between `get_iterator` and the container
18/// being built, and nothing can observe its state or advance it twice.
19///
20/// Each variant owns its value โ€” a reference count, not a copy โ€” and the
21/// position of the next value. Stepping cannot fail, so the items are
22/// values, not `Result`s: only `get_iterator` itself throws.
23///
24/// ```
25/// use nanvm_lib::{vm::{Any, IVm, Number, ToAny, ToArray}, naive::Naive};
26/// fn spread<A: IVm>() {
27///     let a: Any<A> = [Any::from("x"), Any::from("y")].to_array().to_any();
28///     assert_eq!(a.get_iterator().unwrap().count(), 2);
29///     let s: Any<A> = "a๐Ÿ˜€".into();
30///     assert_eq!(s.get_iterator().unwrap().count(), 2);
31///     let n: Any<A> = Number::from(1.0).to_any();
32///     assert!(n.get_iterator().is_err());
33/// }
34/// spread::<Naive>();
35/// ```
36pub enum IteratorRecord<A: IVm> {
37    /// An array's elements, in index order.
38    Array(IndexIter<u32, Array<A>>),
39    /// A string's code points, each a string of its own; `next` is a code
40    /// unit index.
41    String { string: String<A>, next: u32 },
42}
43
44impl<A: IVm> Iterator for IteratorRecord<A> {
45    type Item = Any<A>;
46
47    fn next(&mut self) -> Option<Any<A>> {
48        match self {
49            IteratorRecord::Array(elements) => elements.next(),
50            IteratorRecord::String { string, next } => {
51                let length = string.length();
52                if *next >= length {
53                    return None;
54                }
55                let first = string[*next];
56                let pair = is_high_surrogate(first)
57                    && *next + 1 < length
58                    && is_low_surrogate(string[*next + 1]);
59                let code_point: String<A> = if pair {
60                    [first, string[*next + 1]].to_string()
61                } else {
62                    [first].to_string()
63                };
64                *next += if pair { 2 } else { 1 };
65                Some(code_point.to_any())
66            }
67        }
68    }
69
70    /// Exact for an array. For a string, the code points left are at least
71    /// half its code units left, rounded up, and at most all of them. The
72    /// upper bound counts code units, so it may exceed what a builder
73    /// accepts where the code points produced do not: a builder may reject
74    /// early only on the lower bound.
75    fn size_hint(&self) -> (usize, Option<usize>) {
76        match self {
77            IteratorRecord::Array(elements) => elements.size_hint(),
78            IteratorRecord::String { string, next } => {
79                let left = string.length().saturating_sub(*next) as usize;
80                (left.div_ceil(2), Some(left))
81            }
82        }
83    }
84}
85
86impl<A: IVm> FusedIterator for IteratorRecord<A> {}
87
88#[cfg(test)]
89mod tests {
90    use crate::{
91        naive::Naive,
92        vm::{Any, String, ToAny, ToArray, ToString},
93    };
94
95    type A = Naive;
96
97    fn n(x: f64) -> Any<A> {
98        x.to_any()
99    }
100
101    fn s(x: &str) -> Any<A> {
102        x.into()
103    }
104
105    /// A string of exactly these code units, a lone surrogate included.
106    fn units(u: &[u16]) -> Any<A> {
107        let s: String<A> = u.iter().copied().to_string();
108        s.to_any()
109    }
110
111    fn array(items: &[Any<A>]) -> Any<A> {
112        items.to_vec().to_array().to_any()
113    }
114
115    /// Every value an iterator yields, after `get_iterator` of `v`.
116    fn spread(v: Any<A>) -> Vec<Any<A>> {
117        v.get_iterator().unwrap().collect()
118    }
119
120    #[test]
121    fn array_elements_in_order() {
122        let items = [n(1.0), s("b"), array(&[])];
123        assert_eq!(spread(array(&items)), items);
124        assert!(spread(array(&[])).is_empty());
125    }
126
127    /// `[...'๐Ÿ˜€']` is one element of two code units, and `[...'ab']` two.
128    #[test]
129    fn string_code_points() {
130        assert_eq!(spread(s("ab")), [s("a"), s("b")]);
131        assert_eq!(spread(s("๐Ÿ˜€")), [s("๐Ÿ˜€")]);
132        assert_eq!(spread(s("a๐Ÿ˜€b")), [s("a"), s("๐Ÿ˜€"), s("b")]);
133        assert!(spread(s("")).is_empty());
134    }
135
136    /// A lone surrogate is a code point of its own, high or low, and a high
137    /// one at the end, or before anything but a low one, has no pair.
138    #[test]
139    fn lone_surrogates() {
140        assert_eq!(
141            spread(units(&[0xDE00, 0xD83D])),
142            [units(&[0xDE00]), units(&[0xD83D])]
143        );
144        assert_eq!(spread(units(&[0xD83D, 0x61])), [units(&[0xD83D]), s("a")]);
145    }
146
147    #[test]
148    fn size_hints() {
149        let mut a = array(&[n(1.0), n(2.0)]).get_iterator().unwrap();
150        assert_eq!(a.size_hint(), (2, Some(2)));
151        a.next();
152        assert_eq!(a.size_hint(), (1, Some(1)));
153        let mut t = s("a๐Ÿ˜€").get_iterator().unwrap();
154        assert_eq!(t.size_hint(), (2, Some(3)));
155        t.next();
156        assert_eq!(t.size_hint(), (1, Some(2)));
157        t.next();
158        assert_eq!(t.size_hint(), (0, Some(0)));
159    }
160
161    /// Fused: past the end, `next` keeps answering `None`.
162    #[test]
163    fn fused() {
164        for v in [s("a"), array(&[n(1.0)])] {
165            let mut i = v.get_iterator().unwrap();
166            assert!(i.next().is_some());
167            assert!(i.next().is_none());
168            assert!(i.next().is_none());
169        }
170    }
171}