]> Lady’s Gitweb - Pisces/blob - value.js
Move toFunctionName into value.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 export const {
177 /**
178 * Returns the primitive value of the provided object per its
179 * `.toString` and `.valueOf` methods.
180 *
181 * If the provided hint is "string", then `.toString` takes
182 * precedence; otherwise, `.valueOf` does.
183 *
184 * Throws an error if both of these methods are not callable or do
185 * not return a primitive.
186 */
187 ordinaryToPrimitive,
188
189 /**
190 * Returns a string function name generated from the provided value
191 * and optional prefix.
192 */
193 toFunctionName,
194
195 /**
196 * Returns the provided value converted to a primitive, or throws if
197 * no such conversion is possible.
198 *
199 * The provided preferred type, if specified, should be "string",
200 * "number", or "default". If the provided input has a
201 * `.[Symbol.toPrimitive]` method, this function will throw rather
202 * than calling that method with a preferred type other than one of
203 * the above.
204 */
205 toPrimitive,
206 } = (() => {
207 const { apply: call } = Reflect;
208 const getSymbolDescription = Object.getOwnPropertyDescriptor(
209 Symbol.prototype,
210 "description",
211 ).get;
212
213 return {
214 ordinaryToPrimitive: (O, hint) => {
215 const methodNames = hint == "string"
216 ? ["toString", "valueOf"]
217 : ["valueOf", "toString"];
218 for (let index = 0; index < methodNames.length; ++index) {
219 const method = O[methodNames[index]];
220 if (typeof method === "function") {
221 // Method is callable.
222 const result = call(method, O, []);
223 if (type(result) !== "object") {
224 // Method returns a primitive.
225 return result;
226 } else {
227 // Method returns an object.
228 continue;
229 }
230 } else {
231 // Method is not callable.
232 continue;
233 }
234 }
235 throw new TypeError(
236 "Piscēs: Unable to convert object to primitive",
237 );
238 },
239 toFunctionName: ($, prefix = undefined) => {
240 const key = toPrimitive($, "string");
241 const name = (() => {
242 if (typeof key === "symbol") {
243 // The provided value is a symbol; format its description.
244 const description = call(getSymbolDescription, key, []);
245 return description === undefined ? "" : `[${description}]`;
246 } else {
247 // The provided value not a symbol; convert it to a string
248 // property key.
249 return `${key}`;
250 }
251 })();
252 return prefix !== undefined ? `${prefix} ${name}` : name;
253 },
254 toPrimitive: ($, preferredType = "default") => {
255 const hint = `${preferredType}`;
256 if (
257 "default" !== hint && "string" !== hint &&
258 "number" !== hint
259 ) {
260 // An invalid preferred type was specified.
261 throw new TypeError(
262 `Piscēs: Invalid preferred type: ${preferredType}.`,
263 );
264 } else if (type($) === "object") {
265 // The provided value is an object.
266 const exoticToPrim = $[TO_PRIMITIVE] ?? undefined;
267 if (exoticToPrim !== undefined) {
268 // The provided value has an exotic primitive conversion
269 // method.
270 if (typeof exoticToPrim !== "function") {
271 // The method is not callable.
272 throw new TypeError(
273 "Piscēs: `.[Symbol.toPrimitive]` was neither nullish nor callable.",
274 );
275 } else {
276 // The method is callable.
277 return call(exoticToPrim, $, [hint]);
278 }
279 } else {
280 // Use the ordinary primitive conversion function.
281 return ordinaryToPrimitive($, hint);
282 }
283 } else {
284 // The provided value is already a primitive.
285 return $;
286 }
287 },
288 };
289 })();
290
291 export const {
292 /**
293 * Returns whether the provided values are the same value.
294 *
295 * ※ This differs from `===` in the cases of nan and zero.
296 */
297 sameValue,
298
299 /**
300 * Returns whether the provided values are either the same value or
301 * both zero (either positive or negative).
302 *
303 * ※ This differs from `===` in the case of nan.
304 */
305 sameValueZero,
306
307 /**
308 * Returns the result of converting the provided value to an index,
309 * or throws an error if it is out of range.
310 */
311 toIndex,
312
313 /**
314 * Returns the result of converting the provided value to a length.
315 */
316 toLength,
317 } = (() => {
318 const { floor, max, min } = Math;
319 const { isNaN: isNan } = Number;
320 const { is } = Object;
321 return {
322 sameValue: (a, b) => is(a, b),
323 sameValueZero: ($1, $2) => {
324 const type1 = type($1);
325 const type2 = type($2);
326 if (type1 !== type2) {
327 // The provided values are not of the same type.
328 return false;
329 } else if (type1 === "number") {
330 // The provided values are numbers; check if they are nan and
331 // use strict equality otherwise.
332 return isNan($1) && isNan($2) || $1 === $2;
333 } else {
334 // The provided values are not numbers; use strict equality.
335 return $1 === $2;
336 }
337 },
338 toIndex: ($) => {
339 const integer = floor($);
340 if (isNan(integer) || integer == 0) {
341 // The value is zero·like.
342 return 0;
343 } else {
344 // The value is not zero·like.
345 const clamped = toLength(integer);
346 if (clamped !== integer) {
347 // Clamping the value changes it.
348 throw new RangeError(`Piscēs: Index out of range: ${$}.`);
349 } else {
350 // The value is within appropriate bounds.
351 return integer;
352 }
353 }
354 },
355 toLength: ($) => {
356 const len = floor($);
357 return isNan(len) || len == 0
358 ? 0
359 : max(min(len, MAXIMUM_SAFE_INTEGRAL_NUMBER), 0);
360 },
361 };
362 })();
363
364 /**
365 * Returns a lowercase string identifying the type of the provided
366 * value.
367 *
368 * This differs from the value of the `typeof` operator only in the
369 * cases of objects and null.
370 */
371 export const type = ($) => {
372 if ($ === null) {
373 // The provided value is null.
374 return "null";
375 } else {
376 // The provided value is not null.
377 const type·of = typeof $;
378 return type·of === "function" ? "object" : type·of;
379 }
380 };
This page took 0.155223 seconds and 5 git commands to generate.