]> Lady’s Gitweb - Pisces/blobdiff - value.js
Make minor improvements to function.js
[Pisces] / value.js
index 5e9f0524c9419291802716602f1080c518db1ad8..6956b6fc6d45945e52f5234ae43b35ebb3d73da3 100644 (file)
--- a/value.js
+++ b/value.js
-// ♓🌟 Piscēs ∷ value.js
-// ====================================================================
-//
-// Copyright © 2022‐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 <https://mozilla.org/MPL/2.0/>.
+// SPDX-FileCopyrightText: 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
+// SPDX-License-Identifier: MPL-2.0
+/**
+ * ⁌ ♓🧩 Piscēs ∷ value.js
+ *
+ * Copyright © 2022–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 <https://mozilla.org/MPL/2.0/>.
+ */
+
+const PISCĒS = "♓🧩 Piscēs";
 
 export const {
-  /** The welknown `@@asyncIterator` symbol. */
+  /** The welknown `@@asyncIterator´ symbol. */
   asyncIterator: ASYNC_ITERATOR,
 
-  /** The welknown `@@hasInstance` symbol. */
+  /** The welknown `@@hasInstance´ symbol. */
   hasInstance: HAS_INSTANCE,
 
-  /** The welknown `@@isConcatSpreadable` symbol. */
+  /** The welknown `@@isConcatSpreadable´ symbol. */
   isConcatSpreadable: IS_CONCAT_SPREADABLE,
 
-  /** The welknown `@@iterator` symbol. */
+  /** The welknown `@@iterator´ symbol. */
   iterator: ITERATOR,
 
-  /** The welknown `@@match` symbol. */
+  /** The welknown `@@match´ symbol. */
   match: MATCH,
 
-  /** The welknown `@@matchAll` symbol. */
+  /** The welknown `@@matchAll´ symbol. */
   matchAll: MATCH_ALL,
 
-  /** The welknown `@@replace` symbol. */
+  /** The welknown `@@replace´ symbol. */
   replace: REPLACE,
 
-  /** The welknown `@@species` symbol. */
+  /** The welknown `@@species´ symbol. */
   species: SPECIES,
 
-  /** The welknown `@@split` symbol. */
+  /** The welknown `@@split´ symbol. */
   split: SPLIT,
 
-  /** The welknown `@@toPrimitive` symbol. */
+  /** The welknown `@@toPrimitive´ symbol. */
   toPrimitive: TO_PRIMITIVE,
 
-  /** The welknown `@@toStringTag` symbol. */
+  /** The welknown `@@toStringTag´ symbol. */
   toStringTag: TO_STRING_TAG,
 
-  /** The welknown `@@unscopables` symbol. */
+  /** The welknown `@@unscopables´ symbol. */
   unscopables: UNSCOPABLES,
 } = Symbol;
 
+export const {
+  /**
+   * ln(10).
+   *
+   * ※ This is an alias for `Math.LN10´.
+   */
+  LN10: LN_10,
+
+  /**
+   * ln(2).
+   *
+   * ※ This is an alias for `Math.LN2´.
+   */
+  LN2: LN_2,
+
+  /**
+   * log10(e).
+   *
+   * ※ This is an alias for `Math.LOG10E´.
+   */
+  LOG10E: LOG10_𝑒,
+
+  /**
+   * log2(e).
+   *
+   * ※ This is an alias for `Math.LOG2E´.
+   */
+  LOG2E: LOG2_𝑒,
+
+  /**
+   * sqrt(½).
+   *
+   * ※ This is an alias for `Math.SQRT1_2´.
+   */
+  SQRT1_2: RECIPROCAL_SQRT_2,
+
+  /**
+   * sqrt(2).
+   *
+   * ※ This is an alias for `Math.SQRT2´.
+   */
+  SQRT2: SQRT_2,
+
+  /**
+   * The mathematical constant 𝑒.
+   *
+   * ※ This is an alias for `Math.E´.
+   */
+  E: 𝑒,
+
+  /**
+   * The mathematical constant 𝜋.
+   *
+   * ※ This is an alias for `Math.PI´.
+   */
+  PI: 𝜋,
+} = Math;
+
+export const {
+  /**
+   * The largest number value less than infinity.
+   *
+   * ※ This is an alias for `Number.MAX_VALUE´.
+   */
+  MAX_VALUE: MAXIMUM_NUMBER,
+
+  /**
+   * 2^53 - 1.
+   *
+   * ※ This is an alias for `Number.MAX_SAFE_INTEGER´.
+   */
+  MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER,
+
+  /**
+   * The smallest number value greater than negative infinity.
+   *
+   * ※ This is an alias for `Number.MIN_VALUE´.
+   */
+  MIN_VALUE: MINIMUM_NUMBER,
+
+  /**
+   * -(2^53 - 1).
+   *
+   * ※ This is an alias for `Number.MIN_SAFE_INTEGER´.
+   */
+  MIN_SAFE_INTEGER: MINIMUM_SAFE_INTEGRAL_NUMBER,
+
+  /**
+   * Negative infinity.
+   *
+   * ※ This is an alias for `Number.NEGATIVE_INFINITY´.
+   */
+  NEGATIVE_INFINITY,
+
+  /**
+   * Nan.
+   *
+   * ※ This is an alias for `Number.NaN´.
+   */
+  NaN: NAN,
+
+  /**
+   * Positive infinity.
+   *
+   * ※ This is an alias for `Number.POSITIVE_INFINITY´.
+   */
+  POSITIVE_INFINITY,
+
+  /**
+   * The difference between 1 and the smallest number greater than 1.
+   *
+   * ※ This is an alias for `Number.EPSILON´.
+   */
+  EPSILON: 𝜀,
+} = Number;
+
+/** Negative zero. */
+export const NEGATIVE_ZERO = -0;
+
 /** The null primitive. */
 export const NULL = null;
 
+/** Positive zero. */
+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.
+ * Completes the provided property descriptor by setting missing values
+ * to their defaults.
  *
- * There is no clamping of the numeric index, but note that numbers
- * above 2^53 − 1 are not safe nor valid integer indices.
+ * ※ This method modifies the provided object and returns undefined.
  */
-export const canonicalNumericIndexString = ($) => {
-  if (typeof $ !== "string") {
-    return undefined;
-  } else if ($ === "-0") {
-    return -0;
+export const completePropertyDescriptor = (Desc) => {
+  if (Desc === UNDEFINED) {
+    // A description was not provided; this is an error.
+    throw new TypeError(
+      `${PISCĒS}: Cannot complete undefined property descriptor.`,
+    );
+  } else if (!("get" in Desc || "set" in Desc)) {
+    // This is a generic or data descriptor.
+    if (!("value" in Desc)) {
+      // `value´ is not defined on this.
+      Desc.value = UNDEFINED;
+    } else {
+      // `value´ is already defined on this.
+      /* do nothing */
+    }
+    if (!("writable" in Desc)) {
+      // `writable´ is not defined on this.
+      Desc.writable = false;
+    } else {
+      // `writable´ is already defined on this.
+      /* do nothing */
+    }
+  } else {
+    // This is not a generic or data descriptor.
+    if (!("get" in Desc)) {
+      // `get´ is not defined on this.
+      Desc.get = UNDEFINED;
+    } else {
+      // `get´ is already defined on this.
+      /* do nothing */
+    }
+    if (!("set" in Desc)) {
+      // `set´ is not defined on this.
+      Desc.set = UNDEFINED;
+    } else {
+      // `set´ is already defined on this.
+      /* do nothing */
+    }
+  }
+  if (!("enumerable" in Desc)) {
+    // `enumerable´ is not defined on this.
+    Desc.enumerable = false;
   } else {
-    const n = +$;
-    return $ === `${n}` ? n : undefined;
+    // `enumerable´ is already defined on this.
+    /* do nothing */
+  }
+  if (!("configurable" in Desc)) {
+    // `configurable´ is not defined on this.
+    Desc.configurable = false;
+  } else {
+    // `configurable´ is already defined on this.
+    /* do nothing */
   }
 };
 
+/** Gets whether the provided value is an accessor descrtiptor. */
+export const isAccessorDescriptor = (Desc) =>
+  Desc !== UNDEFINED && ("get" in Desc || "set" in Desc);
+
+/** Gets whether the provided value is a data descrtiptor. */
+export const isDataDescriptor = (Desc) =>
+  Desc !== UNDEFINED && ("value" in Desc || "writable" in Desc);
+
 /**
- * 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.
+ * Gets whether the provided value is a fully‐populated property
+ * descriptor.
  */
-export const lengthOfArraylike = ({ length }) => toLength(length);
+export const isFullyPopulatedDescriptor = (Desc) =>
+  Desc !== UNDEFINED
+  && ("value" in Desc && "writable" in Desc
+    || "get" in Desc && "set" in Desc)
+  && "enumerable" in Desc && "configurable" in Desc;
+
+/**
+ * Gets whether the provided value is a generic (not accessor or data)
+ * descrtiptor.
+ */
+export const isGenericDescriptor = (Desc) =>
+  Desc !== UNDEFINED
+  && !("get" in Desc || "set" in Desc || "value" in Desc
+    || "writable" in Desc);
 
 export const {
   /**
    * Returns the primitive value of the provided object per its
-   * `.toString` and `.valueOf` methods.
+   * `.toString´ and `.valueOf´ methods.
    *
-   * If the provided hint is "string", then `.toString` takes
-   * precedence; otherwise, `.valueOf` does.
+   * If the provided hint is "string", then `.toString´ takes
+   * precedence; otherwise, `.valueOf´ does.
    *
    * Throws an error if both of these methods are not callable or do
    * not return a primitive.
    */
   ordinaryToPrimitive,
 
+  /**
+   * Returns a string function name generated from the provided value
+   * and optional prefix.
+   */
+  toFunctionName,
+
   /**
    * Returns the provided value converted to a primitive, or throws if
    * no such conversion is possible.
    *
    * The provided preferred type, if specified, should be "string",
    * "number", or "default". If the provided input has a
-   * `.[Symbol.toPrimitive]` method, this function will throw rather
+   * `.[Symbol.toPrimitive]´ method, this function will throw rather
    * than calling that method with a preferred type other than one of
    * the above.
    */
   toPrimitive,
 } = (() => {
   const { apply: call } = Reflect;
+  const getSymbolDescription = Object.getOwnPropertyDescriptor(
+    Symbol.prototype,
+    "description",
+  ).get;
 
   return {
     ordinaryToPrimitive: (O, hint) => {
@@ -113,6 +309,11 @@ export const {
         ? ["toString", "valueOf"]
         : ["valueOf", "toString"];
       for (let index = 0; index < methodNames.length; ++index) {
+        // Test the methods in the order determined above (based on the
+        // hint) and return the result if the method returns a
+        // primitive.
+        //
+        // ☡ If this loop exits with·out returning, it is an error.
         const method = O[methodNames[index]];
         if (typeof method === "function") {
           // Method is callable.
@@ -130,29 +331,47 @@ export const {
         }
       }
       throw new TypeError(
-        "Piscēs: Unable to convert object to primitive",
+        `${PISCĒS}: Unable to convert object to primitive.`,
       );
     },
+    toFunctionName: ($, prefix = UNDEFINED) => {
+      const key = toPrimitive($, "string");
+      const name = (() => {
+        if (typeof key === "symbol") {
+          // The provided value is a symbol.
+          //
+          // Format its description.
+          const description = call(getSymbolDescription, key, []);
+          return description === UNDEFINED ? "" : `[${description}]`;
+        } else {
+          // The provided value not a symbol.
+          //
+          // Convert it to a string property key.
+          return `${key}`;
+        }
+      })();
+      return prefix !== UNDEFINED ? `${prefix} ${name}` : name;
+    },
     toPrimitive: ($, preferredType = "default") => {
       const hint = `${preferredType}`;
       if (
-        "default" !== hint && "string" !== hint &&
-        "number" !== hint
+        "default" !== hint && "string" !== hint
+        && "number" !== hint
       ) {
         // An invalid preferred type was specified.
         throw new TypeError(
-          `Piscēs: Invalid preferred type: ${preferredType}.`,
+          `${PISCĒS}: Invalid preferred type: ${preferredType}.`,
         );
       } else if (type($) === "object") {
         // The provided value is an object.
-        const exoticToPrim = $[TO_PRIMITIVE] ?? undefined;
-        if (exoticToPrim !== undefined) {
+        const exoticToPrim = $[TO_PRIMITIVE] ?? UNDEFINED;
+        if (exoticToPrim !== UNDEFINED) {
           // The provided value has an exotic primitive conversion
           // method.
           if (typeof exoticToPrim !== "function") {
             // The method is not callable.
             throw new TypeError(
-              "Piscēs: `.[Symbol.toPrimitive]` was neither nullish nor callable.",
+              `${PISCĒS}: .[Symbol.toPrimitive] was neither nullish nor callable.`,
             );
           } else {
             // The method is callable.
@@ -171,13 +390,10 @@ export const {
 })();
 
 export const {
-  /** Returns whether the provided value is an integer index string. */
-  isIntegerIndexString,
-
   /**
    * Returns whether the provided values are the same value.
    *
-   * ※ This differs from `===` in the cases of nan and zero.
+   * ※ This differs from `===´ in the cases of nan and zero.
    */
   sameValue,
 
@@ -185,7 +401,7 @@ export const {
    * Returns whether the provided values are either the same value or
    * both zero (either positive or negative).
    *
-   * ※ This differs from `===` in the case of nan.
+   * ※ This differs from `===´ in the case of nan.
    */
   sameValueZero,
 
@@ -201,27 +417,9 @@ export const {
   toLength,
 } = (() => {
   const { floor, max, min } = Math;
-  const {
-    MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER,
-    isInteger: isIntegralNumber,
-    isNaN: isNan,
-  } = Number;
+  const { 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);
@@ -230,11 +428,14 @@ export const {
         // The provided values are not of the same type.
         return false;
       } else if (type1 === "number") {
-        // The provided values are numbers; check if they are nan and
-        // use strict equality otherwise.
+        // The provided values are numbers.
+        //
+        // Check if they are nan and use strict equality otherwise.
         return isNan($1) && isNan($2) || $1 === $2;
       } else {
-        // The provided values are not numbers; use strict equality.
+        // The provided values are not numbers.
+        //
+        // Use strict equality.
         return $1 === $2;
       }
     },
@@ -242,15 +443,19 @@ export const {
       const integer = floor($);
       if (isNan(integer) || integer == 0) {
         // The value is zero·like.
+        //
+        // Return positive zero.
         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: ${$}.`);
+          // Clamping the value changes it; this is an error.
+          throw new RangeError(`${PISCĒS}: Index out of range: ${$}.`);
         } else {
           // The value is within appropriate bounds.
+          //
+          // Return it.
           return integer;
         }
       }
@@ -264,12 +469,21 @@ export const {
   };
 })();
 
+/**
+ * Returns the property key (symbol or string) corresponding to the
+ * provided value.
+ */
+export const toPropertyKey = ($) => {
+  const key = toPrimitive($, "string");
+  return typeof key === "symbol" ? key : `${key}`;
+};
+
 /**
  * Returns a lowercase string identifying the type of the provided
  * value.
  *
- * This differs from the value of the `typeof` operator only in the
- * cases of objects and null.
+ * This differs from the value of the `typeof´ operator only in the
+ * cases of callable objects and null.
  */
 export const type = ($) => {
   if ($ === null) {
This page took 0.344273 seconds and 4 git commands to generate.