]> Lady’s Gitweb - Pisces/blob - value.js
4e46f637836cfbd2c8c676e6c0bef5b5dc48424b
[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 /** The null primitive. */
49 export const NULL = null;
50
51 /** The undefined primitive. */
52 export const UNDEFINED = undefined;
53
54 /**
55 * Returns the length of the provided arraylike value.
56 *
57 * This can produce larger lengths than can actually be stored in
58 * arrays, because no such restrictions exist on arraylike methods.
59 *
60 * ☡ This function throws if the provided value is not arraylike.
61 */
62 export const lengthOfArraylike = ({ length }) => toLength(length);
63
64 export const {
65 /**
66 * Returns the primitive value of the provided object per its
67 * `.toString` and `.valueOf` methods.
68 *
69 * If the provided hint is "string", then `.toString` takes
70 * precedence; otherwise, `.valueOf` does.
71 *
72 * Throws an error if both of these methods are not callable or do
73 * not return a primitive.
74 */
75 ordinaryToPrimitive,
76
77 /**
78 * Returns the provided value converted to a primitive, or throws if
79 * no such conversion is possible.
80 *
81 * The provided preferred type, if specified, should be "string",
82 * "number", or "default". If the provided input has a
83 * `.[Symbol.toPrimitive]` method, this function will throw rather
84 * than calling that method with a preferred type other than one of
85 * the above.
86 */
87 toPrimitive,
88 } = (() => {
89 const { apply: call } = Reflect;
90
91 return {
92 ordinaryToPrimitive: (O, hint) => {
93 const methodNames = hint == "string"
94 ? ["toString", "valueOf"]
95 : ["valueOf", "toString"];
96 for (let index = 0; index < methodNames.length; ++index) {
97 const method = O[methodNames[index]];
98 if (typeof method === "function") {
99 // Method is callable.
100 const result = call(method, O, []);
101 if (type(result) !== "object") {
102 // Method returns a primitive.
103 return result;
104 } else {
105 // Method returns an object.
106 continue;
107 }
108 } else {
109 // Method is not callable.
110 continue;
111 }
112 }
113 throw new TypeError(
114 "Piscēs: Unable to convert object to primitive",
115 );
116 },
117 toPrimitive: ($, preferredType = "default") => {
118 const hint = `${preferredType}`;
119 if (
120 "default" !== hint && "string" !== hint &&
121 "number" !== hint
122 ) {
123 // An invalid preferred type was specified.
124 throw new TypeError(
125 `Piscēs: Invalid preferred type: ${preferredType}.`,
126 );
127 } else if (type($) === "object") {
128 // The provided value is an object.
129 const exoticToPrim = $[TO_PRIMITIVE] ?? undefined;
130 if (exoticToPrim !== undefined) {
131 // The provided value has an exotic primitive conversion
132 // method.
133 if (typeof exoticToPrim !== "function") {
134 // The method is not callable.
135 throw new TypeError(
136 "Piscēs: `.[Symbol.toPrimitive]` was neither nullish nor callable.",
137 );
138 } else {
139 // The method is callable.
140 return call(exoticToPrim, $, [hint]);
141 }
142 } else {
143 // Use the ordinary primitive conversion function.
144 return ordinaryToPrimitive($, hint);
145 }
146 } else {
147 // The provided value is already a primitive.
148 return $;
149 }
150 },
151 };
152 })();
153
154 export const {
155 /**
156 * Returns whether the provided values are the same value.
157 *
158 * ※ This differs from `===` in the cases of nan and zero.
159 */
160 sameValue,
161
162 /**
163 * Returns whether the provided values are either the same value or
164 * both zero (either positive or negative).
165 *
166 * ※ This differs from `===` in the case of nan.
167 */
168 sameValueZero,
169
170 /**
171 * Returns the result of converting the provided value to an index,
172 * or throws an error if it is out of range.
173 */
174 toIndex,
175
176 /**
177 * Returns the result of converting the provided value to a length.
178 */
179 toLength,
180 } = (() => {
181 const { floor, max, min } = Math;
182 const {
183 MAX_SAFE_INTEGER: MAXIMUM_SAFE_INTEGRAL_NUMBER,
184 isNaN: isNan,
185 } = Number;
186 const { is } = Object;
187 return {
188 sameValue: (a, b) => is(a, b),
189 sameValueZero: ($1, $2) => {
190 const type1 = type($1);
191 const type2 = type($2);
192 if (type1 !== type2) {
193 // The provided values are not of the same type.
194 return false;
195 } else if (type1 === "number") {
196 // The provided values are numbers; check if they are nan and
197 // use strict equality otherwise.
198 return isNan($1) && isNan($2) || $1 === $2;
199 } else {
200 // The provided values are not numbers; use strict equality.
201 return $1 === $2;
202 }
203 },
204 toIndex: ($) => {
205 const integer = floor($);
206 if (isNan(integer) || integer == 0) {
207 // The value is zero·like.
208 return 0;
209 } else {
210 // The value is not zero·like.
211 const clamped = toLength(integer);
212 if (clamped !== integer) {
213 // Clamping the value changes it.
214 throw new RangeError(`Piscēs: Index out of range: ${$}.`);
215 } else {
216 // The value is within appropriate bounds.
217 return integer;
218 }
219 }
220 },
221 toLength: ($) => {
222 const len = floor($);
223 return isNan(len) || len == 0
224 ? 0
225 : max(min(len, MAXIMUM_SAFE_INTEGRAL_NUMBER), 0);
226 },
227 };
228 })();
229
230 /**
231 * Returns a lowercase string identifying the type of the provided
232 * value.
233 *
234 * This differs from the value of the `typeof` operator only in the
235 * cases of objects and null.
236 */
237 export const type = ($) => {
238 if ($ === null) {
239 // The provided value is null.
240 return "null";
241 } else {
242 // The provided value is not null.
243 const type·of = typeof $;
244 return type·of === "function" ? "object" : type·of;
245 }
246 };
This page took 0.11242 seconds and 3 git commands to generate.