gofront 1.8.0 → 1.9.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 (177) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -4
  3. package/src/backend/js/expressions.js +849 -371
  4. package/src/backend/js/index.js +241 -147
  5. package/src/backend/js/natives.js +383 -0
  6. package/src/backend/js/runtime.js +512 -2547
  7. package/src/backend/js/statements.js +506 -103
  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 +177 -13
  11. package/src/backend/shared/fixed-float.js +20 -0
  12. package/src/backend/shared/fmt-types.js +63 -171
  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 +643 -256
  16. package/src/backend/wasm/emit-exprs.js +124 -57
  17. package/src/backend/wasm/emit-maps.js +17 -367
  18. package/src/backend/wasm/emit-stmts.js +325 -75
  19. package/src/backend/wasm/emit.js +104 -13
  20. package/src/backend/wasm/globals.js +47 -8
  21. package/src/backend/wasm/glue.js +2 -4
  22. package/src/backend/wasm/index.js +36 -530
  23. package/src/backend/wasm/method-expr.js +12 -1
  24. package/src/backend/wasm/module-emitter.js +92 -717
  25. package/src/backend/wasm/monomorph.js +103 -6
  26. package/src/backend/wasm/natives.js +596 -0
  27. package/src/backend/wasm/prescan.js +5 -153
  28. package/src/backend/wasm/prune-funcs.js +89 -0
  29. package/src/backend/wasm/stdlib/index.js +2 -32
  30. package/src/backend/wasm/types.js +68 -20
  31. package/src/backend/wasm/variadic.js +3 -3
  32. package/src/cli/cli-core.js +5 -1
  33. package/src/cli/test-runner.js +115 -342
  34. package/src/compiler.js +108 -130
  35. package/src/lexer/lexer.js +105 -11
  36. package/src/lexer/templ-lexer.js +61 -18
  37. package/src/lexer/tokens.js +1 -0
  38. package/src/link/stdlib.js +354 -132
  39. package/src/link/type-names.js +148 -0
  40. package/src/link/wasm.js +30 -6
  41. package/src/lower/bounds.js +153 -0
  42. package/src/lower/captures.js +92 -33
  43. package/src/lower/embedding.js +32 -3
  44. package/src/lower/errors-as.js +196 -0
  45. package/src/lower/fmt-formatters.js +905 -0
  46. package/src/lower/functions.js +2 -0
  47. package/src/lower/gen-types.js +274 -0
  48. package/src/lower/index.js +7 -1
  49. package/src/lower/json-codecs.js +228 -247
  50. package/src/lower/ownership.js +25 -7
  51. package/src/lower/reachability.js +69 -51
  52. package/src/lower/walk.js +12 -0
  53. package/src/parser/declarations.js +28 -5
  54. package/src/parser/expressions.js +86 -16
  55. package/src/parser/statements.js +1 -0
  56. package/src/parser/templ.js +44 -5
  57. package/src/parser/types.js +1 -0
  58. package/src/resolver.js +1 -2
  59. package/src/typechecker/assignability.js +124 -11
  60. package/src/typechecker/const-fold.js +70 -27
  61. package/src/typechecker/constants.js +32 -6
  62. package/src/typechecker/expressions.js +326 -98
  63. package/src/typechecker/index.js +208 -33
  64. package/src/typechecker/init-order.js +136 -40
  65. package/src/typechecker/operand-text.js +40 -0
  66. package/src/typechecker/resolve.js +192 -36
  67. package/src/typechecker/statements.js +122 -11
  68. package/src/typechecker/stdlib/excluded.js +9 -5
  69. package/src/typechecker/stdlib/index.js +25 -9
  70. package/src/typechecker/termination.js +10 -1
  71. package/src/typechecker/types.js +203 -573
  72. package/stdlib/bytes/buffer.go +479 -0
  73. package/stdlib/bytes/bytes.go +1010 -0
  74. package/stdlib/bytes/reader.go +161 -0
  75. package/stdlib/context/context.go +115 -0
  76. package/stdlib/crypto/rand/rand.go +34 -0
  77. package/stdlib/crypto/sha256/sha256.go +249 -0
  78. package/stdlib/encoding/base64/base64.go +331 -0
  79. package/stdlib/encoding/binary/binary.go +332 -0
  80. package/stdlib/encoding/hex/hex.go +140 -0
  81. package/stdlib/encoding/json/json.go +62 -0
  82. package/stdlib/errors/errors.go +174 -0
  83. package/stdlib/fmt/errors.go +110 -0
  84. package/stdlib/fmt/format.go +732 -0
  85. package/stdlib/fmt/print.go +1232 -0
  86. package/stdlib/gom/gom.go +293 -0
  87. package/stdlib/html/html.go +251 -0
  88. package/stdlib/internal/fmtrt/fmtrt.go +163 -0
  89. package/stdlib/internal/jsonrt/dec.go +171 -38
  90. package/stdlib/internal/jsonrt/enc.go +186 -36
  91. package/stdlib/internal/jsonrt/scanner.go +17 -8
  92. package/stdlib/io/io.go +157 -1
  93. package/stdlib/maps/maps.go +131 -0
  94. package/stdlib/math/bits/bits.go +507 -0
  95. package/stdlib/math/extra.go +16 -14
  96. package/stdlib/math/math.go +439 -0
  97. package/stdlib/math/rand/rand.go +297 -0
  98. package/stdlib/os/os.go +93 -0
  99. package/stdlib/path/filepath/filepath.go +47 -0
  100. package/stdlib/path/path.go +394 -0
  101. package/stdlib/regexp/regexp.go +231 -0
  102. package/stdlib/slices/slices.go +526 -0
  103. package/stdlib/slices/sort.go +209 -0
  104. package/stdlib/slices/zsort_cmp.go +480 -0
  105. package/stdlib/slices/zsort_ordered.go +482 -0
  106. package/stdlib/sort/sort.go +391 -452
  107. package/stdlib/sort/zsort_func.go +480 -0
  108. package/stdlib/sort/zsort_iface.go +480 -0
  109. package/stdlib/strconv/float.go +1699 -0
  110. package/stdlib/strconv/isprint.go +752 -0
  111. package/stdlib/strconv/quote.go +235 -50
  112. package/stdlib/strconv/strconv.go +616 -0
  113. package/stdlib/strings/builder.go +95 -0
  114. package/stdlib/strings/reader.go +163 -0
  115. package/stdlib/strings/replacer.go +102 -0
  116. package/stdlib/strings/strings.go +807 -0
  117. package/stdlib/sync/sync.go +268 -0
  118. package/stdlib/testing/async_js.go +61 -0
  119. package/stdlib/testing/benchmark.go +297 -0
  120. package/stdlib/testing/testing.go +523 -0
  121. package/stdlib/time/duration.go +233 -0
  122. package/stdlib/time/format.go +745 -0
  123. package/stdlib/time/parse.go +790 -0
  124. package/stdlib/time/sleep_js.go +63 -0
  125. package/stdlib/time/time.go +834 -0
  126. package/stdlib/unicode/unicode.go +141 -3
  127. package/stdlib/unicode/utf8/utf8.go +464 -0
  128. package/src/backend/js/fmt-runtime.js +0 -573
  129. package/src/backend/js/stdlib/base64.js +0 -12
  130. package/src/backend/js/stdlib/binary.js +0 -102
  131. package/src/backend/js/stdlib/bits.js +0 -119
  132. package/src/backend/js/stdlib/builder.js +0 -98
  133. package/src/backend/js/stdlib/bytes.js +0 -368
  134. package/src/backend/js/stdlib/errors.js +0 -45
  135. package/src/backend/js/stdlib/fmt.js +0 -517
  136. package/src/backend/js/stdlib/gom.js +0 -155
  137. package/src/backend/js/stdlib/hex.js +0 -17
  138. package/src/backend/js/stdlib/html.js +0 -20
  139. package/src/backend/js/stdlib/io.js +0 -137
  140. package/src/backend/js/stdlib/json.js +0 -1167
  141. package/src/backend/js/stdlib/maps.js +0 -37
  142. package/src/backend/js/stdlib/math.js +0 -85
  143. package/src/backend/js/stdlib/os.js +0 -13
  144. package/src/backend/js/stdlib/path.js +0 -48
  145. package/src/backend/js/stdlib/rand.js +0 -32
  146. package/src/backend/js/stdlib/regexp.js +0 -54
  147. package/src/backend/js/stdlib/sha256.js +0 -12
  148. package/src/backend/js/stdlib/shared.js +0 -17
  149. package/src/backend/js/stdlib/simd.js +0 -34
  150. package/src/backend/js/stdlib/slices.js +0 -107
  151. package/src/backend/js/stdlib/sort.js +0 -30
  152. package/src/backend/js/stdlib/strconv.js +0 -79
  153. package/src/backend/js/stdlib/strings.js +0 -301
  154. package/src/backend/js/stdlib/sync.js +0 -13
  155. package/src/backend/js/stdlib/testing.js +0 -41
  156. package/src/backend/js/stdlib/time.js +0 -152
  157. package/src/backend/js/stdlib/unicode.js +0 -25
  158. package/src/backend/js/stdlib/utf8.js +0 -26
  159. package/src/backend/wasm/stdlib/bytes.js +0 -6716
  160. package/src/backend/wasm/stdlib/core.js +0 -10278
  161. package/src/backend/wasm/stdlib/io.js +0 -3578
  162. package/src/backend/wasm/stdlib/packages.js +0 -2402
  163. package/src/backend/wasm/stdlib/pure.js +0 -720
  164. package/src/backend/wasm/stdlib/time.js +0 -2229
  165. package/src/typechecker/stdlib/core.js +0 -1079
  166. package/src/typechecker/stdlib/extended.js +0 -1210
  167. package/stdlib/bytes/iter.go +0 -192
  168. package/stdlib/bytes/search.go +0 -240
  169. package/stdlib/bytes/split.go +0 -218
  170. package/stdlib/errors/overlay.go +0 -12
  171. package/stdlib/maps/iter.go +0 -65
  172. package/stdlib/slices/extra.go +0 -73
  173. package/stdlib/slices/iter.go +0 -113
  174. package/stdlib/strings/extra.go +0 -35
  175. package/stdlib/strings/iter.go +0 -122
  176. package/stdlib/sync/extra.go +0 -105
  177. package/stdlib/unicode/utf8/bytes.go +0 -239
@@ -271,6 +271,8 @@ function normalizeDeferStmt(stmt, parentList = null, index = -1, ctx = null) {
271
271
  ...call,
272
272
  func: funcExpr,
273
273
  args: callArgs,
274
+ // A deferred native call runs the Go body, as on the JS backend.
275
+ _noNative: true,
274
276
  _multiForward: tempIdents.length > 0 ? false : call._multiForward,
275
277
  };
276
278
  const synthFuncLit = {
@@ -0,0 +1,274 @@
1
+ // src/lower/gen-types.js
2
+ // Spells checked types as Go source for the generated per-package file
3
+ // (src/lower/json-codecs.js, src/lower/errors-as.js,
4
+ // src/lower/fmt-formatters.js), and holds what those generators share: the
5
+ // imports the spelled code needs and the enumeration of the program's types.
6
+
7
+ import { defaultType } from "../typechecker/types.js";
8
+ import { forEachChild } from "./walk.js";
9
+
10
+ export function under(t) {
11
+ return t?.kind === "named" ? under(t.underlying) : t;
12
+ }
13
+
14
+ export const isEmptyIface = (t) =>
15
+ (t?.kind === "basic" && t.name === "any") ||
16
+ (under(t)?.kind === "interface" && under(t).methods?.size === 0);
17
+
18
+ // Arrays and maps of these share one WebAssembly representation (and so one
19
+ // type-switch case), so only literals may register them: a `[2]byte` conversion
20
+ // must not shadow a `[2]int32` that is stored in an interface. A slice of
21
+ // them is tagged with its Go type in an interface and registers like any other.
22
+ export const SHARED_REPR = new Set([
23
+ "bool",
24
+ "int8",
25
+ "int16",
26
+ "int32",
27
+ "uint8",
28
+ "uint16",
29
+ "uint32",
30
+ "byte",
31
+ "rune",
32
+ ]);
33
+
34
+ export function hasSharedRepr(t, depth = 0) {
35
+ if (!t || depth > 8) return false;
36
+ const u = under(t);
37
+ if (u?.kind === "basic") return SHARED_REPR.has(u.name);
38
+ if (u?.kind === "slice" || u?.kind === "array")
39
+ return hasSharedRepr(u.elem, depth + 1);
40
+ if (u?.kind === "map")
41
+ return hasSharedRepr(u.key, depth + 1) || hasSharedRepr(u.value, depth + 1);
42
+ return false;
43
+ }
44
+
45
+ // A method's results; a multi-value result is one tuple type.
46
+ export function results(m) {
47
+ const r = (m.returns ?? []).filter(
48
+ (t) => !(t?.kind === "basic" && t.name === "void"),
49
+ );
50
+ return r.length === 1 && r[0]?.kind === "tuple" ? r[0].types : r;
51
+ }
52
+
53
+ // Calls every `fn(node)` for the AST nodes under `root`.
54
+ export function walkNodes(root, fn) {
55
+ if (!root || typeof root !== "object") return;
56
+ if (Array.isArray(root)) {
57
+ for (const n of root) walkNodes(n, fn);
58
+ return;
59
+ }
60
+ fn(root);
61
+ forEachChild(root, (c) => walkNodes(c, fn));
62
+ }
63
+
64
+ // True when `t` mentions a type parameter (a generic declaration, or a type
65
+ // built from one inside a generic function).
66
+ export function mentionsTypeParam(t, seen = new Set()) {
67
+ if (!t || seen.has(t)) return false;
68
+ seen.add(t);
69
+ const m = (x) => mentionsTypeParam(x, seen);
70
+ switch (t.kind) {
71
+ case "typeParam":
72
+ return true;
73
+ case "named":
74
+ return (t.typeArgs ?? []).some(m) || m(t.underlying);
75
+ case "pointer":
76
+ return m(t.base);
77
+ case "slice":
78
+ case "array":
79
+ return m(t.elem);
80
+ case "map":
81
+ return m(t.key) || m(t.value);
82
+ case "struct":
83
+ return (t._structFields ?? []).some((f) => m(f.type));
84
+ default:
85
+ return false;
86
+ }
87
+ }
88
+
89
+ // The last element of an import path: the package's name.
90
+ const canonicalName = (modPath) => modPath.split("/").pop();
91
+
92
+ // The names the programs declare at package level.
93
+ function declaredNames(programs) {
94
+ const names = new Set();
95
+ for (const p of programs)
96
+ for (const d of p.decls ?? []) {
97
+ if (d.kind === "TypeDecl" || d.kind === "FuncDecl") names.add(d.name);
98
+ else if (d.kind === "VarDecl" || d.kind === "ConstDecl")
99
+ for (const spec of d.decls ?? [])
100
+ for (const n of spec.names ?? []) names.add(n);
101
+ }
102
+ return names;
103
+ }
104
+
105
+ // Every name an import answers to (its alias, its package name) -> its path.
106
+ function importPaths(programs) {
107
+ const paths = new Map();
108
+ for (const p of programs)
109
+ for (const imp of p.imports ?? [])
110
+ for (const spec of imp.imports) {
111
+ if (spec.alias === "_" || spec.alias === ".") continue;
112
+ paths.set(canonicalName(spec.path), spec.path);
113
+ if (spec.alias) paths.set(spec.alias, spec.path);
114
+ }
115
+ return paths;
116
+ }
117
+
118
+ // The generated file's imports for the qualifiers (name -> path) its code
119
+ // spells: a path, or { path, alias } for a qualifier renamed to stay clear of
120
+ // a package-level name.
121
+ export function importsFor(qualifiers) {
122
+ return [...qualifiers].map(([name, path]) =>
123
+ name === canonicalName(path) ? path : { path, alias: name },
124
+ );
125
+ }
126
+
127
+ // Every type a closed-world generator may need to describe: the entries
128
+ // { type, node, via } for the type arguments of generic calls (`typeArg`),
129
+ // the type of every expression (`expr`) and the package's declared named
130
+ // types (`declared`, node null).
131
+ export function collectProgramTypes(programs, checker) {
132
+ const found = [];
133
+ for (const p of programs)
134
+ walkNodes(p.decls, (node) => {
135
+ for (const a of node._typeArgs ?? [])
136
+ found.push({ type: defaultType(a), node, via: "typeArg" });
137
+ if (node._type)
138
+ found.push({ type: defaultType(node._type), node, via: "expr" });
139
+ });
140
+ for (const [name, t] of checker.types) {
141
+ if (t?.kind !== "named" || t.name !== name) continue;
142
+ if (name.includes(".") || name.includes("$$")) continue;
143
+ if (t._genericOf || t.typeArgs?.length) continue;
144
+ found.push({ type: t, node: null, via: "declared" });
145
+ }
146
+ return found;
147
+ }
148
+
149
+ export class TypeSpeller {
150
+ // `qualifiers` receives name -> import path for each package the spelled
151
+ // code names.
152
+ constructor(pkgName, qualifiers, programs = []) {
153
+ this.pkgName = pkgName;
154
+ this.qualifiers = qualifiers;
155
+ this.programs = programs;
156
+ this.importPaths = importPaths(programs);
157
+ this.declared = declaredNames(programs);
158
+ }
159
+
160
+ // Go source for the type.
161
+ spell(t) {
162
+ const sp = (x) => this.spell(x);
163
+ const byKind = {
164
+ basic: () => t.name,
165
+ named: () => this.spellNamed(t),
166
+ pointer: () => `*${sp(t.base)}`,
167
+ slice: () => `[]${sp(t.elem)}`,
168
+ array: () => `[${t.size}]${sp(t.elem)}`,
169
+ map: () => `map[${sp(t.key)}]${sp(t.value)}`,
170
+ struct: () => this.spellStruct(t),
171
+ interface: () => (t.name === "error" ? "error" : "any"),
172
+ func: () => this.funcString(t, sp),
173
+ };
174
+ if (!byKind[t.kind])
175
+ throw new Error(`json: cannot spell type kind ${t.kind}`);
176
+ return byKind[t.kind]();
177
+ }
178
+
179
+ // The package name `%T` and errors print for the bare-named type `t`: this
180
+ // package, or the local package an importer reaches it through as `p.Name`.
181
+ declaringPackage(t) {
182
+ if (this.declared.has(t.name) || !this.checker) return this.pkgName;
183
+ for (const [key, v] of this.checker.types) {
184
+ const dot = key.indexOf(".");
185
+ if (v !== t || dot <= 0 || key.slice(dot + 1) !== t.name) continue;
186
+ const modPath = this.importPaths.get(key.slice(0, dot));
187
+ if (modPath) return canonicalName(modPath);
188
+ }
189
+ return this.pkgName;
190
+ }
191
+
192
+ // The name the generated file uses for the package at `modPath`: its
193
+ // package name, or `_gf_<name>` when the package declares that name itself
194
+ // or another path took it.
195
+ qualifierFor(modPath) {
196
+ for (const [name, path] of this.qualifiers)
197
+ if (path === modPath) return name;
198
+ const base = canonicalName(modPath);
199
+ const taken = (n) => this.declared.has(n) || this.qualifiers.has(n);
200
+ let name = base;
201
+ for (let i = 1; taken(name); i++) name = `_gf_${base}${i > 1 ? i : ""}`;
202
+ this.qualifiers.set(name, modPath);
203
+ return name;
204
+ }
205
+
206
+ spellNamed(t) {
207
+ let name = t.name;
208
+ const at = name.indexOf("$$");
209
+ const dot = name.indexOf(".");
210
+ if (at >= 0) {
211
+ const orig = name.slice(0, at);
212
+ const modPath = name.slice(at + 2).replaceAll("$", "/");
213
+ name = `${this.qualifierFor(modPath)}.${orig}`;
214
+ } else if (dot > 0) {
215
+ const modPath = this.importPaths.get(name.slice(0, dot));
216
+ if (modPath) name = `${this.qualifierFor(modPath)}${name.slice(dot)}`;
217
+ }
218
+ return t.typeArgs?.length
219
+ ? `${name}[${t.typeArgs.map((a) => this.spell(a)).join(", ")}]`
220
+ : name;
221
+ }
222
+
223
+ spellStruct(t) {
224
+ const fields = (t._structFields ?? []).map((f) => {
225
+ const tag = f.tag ? ` \`${f.tag}\`` : "";
226
+ if (f.embedded) return `${this.spell(f.type)}${tag}`;
227
+ return `${f.names.join(", ")} ${this.spell(f.type)}${tag}`;
228
+ });
229
+ return `struct { ${fields.join("; ")} }`;
230
+ }
231
+
232
+ funcString(t, name) {
233
+ const rs = results(t);
234
+ const res =
235
+ rs.length === 0
236
+ ? ""
237
+ : rs.length === 1
238
+ ? ` ${name(rs[0])}`
239
+ : ` (${rs.map(name).join(", ")})`;
240
+ // The signature keeps a variadic parameter's element type.
241
+ const params = t.params.map((p, i) =>
242
+ t.variadic && i === t.params.length - 1 ? `...${name(p)}` : name(p),
243
+ );
244
+ return `func(${params.join(", ")})${res}`;
245
+ }
246
+
247
+ // True when the type can be written as Go source in the generated file.
248
+ // Subclasses decide what a named type, an interface and a struct field
249
+ // need.
250
+ spellable(t, depth = 0, seen = new Set()) {
251
+ if (!t || depth > 8) return false;
252
+ const ok = (x) => this.spellable(x, depth + 1, seen);
253
+ const byKind = {
254
+ basic: () => t.name !== "void" && t.name !== "nil",
255
+ named: () => this.spellableNamed(t, ok, seen),
256
+ pointer: () => ok(t.base),
257
+ slice: () => ok(t.elem),
258
+ array: () => ok(t.elem),
259
+ map: () => ok(t.key) && ok(t.value),
260
+ interface: () => this.spellableInterface(t),
261
+ struct: () => this.spellableStruct(t, ok),
262
+ func: () => t.params.every(ok) && results(t).every(ok),
263
+ };
264
+ return byKind[t.kind]?.() ?? false;
265
+ }
266
+
267
+ spellableInterface(_t) {
268
+ return true;
269
+ }
270
+
271
+ spellableStruct(t, ok) {
272
+ return (t._structFields ?? []).every((f) => ok(f.type));
273
+ }
274
+ }
@@ -18,7 +18,9 @@ import { computeEmbeddedStubs } from "./embedding.js";
18
18
  import { paramEscapes } from "./escape.js";
19
19
  import { extractNamedReturns, hasDefer, normalizeDefers } from "./functions.js";
20
20
  import { computeGlobalAnalysis } from "./globals.js";
21
- import { OwnershipContext } from "./ownership.js";
21
+ import { OwnershipContext, takesElementAddress } from "./ownership.js";
22
+
23
+ export { takesElementAddress };
22
24
 
23
25
  class LowerResult {
24
26
  constructor() {
@@ -40,6 +42,8 @@ class LowerResult {
40
42
  this.initPathFunctions = new Set();
41
43
  // Map<ASTNode, Array<string>>
42
44
  this.cachedGlobalsByFunc = new Map();
45
+ // True when some code takes `&xs[i]` (see takesElementAddress)
46
+ this.elemAddrTaken = false;
43
47
  }
44
48
  }
45
49
 
@@ -81,6 +85,8 @@ export function lower(programOrPrograms, checker = null) {
81
85
 
82
86
  normalizeDefers(programs);
83
87
 
88
+ res.elemAddrTaken = programs.some((p) => takesElementAddress(p?.decls));
89
+
84
90
  // 1. Aggregate methods across all files in the package
85
91
  const methodMap = new Map();
86
92
  for (const program of programs) {