]> Lady’s Gitweb - Pisces/blob - value.js
Move index string functions back into value.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 * 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.
185 *
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.
188 */
189 export const canonicalNumericIndexString = ($) => {
190 if (typeof $ !== "string") {
191 return UNDEFINED;
192 } else if ($ === "-0") {
193 return -0;
194 } else {
195 const n = +$;
196 return $ === `${n}` ? n : UNDEFINED;
197 }
198 };
199
200 /**
201 * Completes the provided property descriptor by setting missing values
202 * to their defaults.
203 *
204 * ※ This method modifies the provided object and returns undefined.
205 */
206 export const completePropertyDescriptor = (Desc) => {
207 if (Desc === UNDEFINED) {
208 // A description was not provided; this is an error.
209 throw new TypeError(
210 `${PISCĒS}: Cannot complete undefined property descriptor.`,
211 );
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;
217 } else {
218 // `value´ is already defined on this.
219 /* do nothing */
220 }
221 if (!("writable" in Desc)) {
222 // `writable´ is not defined on this.
223 Desc.writable = false;
224 } else {
225 // `writable´ is already defined on this.
226 /* do nothing */
227 }
228 } else {
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;
233 } else {
234 // `get´ is already defined on this.
235 /* do nothing */
236 }
237 if (!("set" in Desc)) {
238 // `set´ is not defined on this.
239 Desc.set = UNDEFINED;
240 } else {
241 // `set´ is already defined on this.
242 /* do nothing */
243 }
244 }
245 if (!("enumerable" in Desc)) {
246 // `enumerable´ is not defined on this.
247 Desc.enumerable = false;
248 } else {
249 // `enumerable´ is already defined on this.
250 /* do nothing */
251 }
252 if (!("configurable" in Desc)) {
253 // `configurable´ is not defined on this.
254 Desc.configurable = false;
255 } else {
256 // `configurable´ is already defined on this.
257 /* do nothing */
258 }
259 };
260
261 /** Gets whether the provided value is an accessor descrtiptor. */
262 export const isAccessorDescriptor = (Desc) =>
263 Desc !== UNDEFINED && ("get" in Desc || "set" in Desc);
264
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.
270 //
271 // Return whether it is in range for array indices.
272 return sameValue(value, 0)
273 || value === toLength(value) && value > 0 && value < -1 >>> 0;
274 } else {
275 // The provided value is not a canonical numeric index string.
276 return false;
277 }
278 };
279
280 /** Gets whether the provided value is a data descrtiptor. */
281 export const isDataDescriptor = (Desc) =>
282 Desc !== UNDEFINED && ("value" in Desc || "writable" in Desc);
283
284 /**
285 * Gets whether the provided value is a fully‐populated property
286 * descriptor.
287 */
288 export const isFullyPopulatedDescriptor = (Desc) =>
289 Desc !== UNDEFINED
290 && ("value" in Desc && "writable" in Desc
291 || "get" in Desc && "set" in Desc)
292 && "enumerable" in Desc && "configurable" in Desc;
293
294 /**
295 * Gets whether the provided value is a generic (not accessor or data)
296 * descrtiptor.
297 */
298 export const isGenericDescriptor = (Desc) =>
299 Desc !== UNDEFINED
300 && !("get" in Desc || "set" in Desc || "value" in Desc
301 || "writable" in Desc);
302
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.
308 //
309 // Return whether it is in range for integer indices.
310 return sameValue(value, 0)
311 || value === toLength(value) && value > 0;
312 } else {
313 // The provided value is not a canonical numeric index string.
314 return false;
315 }
316 };
317
318 export const {
319 /**
320 * Returns the primitive value of the provided object per its
321 * `.toString´ and `.valueOf´ methods.
322 *
323 * If the provided hint is "string", then `.toString´ takes
324 * precedence; otherwise, `.valueOf´ does.
325 *
326 * Throws an error if both of these methods are not callable or do
327 * not return a primitive.
328 */
329 ordinaryToPrimitive,
330
331 /**
332 * Returns a string function name generated from the provided value
333 * and optional prefix.
334 */
335 toFunctionName,
336
337 /**
338 * Returns the provided value converted to a primitive, or throws if
339 * no such conversion is possible.
340 *
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
345 * the above.
346 */
347 toPrimitive,
348 } = (() => {
349 const { apply: call } = Reflect;
350 const getSymbolDescription = Object.getOwnPropertyDescriptor(
351 Symbol.prototype,
352 "description",
353 ).get;
354
355 return {
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
363 // primitive.
364 //
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.
372 return result;
373 } else {
374 // Method returns an object.
375 continue;
376 }
377 } else {
378 // Method is not callable.
379 continue;
380 }
381 }
382 throw new TypeError(
383 `${PISCĒS}: Unable to convert object to primitive.`,
384 );
385 },
386 toFunctionName: ($, prefix = UNDEFINED) => {
387 const key = toPrimitive($, "string");
388 const name = (() => {
389 if (typeof key === "symbol") {
390 // The provided value is a symbol.
391 //
392 // Format its description.
393 const description = call(getSymbolDescription, key, []);
394 return description === UNDEFINED ? "" : `[${description}]`;
395 } else {
396 // The provided value not a symbol.
397 //
398 // Convert it to a string property key.
399 return `${key}`;
400 }
401 })();
402 return prefix !== UNDEFINED ? `${prefix} ${name}` : name;
403 },
404 toPrimitive: ($, preferredType = "default") => {
405 const hint = `${preferredType}`;
406 if (
407 "default" !== hint && "string" !== hint
408 && "number" !== hint
409 ) {
410 // An invalid preferred type was specified.
411 throw new TypeError(
412 `${PISCĒS}: Invalid preferred type: ${preferredType}.`,
413 );
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
419 // method.
420 if (typeof exoticToPrim !== "function") {
421 // The method is not callable.
422 throw new TypeError(
423 `${PISCĒS}: .[Symbol.toPrimitive] was neither nullish nor callable.`,
424 );
425 } else {
426 // The method is callable.
427 return call(exoticToPrim, $, [hint]);
428 }
429 } else {
430 // Use the ordinary primitive conversion function.
431 return ordinaryToPrimitive($, hint);
432 }
433 } else {
434 // The provided value is already a primitive.
435 return $;
436 }
437 },
438 };
439 })();
440
441 export const {
442 /**
443 * Returns whether the provided values are the same value.
444 *
445 * ※ This differs from `===´ in the cases of nan and zero.
446 */
447 sameValue,
448
449 /**
450 * Returns whether the provided values are either the same value or
451 * both zero (either positive or negative).
452 *
453 * ※ This differs from `===´ in the case of nan.
454 */
455 sameValueZero,
456
457 /**
458 * Returns the result of converting the provided value to an index,
459 * or throws an error if it is out of range.
460 */
461 toIndex,
462
463 /**
464 * Returns the result of converting the provided value to a length.
465 */
466 toLength,
467 } = (() => {
468 const { floor, max, min } = Math;
469 const { isNaN: isNan } = Number;
470 const { is } = Object;
471 return {
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.
478 return false;
479 } else if (type1 === "number") {
480 // The provided values are numbers.
481 //
482 // Check if they are nan and use strict equality otherwise.
483 return isNan($1) && isNan($2) || $1 === $2;
484 } else {
485 // The provided values are not numbers.
486 //
487 // Use strict equality.
488 return $1 === $2;
489 }
490 },
491 toIndex: ($) => {
492 const integer = floor($);
493 if (isNan(integer) || integer == 0) {
494 // The value is zero·like.
495 //
496 // Return positive zero.
497 return 0;
498 } else {
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: ${$}.`);
504 } else {
505 // The value is within appropriate bounds.
506 //
507 // Return it.
508 return integer;
509 }
510 }
511 },
512 toLength: ($) => {
513 const len = floor($);
514 return isNan(len) || len == 0
515 ? 0
516 : max(min(len, MAXIMUM_SAFE_INTEGRAL_NUMBER), 0);
517 },
518 };
519 })();
520
521 /**
522 * Returns the property key (symbol or string) corresponding to the
523 * provided value.
524 */
525 export const toPropertyKey = ($) => {
526 const key = toPrimitive($, "string");
527 return typeof key === "symbol" ? key : `${key}`;
528 };
529
530 /**
531 * Returns a lowercase string identifying the type of the provided
532 * value.
533 *
534 * This differs from the value of the `typeof´ operator only in the
535 * cases of callable objects and null.
536 */
537 export const type = ($) => {
538 if ($ === null) {
539 // The provided value is null.
540 return "null";
541 } else {
542 // The provided value is not null.
543 const type·of = typeof $;
544 return type·of === "function" ? "object" : type·of;
545 }
546 };
This page took 0.263663 seconds and 5 git commands to generate.