1 // SPDX-FileCopyrightText: 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
2 // SPDX-License-Identifier: MPL-2.0
4 * ⁌ ♓🧩 Piscēs ∷ value.js
6 * Copyright © 2022–2023, 2025 Lady [@ Ladys Computer].
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
13 const PISC
ĒS
= "♓🧩 Piscēs";
16 /** The welknown `@@asyncIterator´ symbol. */
17 asyncIterator: ASYNC_ITERATOR
,
19 /** The welknown `@@hasInstance´ symbol. */
20 hasInstance: HAS_INSTANCE
,
22 /** The welknown `@@isConcatSpreadable´ symbol. */
23 isConcatSpreadable: IS_CONCAT_SPREADABLE
,
25 /** The welknown `@@iterator´ symbol. */
28 /** The welknown `@@match´ symbol. */
31 /** The welknown `@@matchAll´ symbol. */
34 /** The welknown `@@replace´ symbol. */
37 /** The welknown `@@species´ symbol. */
40 /** The welknown `@@split´ symbol. */
43 /** The welknown `@@toPrimitive´ symbol. */
44 toPrimitive: TO_PRIMITIVE
,
46 /** The welknown `@@toStringTag´ symbol. */
47 toStringTag: TO_STRING_TAG
,
49 /** The welknown `@@unscopables´ symbol. */
50 unscopables: UNSCOPABLES
,
57 * ※ This is an alias for `Math.LN10´.
64 * ※ This is an alias for `Math.LN2´.
71 * ※ This is an alias for `Math.LOG10E´.
78 * ※ This is an alias for `Math.LOG2E´.
85 * ※ This is an alias for `Math.SQRT1_2´.
87 SQRT1_2: RECIPROCAL_SQRT_2
,
92 * ※ This is an alias for `Math.SQRT2´.
97 * The mathematical constant 𝑒.
99 * ※ This is an alias for `Math.E´.
104 * The mathematical constant 𝜋.
106 * ※ This is an alias for `Math.PI´.
113 * The largest number value less than infinity.
115 * ※ This is an alias for `Number.MAX_VALUE´.
117 MAX_VALUE: MAXIMUM_NUMBER
,
122 * ※ This is an alias for `Number.MAX_SAFE_INTEGER´.
124 MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER
,
127 * The smallest number value greater than negative infinity.
129 * ※ This is an alias for `Number.MIN_VALUE´.
131 MIN_VALUE: MINIMUM_NUMBER
,
136 * ※ This is an alias for `Number.MIN_SAFE_INTEGER´.
138 MIN_SAFE_INTEGER: MINIMUM_SAFE_INTEGRAL_NUMBER
,
143 * ※ This is an alias for `Number.NEGATIVE_INFINITY´.
150 * ※ This is an alias for `Number.NaN´.
157 * ※ This is an alias for `Number.POSITIVE_INFINITY´.
162 * The difference between 1 and the smallest number greater than 1.
164 * ※ This is an alias for `Number.EPSILON´.
169 /** Negative zero. */
170 export const NEGATIVE_ZERO
= -0;
172 /** The null primitive. */
173 export const NULL
= null;
175 /** Positive zero. */
176 export const POSITIVE_ZERO
= 0;
178 /** The undefined primitive. */
179 export const UNDEFINED
= undefined;
182 * Returns −0 if the provided argument is `"-0"´; returns a number
183 * representing the index if the provided argument is a canonical
184 * numeric index string; otherwise, returns undefined.
186 * There is no clamping of the numeric index, but note that numbers
187 * above 2^53 − 1 are not safe nor valid integer indices.
189 export const canonicalNumericIndexString
= ($) => {
190 if (typeof $ !== "string") {
192 } else if ($ === "-0") {
196 return $ === `${n}` ? n : UNDEFINED
;
201 * Completes the provided property descriptor by setting missing values
204 * ※ This method modifies the provided object and returns undefined.
206 export const completePropertyDescriptor
= (Desc
) => {
207 if (Desc
=== UNDEFINED
) {
208 // A description was not provided; this is an error.
210 `${PISCĒS}: Cannot complete undefined property descriptor.`,
212 } else if (!("get" in Desc
|| "set" in Desc
)) {
213 // This is a generic or data descriptor.
214 if (!("value" in Desc
)) {
215 // `value´ is not defined on this.
216 Desc
.value
= UNDEFINED
;
218 // `value´ is already defined on this.
221 if (!("writable" in Desc
)) {
222 // `writable´ is not defined on this.
223 Desc
.writable
= false;
225 // `writable´ is already defined on this.
229 // This is not a generic or data descriptor.
230 if (!("get" in Desc
)) {
231 // `get´ is not defined on this.
232 Desc
.get = UNDEFINED
;
234 // `get´ is already defined on this.
237 if (!("set" in Desc
)) {
238 // `set´ is not defined on this.
239 Desc
.set = UNDEFINED
;
241 // `set´ is already defined on this.
245 if (!("enumerable" in Desc
)) {
246 // `enumerable´ is not defined on this.
247 Desc
.enumerable
= false;
249 // `enumerable´ is already defined on this.
252 if (!("configurable" in Desc
)) {
253 // `configurable´ is not defined on this.
254 Desc
.configurable
= false;
256 // `configurable´ is already defined on this.
261 /** Gets whether the provided value is an accessor descrtiptor. */
262 export const isAccessorDescriptor
= (Desc
) =>
263 Desc
!== UNDEFINED
&& ("get" in Desc
|| "set" in Desc
);
265 /** Returns whether the provided value is an array index. */
266 export const isArrayIndexString
= ($) => {
267 const value
= canonicalNumericIndexString($);
268 if (value
!== UNDEFINED
) {
269 // The provided value is a canonical numeric index string.
271 // Return whether it is in range for array indices.
272 return sameValue(value
, 0)
273 || value
=== toLength(value
) && value
> 0 && value
< -1 >>> 0;
275 // The provided value is not a canonical numeric index string.
280 /** Gets whether the provided value is a data descrtiptor. */
281 export const isDataDescriptor
= (Desc
) =>
282 Desc
!== UNDEFINED
&& ("value" in Desc
|| "writable" in Desc
);
285 * Gets whether the provided value is a fully‐populated property
288 export const isFullyPopulatedDescriptor
= (Desc
) =>
290 && ("value" in Desc
&& "writable" in Desc
291 || "get" in Desc
&& "set" in Desc
)
292 && "enumerable" in Desc
&& "configurable" in Desc
;
295 * Gets whether the provided value is a generic (not accessor or data)
298 export const isGenericDescriptor
= (Desc
) =>
300 && !("get" in Desc
|| "set" in Desc
|| "value" in Desc
301 || "writable" in Desc
);
303 /** Returns whether the provided value is an integer index string. */
304 export const isIntegerIndexString
= ($) => {
305 const value
= canonicalNumericIndexString($);
306 if (value
!== UNDEFINED
) {
307 // The provided value is a canonical numeric index string.
309 // Return whether it is in range for integer indices.
310 return sameValue(value
, 0)
311 || value
=== toLength(value
) && value
> 0;
313 // The provided value is not a canonical numeric index string.
320 * Returns the primitive value of the provided object per its
321 * `.toString´ and `.valueOf´ methods.
323 * If the provided hint is "string", then `.toString´ takes
324 * precedence; otherwise, `.valueOf´ does.
326 * Throws an error if both of these methods are not callable or do
327 * not return a primitive.
332 * Returns a string function name generated from the provided value
333 * and optional prefix.
338 * Returns the provided value converted to a primitive, or throws if
339 * no such conversion is possible.
341 * The provided preferred type, if specified, should be "string",
342 * "number", or "default". If the provided input has a
343 * `.[Symbol.toPrimitive]´ method, this function will throw rather
344 * than calling that method with a preferred type other than one of
349 const { apply: call
} = Reflect
;
350 const getSymbolDescription
= Object
.getOwnPropertyDescriptor(
356 ordinaryToPrimitive: (O
, hint
) => {
357 const methodNames
= hint
== "string"
358 ? ["toString", "valueOf"]
359 : ["valueOf", "toString"];
360 for (let index
= 0; index
< methodNames
.length
; ++index
) {
361 // Test the methods in the order determined above (based on the
362 // hint) and return the result if the method returns a
365 // ☡ If this loop exits with·out returning, it is an error.
366 const method
= O
[methodNames
[index
]];
367 if (typeof method
=== "function") {
368 // Method is callable.
369 const result
= call(method
, O
, []);
370 if (type(result
) !== "object") {
371 // Method returns a primitive.
374 // Method returns an object.
378 // Method is not callable.
383 `${PISCĒS}: Unable to convert object to primitive.`,
386 toFunctionName: ($, prefix
= UNDEFINED
) => {
387 const key
= toPrimitive($, "string");
388 const name
= (() => {
389 if (typeof key
=== "symbol") {
390 // The provided value is a symbol.
392 // Format its description.
393 const description
= call(getSymbolDescription
, key
, []);
394 return description
=== UNDEFINED
? "" : `[${description}]`;
396 // The provided value not a symbol.
398 // Convert it to a string property key.
402 return prefix
!== UNDEFINED
? `${prefix} ${name}` : name
;
404 toPrimitive: ($, preferredType
= "default") => {
405 const hint
= `${preferredType}`;
407 "default" !== hint
&& "string" !== hint
410 // An invalid preferred type was specified.
412 `${PISCĒS}: Invalid preferred type: ${preferredType}.`,
414 } else if (type($) === "object") {
415 // The provided value is an object.
416 const exoticToPrim
= $[TO_PRIMITIVE
] ?? UNDEFINED
;
417 if (exoticToPrim
!== UNDEFINED
) {
418 // The provided value has an exotic primitive conversion
420 if (typeof exoticToPrim
!== "function") {
421 // The method is not callable.
423 `${PISCĒS}: .[Symbol.toPrimitive] was neither nullish nor callable.`,
426 // The method is callable.
427 return call(exoticToPrim
, $, [hint
]);
430 // Use the ordinary primitive conversion function.
431 return ordinaryToPrimitive($, hint
);
434 // The provided value is already a primitive.
443 * Returns whether the provided values are the same value.
445 * ※ This differs from `===´ in the cases of nan and zero.
450 * Returns whether the provided values are either the same value or
451 * both zero (either positive or negative).
453 * ※ This differs from `===´ in the case of nan.
458 * Returns the result of converting the provided value to an index,
459 * or throws an error if it is out of range.
464 * Returns the result of converting the provided value to a length.
468 const { floor
, max
, min
} = Math
;
469 const { isNaN: isNan
} = Number
;
470 const { is
} = Object
;
472 sameValue: ($1, $2) => is($1, $2),
473 sameValueZero: ($1, $2) => {
474 const type1
= type($1);
475 const type2
= type($2);
476 if (type1
!== type2
) {
477 // The provided values are not of the same type.
479 } else if (type1
=== "number") {
480 // The provided values are numbers.
482 // Check if they are nan and use strict equality otherwise.
483 return isNan($1) && isNan($2) || $1 === $2;
485 // The provided values are not numbers.
487 // Use strict equality.
492 const integer
= floor($);
493 if (isNan(integer
) || integer
== 0) {
494 // The value is zero·like.
496 // Return positive zero.
499 // The value is not zero·like.
500 const clamped
= toLength(integer
);
501 if (clamped
!== integer
) {
502 // Clamping the value changes it; this is an error.
503 throw new RangeError(`${PISCĒS}: Index out of range: ${$}.`);
505 // The value is within appropriate bounds.
513 const len
= floor($);
514 return isNan(len
) || len
== 0
516 : max(min(len
, MAXIMUM_SAFE_INTEGRAL_NUMBER
), 0);
522 * Returns the property key (symbol or string) corresponding to the
525 export const toPropertyKey
= ($) => {
526 const key
= toPrimitive($, "string");
527 return typeof key
=== "symbol" ? key : `${key}`;
531 * Returns a lowercase string identifying the type of the provided
534 * This differs from the value of the `typeof´ operator only in the
535 * cases of callable objects and null.
537 export const type
= ($) => {
539 // The provided value is null.
542 // The provided value is not null.
543 const type
·of = typeof $;
544 return type
·of === "function" ? "object" : type
·of;