From: Lady Date: Sun, 27 Jul 2025 18:26:41 +0000 (-0400) Subject: Move index string functions back into value.js X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/ea0c5228a40540f4e3263f303b22f94df29abeaa?ds=inline;hp=2e6fac40384b2e17dd3a6b8700abc787b0b57479 Move index string functions back into value.js While index strings are always strings, really they are special kinds of values, and `canonicalNumericIndexString` notably does not return a string but rather a number or undefined. Since `toLength` needs to be in `value.js`, and these functions are very similar in function and applicability, it seems best to keep them in the same place. --- diff --git a/string.js b/string.js index ddc05d7..99528a8 100644 --- a/string.js +++ b/string.js @@ -345,25 +345,6 @@ export const { }; })(); -/** - * 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; - } -}; - export const { /** * Returns an iterator over the codepoints in the string representation @@ -548,36 +529,6 @@ export const getLastSubstringIndex = createCallableFunction( { name: "getLastSubstringIndex" }, ); -/** Returns whether the provided value is an array index. */ -export const isArrayIndexString = ($) => { - const value = canonicalNumericIndexString($); - if (value !== UNDEFINED) { - // The provided value is a canonical numeric index string. - // - // Return whether it is in range for array indices. - return sameValue(value, 0) - || value === toLength(value) && value > 0 && value < -1 >>> 0; - } else { - // The provided value is not a canonical numeric index string. - return false; - } -}; - -/** Returns whether the provided value is an integer index string. */ -export const isIntegerIndexString = ($) => { - const value = canonicalNumericIndexString($); - if (value !== UNDEFINED) { - // The provided value is a canonical numeric index string. - // - // Return whether it is in range for integer indices. - return sameValue(value, 0) - || value === toLength(value) && value > 0; - } else { - // The provided value is not a canonical numeric index string. - return false; - } -}; - /** * Returns the result of joining the provided iterable. * diff --git a/string.test.js b/string.test.js index 147e109..e567060 100644 --- a/string.test.js +++ b/string.test.js @@ -23,7 +23,6 @@ import { import { asciiLowercase, asciiUppercase, - canonicalNumericIndexString, characters, codepoints, codeUnits, @@ -32,8 +31,6 @@ import { getCodeUnit, getFirstSubstringIndex, getLastSubstringIndex, - isArrayIndexString, - isIntegerIndexString, join, Matcher, rawString, @@ -635,59 +632,6 @@ describe("asciiUppercase", () => { }); }); -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("characters", () => { it("[[Call]] returns an iterable", () => { assertStrictEquals( @@ -1008,131 +952,6 @@ describe("getLastSubstringIndex", () => { }); }); -describe("isArrayIndexString", () => { - it("[[Call]] returns false for nonstrings", () => { - assertStrictEquals(isArrayIndexString(1), false); - }); - - it("[[Call]] returns false for noncanonical strings", () => { - assertStrictEquals(isArrayIndexString(""), false); - assertStrictEquals(isArrayIndexString("01"), false); - assertStrictEquals(isArrayIndexString("9007199254740993"), false); - }); - - it("[[Call]] returns false for nonfinite numbers", () => { - assertStrictEquals(isArrayIndexString("NaN"), false); - assertStrictEquals(isArrayIndexString("Infinity"), false); - assertStrictEquals(isArrayIndexString("-Infinity"), false); - }); - - it("[[Call]] returns false for negative numbers", () => { - assertStrictEquals(isArrayIndexString("-0"), false); - assertStrictEquals(isArrayIndexString("-1"), false); - }); - - it("[[Call]] returns false for nonintegers", () => { - assertStrictEquals(isArrayIndexString("0.25"), false); - assertStrictEquals(isArrayIndexString("1.1"), false); - }); - - it("[[Call]] returns false for numbers greater than or equal to -1 >>> 0", () => { - assertStrictEquals(isArrayIndexString(String(-1 >>> 0)), false); - assertStrictEquals( - isArrayIndexString(String((-1 >>> 0) + 1)), - false, - ); - }); - - it("[[Call]] returns true for array lengths less than -1 >>> 0", () => { - assertStrictEquals(isArrayIndexString("0"), true); - assertStrictEquals( - isArrayIndexString(String((-1 >>> 0) - 1)), - true, - ); - }); - - it("[[Construct]] throws an error", () => { - assertThrows(() => new isArrayIndexString("0")); - }); - - describe(".length", () => { - it("[[Get]] returns the correct length", () => { - assertStrictEquals(isArrayIndexString.length, 1); - }); - }); - - describe(".name", () => { - it("[[Get]] returns the correct name", () => { - assertStrictEquals( - isArrayIndexString.name, - "isArrayIndexString", - ); - }); - }); -}); - -describe("isIntegerIndexString", () => { - it("[[Call]] returns false for nonstrings", () => { - assertStrictEquals(isIntegerIndexString(1), false); - }); - - it("[[Call]] returns false for noncanonical strings", () => { - assertStrictEquals(isIntegerIndexString(""), false); - assertStrictEquals(isIntegerIndexString("01"), false); - assertStrictEquals( - isIntegerIndexString("9007199254740993"), - false, - ); - }); - - it("[[Call]] returns false for nonfinite numbers", () => { - assertStrictEquals(isIntegerIndexString("NaN"), false); - assertStrictEquals(isIntegerIndexString("Infinity"), false); - assertStrictEquals(isIntegerIndexString("-Infinity"), false); - }); - - it("[[Call]] returns false for negative numbers", () => { - assertStrictEquals(isIntegerIndexString("-0"), false); - assertStrictEquals(isIntegerIndexString("-1"), false); - }); - - it("[[Call]] returns false for nonintegers", () => { - assertStrictEquals(isIntegerIndexString("0.25"), false); - assertStrictEquals(isIntegerIndexString("1.1"), false); - }); - - it("[[Call]] returns false for numbers greater than or equal to 2 ** 53", () => { - assertStrictEquals( - isIntegerIndexString("9007199254740992"), - false, - ); - }); - - it("[[Call]] returns true for safe canonical integer strings", () => { - assertStrictEquals(isIntegerIndexString("0"), true); - assertStrictEquals(isIntegerIndexString("9007199254740991"), true); - }); - - it("[[Construct]] throws an error", () => { - assertThrows(() => new isIntegerIndexString("0")); - }); - - describe(".length", () => { - it("[[Get]] returns the correct length", () => { - assertStrictEquals(isIntegerIndexString.length, 1); - }); - }); - - describe(".name", () => { - it("[[Get]] returns the correct name", () => { - assertStrictEquals( - isIntegerIndexString.name, - "isIntegerIndexString", - ); - }); - }); -}); - describe("join", () => { it("[[Call]] joins the provided iterator with the provided separartor", () => { assertStrictEquals(join([1, 2, 3, 4].values(), "☂"), "1☂2☂3☂4"); diff --git a/value.js b/value.js index 6956b6f..341ecdf 100644 --- a/value.js +++ b/value.js @@ -178,6 +178,25 @@ export const POSITIVE_ZERO = 0; /** 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; + } +}; + /** * Completes the provided property descriptor by setting missing values * to their defaults. @@ -243,6 +262,21 @@ export const completePropertyDescriptor = (Desc) => { export const isAccessorDescriptor = (Desc) => Desc !== UNDEFINED && ("get" in Desc || "set" in Desc); +/** Returns whether the provided value is an array index. */ +export const isArrayIndexString = ($) => { + const value = canonicalNumericIndexString($); + if (value !== UNDEFINED) { + // The provided value is a canonical numeric index string. + // + // Return whether it is in range for array indices. + return sameValue(value, 0) + || value === toLength(value) && value > 0 && value < -1 >>> 0; + } else { + // The provided value is not a canonical numeric index string. + return false; + } +}; + /** Gets whether the provided value is a data descrtiptor. */ export const isDataDescriptor = (Desc) => Desc !== UNDEFINED && ("value" in Desc || "writable" in Desc); @@ -266,6 +300,21 @@ export const isGenericDescriptor = (Desc) => && !("get" in Desc || "set" in Desc || "value" in Desc || "writable" in Desc); +/** Returns whether the provided value is an integer index string. */ +export const isIntegerIndexString = ($) => { + const value = canonicalNumericIndexString($); + if (value !== UNDEFINED) { + // The provided value is a canonical numeric index string. + // + // Return whether it is in range for integer indices. + return sameValue(value, 0) + || value === toLength(value) && value > 0; + } else { + // The provided value is not a canonical numeric index string. + return false; + } +}; + export const { /** * Returns the primitive value of the provided object per its @@ -420,7 +469,7 @@ export const { const { isNaN: isNan } = Number; const { is } = Object; return { - sameValue: (a, b) => is(a, b), + sameValue: ($1, $2) => is($1, $2), sameValueZero: ($1, $2) => { const type1 = type($1); const type2 = type($2); diff --git a/value.test.js b/value.test.js index b7dcd69..c55b591 100644 --- a/value.test.js +++ b/value.test.js @@ -19,13 +19,16 @@ import { } from "./dev-deps.js"; import { ASYNC_ITERATOR, + canonicalNumericIndexSting, completePropertyDescriptor, HAS_INSTANCE, IS_CONCAT_SPREADABLE, isAccessorDescriptor, + isArrayIndexString, isDataDescriptor, isFullyPopulatedDescriptor, isGenericDescriptor, + isIntegerIndexString, ITERATOR, LN_10, LN_2, @@ -249,6 +252,59 @@ 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("completePropertyDescriptor", () => { it("[[Call]] completes a generic descriptor", () => { const desc = {}; @@ -352,6 +408,69 @@ describe("isAccessorDescriptor", () => { }); }); +describe("isArrayIndexString", () => { + it("[[Call]] returns false for nonstrings", () => { + assertStrictEquals(isArrayIndexString(1), false); + }); + + it("[[Call]] returns false for noncanonical strings", () => { + assertStrictEquals(isArrayIndexString(""), false); + assertStrictEquals(isArrayIndexString("01"), false); + assertStrictEquals(isArrayIndexString("9007199254740993"), false); + }); + + it("[[Call]] returns false for nonfinite numbers", () => { + assertStrictEquals(isArrayIndexString("NaN"), false); + assertStrictEquals(isArrayIndexString("Infinity"), false); + assertStrictEquals(isArrayIndexString("-Infinity"), false); + }); + + it("[[Call]] returns false for negative numbers", () => { + assertStrictEquals(isArrayIndexString("-0"), false); + assertStrictEquals(isArrayIndexString("-1"), false); + }); + + it("[[Call]] returns false for nonintegers", () => { + assertStrictEquals(isArrayIndexString("0.25"), false); + assertStrictEquals(isArrayIndexString("1.1"), false); + }); + + it("[[Call]] returns false for numbers greater than or equal to -1 >>> 0", () => { + assertStrictEquals(isArrayIndexString(String(-1 >>> 0)), false); + assertStrictEquals( + isArrayIndexString(String((-1 >>> 0) + 1)), + false, + ); + }); + + it("[[Call]] returns true for array lengths less than -1 >>> 0", () => { + assertStrictEquals(isArrayIndexString("0"), true); + assertStrictEquals( + isArrayIndexString(String((-1 >>> 0) - 1)), + true, + ); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new isArrayIndexString("0")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(isArrayIndexString.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + isArrayIndexString.name, + "isArrayIndexString", + ); + }); + }); +}); + describe("isDataDescriptor", () => { it("[[Call]] returns false for a generic descriptor", () => { assertStrictEquals(isDataDescriptor({}), false); @@ -505,6 +624,68 @@ describe("isGenericDescriptor", () => { }); }); +describe("isIntegerIndexString", () => { + it("[[Call]] returns false for nonstrings", () => { + assertStrictEquals(isIntegerIndexString(1), false); + }); + + it("[[Call]] returns false for noncanonical strings", () => { + assertStrictEquals(isIntegerIndexString(""), false); + assertStrictEquals(isIntegerIndexString("01"), false); + assertStrictEquals( + isIntegerIndexString("9007199254740993"), + false, + ); + }); + + it("[[Call]] returns false for nonfinite numbers", () => { + assertStrictEquals(isIntegerIndexString("NaN"), false); + assertStrictEquals(isIntegerIndexString("Infinity"), false); + assertStrictEquals(isIntegerIndexString("-Infinity"), false); + }); + + it("[[Call]] returns false for negative numbers", () => { + assertStrictEquals(isIntegerIndexString("-0"), false); + assertStrictEquals(isIntegerIndexString("-1"), false); + }); + + it("[[Call]] returns false for nonintegers", () => { + assertStrictEquals(isIntegerIndexString("0.25"), false); + assertStrictEquals(isIntegerIndexString("1.1"), false); + }); + + it("[[Call]] returns false for numbers greater than or equal to 2 ** 53", () => { + assertStrictEquals( + isIntegerIndexString("9007199254740992"), + false, + ); + }); + + it("[[Call]] returns true for safe canonical integer strings", () => { + assertStrictEquals(isIntegerIndexString("0"), true); + assertStrictEquals(isIntegerIndexString("9007199254740991"), true); + }); + + it("[[Construct]] throws an error", () => { + assertThrows(() => new isIntegerIndexString("0")); + }); + + describe(".length", () => { + it("[[Get]] returns the correct length", () => { + assertStrictEquals(isIntegerIndexString.length, 1); + }); + }); + + describe(".name", () => { + it("[[Get]] returns the correct name", () => { + assertStrictEquals( + isIntegerIndexString.name, + "isIntegerIndexString", + ); + }); + }); +}); + describe("ordinaryToPrimitive", () => { it("[[Call]] prefers `valueOf` by default", () => { const obj = {