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
@@ -79,10 +79,40 @@ export const statementCheckMethods = {
79
79
  const name = stmt.lhs[i].name ?? stmt.lhs[i];
80
80
  if (name === "_") continue;
81
81
  if (scope.symbols.has(name)) stmt.lhs[i]._redecl = true;
82
+ if (stmt.rhs.length === stmt.lhs.length)
83
+ this._checkConstDefault(
84
+ rhsFlat[i],
85
+ stmt.rhs[i],
86
+ "variable declaration",
87
+ );
82
88
  scope.defineLocal(name, defaultType(rhsFlat[i]) ?? ANY);
83
89
  }
84
90
  },
85
91
 
92
+ _checkDefineCommaOkMap(stmt, scope) {
93
+ if (
94
+ stmt.lhs.length !== 2 ||
95
+ stmt.rhs.length !== 1 ||
96
+ stmt.rhs[0].kind !== "IndexExpr"
97
+ )
98
+ return false;
99
+ const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
100
+ const resolvedBase =
101
+ mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
102
+ if (resolvedBase?.kind !== "map") return false;
103
+ const keyType = this.checkExpr(stmt.rhs[0].index, scope);
104
+ this._markIfaceKey(resolvedBase, stmt.rhs[0].index, keyType);
105
+ stmt._commaOkMap = true;
106
+ stmt.rhs[0]._mapValueType = resolvedBase.value;
107
+ this._declareDefineVars(stmt, [resolvedBase.value, BOOL], scope);
108
+ const allRedecl = stmt.lhs.every((e) => e._redecl || (e.name ?? e) === "_");
109
+ if (allRedecl && stmt.lhs.length > 0) {
110
+ this.err("no new variables on left side of :=", stmt.lhs[0]);
111
+ }
112
+ stmt._rhsTypes = [resolvedBase.value, BOOL];
113
+ return true;
114
+ },
115
+
86
116
  _checkDefineStmt(stmt, scope) {
87
117
  // comma-ok type assertion: n, ok := x.(T)
88
118
  if (
@@ -92,10 +122,24 @@ export const statementCheckMethods = {
92
122
  ) {
93
123
  stmt.rhs[0]._commaOk = true;
94
124
  }
125
+ if (this._checkDefineCommaOkMap(stmt, scope)) return;
95
126
  const rhs = stmt.rhs.map((e) => this.checkExpr(e, scope));
96
- const rhsFlat =
97
- rhs.length === 1 && rhs[0].kind === "tuple" ? rhs[0].types : rhs;
127
+ const isTuple = rhs.length === 1 && rhs[0]?.kind === "tuple";
128
+ const rhsFlat = isTuple ? rhs[0].types : rhs;
98
129
  if (this._checkDefineCommaOkAssert(stmt, rhs, scope)) return;
130
+ if (
131
+ stmt.rhs[0]?.kind !== "RangeExpr" &&
132
+ stmt.lhs.length !== rhsFlat.length
133
+ ) {
134
+ const vars =
135
+ stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
136
+ const vals = isTuple
137
+ ? `returns ${rhsFlat.length} values`
138
+ : rhsFlat.length === 1
139
+ ? "1 value"
140
+ : `${rhsFlat.length} values`;
141
+ this.err(`assignment mismatch: ${vars} but ${vals}`, stmt);
142
+ }
99
143
  this._declareDefineVars(stmt, rhsFlat, scope);
100
144
  const allRedecl = stmt.lhs.every((e) => e._redecl || (e.name ?? e) === "_");
101
145
  if (allRedecl && stmt.lhs.length > 0) {
@@ -110,15 +154,16 @@ export const statementCheckMethods = {
110
154
  stmt.rhs.length !== 1 ||
111
155
  stmt.rhs[0].kind !== "IndexExpr"
112
156
  )
113
- return false;
157
+ return null;
114
158
  const mapBaseType = this.checkExpr(stmt.rhs[0].expr, scope);
115
159
  const resolvedBase =
116
160
  mapBaseType?.kind === "named" ? mapBaseType.underlying : mapBaseType;
117
- if (resolvedBase?.kind !== "map") return false;
118
- this.checkExpr(stmt.rhs[0].index, scope);
161
+ if (resolvedBase?.kind !== "map") return null;
162
+ const keyType = this.checkExpr(stmt.rhs[0].index, scope);
163
+ this._markIfaceKey(resolvedBase, stmt.rhs[0].index, keyType);
119
164
  stmt._commaOkMap = true;
120
165
  stmt.rhs[0]._mapValueType = resolvedBase.value;
121
- return true;
166
+ return [resolvedBase.value, BOOL];
122
167
  },
123
168
 
124
169
  _checkConstAssignment(stmt, scope) {
@@ -154,6 +199,16 @@ export const statementCheckMethods = {
154
199
  this.resolveType(r),
155
200
  stmt.rhs[i],
156
201
  );
202
+ // A shift count is not converted to the left operand's type.
203
+ if (stmt.op === "<<=" || stmt.op === ">>=") {
204
+ if (r && !isAny(r)) this.checkShiftCount(r, stmt.rhs[i]);
205
+ continue;
206
+ }
207
+ if (
208
+ stmt.rhs.length === lhsTypes.length &&
209
+ this._checkConstOverflow(lhsTypes[i], r, stmt.rhs[i], "assignment")
210
+ )
211
+ continue;
157
212
  if (!isAny(lhsTypes[i]) && !isAny(r)) {
158
213
  this._skipIfaceMark = true;
159
214
  this.assertAssignable(lhsTypes[i], r, stmt.lhs[i]);
@@ -163,12 +218,34 @@ export const statementCheckMethods = {
163
218
  },
164
219
 
165
220
  _checkAssignStmt(stmt, scope) {
166
- if (this._checkAssignCommaOkMap(stmt, scope)) return;
221
+ const commaOkMapTypes = this._checkAssignCommaOkMap(stmt, scope);
167
222
  if (this._isCommaOkTypeAssertAssign(stmt)) stmt.rhs[0]._commaOk = true;
168
223
  this._checkConstAssignment(stmt, scope);
169
- const rhs = stmt.rhs.map((e) => this.checkExpr(e, scope));
170
- const rhsFlat =
171
- rhs.length === 1 && rhs[0]?.kind === "tuple" ? rhs[0].types : rhs;
224
+ let rhsFlat;
225
+ if (commaOkMapTypes) {
226
+ rhsFlat = commaOkMapTypes;
227
+ } else {
228
+ const rhs = stmt.rhs.map((e) => this.checkExpr(e, scope));
229
+ const isTuple = rhs.length === 1 && rhs[0]?.kind === "tuple";
230
+ rhsFlat = isTuple ? rhs[0].types : rhs;
231
+ if (
232
+ !stmt.rhs[0]?._commaOk &&
233
+ stmt.rhs[0]?.kind !== "RangeExpr" &&
234
+ stmt.lhs.length !== rhsFlat.length
235
+ ) {
236
+ const vars =
237
+ stmt.lhs.length === 1 ? "1 variable" : `${stmt.lhs.length} variables`;
238
+ const vals = isTuple
239
+ ? `returns ${rhsFlat.length} values`
240
+ : rhsFlat.length === 1
241
+ ? "1 value"
242
+ : `${rhsFlat.length} values`;
243
+ this.err(`assignment mismatch: ${vars} but ${vals}`, stmt);
244
+ }
245
+ if (stmt.rhs[0]?._commaOk) {
246
+ rhsFlat = [rhs[0], BOOL];
247
+ }
248
+ }
172
249
  const lhsTypes = stmt.lhs.map((e) => this._checkAssignLhsType(e, scope));
173
250
  this._checkAssignTypes(stmt, rhsFlat, lhsTypes);
174
251
  },
@@ -187,9 +264,34 @@ export const statementCheckMethods = {
187
264
  if (!isVoid(returnType) && types.length > 0) {
188
265
  const expected =
189
266
  returnType.kind === "tuple" ? returnType.types : [returnType];
267
+ // `return f()` forwards every result of a multi-value call.
268
+ if (types.length === 1 && types[0]?.kind === "tuple") {
269
+ const forwarded = types[0].types;
270
+ if (forwarded.length !== expected.length) {
271
+ this.err(
272
+ `return count mismatch: have ${forwarded.length} values, want ${expected.length}`,
273
+ stmt,
274
+ );
275
+ return;
276
+ }
277
+ for (let i = 0; i < expected.length; i++) {
278
+ this.assertAssignable(
279
+ expected[i],
280
+ forwarded[i],
281
+ stmt.values[0],
282
+ "return statement",
283
+ );
284
+ }
285
+ return;
286
+ }
190
287
  for (let i = 0; i < expected.length; i++) {
191
288
  if (types[i])
192
- this.assertAssignable(expected[i], types[i], stmt.values[i] ?? stmt);
289
+ this.assertAssignable(
290
+ expected[i],
291
+ types[i],
292
+ stmt.values[i] ?? stmt,
293
+ "return statement",
294
+ );
193
295
  }
194
296
  }
195
297
  },
@@ -229,7 +331,14 @@ export const statementCheckMethods = {
229
331
  }
230
332
  },
231
333
 
232
- _checkForStmt(stmt, scope, returnType) {
334
+ // `checkBody(bodyScope)` checks the loop body; templ `for` loops pass one
335
+ // that checks their HTML nodes in place of the header's empty block.
336
+ _checkForStmt(
337
+ stmt,
338
+ scope,
339
+ returnType,
340
+ checkBody = (s) => this.checkBlock(stmt.body, s, returnType),
341
+ ) {
233
342
  const inner = new Scope(scope);
234
343
  let iterInfo = null;
235
344
  if (stmt.init?.rhs?.[0]?.kind === "RangeExpr") {
@@ -240,7 +349,7 @@ export const statementCheckMethods = {
240
349
  if (stmt.post) this.checkStmt(stmt.post, inner, returnType);
241
350
  this._loopDepth++;
242
351
  const bodyScope = new Scope(inner);
243
- this.checkBlock(stmt.body, bodyScope, returnType);
352
+ checkBody(bodyScope);
244
353
  this._reportUnused(bodyScope, stmt);
245
354
  this._loopDepth--;
246
355
  this._reportUnused(inner, stmt);
@@ -261,6 +370,19 @@ export const statementCheckMethods = {
261
370
  this._reportUnused(inner, stmt);
262
371
  },
263
372
 
373
+ // A case naming an interface (literal or declared) records the resolved
374
+ // type so backends test its method set.
375
+ _markInterfaceCaseType(t, scope) {
376
+ if (!t || (t.kind !== "InterfaceType" && t.kind !== "TypeName")) return;
377
+ if (t.kind === "TypeName" && (t.name === "nil" || t.name === "error"))
378
+ return;
379
+ const resolved = this.resolveTypeNode(t, scope);
380
+ const u = this.resolveType(
381
+ resolved?.kind === "named" ? resolved.underlying : resolved,
382
+ );
383
+ if (u?.kind === "interface") t._type = resolved;
384
+ },
385
+
264
386
  _checkTypeSwitchStmt(stmt, scope, returnType) {
265
387
  const inner = new Scope(scope);
266
388
  this.checkExpr(stmt.expr, inner);
@@ -268,10 +390,23 @@ export const statementCheckMethods = {
268
390
  this._typeSwitchDepth++;
269
391
  for (const c of stmt.cases) {
270
392
  const caseScope = new Scope(inner);
393
+ // Resolve instantiated generic case types so backends can tell
394
+ // `*Stack[int]` from `*Stack[string]`.
395
+ for (const t of c.types ?? []) {
396
+ const base = t?.kind === "PointerType" ? t.base : t;
397
+ if (base?.kind === "GenericTypeName") this.resolveTypeNode(t, inner);
398
+ this._markInterfaceCaseType(t, inner);
399
+ }
271
400
  if (stmt.assign) {
272
- // Bind the variable to the single case type, or any for multi/default
273
- const bindType =
274
- c.types?.length === 1 ? this.resolveTypeNode(c.types[0], inner) : ANY;
401
+ // Bind the variable to the single case type, or any for multi/default.
402
+ // `case nil` binds the switch expression's own type, as in Go.
403
+ const only = c.types?.length === 1 ? c.types[0] : null;
404
+ const isNilCase = only?.kind === "TypeName" && only.name === "nil";
405
+ const bindType = isNilCase
406
+ ? (stmt.expr._type ?? ANY)
407
+ : only
408
+ ? this.resolveTypeNode(only, inner)
409
+ : ANY;
275
410
  caseScope.defineLocal(stmt.assign, bindType);
276
411
  // The capture variable is always considered used — it's the whole
277
412
  // point of the switch-with-assign form. Each case body uses its own
@@ -287,7 +422,12 @@ export const statementCheckMethods = {
287
422
 
288
423
  _checkBranchStmt(stmt) {
289
424
  const kw = stmt.keyword;
290
- if (kw === "continue" && this._loopDepth === 0)
425
+ if (kw === "goto")
426
+ this.err(
427
+ "goto is not supported; restructure with labeled break/continue",
428
+ stmt,
429
+ );
430
+ else if (kw === "continue" && this._loopDepth === 0)
291
431
  this.err("continue statement outside for loop", stmt);
292
432
  else if (kw === "break" && this._loopDepth === 0 && this._switchDepth === 0)
293
433
  this.err("break statement outside for loop or switch", stmt);