1 // SPDX-FileCopyrightText: 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
2 // SPDX-License-Identifier: MPL-2.0
4 * ⁌ ♓🧩 Piscēs ∷ iterable.js
6 * Copyright © 2023, 2025 Lady [@ Ladys Computer].
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
13 import { bind
, call
, identity
} from "./function.js";
15 defineOwnDataProperty
,
21 import { ITERATOR
, TO_STRING_TAG
, UNDEFINED
} from "./value.js";
25 * Returns an iterator function which iterates over the values in its
26 * provided arraylike object and yields them.
28 * If a first argument is provided, it must be a generator function.
29 * The yielded values are instead those yielded by calling that
30 * function with each value in turn.
32 * If a second argument is provided, it is used as the string tag for
33 * the resulting iterator.
35 * The resulting function also takes a second argument, which will be
36 * used as the this value when calling the provided generator
37 * function, if provided.
39 * ※ The returned function is an ordinary nonconstructible (arrow)
40 * function which, when called with an array, returns an iterator.
42 * ※ The prototypes of iterators returned by a single iterator
43 * function will all be the same.
45 arrayIteratorFunction
,
48 * Returns an iterator function which iterates over the values
49 * yielded by its provided generator function and yields them.
51 * If a first argument is provided, it must be a generator function.
52 * The yielded values are instead those yielded by calling that
53 * function with each value in turn.
55 * If a second argument is provided, it is used as the string tag for
56 * the resulting iterator.
58 * The resulting function also takes a second argument, which will be
59 * used as the this value when calling the provided generator
60 * function, if provided.
62 * ※ The returned function is an ordinary nonconstructible (arrow)
63 * function which, when called with a generator function, returns an
66 * ☡ There is no way to detect whether the function provided to the
67 * returned function is, in fact, a generator function. Consequently,
68 * if a non‐generator function is provided, it will not throw until
69 * the first attempt at reading a value.
71 * ※ The prototypes of iterators returned by a single iterator
72 * function will all be the same.
74 generatorIteratorFunction
,
77 * Returns an iterator function which iterates over the values in its
78 * provided map and yields them.
80 * If a first argument is provided, it must be a generator function.
81 * The yielded values are instead those yielded by calling that
82 * function with each value in turn.
84 * If a second argument is provided, it is used as the string tag for
85 * the resulting iterator.
87 * The resulting function also takes a second argument, which will be
88 * used as the this value when calling the provided generator
89 * function, if provided.
91 * ※ The returned function is an ordinary nonconstructible (arrow)
92 * function which, when called with a map, returns an iterator.
94 * ※ The prototypes of iterators returned by a single iterator
95 * function will all be the same.
100 * Returns an iterator function which iterates over the values in its
101 * provided set and yields them.
103 * If a first argument is provided, it must be a generator function.
104 * The yielded values are instead those yielded by calling that
105 * function with each value in turn.
107 * If a second argument is provided, it is used as the string tag for
108 * the resulting iterator.
110 * The resulting function also takes a second argument, which will be
111 * used as the this value when calling the provided generator
112 * function, if provided.
114 * ※ The returned function is an ordinary nonconstructible (arrow)
115 * function which, when called with a set, returns an iterator.
117 * ※ The prototypes of iterators returned by a single iterator
118 * function will all be the same.
123 * Returns an iterator function which iterates over the characters in
124 * its provided string and yields them.
126 * If a first argument is provided, it must be a generator function.
127 * The yielded values are instead those yielded by calling that
128 * function with each value in turn.
130 * If a second argument is provided, it is used as the string tag for
131 * the resulting iterator.
133 * The resulting function also takes a second argument, which will be
134 * used as the this value when calling the provided generator
135 * function, if provided.
137 * ※ This iterator function iterates over characters; use
138 * arrayIteratorFunction to iterate over code units.
140 * ※ The returned function is an ordinary nonconstructible (arrow)
141 * function which, when called with a string, returns an iterator.
143 * ※ The prototypes of iterators returned by a single iterator
144 * function will all be the same.
146 stringIteratorFunction
,
148 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
149 const arrayIteratorPrototype
= getPrototype(
152 const { next: arrayIteratorNext
} = arrayIteratorPrototype
;
153 const { [ITERATOR
]: stringIterator
} = String
.prototype;
154 const stringIteratorPrototype
= getPrototype(
157 const { next: stringIteratorNext
} = stringIteratorPrototype
;
158 const { [ITERATOR
]: mapIterator
} = Map
.prototype;
159 const mapIteratorPrototype
= getPrototype(
160 new Map()[ITERATOR
](),
162 const { next: mapIteratorNext
} = mapIteratorPrototype
;
163 const { [ITERATOR
]: setIterator
} = Set
.prototype;
164 const setIteratorPrototype
= getPrototype(
165 new Set()[ITERATOR
](),
167 const { next: setIteratorNext
} = setIteratorPrototype
;
168 const generatorIteratorPrototype
= getPrototype(
169 function* () {}.prototype,
171 const { next: generatorIteratorNext
} = generatorIteratorPrototype
;
172 const iteratorPrototype
= getPrototype(
173 generatorIteratorPrototype
,
177 * An iterator generated by an iterator function.
179 * This class provides the internal data structure of all the
180 * iterator functions as well as the `::next´ behaviour they all use.
182 * ※ This class extends the identity function to allow for arbitrary
183 * construction of its superclass instance based on the provided
186 * ※ This class is not exposed.
188 const Iterator
= class extends identity
{
192 #nextIterator
= null;
195 * Constructs a new iterator with the provided prototype which
196 * wraps the provided base iterator (advance·able using the
197 * provided next method) and yields the result of calling the
198 * provided generator function with each value (or just yields each
199 * value if no generator function is provided).
201 * ☡ It is not possible to type·check the provided next method or
202 * generator function to ensure that they actually are correct and
203 * appropriately callable; if they aren¦t, an error will be thrown
204 * when attempting to yield the first value.
212 super(objectCreate(prototype));
213 this.#baseIterator
= baseIterator
;
214 this.#baseIteratorNext
= baseIteratorNext
;
215 this.#generateNext
= generateNext
;
219 * Returns an object conforming to the requirements of the iterator
220 * protocol, yielding successive iterator values.
223 const baseIteratorNext
= this.#baseIteratorNext
;
224 const generateNext
= this.#generateNext
;
226 // This function some·times needs to repeat its processing
227 // steps in the case that a generator function was provided.
229 // To avoid potentially large amounts of recursive calls, it is
230 // defined in a loop which will exit the first time a suitable
231 // response is acquired.
232 const baseIterator
= this.#baseIterator
;
233 const nextIterator
= this.#nextIterator
;
234 if (baseIterator
=== null) {
235 // The base iterator has already been exhausted.
236 return { value: UNDEFINED
, done: true };
237 } else if (nextIterator
=== null) {
238 // This iterator is not currently yielding values from the
239 // provided generator function, either because it doesn¦t
240 // exist or because its values have been exhausted.
242 // Get the next value in the base iterator and either provide
243 // it to the generator function or yield it, as appropriate.
247 } = call(baseIteratorNext
, baseIterator
, []);
249 // The base iterator is exhausted.
251 // Free it up from memory and rerun these steps to signal
252 // that the iteration has completed.
253 this.#baseIterator
= null;
254 this.#generateNext
= null;
256 } else if (generateNext
!== null) {
257 // The base iterator has yielded another value and there is
258 // a generator function.
260 // Initialize a new iterator of result values by calling
261 // the function with the current value, and then rerun
262 // these steps to actually yield a value from it.
263 this.#nextIterator
= generateNext(value
);
266 // The base iterator has yielded another value and there is
267 // no generator function.
269 // Just yield the value.
270 return { value
, done: false };
273 // This iterator is currently yielding values from the
274 // provided generator function.
278 } = call(generatorIteratorNext
, nextIterator
, []);
280 // The current iterator of values from the provided
281 // generator function is exhausted.
283 // Remove it, and rerun these steps to advance to the next
284 // value in the base iterator.
285 this.#nextIterator
= null;
288 // The current iterator of values has yielded another
292 return { value
, done: false };
301 } = Iterator
.prototype;
302 const makePrototype
= (stringTag
) =>
306 next: setPropertyValues(objectCreate(null), {
309 value: function next() {
310 return call(iteratorNext
, this, []);
314 [TO_STRING_TAG
]: setPropertyValues(objectCreate(null), {
324 * Returns a new function capable of generating iterators using the
325 * provided iterator generation method, iterator advancement method,
326 * optional generator function, and optional string tag.
328 * ※ The first two arguments to this function are bound to generate
329 * the actual exported iterator function makers.
331 * ※ This function is not exposed.
333 const iteratorFunction
= (
336 iteratorFunctionName
,
338 stringTag
= "Iterator",
340 const prototype = makePrototype(stringTag
); // intentionally cached
341 return defineOwnProperty(
342 ($, thisArg
= UNDEFINED
) =>
345 call(makeBaseIterator
, $, []),
349 : bind(generateNext
, thisArg
, []),
352 defineOwnDataProperty(
355 iteratorFunctionName
,
361 arrayIteratorFunction: defineOwnProperty(
365 [arrayIterator
, arrayIteratorNext
, "values"],
368 defineOwnDataProperty(
371 "arrayIteratorFunction",
374 generatorIteratorFunction: defineOwnProperty(
382 generatorIteratorNext
,
387 defineOwnDataProperty(
390 "generatorIteratorFunction",
393 mapIteratorFunction: defineOwnProperty(
397 [mapIterator
, mapIteratorNext
, "entries"],
400 defineOwnDataProperty(
403 "mapIteratorFunction",
406 setIteratorFunction: defineOwnProperty(
410 [setIterator
, setIteratorNext
, "values"],
413 defineOwnDataProperty(
416 "setIteratorFunction",
419 stringIteratorFunction: defineOwnProperty(
423 [stringIterator
, stringIteratorNext
, "characters"],
426 defineOwnDataProperty(
429 "stringIteratorFunction",