X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/8c273de93ce5adb58bb90c3fe676730a451b3c89..9c6be9646631101a3883d4dac08495d5e35e9977:/value.js?ds=sidebyside diff --git a/value.js b/value.js index 5e9f052..6956b6f 100644 --- a/value.js +++ b/value.js @@ -1,111 +1,307 @@ -// ♓🌟 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 . +// SPDX-FileCopyrightText: 2022, 2023, 2025 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 . + */ + +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) {