gofront 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -466
- package/package.json +16 -20
- package/src/backend/js/expressions.js +50 -19
- package/src/backend/js/index.js +100 -55
- package/src/backend/js/runtime.js +102 -0
- package/src/backend/js/source-map.js +32 -0
- package/src/backend/js/statements.js +96 -16
- package/src/backend/js/stdlib/errors.js +2 -2
- package/src/backend/js/stdlib/fmt.js +26 -8
- package/src/backend/js/stdlib/index.js +3 -0
- package/src/backend/js/stdlib/maps.js +1 -1
- package/src/backend/js/stdlib/path.js +2 -2
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/backend/js/stdlib/slices.js +1 -1
- package/src/backend/js/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +34 -9
- package/src/backend/js/stdlib/strings.js +4 -1
- package/src/backend/js/stdlib/testing.js +2 -2
- package/src/backend/js/stdlib/time.js +3 -3
- package/src/backend/js/templ.js +1 -1
- package/src/backend/wasm/boundary.js +486 -33
- package/src/backend/wasm/emit-builtins.js +1103 -0
- package/src/backend/wasm/emit-exprs.js +1908 -0
- package/src/backend/wasm/emit-maps.js +1863 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +3006 -0
- package/src/backend/wasm/emit-stmts.js +2472 -0
- package/src/backend/wasm/emit.js +606 -5009
- package/src/backend/wasm/encode.js +212 -64
- package/src/backend/wasm/glue.js +19 -5
- package/src/backend/wasm/index.js +1426 -49
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +227 -0
- package/src/backend/wasm/types.js +159 -35
- package/src/backend/wasm/wat.js +49 -2
- package/src/cli-core.js +275 -72
- package/src/compiler.js +144 -21
- package/src/index.js +86 -115
- package/src/lower/boxing.js +3 -4
- package/src/lower/captures.js +10 -17
- package/src/lower/escape.js +4 -9
- package/src/lower/functions.js +277 -5
- package/src/lower/globals.js +7 -15
- package/src/lower/index.js +5 -2
- package/src/lower/ownership.js +5 -10
- package/src/lower/range.js +2 -5
- package/src/lower/walk.js +25 -0
- package/src/resolver.js +2 -1
- package/src/test-runner.js +20 -0
- package/src/typechecker/assignability.js +42 -1
- package/src/typechecker/expressions.js +142 -2
- package/src/typechecker/index.js +27 -1
- package/src/typechecker/resolve.js +35 -6
- package/src/typechecker/statements.js +0 -7
- package/src/typechecker/stdlib/core.js +17 -19
- package/src/typechecker/stdlib/extended.js +18 -3
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +206 -7
- package/src/vendor.js +2 -2
|
@@ -31,27 +31,35 @@ export const statementGenMethods = {
|
|
|
31
31
|
},
|
|
32
32
|
|
|
33
33
|
// Shared helper for comma-ok map index in both DefineStmt and AssignStmt.
|
|
34
|
-
// isDefine=true → emit `let v = m[k]; let ok = k in m;`
|
|
35
|
-
// isDefine=false → emit `v = k in m ? m[k] : zero; ok = k in m;`
|
|
36
34
|
_genCommaOkMapIndex(stmt, rhsNode, isDefine) {
|
|
37
35
|
const mapExpr = this.genExpr(rhsNode.expr);
|
|
38
36
|
const keyExpr = this.genExpr(rhsNode.index);
|
|
39
37
|
const [vName, okName] = stmt.lhs.map((e) => e.name ?? this.genExpr(e));
|
|
40
38
|
const decl = isDefine ? "let " : "";
|
|
41
|
-
if (
|
|
42
|
-
if (
|
|
39
|
+
if (isDefine && !this.strict) {
|
|
40
|
+
if (vName !== "_") {
|
|
43
41
|
this.line(`let ${vName} = ${mapExpr}[${keyExpr}];`);
|
|
44
|
-
} else {
|
|
45
|
-
const zero = rhsNode._mapValueType
|
|
46
|
-
? this.zeroValueForType(rhsNode._mapValueType)
|
|
47
|
-
: "undefined";
|
|
48
|
-
this.line(
|
|
49
|
-
`${vName} = (${keyExpr}) in ${mapExpr} ? ${mapExpr}[${keyExpr}] : ${zero};`,
|
|
50
|
-
);
|
|
51
42
|
}
|
|
43
|
+
if (okName !== "_") {
|
|
44
|
+
this.line(`let ${okName} = (${keyExpr}) in ${mapExpr};`);
|
|
45
|
+
}
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const zero = rhsNode._mapValueType
|
|
49
|
+
? this.zeroValueForType(rhsNode._mapValueType)
|
|
50
|
+
: "undefined";
|
|
51
|
+
this._tmpCounter = (this._tmpCounter ?? 0) + 1;
|
|
52
|
+
const mTmp = `__m${this._tmpCounter}`;
|
|
53
|
+
const kTmp = `__k${this._tmpCounter}`;
|
|
54
|
+
this.line(`const ${mTmp} = ${mapExpr}, ${kTmp} = ${keyExpr};`);
|
|
55
|
+
if (vName !== "_") {
|
|
56
|
+
this.line(
|
|
57
|
+
`${decl}${vName} = (${mTmp} && (${kTmp} in ${mTmp})) ? ${mTmp}[${kTmp}] : ${zero};`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (okName !== "_") {
|
|
61
|
+
this.line(`${decl}${okName} = Boolean(${mTmp} && (${kTmp} in ${mTmp}));`);
|
|
52
62
|
}
|
|
53
|
-
if (okName !== "_")
|
|
54
|
-
this.line(`${decl}${okName} = (${keyExpr}) in ${mapExpr};`);
|
|
55
63
|
},
|
|
56
64
|
|
|
57
65
|
genStmt(stmt) {
|
|
@@ -63,6 +71,33 @@ export const statementGenMethods = {
|
|
|
63
71
|
this.genTypeDeclWithMethods(stmt, []);
|
|
64
72
|
break;
|
|
65
73
|
case "IncDecStmt": {
|
|
74
|
+
if (stmt.expr.kind === "IndexExpr" && stmt.expr._mapValueType) {
|
|
75
|
+
const zero = this.zeroValueForType(stmt.expr._mapValueType);
|
|
76
|
+
const wrapField = this._namedWrapperField(
|
|
77
|
+
stmt.expr.expr._type,
|
|
78
|
+
stmt.expr.expr,
|
|
79
|
+
);
|
|
80
|
+
const base = wrapField
|
|
81
|
+
? `${this.genExpr(stmt.expr.expr)}.${wrapField}`
|
|
82
|
+
: this.genExpr(stmt.expr.expr);
|
|
83
|
+
const idx = this.genExpr(stmt.expr.index);
|
|
84
|
+
const op = stmt.op === "++" ? "+" : "-";
|
|
85
|
+
if (
|
|
86
|
+
this._hasCallExpr(stmt.expr.expr) ||
|
|
87
|
+
this._hasCallExpr(stmt.expr.index)
|
|
88
|
+
) {
|
|
89
|
+
this.line(
|
|
90
|
+
`((__m, __k) => { __m[__k] = (__m[__k] ?? ${zero}) ${op} 1; })(${base}, ${idx});`,
|
|
91
|
+
srcLine,
|
|
92
|
+
);
|
|
93
|
+
} else {
|
|
94
|
+
this.line(
|
|
95
|
+
`${base}[${idx}] = (${base}[${idx}] ?? ${zero}) ${op} 1;`,
|
|
96
|
+
srcLine,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
66
101
|
if (
|
|
67
102
|
this.strict &&
|
|
68
103
|
this._genStrictCompound(
|
|
@@ -266,6 +301,35 @@ export const statementGenMethods = {
|
|
|
266
301
|
if (active.length === 0) {
|
|
267
302
|
if (rhs.length > 0) this.line(`${rhs[0]};`);
|
|
268
303
|
} else if (lhs.length === 1) {
|
|
304
|
+
if (
|
|
305
|
+
stmt.op !== "=" &&
|
|
306
|
+
stmt.lhs[0].kind === "IndexExpr" &&
|
|
307
|
+
stmt.lhs[0]._mapValueType
|
|
308
|
+
) {
|
|
309
|
+
const zero = this.zeroValueForType(stmt.lhs[0]._mapValueType);
|
|
310
|
+
const wrapField = this._namedWrapperField(
|
|
311
|
+
stmt.lhs[0].expr._type,
|
|
312
|
+
stmt.lhs[0].expr,
|
|
313
|
+
);
|
|
314
|
+
const base = wrapField
|
|
315
|
+
? `${this.genExpr(stmt.lhs[0].expr)}.${wrapField}`
|
|
316
|
+
: this.genExpr(stmt.lhs[0].expr);
|
|
317
|
+
const idx = this.genExpr(stmt.lhs[0].index);
|
|
318
|
+
const binOp = stmt.op.slice(0, -1);
|
|
319
|
+
if (
|
|
320
|
+
this._hasCallExpr(stmt.lhs[0].expr) ||
|
|
321
|
+
this._hasCallExpr(stmt.lhs[0].index)
|
|
322
|
+
) {
|
|
323
|
+
this.line(
|
|
324
|
+
`((__m, __k) => { __m[__k] = (__m[__k] ?? ${zero}) ${binOp} (${active[0].r}); })(${base}, ${idx});`,
|
|
325
|
+
);
|
|
326
|
+
} else {
|
|
327
|
+
this.line(
|
|
328
|
+
`${base}[${idx}] = (${base}[${idx}] ?? ${zero}) ${binOp} (${active[0].r});`,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
269
333
|
if (
|
|
270
334
|
this.strict &&
|
|
271
335
|
stmt.op !== "=" &&
|
|
@@ -333,11 +397,11 @@ export const statementGenMethods = {
|
|
|
333
397
|
result = `{ re: ${lhsStr}.re - ${rhsExpr}.re, im: ${lhsStr}.im - ${rhsExpr}.im }`;
|
|
334
398
|
break;
|
|
335
399
|
case "*":
|
|
336
|
-
this.
|
|
400
|
+
this.useHelper("cmul");
|
|
337
401
|
result = `__cmul(${lhsStr}, ${rhsExpr})`;
|
|
338
402
|
break;
|
|
339
403
|
case "/":
|
|
340
|
-
this.
|
|
404
|
+
this.useHelper("cdiv");
|
|
341
405
|
result = `__cdiv(${lhsStr}, ${rhsExpr})`;
|
|
342
406
|
break;
|
|
343
407
|
default:
|
|
@@ -379,6 +443,22 @@ export const statementGenMethods = {
|
|
|
379
443
|
} else {
|
|
380
444
|
this.line("return;");
|
|
381
445
|
}
|
|
446
|
+
} else if (this.namedReturnVars?.length > 0) {
|
|
447
|
+
const vars = this.namedReturnVars;
|
|
448
|
+
if (stmt.values.length === 1 && vars.length === 1) {
|
|
449
|
+
this.line(`${vars[0]} = ${this._genReturnValue(stmt.values[0])};`);
|
|
450
|
+
} else if (stmt.values.length === 1 && vars.length > 1) {
|
|
451
|
+
const val = this._genReturnValue(stmt.values[0]);
|
|
452
|
+
this.line(`[${vars.join(", ")}] = ${val};`);
|
|
453
|
+
} else if (stmt.values.length > 1 && stmt.values.length === vars.length) {
|
|
454
|
+
const temps = stmt.values.map((v) => this._genReturnValue(v));
|
|
455
|
+
this.line(`[${vars.join(", ")}] = [${temps.join(", ")}];`);
|
|
456
|
+
}
|
|
457
|
+
this.line(
|
|
458
|
+
vars.length === 1
|
|
459
|
+
? `return ${vars[0]};`
|
|
460
|
+
: `return [${vars.join(", ")}];`,
|
|
461
|
+
);
|
|
382
462
|
} else if (stmt.values.length === 1) {
|
|
383
463
|
this.line(`return ${this._genReturnValue(stmt.values[0])};`);
|
|
384
464
|
} else {
|
|
@@ -662,7 +742,7 @@ export const statementGenMethods = {
|
|
|
662
742
|
if (lhs.length === 1) return `Array.from(${iteree}).keys()`;
|
|
663
743
|
return `Array.from(${iteree}, (__c, __i) => [__i, __c.codePointAt(0)])`;
|
|
664
744
|
}
|
|
665
|
-
this.
|
|
745
|
+
this.useHelper("sliceGuard");
|
|
666
746
|
if (lhs.length === 1) return `__s(${iteree}).keys()`;
|
|
667
747
|
return `__s(${iteree}).entries()`;
|
|
668
748
|
},
|
|
@@ -7,11 +7,11 @@ export const errorsMethods = {
|
|
|
7
7
|
_genErrors(fn, a) {
|
|
8
8
|
const args = a();
|
|
9
9
|
if (fn === "New") {
|
|
10
|
-
this.
|
|
10
|
+
this.useHelper("error");
|
|
11
11
|
return `__error(${args[0]})`;
|
|
12
12
|
}
|
|
13
13
|
if (fn === "Is") {
|
|
14
|
-
this.
|
|
14
|
+
this.useHelper("errorIs");
|
|
15
15
|
return `__errorIs(${args[0]}, ${args[1]})`;
|
|
16
16
|
}
|
|
17
17
|
if (fn === "Unwrap") return `(${args[0]}?._cause ?? null)`;
|
|
@@ -15,11 +15,11 @@ export const fmtMethods = {
|
|
|
15
15
|
const fmtArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
16
16
|
switch (fn) {
|
|
17
17
|
case "Sprintf":
|
|
18
|
-
this.
|
|
18
|
+
this.useHelper("sprintf");
|
|
19
19
|
return `__sprintf(${fmtArgs})`;
|
|
20
20
|
case "Errorf": {
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
21
|
+
this.useHelper("sprintf");
|
|
22
|
+
this.useHelper("error");
|
|
23
23
|
const fmtStr = expr.args[0];
|
|
24
24
|
if (fmtStr?.kind === "BasicLit" && fmtStr.value?.includes("%w")) {
|
|
25
25
|
const lastArg = this.genExpr(expr.args[expr.args.length - 1]);
|
|
@@ -28,12 +28,15 @@ export const fmtMethods = {
|
|
|
28
28
|
return `__error(__sprintf(${fmtArgs}))`;
|
|
29
29
|
}
|
|
30
30
|
case "Printf":
|
|
31
|
+
this.useHelper("sprintf");
|
|
32
|
+
return this._genStdoutWrite(`__sprintf(${fmtArgs})`);
|
|
31
33
|
case "Print":
|
|
32
|
-
this.
|
|
33
|
-
return
|
|
34
|
+
this.useHelper("sprintf");
|
|
35
|
+
return this._genStdoutWrite(this._genPrintOperands(expr, "", ""));
|
|
34
36
|
case "Println":
|
|
35
|
-
this.
|
|
36
|
-
|
|
37
|
+
this.useHelper("sprintf");
|
|
38
|
+
// console.log supplies the trailing newline.
|
|
39
|
+
return `console.log(${this._genPrintOperands(expr, " ", "")})`;
|
|
37
40
|
case "Fprintf":
|
|
38
41
|
case "Fprintln":
|
|
39
42
|
case "Fprint":
|
|
@@ -69,7 +72,7 @@ export const fmtMethods = {
|
|
|
69
72
|
},
|
|
70
73
|
|
|
71
74
|
_genFmtFprint(fn, expr) {
|
|
72
|
-
this.
|
|
75
|
+
this.useHelper("sprintf");
|
|
73
76
|
const writerArg = expr.args[0];
|
|
74
77
|
const rest = expr.args.slice(1);
|
|
75
78
|
const restJs = rest.map((e) => this.genExpr(e)).join(", ");
|
|
@@ -93,8 +96,23 @@ export const fmtMethods = {
|
|
|
93
96
|
return `${w}.WriteString(${sprintfCall})`;
|
|
94
97
|
},
|
|
95
98
|
|
|
99
|
+
_genStdoutWrite(strJs) {
|
|
100
|
+
return `((__s) => (typeof process !== "undefined" && process?.stdout?.write ? process.stdout.write(__s) : console.log(__s)))(${strJs})`;
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// Formats Print/Println operands with %v so the first operand is never
|
|
104
|
+
// mistaken for a format string.
|
|
105
|
+
_genPrintOperands(expr, sep, suffix) {
|
|
106
|
+
const argc = expr.args.length;
|
|
107
|
+
if (argc === 0) return JSON.stringify(suffix);
|
|
108
|
+
const fmt = Array(argc).fill("%v").join(sep) + suffix;
|
|
109
|
+
const args = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
110
|
+
return `__sprintf(${JSON.stringify(fmt)}, ${args})`;
|
|
111
|
+
},
|
|
112
|
+
|
|
96
113
|
_buildFprintSprintfCall(fn, argc, restJs) {
|
|
97
114
|
if (fn === "Fprintf") return `__sprintf(${restJs})`;
|
|
115
|
+
if (argc === 0) return fn === "Fprintln" ? '"\\n"' : '""';
|
|
98
116
|
const sep = fn === "Fprintln" ? " " : "";
|
|
99
117
|
const suffix = fn === "Fprintln" ? "\\n" : "";
|
|
100
118
|
const fmt = Array(argc).fill("%v").join(sep) + suffix;
|
|
@@ -15,6 +15,7 @@ import { osMethods } from "./os.js";
|
|
|
15
15
|
import { pathMethods } from "./path.js";
|
|
16
16
|
import { randMethods } from "./rand.js";
|
|
17
17
|
import { regexpMethods } from "./regexp.js";
|
|
18
|
+
import { sharedMethods } from "./shared.js";
|
|
18
19
|
import { slicesMethods } from "./slices.js";
|
|
19
20
|
import { sortMethods } from "./sort.js";
|
|
20
21
|
import { strconvMethods } from "./strconv.js";
|
|
@@ -49,6 +50,7 @@ const STDLIB_METHOD_MAP = {
|
|
|
49
50
|
utf8: "_genUtf8",
|
|
50
51
|
path: "_genPath",
|
|
51
52
|
testing: "_genTesting",
|
|
53
|
+
shared: "_genShared",
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
const dispatchMethods = {
|
|
@@ -92,6 +94,7 @@ export const stdlibGenMethods = {
|
|
|
92
94
|
...pathMethods,
|
|
93
95
|
...randMethods,
|
|
94
96
|
...regexpMethods,
|
|
97
|
+
...sharedMethods,
|
|
95
98
|
...slicesMethods,
|
|
96
99
|
...sortMethods,
|
|
97
100
|
...strconvMethods,
|
|
@@ -20,12 +20,12 @@ const PATH_DISPATCH = {
|
|
|
20
20
|
export const pathMethods = {
|
|
21
21
|
_genPath(fn, a, expr) {
|
|
22
22
|
if (fn === "Join") {
|
|
23
|
-
this.
|
|
23
|
+
this.useHelper("pathClean");
|
|
24
24
|
const allArgs = expr.args.map((e) => this.genExpr(e)).join(", ");
|
|
25
25
|
return `__pathClean([${allArgs}].filter(x => x !== "").join("/"))`;
|
|
26
26
|
}
|
|
27
27
|
if (fn === "Clean") {
|
|
28
|
-
this.
|
|
28
|
+
this.useHelper("pathClean");
|
|
29
29
|
return `__pathClean(${a()[0]})`;
|
|
30
30
|
}
|
|
31
31
|
const gen = PATH_DISPATCH[fn];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// CodeGen for the `gofront/shared` package: in js packages a shared buffer is
|
|
2
|
+
// just a TypedArray (no linear memory is involved); in wasm packages the facade
|
|
3
|
+
// hands out zero-copy views onto the module memory.
|
|
4
|
+
|
|
5
|
+
import { SHARED_KINDS } from "../../../typechecker/stdlib/shared.js";
|
|
6
|
+
|
|
7
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
8
|
+
|
|
9
|
+
/** @type {ThisType<CodeGen>} */
|
|
10
|
+
export const sharedMethods = {
|
|
11
|
+
_genShared(fn, a) {
|
|
12
|
+
if (!fn.startsWith("New")) return undefined;
|
|
13
|
+
const info = SHARED_KINDS[fn.slice(3)];
|
|
14
|
+
if (!info) return undefined;
|
|
15
|
+
return `new ${info.ctor}(${a()[0]})`;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -23,7 +23,7 @@ const SORT_DISPATCH = {
|
|
|
23
23
|
/** @type {ThisType<CodeGen>} */
|
|
24
24
|
export const sortMethods = {
|
|
25
25
|
_genSort(fn, a) {
|
|
26
|
-
if (fn === "Slice" || fn === "SliceStable") this.
|
|
26
|
+
if (fn === "Slice" || fn === "SliceStable") this.useHelper("sortSlice");
|
|
27
27
|
const gen = SORT_DISPATCH[fn];
|
|
28
28
|
return gen ? gen(a()) : undefined;
|
|
29
29
|
},
|
|
@@ -2,32 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
4
|
|
|
5
|
+
// Parsers return [value, msg|null] from the shared __strconv_* helpers
|
|
6
|
+
// (runtime.js); the message becomes a Go error value.
|
|
7
|
+
const parsed = (call, toValue = "r[0]") =>
|
|
8
|
+
`((r) => [${toValue}, r[1] === null ? null : __error(r[1])])(${call})`;
|
|
9
|
+
|
|
5
10
|
const STRCONV_DISPATCH = {
|
|
6
11
|
Itoa: (a) => `String(${a[0]})`,
|
|
7
|
-
Atoi: (a) =>
|
|
8
|
-
`(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}) | 0, null])`,
|
|
12
|
+
Atoi: (a) => parsed(`__strconv_atoi(${a[0]})`, "Number(r[0])"),
|
|
9
13
|
FormatBool: (a) => `String(${a[0]})`,
|
|
10
14
|
FormatInt: (a) => `(${a[0]}).toString(${a[1]})`,
|
|
11
|
-
FormatFloat: (a) =>
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
FormatFloat: (a) =>
|
|
16
|
+
a.length < 3
|
|
17
|
+
? `String(${a[0]})`
|
|
18
|
+
: `__strconv_format_float(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
19
|
+
ParseFloat: (a) => parsed(`__strconv_parse_float(${a[0]})`),
|
|
14
20
|
ParseInt: (a) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
parsed(
|
|
22
|
+
`__strconv_parse_int(${a[0]}, ${a[1]}, ${a[2] ?? 64})`,
|
|
23
|
+
"Number(r[0])",
|
|
24
|
+
),
|
|
25
|
+
ParseBool: (a) => parsed(`__strconv_parse_bool(${a[0]})`),
|
|
18
26
|
Quote: (a) => `JSON.stringify(${a[0]})`,
|
|
19
27
|
Unquote: (a) =>
|
|
20
|
-
`((s) => { try { const v = JSON.parse(s); return [v, null]; } catch(e) { return ["", "invalid syntax"]; } })(${a[0]})`,
|
|
28
|
+
`((s) => { try { const v = JSON.parse(s); return [v, null]; } catch(e) { return ["", __error("invalid syntax")]; } })(${a[0]})`,
|
|
21
29
|
AppendInt: (a) =>
|
|
22
30
|
`[...(${a[0]}), ...new TextEncoder().encode((${a[1]}).toString(${a[2]}))]`,
|
|
23
31
|
AppendFloat: (a) =>
|
|
24
32
|
`[...(${a[0]}), ...new TextEncoder().encode(${a[1]}.toFixed(${a[3]} < 0 ? 6 : ${a[3]}))]`,
|
|
25
33
|
};
|
|
26
34
|
|
|
35
|
+
const USES_ERROR = new Set([
|
|
36
|
+
"Atoi",
|
|
37
|
+
"ParseFloat",
|
|
38
|
+
"ParseInt",
|
|
39
|
+
"ParseBool",
|
|
40
|
+
"Unquote",
|
|
41
|
+
]);
|
|
42
|
+
const USES_STRCONV = new Set([
|
|
43
|
+
"Atoi",
|
|
44
|
+
"ParseFloat",
|
|
45
|
+
"ParseInt",
|
|
46
|
+
"ParseBool",
|
|
47
|
+
"FormatFloat",
|
|
48
|
+
]);
|
|
49
|
+
|
|
27
50
|
/** @type {ThisType<CodeGen>} */
|
|
28
51
|
export const strconvMethods = {
|
|
29
52
|
_genStrconv(fn, a) {
|
|
30
53
|
const gen = STRCONV_DISPATCH[fn];
|
|
54
|
+
if (gen && USES_ERROR.has(fn)) this.useHelper("error");
|
|
55
|
+
if (gen && USES_STRCONV.has(fn)) this.useHelper("strconv");
|
|
31
56
|
return gen ? gen(a()) : undefined;
|
|
32
57
|
},
|
|
33
58
|
};
|
|
@@ -21,7 +21,10 @@ const STRINGS_M2 = {
|
|
|
21
21
|
const STRINGS_DISPATCH = {
|
|
22
22
|
Count: ([s, s2]) =>
|
|
23
23
|
`((s, sep) => sep === "" ? s.length + 1 : s.split(sep).length - 1)(${s}, ${s2})`,
|
|
24
|
-
Replace: ([s, s2, s3]) =>
|
|
24
|
+
Replace: ([s, s2, s3, n]) =>
|
|
25
|
+
n === undefined
|
|
26
|
+
? `${s}.replace(${s2}, ${s3})`
|
|
27
|
+
: `((str, o, nw, cnt) => { if (cnt < 0) return str.replaceAll(o, nw); let r = ""; let i = 0; for (; cnt > 0; cnt--) { const j = str.indexOf(o, i); if (j < 0) break; r += str.slice(i, j) + nw; i = j + o.length; if (o.length === 0) { if (i >= str.length) break; r += str[i]; i++; } } return r + str.slice(i); })(${s}, ${s2}, ${s3}, ${n})`,
|
|
25
28
|
ReplaceAll: ([s, s2, s3]) => `${s}.replaceAll(${s2}, ${s3})`,
|
|
26
29
|
Trim: ([s, s2]) =>
|
|
27
30
|
`${s}.replace(new RegExp(\`^[\${${s2}}]+|[\${${s2}}]+$\`, "g"), "")`,
|
|
@@ -15,7 +15,7 @@ export const testingMethods = {
|
|
|
15
15
|
},
|
|
16
16
|
|
|
17
17
|
_genTestingMethodCall(method, expr) {
|
|
18
|
-
this.
|
|
18
|
+
this.useHelper("testing");
|
|
19
19
|
const recv = expr.func.expr;
|
|
20
20
|
const t = this.genExpr(recv);
|
|
21
21
|
const args = expr.args
|
|
@@ -27,7 +27,7 @@ export const testingMethods = {
|
|
|
27
27
|
method === "Logf" ||
|
|
28
28
|
method === "Skipf"
|
|
29
29
|
) {
|
|
30
|
-
this.
|
|
30
|
+
this.useHelper("sprintf");
|
|
31
31
|
}
|
|
32
32
|
return `${t}.${method}(${args})`;
|
|
33
33
|
},
|
|
@@ -35,11 +35,11 @@ export const timeMethods = {
|
|
|
35
35
|
const recvJs = this.genExpr(recv);
|
|
36
36
|
const args = expr.args.map((a) => this.genExpr(a));
|
|
37
37
|
if (method === "Format") {
|
|
38
|
-
this.
|
|
38
|
+
this.useHelper("timeFmt");
|
|
39
39
|
return `__timeFmt(${recvJs}._d, ${args[0]})`;
|
|
40
40
|
}
|
|
41
41
|
if (method === "String") {
|
|
42
|
-
this.
|
|
42
|
+
this.useHelper("timeFmt");
|
|
43
43
|
return `__timeFmt(${recvJs}._d, "2006-01-02T15:04:05Z07:00")`;
|
|
44
44
|
}
|
|
45
45
|
const gen = TIME_METHOD_DISPATCH[method];
|
|
@@ -48,7 +48,7 @@ export const timeMethods = {
|
|
|
48
48
|
|
|
49
49
|
_genTime(fn, a) {
|
|
50
50
|
if (fn === "Parse") {
|
|
51
|
-
this.
|
|
51
|
+
this.useHelper("timeParse");
|
|
52
52
|
return `__timeParse(${a()[0]}, ${a()[1]})`;
|
|
53
53
|
}
|
|
54
54
|
const gen = TIME_DISPATCH[fn];
|
package/src/backend/js/templ.js
CHANGED
|
@@ -286,7 +286,7 @@ export const templGenMethods = {
|
|
|
286
286
|
if (valName === "_")
|
|
287
287
|
return `for (const [${keyName}] of Object.entries(${iterJs})) {`;
|
|
288
288
|
// Default to .entries() for arrays, Object.entries for objects
|
|
289
|
-
this.
|
|
289
|
+
this.useHelper("sliceGuard");
|
|
290
290
|
return `for (const [${keyName}, ${valName}] of __s(${iterJs}).entries()) {`;
|
|
291
291
|
},
|
|
292
292
|
|