]> Lady’s Gitweb - Pisces/blob - value.js
Minor refactors to numeric.js
[Pisces] / value.js
1 // SPDX-FileCopyrightText: 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
2 // SPDX-License-Identifier: MPL-2.0
3 /**
4 * ⁌ ♓🧩 Piscēs ∷ value.js
5 *
6 * Copyright © 2022–2023, 2025 Lady [@ Ladys Computer].
7 *
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/>.
11 */
12
13 const PISCĒS = "♓🧩 Piscēs";
14
15 export const {
16 /** The welknown `@@asyncIterator´ symbol. */
17 asyncIterator: ASYNC_ITERATOR,
18
19 /** The welknown `@@hasInstance´ symbol. */
20 hasInstance: HAS_INSTANCE,
21
22 /** The welknown `@@isConcatSpreadable´ symbol. */
23 isConcatSpreadable: IS_CONCAT_SPREADABLE,
24
25 /** The welknown `@@iterator´ symbol. */
26 iterator: ITERATOR,
27
28 /** The welknown `@@match´ symbol. */
29 match: MATCH,
30
31 /** The welknown `@@matchAll´ symbol. */
32 matchAll: MATCH_ALL,
33
34 /** The welknown `@@replace´ symbol. */
35 replace: REPLACE,
36
37 /** The welknown `@@species´ symbol. */
38 species: SPECIES,
39
40 /** The welknown `@@split´ symbol. */
41 split: SPLIT,
42
43 /** The welknown `@@toPrimitive´ symbol. */
44 toPrimitive: TO_PRIMITIVE,
45
46 /** The welknown `@@toStringTag´ symbol. */
47 toStringTag: TO_STRING_TAG,
48
49 /** The welknown `@@unscopables´ symbol. */
50 unscopables: UNSCOPABLES,
51 } = Symbol;
52
53 export const {
54 /**
55 * ln(10).
56 *
57 * ※ This is an alias for `Math.LN10´.
58 */
59 LN10: LN_10,
60
61 /**
62 * ln(2).
63 *
64 * ※ This is an alias for `Math.LN2´.
65 */
66 LN2: LN_2,
67
68 /**
69 * log10(e).
70 *
71 * ※ This is an alias for `Math.LOG10E´.
72 */
73 LOG10E: LOG10_𝑒,
74
75 /**
76 * log2(e).
77 *
78 * ※ This is an alias for `Math.LOG2E´.
79 */
80 LOG2E: LOG2_𝑒,
81
82 /**
83 * sqrt(½).
84 *
85 * ※ This is an alias for `Math.SQRT1_2´.
86 */
87 SQRT1_2: RECIPROCAL_SQRT_2,
88
89 /**
90 * sqrt(2).
91 *
92 * ※ This is an alias for `Math.SQRT2´.
93 */
94 SQRT2: SQRT_2,
95
96 /**
97 * The mathematical constant 𝑒.
98 *
99 * ※ This is an alias for `Math.E´.
100 */
101 E: 𝑒,
102
103 /**
104 * The mathematical constant 𝜋.
105 *
106 * ※ This is an alias for `Math.PI´.
107 */
108 PI: 𝜋,
109 } = Math;
110
111 export const {
112 /**
113 * The largest number value less than infinity.
114 *
115 * ※ This is an alias for `Number.MAX_VALUE´.
116 */
117 MAX_VALUE: MAXIMUM_NUMBER,
118
119 /**
120 * 2^53 - 1.
121 *
122 * ※ This is an alias for `Number.MAX_SAFE_INTEGER´.
123 */
124 MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER,
125
126 /**
127 * The smallest number value greater than negative infinity.
128 *
129 * ※ This is an alias for `Number.MIN_VALUE´.
130 */
131 MIN_VALUE: MINIMUM_NUMBER,
132
133 /**
134 * -(2^53 - 1).
135 *
136 * ※ This is an alias for `Number.MIN_SAFE_INTEGER´.
137 */
138 MIN_SAFE_INTEGER: MINIMUM_SAFE_INTEGRAL_NUMBER,
139
140 /**
141 * Negative infinity.
142 *
143 * ※ This is an alias for `Number.NEGATIVE_INFINITY´.
144 */
145 NEGATIVE_INFINITY,
146
147 /**
148 * Nan.
149 *
150 * ※ This is an alias for `Number.NaN´.
151 */
152 NaN: NAN,
153
154 /**
155 * Positive infinity.
156 *
157 * ※ This is an alias for `Number.POSITIVE_INFINITY´.
158 */
159 POSITIVE_INFINITY,
160
161 /**
162 * The difference between 1 and the smallest number greater than 1.
163 *
164 * ※ This is an alias for `Number.EPSILON´.
165 */
166 EPSILON: 𝜀,
167 } = Number;
168
169 /** Negative zero. */
170 export const NEGATIVE_ZERO = -0;
171
172 /** The null primitive. */
173 export const NULL = null;
174
175 /** Positive zero. */
176 export const POSITIVE_ZERO = 0;
177
178 /** The undefined primitive. */
179 export const UNDEFINED = undefined;
180
181 /**
182 * Completes the provided property descriptor by setting missing values
183 * to their defaults.
184 *
185 * ※ This method modifies the provided object and returns undefined.
186 */
187 export const completePropertyDescriptor = (Desc) => {
188 if (Desc === UNDEFINED) {
189 // A description was not provided; this is an error.
190 throw new TypeError(
191 `${PISCĒS}: Cannot complete undefined property descriptor.`,
192 );
193 } else if (!("get" in Desc || "set" in Desc)) {
194 // This is a generic or data descriptor.
195 if (!("value" in Desc)) {
196 // `value´ is not defined on this.
197 Desc.value = UNDEFINED;
198 } else {
199 // `value´ is already defined on this.
200 /* do nothing */
201 }
202 if (!("writable" in Desc)) {
203 // `writable´ is not defined on this.
204 Desc.writable = false;
205 } else {
206 // `writable´ is already defined on this.
207 /* do nothing */
208 }
209 } else {
210 // This is not a generic or data descriptor.
211 if (!("get" in Desc)) {
212 // `get´ is not defined on this.
213 Desc.get = UNDEFINED;
214 } else {
215 // `get´ is already defined on this.
216 /* do nothing */
217 }
218 if (!("set" in Desc)) {
219 // `set´ is not defined on this.
220 Desc.set = UNDEFINED;
221 } else {
222 // `set´ is already defined on this.
223 /* do nothing */
224 }
225 }
226 if (!("enumerable" in Desc)) {
227 // `enumerable´ is not defined on this.
228 Desc.enumerable = false;
229 } else {
230 // `enumerable´ is already defined on this.
231 /* do nothing */
232 }
233 if (!("configurable" in Desc)) {
234 // `configurable´ is not defined on this.
235 Desc.configurable = false;
236 } else {
237 // `configurable´ is already defined on this.
238 /* do nothing */
239 }
240 };
241
242 /** Gets whether the provided value is an accessor descrtiptor. */
243 export const isAccessorDescriptor = (Desc) =>
244 Desc !== UNDEFINED && ("get" in Desc || "set" in Desc);
245
246 /** Gets whether the provided value is a data descrtiptor. */
247 export const isDataDescriptor = (Desc) =>
248 Desc !== UNDEFINED && ("value" in Desc || "writable" in Desc);
249
250 /**
251 * Gets whether the provided value is a fully‐populated property
252 * descriptor.
253 */
254 export const isFullyPopulatedDescriptor = (Desc) =>
255 Desc !== UNDEFINED
256 && ("value" in Desc && "writable" in Desc
257 || "get" in Desc && "set" in Desc)
258 && "enumerable" in Desc && "configurable" in Desc;
259
260 /**
261 * Gets whether the provided value is a generic (not accessor or data)
262 * descrtiptor.
263 */
264 export const isGenericDescriptor = (Desc) =>
265 Desc !== UNDEFINED
266 && !("get" in Desc || "set" in Desc || "value" in Desc
267 || "writable" in Desc);
268
269 export const {
270 /**
271 * Returns the primitive value of the provided object per its
272 * `.toString´ and `.valueOf´ methods.
273 *
274 * If the provided hint is "string", then `.toString´ takes
275 * precedence; otherwise, `.valueOf´ does.
276 *
277 * Throws an error if both of these methods are not callable or do
278 * not return a primitive.
279 */
280 ordinaryToPrimitive,
281
282 /**
283 * Returns a string function name generated from the provided value
284 * and optional prefix.
285 */
286 toFunctionName,
287
288 /**
289 * Returns the provided value converted to a primitive, or throws if
290 * no such conversion is possible.
291 *
292 * The provided preferred type, if specified, should be "string",
293 * "number", or "default". If the provided input has a
294 * `.[Symbol.toPrimitive]´ method, this function will throw rather
295 * than calling that method with a preferred type other than one of
296 * the above.
297 */
298 toPrimitive,
299 } = (() => {
300 const { apply: call } = Reflect;
301 const getSymbolDescription = Object.getOwnPropertyDescriptor(
302 Symbol.prototype,
303 "description",
304 ).get;
305
306 return {
307 ordinaryToPrimitive: (O, hint) => {
308 const methodNames = hint == "string"
309 ? ["toString", "valueOf"]
310 : ["valueOf", "toString"];
311 for (let index = 0; index < methodNames.length; ++index) {
312 // Test the methods in the order determined above (based on the
313 // hint) and return the result if the method returns a
314 // primitive.
315 //
316 // ☡ If this loop exits with·out returning, it is an error.
317 const method = O[methodNames[index]];
318 if (typeof method === "function") {
319 // Method is callable.
320 const result = call(method, O, []);
321 if (type(result) !== "object") {
322 // Method returns a primitive.
323 return result;
324 } else {
325 // Method returns an object.
326 continue;
327 }
328 } else {
329 // Method is not callable.
330 continue;
331 }
332 }
333 throw new TypeError(
334 `${PISCĒS}: Unable to convert object to primitive.`,
335 );
336 },
337 toFunctionName: ($, prefix = UNDEFINED) => {
338 const key = toPrimitive($, "string");
339 const name = (() => {
340 if (typeof key === "symbol") {
341 // The provided value is a symbol.
342 //
343 // Format its description.
344 const description = call(getSymbolDescription, key, []);
345 return description === UNDEFINED ? "" : `[${description}]`;
346 } else {
347 // The provided value not a symbol.
348 //
349 // Convert it to a string property key.
350 return `${key}`;
351 }
352 })();
353 return prefix !== UNDEFINED ? `${prefix} ${name}` : name;
354 },
355 toPrimitive: ($, preferredType = "default") => {
356 const hint = `${preferredType}`;
357 if (
358 "default" !== hint && "string" !== hint
359 && "number" !== hint
360 ) {
361 // An invalid preferred type was specified.
362 throw new TypeError(
363 `${PISCĒS}: Invalid preferred type: ${preferredType}.`,
364 );
365 } else if (type($) === "object") {
366 // The provided value is an object.
367 const exoticToPrim = $[TO_PRIMITIVE] ?? UNDEFINED;
368 if (exoticToPrim !== UNDEFINED) {
369 // The provided value has an exotic primitive conversion
370 // method.
371 if (typeof exoticToPrim !== "function") {
372 // The method is not callable.
373 throw new TypeError(
374 `${PISCĒS}: .[Symbol.toPrimitive] was neither nullish nor callable.`,
375 );
376 } else {
377 // The method is callable.
378 return call(exoticToPrim, $, [hint]);
379 }
380 } else {
381 // Use the ordinary primitive conversion function.
382 return ordinaryToPrimitive($, hint);
383 }
384 } else {
385 // The provided value is already a primitive.
386 return $;
387 }
388 },
389 };
390 })();
391
392 export const {
393 /**
394 * Returns whether the provided values are the same value.
395 *
396 * ※ This differs from `===´ in the cases of nan and zero.
397 */
398 sameValue,
399
400 /**
401 * Returns whether the provided values are either the same value or
402 * both zero (either positive or negative).
403 *
404 * ※ This differs from `===´ in the case of nan.
405 */
406 sameValueZero,
407
408 /**
409 * Returns the result of converting the provided value to an index,
410 * or throws an error if it is out of range.
411 */
412 toIndex,
413
414 /**
415 * Returns the result of converting the provided value to a length.
416 */
417 toLength,
418 } = (() => {
419 const { floor, max, min } = Math;
420 const { isNaN: isNan } = Number;
421 const { is } = Object;
422 return {
423 sameValue: (a, b) => is(a, b),
424 sameValueZero: ($1, $2) => {
425 const type1 = type($1);
426 const type2 = type($2);
427 if (type1 !== type2) {
428 // The provided values are not of the same type.
429 return false;
430 } else if (type1 === "number") {
431 // The provided values are numbers.
432 //
433 // Check if they are nan and use strict equality otherwise.
434 return isNan($1) && isNan($2) || $1 === $2;
435 } else {
436 // The provided values are not numbers.
437 //
438 // Use strict equality.
439 return $1 === $2;
440 }
441 },
442 toIndex: ($) => {
443 const integer = floor($);
444 if (isNan(integer) || integer == 0) {
445 // The value is zero·like.
446 //
447 // Return positive zero.
448 return 0;
449 } else {
450 // The value is not zero·like.
451 const clamped = toLength(integer);
452 if (clamped !== integer) {
453 // Clamping the value changes it; this is an error.
454 throw new RangeError(`${PISCĒS}: Index out of range: ${$}.`);
455 } else {
456 // The value is within appropriate bounds.
457 //
458 // Return it.
459 return integer;
460 }
461 }
462 },
463 toLength: ($) => {
464 const len = floor($);
465 return isNan(len) || len == 0
466 ? 0
467 : max(min(len, MAXIMUM_SAFE_INTEGRAL_NUMBER), 0);
468 },
469 };
470 })();
471
472 /**
473 * Returns the property key (symbol or string) corresponding to the
474 * provided value.
475 */
476 export const toPropertyKey = ($) => {
477 const key = toPrimitive($, "string");
478 return typeof key === "symbol" ? key : `${key}`;
479 };
480
481 /**
482 * Returns a lowercase string identifying the type of the provided
483 * value.
484 *
485 * This differs from the value of the `typeof´ operator only in the
486 * cases of callable objects and null.
487 */
488 export const type = ($) => {
489 if ($ === null) {
490 // The provided value is null.
491 return "null";
492 } else {
493 // The provided value is not null.
494 const type·of = typeof $;
495 return type·of === "function" ? "object" : type·of;
496 }
497 };
This page took 0.734574 seconds and 5 git commands to generate.