feat: implement a lot of built-ins

This commit is contained in:
TopchetoEU 2024-12-25 02:48:04 +02:00
parent c18015b9a0
commit c7c31c3859
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
10 changed files with 189 additions and 40 deletions

View File

@ -12,6 +12,8 @@ import { RegExp } from "./regex.ts";
import { Date } from "./date.ts"; import { Date } from "./date.ts";
import { Math as _Math } from "./math.ts"; import { Math as _Math } from "./math.ts";
import { Set, WeakSet } from "./set.ts"; import { Set, WeakSet } from "./set.ts";
import { JSON } from "./json.ts";
import { encodeURI, encodeURIComponent } from "./url.ts";
declare global { declare global {
function print(...args: any[]): void; function print(...args: any[]): void;
@ -47,12 +49,14 @@ target.WeakSet = fixup(WeakSet);
target.RegExp = fixup(RegExp); target.RegExp = fixup(RegExp);
target.Date = fixup(Date); target.Date = fixup(Date);
target.Math = object.setPrototype(_Math, Object.prototype); target.Math = object.setPrototype(_Math, Object.prototype);
target.JSON = object.setPrototype(JSON, Object.prototype);
target.parseInt = Number.parseInt; target.parseInt = Number.parseInt;
target.parseFloat = Number.parseFloat; target.parseFloat = Number.parseFloat;
target.NaN = Number.NaN; target.NaN = Number.NaN;
target.Infinity = Number.POSITIVE_INFINITY; target.Infinity = Number.POSITIVE_INFINITY;
target.encodeURI = encodeURI;
target.encodeURIComponent = encodeURIComponent;
setGlobalPrototypes({ setGlobalPrototypes({
string: String.prototype, string: String.prototype,

View File

@ -26,6 +26,7 @@ export const Array = (() => {
return string.stringBuild(parts); return string.stringBuild(parts);
} }
public push(this: any[]) { public push(this: any[]) {
const start = this.length; const start = this.length;
for (let i = arguments.length - 1; i >= 0; i--) { for (let i = arguments.length - 1; i >= 0; i--) {
@ -33,6 +34,38 @@ export const Array = (() => {
} }
return arguments.length; 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[]) { public concat(this: any[]) {
const res: any[] = []; const res: any[] = [];
@ -71,7 +104,10 @@ export const Array = (() => {
return res; 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); start = limitI(wrapI(start, this.length), this.length);
count = limitI(wrapI(count, this.length), this.length - start); count = limitI(wrapI(count, this.length), this.length - start);
@ -105,7 +141,7 @@ export const Array = (() => {
const res = []; const res = [];
res.length = this.length; 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]); 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) { public filter(this: any[], cb: Function, self?: any) {
const res = []; 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]; if (i in this && func.invoke(cb, self, [this[i], i, this])) res[res.length] = this[i];
} }
return res; return res;
} }
public some(this: any[], cb: Function, self?: any) { 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; if (i in this && func.invoke(cb, self, [this[i], i, this])) return true;
} }
return false; 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[]) { public [symbols.iterator](this: any[]) {
let i = 0; let i = 0;
let arr: any[] | undefined = this; let arr: any[] | undefined = func.invoke(Array.prototype.slice, this, []);
return { return {
next() { next() {
if (arr == null) return { done: true, value: undefined }; if (arr == null) return { done: true, value: undefined };
if (i >= arr.length) { if (i >= arr.length) {
arr = undefined; arr = undefined;
return { done: true, value: undefined }; return { done: true, value: undefined };
} }
else {
const val = arr[i++]; while (true) {
if (i >= arr.length) arr = undefined; const res = arr![i];
return { done: false, value: val };
if (i in arr!) {
i++;
return { done: false, value: res };
}
else i++;
} }
}, },
[symbols.iterator]() { return this; } [symbols.iterator]() { return this; }
@ -155,8 +214,14 @@ export const Array = (() => {
res.length = len; res.length = len;
return res as any; return res as any;
} }
// TODO: Implement spreading else {
else throw new Error("Spreading not implemented"); 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[] { public static isArray(val: any): val is any[] {

15
src/lib/libs/json.ts Normal file
View File

@ -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);
});

View File

@ -7,6 +7,10 @@ const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any;
export class Map<K, V> { export class Map<K, V> {
private [mapKey]: InstanceType<typeof map>; private [mapKey]: InstanceType<typeof map>;
public get size() {
return this[mapKey].size();
}
public get(key: K): V { public get(key: K): V {
return this[mapKey].get(key); return this[mapKey].get(key);
} }

View File

@ -16,6 +16,22 @@ method("max", function max() {
return res; 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) { method("floor", function floor(val: number) {
val = val - 0; val = val - 0;
if (number.isNaN(val)) return number.NaN; if (number.isNaN(val)) return number.NaN;
@ -25,3 +41,7 @@ method("floor", function floor(val: number) {
return val - rem; return val - rem;
}); });
method("pow", function pow(a: number, b: number) {
return number.pow(a, b);
});

View File

@ -10,6 +10,8 @@ export interface NumberPrimordials {
isNaN(num: number): boolean; isNaN(num: number): boolean;
NaN: number; NaN: number;
Infinity: number; Infinity: number;
pow(a: number, b: number): number;
} }
export interface StringPrimordials { export interface StringPrimordials {
stringBuild(parts: string[]): string; stringBuild(parts: string[]): string;
@ -37,6 +39,7 @@ export interface ObjectPrimordials {
freeze(obj: object): void; freeze(obj: object): void;
memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void; memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void;
sort(arr: any[], cb: Function): any[];
} }
export interface FunctionPrimordials { export interface FunctionPrimordials {
invokeType(args: IArguments, self: any): "new" | "call"; invokeType(args: IArguments, self: any): "new" | "call";
@ -66,9 +69,10 @@ export interface Primordials {
delete(key: any): void; delete(key: any): void;
keys(): any[]; keys(): any[];
clear(): void; 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; exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null;
groupCount(): number; groupCount(): number;
}; };
@ -94,3 +98,5 @@ export const {
compile, compile,
now, now,
} = primordials; } = primordials;
export type regex = InstanceType<typeof regex>;

View File

@ -7,6 +7,10 @@ const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any;
export class Set<T> { export class Set<T> {
private [mapKey]: InstanceType<typeof map>; private [mapKey]: InstanceType<typeof map>;
public get size() {
return this[mapKey].size();
}
public has(key: T): boolean { public has(key: T): boolean {
return this[mapKey].has(key); return this[mapKey].has(key);
} }

View File

@ -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 { RegExp } from "./regex.ts";
import { applyReplaces, applySplits, limitI, ReplaceRange, symbols, unwrapThis, valueKey, wrapI } from "./utils.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 = (() => { export const String = (() => {
class String { class String {
[valueKey]!: string; [valueKey]!: string;
@ -33,6 +36,31 @@ export const String = (() => {
return string.indexOf(self, search, offset, true); 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) { public charAt(i: number) {
const self = unwrapThis(this, "string", String, "String.prototype.charAt"); const self = unwrapThis(this, "string", String, "String.prototype.charAt");
return self[i]; return self[i];
@ -43,7 +71,7 @@ export const String = (() => {
} }
public codePointAt(i: number) { public codePointAt(i: number) {
const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt"); 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) { public split(val?: any, limit?: number) {
@ -159,7 +187,7 @@ export const String = (() => {
this[valueKey] = (String as any)(value); this[valueKey] = (String as any)(value);
} }
public static fromCharCode(...args: number[]) { public static fromCharCode() {
const res: string[] = []; const res: string[] = [];
res[arguments.length] = ""; res[arguments.length] = "";
@ -169,7 +197,7 @@ export const String = (() => {
return string.stringBuild(res); return string.stringBuild(res);
} }
public static fromCodePoint(...args: number[]) { public static fromCodePoint() {
const res: string[] = []; const res: string[] = [];
res[arguments.length] = ""; res[arguments.length] = "";

View File

@ -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());

26
src/lib/libs/url.ts Normal file
View File

@ -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\\-+.!~*'();/?:@&=+$,#]"));