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