1 // ♓🌟 Piscēs ∷ iterable.js
2 // ====================================================================
4 // Copyright © 2023 Lady [@ Lady’s Computer].
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
10 import { bind
, call
, identity
} from "./function.js";
12 defineOwnDataProperty
,
18 import { ITERATOR
, TO_STRING_TAG
, UNDEFINED
} from "./value.js";
22 * Returns an iterator function which iterates over the values in its
23 * provided arraylike object and yields them.
25 * If a first argument is provided, it must be a generator function.
26 * The yielded values are instead those yielded by calling that
27 * function with each value in turn.
29 * If a second argument is provided, it is used as the string tag for
30 * the resulting iterator.
32 * The resulting function also takes a second argument, which will be
33 * used as the `this` value when calling the provided generator
34 * function, if provided.
36 * ※ The returned function is an ordinary nonconstructible (arrow)
37 * function which, when called with an array, returns an iterator.
39 * ※ The prototypes of iterators returned by a single iterator
40 * function will all be the same.
42 arrayIteratorFunction
,
45 * Returns an iterator function which iterates over the values
46 * yielded by its provided generator function and yields them.
48 * If a first argument is provided, it must be a generator function.
49 * The yielded values are instead those yielded by calling that
50 * function with each value in turn.
52 * If a second argument is provided, it is used as the string tag for
53 * the resulting iterator.
55 * The resulting function also takes a second argument, which will be
56 * used as the `this` value when calling the provided generator
57 * function, if provided.
59 * ※ The returned function is an ordinary nonconstructible (arrow)
60 * function which, when called with a generator function, returns an
63 * ☡ There is no way to detect whether the function provided to the
64 * returned function is, in fact, a generator function. Consequently,
65 * if a non‐generator function is provided, it will not throw until
66 * the first attempt at reading a value.
68 * ※ The prototypes of iterators returned by a single iterator
69 * function will all be the same.
71 generatorIteratorFunction
,
74 * Returns an iterator function which iterates over the values in its
75 * provided map and yields them.
77 * If a first argument is provided, it must be a generator function.
78 * The yielded values are instead those yielded by calling that
79 * function with each value in turn.
81 * If a second argument is provided, it is used as the string tag for
82 * the resulting iterator.
84 * The resulting function also takes a second argument, which will be
85 * used as the `this` value when calling the provided generator
86 * function, if provided.
88 * ※ The returned function is an ordinary nonconstructible (arrow)
89 * function which, when called with a map, returns an iterator.
91 * ※ The prototypes of iterators returned by a single iterator
92 * function will all be the same.
97 * Returns an iterator function which iterates over the values in its
98 * provided set and yields them.
100 * If a first argument is provided, it must be a generator function.
101 * The yielded values are instead those yielded by calling that
102 * function with each value in turn.
104 * If a second argument is provided, it is used as the string tag for
105 * the resulting iterator.
107 * The resulting function also takes a second argument, which will be
108 * used as the `this` value when calling the provided generator
109 * function, if provided.
111 * ※ The returned function is an ordinary nonconstructible (arrow)
112 * function which, when called with a set, returns an iterator.
114 * ※ The prototypes of iterators returned by a single iterator
115 * function will all be the same.
120 * Returns an iterator function which iterates over the characters in
121 * its provided string and yields them.
123 * If a first argument is provided, it must be a generator function.
124 * The yielded values are instead those yielded by calling that
125 * function with each value in turn.
127 * If a second argument is provided, it is used as the string tag for
128 * the resulting iterator.
130 * The resulting function also takes a second argument, which will be
131 * used as the `this` value when calling the provided generator
132 * function, if provided.
134 * ※ This iterator function iterates over characters; use
135 * `arrayIteratorFunction` to iterate over code units.
137 * ※ The returned function is an ordinary nonconstructible (arrow)
138 * function which, when called with a string, returns an iterator.
140 * ※ The prototypes of iterators returned by a single iterator
141 * function will all be the same.
143 stringIteratorFunction
,
145 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
146 const arrayIteratorPrototype
= getPrototype(
149 const { next
: arrayIteratorNext
} = arrayIteratorPrototype
;
150 const { [ITERATOR
]: stringIterator
} = String
.prototype;
151 const stringIteratorPrototype
= getPrototype(
154 const { next
: stringIteratorNext
} = stringIteratorPrototype
;
155 const { [ITERATOR
]: mapIterator
} = Map
.prototype;
156 const mapIteratorPrototype
= getPrototype(
157 new Map()[ITERATOR
](),
159 const { next
: mapIteratorNext
} = mapIteratorPrototype
;
160 const { [ITERATOR
]: setIterator
} = Set
.prototype;
161 const setIteratorPrototype
= getPrototype(
162 new Set()[ITERATOR
](),
164 const { next
: setIteratorNext
} = setIteratorPrototype
;
165 const generatorIteratorPrototype
= getPrototype(
166 function* () {}.prototype,
168 const { next
: generatorIteratorNext
} = generatorIteratorPrototype
;
169 const iteratorPrototype
= getPrototype(
170 generatorIteratorPrototype
,
174 * An iterator generated by an iterator function.
176 * This class provides the internal data structure of all the
177 * iterator functions as well as the `::next` behaviour they all use.
179 * ※ This class extends the identity function to allow for arbitrary
180 * construction of its superclass instance based on the provided
183 * ※ This class is not exposed.
185 const Iterator
= class extends identity
{
189 #nextIterator
= null;
192 * Constructs a new iterator with the provided prototype which
193 * wraps the provided base iterator (advance·able using the
194 * provided next method) and yields the result of calling the
195 * provided generator function with each value (or just yields each
196 * value if no generator function is provided).
198 * ☡ It is not possible to type·check the provided next method or
199 * generator function to ensure that they actually are correct and
200 * appropriately callable; if they aren’t, an error will be thrown
201 * when attempting to yield the first value.
209 super(objectCreate(prototype));
210 this.#baseIterator
= baseIterator
;
211 this.#baseIteratorNext
= baseIteratorNext
;
212 this.#generateNext
= generateNext
;
216 * Returns an object conforming to the requirements of the iterator
217 * protocol, yielding successive iterator values.
220 const baseIteratorNext
= this.#baseIteratorNext
;
221 const generateNext
= this.#generateNext
;
223 // This function sometimes needs to repeat its processing steps
224 // in the case that a generator function was provided.
226 // To avoid potentially large amounts of recursive calls, it is
227 // defined in a loop which will exit the first time a suitable
228 // response is acquired.
229 const baseIterator
= this.#baseIterator
;
230 const nextIterator
= this.#nextIterator
;
231 if (baseIterator
=== null) {
232 // The base iterator has already been exhausted.
233 return { value
: UNDEFINED
, done
: true };
234 } else if (nextIterator
=== null) {
235 // This iterator is not currently yielding values from the
236 // provided generator function, either because it doesn’t
237 // exist or because its values have been exhausted.
239 // Get the next value in the base iterator and either provide
240 // it to the generator function or yield it, as appropriate.
244 } = call(baseIteratorNext
, baseIterator
, []);
246 // The base iterator is exhausted.
248 // Free it up from memory and rerun these steps to signal
249 // that the iteration has completed.
250 this.#baseIterator
= null;
251 this.#generateNext
= null;
253 } else if (generateNext
!== null) {
254 // The base iterator has yielded another value and there is
255 // a generator function.
257 // Initialize a new iterator of result values by calling
258 // the function with the current value, and then rerun
259 // these steps to actually yield a value from it.
260 this.#nextIterator
= generateNext(value
);
263 // The base iterator has yielded another value and there is
264 // no generator function.
266 // Just yield the value.
267 return { value
, done
: false };
270 // This iterator is currently yielding values from the
271 // provided generator function.
275 } = call(generatorIteratorNext
, nextIterator
, []);
277 // The current iterator of values from the provided
278 // generator function is exhausted.
280 // Remove it, and rerun these steps to advance to the next
281 // value in the base iterator.
282 this.#nextIterator
= null;
285 // The current iterator of values has yielded another
286 // value; reyield it.
287 return { value
, done
: false };
296 } = Iterator
.prototype;
297 const makePrototype
= (stringTag
) =>
301 next
: setPropertyValues(objectCreate(null), {
304 value
: function next() {
305 return call(iteratorNext
, this, []);
309 [TO_STRING_TAG
]: setPropertyValues(objectCreate(null), {
319 * Returns a new function capable of generating iterators using the
320 * provided iterator generation method, iterator advancement method,
321 * optional generator function, and optional string tag.
323 * ※ The first two arguments to this function are bound to generate
324 * the actual exported iterator function makers.
326 * ※ This function is not exposed.
328 const iteratorFunction
= (
332 stringTag
= "Iterator",
334 const prototype = makePrototype(stringTag
); // intentionally cached
335 return ($, thisArg
= UNDEFINED
) =>
338 call(makeBaseIterator
, $, []),
340 generateNext
== null ? null : bind(generateNext
, thisArg
, []),
345 arrayIteratorFunction
: defineOwnProperty(
349 [arrayIterator
, arrayIteratorNext
],
352 defineOwnDataProperty(
355 "arrayIteratorFunction",
358 generatorIteratorFunction
: defineOwnProperty(
366 generatorIteratorNext
,
370 defineOwnDataProperty(
373 "generatorIteratorFunction",
376 mapIteratorFunction
: defineOwnProperty(
380 [mapIterator
, mapIteratorNext
],
383 defineOwnDataProperty(
386 "mapIteratorFunction",
389 setIteratorFunction
: defineOwnProperty(
393 [setIterator
, setIteratorNext
],
396 defineOwnDataProperty(
399 "setIteratorFunction",
402 stringIteratorFunction
: defineOwnProperty(
406 [stringIterator
, stringIteratorNext
],
409 defineOwnDataProperty(
412 "stringIteratorFunction",