]> Lady’s Gitweb - Pisces/blobdiff - string.js
Add methods for own entries and values to object.js
[Pisces] / string.js
index 774261dbdf90f8a8e49e38e51f4e61069d318a6a..e8126d14fa44be9155723de05b4720403ee327f9 100644 (file)
--- a/string.js
+++ b/string.js
@@ -19,13 +19,14 @@ import {
   stringIteratorFunction,
 } from "./iterable.js";
 import {
+  defineOwnDataProperty,
   defineOwnProperties,
   getOwnPropertyDescriptors,
-  getPrototype,
   objectCreate,
+  setPropertyValues,
   setPrototype,
 } from "./object.js";
-import { ITERATOR } from "./value.js";
+import { sameValue, toLength, UNDEFINED } from "./value.js";
 
 const RE = RegExp;
 const { prototype: rePrototype } = RE;
@@ -96,7 +97,7 @@ export const {
      * expression with `^(?:` and `)$` if you don’t want nongreedy
      * regular expressions to fail when shorter matches are possible.
      */
-    constructor(source, name = undefined, constraint = null) {
+    constructor(source, name = UNDEFINED, constraint = null) {
       super(
         ($) => {
           if (typeof $ !== "string") {
@@ -141,17 +142,19 @@ export const {
         return defineOwnProperties(
           setPrototype(this, matcherPrototype),
           {
-            lastIndex: {
+            lastIndex: setPropertyValues(objectCreate(null), {
               configurable: false,
               enumerable: false,
               value: 0,
               writable: false,
-            },
-            name: {
-              value: name != null
+            }),
+            name: defineOwnDataProperty(
+              objectCreate(null),
+              "value",
+              name != null
                 ? `${name}`
                 : `Matcher(${call(reToString, regExp, [])})`,
-            },
+            ),
           },
         );
       }
@@ -249,21 +252,31 @@ export const {
     }
   };
 
-  const matcherConstructor = defineOwnProperties(
+  const matcherConstructor = Object.defineProperties(
     class extends RegExp {
       constructor(...args) {
         return new Matcher(...args);
       }
     },
     {
-      name: { value: "Matcher" },
-      length: { value: 1 },
+      name: defineOwnDataProperty(
+        Object.create(null),
+        "value",
+        "Matcher",
+      ),
+      length: defineOwnDataProperty(Object.create(null), "value", 1),
     },
   );
   const matcherPrototype = defineOwnProperties(
     matcherConstructor.prototype,
     getOwnPropertyDescriptors(Matcher.prototype),
-    { constructor: { value: matcherConstructor } },
+    {
+      constructor: defineOwnDataProperty(
+        Object.create(null),
+        "value",
+        matcherConstructor,
+      ),
+    },
   );
 
   return { Matcher: matcherConstructor };
@@ -302,6 +315,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
@@ -330,13 +362,6 @@ export const {
    * with U+FFFD.
    */
   scalarValues,
-
-  /**
-   * Returns the result of converting the provided value to a string of
-   * scalar values by replacing (unpaired) surrogate values with
-   * U+FFFD.
-   */
-  scalarValueString,
 } = (() => {
   const generateCharacters = function* (character) {
     yield character;
@@ -368,31 +393,12 @@ export const {
     bind(generateCodepoints, { allowSurrogates: false }, []),
     "String Scalar Value Iterator",
   );
-  const {
-    next: scalarValuesNext,
-  } = getPrototype(scalarValuesIterator(""));
-  const scalarValueIterablePrototype = {
-    [ITERATOR]() {
-      return {
-        next: bind(
-          scalarValuesNext,
-          scalarValuesIterator(this.source),
-          [],
-        ),
-      };
-    },
-  };
 
   return {
     characters: ($) => charactersIterator(`${$}`),
     codeUnits: ($) => codeUnitsIterator(`${$}`),
     codepoints: ($) => codepointsIterator(`${$}`),
     scalarValues: ($) => scalarValuesIterator(`${$}`),
-    scalarValueString: ($) =>
-      stringFromCodepoints(...objectCreate(
-        scalarValueIterablePrototype,
-        { source: { value: `${$}` } },
-      )),
   };
 })();
 
@@ -404,19 +410,30 @@ export const {
 export const getCharacter = ($, pos) => {
   const codepoint = getCodepoint($, pos);
   return codepoint == null
-    ? undefined
+    ? UNDEFINED
     : 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 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
@@ -429,18 +446,45 @@ export const {
    */
   stringCatenate,
 } = (() => {
+  const { fromCharCode } = String;
   const { charCodeAt, concat } = String.prototype;
-  const { isNaN: isNan } = Number;
+  const {
+    isInteger: isIntegralNumber,
+    isNaN: isNan,
+  } = Number;
 
   return {
     getCodeUnit: ($, n) => {
       const codeUnit = call(charCodeAt, $, [n]);
-      return isNan(codeUnit) ? undefined : codeUnit;
+      return isNan(codeUnit) ? UNDEFINED : codeUnit;
     },
-    stringCatenate: defineOwnProperties(
+    stringCatenate: Object.defineProperties(
       (...args) => call(concat, "", args),
       { name: { value: "stringCatenate" }, length: { value: 2 } },
     ),
+    stringFromCodeUnits: Object.defineProperties(
+      (...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 } },
+    ),
   };
 })();
 
@@ -474,6 +518,34 @@ 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.
  *
@@ -487,7 +559,7 @@ export const join = (() => {
     call(
       arrayJoin,
       [...$],
-      [separator === undefined ? "," : `${separator}`],
+      [separator === UNDEFINED ? "," : `${separator}`],
     );
   return join;
 })();
@@ -502,48 +574,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.
  *
@@ -558,19 +588,19 @@ export const stringFromCodepoints = createArrowFunction(
 );
 
 /**
- * Returns the result of splitting the provided value on A·S·C·I·I
+ * Returns the result of splitting the provided value on Ascii
  * whitespace.
  */
-export const splitOnASCIIWhitespace = ($) =>
-  stringSplit(stripAndCollapseASCIIWhitespace($), " ");
+export const splitOnAsciiWhitespace = ($) =>
+  stringSplit(stripAndCollapseAsciiWhitespace($), " ");
 
 /**
  * Returns the result of splitting the provided value on commas,
- * trimming A·S·C·I·I whitespace from the resulting tokens.
+ * trimming Ascii whitespace from the resulting tokens.
  */
 export const splitOnCommas = ($) =>
   stringSplit(
-    stripLeadingAndTrailingASCIIWhitespace(
+    stripLeadingAndTrailingAsciiWhitespace(
       stringReplaceAll(
         `${$}`,
         /[\n\r\t\f ]*,[\n\r\t\f ]*/gu,
@@ -732,12 +762,12 @@ export const stringValue = createCallableFunction(
 );
 
 /**
- * Returns the result of stripping leading and trailing A·S·C·I·I
- * whitespace from the provided value and collapsing other A·S·C·I·I
+ * Returns the result of stripping leading and trailing Ascii
+ * whitespace from the provided value and collapsing other Ascii
  * whitespace in the string representation of the provided value.
  */
-export const stripAndCollapseASCIIWhitespace = ($) =>
-  stripLeadingAndTrailingASCIIWhitespace(
+export const stripAndCollapseAsciiWhitespace = ($) =>
+  stripLeadingAndTrailingAsciiWhitespace(
     stringReplaceAll(
       `${$}`,
       /[\n\r\t\f ]+/gu,
@@ -746,10 +776,10 @@ export const stripAndCollapseASCIIWhitespace = ($) =>
   );
 
 /**
- * Returns the result of stripping leading and trailing A·S·C·I·I
+ * Returns the result of stripping leading and trailing Ascii
  * whitespace from the string representation of the provided value.
  */
-export const stripLeadingAndTrailingASCIIWhitespace = ($) =>
+export const stripLeadingAndTrailingAsciiWhitespace = ($) =>
   call(reExec, /^[\n\r\t\f ]*([^]*?)[\n\r\t\f ]*$/u, [$])[1];
 
 /**
@@ -760,6 +790,16 @@ export const substring = createCallableFunction(
   stringPrototype.substring,
 );
 
+/**
+ * Returns the result of converting the provided value to a string of
+ * scalar values by replacing (unpaired) surrogate values with
+ * U+FFFD.
+ */
+export const toScalarValueString = createCallableFunction(
+  String.prototype.toWellFormed,
+  { name: "toScalarValueString" },
+);
+
 /**
  * Returns the result of converting the provided value to a string.
  *
This page took 0.065141 seconds and 4 git commands to generate.