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";
16 import { ITERATOR
, TO_STRING_TAG
} from "./value.js";
20 * Returns an iterator function which iterates over the values in its
21 * provided arraylike object and yields them.
23 * If a first argument is provided, it must be a generator function.
24 * The yielded values are instead those yielded by calling that
25 * function with each value in turn.
27 * If a second argument is provided, it is used as the string tag for
28 * the resulting iterator.
30 * The resulting function also takes a second argument, which will be
31 * used as the `this` value when calling the provided generator
32 * function, if provided.
34 * ※ The returned function is an ordinary nonconstructible (arrow)
35 * function which, when called with an array, returns an iterator.
37 * ※ The prototypes of iterators returned by a single iterator
38 * function will all be the same.
40 arrayIteratorFunction
,
43 * Returns an iterator function which iterates over the values
44 * yielded by its provided generator function and yields them.
46 * If a first argument is provided, it must be a generator function.
47 * The yielded values are instead those yielded by calling that
48 * function with each value in turn.
50 * If a second argument is provided, it is used as the string tag for
51 * the resulting iterator.
53 * The resulting function also takes a second argument, which will be
54 * used as the `this` value when calling the provided generator
55 * function, if provided.
57 * ※ The returned function is an ordinary nonconstructible (arrow)
58 * function which, when called with a generator function, returns an
61 * ☡ There is no way to detect whether the function provided to the
62 * returned function is, in fact, a generator function. Consequently,
63 * if a non‐generator function is provided, it will not throw until
64 * the first attempt at reading a value.
66 * ※ The prototypes of iterators returned by a single iterator
67 * function will all be the same.
69 generatorIteratorFunction
,
72 * Returns an iterator function which iterates over the values in its
73 * provided map and yields them.
75 * If a first argument is provided, it must be a generator function.
76 * The yielded values are instead those yielded by calling that
77 * function with each value in turn.
79 * If a second argument is provided, it is used as the string tag for
80 * the resulting iterator.
82 * The resulting function also takes a second argument, which will be
83 * used as the `this` value when calling the provided generator
84 * function, if provided.
86 * ※ The returned function is an ordinary nonconstructible (arrow)
87 * function which, when called with a map, returns an iterator.
89 * ※ The prototypes of iterators returned by a single iterator
90 * function will all be the same.
95 * Returns an iterator function which iterates over the values in its
96 * provided set and yields them.
98 * If a first argument is provided, it must be a generator function.
99 * The yielded values are instead those yielded by calling that
100 * function with each value in turn.
102 * If a second argument is provided, it is used as the string tag for
103 * the resulting iterator.
105 * The resulting function also takes a second argument, which will be
106 * used as the `this` value when calling the provided generator
107 * function, if provided.
109 * ※ The returned function is an ordinary nonconstructible (arrow)
110 * function which, when called with a set, returns an iterator.
112 * ※ The prototypes of iterators returned by a single iterator
113 * function will all be the same.
118 * Returns an iterator function which iterates over the characters in
119 * its provided string and yields them.
121 * If a first argument is provided, it must be a generator function.
122 * The yielded values are instead those yielded by calling that
123 * function with each value in turn.
125 * If a second argument is provided, it is used as the string tag for
126 * the resulting iterator.
128 * The resulting function also takes a second argument, which will be
129 * used as the `this` value when calling the provided generator
130 * function, if provided.
132 * ※ This iterator function iterates over characters; use
133 * `arrayIteratorFunction` to iterate over code units.
135 * ※ The returned function is an ordinary nonconstructible (arrow)
136 * function which, when called with a string, returns an iterator.
138 * ※ The prototypes of iterators returned by a single iterator
139 * function will all be the same.
141 stringIteratorFunction
,
143 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
144 const arrayIteratorPrototype
= getPrototype(
147 const { next
: arrayIteratorNext
} = arrayIteratorPrototype
;
148 const { [ITERATOR
]: stringIterator
} = String
.prototype;
149 const stringIteratorPrototype
= getPrototype(
152 const { next
: stringIteratorNext
} = stringIteratorPrototype
;
153 const { [ITERATOR
]: mapIterator
} = Map
.prototype;
154 const mapIteratorPrototype
= getPrototype(
155 new Map()[ITERATOR
](),
157 const { next
: mapIteratorNext
} = mapIteratorPrototype
;
158 const { [ITERATOR
]: setIterator
} = Set
.prototype;
159 const setIteratorPrototype
= getPrototype(
160 new Set()[ITERATOR
](),
162 const { next
: setIteratorNext
} = setIteratorPrototype
;
163 const generatorIteratorPrototype
= getPrototype(
164 function* () {}.prototype,
166 const { next
: generatorIteratorNext
} = generatorIteratorPrototype
;
167 const iteratorPrototype
= getPrototype(
168 generatorIteratorPrototype
,
172 * An iterator generated by an iterator function.
174 * This class provides the internal data structure of all the
175 * iterator functions as well as the `::next` behaviour they all use.
177 * ※ This class extends the identity function to allow for arbitrary
178 * construction of its superclass instance based on the provided
181 * ※ This class is not exposed.
183 const Iterator
= class extends identity
{
187 #nextIterator
= null;
190 * Constructs a new iterator with the provided prototype which
191 * wraps the provided base iterator (advance·able using the
192 * provided next method) and yields the result of calling the
193 * provided generator function with each value (or just yields each
194 * value if no generator function is provided).
196 * ☡ It is not possible to type·check the provided next method or
197 * generator function to ensure that they actually are correct and
198 * appropriately callable; if they aren’t, an error will be thrown
199 * when attempting to yield the first value.
207 super(objectCreate(prototype));
208 this.#baseIterator
= baseIterator
;
209 this.#baseIteratorNext
= baseIteratorNext
;
210 this.#generateNext
= generateNext
;
214 * Returns an object conforming to the requirements of the iterator
215 * protocol, yielding successive iterator values.
218 const baseIteratorNext
= this.#baseIteratorNext
;
219 const generateNext
= this.#generateNext
;
221 // This function sometimes needs to repeat its processing steps
222 // in the case that a generator function was provided.
224 // To avoid potentially large amounts of recursive calls, it is
225 // defined in a loop which will exit the first time a suitable
226 // response is acquired.
227 const baseIterator
= this.#baseIterator
;
228 const nextIterator
= this.#nextIterator
;
229 if (baseIterator
=== null) {
230 // The base iterator has already been exhausted.
231 return { value
: undefined, done
: true };
232 } else if (nextIterator
=== null) {
233 // This iterator is not currently yielding values from the
234 // provided generator function, either because it doesn’t
235 // exist or because its values have been exhausted.
237 // Get the next value in the base iterator and either provide
238 // it to the generator function or yield it, as appropriate.
242 } = call(baseIteratorNext
, baseIterator
, []);
244 // The base iterator is exhausted.
246 // Free it up from memory and rerun these steps to signal
247 // that the iteration has completed.
248 this.#baseIterator
= null;
249 this.#generateNext
= null;
251 } else if (generateNext
!== null) {
252 // The base iterator has yielded another value and there is
253 // a generator function.
255 // Initialize a new iterator of result values by calling
256 // the function with the current value, and then rerun
257 // these steps to actually yield a value from it.
258 this.#nextIterator
= generateNext(value
);
261 // The base iterator has yielded another value and there is
262 // no generator function.
264 // Just yield the value.
265 return { value
, done
: false };
268 // This iterator is currently yielding values from the
269 // provided generator function.
273 } = call(generatorIteratorNext
, nextIterator
, []);
275 // The current iterator of values from the provided
276 // generator function is exhausted.
278 // Remove it, and rerun these steps to advance to the next
279 // value in the base iterator.
280 this.#nextIterator
= null;
283 // The current iterator of values has yielded another
284 // value; reyield it.
285 return { value
, done
: false };
294 } = Iterator
.prototype;
295 const makePrototype
= (stringTag
) =>
302 value
: function next() {
303 return call(iteratorNext
, this, []);
317 * Returns a new function capable of generating iterators using the
318 * provided iterator generation method, iterator advancement method,
319 * optional generator function, and optional string tag.
321 * ※ The first two arguments to this function are bound to generate
322 * the actual exported iterator function makers.
324 * ※ This function is not exposed.
326 const iteratorFunction
= (
330 stringTag
= "Iterator",
332 const prototype = makePrototype(stringTag
); // intentionally cached
333 return ($, thisArg
= undefined) =>
336 call(makeBaseIterator
, $, []),
338 generateNext
=== null ? null : bind(generateNext
, thisArg
, []),
343 arrayIteratorFunction
: defineOwnProperty(
347 [arrayIterator
, arrayIteratorNext
],
350 { value
: "arrayIteratorFunction" },
352 generatorIteratorFunction
: defineOwnProperty(
360 generatorIteratorNext
,
364 { value
: "generatorIteratorFunction" },
366 mapIteratorFunction
: defineOwnProperty(
370 [mapIterator
, mapIteratorNext
],
373 { value
: "mapIteratorFunction" },
375 setIteratorFunction
: defineOwnProperty(
379 [setIterator
, setIteratorNext
],
382 { value
: "setIteratorFunction" },
384 stringIteratorFunction
: defineOwnProperty(
388 [stringIterator
, stringIteratorNext
],
391 { value
: "stringIteratorFunction" },