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
@@ -8,10 +8,9 @@
8
8
  // (a constant may name one declared later).
9
9
  //
10
10
  // The checker reports cycles; both backends emit initialisers in this order.
11
- // Shadowing is handled conservatively: a name declared anywhere in a function
12
- // body (or as a parameter) is never counted as a reference to the package-level
13
- // name inside that function, so the analysis can miss a dependency but never
14
- // invents a cycle.
11
+ // Shadowing follows Go's block scoping: a name declared by a parameter, result,
12
+ // receiver or statement hides the package-level one from the next statement to
13
+ // the end of its block, and not outside it.
15
14
 
16
15
  function forEachChildNode(node, fn) {
17
16
  for (const key of Object.keys(node)) {
@@ -21,34 +20,35 @@ function forEachChildNode(node, fn) {
21
20
  }
22
21
  }
23
22
 
24
- // Names declared inside a function-like node (parameters, results, locals).
25
- function declaredNames(node, out = new Set()) {
26
- if (!node || typeof node !== "object") return out;
27
- if (Array.isArray(node)) {
28
- for (const n of node) declaredNames(n, out);
29
- return out;
23
+ // A lexical scope of locally declared names.
24
+ class Shadow {
25
+ constructor(parent = null) {
26
+ this.parent = parent;
27
+ this.names = new Set();
30
28
  }
31
- if (node.kind === "DefineStmt") {
32
- for (const l of [node.lhs].flat()) if (l?.kind === "Ident") out.add(l.name);
33
- } else if (node.kind === "VarDecl" || node.kind === "ConstDecl") {
34
- for (const spec of node.decls ?? []) for (const n of spec.names) out.add(n);
35
- } else if (node.kind === "RangeStmt" || node.kind === "ForRangeStmt") {
36
- for (const k of [node.key, node.value, node.keyVar, node.valueVar])
37
- if (k?.kind === "Ident") out.add(k.name);
38
- if (typeof node.key === "string") out.add(node.key);
39
- if (typeof node.value === "string") out.add(node.value);
40
- } else if (node.kind === "TypeSwitchStmt" && node.assign) {
41
- out.add(node.assign);
29
+
30
+ has(name) {
31
+ for (let s = this; s; s = s.parent) if (s.names.has(name)) return true;
32
+ return false;
42
33
  }
43
- if (Array.isArray(node.params))
44
- for (const p of node.params) if (p?.name) out.add(p.name);
45
- if (node.recvName) out.add(node.recvName);
46
- for (const r of node.returnType?._namedReturns ?? [])
47
- if (r.name) out.add(r.name);
48
- forEachChildNode(node, (c) => declaredNames(c, out));
34
+
35
+ add(...names) {
36
+ for (const n of names) if (n) this.names.add(n);
37
+ }
38
+ }
39
+
40
+ // Names a function-like node declares for its body: receiver, parameters and
41
+ // named results.
42
+ function signatureNames(node) {
43
+ const out = [];
44
+ if (node.recvName) out.push(node.recvName);
45
+ for (const p of node.params ?? []) if (p?.name) out.push(p.name);
46
+ for (const r of node.returnType?._namedReturns ?? []) out.push(r.name);
49
47
  return out;
50
48
  }
51
49
 
50
+ const lhsName = (l) => (typeof l === "string" ? l : l?.name);
51
+
52
52
  // The `Type.method` key of the declaration `name` selects on a value of type
53
53
  // `t`, found the way the checker finds it: on the type itself, else on an
54
54
  // embedded type (through pointers too). Interface methods have no body to
@@ -83,6 +83,60 @@ function litHasExprKeys(lit) {
83
83
  return ["MapType", "SliceType", "ArrayType"].includes(lit.typeExpr?.kind);
84
84
  }
85
85
 
86
+ // Statements that open a scope and declare names as they run. Each handler
87
+ // walks the node's children in order, adding declarations to `shadow` after
88
+ // the expressions that cannot see them.
89
+ const SCOPED = {
90
+ FuncDecl: walkFunc,
91
+ MethodDecl: walkFunc,
92
+ FuncLit: walkFunc,
93
+ Block: (node, shadow, ctx, out) =>
94
+ walkRefs(node.stmts, new Shadow(shadow), ctx, out),
95
+ DefineStmt: (node, shadow, ctx, out) => {
96
+ walkRefs(node.rhs, shadow, ctx, out);
97
+ shadow.add(...[node.lhs].flat().map(lhsName));
98
+ },
99
+ VarDecl: walkDeclSpecs,
100
+ ConstDecl: walkDeclSpecs,
101
+ IfStmt: (node, shadow, ctx, out) => {
102
+ const inner = new Shadow(shadow);
103
+ for (const c of [node.init, node.cond, node.body, node.elseBody])
104
+ walkRefs(c, inner, ctx, out);
105
+ },
106
+ ForStmt: (node, shadow, ctx, out) => {
107
+ const inner = new Shadow(shadow);
108
+ for (const c of [node.init, node.cond, node.post, node.body])
109
+ walkRefs(c, inner, ctx, out);
110
+ },
111
+ SwitchStmt: walkSwitch,
112
+ TypeSwitchStmt: walkSwitch,
113
+ };
114
+
115
+ function walkFunc(node, shadow, ctx, out) {
116
+ const inner = new Shadow(shadow);
117
+ inner.add(...signatureNames(node));
118
+ walkRefs(node.body, inner, ctx, out);
119
+ }
120
+
121
+ function walkDeclSpecs(node, shadow, ctx, out) {
122
+ for (const spec of node.decls ?? []) {
123
+ walkRefs(spec.value, shadow, ctx, out);
124
+ shadow.add(...(spec.names ?? []));
125
+ }
126
+ }
127
+
128
+ function walkSwitch(node, shadow, ctx, out) {
129
+ const inner = new Shadow(shadow);
130
+ walkRefs(node.init, inner, ctx, out);
131
+ walkRefs(node.tag ?? node.expr, inner, ctx, out);
132
+ for (const c of node.cases ?? []) {
133
+ const clause = new Shadow(inner);
134
+ if (node.assign) clause.add(node.assign);
135
+ walkRefs(c.list, clause, ctx, out);
136
+ walkRefs(c.stmts, clause, ctx, out);
137
+ }
138
+ }
139
+
86
140
  function walkRefs(node, shadow, ctx, out) {
87
141
  if (!node || typeof node !== "object") return;
88
142
  if (Array.isArray(node)) {
@@ -90,10 +144,17 @@ function walkRefs(node, shadow, ctx, out) {
90
144
  return;
91
145
  }
92
146
  if (node.kind === "Ident") {
93
- if (ctx.pkgNames.has(node.name) && !shadow.has(node.name))
147
+ // The blank identifier names nothing, whatever `var _` the package has.
148
+ if (
149
+ node.name !== "_" &&
150
+ ctx.pkgNames.has(node.name) &&
151
+ !shadow.has(node.name)
152
+ )
94
153
  out.names.add(node.name);
95
154
  return;
96
155
  }
156
+ const scoped = SCOPED[node.kind];
157
+ if (scoped) return scoped(node, shadow, ctx, out);
97
158
  if (node.kind === "SelectorExpr") {
98
159
  const key = methodKeyOf(node.expr?._type, node.field, ctx.funcs);
99
160
  if (key) out.methods.add(key);
@@ -118,11 +179,10 @@ function walkRefs(node, shadow, ctx, out) {
118
179
  /**
119
180
  * Collects what `root` (an initialiser expression or a function declaration)
120
181
  * refers to: package-level names (`names`) and declarations selected as methods
121
- * (`methods`, keyed `Type.method`). The single place shadowing is decided:
122
- * a name declared anywhere inside `root` never counts as the package-level one.
182
+ * (`methods`, keyed `Type.method`), honouring block-scoped shadowing.
123
183
  */
124
184
  function collectRefs(root, ctx, out) {
125
- walkRefs(root, declaredNames(root), ctx, out);
185
+ walkRefs(root, new Shadow(), ctx, out);
126
186
  }
127
187
 
128
188
  const emptyRefs = () => ({ names: new Set(), methods: new Set() });
@@ -261,24 +321,52 @@ function orderVars(varEntries, depsOf) {
261
321
  return { vars, cycles: [] };
262
322
  }
263
323
 
264
- // Constants depend on other constants only.
324
+ // Constants depend on other constants only; a constant that reaches itself is
325
+ // a cycle (reported once, at the constant where the walk closed it).
265
326
  function orderConsts(constEntries) {
266
327
  const constNames = new Set(constEntries.flatMap((e) => e.names));
267
328
  const ctx = { pkgNames: constNames, funcs: new Map() };
268
329
  const owner = new Map();
269
330
  for (const e of constEntries) for (const n of e.names) owner.set(n, e);
270
331
  const consts = [];
271
- const state = new Set();
332
+ const cycles = [];
333
+ const state = new Map(); // entry -> 1 while on the walk's path, 2 when done
334
+ const path = [];
272
335
  const visit = (e) => {
273
- if (state.has(e)) return;
274
- state.add(e);
336
+ state.set(e, 1);
337
+ path.push(e);
275
338
  const refs = emptyRefs();
276
339
  for (const v of e.spec.value ?? []) collectRefs(v, ctx, refs);
277
- for (const n of refs.names) if (owner.has(n)) visit(owner.get(n));
340
+ for (const n of refs.names) {
341
+ const dep = owner.get(n);
342
+ if (!dep) continue;
343
+ if (state.get(dep) === 1) {
344
+ const names = path.slice(path.indexOf(dep)).map((p) => p.names[0]);
345
+ const chain = names.length === 1 ? names : [...names, names[0]];
346
+ cycles.push({ spec: dep.spec, message: cycleMessage(dep, chain) });
347
+ } else if (!state.has(dep)) visit(dep);
348
+ }
349
+ path.pop();
350
+ state.set(e, 2);
278
351
  consts.push(e);
279
352
  };
280
- for (const e of constEntries) visit(e);
281
- return consts;
353
+ for (const e of constEntries) if (!state.has(e)) visit(e);
354
+ return { consts, cycles };
355
+ }
356
+
357
+ // A literal initialiser (or none: the zero value) reads nothing and has no
358
+ // effect, and gc initialises it statically, so a function that runs during
359
+ // another variable's initialiser sees it set even when it is declared later
360
+ // (an interface method call adds no dependency in the spec).
361
+ const isStaticValue = (v) =>
362
+ v?.kind === "BasicLit" ||
363
+ (v?.kind === "UnaryExpr" &&
364
+ ["-", "+"].includes(v.op) &&
365
+ isStaticValue(v.operand));
366
+
367
+ function staticInitsFirst(vars) {
368
+ const isStatic = (e) => (e.spec.value ?? []).every(isStaticValue);
369
+ return [...vars.filter(isStatic), ...vars.filter((e) => !isStatic(e))];
282
370
  }
283
371
 
284
372
  /**
@@ -290,8 +378,16 @@ function orderConsts(constEntries) {
290
378
  export function orderPackageInits(programs) {
291
379
  const decls = collectDecls(programs);
292
380
  const depsOf = buildVarDeps(decls);
293
- const { vars, cycles } = orderVars(decls.varEntries, depsOf);
294
- return { consts: orderConsts(decls.constEntries), vars, cycles };
381
+ const ordered = orderVars(decls.varEntries, depsOf);
382
+ const { cycles } = ordered;
383
+ const vars =
384
+ cycles.length === 0 ? staticInitsFirst(ordered.vars) : ordered.vars;
385
+ const constOrder = orderConsts(decls.constEntries);
386
+ return {
387
+ consts: constOrder.consts,
388
+ vars,
389
+ cycles: [...constOrder.cycles, ...cycles],
390
+ };
295
391
  }
296
392
 
297
393
  /**
@@ -0,0 +1,41 @@
1
+ // Source-like text of an expression for diagnostics ("cannot use y (variable
2
+ // of type float64) as int value"). Only the common shapes are printed in full;
3
+ // anything else falls back to "expression".
4
+
5
+ function litText(node) {
6
+ switch (node.litKind) {
7
+ case "STRING":
8
+ return JSON.stringify(node.value);
9
+ case "NIL":
10
+ return "nil";
11
+ default:
12
+ return String(node.value);
13
+ }
14
+ }
15
+
16
+ const PRINTERS = {
17
+ Ident: (n) => n.name,
18
+ BasicLit: litText,
19
+ ImagLit: (n) => `${n.value}i`,
20
+ ParenExpr: (n) => `(${exprText(n.expr)})`,
21
+ SelectorExpr: (n) => `${exprText(n.expr)}.${n.field}`,
22
+ UnaryExpr: (n) => `${n.op}${exprText(n.operand)}`,
23
+ BinaryExpr: (n) => `${exprText(n.left)} ${n.op} ${exprText(n.right)}`,
24
+ CallExpr: (n) =>
25
+ `${exprText(n.func)}(${(n.args ?? []).map(exprText).join(", ")})`,
26
+ IndexExpr: (n) => `${exprText(n.expr)}[${exprText(n.index)}]`,
27
+ };
28
+
29
+ export function exprText(node) {
30
+ const print = node && PRINTERS[node.kind];
31
+ return print ? print(node) : "expression";
32
+ }
33
+
34
+ // "variable of type float64", "untyped int constant", "value of type float64".
35
+ export function operandKind(node, typeName, isUntypedConst) {
36
+ if (isUntypedConst)
37
+ return `untyped ${typeName === "float64" ? "float" : typeName} constant`;
38
+ return node?.kind === "Ident"
39
+ ? `variable of type ${typeName}`
40
+ : `value of type ${typeName}`;
41
+ }