gofront 1.6.1 → 1.8.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 (132) hide show
  1. package/README.md +45 -49
  2. package/package.json +12 -8
  3. package/src/backend/js/expressions.js +1627 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +508 -102
  6. package/src/backend/js/runtime.js +1997 -106
  7. package/src/backend/js/statements.js +931 -87
  8. package/src/backend/js/stdlib/bits.js +87 -14
  9. package/src/backend/js/stdlib/builder.js +41 -6
  10. package/src/backend/js/stdlib/bytes.js +253 -7
  11. package/src/backend/js/stdlib/errors.js +7 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +19 -2
  14. package/src/backend/js/stdlib/json.js +1167 -0
  15. package/src/backend/js/stdlib/maps.js +0 -8
  16. package/src/backend/js/stdlib/math.js +17 -1
  17. package/src/backend/js/stdlib/simd.js +34 -0
  18. package/src/backend/js/stdlib/slices.js +36 -6
  19. package/src/backend/js/stdlib/strconv.js +10 -5
  20. package/src/backend/js/stdlib/strings.js +83 -4
  21. package/src/backend/js/stdlib/testing.js +7 -0
  22. package/src/backend/js/stdlib/time.js +79 -55
  23. package/src/backend/js/stdlib/unicode.js +1 -1
  24. package/src/backend/js/stdlib/utf8.js +1 -1
  25. package/src/backend/js/templ.js +38 -76
  26. package/src/backend/js/type-info.js +272 -0
  27. package/src/backend/shared/fmt-types.js +188 -0
  28. package/src/backend/wasm/boundary.js +446 -72
  29. package/src/backend/wasm/emit-builtins.js +320 -13
  30. package/src/backend/wasm/emit-exprs.js +1404 -199
  31. package/src/backend/wasm/emit-maps.js +61 -13
  32. package/src/backend/wasm/emit-stmts.js +1359 -164
  33. package/src/backend/wasm/emit.js +518 -74
  34. package/src/backend/wasm/encode.js +128 -13
  35. package/src/backend/wasm/globals.js +452 -0
  36. package/src/backend/wasm/index.js +194 -2489
  37. package/src/backend/wasm/method-expr.js +381 -0
  38. package/src/backend/wasm/module-emitter.js +2240 -0
  39. package/src/backend/wasm/monomorph.js +62 -4
  40. package/src/backend/wasm/optimize.js +4 -2
  41. package/src/backend/wasm/prescan.js +397 -0
  42. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  43. package/src/backend/wasm/stdlib/core.js +1574 -199
  44. package/src/backend/wasm/stdlib/index.js +3 -5
  45. package/src/backend/wasm/stdlib/io.js +1567 -150
  46. package/src/backend/wasm/stdlib/packages.js +2 -0
  47. package/src/backend/wasm/stdlib/simd.js +499 -0
  48. package/src/backend/wasm/stdlib/time.js +1041 -12
  49. package/src/backend/wasm/types.js +221 -15
  50. package/src/backend/wasm/variadic.js +93 -0
  51. package/src/backend/wasm/wat.js +84 -20
  52. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  53. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  54. package/src/{vendor.js → cli/vendor.js} +89 -15
  55. package/src/compiler.js +107 -269
  56. package/src/dts-parser.js +71 -14
  57. package/src/index.js +15 -169
  58. package/src/{lexer.js → lexer/lexer.js} +102 -55
  59. package/src/{tokens.js → lexer/tokens.js} +1 -0
  60. package/src/link/sourcemap.js +97 -0
  61. package/src/link/stdlib.js +515 -0
  62. package/src/link/wasm.js +294 -0
  63. package/src/lower/bounds.js +394 -0
  64. package/src/lower/boxing.js +1 -6
  65. package/src/lower/captures.js +176 -32
  66. package/src/lower/embedding.js +2 -1
  67. package/src/lower/index.js +26 -18
  68. package/src/lower/json-codecs.js +1398 -0
  69. package/src/lower/ownership.js +22 -1
  70. package/src/lower/range.js +86 -1
  71. package/src/lower/reachability.js +132 -0
  72. package/src/parser/declarations.js +58 -13
  73. package/src/parser/expressions.js +29 -7
  74. package/src/parser/index.js +2 -1
  75. package/src/parser/statements.js +37 -22
  76. package/src/parser/struct-tag.js +144 -0
  77. package/src/{templ-parser.js → parser/templ.js} +138 -15
  78. package/src/parser/types.js +130 -13
  79. package/src/resolver.js +22 -1
  80. package/src/typechecker/assignability.js +215 -11
  81. package/src/typechecker/const-fold.js +356 -0
  82. package/src/typechecker/constants.js +256 -0
  83. package/src/typechecker/expressions.js +960 -99
  84. package/src/typechecker/index.js +491 -72
  85. package/src/typechecker/init-order.js +330 -0
  86. package/src/typechecker/resolve.js +596 -52
  87. package/src/typechecker/statements.js +157 -17
  88. package/src/typechecker/stdlib/core.js +333 -55
  89. package/src/typechecker/stdlib/excluded.js +898 -0
  90. package/src/typechecker/stdlib/extended.js +359 -26
  91. package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
  92. package/src/typechecker/stdlib/simd.js +123 -0
  93. package/src/typechecker/termination.js +1 -1
  94. package/src/typechecker/types.js +165 -26
  95. package/stdlib/bytes/iter.go +192 -0
  96. package/stdlib/bytes/search.go +240 -0
  97. package/stdlib/bytes/split.go +218 -0
  98. package/stdlib/cmp/cmp.go +65 -0
  99. package/stdlib/encoding/json/errors.go +31 -0
  100. package/stdlib/encoding/json/indent.go +71 -0
  101. package/stdlib/encoding/json/number.go +12 -0
  102. package/stdlib/encoding/json/stream.go +173 -0
  103. package/stdlib/errors/overlay.go +12 -0
  104. package/stdlib/hash/hash.go +53 -0
  105. package/stdlib/internal/jsonrt/dec.go +738 -0
  106. package/stdlib/internal/jsonrt/enc.go +366 -0
  107. package/stdlib/internal/jsonrt/indent.go +135 -0
  108. package/stdlib/internal/jsonrt/number.go +27 -0
  109. package/stdlib/internal/jsonrt/reader.go +73 -0
  110. package/stdlib/internal/jsonrt/scanner.go +649 -0
  111. package/stdlib/io/io.go +346 -0
  112. package/stdlib/iter/iter.go +18 -0
  113. package/stdlib/maps/iter.go +65 -0
  114. package/stdlib/math/extra.go +270 -0
  115. package/stdlib/slices/extra.go +73 -0
  116. package/stdlib/slices/iter.go +113 -0
  117. package/stdlib/sort/sort.go +561 -0
  118. package/stdlib/strconv/quote.go +341 -0
  119. package/stdlib/strings/extra.go +35 -0
  120. package/stdlib/strings/iter.go +122 -0
  121. package/stdlib/sync/extra.go +105 -0
  122. package/stdlib/unicode/unicode.go +112 -0
  123. package/stdlib/unicode/utf8/bytes.go +239 -0
  124. package/src/backend/wasm/const-fold.js +0 -205
  125. package/src/backend/wasm/emit-stdlib.js +0 -13
  126. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  127. /package/src/{colors.js → cli/colors.js} +0 -0
  128. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  129. /package/src/{minifier.js → cli/minifier.js} +0 -0
  130. /package/src/{project-config.js → cli/project-config.js} +0 -0
  131. /package/src/{pwa.js → cli/pwa.js} +0 -0
  132. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -1,5 +1,13 @@
1
1
  // CodeGen for Go `fmt` package — installed onto CodeGen.prototype via stdlibGenMethods.
2
2
 
3
+ import {
4
+ fmtDescHasFloat,
5
+ fmtDescIsSeq,
6
+ fmtTypeDesc,
7
+ fmtWantsTypes,
8
+ structOwnFields,
9
+ } from "../../shared/fmt-types.js";
10
+
3
11
  /** @typedef {import('../index.js').CodeGen} CodeGen */
4
12
 
5
13
  const _SSCAN_LOOP =
@@ -14,23 +22,341 @@ const _SSCAN_LOOP =
14
22
  `else if (typeof p.value === "boolean") { const tl = String(t).toLowerCase(); if (tl === "true" || tl === "1" || tl === "t") p.value = true; else if (tl === "false" || tl === "0" || tl === "f") p.value = false; else { err = __error("expected boolean"); break; } } ` +
15
23
  `else p.value = t; n++; }`;
16
24
 
25
+ const FMT_METHODS = ["Error", "String"];
26
+ const isExportedName = (n) => /^\p{Lu}/u.test(n);
27
+ // Descriptor of a struct value whose class has a pointer-receiver Error/String:
28
+ // a value element or field must not be taken for a pointer because an earlier
29
+ // &x tagged the same object (see isPointer in __sprintf).
30
+ const FMT_STRUCT_VALUE = "{ sv: 1 }";
31
+
17
32
  /** @type {ThisType<CodeGen>} */
18
33
  export const fmtMethods = {
19
- _fmtArgJs(e) {
34
+ // ── Stringer/error dispatch metadata (read by __sprintf) ─────────────
35
+ //
36
+ // Named non-struct values are raw numbers/strings/arrays inside slices,
37
+ // maps and struct fields, and a struct and a pointer to it share one class.
38
+ // The type is therefore carried two ways, both only where a method can be
39
+ // called: a descriptor of the container's element types, attached when a
40
+ // slice/array/map is converted to an interface (static type passing, see
41
+ // __ifd), and per-class metadata for struct fields (__ff) and for
42
+ // pointer-receiver Error/String methods (__pr).
43
+
44
+ // JS source of the fmt descriptor for static type `t`, or null when no
45
+ // value of the type needs one:
46
+ // { b: Class, n } a raw value of a named type with a value-receiver
47
+ // Error/String; Class boxes it, n: numeric (map keys)
48
+ // { sv: 1 } a struct value with a pointer-receiver Error/String
49
+ // { e } elements of a slice or array
50
+ // { k, v } keys and values of a map
51
+ // { np: obj } a *T whose Error/String has a pointer receiver: obj
52
+ // stands in for a nil element (see _fmtNilPtrOperand)
53
+ // { m: 1, ... } a map that is not boxed itself (a struct field or an
54
+ // element): the object map carries no marker of its own
55
+ // `bare`: the value is the operand being boxed (a map is then marked by
56
+ // the boxing itself).
57
+ _fmtDesc(t, seen = new Set(), bare = true) {
58
+ t = this.checker?.resolveType?.(t) ?? t;
59
+ if (!t || seen.has(t)) return null;
60
+ const next = new Set(seen).add(t);
61
+ if (t.kind === "named") return this._fmtNamedDesc(t, next, bare);
62
+ if (t.kind === "slice" || t.kind === "array") {
63
+ const e = this._fmtDesc(t.elem, next, false);
64
+ return e ? `{ e: ${e} }` : null;
65
+ }
66
+ if (t.kind === "struct") return this._fmtAnonStructDesc(t, next);
67
+ if (t.kind === "pointer") {
68
+ if (bare) return null;
69
+ const np = this._fmtNilPtrOperand(t);
70
+ return np ? `{ np: ${np} }` : null;
71
+ }
72
+ if (t.kind !== "map") return null;
73
+ const parts = [
74
+ ["k", this._fmtDesc(t.key, next, false)],
75
+ ["v", this._fmtDesc(t.value, next, false)],
76
+ ].filter(([, d]) => d);
77
+ if (!bare) parts.unshift(["m", "1"]);
78
+ return parts.length
79
+ ? `{ ${parts.map(([n, d]) => `${n}: ${d}`).join(", ")} }`
80
+ : null;
81
+ },
82
+
83
+ // An anonymous struct has no class to carry field descriptors: { ff } holds
84
+ // them, keyed by field name.
85
+ _fmtAnonStructDesc(st, seen) {
86
+ const fields = [];
87
+ for (const [fname, ftype] of structOwnFields(st)) {
88
+ const desc = this._fmtDesc(ftype, seen, false);
89
+ if (desc) fields.push(`${JSON.stringify(fname)}: ${desc}`);
90
+ }
91
+ return fields.length > 0 ? `{ ff: { ${fields.join(", ")} } }` : null;
92
+ },
93
+
94
+ _fmtNamedDesc(t, seen, bare = true) {
95
+ const u = this.checker?.resolveType?.(t.underlying) ?? t.underlying;
96
+ const info = this._namedBoxInfo(t);
97
+ if (
98
+ info &&
99
+ FMT_METHODS.some((m) => info.methods.get(m)?._ptrRecv === false)
100
+ ) {
101
+ const numeric = u?.kind === "basic" && /^(u?int|float)/.test(u.name);
102
+ return `{ b: ${info.cls}, n: ${numeric ? 1 : 0} }`;
103
+ }
104
+ if (u?.kind === "struct" && this._fmtHasPtrMethod(u))
105
+ return FMT_STRUCT_VALUE;
106
+ if (!u || u.kind === "struct" || u.kind === "interface") return null;
107
+ return this._fmtDesc(u, seen, bare);
108
+ },
109
+
110
+ // Whether struct `st` has a pointer-receiver Error/String, declared or
111
+ // promoted from an embedded struct.
112
+ _fmtHasPtrMethod(st, seen = new Set()) {
113
+ if (seen.has(st)) return false;
114
+ seen.add(st);
115
+ if (FMT_METHODS.some((m) => st.methods?.get(m)?._ptrRecv)) return true;
116
+ return (st._embeds ?? []).some((raw) => {
117
+ const inner = (raw.kind === "pointer" ? raw.base : raw).underlying;
118
+ return inner?.kind === "struct" && this._fmtHasPtrMethod(inner, seen);
119
+ });
120
+ },
121
+
122
+ // Error/String promoted from embedded interface fields, as { method: field }.
123
+ _fmtPromoted(st, methodDecls) {
124
+ const out = {};
125
+ for (const f of st._structFields ?? []) {
126
+ if (!f.embedded) continue;
127
+ const t = this.checker?.resolveType?.(f.type) ?? f.type;
128
+ const it = t?.kind === "named" ? t.underlying : t;
129
+ const key = (f.astType?.name ?? t?.name ?? "").split(".").pop();
130
+ if (it?.kind !== "interface" || !key) continue;
131
+ for (const mn of FMT_METHODS) {
132
+ const m = it.methods?.get(mn);
133
+ if (!m || m.params?.length > 0 || out[mn]) continue;
134
+ if (methodDecls.some((d) => d.name === mn)) continue;
135
+ out[mn] = key;
136
+ }
137
+ }
138
+ return out;
139
+ },
140
+
141
+ // Per-class fmt metadata of a struct: which of Error/String have a pointer
142
+ // receiver, and the descriptors of the exported fields that hold raw
143
+ // Stringer values.
144
+ _genFmtClassMeta(name, methodDecls) {
145
+ const st = this.checker?.types.get(name)?.underlying;
146
+ if (st?.kind !== "struct") return;
147
+ const ptrRecv = FMT_METHODS.filter((mn) =>
148
+ this._fmtPtrRecv(st, methodDecls, mn),
149
+ );
150
+ if (ptrRecv.length > 0)
151
+ this.line(
152
+ `Object.defineProperty(${name}.prototype, "__pr", { value: ${JSON.stringify(ptrRecv)} });`,
153
+ );
154
+ const promoted = this._fmtPromoted(st, methodDecls);
155
+ if (Object.keys(promoted).length > 0)
156
+ this.line(
157
+ `Object.defineProperty(${name}.prototype, "__pe", { value: ${JSON.stringify(promoted)} });`,
158
+ );
159
+ const fields = [];
160
+ const own = structOwnFields(st);
161
+ // The class also carries a copy of every promoted field (see
162
+ // _promotedPath); fmt lists only the fields the struct declares.
163
+ if (own.length < st.fields.size)
164
+ this.line(
165
+ `Object.defineProperty(${name}.prototype, "__fk", { value: ${JSON.stringify(own.map(([n]) => n))} });`,
166
+ );
167
+ for (const [fname, ftype] of own) {
168
+ const desc = this._fmtDesc(ftype, new Set(), false);
169
+ // Unexported fields get no method dispatch, only the value marker
170
+ // (and the map marker).
171
+ if (
172
+ desc &&
173
+ (isExportedName(fname) ||
174
+ desc === FMT_STRUCT_VALUE ||
175
+ /\bm: 1\b/.test(desc))
176
+ )
177
+ fields.push(`${fname}: ${desc}`);
178
+ }
179
+ // A thunk: the descriptors name classes declared further down.
180
+ if (fields.length > 0)
181
+ this.line(
182
+ `Object.defineProperty(${name}.prototype, "__ff", { value: () => ({ ${fields.join(", ")} }) });`,
183
+ );
184
+ },
185
+
186
+ // Whether method `mn` of struct `st` has a pointer receiver: declared on it,
187
+ // or promoted from an embedded struct (an embedded pointer promotes every
188
+ // method to the value's method set).
189
+ _fmtPtrRecv(st, methodDecls, mn) {
190
+ const decl = methodDecls.find((d) => d.name === mn);
191
+ if (decl) return Boolean(decl.recvPointer);
192
+ for (const raw of st._embeds ?? []) {
193
+ const isPtr = raw.kind === "pointer";
194
+ const m = (isPtr ? raw.base : raw).underlying?.methods?.get(mn);
195
+ if (m) return !isPtr && Boolean(m._ptrRecv);
196
+ }
197
+ return false;
198
+ },
199
+
200
+ // A nil `*T` whose Error/String has a pointer receiver still calls the method
201
+ // (Go does; a panic in it, usually a nil dereference, prints <nil>). The JS
202
+ // source of a stand-in for the nil pointer, or null when `t` is not such a
203
+ // pointer. Only operands whose static type is the pointer are covered.
204
+ _fmtNilPtrOperand(t) {
205
+ t = this.checker?.resolveType?.(t) ?? t;
206
+ if (t?.kind !== "pointer") return null;
207
+ const base = this.checker?.resolveType?.(t.base) ?? t.base;
208
+ const st =
209
+ this.checker?.resolveType?.(base?.underlying) ?? base?.underlying;
210
+ if (base?.kind !== "named" || st?.kind !== "struct" || !base.name)
211
+ return null;
212
+ for (const mn of FMT_METHODS) {
213
+ const m = st.methods?.get(mn) ?? base.methods?.get(mn);
214
+ // Instantiated generic methods are copies without _ptrRecv.
215
+ const generic = base.typeArgs
216
+ ? this.checker?.types.get(base.name)?._generic
217
+ : null;
218
+ const ptrRecv = m?._ptrRecv ?? generic?.methods?.get(mn)?._ptrRecv;
219
+ if (!m || !ptrRecv || m.params?.length > 0) continue;
220
+ const cls = this._dequalify(base.name).replace(/\[.*$/, "");
221
+ if (cls.includes(".")) return null;
222
+ return `{ ${mn}() { try { return ${cls}.prototype.${mn}.call(null); } catch (e) { return "<nil>"; } }, __p: true, __nilp: true }`;
223
+ }
224
+ return null;
225
+ },
226
+
227
+ // `typed`: the format string needs operand types (`%T`, `%#v`, EXTRA, see
228
+ // fmtWantsTypes), so the operand travels with its static type as
229
+ // `__ft(value, descriptor)`; operands of interface type carry their dynamic
230
+ // type and stay bare.
231
+ _fmtArgJs(e, typed = false) {
232
+ // `xs...` passes each element as its own operand.
233
+ if (e?._spread) return `...(${this.genExpr(e)} ?? [])`;
234
+ // A multi-value call (`f()` returning a tuple) passes every result.
235
+ if (e?._type?.kind === "tuple") return this._fmtTupleJs(e);
236
+ const nilOperand = this._fmtNilPtrOperand(e?._type);
237
+ if (nilOperand) return `(${this.genExpr(e)} ?? ${nilOperand})`;
20
238
  const js = this.genExpr(e);
239
+ // An operand of a type parameter type: the type argument knows how fmt
240
+ // should see it (a Stringer named type is boxed, a slice gets its elements).
241
+ const tpf =
242
+ e?._type?.kind === "typeParam"
243
+ ? this._typeParamZeroFactory(e._type.name)
244
+ : null;
245
+ if (tpf) return `(${tpf}.fb ?? ${tpf}.b ?? ((__v) => __v))(${js})`;
21
246
  const base =
22
247
  e?._type?.kind === "named" ? (e._type.underlying ?? e._type) : e?._type;
23
- return base?.kind === "slice" ? `(${js} ?? [])` : js;
248
+ if (e?._type?.name === "time.Duration") {
249
+ this.useHelper("duration");
250
+ this.useHelper("sprintf");
251
+ return `__ft(${js}, { k: "named", n: "time.Duration" })`;
252
+ }
253
+ const desc = fmtTypeDesc(
254
+ e?._type,
255
+ (t) => this.checker?.resolveType?.(t) ?? t,
256
+ );
257
+ if ((typed || fmtDescHasFloat(desc)) && desc && desc.k !== "i") {
258
+ this.useHelper("sprintf");
259
+ return `__ft(${js}, ${JSON.stringify(desc)})`;
260
+ }
261
+ return this._fmtNilValue(base, js, false);
262
+ },
263
+
264
+ // A nil slice or map operand prints as `[]` / `map[]`: coalesce the null
265
+ // to an empty value. Shared by the single-operand and the tuple path.
266
+ // `unboxed`: the map still needs its marker (the caller boxes the result).
267
+ _fmtNilValue(base, js, unboxed = true) {
268
+ if (base?.kind === "slice") return `(${js} ?? [])`;
269
+ if (unboxed && base?.kind === "map") return `(${js} ?? {})`;
270
+ return js;
271
+ },
272
+
273
+ // `js` (a value of static type `t`) boxed as converting it to an `any` does,
274
+ // with the same boxing the single-operand path gets from the checker
275
+ // (_markIfaceBox): named types with methods, pointers, slices, maps, structs
276
+ // and the fmt descriptor of Stringer elements.
277
+ _fmtBoxValue(js, t) {
278
+ const resolve = (x) => this.checker?.resolveType?.(x) ?? x;
279
+ let base = resolve(t);
280
+ if (base?.kind === "named") base = resolve(base.underlying);
281
+ const meta = this.checker?._ifaceBoxMeta?.(t);
282
+ if (meta?.type)
283
+ return this._genNamedIfaceBox(js, meta.type, meta.box === "named_ptr");
284
+ let out = this._fmtNilValue(base, js);
285
+ if (meta) out = this._ifaceBoxWrap(out, meta.box, meta.ptrFields);
286
+ const desc = this._fmtDesc(t);
287
+ return desc ? this._fmtDescWrap(out, desc) : out;
288
+ },
289
+
290
+ // The results of a multi-value call as separate operands. Each result is
291
+ // boxed as if it were assigned to an `any`, so pointers print as `&{...}`
292
+ // and floats keep their width.
293
+ _fmtTupleJs(e) {
294
+ const resolve = (t) => this.checker?.resolveType?.(t) ?? t;
295
+ const parts = e._type.types.map((t, i) => {
296
+ const boxed = this._fmtBoxValue(`__t[${i}]`, t);
297
+ const desc = fmtTypeDesc(t, resolve);
298
+ if (fmtDescHasFloat(desc) && desc.k !== "i") {
299
+ this.useHelper("sprintf");
300
+ return `__ft(${boxed}, ${JSON.stringify(desc)})`;
301
+ }
302
+ return boxed;
303
+ });
304
+ return `...((__t) => [${parts.join(", ")}])(${this.genExpr(e)})`;
305
+ },
306
+
307
+ // The operands of a Printf-style call whose format is args[fmtIdx].
308
+ _fmtFormatArgsJs(args, fmtIdx) {
309
+ const lit = args[fmtIdx];
310
+ const spread = args.some((e) => e?._spread);
311
+ const resolve = (t) => this.checker?.resolveType?.(t) ?? t;
312
+ const seqArgs = args
313
+ .slice(fmtIdx + 1)
314
+ .some((e) => fmtDescIsSeq(fmtTypeDesc(e?._type, resolve)));
315
+ const typed =
316
+ lit?.kind === "BasicLit" &&
317
+ fmtWantsTypes(lit.value, spread ? -1 : args.length - fmtIdx - 1, seqArgs);
318
+ if (typed) this.useHelper("sprintf");
319
+ return args.map((e, idx) =>
320
+ idx < fmtIdx
321
+ ? this.genExpr(e)
322
+ : idx === fmtIdx
323
+ ? this.genExpr(e)
324
+ : this._fmtArgJs(e, typed),
325
+ );
24
326
  },
25
327
 
26
328
  _genFmt(fn, _a, expr) {
27
- const fmtArgs = expr.args
28
- .map((e, idx) => (idx === 0 ? this.genExpr(e) : this._fmtArgJs(e)))
29
- .join(", ");
329
+ const fmtArgs = ["Sprintf", "Printf", "Errorf"].includes(fn)
330
+ ? this._fmtFormatArgsJs(expr.args, 0).join(", ")
331
+ : "";
30
332
  switch (fn) {
31
333
  case "Sprintf":
32
334
  this.useHelper("sprintf");
33
335
  return `__sprintf(${fmtArgs})`;
336
+ case "Sprint":
337
+ case "Sprintln":
338
+ this.useHelper("sprint");
339
+ return `__sprint(${fn === "Sprintln"}${expr.args.map((e) => `, ${this._fmtArgJs(e)}`).join("")})`;
340
+ case "Append":
341
+ case "Appendln":
342
+ case "Appendf": {
343
+ this.useHelper("sprint");
344
+ const [dst, ...rest] = expr.args;
345
+ const restJs = (
346
+ fn === "Appendf"
347
+ ? this._fmtFormatArgsJs(rest, 0)
348
+ : rest.map((e) => this._fmtArgJs(e))
349
+ ).join(", ");
350
+ const str =
351
+ fn === "Appendf"
352
+ ? `__sprintf(${restJs})`
353
+ : `__sprint(${fn === "Appendln"}${restJs ? `, ${restJs}` : ""})`;
354
+ // Like append: write in place only when the whole result fits in
355
+ // dst's spare capacity, otherwise copy into one fresh array and
356
+ // leave dst's backing array untouched (never chunk the write).
357
+ this.useHelper("append");
358
+ return `((d, s) => { const e = new TextEncoder().encode(s); if (d == null && e.length === 0) return null; const r = d ?? new Uint8Array(0); const n = r.length + e.length; const c = __tcap.get(r); if (c !== undefined && n <= c) { const v = new Uint8Array(r.buffer, r.byteOffset, n); v.set(e, r.length); if (c > n) __tcap.set(v, c); return v; } const v = new Uint8Array(n); v.set(r); v.set(e, r.length); return v; })(${this.genExpr(dst)}, ${str})`;
359
+ }
34
360
  case "Errorf": {
35
361
  this.useHelper("sprintf");
36
362
  this.useHelper("error");
@@ -46,7 +372,10 @@ export const fmtMethods = {
46
372
  return this._genStdoutWrite(`__sprintf(${fmtArgs})`);
47
373
  case "Print":
48
374
  this.useHelper("sprintf");
49
- return this._genStdoutWrite(this._genPrintOperands(expr, "", ""));
375
+ this.useHelper("sprint");
376
+ return this._genStdoutWrite(
377
+ `__sprint(false${expr.args.map((e) => `, ${this._fmtArgJs(e)}`).join("")})`,
378
+ );
50
379
  case "Println":
51
380
  this.useHelper("sprintf");
52
381
  // console.log supplies the trailing newline.
@@ -60,7 +389,7 @@ export const fmtMethods = {
60
389
  const strArg = this.genExpr(expr.args[0]);
61
390
  const restArgs = expr.args
62
391
  .slice(1)
63
- .map((e) => this.genExpr(e))
392
+ .map((e) => (e._spread ? `...${this.genExpr(e)}` : this.genExpr(e)))
64
393
  .join(", ");
65
394
  return `((str, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); ${_SSCAN_LOOP} return [n, err ? err : (n < ptrs.length ? __error("unexpected EOF") : null)]; })(${strArg}, ${restArgs})`;
66
395
  }
@@ -69,7 +398,7 @@ export const fmtMethods = {
69
398
  const strArg = this.genExpr(expr.args[0]);
70
399
  const restArgs = expr.args
71
400
  .slice(1)
72
- .map((e) => this.genExpr(e))
401
+ .map((e) => (e._spread ? `...${this.genExpr(e)}` : this.genExpr(e)))
73
402
  .join(", ");
74
403
  return `((str, ...ptrs) => { const tokens = str.split("\\n")[0].trim().split(/\\s+/).filter(Boolean); ${_SSCAN_LOOP} return [n, err ? err : (n < ptrs.length ? __error("unexpected EOF") : null)]; })(${strArg}, ${restArgs})`;
75
404
  }
@@ -79,7 +408,7 @@ export const fmtMethods = {
79
408
  const fmtArg = this.genExpr(expr.args[1]);
80
409
  const restArgs = expr.args
81
410
  .slice(2)
82
- .map((e) => this.genExpr(e))
411
+ .map((e) => (e._spread ? `...${this.genExpr(e)}` : this.genExpr(e)))
83
412
  .join(", ");
84
413
  return (
85
414
  `((str, fmt, ...ptrs) => { ` +
@@ -132,7 +461,11 @@ export const fmtMethods = {
132
461
  this.useHelper("sprintf");
133
462
  const writerArg = expr.args[0];
134
463
  const rest = expr.args.slice(1);
135
- const restJs = rest.map((e) => this._fmtArgJs(e)).join(", ");
464
+ const restJs = (
465
+ fn === "Fprintf"
466
+ ? this._fmtFormatArgsJs(rest, 0)
467
+ : rest.map((e) => this._fmtArgJs(e))
468
+ ).join(", ");
136
469
  const writerType = writerArg._type;
137
470
  const targetTypeName =
138
471
  writerType?.name ??
@@ -162,6 +495,12 @@ export const fmtMethods = {
162
495
  _genPrintOperands(expr, sep, suffix) {
163
496
  const argc = expr.args.length;
164
497
  if (argc === 0) return JSON.stringify(suffix);
498
+ if (expr.args.some((e) => e._spread || e._type?.kind === "tuple")) {
499
+ // The operand count is only known at run time: join with Sprint's rules.
500
+ this.useHelper("sprint");
501
+ const joined = `__sprint(${sep === " "}, ${expr.args.map((e) => this._fmtArgJs(e)).join(", ")})`;
502
+ return sep === " " ? `${joined}.slice(0, -1)` : joined;
503
+ }
165
504
  const fmt = Array(argc).fill("%v").join(sep) + suffix;
166
505
  const args = expr.args.map((e) => this._fmtArgJs(e)).join(", ");
167
506
  return `__sprintf(${JSON.stringify(fmt)}, ${args})`;
@@ -12,6 +12,7 @@ import { gomMethods } from "./gom.js";
12
12
  import { hexMethods } from "./hex.js";
13
13
  import { htmlMethods } from "./html.js";
14
14
  import { ioMethods } from "./io.js";
15
+ import { jsonMethods } from "./json.js";
15
16
  import { mapsMethods } from "./maps.js";
16
17
  import { mathMethods } from "./math.js";
17
18
  import { osMethods } from "./os.js";
@@ -20,6 +21,7 @@ import { randMethods } from "./rand.js";
20
21
  import { regexpMethods } from "./regexp.js";
21
22
  import { sha256Methods } from "./sha256.js";
22
23
  import { sharedMethods } from "./shared.js";
24
+ import { simdMethods } from "./simd.js";
23
25
  import { slicesMethods } from "./slices.js";
24
26
  import { sortMethods } from "./sort.js";
25
27
  import { strconvMethods } from "./strconv.js";
@@ -62,6 +64,8 @@ const STDLIB_METHOD_MAP = {
62
64
  binary: "_genBinary",
63
65
  hex: "_genHex",
64
66
  base64: "_genBase64",
67
+ json: "_genJson",
68
+ simd: "_genSimd",
65
69
  };
66
70
 
67
71
  const dispatchMethods = {
@@ -82,10 +86,21 @@ const dispatchMethods = {
82
86
  // Dispatch table for stdlib namespace calls (pkg.Func → inline JS).
83
87
  // Returns the generated JS string, or undefined if not handled.
84
88
  _genStdlibCall(ns, fn, expr) {
85
- const a = () => expr.args.map((e) => this.genExpr(e));
86
89
  if (ns === "gom") return this._genGom(fn, expr);
87
90
  const method = STDLIB_METHOD_MAP[ns];
88
- return method ? this[method](fn, a, expr) : undefined;
91
+ if (!method) return undefined;
92
+ // Stdlib shims take raw values; only fmt and errors use the interface box
93
+ // of a named value (Error() dispatch, error comparisons).
94
+ const isNamespace = expr.func?.expr?._type?.kind === "namespace";
95
+ if (isNamespace && ns !== "fmt" && ns !== "errors") {
96
+ for (const e of expr.args) if (e) e._noNamedBox = true;
97
+ }
98
+ const a = () => expr.args.map((e) => this.genExpr(e));
99
+ const res = this[method](fn, a, expr);
100
+ if (res === undefined && isNamespace && ns !== "fmt" && ns !== "errors") {
101
+ for (const e of expr.args) if (e) delete e._noNamedBox;
102
+ }
103
+ return res;
89
104
  },
90
105
  };
91
106
 
@@ -106,6 +121,7 @@ export const stdlibGenMethods = {
106
121
  ...randMethods,
107
122
  ...regexpMethods,
108
123
  ...sharedMethods,
124
+ ...simdMethods,
109
125
  ...slicesMethods,
110
126
  ...sortMethods,
111
127
  ...strconvMethods,
@@ -119,5 +135,6 @@ export const stdlibGenMethods = {
119
135
  ...binaryMethods,
120
136
  ...hexMethods,
121
137
  ...base64Methods,
138
+ ...jsonMethods,
122
139
  ...dispatchMethods,
123
140
  };