From: Lady Date: Fri, 17 Nov 2023 02:26:48 +0000 (-0500) Subject: Move string functions from value.js to string.js X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/3c074e801e651cfea23eb9a1c0a8aa373c46bed0?ds=inline Move string functions from value.js to string.js These actually make more sense here! --- diff --git a/collection.js b/collection.js index d106ba4..0d4e623 100644 --- a/collection.js +++ b/collection.js @@ -8,8 +8,8 @@ // file, You can obtain one at . import { call, createCallableFunction } from "./function.js"; +import { canonicalNumericIndexString } from "./string.js"; import { - canonicalNumericIndexString, lengthOfArraylike, sameValue, toIndex, diff --git a/string.js b/string.js index 6d4d229..bffa6d2 100644 --- a/string.js +++ b/string.js @@ -23,6 +23,7 @@ import { getOwnPropertyDescriptors, setPrototype, } from "./object.js"; +import { sameValue, toLength } from "./value.js"; const RE = RegExp; const { prototype: rePrototype } = RE; @@ -299,6 +300,25 @@ 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 @@ -379,15 +399,29 @@ export const getCharacter = ($, pos) => { : stringFromCodepoints(codepoint); }; -/** - * Returns the code unit at the provided position in the string - * representation of the provided value according to the algorithm of - * `String::charAt`, except that out‐of‐bounds values return undefined - * in place of nan. - */ export const { + /** + * Returns the code unit at the provided position in the string + * representation of the provided value according to the algorithm of + * `String::charAt`, except that out‐of‐bounds values return undefined + * in place of nan. + */ getCodeUnit, + /** Returns whether the provided value is an integer index string. */ + isIntegerIndexString, + + /** + * Returns a string created from the provided code units. + * + * ※ This is effectively an alias for `String.fromCharCode`, but + * with the same error behaviour as `String.fromCodePoint`. + * + * ☡ This function throws an error if provided with an argument which + * is not an integral number from 0 to FFFF₁₆ inclusive. + */ + stringFromCodeUnits, + /** * Returns the result of catenating the string representations of the * provided values, returning a new string according to the algorithm @@ -400,18 +434,60 @@ export const { */ stringCatenate, } = (() => { + const { fromCharCode } = String; const { charCodeAt, concat } = String.prototype; - const { isNaN: isNan } = Number; + const { + MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER, + isInteger: isIntegralNumber, + isNaN: isNan, + } = Number; return { getCodeUnit: ($, n) => { const codeUnit = call(charCodeAt, $, [n]); return isNan(codeUnit) ? undefined : codeUnit; }, + isIntegerIndexString: ($) => { + const value = canonicalNumericIndexString($); + if (value !== undefined && isIntegralNumber(value)) { + // The provided value is an integral canonical numeric index + // string. + return sameValue(value, 0) || + value > 0 && value <= MAXIMUM_SAFE_INTEGRAL_NUMBER && + value === toLength(value); + } else { + // The provided value is not an integral canonical numeric + // index string. + return false; + } + }, stringCatenate: defineOwnProperties( (...args) => call(concat, "", args), { name: { value: "stringCatenate" }, length: { value: 2 } }, ), + stringFromCodeUnits: defineOwnProperties( + (...codeUnits) => { + for (let index = 0; index < codeUnits.length; ++index) { + // Iterate over each provided code unit and throw if it is + // out of range. + const nextCU = +codeUnits[index]; + if ( + !isIntegralNumber(nextCU) || nextCU < 0 || nextCU > 0xFFFF + ) { + // The code unit is not an integral number between 0 and + // 0xFFFF. + throw new RangeError( + `Piscēs: Code unit out of range: ${nextCU}.`, + ); + } else { + // The code unit is acceptable. + /* do nothing */ + } + } + return call(fromCharCode, undefined, codeUnits); + }, + { name: { value: "stringFromCodeUnits" }, length: { value: 1 } }, + ), }; })(); @@ -473,48 +549,6 @@ export const rawString = createArrowFunction(String.raw, { name: "rawString", }); -export const { - /** - * Returns a string created from the provided code units. - * - * ※ This is effectively an alias for `String.fromCharCode`, but - * with the same error behaviour as `String.fromCodePoint`. - * - * ☡ This function throws an error if provided with an argument which - * is not an integral number from 0 to FFFF₁₆ inclusive. - */ - stringFromCodeUnits, -} = (() => { - const { fromCharCode } = String; - const { isInteger: isIntegralNumber } = Number; - - return { - stringFromCodeUnits: defineOwnProperties( - (...codeUnits) => { - for (let index = 0; index < codeUnits.length; ++index) { - // Iterate over each provided code unit and throw if it is - // out of range. - const nextCU = +codeUnits[index]; - if ( - !isIntegralNumber(nextCU) || nextCU < 0 || nextCU > 0xFFFF - ) { - // The code unit is not an integral number between 0 and - // 0xFFFF. - throw new RangeError( - `Piscēs: Code unit out of range: ${nextCU}.`, - ); - } else { - // The code unit is acceptable. - /* do nothing */ - } - } - return call(fromCharCode, undefined, codeUnits); - }, - { name: { value: "stringFromCodeUnits" }, length: { value: 1 } }, - ), - }; -})(); - /** * Returns a string created from the provided codepoints. * diff --git a/string.test.js b/string.test.js index 1adcd08..bdef389 100644 --- a/string.test.js +++ b/string.test.js @@ -20,6 +20,7 @@ import { import { asciiLowercase, asciiUppercase, + canonicalNumericIndexString, characters, codepoints, codeUnits, @@ -28,6 +29,7 @@ import { getCodeUnit, getFirstSubstringIndex, getLastSubstringIndex, + isIntegerIndexString, join, Matcher, rawString, @@ -559,6 +561,59 @@ 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( @@ -879,6 +934,49 @@ describe("getLastSubstringIndex", () => { }); }); +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); + }); +}); + 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 5e9f052..4e46f63 100644 --- a/value.js +++ b/value.js @@ -51,25 +51,6 @@ 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. * @@ -171,9 +152,6 @@ export const { })(); export const { - /** Returns whether the provided value is an integer index string. */ - isIntegerIndexString, - /** * Returns whether the provided values are the same value. * @@ -203,25 +181,10 @@ export const { const { floor, max, min } = Math; const { MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER, - isInteger: isIntegralNumber, isNaN: isNan, } = Number; const { is } = Object; return { - isIntegerIndexString: ($) => { - const value = canonicalNumericIndexString($); - if (value !== undefined && isIntegralNumber(value)) { - // The provided value is an integral canonical numeric index - // string. - return sameValue(value, 0) || - value > 0 && value <= MAXIMUM_SAFE_INTEGRAL_NUMBER && - value === toLength(value); - } else { - // The provided value is not an integral canonical numeric - // index string. - return false; - } - }, sameValue: (a, b) => is(a, b), sameValueZero: ($1, $2) => { const type1 = type($1); diff --git a/value.test.js b/value.test.js index babd59d..d68ff65 100644 --- a/value.test.js +++ b/value.test.js @@ -15,10 +15,8 @@ import { } from "./dev-deps.js"; import { ASYNC_ITERATOR, - canonicalNumericIndexString, HAS_INSTANCE, IS_CONCAT_SPREADABLE, - isIntegerIndexString, ITERATOR, lengthOfArraylike, MATCH, @@ -127,102 +125,6 @@ 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("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); - }); -}); - describe("lengthOfArraylike", () => { it("[[Call]] returns the length", () => { assertStrictEquals(