From c7c31c385967cb47ce03999f7e2f5b31a17e59f1 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Wed, 25 Dec 2024 02:48:04 +0200 Subject: [PATCH] feat: implement a lot of built-ins --- src/lib/libs/_entry.ts | 6 ++- src/lib/libs/array.ts | 87 ++++++++++++++++++++++++++++++++----- src/lib/libs/json.ts | 15 +++++++ src/lib/libs/map.ts | 4 ++ src/lib/libs/math.ts | 20 +++++++++ src/lib/libs/primordials.ts | 8 +++- src/lib/libs/set.ts | 4 ++ src/lib/libs/string.ts | 36 +++++++++++++-- src/lib/libs/test.js | 23 ---------- src/lib/libs/url.ts | 26 +++++++++++ 10 files changed, 189 insertions(+), 40 deletions(-) create mode 100644 src/lib/libs/json.ts delete mode 100644 src/lib/libs/test.js create mode 100644 src/lib/libs/url.ts diff --git a/src/lib/libs/_entry.ts b/src/lib/libs/_entry.ts index 1ac7458..2358c1d 100644 --- a/src/lib/libs/_entry.ts +++ b/src/lib/libs/_entry.ts @@ -12,6 +12,8 @@ import { RegExp } from "./regex.ts"; import { Date } from "./date.ts"; import { Math as _Math } from "./math.ts"; import { Set, WeakSet } from "./set.ts"; +import { JSON } from "./json.ts"; +import { encodeURI, encodeURIComponent } from "./url.ts"; declare global { function print(...args: any[]): void; @@ -47,12 +49,14 @@ target.WeakSet = fixup(WeakSet); target.RegExp = fixup(RegExp); target.Date = fixup(Date); target.Math = object.setPrototype(_Math, Object.prototype); +target.JSON = object.setPrototype(JSON, Object.prototype); target.parseInt = Number.parseInt; target.parseFloat = Number.parseFloat; target.NaN = Number.NaN; target.Infinity = Number.POSITIVE_INFINITY; - +target.encodeURI = encodeURI; +target.encodeURIComponent = encodeURIComponent; setGlobalPrototypes({ string: String.prototype, diff --git a/src/lib/libs/array.ts b/src/lib/libs/array.ts index 84bab28..a47c9be 100644 --- a/src/lib/libs/array.ts +++ b/src/lib/libs/array.ts @@ -26,6 +26,7 @@ export const Array = (() => { return string.stringBuild(parts); } + public push(this: any[]) { const start = this.length; for (let i = arguments.length - 1; i >= 0; i--) { @@ -33,6 +34,38 @@ export const Array = (() => { } return arguments.length; } + public pop(this: any[]) { + if (this.length === 0) return undefined; + else { + const res = this[this.length - 1]; + this.length--; + return res; + } + } + + public unshift(this: any[]) { + for (let i = this.length + arguments.length - 1; i >= arguments.length; i--) { + this[i] = this[i - arguments.length]; + } + for (let i = 0; i < arguments.length; i++) { + this[i] = arguments[i]; + } + return arguments.length; + } + public shift(this: any[]) { + if (this.length === 0) return undefined; + + const tmp = this[0]; + + for (let i = 1; i < this.length; i++) { + this[i - 1] = this[i]; + } + + this.length--; + + return tmp; + } + public concat(this: any[]) { const res: any[] = []; @@ -71,7 +104,10 @@ export const Array = (() => { return res; } - public splice(this: any[], start = 0, count = this.length - start, ...vals: any[]) { + public splice(this: any[], start = 0, count = this.length - start) { + const vals: any[] = [] + for (let i = 0; i < arguments.length - 2; i++) vals[i] = arguments[i + 2]; + start = limitI(wrapI(start, this.length), this.length); count = limitI(wrapI(count, this.length), this.length - start); @@ -105,7 +141,7 @@ export const Array = (() => { const res = []; res.length = this.length; - for (let i = 0; i < arguments.length; i++) { + for (let i = 0; i < this.length; i++) { if (i in this) res[i] = func.invoke(cb, self, [this[i], i, this]); } @@ -114,35 +150,58 @@ export const Array = (() => { public filter(this: any[], cb: Function, self?: any) { const res = []; - for (let i = 0; i < arguments.length; i++) { + for (let i = 0; i < this.length; i++) { if (i in this && func.invoke(cb, self, [this[i], i, this])) res[res.length] = this[i]; } return res; } public some(this: any[], cb: Function, self?: any) { - for (let i = 0; i < arguments.length; i++) { + for (let i = 0; i < this.length; i++) { if (i in this && func.invoke(cb, self, [this[i], i, this])) return true; } return false; } + public find(this: any[], cb: Function, self?: any) { + for (let i = 0; i < this.length; i++) { + if (i in this && func.invoke(cb, self, [this[i], i, this])) return this[i]; + } + + return undefined; + } + + public sort(this: any[], cb?: Function) { + cb ||= (a: any, b: any) => { + if (String(a) < String(b)) return -1; + if (String(a) === String(b)) return 0; + return 1; + }; + + return object.sort(this, cb); + } public [symbols.iterator](this: any[]) { let i = 0; - let arr: any[] | undefined = this; + let arr: any[] | undefined = func.invoke(Array.prototype.slice, this, []); return { next() { if (arr == null) return { done: true, value: undefined }; + if (i >= arr.length) { arr = undefined; return { done: true, value: undefined }; } - else { - const val = arr[i++]; - if (i >= arr.length) arr = undefined; - return { done: false, value: val }; + + while (true) { + const res = arr![i]; + + if (i in arr!) { + i++; + return { done: false, value: res }; + } + else i++; } }, [symbols.iterator]() { return this; } @@ -155,8 +214,14 @@ export const Array = (() => { res.length = len; return res as any; } - // TODO: Implement spreading - else throw new Error("Spreading not implemented"); + else { + const res: any[] = []; + res.length = arguments.length; + for (let i = 0; i < arguments.length; i++) { + res[i] = arguments[i]; + } + return res as any; + } } public static isArray(val: any): val is any[] { diff --git a/src/lib/libs/json.ts b/src/lib/libs/json.ts new file mode 100644 index 0000000..0e2f06a --- /dev/null +++ b/src/lib/libs/json.ts @@ -0,0 +1,15 @@ +import { json, object } from "./primordials"; + +export const JSON = {}; + +function method(name: string, func: Function) { + object.defineField(JSON, name, { c: true, e: false, w: true, v: func }); +} + +method("parse", function parse(val: string) { + return json.parse(val); +}); + +method("stringify", function stringify(val: string) { + return json.stringify(val); +}); diff --git a/src/lib/libs/map.ts b/src/lib/libs/map.ts index 3611c56..7a6a929 100644 --- a/src/lib/libs/map.ts +++ b/src/lib/libs/map.ts @@ -7,6 +7,10 @@ const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any; export class Map { private [mapKey]: InstanceType; + public get size() { + return this[mapKey].size(); + } + public get(key: K): V { return this[mapKey].get(key); } diff --git a/src/lib/libs/math.ts b/src/lib/libs/math.ts index 8de38fe..c7b2ad5 100644 --- a/src/lib/libs/math.ts +++ b/src/lib/libs/math.ts @@ -16,6 +16,22 @@ method("max", function max() { return res; }); +method("min", function min() { + let res = +number.Infinity; + + for (let i = 0; i < arguments.length; i++) { + if (res > arguments[i]) res = arguments[i]; + } + + return res; +}); + +method("abs", function abs(val: number) { + val = +val; + if (val < 0) return -val; + else return val; +}); + method("floor", function floor(val: number) { val = val - 0; if (number.isNaN(val)) return number.NaN; @@ -25,3 +41,7 @@ method("floor", function floor(val: number) { return val - rem; }); + +method("pow", function pow(a: number, b: number) { + return number.pow(a, b); +}); diff --git a/src/lib/libs/primordials.ts b/src/lib/libs/primordials.ts index 6899ce3..947cfb8 100644 --- a/src/lib/libs/primordials.ts +++ b/src/lib/libs/primordials.ts @@ -10,6 +10,8 @@ export interface NumberPrimordials { isNaN(num: number): boolean; NaN: number; Infinity: number; + + pow(a: number, b: number): number; } export interface StringPrimordials { stringBuild(parts: string[]): string; @@ -37,6 +39,7 @@ export interface ObjectPrimordials { freeze(obj: object): void; memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void; + sort(arr: any[], cb: Function): any[]; } export interface FunctionPrimordials { invokeType(args: IArguments, self: any): "new" | "call"; @@ -66,9 +69,10 @@ export interface Primordials { delete(key: any): void; keys(): any[]; clear(): void; + size(): number; }; - regex: new (source: string, multiline: boolean, noCase: boolean, dotall: boolean, unicode: boolean, unicodeClass: boolean) => { + regex: new (source: string, multiline?: boolean, noCase?: boolean, dotall?: boolean, unicode?: boolean, unicodeClass?: boolean) => { exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null; groupCount(): number; }; @@ -94,3 +98,5 @@ export const { compile, now, } = primordials; + +export type regex = InstanceType; \ No newline at end of file diff --git a/src/lib/libs/set.ts b/src/lib/libs/set.ts index 7e10d1c..ea960ca 100644 --- a/src/lib/libs/set.ts +++ b/src/lib/libs/set.ts @@ -7,6 +7,10 @@ const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any; export class Set { private [mapKey]: InstanceType; + public get size() { + return this[mapKey].size(); + } + public has(key: T): boolean { return this[mapKey].has(key); } diff --git a/src/lib/libs/string.ts b/src/lib/libs/string.ts index 8a1c713..a2ea97b 100644 --- a/src/lib/libs/string.ts +++ b/src/lib/libs/string.ts @@ -1,7 +1,10 @@ -import { func, number, string } from "./primordials.ts"; +import { func, number, regex, string } from "./primordials.ts"; import { RegExp } from "./regex.ts"; import { applyReplaces, applySplits, limitI, ReplaceRange, symbols, unwrapThis, valueKey, wrapI } from "./utils.ts"; +const trimStartRegex = new regex("^\\s+", false, false, false, false, false); +const trimEndRegex = new regex("\\s+$", false, false, false, false, false); + export const String = (() => { class String { [valueKey]!: string; @@ -33,6 +36,31 @@ export const String = (() => { return string.indexOf(self, search, offset, true); } + public trim() { + const self = unwrapThis(this, "string", String, "String.prototype.trim"); + const start = trimStartRegex.exec(self, 0, false); + const end = trimEndRegex.exec(self, 0, false); + + const startI = start == null ? 0 : start.end; + const endI = end == null ? self.length : end.matches.index!; + + return string.substring(self, startI, endI); + } + public trimStart() { + const self = unwrapThis(this, "string", String, "String.prototype.trim"); + const start = trimStartRegex.exec(self, 0, false); + const startI = start == null ? 0 : start.end; + + return string.substring(self, startI, self.length); + } + public trimEnd() { + const self = unwrapThis(this, "string", String, "String.prototype.trim"); + const end = trimEndRegex.exec(self, 0, false); + const endI = end == null ? self.length : end.matches.index!; + + return string.substring(self, 0, endI); + } + public charAt(i: number) { const self = unwrapThis(this, "string", String, "String.prototype.charAt"); return self[i]; @@ -43,7 +71,7 @@ export const String = (() => { } public codePointAt(i: number) { const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt"); - return i > 0 && i <= self.length ? string.toCodePoint(self, i) : number.NaN; + return i >= 0 && i < self.length ? string.toCodePoint(self, i) : number.NaN; } public split(val?: any, limit?: number) { @@ -159,7 +187,7 @@ export const String = (() => { this[valueKey] = (String as any)(value); } - public static fromCharCode(...args: number[]) { + public static fromCharCode() { const res: string[] = []; res[arguments.length] = ""; @@ -169,7 +197,7 @@ export const String = (() => { return string.stringBuild(res); } - public static fromCodePoint(...args: number[]) { + public static fromCodePoint() { const res: string[] = []; res[arguments.length] = ""; diff --git a/src/lib/libs/test.js b/src/lib/libs/test.js deleted file mode 100644 index 4ef63df..0000000 --- a/src/lib/libs/test.js +++ /dev/null @@ -1,23 +0,0 @@ -function split(val, limit) { - var self = unwrapThis(this, "string", String, "String.prototype.split"); - if (val === undefined) return [self]; - if (val !== null && typeof val === "object" && Symbol.split in val) { - return val[Symbol.split](self, limit); - } - val = String(val); - var offset = 0; - var res = []; - while (true) { - var start = string.indexOf(self, val, offset, false); - if (start < 0) { - res[res.length] = string.substring(self, offset, self.length); - break; - } - var end = start + val.length; - res[res.length] = string.substring(self, offset, start); - offset = end; - } - return res; -} - -split(print(), print()); \ No newline at end of file diff --git a/src/lib/libs/url.ts b/src/lib/libs/url.ts new file mode 100644 index 0000000..d7c3947 --- /dev/null +++ b/src/lib/libs/url.ts @@ -0,0 +1,26 @@ +import { regex, string } from "./primordials"; + +function escaper(matcher: regex) { + return (text: string) => { + const parts: string[] = []; + let i = 0; + + while (true) { + const match = matcher.exec(text, i, false); + if (match == null) break; + + const char = match.matches[0]; + const code = string.toCharCode(char); + parts[parts.length] = string.substring(text, i, match.matches.index!); + parts[parts.length] = "%" + code; + i = match.end; + } + + parts[parts.length] = string.substring(text, i, text.length); + + return string.stringBuild(parts); + }; +} + +export const encodeURI = escaper(new regex("[^A-Za-z0-9\\-+.!~*'()]")); +export const encodeURIComponent = escaper(new regex("[^A-Za-z0-9\\-+.!~*'();/?:@&=+$,#]")); \ No newline at end of file