1 // ♓🌟 Piscēs ∷ string.js
2 // ====================================================================
4 // Copyright © 2022 Lady [@ Lady’s Computer].
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/>.
10 import { bind
, call
, identity
, makeCallable
} from "./function.js";
11 import { getPrototype
, objectCreate
} from "./object.js";
15 * Returns the result of converting the provided value to A·S·C·I·I
21 * Returns the result of converting the provided value to A·S·C·I·I
27 toLowerCase
: stringToLowercase
,
28 toUpperCase
: stringToUppercase
,
31 asciiLowercase
: ($) =>
35 makeCallable(stringToLowercase
),
37 asciiUppercase
: ($) =>
41 makeCallable(stringToUppercase
),
48 * Returns an iterator over the code units in the string
49 * representation of the provided value.
54 * Returns an iterator over the codepoints in the string
55 * representation of the provided value.
60 * Returns an iterator over the scalar values in the string
61 * representation of the provided value.
63 * Codepoints which are not valid Unicode scalar values are replaced
69 * Returns the result of converting the provided value to a string of
70 * scalar values by replacing (unpaired) surrogate values with
76 iterator
: iteratorSymbol
,
77 toStringTag
: toStringTagSymbol
,
79 const { [iteratorSymbol
]: arrayIterator
} = Array
.prototype;
80 const arrayIteratorPrototype
= Object
.getPrototypeOf(
83 const { next
: arrayIteratorNext
} = arrayIteratorPrototype
;
84 const iteratorPrototype
= Object
.getPrototypeOf(
85 arrayIteratorPrototype
,
87 const { [iteratorSymbol
]: stringIterator
} = String
.prototype;
88 const stringIteratorPrototype
= Object
.getPrototypeOf(
91 const { next
: stringIteratorNext
} = stringIteratorPrototype
;
94 * An iterator object for iterating over code values (either code
95 * units or codepoints) in a string.
97 * ※ This constructor is not exposed.
99 const StringCodeValueIterator
= class extends identity
{
104 * Constructs a new string code value iterator from the provided
107 * If the provided base iterator is an array iterator, this is a
108 * code unit iterator. If the provided iterator is a string
109 * iterator and surrogates are allowed, this is a codepoint
110 * iterator. If the provided iterator is a string iterator and
111 * surrogates are not allowed, this is a scalar value iterator.
113 constructor(baseIterator
, allowSurrogates
= true) {
114 super(objectCreate(stringCodeValueIteratorPrototype
));
115 this.#allowSurrogates
= !!allowSurrogates
;
116 this.#baseIterator
= baseIterator
;
119 /** Provides the next code value in the iterator. */
121 const baseIterator
= this.#baseIterator
;
122 switch (getPrototype(baseIterator
)) {
123 case arrayIteratorPrototype
: {
124 // The base iterator is iterating over U·C·S characters.
128 } = call(arrayIteratorNext
, baseIterator
, []);
130 ? { value
: undefined, done
: true }
131 : { value
: getCodeUnit(ucsCharacter
, 0), done
: false };
133 case stringIteratorPrototype
: {
134 // The base iterator is iterating over Unicode characters.
138 } = call(stringIteratorNext
, baseIterator
, []);
140 // The base iterator has been exhausted.
141 return { value
: undefined, done
: true };
143 // The base iterator provided a character; yield the
145 const codepoint
= getCodepoint(character
, 0);
147 value
: this.#allowSurrogates
|| codepoint
<= 0xD7FF ||
156 // Should not be possible!
158 "Piscēs: Unrecognized base iterator type in %StringCodeValueIterator%.",
166 next
: stringCodeValueIteratorNext
,
167 } = StringCodeValueIterator
.prototype;
168 const stringCodeValueIteratorPrototype
= objectCreate(
174 value
: stringCodeValueIteratorNext
,
177 [toStringTagSymbol
]: {
180 value
: "String Code Value Iterator",
185 const scalarValueIterablePrototype
= {
189 stringCodeValueIteratorNext
,
190 new StringCodeValueIterator(
191 call(stringIterator
, this.source
, []),
202 new StringCodeValueIterator(call(arrayIterator
, $, [])),
204 new StringCodeValueIterator(
205 call(stringIterator
, $, []),
209 new StringCodeValueIterator(
210 call(stringIterator
, $, []),
213 scalarValueString
: ($) =>
214 stringFromCodepoints(...objectCreate(
215 scalarValueIterablePrototype
,
216 { source
: { value
: $ } },
222 * Returns an iterator over the codepoints in the string representation
223 * of the provided value according to the algorithm of
224 * String::[Symbol.iterator].
226 export const characters
= makeCallable(
227 String
.prototype[Symbol
.iterator
],
231 * Returns the character at the provided position in the string
232 * representation of the provided value according to the algorithm of
233 * String::codePointAt.
235 export const getCharacter
= ($, pos
) => {
236 const codepoint
= getCodepoint($, pos
);
237 return codepoint
== null
239 : stringFromCodepoints(codepoint
);
243 * Returns the code unit at the provided position in the string
244 * representation of the provided value according to the algorithm of
247 export const getCodeUnit
= makeCallable(String
.prototype.charCodeAt
);
250 * Returns the codepoint at the provided position in the string
251 * representation of the provided value according to the algorithm of
252 * String::codePointAt.
254 export const getCodepoint
= makeCallable(String
.prototype.codePointAt
);
257 * Returns the index of the first occurrence of the search string in
258 * the string representation of the provided value according to the
259 * algorithm of String::indexOf.
261 export const getFirstSubstringIndex
= makeCallable(
262 String
.prototype.indexOf
,
266 * Returns the index of the last occurrence of the search string in the
267 * string representation of the provided value according to the
268 * algorithm of String::lastIndexOf.
270 export const getLastSubstringIndex
= makeCallable(
271 String
.prototype.lastIndexOf
,
275 * Returns the result of joining the provided iterable.
277 * If no separator is provided, it defaults to ",".
279 * If a value is nullish, it will be stringified as the empty string.
281 export const join
= (() => {
282 const { join
: arrayJoin
} = Array
.prototype;
283 const join
= ($, separator
= ",") =>
284 call(arrayJoin
, [...$], [`${separator}`]);
290 * Returns a string created from the raw value of the tagged template
293 * ※ This is an alias for String.raw.
298 * Returns a string created from the provided code units.
300 * ※ This is an alias for String.fromCharCode.
302 fromCharCode
: stringFromCodeUnits
,
305 * Returns a string created from the provided codepoints.
307 * ※ This is an alias for String.fromCodePoint.
309 fromCodePoint
: stringFromCodepoints
,
313 * Returns the result of splitting the provided value on A·S·C·I·I
316 export const splitOnASCIIWhitespace
= ($) =>
317 stringSplit(stripAndCollapseASCIIWhitespace($), " ");
320 * Returns the result of splitting the provided value on commas,
321 * trimming A·S·C·I·I whitespace from the resulting tokens.
323 export const splitOnCommas
= ($) =>
325 stripLeadingAndTrailingASCIIWhitespace(
328 /[\n\r\t\f ]*,[\n\r\t\f ]*/gu,
336 * Returns the result of catenating the string representations of the
337 * provided values, returning a new string according to the algorithm
340 export const stringCatenate
= makeCallable(String
.prototype.concat
);
343 * Returns whether the string representation of the provided value ends
344 * with the provided search string according to the algorithm of
347 export const stringEndsWith
= makeCallable(String
.prototype.endsWith
);
350 * Returns whether the string representation of the provided value
351 * contains the provided search string according to the algorithm of
354 export const stringIncludes
= makeCallable(String
.prototype.includes
);
357 * Returns the result of matching the string representation of the
358 * provided value with the provided matcher according to the algorithm
361 export const stringMatch
= makeCallable(String
.prototype.match
);
364 * Returns the result of matching the string representation of the
365 * provided value with the provided matcher according to the algorithm
366 * of String::matchAll.
368 export const stringMatchAll
= makeCallable(String
.prototype.matchAll
);
371 * Returns the normalized form of the string representation of the
372 * provided value according to the algorithm of String::matchAll.
374 export const stringNormalize
= makeCallable(
375 String
.prototype.normalize
,
379 * Returns the result of padding the end of the string representation
380 * of the provided value padded until it is the desired length
381 * according to the algorithm of String::padEnd.
383 export const stringPadEnd
= makeCallable(String
.prototype.padEnd
);
386 * Returns the result of padding the start of the string representation
387 * of the provided value padded until it is the desired length
388 * according to the algorithm of String::padStart.
390 export const stringPadStart
= makeCallable(String
.prototype.padStart
);
393 * Returns the result of repeating the string representation of the
394 * provided value the provided number of times according to the
395 * algorithm of String::repeat.
397 export const stringRepeat
= makeCallable(String
.prototype.repeat
);
400 * Returns the result of replacing the string representation of the
401 * provided value with the provided replacement, using the provided
402 * matcher and according to the algorithm of String::replace.
404 export const stringReplace
= makeCallable(String
.prototype.replace
);
407 * Returns the result of replacing the string representation of the
408 * provided value with the provided replacement, using the provided
409 * matcher and according to the algorithm of String::replaceAll.
411 export const stringReplaceAll
= makeCallable(
412 String
.prototype.replaceAll
,
416 * Returns the result of searching the string representation of the
417 * provided value using the provided matcher and according to the
418 * algorithm of String::search.
420 export const stringSearch
= makeCallable(String
.prototype.search
);
423 * Returns a slice of the string representation of the provided value
424 * according to the algorithm of String::slice.
426 export const stringSlice
= makeCallable(String
.prototype.slice
);
429 * Returns the result of splitting of the string representation of the
430 * provided value on the provided separator according to the algorithm
433 export const stringSplit
= makeCallable(String
.prototype.split
);
436 * Returns whether the string representation of the provided value
437 * starts with the provided search string according to the algorithm of
438 * String::startsWith.
440 export const stringStartsWith
= makeCallable(
441 String
.prototype.startsWith
,
445 * Returns the `[[StringData]]` of the provided value.
447 * ☡ This function will throw if the provided object does not have a
448 * `[[StringData]]` internal slot.
450 export const stringValue
= makeCallable(String
.prototype.valueOf
);
453 * Returns the result of stripping leading and trailing A·S·C·I·I
454 * whitespace from the provided value and collapsing other A·S·C·I·I
455 * whitespace in the string representation of the provided value.
457 export const stripAndCollapseASCIIWhitespace
= ($) =>
458 stripLeadingAndTrailingASCIIWhitespace(
467 * Returns the result of stripping leading and trailing A·S·C·I·I
468 * whitespace from the string representation of the provided value.
470 export const stripLeadingAndTrailingASCIIWhitespace
= (() => {
471 const { exec
: reExec
} = RegExp
.prototype;
473 call(reExec
, /^[\n\r\t\f ]*([^]*?)[\n\r\t\f ]*$/u, [$])[1];
477 * Returns a substring of the string representation of the provided
478 * value according to the algorithm of String::substring.
480 export const substring
= makeCallable(String
.prototype.substring
);