gofront 1.7.0 → 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 (128) hide show
  1. package/README.md +13 -13
  2. package/package.json +8 -37
  3. package/src/backend/js/expressions.js +1341 -148
  4. package/src/backend/js/fmt-runtime.js +573 -0
  5. package/src/backend/js/index.js +375 -57
  6. package/src/backend/js/runtime.js +1460 -133
  7. package/src/backend/js/statements.js +900 -86
  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 +2 -1
  12. package/src/backend/js/stdlib/fmt.js +349 -10
  13. package/src/backend/js/stdlib/index.js +8 -3
  14. package/src/backend/js/stdlib/json.js +247 -52
  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 +10 -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 +436 -80
  29. package/src/backend/wasm/emit-builtins.js +303 -13
  30. package/src/backend/wasm/emit-exprs.js +1397 -203
  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 +512 -77
  34. package/src/backend/wasm/encode.js +43 -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/prescan.js +397 -0
  41. package/src/backend/wasm/stdlib/bytes.js +1804 -127
  42. package/src/backend/wasm/stdlib/core.js +1574 -199
  43. package/src/backend/wasm/stdlib/index.js +0 -5
  44. package/src/backend/wasm/stdlib/io.js +1567 -150
  45. package/src/backend/wasm/stdlib/packages.js +2 -0
  46. package/src/backend/wasm/stdlib/simd.js +293 -28
  47. package/src/backend/wasm/stdlib/time.js +1041 -12
  48. package/src/backend/wasm/types.js +200 -15
  49. package/src/backend/wasm/variadic.js +93 -0
  50. package/src/backend/wasm/wat.js +47 -20
  51. package/src/{cli-core.js → cli/cli-core.js} +236 -10
  52. package/src/{test-runner.js → cli/test-runner.js} +332 -39
  53. package/src/{vendor.js → cli/vendor.js} +3 -4
  54. package/src/compiler.js +107 -269
  55. package/src/index.js +15 -169
  56. package/src/{lexer.js → lexer/lexer.js} +102 -55
  57. package/src/{tokens.js → lexer/tokens.js} +1 -0
  58. package/src/link/sourcemap.js +97 -0
  59. package/src/link/stdlib.js +515 -0
  60. package/src/link/wasm.js +294 -0
  61. package/src/lower/bounds.js +394 -0
  62. package/src/lower/boxing.js +1 -1
  63. package/src/lower/captures.js +176 -32
  64. package/src/lower/index.js +26 -18
  65. package/src/lower/json-codecs.js +1398 -0
  66. package/src/lower/ownership.js +22 -1
  67. package/src/lower/range.js +86 -1
  68. package/src/lower/reachability.js +132 -0
  69. package/src/parser/declarations.js +58 -13
  70. package/src/parser/expressions.js +29 -7
  71. package/src/parser/index.js +2 -1
  72. package/src/parser/statements.js +37 -22
  73. package/src/{templ-parser.js → parser/templ.js} +138 -15
  74. package/src/parser/types.js +69 -9
  75. package/src/resolver.js +22 -1
  76. package/src/typechecker/assignability.js +176 -17
  77. package/src/typechecker/const-fold.js +356 -0
  78. package/src/typechecker/constants.js +256 -0
  79. package/src/typechecker/expressions.js +931 -122
  80. package/src/typechecker/index.js +459 -67
  81. package/src/typechecker/init-order.js +330 -0
  82. package/src/typechecker/resolve.js +451 -56
  83. package/src/typechecker/statements.js +90 -9
  84. package/src/typechecker/stdlib/core.js +333 -55
  85. package/src/typechecker/stdlib/excluded.js +898 -0
  86. package/src/typechecker/stdlib/extended.js +322 -26
  87. package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
  88. package/src/typechecker/stdlib/simd.js +65 -15
  89. package/src/typechecker/termination.js +1 -1
  90. package/src/typechecker/types.js +149 -25
  91. package/stdlib/bytes/iter.go +192 -0
  92. package/stdlib/bytes/search.go +240 -0
  93. package/stdlib/bytes/split.go +218 -0
  94. package/stdlib/cmp/cmp.go +65 -0
  95. package/stdlib/encoding/json/errors.go +31 -0
  96. package/stdlib/encoding/json/indent.go +71 -0
  97. package/stdlib/encoding/json/number.go +12 -0
  98. package/stdlib/encoding/json/stream.go +173 -0
  99. package/stdlib/errors/overlay.go +12 -0
  100. package/stdlib/hash/hash.go +53 -0
  101. package/stdlib/internal/jsonrt/dec.go +738 -0
  102. package/stdlib/internal/jsonrt/enc.go +366 -0
  103. package/stdlib/internal/jsonrt/indent.go +135 -0
  104. package/stdlib/internal/jsonrt/number.go +27 -0
  105. package/stdlib/internal/jsonrt/reader.go +73 -0
  106. package/stdlib/internal/jsonrt/scanner.go +649 -0
  107. package/stdlib/io/io.go +346 -0
  108. package/stdlib/iter/iter.go +18 -0
  109. package/stdlib/maps/iter.go +65 -0
  110. package/stdlib/math/extra.go +270 -0
  111. package/stdlib/slices/extra.go +73 -0
  112. package/stdlib/slices/iter.go +113 -0
  113. package/stdlib/sort/sort.go +561 -0
  114. package/stdlib/strconv/quote.go +341 -0
  115. package/stdlib/strings/extra.go +35 -0
  116. package/stdlib/strings/iter.go +122 -0
  117. package/stdlib/sync/extra.go +105 -0
  118. package/stdlib/unicode/unicode.go +112 -0
  119. package/stdlib/unicode/utf8/bytes.go +239 -0
  120. package/src/backend/wasm/const-fold.js +0 -205
  121. package/src/backend/wasm/emit-stdlib.js +0 -13
  122. /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
  123. /package/src/{colors.js → cli/colors.js} +0 -0
  124. /package/src/{dev-server.js → cli/dev-server.js} +0 -0
  125. /package/src/{minifier.js → cli/minifier.js} +0 -0
  126. /package/src/{project-config.js → cli/project-config.js} +0 -0
  127. /package/src/{pwa.js → cli/pwa.js} +0 -0
  128. /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
@@ -0,0 +1,381 @@
1
+ // src/backend/wasm/method-expr.js
2
+ // Method expressions (`T.M`, `(*T).M`) and method values (`x.M`) in WASM.
3
+ // A call through a method expression, `(*T).M(p, a)`, becomes the ordinary
4
+ // method call `p.M(a)`. Any other use becomes a func literal that forwards
5
+ // to the method, `func(recv *T, a0 A) R { return recv.M(a0) }`, which the
6
+ // closure lifting then emits like any other func value. A method value
7
+ // binds its receiver when it is evaluated (copying a value receiver, taking
8
+ // the address of an addressable one for a pointer receiver):
9
+ // `func(recv$ *T) func(A) R { return func(a0 A) R { return recv$.M(a0) } }(&x)`. Runs after monomorphisation, so the
10
+ // receiver and method types are concrete, on the WASM pipeline's own copy of
11
+ // the programs.
12
+
13
+ import { typeToTypeNode as toNode } from "./monomorph.js";
14
+
15
+ // Func types stay semantic types: `typeToTypeNode` has no func form and
16
+ // would turn them into `any`.
17
+ function typeToTypeNode(t) {
18
+ return t?.kind === "func" ? t : toNode(t);
19
+ }
20
+
21
+ function isVoid(t) {
22
+ return !t || (t.kind === "basic" && t.name === "void");
23
+ }
24
+
25
+ function returnTypeNode(t) {
26
+ if (isVoid(t)) return null;
27
+ if (t.kind === "tuple")
28
+ return { kind: "TupleType", types: t.types.map(typeToTypeNode) };
29
+ return typeToTypeNode(t);
30
+ }
31
+
32
+ function ident(name, type, at) {
33
+ return { kind: "Ident", name, _type: type, _line: at._line, _col: at._col };
34
+ }
35
+
36
+ // `recv.M(args)` for the method expression `sel` (type `func(recv, …)`).
37
+ function methodCall(sel, recvExpr, args, fnType) {
38
+ const result = fnType.returns?.[0];
39
+ return {
40
+ kind: "CallExpr",
41
+ func: {
42
+ kind: "SelectorExpr",
43
+ expr: recvExpr,
44
+ field: sel.field,
45
+ _type: {
46
+ kind: "func",
47
+ params: fnType.params.slice(1),
48
+ returns: fnType.returns,
49
+ variadic: fnType.variadic,
50
+ },
51
+ _line: sel._line,
52
+ _col: sel._col,
53
+ },
54
+ args,
55
+ _type: isVoid(result) ? { kind: "basic", name: "void" } : result,
56
+ _line: sel._line,
57
+ _col: sel._col,
58
+ };
59
+ }
60
+
61
+ // Each method value's outer literal gets its own receiver name: captures are
62
+ // resolved by name, so two method values of different receiver types in one
63
+ // function must not share `recv$`.
64
+ let recvCounter = 0;
65
+ let bundled = null;
66
+
67
+ function forwardingFuncLit(sel, recvName = "recv$") {
68
+ const fnType = sel._type;
69
+ const [recvType, ...paramTypes] = fnType.params;
70
+ const params = [
71
+ { name: recvName, type: typeToTypeNode(recvType) },
72
+ ...paramTypes.map((t, i) => ({
73
+ name: `a$${i}`,
74
+ type: typeToTypeNode(t),
75
+ variadic: fnType.variadic && i === paramTypes.length - 1,
76
+ })),
77
+ ];
78
+ const args = paramTypes.map((t, i) => {
79
+ const a = ident(`a$${i}`, t, sel);
80
+ if (fnType.variadic && i === paramTypes.length - 1) a._spread = true;
81
+ return a;
82
+ });
83
+ const call = methodCall(sel, ident(recvName, recvType, sel), args, fnType);
84
+ const result = fnType.returns?.[0];
85
+ const stmts = isVoid(result)
86
+ ? [{ kind: "ExprStmt", expr: call }]
87
+ : [{ kind: "ReturnStmt", values: [call] }];
88
+ return {
89
+ kind: "FuncLit",
90
+ params,
91
+ returnType: returnTypeNode(result),
92
+ body: { kind: "Block", stmts },
93
+ _type: fnType,
94
+ _line: sel._line,
95
+ _col: sel._col,
96
+ };
97
+ }
98
+
99
+ function methodValueLit(sel) {
100
+ const fnType = sel._type;
101
+ let recvType = sel.expr._type;
102
+ let recvArg = sel.expr;
103
+ if (fnType._ptrRecv && recvType?.kind !== "pointer") {
104
+ recvType = { kind: "pointer", base: recvType };
105
+ recvArg = {
106
+ kind: "UnaryExpr",
107
+ op: "&",
108
+ operand: sel.expr,
109
+ _type: recvType,
110
+ _line: sel._line,
111
+ _col: sel._col,
112
+ };
113
+ } else if (sel._derefRecv && recvType?.kind === "pointer") {
114
+ // `p.M` with a value receiver binds a copy of `*p` now.
115
+ recvType = recvType.base;
116
+ recvArg = {
117
+ kind: "UnaryExpr",
118
+ op: "*",
119
+ operand: sel.expr,
120
+ _type: recvType,
121
+ _line: sel._line,
122
+ _col: sel._col,
123
+ };
124
+ }
125
+ const params = fnType.params ?? [];
126
+ const recvName = `recv$${recvCounter++}`;
127
+ const inner = forwardingFuncLit(
128
+ {
129
+ ...sel,
130
+ _type: { ...fnType, params: [recvType, ...params] },
131
+ },
132
+ recvName,
133
+ );
134
+ // The receiver is the outer parameter; the inner literal takes the rest.
135
+ inner.params = inner.params.slice(1);
136
+ inner._type = fnType;
137
+ const outerType = { kind: "func", params: [recvType], returns: [fnType] };
138
+ return {
139
+ kind: "CallExpr",
140
+ func: {
141
+ kind: "FuncLit",
142
+ params: [{ name: recvName, type: typeToTypeNode(recvType) }],
143
+ returnType: typeToTypeNode(fnType),
144
+ body: {
145
+ kind: "Block",
146
+ stmts: [{ kind: "ReturnStmt", values: [inner] }],
147
+ },
148
+ _type: outerType,
149
+ _line: sel._line,
150
+ _col: sel._col,
151
+ },
152
+ args: [recvArg],
153
+ _type: fnType,
154
+ _line: sel._line,
155
+ _col: sel._col,
156
+ };
157
+ }
158
+
159
+ // True for a stdlib function used as a value: `pkg.Fn` on an imported
160
+ // package that is not bundled GoFront source (those resolve as plain
161
+ // functions), with a concrete (non-generic) func type.
162
+ function isStdlibFuncValue(node) {
163
+ if (node?.kind !== "SelectorExpr" || node._type?.kind !== "func")
164
+ return false;
165
+ const ns = node.expr;
166
+ if (ns?.kind !== "Ident" || ns._type?.kind !== "namespace") return false;
167
+ return !bundled?.has(ns.name) && !hasTypeParam(node._type);
168
+ }
169
+
170
+ function hasTypeParam(t) {
171
+ const text = JSON.stringify([t.params, t.returns], (k, v) =>
172
+ k.startsWith("_") || k === "underlying" ? undefined : v,
173
+ );
174
+ return text.includes('"typeParam"') || text.includes('"typeparam"');
175
+ }
176
+
177
+ // Largest number of variadic operands a forwarded stdlib function value
178
+ // accepts: stdlib emitters take their operands unpacked, so the forwarder
179
+ // dispatches on the operand count.
180
+ const MAX_FORWARDED_VARIADIC = 8;
181
+
182
+ const INT_TYPE = { kind: "basic", name: "int" };
183
+
184
+ function intLit(n, at) {
185
+ return {
186
+ kind: "BasicLit",
187
+ litKind: "INT",
188
+ value: String(n),
189
+ _type: INT_TYPE,
190
+ _line: at._line,
191
+ _col: at._col,
192
+ };
193
+ }
194
+
195
+ function lineInfo(at) {
196
+ return { _line: at._line, _col: at._col };
197
+ }
198
+
199
+ function typedCall(func, args, type, at) {
200
+ return { kind: "CallExpr", func, args, _type: type, ...lineInfo(at) };
201
+ }
202
+
203
+ // The statements calling `sel(args)` and returning its result.
204
+ function callStmts(sel, args) {
205
+ const result = sel._type.returns?.[0];
206
+ const type = isVoid(result) ? { kind: "basic", name: "void" } : result;
207
+ const call = typedCall(sel, args, type, sel);
208
+ return isVoid(result)
209
+ ? [
210
+ { kind: "ExprStmt", expr: call },
211
+ { kind: "ReturnStmt", values: [] },
212
+ ]
213
+ : [{ kind: "ReturnStmt", values: [call] }];
214
+ }
215
+
216
+ // `if len(rest) == k { return pkg.Fn(fixed..., rest[0..k-1]) }` for one k.
217
+ function operandCountBranch(sel, fixed, rest, elemType, k) {
218
+ const at = sel;
219
+ const elems = Array.from({ length: k }, (_, i) => ({
220
+ kind: "IndexExpr",
221
+ expr: rest,
222
+ index: intLit(i, at),
223
+ _type: elemType,
224
+ ...lineInfo(at),
225
+ }));
226
+ const len = typedCall(ident("len", null, at), [rest], INT_TYPE, at);
227
+ const body = callStmts(sel, [...fixed, ...elems]);
228
+ return {
229
+ kind: "IfStmt",
230
+ init: null,
231
+ cond: {
232
+ kind: "BinaryExpr",
233
+ left: len,
234
+ op: "==",
235
+ right: intLit(k, at),
236
+ _type: { kind: "basic", name: "bool" },
237
+ ...lineInfo(at),
238
+ },
239
+ body: { kind: "Block", stmts: body, list: body },
240
+ elseBody: null,
241
+ };
242
+ }
243
+
244
+ function tooManyOperands(sel) {
245
+ const msg = {
246
+ kind: "BasicLit",
247
+ litKind: "STRING",
248
+ value: `too many arguments in call to ${sel.expr.name}.${sel.field}`,
249
+ _type: { kind: "basic", name: "string" },
250
+ };
251
+ const panic = typedCall(
252
+ ident("panic", null, sel),
253
+ [msg],
254
+ { kind: "basic", name: "void" },
255
+ sel,
256
+ );
257
+ return [{ kind: "ExprStmt", expr: panic }];
258
+ }
259
+
260
+ function variadicDispatch(sel, fixed, elemType) {
261
+ const rest = ident(
262
+ `a$${fixed.length}`,
263
+ { kind: "slice", elem: elemType },
264
+ sel,
265
+ );
266
+ const stmts = [];
267
+ for (let k = 0; k <= MAX_FORWARDED_VARIADIC; k++)
268
+ stmts.push(operandCountBranch(sel, fixed, rest, elemType, k));
269
+ return [...stmts, ...tooManyOperands(sel)];
270
+ }
271
+
272
+ // `func(a0 A, a1 B) R { return pkg.Fn(a0, a1) }` for the stdlib function
273
+ // value `pkg.Fn`. A variadic one dispatches on the operand count:
274
+ // `if len(rest) == 0 { return pkg.Fn(a0) }; if len(rest) == 1 { ... }`.
275
+ function stdlibFuncLit(sel) {
276
+ const fnType = sel._type;
277
+ const ps = fnType.params ?? [];
278
+ const nFixed = fnType.variadic ? ps.length - 1 : ps.length;
279
+ const params = ps.map((t, i) => ({
280
+ name: `a$${i}`,
281
+ type: typeToTypeNode(t),
282
+ variadic: i === nFixed,
283
+ }));
284
+ const fixed = ps.slice(0, nFixed).map((t, i) => ident(`a$${i}`, t, sel));
285
+ const stmts = fnType.variadic
286
+ ? variadicDispatch(sel, fixed, ps[nFixed])
287
+ : callStmts(sel, fixed);
288
+ return {
289
+ kind: "FuncLit",
290
+ params,
291
+ returnType: returnTypeNode(fnType.returns?.[0]),
292
+ body: { kind: "Block", stmts, list: stmts },
293
+ _type: fnType,
294
+ ...lineInfo(sel),
295
+ };
296
+ }
297
+
298
+ function rewrite(node, isCallee) {
299
+ return rewriteMethods(node, isCallee) ?? rewriteStdlibValue(node, isCallee);
300
+ }
301
+
302
+ // The forwarder passes plain operands and returns one value, so a stdlib
303
+ // function that takes a callback or returns several values cannot be one.
304
+ function assertForwardable(sel) {
305
+ const t = sel._type;
306
+ // Stdlib signatures type a callback as `any`, and the forwarder cannot
307
+ // pass an `any` operand to an emitter that expects a slice or function.
308
+ const fixed = t.variadic ? (t.params ?? []).slice(0, -1) : (t.params ?? []);
309
+ const hasCallback = fixed.some(
310
+ (p) => p?.kind === "func" || (p?.kind === "basic" && p.name === "any"),
311
+ );
312
+ if (hasCallback || (t.returns?.length ?? 0) > 1)
313
+ throw new Error(
314
+ `${sel.expr.name}.${sel.field} cannot be used as a value in WASM (it takes a callback or returns several values)`,
315
+ );
316
+ }
317
+
318
+ function rewriteStdlibValue(node, isCallee) {
319
+ if (isCallee || !isStdlibFuncValue(node)) return null;
320
+ assertForwardable(node);
321
+ return stdlibFuncLit(node);
322
+ }
323
+
324
+ function rewriteMethods(node, isCallee) {
325
+ if (node?.kind === "SelectorExpr" && node._isMethodExpr && node._type)
326
+ return forwardingFuncLit(node);
327
+ if (
328
+ node?.kind === "SelectorExpr" &&
329
+ node._isMethodValue &&
330
+ node._type?.kind === "func" &&
331
+ !isCallee
332
+ )
333
+ return methodValueLit(node);
334
+ if (
335
+ node?.kind === "CallExpr" &&
336
+ node.func?.kind === "SelectorExpr" &&
337
+ node.func._isMethodExpr &&
338
+ node.func._type &&
339
+ node.args?.length > 0 &&
340
+ !node._multiForward
341
+ ) {
342
+ const [recv, ...rest] = node.args;
343
+ const call = methodCall(node.func, recv, rest, node.func._type);
344
+ call._type = node._type;
345
+ return call;
346
+ }
347
+ return null;
348
+ }
349
+
350
+ /** Rewrites method expressions in place; true if anything changed. */
351
+ export function desugarMethodExprs(node, bundledPackages = null) {
352
+ recvCounter = 0;
353
+ bundled = bundledPackages;
354
+ return desugar(node);
355
+ }
356
+
357
+ function desugar(node) {
358
+ if (!node || typeof node !== "object") return false;
359
+ let changed = false;
360
+ if (Array.isArray(node)) {
361
+ for (let i = 0; i < node.length; i++) {
362
+ const r = rewrite(node[i]);
363
+ if (r) {
364
+ node[i] = r;
365
+ changed = true;
366
+ }
367
+ changed = desugar(node[i]) || changed;
368
+ }
369
+ return changed;
370
+ }
371
+ for (const key of Object.keys(node)) {
372
+ if (key.charCodeAt(0) === 95) continue; // "_" side tables
373
+ const r = rewrite(node[key], node.kind === "CallExpr" && key === "func");
374
+ if (r) {
375
+ node[key] = r;
376
+ changed = true;
377
+ }
378
+ changed = desugar(node[key]) || changed;
379
+ }
380
+ return changed;
381
+ }