Files
jsrt/lib/stdlib/string.js
T
2026-10-03 14:06:24 +00:00

144 lines
5.0 KiB
JavaScript

// Converts any value to a string, like the spec's ToString
function String(x) {
switch (typeof x) {
case "string": return x;
case "number": return __native_number_to_string(x);
case "boolean": return x ? "true" : "false";
case "undefined": return "undefined";
case "function": return "function () { [native code] }";
case "object":
if (x === null) return "null";
if (typeof x.toString === "function") return x.toString();
return "[object Object]";
}
}
String.fromCharCode = function (code) {
return __native_string_from_char_code(code);
};
// In these methods `this` is the primitive string itself
String.prototype = {
toString() { return this; },
valueOf() { return this; },
// ── Characters ──
charAt(i) {
if (i < 0 || i >= this.length) return "";
return __native_string_slice(this, i, i + 1);
},
charCodeAt(i) {
if (i < 0 || i >= this.length) return NaN;
return __native_string_char_code_at(this, i);
},
at(i) {
if (i < 0) i += this.length;
if (i < 0 || i >= this.length) return undefined;
return __native_string_slice(this, i, i + 1);
},
// ── Search ──
indexOf(search, from) {
let start = from === undefined ? 0 : clampIndex(from, this.length);
for (let i = start; i + search.length <= this.length; i++) {
if (__native_string_slice(this, i, i + search.length) === search) return i;
}
return -1;
},
lastIndexOf(search) {
for (let i = this.length - search.length; i >= 0; i--) {
if (__native_string_slice(this, i, i + search.length) === search) return i;
}
return -1;
},
includes(search, from) { return this.indexOf(search, from) !== -1; },
startsWith(search, from) {
let start = from === undefined ? 0 : clampIndex(from, this.length);
return __native_string_slice(this, start, start + search.length) === search;
},
endsWith(search, end) {
let stop = end === undefined ? this.length : clampIndex(end, this.length);
let start = stop - search.length;
return start >= 0 && __native_string_slice(this, start, stop) === search;
},
// ── Substrings ──
slice(start, end) {
let len = this.length;
let s = relativeIndex(start === undefined ? 0 : start, len);
let e = relativeIndex(end === undefined ? len : end, len);
return s < e ? __native_string_slice(this, s, e) : "";
},
substring(start, end) {
let len = this.length;
let s = clampIndex(start === undefined ? 0 : start, len);
let e = clampIndex(end === undefined ? len : end, len);
return s < e ? __native_string_slice(this, s, e) : __native_string_slice(this, e, s);
},
// ── Case ──
toUpperCase() { return mapCodes(this, 97, 122, -32); }, // a-z to A-Z
toLowerCase() { return mapCodes(this, 65, 90, 32); }, // A-Z to a-z
// ── Whitespace ──
trim() { return this.trimStart().trimEnd(); },
trimStart() {
let i = 0;
while (i < this.length && isSpace(__native_string_char_code_at(this, i))) i++;
return __native_string_slice(this, i, this.length);
},
trimEnd() {
let i = this.length;
while (i > 0 && isSpace(__native_string_char_code_at(this, i - 1))) i--;
return __native_string_slice(this, 0, i);
},
// ── Building ──
repeat(count) {
let s = "";
for (let i = 0; i < count; i++) s = __native_string_concat(s, this);
return s;
},
padStart(length, fill) { return pad(this, length, fill, true); },
padEnd(length, fill) { return pad(this, length, fill, false); },
};
__link_proto_string(String.prototype);
// An index clamped to [0, len]
function clampIndex(i, len) {
return i < 0 ? 0 : i > len ? len : i;
}
// An index where a negative value counts from the end, clamped to [0, len]
function relativeIndex(i, len) {
return clampIndex(i < 0 ? i + len : i, len);
}
// `s` with every char code in [lo, hi] moved by `shift`, for ASCII case mapping
function mapCodes(s, lo, hi, shift) {
let out = "";
for (let i = 0; i < s.length; i++) {
let c = __native_string_char_code_at(s, i);
if (c >= lo && c <= hi) c += shift;
out = __native_string_concat(out, __native_string_from_char_code(c));
}
return out;
}
function isSpace(c) {
// tab, line feed, vertical tab, form feed, carriage return, space, nbsp
return (c >= 9 && c <= 13) || c === 32 || c === 160;
}
function pad(s, length, fill, atStart) {
let f = fill === undefined ? " " : String(fill);
if (s.length >= length || f.length === 0) return s;
let padding = "";
while (padding.length < length - s.length) padding = __native_string_concat(padding, f);
padding = __native_string_slice(padding, 0, length - s.length);
return atStart ? __native_string_concat(padding, s) : __native_string_concat(s, padding);
}