From: Lady Date: Thu, 24 Jul 2025 03:35:47 +0000 (-0400) Subject: Define names and lengths for iterator functions X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/d8b8bf6b37eb780a98bca3be06d2d98df4c83691?hp=e7f1624844b6bb27626d982ac046fddf68845136 Define names and lengths for iterator functions Iterator function generators had names, but the functions they generated did not. Now they do, following the general conventions of existing functions like `Array::[Symbol.iterator]` (not that these _particularly_ make sense). Their lengths were already 1 and they were already not constructable, but this is now tested. Also, minor changes to documentation, ⁊·c. --- diff --git a/iterable.js b/iterable.js index 7149a28..6b45000 100644 --- a/iterable.js +++ b/iterable.js @@ -1,11 +1,14 @@ -// ♓🌟 Piscēs ∷ iterable.js -// ==================================================================== -// -// Copyright © 2023 Lady [@ Lady’s Computer]. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at . +// SPDX-FileCopyrightText: 2023, 2025 Lady +// SPDX-License-Identifier: MPL-2.0 +/** + * ⁌ ♓🧩 Piscēs ∷ iterable.js + * + * Copyright © 2023, 2025 Lady [@ Ladys Computer]. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at . + */ import { bind, call, identity } from "./function.js"; import { @@ -30,7 +33,7 @@ export const { * the resulting iterator. * * The resulting function also takes a second argument, which will be - * used as the `this` value when calling the provided generator + * used as the this value when calling the provided generator * function, if provided. * * ※ The returned function is an ordinary nonconstructible (arrow) @@ -53,7 +56,7 @@ export const { * the resulting iterator. * * The resulting function also takes a second argument, which will be - * used as the `this` value when calling the provided generator + * used as the this value when calling the provided generator * function, if provided. * * ※ The returned function is an ordinary nonconstructible (arrow) @@ -82,7 +85,7 @@ export const { * the resulting iterator. * * The resulting function also takes a second argument, which will be - * used as the `this` value when calling the provided generator + * used as the this value when calling the provided generator * function, if provided. * * ※ The returned function is an ordinary nonconstructible (arrow) @@ -105,7 +108,7 @@ export const { * the resulting iterator. * * The resulting function also takes a second argument, which will be - * used as the `this` value when calling the provided generator + * used as the this value when calling the provided generator * function, if provided. * * ※ The returned function is an ordinary nonconstructible (arrow) @@ -128,11 +131,11 @@ export const { * the resulting iterator. * * The resulting function also takes a second argument, which will be - * used as the `this` value when calling the provided generator + * used as the this value when calling the provided generator * function, if provided. * * ※ This iterator function iterates over characters; use - * `arrayIteratorFunction` to iterate over code units. + * arrayIteratorFunction to iterate over code units. * * ※ The returned function is an ordinary nonconstructible (arrow) * function which, when called with a string, returns an iterator. @@ -174,7 +177,7 @@ export const { * An iterator generated by an iterator function. * * This class provides the internal data structure of all the - * iterator functions as well as the `::next` behaviour they all use. + * iterator functions as well as the `::next´ behaviour they all use. * * ※ This class extends the identity function to allow for arbitrary * construction of its superclass instance based on the provided @@ -197,7 +200,7 @@ export const { * * ☡ It is not possible to type·check the provided next method or * generator function to ensure that they actually are correct and - * appropriately callable; if they aren’t, an error will be thrown + * appropriately callable; if they aren¦t, an error will be thrown * when attempting to yield the first value. */ constructor( @@ -220,8 +223,8 @@ export const { const baseIteratorNext = this.#baseIteratorNext; const generateNext = this.#generateNext; while (true) { - // This function sometimes needs to repeat its processing steps - // in the case that a generator function was provided. + // This function some·times needs to repeat its processing + // steps in the case that a generator function was provided. // // To avoid potentially large amounts of recursive calls, it is // defined in a loop which will exit the first time a suitable @@ -233,7 +236,7 @@ export const { return { value: UNDEFINED, done: true }; } else if (nextIterator === null) { // This iterator is not currently yielding values from the - // provided generator function, either because it doesn’t + // provided generator function, either because it doesn¦t // exist or because its values have been exhausted. // // Get the next value in the base iterator and either provide @@ -283,7 +286,9 @@ export const { continue; } else { // The current iterator of values has yielded another - // value; reyield it. + // value. + // + // Reyield it. return { value, done: false }; } } @@ -328,17 +333,28 @@ export const { const iteratorFunction = ( makeBaseIterator, baseIteratorNext, + iteratorFunctionName, generateNext, stringTag = "Iterator", ) => { const prototype = makePrototype(stringTag); // intentionally cached - return ($, thisArg = UNDEFINED) => - new Iterator( - prototype, - call(makeBaseIterator, $, []), - baseIteratorNext, - generateNext == null ? null : bind(generateNext, thisArg, []), - ); + return defineOwnProperty( + ($, thisArg = UNDEFINED) => + new Iterator( + prototype, + call(makeBaseIterator, $, []), + baseIteratorNext, + generateNext == null + ? null + : bind(generateNext, thisArg, []), + ), + "name", + defineOwnDataProperty( + objectCreate(null), + "value", + iteratorFunctionName, + ), + ); }; return { @@ -346,7 +362,7 @@ export const { bind( iteratorFunction, UNDEFINED, - [arrayIterator, arrayIteratorNext], + [arrayIterator, arrayIteratorNext, "values"], ), "name", defineOwnDataProperty( @@ -364,6 +380,7 @@ export const { return this(); }, generatorIteratorNext, + "yields", ], ), "name", @@ -377,7 +394,7 @@ export const { bind( iteratorFunction, UNDEFINED, - [mapIterator, mapIteratorNext], + [mapIterator, mapIteratorNext, "entries"], ), "name", defineOwnDataProperty( @@ -390,7 +407,7 @@ export const { bind( iteratorFunction, UNDEFINED, - [setIterator, setIteratorNext], + [setIterator, setIteratorNext, "values"], ), "name", defineOwnDataProperty( @@ -403,7 +420,7 @@ export const { bind( iteratorFunction, UNDEFINED, - [stringIterator, stringIteratorNext], + [stringIterator, stringIteratorNext, "characters"], ), "name", defineOwnDataProperty( diff --git a/iterable.test.js b/iterable.test.js index 0f7c9fc..3651c6c 100644 --- a/iterable.test.js +++ b/iterable.test.js @@ -1,11 +1,14 @@ -// ♓🌟 Piscēs ∷ iterable.test.js -// ==================================================================== -// -// Copyright © 2023 Lady [@ Lady’s Computer]. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at . +// SPDX-FileCopyrightText: 2023, 2025 Lady +// SPDX-License-Identifier: MPL-2.0 +/** + * ⁌ ♓🧩 Piscēs ∷ iterable.test.js + * + * Copyright © 2023, 2025 Lady [@ Ladys Computer]. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at . + */ import { assertEquals, @@ -126,6 +129,26 @@ describe("arrayIteratorFunction", () => { }); }); + it("[[Construct]] throws an error", () => { + const iterator = arrayIteratorFunction(); + assertThrows(() => new iterator([])); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(arrayIteratorFunction().length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + arrayIteratorFunction().name, + "values", + ); + }); + }); + describe("::next", () => { it("[[Call]] throws if there are values and the mapper is not a generator function", () => { const iterator = arrayIteratorFunction(function () {}); @@ -253,6 +276,26 @@ describe("generatorIteratorFunction", () => { }); }); + it("[[Construct]] throws an error", () => { + const iterator = generatorIteratorFunction(); + assertThrows(() => new iterator(function* () {})); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(generatorIteratorFunction().length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + generatorIteratorFunction().name, + "yields", + ); + }); + }); + describe("::next", () => { it("[[Call]] throws if there are values and the mapper is not a generator function", () => { const generator = function* () { @@ -378,6 +421,26 @@ describe("mapIteratorFunction", () => { }); }); + it("[[Construct]] throws an error", () => { + const iterator = mapIteratorFunction(); + assertThrows(() => new iterator(new Map())); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(mapIteratorFunction().length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + mapIteratorFunction().name, + "entries", + ); + }); + }); + describe("::next", () => { it("[[Call]] throws if there are values and the mapper is not a generator function", () => { const iterator = mapIteratorFunction(function () {}); @@ -493,6 +556,26 @@ describe("setIteratorFunction", () => { }); }); + it("[[Construct]] throws an error", () => { + const iterator = setIteratorFunction(); + assertThrows(() => new iterator(new Set())); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(setIteratorFunction().length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + setIteratorFunction().name, + "values", + ); + }); + }); + describe("::next", () => { it("[[Call]] throws if there are values and the mapper is not a generator function", () => { const iterator = setIteratorFunction(function () {}); @@ -613,6 +696,26 @@ describe("stringIteratorFunction", () => { }); }); + it("[[Construct]] throws an error", () => { + const iterator = stringIteratorFunction(); + assertThrows(() => new iterator("")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(stringIteratorFunction().length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + stringIteratorFunction().name, + "characters", + ); + }); + }); + describe("::next", () => { it("[[Call]] throws if there are values and the mapper is not a generator function", () => { const iterator = stringIteratorFunction(function () {});