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.
Files changed (60) hide show
  1. package/README.md +174 -466
  2. package/package.json +16 -20
  3. package/src/backend/js/expressions.js +50 -19
  4. package/src/backend/js/index.js +100 -55
  5. package/src/backend/js/runtime.js +102 -0
  6. package/src/backend/js/source-map.js +32 -0
  7. package/src/backend/js/statements.js +96 -16
  8. package/src/backend/js/stdlib/errors.js +2 -2
  9. package/src/backend/js/stdlib/fmt.js +26 -8
  10. package/src/backend/js/stdlib/index.js +3 -0
  11. package/src/backend/js/stdlib/maps.js +1 -1
  12. package/src/backend/js/stdlib/path.js +2 -2
  13. package/src/backend/js/stdlib/shared.js +17 -0
  14. package/src/backend/js/stdlib/slices.js +1 -1
  15. package/src/backend/js/stdlib/sort.js +1 -1
  16. package/src/backend/js/stdlib/strconv.js +34 -9
  17. package/src/backend/js/stdlib/strings.js +4 -1
  18. package/src/backend/js/stdlib/testing.js +2 -2
  19. package/src/backend/js/stdlib/time.js +3 -3
  20. package/src/backend/js/templ.js +1 -1
  21. package/src/backend/wasm/boundary.js +486 -33
  22. package/src/backend/wasm/emit-builtins.js +1103 -0
  23. package/src/backend/wasm/emit-exprs.js +1908 -0
  24. package/src/backend/wasm/emit-maps.js +1863 -0
  25. package/src/backend/wasm/emit-shared.js +519 -0
  26. package/src/backend/wasm/emit-stdlib.js +3006 -0
  27. package/src/backend/wasm/emit-stmts.js +2472 -0
  28. package/src/backend/wasm/emit.js +606 -5009
  29. package/src/backend/wasm/encode.js +212 -64
  30. package/src/backend/wasm/glue.js +19 -5
  31. package/src/backend/wasm/index.js +1426 -49
  32. package/src/backend/wasm/monomorph.js +711 -0
  33. package/src/backend/wasm/optimize.js +227 -0
  34. package/src/backend/wasm/types.js +159 -35
  35. package/src/backend/wasm/wat.js +49 -2
  36. package/src/cli-core.js +275 -72
  37. package/src/compiler.js +144 -21
  38. package/src/index.js +86 -115
  39. package/src/lower/boxing.js +3 -4
  40. package/src/lower/captures.js +10 -17
  41. package/src/lower/escape.js +4 -9
  42. package/src/lower/functions.js +277 -5
  43. package/src/lower/globals.js +7 -15
  44. package/src/lower/index.js +5 -2
  45. package/src/lower/ownership.js +5 -10
  46. package/src/lower/range.js +2 -5
  47. package/src/lower/walk.js +25 -0
  48. package/src/resolver.js +2 -1
  49. package/src/test-runner.js +20 -0
  50. package/src/typechecker/assignability.js +42 -1
  51. package/src/typechecker/expressions.js +142 -2
  52. package/src/typechecker/index.js +27 -1
  53. package/src/typechecker/resolve.js +35 -6
  54. package/src/typechecker/statements.js +0 -7
  55. package/src/typechecker/stdlib/core.js +17 -19
  56. package/src/typechecker/stdlib/extended.js +18 -3
  57. package/src/typechecker/stdlib/shared.js +53 -0
  58. package/src/typechecker/stdlib.js +2 -0
  59. package/src/typechecker/types.js +206 -7
  60. 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 (vName !== "_") {
42
- if (isDefine) {
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._usesCmul = true;
400
+ this.useHelper("cmul");
337
401
  result = `__cmul(${lhsStr}, ${rhsExpr})`;
338
402
  break;
339
403
  case "/":
340
- this._usesCdiv = true;
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._usesSliceGuard = true;
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._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;
@@ -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,
@@ -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
  },
@@ -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) => `String(${a[0]})`,
12
- ParseFloat: (a) =>
13
- `(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}), null])`,
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
- `(Number.isNaN(parseInt(${a[0]}, ${a[1]} || 10)) ? [0, "invalid syntax"] : [parseInt(${a[0]}, ${a[1]} || 10), null])`,
16
- ParseBool: (a) =>
17
- `(${a[0]} === "true" || ${a[0]} === "1" ? [true, null] : ${a[0]} === "false" || ${a[0]} === "0" ? [false, null] : [false, "invalid syntax"])`,
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]) => `${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];
@@ -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