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