// The stdlib, compiled in front of every program // // Built on the natives in compiler/src/native.rs, the common layer of // operations on primitive values. The compiler's own code for operators (`+` on // strings and so on) uses those natives directly, not these functions. // // Restrictions: // - The prototypes linked with `__link_proto_*` are fixed. User code may not // change them (no monkey-patching) // - `new String(x)`, `new Number(x)` and `new Boolean(x)` (wrapper objects) are // not supported. These functions only convert // // Needs `typeof`. `console.log` and `String.prototype.concat` also need rest // params, which need arrays const NaN = 0 / 0; const Infinity = 1 / 0; // ─────────────────────────────────────────────── Object ─────────────────────────────────────────────── // `new Object()` makes an empty object. Called without `new`, it does the same function Object() { return {}; } Object.prototype = {}; Object.create = function (proto) { return { __proto__: proto }; }; // ─────────────────────────────────────────────── String ─────────────────────────────────────────────── // 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; }, concat(...parts) { let s = this; for (let i = 0; i < parts.length; i++) { s = __native_string_concat(s, String(parts[i])); } return s; }, // ── 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); } // ─────────────────────────────────────────────── Number ─────────────────────────────────────────────── // Converts any value to a number, like the spec's ToNumber function Number(x) { switch (typeof x) { case "number": return x; case "string": return __native_string_to_number(x); case "boolean": return x ? 1 : 0; case "undefined": return NaN; case "object": return x === null ? 0 : NaN; default: return NaN; } } Number.NaN = NaN; Number.POSITIVE_INFINITY = Infinity; Number.NEGATIVE_INFINITY = -Infinity; Number.MAX_SAFE_INTEGER = 9007199254740991; Number.MIN_SAFE_INTEGER = -9007199254740991; Number.isNaN = function (x) { return typeof x === "number" && x !== x; }; Number.isFinite = function (x) { return typeof x === "number" && x === x && x !== Infinity && x !== -Infinity; }; Number.isInteger = function (x) { return Number.isFinite(x) && x % 1 === 0; }; // In these methods `this` is the primitive number itself Number.prototype = { toString(radix) { if (radix === undefined || radix === 10) return __native_number_to_string(this); return integerToString(this, radix); }, toFixed(digits) { return __native_number_to_fixed(this, digits === undefined ? 0 : digits); }, valueOf() { return this; }, }; __link_proto_number(Number.prototype); // An integer in `radix` (2 to 36). Only integers: the fraction is dropped function integerToString(n, radix) { let negative = n < 0; if (negative) n = -n; n = n - (n % 1); let out = ""; do { let digit = n % radix; out = __native_string_concat(__native_string_from_char_code(digit < 10 ? 48 + digit : 87 + digit), out); n = (n - digit) / radix; } while (n > 0); return negative ? __native_string_concat("-", out) : out; } // ─────────────────────────────────────────────── Boolean ────────────────────────────────────────────── // Converts any value to a boolean, like the spec's ToBoolean function Boolean(x) { return !!x; } // In these methods `this` is the primitive boolean itself Boolean.prototype = { toString() { return this ? "true" : "false"; }, valueOf() { return this; }, }; __link_proto_boolean(Boolean.prototype); // ─────────────────────────────────────────────── console ────────────────────────────────────────────── const console = { log(...args) { let line = ""; for (let i = 0; i < args.length; i++) { if (i > 0) line = __native_string_concat(line, " "); line = __native_string_concat(line, String(args[i])); } __native_print_string(line); }, };