gofront 1.8.0 → 1.9.1

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 (178) hide show
  1. package/README.md +3 -3
  2. package/package.json +6 -5
  3. package/src/backend/js/expressions.js +1157 -408
  4. package/src/backend/js/index.js +327 -153
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +559 -2552
  7. package/src/backend/js/statements.js +670 -158
  8. package/src/backend/js/stdlib/index.js +45 -93
  9. package/src/backend/js/templ.js +32 -45
  10. package/src/backend/js/type-info.js +298 -19
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +91 -170
  13. package/src/backend/shared/host-unicode.js +36 -0
  14. package/src/backend/wasm/boundary.js +490 -965
  15. package/src/backend/wasm/emit-builtins.js +884 -256
  16. package/src/backend/wasm/emit-exprs.js +185 -77
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-shared.js +4 -1
  19. package/src/backend/wasm/emit-stmts.js +524 -240
  20. package/src/backend/wasm/emit.js +176 -38
  21. package/src/backend/wasm/globals.js +48 -8
  22. package/src/backend/wasm/glue.js +2 -4
  23. package/src/backend/wasm/index.js +44 -530
  24. package/src/backend/wasm/method-expr.js +12 -1
  25. package/src/backend/wasm/module-emitter.js +299 -770
  26. package/src/backend/wasm/monomorph.js +139 -8
  27. package/src/backend/wasm/natives.js +600 -0
  28. package/src/backend/wasm/prescan.js +5 -153
  29. package/src/backend/wasm/prune-funcs.js +89 -0
  30. package/src/backend/wasm/stdlib/index.js +2 -32
  31. package/src/backend/wasm/types.js +97 -20
  32. package/src/backend/wasm/variadic.js +9 -4
  33. package/src/cli/cli-core.js +5 -1
  34. package/src/cli/test-runner.js +115 -342
  35. package/src/compiler.js +113 -130
  36. package/src/lexer/lexer.js +106 -11
  37. package/src/lexer/templ-lexer.js +61 -18
  38. package/src/lexer/tokens.js +1 -0
  39. package/src/link/stdlib.js +355 -132
  40. package/src/link/type-names.js +204 -0
  41. package/src/link/wasm.js +30 -6
  42. package/src/lower/bounds.js +153 -0
  43. package/src/lower/captures.js +92 -33
  44. package/src/lower/embedding.js +62 -5
  45. package/src/lower/errors-as.js +196 -0
  46. package/src/lower/fmt-formatters.js +929 -0
  47. package/src/lower/functions.js +225 -7
  48. package/src/lower/gen-types.js +293 -0
  49. package/src/lower/index.js +14 -2
  50. package/src/lower/json-codecs.js +334 -245
  51. package/src/lower/ownership.js +25 -7
  52. package/src/lower/reachability.js +69 -51
  53. package/src/lower/walk.js +12 -0
  54. package/src/parser/declarations.js +30 -5
  55. package/src/parser/expressions.js +111 -57
  56. package/src/parser/statements.js +108 -0
  57. package/src/parser/templ.js +44 -5
  58. package/src/parser/types.js +12 -0
  59. package/src/resolver.js +1 -2
  60. package/src/typechecker/assignability.js +214 -19
  61. package/src/typechecker/const-fold.js +70 -27
  62. package/src/typechecker/constants.js +156 -8
  63. package/src/typechecker/expressions.js +490 -108
  64. package/src/typechecker/index.js +346 -42
  65. package/src/typechecker/init-order.js +136 -40
  66. package/src/typechecker/operand-text.js +41 -0
  67. package/src/typechecker/resolve.js +490 -65
  68. package/src/typechecker/statements.js +288 -27
  69. package/src/typechecker/stdlib/excluded.js +9 -5
  70. package/src/typechecker/stdlib/index.js +25 -9
  71. package/src/typechecker/termination.js +10 -1
  72. package/src/typechecker/types.js +287 -575
  73. package/stdlib/bytes/buffer.go +479 -0
  74. package/stdlib/bytes/bytes.go +1010 -0
  75. package/stdlib/bytes/reader.go +161 -0
  76. package/stdlib/context/context.go +115 -0
  77. package/stdlib/crypto/rand/rand.go +34 -0
  78. package/stdlib/crypto/sha256/sha256.go +249 -0
  79. package/stdlib/encoding/base64/base64.go +331 -0
  80. package/stdlib/encoding/binary/binary.go +332 -0
  81. package/stdlib/encoding/hex/hex.go +140 -0
  82. package/stdlib/encoding/json/json.go +62 -0
  83. package/stdlib/errors/errors.go +174 -0
  84. package/stdlib/fmt/errors.go +110 -0
  85. package/stdlib/fmt/format.go +732 -0
  86. package/stdlib/fmt/print.go +1232 -0
  87. package/stdlib/gom/gom.go +293 -0
  88. package/stdlib/html/html.go +251 -0
  89. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  90. package/stdlib/internal/jsonrt/dec.go +171 -38
  91. package/stdlib/internal/jsonrt/enc.go +186 -36
  92. package/stdlib/internal/jsonrt/scanner.go +17 -8
  93. package/stdlib/io/io.go +157 -1
  94. package/stdlib/maps/maps.go +131 -0
  95. package/stdlib/math/bits/bits.go +507 -0
  96. package/stdlib/math/extra.go +16 -14
  97. package/stdlib/math/math.go +439 -0
  98. package/stdlib/math/rand/rand.go +297 -0
  99. package/stdlib/os/os.go +93 -0
  100. package/stdlib/path/filepath/filepath.go +47 -0
  101. package/stdlib/path/path.go +394 -0
  102. package/stdlib/regexp/regexp.go +231 -0
  103. package/stdlib/slices/slices.go +526 -0
  104. package/stdlib/slices/sort.go +209 -0
  105. package/stdlib/slices/zsort_cmp.go +480 -0
  106. package/stdlib/slices/zsort_ordered.go +482 -0
  107. package/stdlib/sort/sort.go +391 -452
  108. package/stdlib/sort/zsort_func.go +480 -0
  109. package/stdlib/sort/zsort_iface.go +480 -0
  110. package/stdlib/strconv/float.go +1699 -0
  111. package/stdlib/strconv/isprint.go +752 -0
  112. package/stdlib/strconv/quote.go +235 -50
  113. package/stdlib/strconv/strconv.go +616 -0
  114. package/stdlib/strings/builder.go +95 -0
  115. package/stdlib/strings/reader.go +163 -0
  116. package/stdlib/strings/replacer.go +102 -0
  117. package/stdlib/strings/strings.go +807 -0
  118. package/stdlib/sync/sync.go +268 -0
  119. package/stdlib/testing/async_js.go +61 -0
  120. package/stdlib/testing/benchmark.go +297 -0
  121. package/stdlib/testing/testing.go +523 -0
  122. package/stdlib/time/duration.go +233 -0
  123. package/stdlib/time/format.go +745 -0
  124. package/stdlib/time/parse.go +790 -0
  125. package/stdlib/time/sleep_js.go +63 -0
  126. package/stdlib/time/time.go +834 -0
  127. package/stdlib/unicode/unicode.go +141 -3
  128. package/stdlib/unicode/utf8/utf8.go +464 -0
  129. package/src/backend/js/fmt-runtime.js +0 -573
  130. package/src/backend/js/stdlib/base64.js +0 -12
  131. package/src/backend/js/stdlib/binary.js +0 -102
  132. package/src/backend/js/stdlib/bits.js +0 -119
  133. package/src/backend/js/stdlib/builder.js +0 -98
  134. package/src/backend/js/stdlib/bytes.js +0 -368
  135. package/src/backend/js/stdlib/errors.js +0 -45
  136. package/src/backend/js/stdlib/fmt.js +0 -517
  137. package/src/backend/js/stdlib/gom.js +0 -155
  138. package/src/backend/js/stdlib/hex.js +0 -17
  139. package/src/backend/js/stdlib/html.js +0 -20
  140. package/src/backend/js/stdlib/io.js +0 -137
  141. package/src/backend/js/stdlib/json.js +0 -1167
  142. package/src/backend/js/stdlib/maps.js +0 -37
  143. package/src/backend/js/stdlib/math.js +0 -85
  144. package/src/backend/js/stdlib/os.js +0 -13
  145. package/src/backend/js/stdlib/path.js +0 -48
  146. package/src/backend/js/stdlib/rand.js +0 -32
  147. package/src/backend/js/stdlib/regexp.js +0 -54
  148. package/src/backend/js/stdlib/sha256.js +0 -12
  149. package/src/backend/js/stdlib/shared.js +0 -17
  150. package/src/backend/js/stdlib/simd.js +0 -34
  151. package/src/backend/js/stdlib/slices.js +0 -107
  152. package/src/backend/js/stdlib/sort.js +0 -30
  153. package/src/backend/js/stdlib/strconv.js +0 -79
  154. package/src/backend/js/stdlib/strings.js +0 -301
  155. package/src/backend/js/stdlib/sync.js +0 -13
  156. package/src/backend/js/stdlib/testing.js +0 -41
  157. package/src/backend/js/stdlib/time.js +0 -152
  158. package/src/backend/js/stdlib/unicode.js +0 -25
  159. package/src/backend/js/stdlib/utf8.js +0 -26
  160. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  161. package/src/backend/wasm/stdlib/core.js +0 -10278
  162. package/src/backend/wasm/stdlib/io.js +0 -3578
  163. package/src/backend/wasm/stdlib/packages.js +0 -2402
  164. package/src/backend/wasm/stdlib/pure.js +0 -720
  165. package/src/backend/wasm/stdlib/time.js +0 -2229
  166. package/src/typechecker/stdlib/core.js +0 -1079
  167. package/src/typechecker/stdlib/extended.js +0 -1210
  168. package/stdlib/bytes/iter.go +0 -192
  169. package/stdlib/bytes/search.go +0 -240
  170. package/stdlib/bytes/split.go +0 -218
  171. package/stdlib/errors/overlay.go +0 -12
  172. package/stdlib/maps/iter.go +0 -65
  173. package/stdlib/slices/extra.go +0 -73
  174. package/stdlib/slices/iter.go +0 -113
  175. package/stdlib/strings/extra.go +0 -35
  176. package/stdlib/strings/iter.go +0 -122
  177. package/stdlib/sync/extra.go +0 -105
  178. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -14,9 +14,29 @@ import {
14
14
  foldUnary,
15
15
  INT_RANGES,
16
16
  intConst,
17
- MATH_INT_CONSTS,
18
17
  } from "./const-fold.js";
19
- import { defaultType, isAny, typeStr, VOID } from "./types.js";
18
+ import {
19
+ compositeLitMaxIndex,
20
+ defaultType,
21
+ isAny,
22
+ isComplex,
23
+ isComplex64,
24
+ isUntyped,
25
+ typeStr,
26
+ VOID,
27
+ } from "./types.js";
28
+
29
+ // True when evaluating `node` runs a call or a channel receive (conversions
30
+ // excluded): such an operand makes `len` of an array a non-constant.
31
+ function hasCallOrReceive(node) {
32
+ if (!node || typeof node !== "object") return false;
33
+ if (Array.isArray(node)) return node.some(hasCallOrReceive);
34
+ if (node.kind === "CallExpr" && !node._isTypeConversion) return true;
35
+ if (node.kind === "UnaryExpr" && node.op === "<-") return true;
36
+ return Object.keys(node).some(
37
+ (key) => key.charCodeAt(0) !== 95 && hasCallOrReceive(node[key]),
38
+ );
39
+ }
20
40
 
21
41
  // Basic type names usable as conversion functions (`int32(x)`).
22
42
  const BASIC_CONVERSIONS = new Set([
@@ -39,6 +59,17 @@ const CONST_KINDS = new Set([
39
59
  "SelectorExpr",
40
60
  ]);
41
61
 
62
+ // Describes a constant that is not integer-valued (`float64 constant`), or
63
+ // null for an integer, an untyped float with an integer value, or a rune.
64
+ function nonIntegerConstKind(v) {
65
+ if (v.k === "INT") return null;
66
+ if (v.k === "FLOAT") {
67
+ if (v.t) return `${v.t} constant`;
68
+ return constIntValue(v) === null ? "untyped float constant" : null;
69
+ }
70
+ return `${v.k.toLowerCase()} constant`;
71
+ }
72
+
42
73
  const NEGATIVE_SHIFT_ERROR = "invalid shift count: negative shift count";
43
74
 
44
75
  /** @type {ThisType<import('./index.js').TypeChecker>} */
@@ -50,6 +81,23 @@ export const constantCheckMethods = {
50
81
  return raw && !raw.overflow && !raw.error ? raw : null;
51
82
  },
52
83
 
84
+ // What makes `expr` a constant that cannot be an array length, index or
85
+ // size (`untyped bool constant`, `untyped nil`), or null when it is an
86
+ // integer constant or not a constant at all.
87
+ _nonIntegerConst(expr, scope = null) {
88
+ let e = expr;
89
+ while (e?.kind === "ParenExpr") e = e.expr;
90
+ if (e?.kind === "ImagLit") return "untyped complex constant";
91
+ if (e?.kind === "BasicLit" && e.litKind === "NIL") return "untyped nil";
92
+ let v = null;
93
+ try {
94
+ v = this._constValue(expr, scope);
95
+ } catch {
96
+ // not a constant the evaluator understands
97
+ }
98
+ return v ? nonIntegerConstKind(v) : null;
99
+ },
100
+
53
101
  // Integer value of an integer (or untyped integral float) constant as a
54
102
  // Number, or null.
55
103
  _constIntValue(expr, scope = null) {
@@ -59,6 +107,23 @@ export const constantCheckMethods = {
59
107
  return i === null ? null : Number(i);
60
108
  },
61
109
 
110
+ // A named untyped complex constant has no folded value (`_const`): its
111
+ // initializer goes on the identifier as `_complexInit`, so the backends
112
+ // can fold it where it takes type complex64.
113
+ // A named `complex64` constant is tagged `_c64Const` as well, so a use
114
+ // takes float32 components as in Go.
115
+ _noteComplexConstInit(expr, scope) {
116
+ if (expr.kind !== "Ident") return;
117
+ const typed64 = !isUntyped(expr._type) && isComplex64(expr._type);
118
+ if (!typed64 && !(isUntyped(expr._type) && isComplex(expr._type))) return;
119
+ const owner = scope.lookupScope(expr.name);
120
+ if (!owner?._consts.has(expr.name)) return;
121
+ const init = owner.lookupConstExpr(expr.name);
122
+ if (!init) return;
123
+ expr._complexInit = init;
124
+ if (typed64) expr._c64Const = true;
125
+ },
126
+
62
127
  // Raw evaluation result, memoized on checked nodes: a value, a value with
63
128
  // `overflow`, `{ error }`, or null.
64
129
  _constRaw(expr, scope) {
@@ -84,6 +149,7 @@ export const constantCheckMethods = {
84
149
  // overflow / division by zero once per node.
85
150
  _noteConst(expr, scope) {
86
151
  if (!CONST_KINDS.has(expr.kind)) return;
152
+ this._noteComplexConstInit(expr, scope ?? this.globals);
87
153
  const raw = this._constRaw(expr, scope);
88
154
  expr._const = raw && !raw.overflow && !raw.error ? raw : null;
89
155
  if (!raw || expr._constReported) return;
@@ -129,7 +195,13 @@ export const constantCheckMethods = {
129
195
 
130
196
  _evalConstIdent(expr, scope) {
131
197
  const owner = scope.lookupScope(expr.name);
132
- if (!owner?._consts.has(expr.name)) return null;
198
+ // A constant declared after the type that uses it (`type B [k]int`) has
199
+ // no symbol yet, but its initializer is registered.
200
+ if (!owner) {
201
+ const early = scope.lookupConstExpr(expr.name);
202
+ return early ? this._constValue(early, scope) : null;
203
+ }
204
+ if (!owner._consts.has(expr.name)) return null;
133
205
  const init = owner.lookupConstExpr(expr.name);
134
206
  const v = init
135
207
  ? this._constValue(init, owner)
@@ -142,14 +214,12 @@ export const constantCheckMethods = {
142
214
  return typed?.overflow ? null : typed;
143
215
  },
144
216
 
145
- // `pkg.K` (an imported GoFront package) and the integer `math` constants.
217
+ // `pkg.K` of an imported GoFront package.
146
218
  _evalConstSelector(expr, scope) {
147
219
  if (expr.expr?.kind !== "Ident") return null;
148
220
  const ns = scope.lookup(expr.expr.name);
149
221
  if (ns?.kind !== "namespace") return null;
150
- if (ns._gofront) return ns._consts?.get(expr.field) ?? null;
151
- const m = ns.name === "math" ? MATH_INT_CONSTS[expr.field] : undefined;
152
- return m === undefined ? null : intConst(m);
222
+ return ns._gofront ? (ns._consts?.get(expr.field) ?? null) : null;
153
223
  },
154
224
 
155
225
  // Conversions, `len` of a constant string, and `min`/`max` of constants.
@@ -162,6 +232,8 @@ export const constantCheckMethods = {
162
232
  const fn = expr.func;
163
233
  if (fn?.kind !== "Ident" || scope.lookup(fn.name)?.kind !== "builtin")
164
234
  return null;
235
+ const arrayLen = this._constArrayLen(fn.name, expr.args, scope);
236
+ if (arrayLen !== null) return intConst(BigInt(arrayLen));
165
237
  const vals = (expr.args ?? []).map((a) => this._constValue(a, scope));
166
238
  if (vals.length === 0 || vals.some((v) => !v)) return null;
167
239
  if (fn.name === "min" || fn.name === "max")
@@ -171,6 +243,53 @@ export const constantCheckMethods = {
171
243
  return isLen ? intConst(BigInt(vals[0].v.length)) : null;
172
244
  },
173
245
 
246
+ // A package variable named in an array length may be declared after the
247
+ // type (or variable) that uses it, so it is not defined yet: its declared
248
+ // type, or the type of its composite literal, is resolved from the spec.
249
+ _earlyPackageVarType(arg, scope) {
250
+ let e = arg;
251
+ while (e?.kind === "ParenExpr") e = e.expr;
252
+ if (e?.kind !== "Ident") return null;
253
+ // An untyped package variable is `any` until its initializer is typed.
254
+ const known = scope.lookup(e.name);
255
+ if (known && !isAny(known)) return null;
256
+ const entry = this._pkgVarSpecs?.get(e.name);
257
+ if (!entry || entry.busy) return null;
258
+ const { spec, index } = entry;
259
+ const lit = spec.value?.[index];
260
+ const typeNode =
261
+ spec.type ?? (lit?.kind === "CompositeLit" ? lit.typeExpr : null);
262
+ if (!typeNode) return null;
263
+ entry.busy = true;
264
+ try {
265
+ const t = this.resolveTypeNode(typeNode, this.globals);
266
+ // `[...]T{...}`: the length is the literal's highest index + 1.
267
+ return t?.kind === "array" && t.size === null && lit?.elems
268
+ ? { ...t, size: compositeLitMaxIndex(lit.elems) + 1 }
269
+ : t;
270
+ } finally {
271
+ entry.busy = false;
272
+ }
273
+ },
274
+
275
+ // `len(a)` and `cap(a)` of an array (or pointer to an array) are constants
276
+ // unless `a` contains a function call or channel receive; `a` itself is not
277
+ // evaluated. Returns the length, or null.
278
+ _constArrayLen(name, args, scope) {
279
+ if ((name !== "len" && name !== "cap") || args?.length !== 1) return null;
280
+ const arg = args[0];
281
+ if (hasCallOrReceive(arg)) return null;
282
+ let t = this._earlyPackageVarType(arg, scope);
283
+ if (!t) {
284
+ if (arg._type === undefined) this.checkExpr(arg, scope);
285
+ t = arg._type;
286
+ }
287
+ t = this.resolveType(t);
288
+ if (t?.kind === "pointer") t = this.resolveType(t.base);
289
+ if (t?.kind === "named") t = this.resolveType(t.underlying);
290
+ return t?.kind === "array" && Number.isInteger(t.size) ? t.size : null;
291
+ },
292
+
174
293
  // Target type of a conversion call (`MyInt(x)`, `int32(x)`), or null.
175
294
  _constConversionTarget(expr, scope) {
176
295
  if (expr.args?.length !== 1) return null;
@@ -201,7 +320,18 @@ export const constantCheckMethods = {
201
320
  );
202
321
  if (base?.kind === "interface" || isAny(base)) target = defaultType(source);
203
322
  const i = constIntValue(node._const);
204
- const range = i === null ? null : this._intRange(target);
323
+ const range = this._intRange(target);
324
+ if (i === null && range)
325
+ return this._reportTruncatedConst(target, node, ctx);
326
+ // A float or complex target holds the constant as a Number: Go rounds
327
+ // the same way, so the JS backend has nothing to warn about.
328
+ if (!range && node.kind) {
329
+ const u = this.resolveType(
330
+ target?.kind === "named" ? target.underlying : target,
331
+ );
332
+ if (u?.kind === "basic" && /^(float|complex)/.test(u.name))
333
+ node._floatConst = true;
334
+ }
205
335
  if (!range) return false;
206
336
  if (i >= range[0] && i <= range[1]) {
207
337
  node._intConst = true; // the JS backend's precision warning reads this
@@ -223,6 +353,24 @@ export const constantCheckMethods = {
223
353
  return true;
224
354
  },
225
355
 
356
+ // `var i int = 1.5`: an untyped float constant with a fractional part
357
+ // cannot become an integer.
358
+ _reportTruncatedConst(target, node, ctx) {
359
+ const c = node._const;
360
+ if (c.k !== "FLOAT" || c.t) return false;
361
+ const shown =
362
+ node.kind === "BasicLit"
363
+ ? `${node.value} (untyped float constant)`
364
+ : `${c.v} (untyped float constant ${c.v})`;
365
+ this.err(
366
+ ctx
367
+ ? `cannot use ${shown} as ${typeStr(target)} value in ${ctx} (truncated)`
368
+ : `${shown} truncated to ${typeStr(target)}`,
369
+ node,
370
+ );
371
+ return true;
372
+ },
373
+
226
374
  // `x := 1 << 63`, `println(1 << 64)`: the constant takes its default type.
227
375
  _checkConstDefault(source, node, ctx) {
228
376
  return this._checkConstOverflow(defaultType(source), source, node, ctx);