2025-01-07 02:58:12 +11:00
|
|
|
export const jit_format = Symbol.for(`re.lua.lstd.jit_format`);
|
|
|
|
|
|
|
|
export interface JitFragment {
|
|
|
|
[jit_format](c: JitCompiler): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function is_jit(x: unknown): x is JitFragment {
|
|
|
|
return typeof x === "object" && x !== null && jit_format in x;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class JitCompiler {
|
|
|
|
#body = "";
|
|
|
|
#args = new Map<unknown, string>();
|
|
|
|
|
|
|
|
write(s: string | JitFragment) {
|
|
|
|
if (is_jit(s)) s[jit_format](this);
|
|
|
|
else this.#body += s;
|
|
|
|
}
|
|
|
|
|
|
|
|
enclose(x: unknown) {
|
2025-01-10 04:05:35 +11:00
|
|
|
switch (typeof x) {
|
|
|
|
case "boolean":
|
|
|
|
case "number":
|
|
|
|
return this.write(`${x}`);
|
|
|
|
case "bigint":
|
|
|
|
return this.write(`${x}n`);
|
|
|
|
case "string":
|
|
|
|
return this.write(JSON.stringify(x));
|
|
|
|
case "object":
|
|
|
|
if (x === null) return this.write(`null`);
|
|
|
|
break;
|
|
|
|
case "undefined":
|
|
|
|
return this.write(`undefined`);
|
|
|
|
}
|
|
|
|
|
2025-01-07 02:58:12 +11:00
|
|
|
let name = this.#args.get(x);
|
|
|
|
if (!name) this.#args.set(x, (name = `__x${this.#args.size}`));
|
|
|
|
this.write(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
format(x: unknown) {
|
|
|
|
is_jit(x) ? this.write(x) : this.enclose(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
compile() {
|
|
|
|
return new Function(
|
|
|
|
...this.#args.values(),
|
2025-01-07 21:46:23 +11:00
|
|
|
`"use strict"; return (${this.#body});`
|
2025-01-07 02:58:12 +11:00
|
|
|
)(...this.#args.keys());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function jit(
|
|
|
|
{ raw: s }: TemplateStringsArray,
|
|
|
|
...xs: unknown[]
|
|
|
|
): JitFragment {
|
|
|
|
return {
|
|
|
|
[jit_format](c) {
|
|
|
|
for (let i = 0, n = s.length; i < n; i++) {
|
|
|
|
if (i !== 0) c.format(xs[i - 1]);
|
|
|
|
c.write(s[i]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
jit.compiled = compiled;
|
|
|
|
jit.raw = raw;
|
|
|
|
jit.fragment = fragment;
|
|
|
|
jit.map = map;
|
|
|
|
jit.if = condition;
|
|
|
|
|
|
|
|
export function compiled<T = unknown>(
|
|
|
|
s: TemplateStringsArray,
|
|
|
|
...xs: unknown[]
|
|
|
|
): T {
|
|
|
|
const c = new JitCompiler();
|
|
|
|
return c.write(jit(s, ...xs)), c.compile();
|
|
|
|
}
|
|
|
|
|
|
|
|
export function raw(s: TemplateStringsArray, ...xs: unknown[]): JitFragment;
|
|
|
|
export function raw(s: string): JitFragment;
|
|
|
|
export function raw(
|
|
|
|
s: TemplateStringsArray | string,
|
|
|
|
...xs: unknown[]
|
|
|
|
): JitFragment {
|
|
|
|
s = typeof s === "string" ? s : String.raw(s, ...xs);
|
|
|
|
return {
|
|
|
|
[jit_format](c) {
|
|
|
|
c.write(s);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function fragment(
|
|
|
|
sep: string | JitFragment,
|
|
|
|
...xs: unknown[]
|
|
|
|
): JitFragment {
|
|
|
|
return {
|
|
|
|
[jit_format](c) {
|
|
|
|
for (let i = 0, n = xs.length; i < n; i++) {
|
|
|
|
if (i !== 0) c.write(sep);
|
|
|
|
c.format(xs[i]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function map<T>(
|
|
|
|
sep: string | JitFragment,
|
|
|
|
xs: Iterable<T>,
|
2025-01-07 21:46:23 +11:00
|
|
|
f: (value: T, index: number) => unknown
|
2025-01-07 02:58:12 +11:00
|
|
|
): JitFragment {
|
|
|
|
return fragment(sep, ...Iterator.from(xs).map(f));
|
|
|
|
}
|
|
|
|
|
|
|
|
export function condition(
|
|
|
|
test: unknown,
|
|
|
|
consequent: JitFragment,
|
2025-01-07 21:46:23 +11:00
|
|
|
alternate: JitFragment = jit``
|
2025-01-07 02:58:12 +11:00
|
|
|
): JitFragment {
|
|
|
|
return test ? consequent : alternate;
|
|
|
|
}
|