gofront 1.4.0 → 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 -403
- package/package.json +16 -19
- package/src/{codegen → backend/js}/expressions.js +265 -77
- package/src/{codegen → backend/js}/index.js +120 -89
- package/src/{codegen → backend/js}/runtime.js +102 -0
- package/src/{codegen → backend/js}/source-map.js +32 -0
- package/src/{codegen → backend/js}/statements.js +139 -64
- package/src/backend/js/stdlib/bits.js +32 -0
- package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
- package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
- package/src/{codegen → backend/js}/stdlib/index.js +6 -0
- package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
- package/src/{codegen → backend/js}/stdlib/path.js +2 -2
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
- package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +58 -0
- package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
- package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
- package/src/{codegen → backend/js}/stdlib/time.js +3 -3
- package/src/{codegen → backend/js}/templ.js +3 -3
- package/src/backend/wasm/boundary.js +1798 -0
- 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 +1376 -0
- package/src/backend/wasm/encode.js +937 -0
- package/src/backend/wasm/glue.js +144 -0
- package/src/backend/wasm/index.js +2790 -0
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +227 -0
- package/src/backend/wasm/types.js +544 -0
- package/src/backend/wasm/wat.js +302 -0
- package/src/cli-core.js +323 -75
- package/src/compiler.js +447 -14
- package/src/dev-server.js +56 -15
- package/src/index.js +99 -120
- package/src/lexer.js +18 -0
- package/src/lower/boxing.js +35 -0
- package/src/lower/captures.js +172 -0
- package/src/lower/embedding.js +30 -0
- package/src/lower/escape.js +164 -0
- package/src/lower/functions.js +302 -0
- package/src/lower/globals.js +316 -0
- package/src/lower/index.js +118 -0
- package/src/lower/ownership.js +137 -0
- package/src/lower/range.js +67 -0
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +2 -0
- package/src/parser/index.js +8 -3
- package/src/resolver.js +3 -1
- package/src/test-runner.js +94 -20
- package/src/typechecker/assignability.js +42 -1
- package/src/typechecker/expressions.js +182 -3
- package/src/typechecker/index.js +261 -5
- package/src/typechecker/resolve.js +35 -6
- package/src/typechecker/stdlib/core.js +34 -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 +259 -6
- package/src/vendor.js +2 -2
- package/src/codegen/stdlib/strconv.js +0 -33
- /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
- /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
bodyRedeclares,
|
|
3
|
+
isIntRangeType,
|
|
4
|
+
isMapRangeType,
|
|
5
|
+
isRangeFor,
|
|
6
|
+
isSimpleIntRange,
|
|
7
|
+
isStringRangeType,
|
|
8
|
+
nodeAssigns,
|
|
9
|
+
} from "../../lower/index.js";
|
|
10
|
+
import { isComplex } from "../../typechecker/types.js";
|
|
4
11
|
|
|
5
12
|
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
6
13
|
|
|
@@ -24,27 +31,35 @@ export const statementGenMethods = {
|
|
|
24
31
|
},
|
|
25
32
|
|
|
26
33
|
// Shared helper for comma-ok map index in both DefineStmt and AssignStmt.
|
|
27
|
-
// isDefine=true → emit `let v = m[k]; let ok = k in m;`
|
|
28
|
-
// isDefine=false → emit `v = k in m ? m[k] : zero; ok = k in m;`
|
|
29
34
|
_genCommaOkMapIndex(stmt, rhsNode, isDefine) {
|
|
30
35
|
const mapExpr = this.genExpr(rhsNode.expr);
|
|
31
36
|
const keyExpr = this.genExpr(rhsNode.index);
|
|
32
37
|
const [vName, okName] = stmt.lhs.map((e) => e.name ?? this.genExpr(e));
|
|
33
38
|
const decl = isDefine ? "let " : "";
|
|
34
|
-
if (
|
|
35
|
-
if (
|
|
39
|
+
if (isDefine && !this.strict) {
|
|
40
|
+
if (vName !== "_") {
|
|
36
41
|
this.line(`let ${vName} = ${mapExpr}[${keyExpr}];`);
|
|
37
|
-
} else {
|
|
38
|
-
const zero = rhsNode._mapValueType
|
|
39
|
-
? this.zeroValueForType(rhsNode._mapValueType)
|
|
40
|
-
: "undefined";
|
|
41
|
-
this.line(
|
|
42
|
-
`${vName} = (${keyExpr}) in ${mapExpr} ? ${mapExpr}[${keyExpr}] : ${zero};`,
|
|
43
|
-
);
|
|
44
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}));`);
|
|
45
62
|
}
|
|
46
|
-
if (okName !== "_")
|
|
47
|
-
this.line(`${decl}${okName} = (${keyExpr}) in ${mapExpr};`);
|
|
48
63
|
},
|
|
49
64
|
|
|
50
65
|
genStmt(stmt) {
|
|
@@ -55,9 +70,47 @@ export const statementGenMethods = {
|
|
|
55
70
|
case "TypeDecl":
|
|
56
71
|
this.genTypeDeclWithMethods(stmt, []);
|
|
57
72
|
break;
|
|
58
|
-
case "IncDecStmt":
|
|
59
|
-
|
|
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
|
+
}
|
|
101
|
+
if (
|
|
102
|
+
this.strict &&
|
|
103
|
+
this._genStrictCompound(
|
|
104
|
+
stmt.expr,
|
|
105
|
+
stmt.op === "++" ? "+" : "-",
|
|
106
|
+
"1",
|
|
107
|
+
srcLine,
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
break;
|
|
111
|
+
this.line(`${this.genExpr(stmt.expr)}${stmt.op};`, srcLine);
|
|
60
112
|
break;
|
|
113
|
+
}
|
|
61
114
|
case "ExprStmt":
|
|
62
115
|
this.line(`${this.genExpr(stmt.expr)};`);
|
|
63
116
|
break;
|
|
@@ -248,6 +301,48 @@ export const statementGenMethods = {
|
|
|
248
301
|
if (active.length === 0) {
|
|
249
302
|
if (rhs.length > 0) this.line(`${rhs[0]};`);
|
|
250
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
|
+
}
|
|
333
|
+
if (
|
|
334
|
+
this.strict &&
|
|
335
|
+
stmt.op !== "=" &&
|
|
336
|
+
this._genStrictCompound(
|
|
337
|
+
stmt.lhs[0],
|
|
338
|
+
stmt.op.slice(0, -1),
|
|
339
|
+
active[0].r,
|
|
340
|
+
null,
|
|
341
|
+
active[0].l,
|
|
342
|
+
)
|
|
343
|
+
) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
251
346
|
this.line(`${active[0].l} ${stmt.op} ${active[0].r};`);
|
|
252
347
|
} else {
|
|
253
348
|
this._genAssignMulti(stmt, lhs, rhs, active, pairs);
|
|
@@ -302,11 +397,11 @@ export const statementGenMethods = {
|
|
|
302
397
|
result = `{ re: ${lhsStr}.re - ${rhsExpr}.re, im: ${lhsStr}.im - ${rhsExpr}.im }`;
|
|
303
398
|
break;
|
|
304
399
|
case "*":
|
|
305
|
-
this.
|
|
400
|
+
this.useHelper("cmul");
|
|
306
401
|
result = `__cmul(${lhsStr}, ${rhsExpr})`;
|
|
307
402
|
break;
|
|
308
403
|
case "/":
|
|
309
|
-
this.
|
|
404
|
+
this.useHelper("cdiv");
|
|
310
405
|
result = `__cdiv(${lhsStr}, ${rhsExpr})`;
|
|
311
406
|
break;
|
|
312
407
|
default:
|
|
@@ -348,6 +443,22 @@ export const statementGenMethods = {
|
|
|
348
443
|
} else {
|
|
349
444
|
this.line("return;");
|
|
350
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
|
+
);
|
|
351
462
|
} else if (stmt.values.length === 1) {
|
|
352
463
|
this.line(`return ${this._genReturnValue(stmt.values[0])};`);
|
|
353
464
|
} else {
|
|
@@ -449,28 +560,19 @@ export const statementGenMethods = {
|
|
|
449
560
|
},
|
|
450
561
|
|
|
451
562
|
isRangeFor(stmt) {
|
|
452
|
-
|
|
453
|
-
const init = stmt.init;
|
|
454
|
-
if (init.kind !== "DefineStmt" && init.kind !== "AssignStmt") return false;
|
|
455
|
-
return init.rhs?.[0]?.kind === "RangeExpr";
|
|
563
|
+
return isRangeFor(stmt);
|
|
456
564
|
},
|
|
457
565
|
|
|
458
566
|
_isIntRangeType(iterType) {
|
|
459
|
-
return
|
|
567
|
+
return isIntRangeType(iterType);
|
|
460
568
|
},
|
|
461
569
|
|
|
462
570
|
_isMapRangeType(iterType) {
|
|
463
|
-
return (
|
|
464
|
-
iterType?.kind === "map" ||
|
|
465
|
-
(iterType?.kind === "named" && iterType.underlying?.kind === "map")
|
|
466
|
-
);
|
|
571
|
+
return isMapRangeType(iterType);
|
|
467
572
|
},
|
|
468
573
|
|
|
469
574
|
_isStringRangeType(iterType) {
|
|
470
|
-
return (
|
|
471
|
-
(iterType?.kind === "basic" && iterType.name === "string") ||
|
|
472
|
-
(iterType?.kind === "untyped" && iterType.base === "string")
|
|
473
|
-
);
|
|
575
|
+
return isStringRangeType(iterType);
|
|
474
576
|
},
|
|
475
577
|
|
|
476
578
|
_rangeVarTarget(e, isAssign) {
|
|
@@ -498,17 +600,7 @@ export const statementGenMethods = {
|
|
|
498
600
|
|
|
499
601
|
// True when the body re-declares one of `names` at its top level (legal Go shadowing).
|
|
500
602
|
_bodyRedeclares(body, names) {
|
|
501
|
-
|
|
502
|
-
for (const s of body.stmts ?? []) {
|
|
503
|
-
if (s.kind === "DefineStmt" && s.lhs.some((e) => names.includes(e.name)))
|
|
504
|
-
return true;
|
|
505
|
-
if (
|
|
506
|
-
(s.kind === "VarDecl" || s.kind === "ConstDecl") &&
|
|
507
|
-
s.decls?.some((d) => d.names?.some((n) => names.includes(n)))
|
|
508
|
-
)
|
|
509
|
-
return true;
|
|
510
|
-
}
|
|
511
|
-
return false;
|
|
603
|
+
return bodyRedeclares(body, names);
|
|
512
604
|
},
|
|
513
605
|
|
|
514
606
|
// Range value variable: aliases the element unless the body writes the variable or the collection.
|
|
@@ -565,9 +657,7 @@ export const statementGenMethods = {
|
|
|
565
657
|
|
|
566
658
|
// Whether `for i := range n` can be emitted as a plain `for (let i = 0; …)` without hidden registers.
|
|
567
659
|
_isSimpleIntRange(stmt, name, isAssign) {
|
|
568
|
-
|
|
569
|
-
if (!name || name === "_") return true;
|
|
570
|
-
return !this._boxedVars.has(name) && !this._nodeAssigns(stmt.body, name);
|
|
660
|
+
return isSimpleIntRange(stmt, name, isAssign, this._boxedVars);
|
|
571
661
|
},
|
|
572
662
|
|
|
573
663
|
_genIntRangeBody(stmt, name, isAssign) {
|
|
@@ -603,22 +693,7 @@ export const statementGenMethods = {
|
|
|
603
693
|
|
|
604
694
|
// True when `node` contains an assignment or ++/-- targeting identifier `name`.
|
|
605
695
|
_nodeAssigns(node, name) {
|
|
606
|
-
|
|
607
|
-
if (Array.isArray(node))
|
|
608
|
-
return node.some((n) => this._nodeAssigns(n, name));
|
|
609
|
-
if (
|
|
610
|
-
(node.kind === "AssignStmt" &&
|
|
611
|
-
node.lhs.some((e) => e.kind === "Ident" && e.name === name)) ||
|
|
612
|
-
(node.kind === "IncDecStmt" &&
|
|
613
|
-
node.expr.kind === "Ident" &&
|
|
614
|
-
node.expr.name === name)
|
|
615
|
-
)
|
|
616
|
-
return true;
|
|
617
|
-
for (const key of Object.keys(node)) {
|
|
618
|
-
if (key.startsWith("_")) continue;
|
|
619
|
-
if (this._nodeAssigns(node[key], name)) return true;
|
|
620
|
-
}
|
|
621
|
-
return false;
|
|
696
|
+
return nodeAssigns(node, name);
|
|
622
697
|
},
|
|
623
698
|
|
|
624
699
|
_genIterFor(init, iterType, iteree, lhs, body) {
|
|
@@ -667,7 +742,7 @@ export const statementGenMethods = {
|
|
|
667
742
|
if (lhs.length === 1) return `Array.from(${iteree}).keys()`;
|
|
668
743
|
return `Array.from(${iteree}, (__c, __i) => [__i, __c.codePointAt(0)])`;
|
|
669
744
|
}
|
|
670
|
-
this.
|
|
745
|
+
this.useHelper("sliceGuard");
|
|
671
746
|
if (lhs.length === 1) return `__s(${iteree}).keys()`;
|
|
672
747
|
return `__s(${iteree}).entries()`;
|
|
673
748
|
},
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/backend/js/stdlib/bits.js
|
|
2
|
+
// CodeGen for Go `math/bits` package.
|
|
3
|
+
|
|
4
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
5
|
+
|
|
6
|
+
const BITS_MAP = {
|
|
7
|
+
LeadingZeros32: ([x]) => `Math.clz32((${x}) >>> 0)`,
|
|
8
|
+
TrailingZeros32: ([x]) =>
|
|
9
|
+
`((() => { const v = (${x}) >>> 0; return v === 0 ? 32 : 31 - Math.clz32(v & -v); })())`,
|
|
10
|
+
OnesCount32: ([x]) =>
|
|
11
|
+
`((() => { let v = (${x}) >>> 0; v = v - ((v >>> 1) & 0x55555555); v = (v & 0x33333333) + ((v >>> 2) & 0x33333333); return (Math.imul((v + (v >>> 4)) & 0x0f0f0f0f, 0x01010101)) >>> 24; })())`,
|
|
12
|
+
RotateLeft32: ([x, k]) =>
|
|
13
|
+
`((() => { const s = (${k}) & 31; const v = (${x}) >>> 0; return ((v << s) | (v >>> ((32 - s) & 31))) >>> 0; })())`,
|
|
14
|
+
|
|
15
|
+
LeadingZeros64: ([x]) =>
|
|
16
|
+
`((() => { const v = BigInt.asUintN(64, BigInt(${x})); if (v === 0n) return 64; const hi = Number(v >> 32n); return hi !== 0 ? Math.clz32(hi) : 32 + Math.clz32(Number(v & 0xffffffffn)); })())`,
|
|
17
|
+
TrailingZeros64: ([x]) =>
|
|
18
|
+
`((() => { const v = BigInt.asUintN(64, BigInt(${x})); if (v === 0n) return 64; const lo = Number(v & 0xffffffffn); if (lo !== 0) return 31 - Math.clz32(lo & -lo); const hi = Number((v >> 32n) & 0xffffffffn); return 32 + (31 - Math.clz32(hi & -hi)); })())`,
|
|
19
|
+
OnesCount64: ([x]) =>
|
|
20
|
+
`((() => { let v = BigInt.asUintN(64, BigInt(${x})); let c = 0; while (v > 0n) { if (v & 1n) c++; v >>= 1n; } return c; })())`,
|
|
21
|
+
RotateLeft64: ([x, k]) =>
|
|
22
|
+
`((() => { const s = BigInt(${k}) & 63n; const v = BigInt.asUintN(64, BigInt(${x})); return BigInt.asUintN(64, (v << s) | (v >> ((64n - s) & 63n))); })())`,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** @type {ThisType<CodeGen>} */
|
|
26
|
+
export const bitsMethods = {
|
|
27
|
+
_genBits(fn, a) {
|
|
28
|
+
const args = a();
|
|
29
|
+
const gen = BITS_MAP[fn];
|
|
30
|
+
return gen ? gen(args) : undefined;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -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;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// CodeGen stdlib call generation — installed as a mixin on CodeGen.prototype.
|
|
2
2
|
// One file per Go package under ./stdlib/. This index merges them.
|
|
3
3
|
|
|
4
|
+
import { bitsMethods } from "./bits.js";
|
|
4
5
|
import { builderMethods } from "./builder.js";
|
|
5
6
|
import { bytesMethods } from "./bytes.js";
|
|
6
7
|
import { errorsMethods } from "./errors.js";
|
|
@@ -14,6 +15,7 @@ import { osMethods } from "./os.js";
|
|
|
14
15
|
import { pathMethods } from "./path.js";
|
|
15
16
|
import { randMethods } from "./rand.js";
|
|
16
17
|
import { regexpMethods } from "./regexp.js";
|
|
18
|
+
import { sharedMethods } from "./shared.js";
|
|
17
19
|
import { slicesMethods } from "./slices.js";
|
|
18
20
|
import { sortMethods } from "./sort.js";
|
|
19
21
|
import { strconvMethods } from "./strconv.js";
|
|
@@ -34,6 +36,7 @@ const STDLIB_METHOD_MAP = {
|
|
|
34
36
|
strconv: "_genStrconv",
|
|
35
37
|
sort: "_genSort",
|
|
36
38
|
math: "_genMath",
|
|
39
|
+
bits: "_genBits",
|
|
37
40
|
unicode: "_genUnicode",
|
|
38
41
|
os: "_genOs",
|
|
39
42
|
errors: "_genErrors",
|
|
@@ -47,6 +50,7 @@ const STDLIB_METHOD_MAP = {
|
|
|
47
50
|
utf8: "_genUtf8",
|
|
48
51
|
path: "_genPath",
|
|
49
52
|
testing: "_genTesting",
|
|
53
|
+
shared: "_genShared",
|
|
50
54
|
};
|
|
51
55
|
|
|
52
56
|
const dispatchMethods = {
|
|
@@ -76,6 +80,7 @@ const dispatchMethods = {
|
|
|
76
80
|
|
|
77
81
|
/** @type {ThisType<CodeGen>} */
|
|
78
82
|
export const stdlibGenMethods = {
|
|
83
|
+
...bitsMethods,
|
|
79
84
|
...builderMethods,
|
|
80
85
|
...bytesMethods,
|
|
81
86
|
...errorsMethods,
|
|
@@ -89,6 +94,7 @@ export const stdlibGenMethods = {
|
|
|
89
94
|
...pathMethods,
|
|
90
95
|
...randMethods,
|
|
91
96
|
...regexpMethods,
|
|
97
|
+
...sharedMethods,
|
|
92
98
|
...slicesMethods,
|
|
93
99
|
...sortMethods,
|
|
94
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
|
},
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// CodeGen for Go `strconv` package.
|
|
2
|
+
|
|
3
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
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
|
+
|
|
10
|
+
const STRCONV_DISPATCH = {
|
|
11
|
+
Itoa: (a) => `String(${a[0]})`,
|
|
12
|
+
Atoi: (a) => parsed(`__strconv_atoi(${a[0]})`, "Number(r[0])"),
|
|
13
|
+
FormatBool: (a) => `String(${a[0]})`,
|
|
14
|
+
FormatInt: (a) => `(${a[0]}).toString(${a[1]})`,
|
|
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]})`),
|
|
20
|
+
ParseInt: (a) =>
|
|
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]})`),
|
|
26
|
+
Quote: (a) => `JSON.stringify(${a[0]})`,
|
|
27
|
+
Unquote: (a) =>
|
|
28
|
+
`((s) => { try { const v = JSON.parse(s); return [v, null]; } catch(e) { return ["", __error("invalid syntax")]; } })(${a[0]})`,
|
|
29
|
+
AppendInt: (a) =>
|
|
30
|
+
`[...(${a[0]}), ...new TextEncoder().encode((${a[1]}).toString(${a[2]}))]`,
|
|
31
|
+
AppendFloat: (a) =>
|
|
32
|
+
`[...(${a[0]}), ...new TextEncoder().encode(${a[1]}.toFixed(${a[3]} < 0 ? 6 : ${a[3]}))]`,
|
|
33
|
+
};
|
|
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
|
+
|
|
50
|
+
/** @type {ThisType<CodeGen>} */
|
|
51
|
+
export const strconvMethods = {
|
|
52
|
+
_genStrconv(fn, a) {
|
|
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");
|
|
56
|
+
return gen ? gen(a()) : undefined;
|
|
57
|
+
},
|
|
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];
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Mounts an HTML/expression tree onto a parent DOM node by emitting plain
|
|
3
3
|
// document.createElement / appendChild / setAttribute calls.
|
|
4
4
|
|
|
5
|
-
import { Parser } from "
|
|
6
|
-
import { T, Token } from "
|
|
5
|
+
import { Parser } from "../../parser/index.js";
|
|
6
|
+
import { T, Token } from "../../tokens.js";
|
|
7
7
|
|
|
8
8
|
/** @typedef {import('./index.js').CodeGen} CodeGen */
|
|
9
9
|
|
|
@@ -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
|
|