]> Lady’s Gitweb - Pisces/commitdiff
Move arraylike & index functions → values.js
authorLady <redacted>
Sun, 29 Oct 2023 23:26:53 +0000 (19:26 -0400)
committerLady <redacted>
Sun, 5 Nov 2023 20:39:41 +0000 (15:39 -0500)
Lengths and indices are properly kinds of values, not kinds of
collections, and this enables their use much earlier in the dependency
chain.

collection.js
collection.test.js
value.js
value.test.js

index dbdc8ed9ee3f366d005b6e1bfa3d586a19fbed1a..4545321af94621bf4e7c35afde39f64b303c175a 100644 (file)
@@ -9,14 +9,17 @@
 
 import { call, createCallableFunction } from "./function.js";
 import {
-  floor,
   isIntegralNumber,
-  isNan,
-  max,
   MAXIMUM_SAFE_INTEGRAL_NUMBER,
-  min,
 } from "./numeric.js";
-import { sameValue, type } from "./value.js";
+import {
+  canonicalNumericIndexString,
+  lengthOfArraylike,
+  sameValue,
+  toIndex,
+  toLength,
+  type,
+} from "./value.js";
 
 const { prototype: arrayPrototype } = Array;
 
@@ -31,25 +34,6 @@ export const {
   from: toArray,
 } = Array;
 
-/**
- * Returns −0 if the provided argument is "-0"; returns a number
- * representing the index if the provided argument is a canonical
- * numeric index string; otherwise, returns undefined.
- *
- * There is no clamping of the numeric index, but note that numbers
- * above 2^53 − 1 are not safe nor valid integer indices.
- */
-export const canonicalNumericIndexString = ($) => {
-  if (typeof $ !== "string") {
-    return undefined;
-  } else if ($ === "-0") {
-    return -0;
-  } else {
-    const n = +$;
-    return $ === `${n}` ? n : undefined;
-  }
-};
-
 /**
  * Returns the result of catenating the provided arraylikes into a new
  * collection according to the algorithm of `Array::concat`.
@@ -316,16 +300,6 @@ export const items = createCallableFunction(
   "items",
 );
 
-/**
- * Returns the length of the provided arraylike object.
- *
- * Will throw if the provided object is not arraylike.
- *
- * This can produce larger lengths than can actually be stored in
- * arrays, because no such restrictions exist on arraylike methods.
- */
-export const lengthOfArraylike = ({ length }) => toLength(length);
-
 /**
  * Returns the result of mapping the provided value with the provided
  * callback according to the algorithm of `Array::map`.
@@ -380,36 +354,6 @@ export const sort = createCallableFunction(arrayPrototype.sort);
  */
 export const splice = createCallableFunction(arrayPrototype.splice);
 
-/**
- * Returns the result of converting the provided value to an array
- * index, or throws an error if it is out of range.
- */
-export const toIndex = ($) => {
-  const integer = floor($);
-  if (isNan(integer) || integer == 0) {
-    // The value is zero·like.
-    return 0;
-  } else {
-    // The value is not zero·like.
-    const clamped = toLength(integer);
-    if (clamped !== integer) {
-      // Clamping the value changes it.
-      throw new RangeError(`Piscēs: Index out of range: ${$}.`);
-    } else {
-      // The value is within appropriate bounds.
-      return integer;
-    }
-  }
-};
-
-/** Returns the result of converting the provided value to a length. */
-export const toLength = ($) => {
-  const len = floor($);
-  return isNan(len) || len == 0
-    ? 0
-    : max(min(len, MAXIMUM_SAFE_INTEGRAL_NUMBER), 0);
-};
-
 /**
  * Unshifts the provided value according to the algorithm of
  * `Array::unshift`.
index bdded97324472d1e5654d8ba5198f7e62bf1a58a..544f454849a8dd4507eaa770ad1960db5798ac96 100644 (file)
@@ -12,58 +12,19 @@ import {
   assertSpyCall,
   assertSpyCalls,
   assertStrictEquals,
-  assertThrows,
   describe,
   it,
   spy,
 } from "./dev-deps.js";
 import {
-  canonicalNumericIndexString,
   findIndexedEntry,
   isArrayIndexString,
   isArraylikeObject,
   isCollection,
   isConcatSpreadable,
   isIntegerIndexString,
-  lengthOfArraylike,
-  toIndex,
-  toLength,
 } from "./collection.js";
 
-describe("canonicalNumericIndexString", () => {
-  it("[[Call]] returns undefined for nonstrings", () => {
-    assertStrictEquals(canonicalNumericIndexString(1), void {});
-  });
-
-  it("[[Call]] returns undefined for noncanonical strings", () => {
-    assertStrictEquals(canonicalNumericIndexString(""), void {});
-    assertStrictEquals(canonicalNumericIndexString("01"), void {});
-    assertStrictEquals(
-      canonicalNumericIndexString("9007199254740993"),
-      void {},
-    );
-  });
-
-  it('[[Call]] returns -0 for "-0"', () => {
-    assertStrictEquals(canonicalNumericIndexString("-0"), -0);
-  });
-
-  it("[[Call]] returns the corresponding number for canonical strings", () => {
-    assertStrictEquals(canonicalNumericIndexString("0"), 0);
-    assertStrictEquals(canonicalNumericIndexString("-0.25"), -0.25);
-    assertStrictEquals(
-      canonicalNumericIndexString("9007199254740992"),
-      9007199254740992,
-    );
-    assertStrictEquals(canonicalNumericIndexString("NaN"), 0 / 0);
-    assertStrictEquals(canonicalNumericIndexString("Infinity"), 1 / 0);
-    assertStrictEquals(
-      canonicalNumericIndexString("-Infinity"),
-      -1 / 0,
-    );
-  });
-});
-
 describe("findIndexedEntry", () => {
   it("[[Call]] returns undefined if no matching entry exists", () => {
     assertStrictEquals(findIndexedEntry([], () => true), void {});
@@ -343,93 +304,3 @@ describe("isIntegerIndexString", () => {
     assertStrictEquals(isIntegerIndexString("9007199254740991"), true);
   });
 });
-
-describe("lengthOfArraylike", () => {
-  it("[[Call]] returns the length", () => {
-    assertStrictEquals(
-      lengthOfArraylike({ length: 9007199254740991 }),
-      9007199254740991,
-    );
-  });
-
-  it("[[Call]] returns a non·nan result", () => {
-    assertStrictEquals(lengthOfArraylike({ length: NaN }), 0);
-    assertStrictEquals(lengthOfArraylike({ length: "failure" }), 0);
-  });
-
-  it("[[Call]] returns an integral result", () => {
-    assertStrictEquals(lengthOfArraylike({ length: 0.25 }), 0);
-    assertStrictEquals(lengthOfArraylike({ length: 1.1 }), 1);
-  });
-
-  it("[[Call]] returns a result greater than or equal to zero", () => {
-    assertStrictEquals(lengthOfArraylike({ length: -0 }), 0);
-    assertStrictEquals(lengthOfArraylike({ length: -1 }), 0);
-    assertStrictEquals(lengthOfArraylike({ length: -Infinity }), 0);
-  });
-
-  it("[[Call]] returns a result less than 2 ** 53", () => {
-    assertStrictEquals(
-      lengthOfArraylike({ length: 9007199254740992 }),
-      9007199254740991,
-    );
-    assertStrictEquals(
-      lengthOfArraylike({ length: Infinity }),
-      9007199254740991,
-    );
-  });
-});
-
-describe("toIndex", () => {
-  it("[[Call]] returns an index", () => {
-    assertStrictEquals(toIndex(9007199254740991), 9007199254740991);
-  });
-
-  it("[[Call]] returns zero for a zerolike result", () => {
-    assertStrictEquals(toIndex(NaN), 0);
-    assertStrictEquals(toIndex("failure"), 0);
-    assertStrictEquals(toIndex(-0), 0);
-  });
-
-  it("[[Call]] rounds down to the nearest integer", () => {
-    assertStrictEquals(toIndex(0.25), 0);
-    assertStrictEquals(toIndex(1.1), 1);
-  });
-
-  it("[[Call]] throws when provided a negative number", () => {
-    assertThrows(() => toIndex(-1));
-    assertThrows(() => toIndex(-Infinity));
-  });
-
-  it("[[Call]] throws when provided a number greater than or equal to 2 ** 53", () => {
-    assertThrows(() => toIndex(9007199254740992));
-    assertThrows(() => toIndex(Infinity));
-  });
-});
-
-describe("toLength", () => {
-  it("[[Call]] returns a length", () => {
-    assertStrictEquals(toLength(9007199254740991), 9007199254740991);
-  });
-
-  it("[[Call]] returns a non·nan result", () => {
-    assertStrictEquals(toLength(NaN), 0);
-    assertStrictEquals(toLength("failure"), 0);
-  });
-
-  it("[[Call]] returns an integral result", () => {
-    assertStrictEquals(toLength(0.25), 0);
-    assertStrictEquals(toLength(1.1), 1);
-  });
-
-  it("[[Call]] returns a result greater than or equal to zero", () => {
-    assertStrictEquals(toLength(-0), 0);
-    assertStrictEquals(toLength(-1), 0);
-    assertStrictEquals(toLength(-Infinity), 0);
-  });
-
-  it("[[Call]] returns a result less than 2 ** 53", () => {
-    assertStrictEquals(toLength(9007199254740992), 9007199254740991);
-    assertStrictEquals(toLength(Infinity), 9007199254740991);
-  });
-});
index bda9180e4347e6cd74f0b36676fa82da208a2384..dca20e83d190a4462fd11f88bd8a5bbe957e5a47 100644 (file)
--- a/value.js
+++ b/value.js
@@ -51,6 +51,35 @@ export const NULL = null;
 /** The undefined primitive. */
 export const UNDEFINED = undefined;
 
+/**
+ * Returns −0 if the provided argument is "-0"; returns a number
+ * representing the index if the provided argument is a canonical
+ * numeric index string; otherwise, returns undefined.
+ *
+ * There is no clamping of the numeric index, but note that numbers
+ * above 2^53 − 1 are not safe nor valid integer indices.
+ */
+export const canonicalNumericIndexString = ($) => {
+  if (typeof $ !== "string") {
+    return undefined;
+  } else if ($ === "-0") {
+    return -0;
+  } else {
+    const n = +$;
+    return $ === `${n}` ? n : undefined;
+  }
+};
+
+/**
+ * Returns the length of the provided arraylike value.
+ *
+ * This can produce larger lengths than can actually be stored in
+ * arrays, because no such restrictions exist on arraylike methods.
+ *
+ * ☡ This function throws if the provided value is not arraylike.
+ */
+export const lengthOfArraylike = ({ length }) => toLength(length);
+
 export const {
   /**
    * Returns the primitive value of the provided object per its
@@ -156,8 +185,23 @@ export const {
    * ※ This differs from `===` in the case of nan.
    */
   sameValueZero,
+
+  /**
+   * Returns the result of converting the provided value to an index,
+   * or throws an error if it is out of range.
+   */
+  toIndex,
+
+  /**
+   * Returns the result of converting the provided value to a length.
+   */
+  toLength,
 } = (() => {
-  const { isNaN: isNan } = Number;
+  const { floor, max, min } = Math;
+  const {
+    MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER,
+    isNaN: isNan,
+  } = Number;
   const { is } = Object;
   return {
     sameValue: (a, b) => is(a, b),
@@ -176,6 +220,29 @@ export const {
         return $1 === $2;
       }
     },
+    toIndex: ($) => {
+      const integer = floor($);
+      if (isNan(integer) || integer == 0) {
+        // The value is zero·like.
+        return 0;
+      } else {
+        // The value is not zero·like.
+        const clamped = toLength(integer);
+        if (clamped !== integer) {
+          // Clamping the value changes it.
+          throw new RangeError(`Piscēs: Index out of range: ${$}.`);
+        } else {
+          // The value is within appropriate bounds.
+          return integer;
+        }
+      }
+    },
+    toLength: ($) => {
+      const len = floor($);
+      return isNan(len) || len == 0
+        ? 0
+        : max(min(len, MAXIMUM_SAFE_INTEGRAL_NUMBER), 0);
+    },
   };
 })();
 
index ae2b7d850793cdd139c7064124064e3ff6db687c..cecafbbe88c5a5d8fd15bbb32a733011fac86f40 100644 (file)
@@ -15,9 +15,11 @@ import {
 } from "./dev-deps.js";
 import {
   ASYNC_ITERATOR,
+  canonicalNumericIndexString,
   HAS_INSTANCE,
   IS_CONCAT_SPREADABLE,
   ITERATOR,
+  lengthOfArraylike,
   MATCH,
   MATCH_ALL,
   NULL,
@@ -29,6 +31,8 @@ import {
   SPLIT,
   TO_PRIMITIVE,
   TO_STRING_TAG,
+  toIndex,
+  toLength,
   toPrimitive,
   type,
   UNDEFINED,
@@ -122,6 +126,116 @@ describe("UNSCOPABLES", () => {
   });
 });
 
+describe("canonicalNumericIndexString", () => {
+  it("[[Call]] returns undefined for nonstrings", () => {
+    assertStrictEquals(canonicalNumericIndexString(1), void {});
+  });
+
+  it("[[Call]] returns undefined for noncanonical strings", () => {
+    assertStrictEquals(canonicalNumericIndexString(""), void {});
+    assertStrictEquals(canonicalNumericIndexString("01"), void {});
+    assertStrictEquals(
+      canonicalNumericIndexString("9007199254740993"),
+      void {},
+    );
+  });
+
+  it('[[Call]] returns -0 for "-0"', () => {
+    assertStrictEquals(canonicalNumericIndexString("-0"), -0);
+  });
+
+  it("[[Call]] returns the corresponding number for canonical strings", () => {
+    assertStrictEquals(canonicalNumericIndexString("0"), 0);
+    assertStrictEquals(canonicalNumericIndexString("-0.25"), -0.25);
+    assertStrictEquals(
+      canonicalNumericIndexString("9007199254740992"),
+      9007199254740992,
+    );
+    assertStrictEquals(canonicalNumericIndexString("NaN"), 0 / 0);
+    assertStrictEquals(canonicalNumericIndexString("Infinity"), 1 / 0);
+    assertStrictEquals(
+      canonicalNumericIndexString("-Infinity"),
+      -1 / 0,
+    );
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new canonicalNumericIndexString(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(canonicalNumericIndexString.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        canonicalNumericIndexString.name,
+        "canonicalNumericIndexString",
+      );
+    });
+  });
+});
+
+describe("lengthOfArraylike", () => {
+  it("[[Call]] returns the length", () => {
+    assertStrictEquals(
+      lengthOfArraylike({ length: 9007199254740991 }),
+      9007199254740991,
+    );
+  });
+
+  it("[[Call]] returns a non·nan result", () => {
+    assertStrictEquals(lengthOfArraylike({ length: NaN }), 0);
+    assertStrictEquals(lengthOfArraylike({ length: "failure" }), 0);
+  });
+
+  it("[[Call]] returns an integral result", () => {
+    assertStrictEquals(lengthOfArraylike({ length: 0.25 }), 0);
+    assertStrictEquals(lengthOfArraylike({ length: 1.1 }), 1);
+  });
+
+  it("[[Call]] returns a result greater than or equal to zero", () => {
+    assertStrictEquals(lengthOfArraylike({ length: -0 }), 0);
+    assertStrictEquals(lengthOfArraylike({ length: -1 }), 0);
+    assertStrictEquals(lengthOfArraylike({ length: -Infinity }), 0);
+  });
+
+  it("[[Call]] returns a result less than 2 ** 53", () => {
+    assertStrictEquals(
+      lengthOfArraylike({ length: 9007199254740992 }),
+      9007199254740991,
+    );
+    assertStrictEquals(
+      lengthOfArraylike({ length: Infinity }),
+      9007199254740991,
+    );
+  });
+
+  it("[[Call]] does not require an object argument", () => {
+    assertStrictEquals(lengthOfArraylike("string"), 6);
+    assertStrictEquals(lengthOfArraylike(Symbol()), 0);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new lengthOfArraylike(""));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(lengthOfArraylike.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(lengthOfArraylike.name, "lengthOfArraylike");
+    });
+  });
+});
+
 describe("ordinaryToPrimitive", () => {
   it("[[Call]] prefers `valueOf` by default", () => {
     const obj = {
@@ -335,6 +449,92 @@ describe("sameValueZero", () => {
   });
 });
 
+describe("toIndex", () => {
+  it("[[Call]] returns an index", () => {
+    assertStrictEquals(toIndex(9007199254740991), 9007199254740991);
+  });
+
+  it("[[Call]] returns zero for a zerolike argument", () => {
+    assertStrictEquals(toIndex(NaN), 0);
+    assertStrictEquals(toIndex("failure"), 0);
+    assertStrictEquals(toIndex(-0), 0);
+  });
+
+  it("[[Call]] rounds down to the nearest integer", () => {
+    assertStrictEquals(toIndex(0.25), 0);
+    assertStrictEquals(toIndex(1.1), 1);
+  });
+
+  it("[[Call]] throws when provided a negative number", () => {
+    assertThrows(() => toIndex(-1));
+    assertThrows(() => toIndex(-Infinity));
+  });
+
+  it("[[Call]] throws when provided a number greater than or equal to 2 ** 53", () => {
+    assertThrows(() => toIndex(9007199254740992));
+    assertThrows(() => toIndex(Infinity));
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new toIndex(0));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(toIndex.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(toIndex.name, "toIndex");
+    });
+  });
+});
+
+describe("toLength", () => {
+  it("[[Call]] returns a length", () => {
+    assertStrictEquals(toLength(9007199254740991), 9007199254740991);
+  });
+
+  it("[[Call]] returns zero for a nan argument", () => {
+    assertStrictEquals(toLength(NaN), 0);
+    assertStrictEquals(toLength("failure"), 0);
+  });
+
+  it("[[Call]] rounds down to the nearest integer", () => {
+    assertStrictEquals(toLength(0.25), 0);
+    assertStrictEquals(toLength(1.1), 1);
+  });
+
+  it("[[Call]] returns a result greater than or equal to zero", () => {
+    assertStrictEquals(toLength(-0), 0);
+    assertStrictEquals(toLength(-1), 0);
+    assertStrictEquals(toLength(-Infinity), 0);
+  });
+
+  it("[[Call]] returns a result less than 2 ** 53", () => {
+    assertStrictEquals(toLength(9007199254740992), 9007199254740991);
+    assertStrictEquals(toLength(Infinity), 9007199254740991);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new toLength(0));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(toLength.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(toLength.name, "toLength");
+    });
+  });
+});
+
 describe("toPrimitive", () => {
   it("[[Call]] returns the argument when passed a primitive", () => {
     const value = Symbol();
This page took 0.084451 seconds and 4 git commands to generate.