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.
Files changed (77) hide show
  1. package/README.md +174 -403
  2. package/package.json +16 -19
  3. package/src/{codegen → backend/js}/expressions.js +265 -77
  4. package/src/{codegen → backend/js}/index.js +120 -89
  5. package/src/{codegen → backend/js}/runtime.js +102 -0
  6. package/src/{codegen → backend/js}/source-map.js +32 -0
  7. package/src/{codegen → backend/js}/statements.js +139 -64
  8. package/src/backend/js/stdlib/bits.js +32 -0
  9. package/src/{codegen → backend/js}/stdlib/errors.js +2 -2
  10. package/src/{codegen → backend/js}/stdlib/fmt.js +26 -8
  11. package/src/{codegen → backend/js}/stdlib/index.js +6 -0
  12. package/src/{codegen → backend/js}/stdlib/maps.js +1 -1
  13. package/src/{codegen → backend/js}/stdlib/path.js +2 -2
  14. package/src/backend/js/stdlib/shared.js +17 -0
  15. package/src/{codegen → backend/js}/stdlib/slices.js +1 -1
  16. package/src/{codegen → backend/js}/stdlib/sort.js +1 -1
  17. package/src/backend/js/stdlib/strconv.js +58 -0
  18. package/src/{codegen → backend/js}/stdlib/strings.js +4 -1
  19. package/src/{codegen → backend/js}/stdlib/testing.js +2 -2
  20. package/src/{codegen → backend/js}/stdlib/time.js +3 -3
  21. package/src/{codegen → backend/js}/templ.js +3 -3
  22. package/src/backend/wasm/boundary.js +1798 -0
  23. package/src/backend/wasm/emit-builtins.js +1103 -0
  24. package/src/backend/wasm/emit-exprs.js +1908 -0
  25. package/src/backend/wasm/emit-maps.js +1863 -0
  26. package/src/backend/wasm/emit-shared.js +519 -0
  27. package/src/backend/wasm/emit-stdlib.js +3006 -0
  28. package/src/backend/wasm/emit-stmts.js +2472 -0
  29. package/src/backend/wasm/emit.js +1376 -0
  30. package/src/backend/wasm/encode.js +937 -0
  31. package/src/backend/wasm/glue.js +144 -0
  32. package/src/backend/wasm/index.js +2790 -0
  33. package/src/backend/wasm/monomorph.js +711 -0
  34. package/src/backend/wasm/optimize.js +227 -0
  35. package/src/backend/wasm/types.js +544 -0
  36. package/src/backend/wasm/wat.js +302 -0
  37. package/src/cli-core.js +323 -75
  38. package/src/compiler.js +447 -14
  39. package/src/dev-server.js +56 -15
  40. package/src/index.js +99 -120
  41. package/src/lexer.js +18 -0
  42. package/src/lower/boxing.js +35 -0
  43. package/src/lower/captures.js +172 -0
  44. package/src/lower/embedding.js +30 -0
  45. package/src/lower/escape.js +164 -0
  46. package/src/lower/functions.js +302 -0
  47. package/src/lower/globals.js +316 -0
  48. package/src/lower/index.js +118 -0
  49. package/src/lower/ownership.js +137 -0
  50. package/src/lower/range.js +67 -0
  51. package/src/lower/walk.js +25 -0
  52. package/src/minifier.js +2 -0
  53. package/src/parser/index.js +8 -3
  54. package/src/resolver.js +3 -1
  55. package/src/test-runner.js +94 -20
  56. package/src/typechecker/assignability.js +42 -1
  57. package/src/typechecker/expressions.js +182 -3
  58. package/src/typechecker/index.js +261 -5
  59. package/src/typechecker/resolve.js +35 -6
  60. package/src/typechecker/stdlib/core.js +34 -19
  61. package/src/typechecker/stdlib/extended.js +18 -3
  62. package/src/typechecker/stdlib/shared.js +53 -0
  63. package/src/typechecker/stdlib.js +2 -0
  64. package/src/typechecker/types.js +259 -6
  65. package/src/vendor.js +2 -2
  66. package/src/codegen/stdlib/strconv.js +0 -33
  67. /package/src/{codegen → backend/js}/stdlib/builder.js +0 -0
  68. /package/src/{codegen → backend/js}/stdlib/bytes.js +0 -0
  69. /package/src/{codegen → backend/js}/stdlib/gom.js +0 -0
  70. /package/src/{codegen → backend/js}/stdlib/html.js +0 -0
  71. /package/src/{codegen → backend/js}/stdlib/io.js +0 -0
  72. /package/src/{codegen → backend/js}/stdlib/math.js +0 -0
  73. /package/src/{codegen → backend/js}/stdlib/os.js +0 -0
  74. /package/src/{codegen → backend/js}/stdlib/rand.js +0 -0
  75. /package/src/{codegen → backend/js}/stdlib/regexp.js +0 -0
  76. /package/src/{codegen → backend/js}/stdlib/unicode.js +0 -0
  77. /package/src/{codegen → backend/js}/stdlib/utf8.js +0 -0
@@ -1,6 +1,13 @@
1
- // CodeGen statement methods — installed as a mixin on CodeGen.prototype.
2
-
3
- import { isComplex, isNumeric } from "../typechecker/types.js";
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 (vName !== "_") {
35
- if (isDefine) {
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
- this.line(`${this.genExpr(stmt.expr)}${stmt.op};`);
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._usesCmul = true;
400
+ this.useHelper("cmul");
306
401
  result = `__cmul(${lhsStr}, ${rhsExpr})`;
307
402
  break;
308
403
  case "/":
309
- this._usesCdiv = true;
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
- if (!stmt.init) return false;
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 isNumeric(iterType);
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
- if (names.length === 0) return false;
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
- if (isAssign) return false;
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
- if (!node || typeof node !== "object") return false;
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._usesSliceGuard = true;
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._usesError = true;
10
+ this.useHelper("error");
11
11
  return `__error(${args[0]})`;
12
12
  }
13
13
  if (fn === "Is") {
14
- this._usesErrorIs = true;
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._usesSprintf = true;
18
+ this.useHelper("sprintf");
19
19
  return `__sprintf(${fmtArgs})`;
20
20
  case "Errorf": {
21
- this._usesSprintf = true;
22
- this._usesError = true;
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._usesSprintf = true;
33
- return `process?.stdout?.write(__sprintf(${fmtArgs}))`;
34
+ this.useHelper("sprintf");
35
+ return this._genStdoutWrite(this._genPrintOperands(expr, "", ""));
34
36
  case "Println":
35
- this._usesSprintf = true;
36
- return `console.log(__sprintf(${fmtArgs}))`;
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._usesSprintf = true;
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,
@@ -6,7 +6,7 @@
6
6
  export const mapsMethods = {
7
7
  _genMaps(fn, a) {
8
8
  if (fn === "Equal") {
9
- this._usesEqual = true;
9
+ this.useHelper("equal");
10
10
  const [a1, b1] = a();
11
11
  return `__equal(${a1}, ${b1})`;
12
12
  }
@@ -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._usesPathClean = true;
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._usesPathClean = true;
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
+ };
@@ -41,7 +41,7 @@ const SLICES_DISPATCH = {
41
41
  export const slicesMethods = {
42
42
  _genSlices(fn, a) {
43
43
  if (fn === "Equal") {
44
- this._usesEqual = true;
44
+ this.useHelper("equal");
45
45
  const [a1, b1] = a();
46
46
  return `__equal(${a1}, ${b1})`;
47
47
  }
@@ -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._usesSortSlice = true;
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]) => `${s}.replace(${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._usesTesting = true;
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._usesSprintf = true;
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._usesTimeFmt = true;
38
+ this.useHelper("timeFmt");
39
39
  return `__timeFmt(${recvJs}._d, ${args[0]})`;
40
40
  }
41
41
  if (method === "String") {
42
- this._usesTimeFmt = true;
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._usesTimeParse = true;
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 "../parser/index.js";
6
- import { T, Token } from "../tokens.js";
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._usesSliceGuard = true;
289
+ this.useHelper("sliceGuard");
290
290
  return `for (const [${keyName}, ${valName}] of __s(${iterJs}).entries()) {`;
291
291
  },
292
292