1 // ♓🌟 Piscēs ∷ value.js
2 // ====================================================================
4 // Copyright © 2022‐2023 Lady [@ Lady’s Computer].
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
11 /** The welknown `@@asyncIterator` symbol. */
12 asyncIterator
: ASYNC_ITERATOR
,
14 /** The welknown `@@hasInstance` symbol. */
15 hasInstance
: HAS_INSTANCE
,
17 /** The welknown `@@isConcatSpreadable` symbol. */
18 isConcatSpreadable
: IS_CONCAT_SPREADABLE
,
20 /** The welknown `@@iterator` symbol. */
23 /** The welknown `@@match` symbol. */
26 /** The welknown `@@matchAll` symbol. */
29 /** The welknown `@@replace` symbol. */
32 /** The welknown `@@species` symbol. */
35 /** The welknown `@@split` symbol. */
38 /** The welknown `@@toPrimitive` symbol. */
39 toPrimitive
: TO_PRIMITIVE
,
41 /** The welknown `@@toStringTag` symbol. */
42 toStringTag
: TO_STRING_TAG
,
44 /** The welknown `@@unscopables` symbol. */
45 unscopables
: UNSCOPABLES
,
52 * ※ This is an alias for `Math.LN10`.
59 * ※ This is an alias for `Math.LN2`.
66 * ※ This is an alias for `Math.LOG10E`.
73 * ※ This is an alias for `Math.LOG2E`.
80 * ※ This is an alias for `Math.SQRT1_2`.
82 SQRT1_2
: RECIPROCAL_SQRT2
,
87 * ※ This is an alias for `Math.SQRT2`.
92 * The mathematical constant π.
94 * ※ This is an alias for `Math.PI`.
101 * ※ This is an alias for `Math.E`.
108 * The largest number value less than infinity.
110 * ※ This is an alias for `Number.MAX_VALUE`.
112 MAX_VALUE
: MAXIMUM_NUMBER
,
117 * ※ This is an alias for `Number.MAX_SAFE_INTEGER`.
119 MAX_SAFE_INTEGER
: MAXIMUM_SAFE_INTEGRAL_NUMBER
,
122 * The smallest number value greater than negative infinity.
124 * ※ This is an alias for `Number.MIN_VALUE`.
126 MIN_VALUE
: MINIMUM_NUMBER
,
131 * ※ This is an alias for `Number.MIN_SAFE_INTEGER`.
133 MIN_SAFE_INTEGER
: MINIMUM_SAFE_INTEGRAL_NUMBER
,
138 * ※ This is an alias for `Number.NEGATIVE_INFINITY`.
145 * ※ This is an alias for `Number.NaN`.
152 * ※ This is an alias for `Number.POSITIVE_INFINITY`.
157 * The difference between 1 and the smallest number greater than 1.
159 * ※ This is an alias for `Number.EPSILON`.
164 /** Negative zero. */
165 export const NEGATIVE_ZERO
= -0;
167 /** The null primitive. */
168 export const NULL
= null;
170 /** Positive zero. */
171 export const POSITIVE_ZERO
= 0;
173 /** The undefined primitive. */
174 export const UNDEFINED
= undefined;
177 * Completes the provided property descriptor by setting missing values
180 * ※ This method modifies the provided object and returns undefined.
182 export const completePropertyDescriptor
= (Desc
) => {
183 if (Desc
=== UNDEFINED
) {
185 "Piscēs: Cannot complete undefined property descriptor.",
187 } else if (!("get" in Desc
|| "set" in Desc
)) {
188 // This is a generic or data descriptor.
189 if (!("value" in Desc
)) {
190 // `value` is not defined on this.
191 Desc
.value
= UNDEFINED
;
193 // `value` is already defined on this.
196 if (!("writable" in Desc
)) {
197 // `writable` is not defined on this.
198 Desc
.writable
= false;
200 // `writable` is already defined on this.
204 // This is not a generic or data descriptor.
205 if (!("get" in Desc
)) {
206 // `get` is not defined on this.
207 Desc
.get = UNDEFINED
;
209 // `get` is already defined on this.
212 if (!("set" in Desc
)) {
213 // `set` is not defined on this.
214 Desc
.set = UNDEFINED
;
216 // `set` is already defined on this.
220 if (!("enumerable" in Desc
)) {
221 // `enumerable` is not defined on this.
222 Desc
.enumerable
= false;
224 // `enumerable` is already defined on this.
227 if (!("configurable" in Desc
)) {
228 // `configurable` is not defined on this.
229 Desc
.configurable
= false;
231 // `configurable` is already defined on this.
236 /** Gets whether the provided value is an accessor descrtiptor. */
237 export const isAccessorDescriptor
= (Desc
) =>
238 Desc
!== UNDEFINED
&& ("get" in Desc
|| "set" in Desc
);
240 /** Gets whether the provided value is a data descrtiptor. */
241 export const isDataDescriptor
= (Desc
) =>
242 Desc
!== UNDEFINED
&& ("value" in Desc
|| "writable" in Desc
);
245 * Gets whether the provided value is a fully‐populated property
248 export const isFullyPopulatedDescriptor
= (Desc
) =>
249 Desc
!== UNDEFINED
&&
250 ("value" in Desc
&& "writable" in Desc
||
251 "get" in Desc
&& "set" in Desc
) &&
252 "enumerable" in Desc
&& "configurable" in Desc
;
255 * Gets whether the provided value is a generic (not accessor or data)
258 export const isGenericDescriptor
= (Desc
) =>
259 Desc
!== UNDEFINED
&&
260 !("get" in Desc
|| "set" in Desc
|| "value" in Desc
||
265 * Returns whether the provided value is a property descriptor record
266 * as created by `toPropertyDescriptor`.
268 * ※ This function is provided to enable inspection of whether an
269 * object uses the property descriptor record proxy implementation,
270 * not as a general test of whether an object satisfies the
271 * requirements for property descriptors. In most cases, a more
272 * specific test, like `isAccessorDescriptor`, `isDataDescriptor`, or
273 * `isGenericDescriptor`, is preferrable.
275 isPropertyDescriptorRecord
,
278 * Converts the provided value to a property descriptor record.
280 * ※ The prototype of a property descriptor record is always `null`.
282 * ※ Actually constructing a property descriptor object using this
283 * class is only necessary if you need strict guarantees about the
284 * types of its properties; the resulting object is proxied to ensure
285 * the types match what one would expect from composing
286 * FromPropertyDescriptor and ToPropertyDescriptor in the Ecmascript
289 toPropertyDescriptor
,
292 assign
: setPropertyValues
,
293 create
: objectCreate
,
294 defineProperty
: defineOwnProperty
,
298 defineProperty
: reflectDefineProperty
,
299 setPrototypeOf
: reflectSetPrototypeOf
,
301 const proxyConstructor
= Proxy
;
302 const { add
: weakSetAdd
, has
: weakSetHas
} = WeakSet
.prototype;
303 const propertyDescriptorRecords
= new WeakSet();
304 const coercePropertyDescriptorValue
= (P
, V
) => {
313 if (V
!== undefined && typeof V
!== "function") {
315 "Piscēs: Getters must be callable.",
321 if (V
!== undefined && typeof V
!== "function") {
323 "Piscēs: Setters must be callable.",
332 const propertyDescriptorProxyHandler
= Object
.freeze(
336 defineProperty(O
, P
, Desc
) {
338 P
=== "configurable" || P
=== "enumerable" ||
339 P
=== "writable" || P
=== "value" ||
340 P
=== "get" || P
=== "set"
342 // P is a property descriptor attribute.
343 const desc
= setPropertyValues(objectCreate(null), Desc
);
344 if ("get" in desc
|| "set" in desc
) {
345 // Desc is an accessor property descriptor.
347 "Piscēs: Property descriptor attributes must be data properties.",
349 } else if ("value" in desc
|| !(P
in O
)) {
350 // Desc has a value or P does not already exist on O.
351 desc
.value
= coercePropertyDescriptorValue(
356 // Desc is not an accessor property descriptor and has no
357 // value, but an existing value is present on O.
360 const isAccessorDescriptor
= "get" === P
|| "set" === P
||
361 "get" in O
|| "set" in O
;
362 const isDataDescriptor
= "value" === P
||
364 "value" in O
|| "writable" in O
;
365 if (isAccessorDescriptor
&& isDataDescriptor
) {
366 // Both accessor and data attributes will be present on O
369 "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
372 // P can be safely defined on O.
373 return reflectDefineProperty(O
, P
, desc
);
376 // P is not a property descriptor attribute.
377 return reflectDefineProperty(O
, P
, Desc
);
380 setPrototypeOf(O
, V
) {
382 // V is not the property descriptor prototype.
385 // V is the property descriptor prototype.
386 return reflectSetPrototypeOf(O
, V
);
394 isPropertyDescriptorRecord
: ($) =>
395 call(weakSetHas
, propertyDescriptorRecords
, [$]),
396 toPropertyDescriptor
: (Obj
) => {
397 if (type(Obj
) !== "object") {
398 // The provided value is not an object.
400 `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
403 // The provided value is an object.
404 const desc
= objectCreate(null);
405 if ("enumerable" in Obj
) {
406 // An enumerable property is specified.
407 defineOwnProperty(desc
, "enumerable", {
410 value
: !!Obj
.enumerable
,
414 // An enumerable property is not specified.
417 if ("configurable" in Obj
) {
418 // A configurable property is specified.
419 defineOwnProperty(desc
, "configurable", {
422 value
: !!Obj
.configurable
,
426 // A configurable property is not specified.
429 if ("value" in Obj
) {
430 // A value property is specified.
431 defineOwnProperty(desc
, "value", {
438 // A value property is not specified.
441 if ("writable" in Obj
) {
442 // A writable property is specified.
443 defineOwnProperty(desc
, "writable", {
446 value
: !!Obj
.writable
,
450 // A writable property is not specified.
454 // A get property is specified.
455 const getter
= Obj
.get;
456 if (getter
!== UNDEFINED
&& typeof getter
!== "function") {
457 // The getter is not callable.
458 throw new TypeError("Piscēs: Getters must be callable.");
460 // The getter is callable.
461 defineOwnProperty(desc
, "get", {
469 // A get property is not specified.
473 // A set property is specified.
474 const setter
= Obj
.set;
475 if (setter
!== UNDEFINED
&& typeof setter
!== "function") {
476 // The setter is not callable.
477 throw new TypeError("Piscēs: Setters must be callable.");
479 // The setter is callable.
480 defineOwnProperty(desc
, "set", {
488 // A set property is not specified.
492 ("get" in desc
|| "set" in desc
) &&
493 ("value" in desc
|| "writable" in desc
)
495 // Both accessor and data attributes have been defined.
497 "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
500 // The property descriptor is valid.
501 const record
= new proxyConstructor(
503 propertyDescriptorProxyHandler
,
505 call(weakSetAdd
, propertyDescriptorRecords
, [record
]);
515 * Returns the primitive value of the provided object per its
516 * `.toString` and `.valueOf` methods.
518 * If the provided hint is "string", then `.toString` takes
519 * precedence; otherwise, `.valueOf` does.
521 * Throws an error if both of these methods are not callable or do
522 * not return a primitive.
527 * Returns a string function name generated from the provided value
528 * and optional prefix.
533 * Returns the provided value converted to a primitive, or throws if
534 * no such conversion is possible.
536 * The provided preferred type, if specified, should be "string",
537 * "number", or "default". If the provided input has a
538 * `.[Symbol.toPrimitive]` method, this function will throw rather
539 * than calling that method with a preferred type other than one of
544 const { apply
: call
} = Reflect
;
545 const getSymbolDescription
= Object
.getOwnPropertyDescriptor(
551 ordinaryToPrimitive
: (O
, hint
) => {
552 const methodNames
= hint
== "string"
553 ? ["toString", "valueOf"]
554 : ["valueOf", "toString"];
555 for (let index
= 0; index
< methodNames
.length
; ++index
) {
556 const method
= O
[methodNames
[index
]];
557 if (typeof method
=== "function") {
558 // Method is callable.
559 const result
= call(method
, O
, []);
560 if (type(result
) !== "object") {
561 // Method returns a primitive.
564 // Method returns an object.
568 // Method is not callable.
573 "Piscēs: Unable to convert object to primitive",
576 toFunctionName
: ($, prefix
= undefined) => {
577 const key
= toPrimitive($, "string");
578 const name
= (() => {
579 if (typeof key
=== "symbol") {
580 // The provided value is a symbol; format its description.
581 const description
= call(getSymbolDescription
, key
, []);
582 return description
=== undefined ? "" : `[${description}]`;
584 // The provided value not a symbol; convert it to a string
589 return prefix
!== undefined ? `${prefix} ${name}` : name
;
591 toPrimitive
: ($, preferredType
= "default") => {
592 const hint
= `${preferredType}`;
594 "default" !== hint
&& "string" !== hint
&&
597 // An invalid preferred type was specified.
599 `Piscēs: Invalid preferred type: ${preferredType}.`,
601 } else if (type($) === "object") {
602 // The provided value is an object.
603 const exoticToPrim
= $[TO_PRIMITIVE
] ?? undefined;
604 if (exoticToPrim
!== undefined) {
605 // The provided value has an exotic primitive conversion
607 if (typeof exoticToPrim
!== "function") {
608 // The method is not callable.
610 "Piscēs: `.[Symbol.toPrimitive]` was neither nullish nor callable.",
613 // The method is callable.
614 return call(exoticToPrim
, $, [hint
]);
617 // Use the ordinary primitive conversion function.
618 return ordinaryToPrimitive($, hint
);
621 // The provided value is already a primitive.
630 * Returns whether the provided values are the same value.
632 * ※ This differs from `===` in the cases of nan and zero.
637 * Returns whether the provided values are either the same value or
638 * both zero (either positive or negative).
640 * ※ This differs from `===` in the case of nan.
645 * Returns the result of converting the provided value to an index,
646 * or throws an error if it is out of range.
651 * Returns the result of converting the provided value to a length.
655 const { floor
, max
, min
} = Math
;
656 const { isNaN
: isNan
} = Number
;
657 const { is
} = Object
;
659 sameValue
: (a
, b
) => is(a
, b
),
660 sameValueZero
: ($1, $2) => {
661 const type1
= type($1);
662 const type2
= type($2);
663 if (type1
!== type2
) {
664 // The provided values are not of the same type.
666 } else if (type1
=== "number") {
667 // The provided values are numbers; check if they are nan and
668 // use strict equality otherwise.
669 return isNan($1) && isNan($2) || $1 === $2;
671 // The provided values are not numbers; use strict equality.
676 const integer
= floor($);
677 if (isNan(integer
) || integer
== 0) {
678 // The value is zero·like.
681 // The value is not zero·like.
682 const clamped
= toLength(integer
);
683 if (clamped
!== integer
) {
684 // Clamping the value changes it.
685 throw new RangeError(`Piscēs: Index out of range: ${$}.`);
687 // The value is within appropriate bounds.
693 const len
= floor($);
694 return isNan(len
) || len
== 0
696 : max(min(len
, MAXIMUM_SAFE_INTEGRAL_NUMBER
), 0);
702 * Returns a lowercase string identifying the type of the provided
705 * This differs from the value of the `typeof` operator only in the
706 * cases of objects and null.
708 export const type
= ($) => {
710 // The provided value is null.
713 // The provided value is not null.
714 const type
·of = typeof $;
715 return type
·of === "function" ? "object" : type
·of;