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.
- package/README.md +45 -49
- package/package.json +12 -8
- package/src/backend/js/expressions.js +1627 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +508 -102
- package/src/backend/js/runtime.js +1997 -106
- package/src/backend/js/statements.js +931 -87
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +7 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +19 -2
- package/src/backend/js/stdlib/json.js +1167 -0
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +34 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +446 -72
- package/src/backend/wasm/emit-builtins.js +320 -13
- package/src/backend/wasm/emit-exprs.js +1404 -199
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +518 -74
- package/src/backend/wasm/encode.js +128 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/optimize.js +4 -2
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +3 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +499 -0
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +221 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +84 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +89 -15
- package/src/compiler.js +107 -269
- package/src/dts-parser.js +71 -14
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -6
- package/src/lower/captures.js +176 -32
- package/src/lower/embedding.js +2 -1
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/parser/struct-tag.js +144 -0
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +130 -13
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +215 -11
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +960 -99
- package/src/typechecker/index.js +491 -72
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +596 -52
- package/src/typechecker/statements.js +157 -17
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +359 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +6 -4
- package/src/typechecker/stdlib/simd.js +123 -0
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +165 -26
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
// Package initialisation order (Go spec, "Package initialization").
|
|
2
|
+
//
|
|
3
|
+
// A package-level variable is initialised once every variable its initialiser
|
|
4
|
+
// refers to has been, where "refers to" follows references to functions and
|
|
5
|
+
// methods transitively. Each step picks the earliest variable in declaration
|
|
6
|
+
// order that is ready; a step with none left over is an initialisation cycle.
|
|
7
|
+
// Constants are folded by value, so they only need ordering among themselves
|
|
8
|
+
// (a constant may name one declared later).
|
|
9
|
+
//
|
|
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.
|
|
15
|
+
|
|
16
|
+
function forEachChildNode(node, fn) {
|
|
17
|
+
for (const key of Object.keys(node)) {
|
|
18
|
+
if (key.charCodeAt(0) === 95) continue; // "_" side tables
|
|
19
|
+
const v = node[key];
|
|
20
|
+
if (v && typeof v === "object") fn(v);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
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;
|
|
30
|
+
}
|
|
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);
|
|
42
|
+
}
|
|
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));
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The `Type.method` key of the declaration `name` selects on a value of type
|
|
53
|
+
// `t`, found the way the checker finds it: on the type itself, else on an
|
|
54
|
+
// embedded type (through pointers too). Interface methods have no body to
|
|
55
|
+
// follow, so a call through an interface adds no dependency (Go does the same).
|
|
56
|
+
function methodKeyOf(t, name, funcs, depth = 0) {
|
|
57
|
+
let x = t;
|
|
58
|
+
if (x?.kind === "pointer") x = x.base;
|
|
59
|
+
if (x?.kind !== "named" || depth > 8) return null;
|
|
60
|
+
for (const typeName of [x.name, x._generic?.name]) {
|
|
61
|
+
const key = `${typeName}.${name}`;
|
|
62
|
+
if (typeName && funcs.has(key)) return key;
|
|
63
|
+
}
|
|
64
|
+
for (const e of x.underlying?._embeds ?? []) {
|
|
65
|
+
const key = methodKeyOf(e, name, funcs, depth + 1);
|
|
66
|
+
if (key) return key;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Whether the keys of a composite literal are expressions (map, slice and
|
|
72
|
+
// array literals) rather than field names (struct literals). Before the
|
|
73
|
+
// literal is checked its written type decides; an unnamed one stays ambiguous
|
|
74
|
+
// and its keys are skipped.
|
|
75
|
+
function litHasExprKeys(lit) {
|
|
76
|
+
let t = lit._type;
|
|
77
|
+
for (let i = 0; t && i < 8; i++) {
|
|
78
|
+
if (t.kind === "named") t = t.underlying;
|
|
79
|
+
else if (t.kind === "pointer") t = t.base;
|
|
80
|
+
else break;
|
|
81
|
+
}
|
|
82
|
+
if (t?.kind) return ["map", "slice", "array"].includes(t.kind);
|
|
83
|
+
return ["MapType", "SliceType", "ArrayType"].includes(lit.typeExpr?.kind);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function walkRefs(node, shadow, ctx, out) {
|
|
87
|
+
if (!node || typeof node !== "object") return;
|
|
88
|
+
if (Array.isArray(node)) {
|
|
89
|
+
for (const n of node) walkRefs(n, shadow, ctx, out);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (node.kind === "Ident") {
|
|
93
|
+
if (ctx.pkgNames.has(node.name) && !shadow.has(node.name))
|
|
94
|
+
out.names.add(node.name);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (node.kind === "SelectorExpr") {
|
|
98
|
+
const key = methodKeyOf(node.expr?._type, node.field, ctx.funcs);
|
|
99
|
+
if (key) out.methods.add(key);
|
|
100
|
+
}
|
|
101
|
+
if (node.kind === "CompositeLit") {
|
|
102
|
+
const exprKeys = litHasExprKeys(node);
|
|
103
|
+
for (const el of node.elems ?? []) {
|
|
104
|
+
if (el?.kind !== "KeyValueExpr") walkRefs(el, shadow, ctx, out);
|
|
105
|
+
else {
|
|
106
|
+
// A struct literal's key is a field name, not a reference.
|
|
107
|
+
if (exprKeys || el.key?.kind !== "Ident")
|
|
108
|
+
walkRefs(el.key, shadow, ctx, out);
|
|
109
|
+
walkRefs(el.value, shadow, ctx, out);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
walkRefs(node.typeExpr, shadow, ctx, out);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
forEachChildNode(node, (c) => walkRefs(c, shadow, ctx, out));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Collects what `root` (an initialiser expression or a function declaration)
|
|
120
|
+
* 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.
|
|
123
|
+
*/
|
|
124
|
+
function collectRefs(root, ctx, out) {
|
|
125
|
+
walkRefs(root, declaredNames(root), ctx, out);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const emptyRefs = () => ({ names: new Set(), methods: new Set() });
|
|
129
|
+
|
|
130
|
+
// Package-level specs and callable declarations of all files.
|
|
131
|
+
function collectDecls(programs) {
|
|
132
|
+
const varEntries = [];
|
|
133
|
+
const constEntries = [];
|
|
134
|
+
const funcs = new Map(); // name | "Type.method" -> declaration
|
|
135
|
+
for (const program of programs) {
|
|
136
|
+
for (const d of program.decls ?? []) {
|
|
137
|
+
if (d.kind === "VarDecl" || d.kind === "ConstDecl") {
|
|
138
|
+
const into = d.kind === "VarDecl" ? varEntries : constEntries;
|
|
139
|
+
for (const spec of d.decls ?? [])
|
|
140
|
+
into.push({ decl: d, spec, names: spec.names ?? [], program });
|
|
141
|
+
} else if (d.kind === "FuncDecl") {
|
|
142
|
+
funcs.set(d.name, d);
|
|
143
|
+
} else if (d.kind === "MethodDecl") {
|
|
144
|
+
const t = d.recvType?.name ?? d.recvType?.base?.name;
|
|
145
|
+
if (t) funcs.set(`${t}.${d.name}`, d);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { varEntries, constEntries, funcs };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// For each var spec, the other var specs its initialiser depends on (through
|
|
153
|
+
// functions and methods too), with the name chain leading to each.
|
|
154
|
+
function buildVarDeps({ varEntries, constEntries, funcs }) {
|
|
155
|
+
const varOwner = new Map(); // var name -> entry
|
|
156
|
+
for (const e of varEntries) for (const n of e.names) varOwner.set(n, e);
|
|
157
|
+
const pkgNames = new Set([
|
|
158
|
+
...varOwner.keys(),
|
|
159
|
+
...funcs.keys(),
|
|
160
|
+
...constEntries.flatMap((e) => e.names),
|
|
161
|
+
]);
|
|
162
|
+
const ctx = { pkgNames, funcs };
|
|
163
|
+
const funcRefs = new Map();
|
|
164
|
+
const refsOfFunc = (key) => {
|
|
165
|
+
if (funcRefs.has(key)) return funcRefs.get(key);
|
|
166
|
+
const decl = funcs.get(key);
|
|
167
|
+
const out = emptyRefs();
|
|
168
|
+
funcRefs.set(key, out);
|
|
169
|
+
if (decl) collectRefs(decl, ctx, out);
|
|
170
|
+
return out;
|
|
171
|
+
};
|
|
172
|
+
const depsOf = (entry) => {
|
|
173
|
+
const deps = new Map(); // owner entry -> path of names
|
|
174
|
+
const seen = new Set();
|
|
175
|
+
const follow = (key, path) => {
|
|
176
|
+
if (!funcs.has(key) || seen.has(key)) return;
|
|
177
|
+
seen.add(key);
|
|
178
|
+
visit(refsOfFunc(key), [...path, key]);
|
|
179
|
+
};
|
|
180
|
+
const visit = (refs, path) => {
|
|
181
|
+
for (const name of refs.names) {
|
|
182
|
+
const owner = varOwner.get(name);
|
|
183
|
+
if (owner) {
|
|
184
|
+
if (!deps.has(owner)) deps.set(owner, [...path, name]);
|
|
185
|
+
} else follow(name, path);
|
|
186
|
+
}
|
|
187
|
+
for (const m of refs.methods) follow(m, path);
|
|
188
|
+
};
|
|
189
|
+
const refs = emptyRefs();
|
|
190
|
+
for (const v of entry.spec.value ?? []) collectRefs(v, ctx, refs);
|
|
191
|
+
visit(refs, []);
|
|
192
|
+
return deps;
|
|
193
|
+
};
|
|
194
|
+
return new Map(varEntries.map((e) => [e, depsOf(e)]));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// A chain of names leading from `start` back to itself through the specs in
|
|
198
|
+
// `pending`, e.g. ["a", "f", "b", "a"]; null when `start` is on no cycle.
|
|
199
|
+
function findCycle(start, depsOf, pending) {
|
|
200
|
+
const visited = new Set();
|
|
201
|
+
const walk = (entry, chain) => {
|
|
202
|
+
for (const [dep, path] of depsOf.get(entry)) {
|
|
203
|
+
const next = [...chain, ...path];
|
|
204
|
+
if (dep === start) return next;
|
|
205
|
+
if (!pending.has(dep) || visited.has(dep)) continue;
|
|
206
|
+
visited.add(dep);
|
|
207
|
+
const found = walk(dep, next);
|
|
208
|
+
if (found) return found;
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
};
|
|
212
|
+
const chain = walk(start, [start.names[0]]);
|
|
213
|
+
return chain && chain.length === 2 ? [chain[0]] : chain;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function cycleMessage(entry, chain) {
|
|
217
|
+
const name = entry.names[0];
|
|
218
|
+
if (!chain) return `initialization cycle for ${name}`;
|
|
219
|
+
if (chain.length === 1)
|
|
220
|
+
return `initialization cycle: ${name} refers to itself`;
|
|
221
|
+
return `initialization cycle for ${name}: ${chain.join(" refers to ")}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Go's rule: repeatedly take the earliest spec whose dependencies are done.
|
|
225
|
+
function orderVars(varEntries, depsOf) {
|
|
226
|
+
const done = new Set();
|
|
227
|
+
const vars = [];
|
|
228
|
+
const ready = (e) => {
|
|
229
|
+
for (const dep of depsOf.get(e).keys()) {
|
|
230
|
+
if (dep === e && e.names.length > 1) continue;
|
|
231
|
+
if (!done.has(dep)) return false;
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
};
|
|
235
|
+
const remaining = varEntries.slice();
|
|
236
|
+
while (remaining.length > 0) {
|
|
237
|
+
const idx = remaining.findIndex(ready);
|
|
238
|
+
if (idx === -1) {
|
|
239
|
+
// Everything left is on or behind a cycle: report the first spec
|
|
240
|
+
// that is on one.
|
|
241
|
+
const pending = new Set(remaining);
|
|
242
|
+
let culprit = remaining[0];
|
|
243
|
+
let chain = null;
|
|
244
|
+
for (const e of remaining) {
|
|
245
|
+
chain = findCycle(e, depsOf, pending);
|
|
246
|
+
if (chain) {
|
|
247
|
+
culprit = e;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
vars.push(...remaining);
|
|
252
|
+
return {
|
|
253
|
+
vars,
|
|
254
|
+
cycles: [{ spec: culprit.spec, message: cycleMessage(culprit, chain) }],
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const [e] = remaining.splice(idx, 1);
|
|
258
|
+
done.add(e);
|
|
259
|
+
vars.push(e);
|
|
260
|
+
}
|
|
261
|
+
return { vars, cycles: [] };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Constants depend on other constants only.
|
|
265
|
+
function orderConsts(constEntries) {
|
|
266
|
+
const constNames = new Set(constEntries.flatMap((e) => e.names));
|
|
267
|
+
const ctx = { pkgNames: constNames, funcs: new Map() };
|
|
268
|
+
const owner = new Map();
|
|
269
|
+
for (const e of constEntries) for (const n of e.names) owner.set(n, e);
|
|
270
|
+
const consts = [];
|
|
271
|
+
const state = new Set();
|
|
272
|
+
const visit = (e) => {
|
|
273
|
+
if (state.has(e)) return;
|
|
274
|
+
state.add(e);
|
|
275
|
+
const refs = emptyRefs();
|
|
276
|
+
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));
|
|
278
|
+
consts.push(e);
|
|
279
|
+
};
|
|
280
|
+
for (const e of constEntries) visit(e);
|
|
281
|
+
return consts;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @param {object[]} programs parsed (and, for method resolution, checked) files
|
|
286
|
+
* @returns {{ consts: object[], vars: object[], cycles: object[] }}
|
|
287
|
+
* `consts` / `vars`: entries `{ decl, spec, names, program }` in initialisation
|
|
288
|
+
* order; `cycles`: `{ spec, message }` for each initialisation cycle.
|
|
289
|
+
*/
|
|
290
|
+
export function orderPackageInits(programs) {
|
|
291
|
+
const decls = collectDecls(programs);
|
|
292
|
+
const depsOf = buildVarDeps(decls);
|
|
293
|
+
const { vars, cycles } = orderVars(decls.varEntries, depsOf);
|
|
294
|
+
return { consts: orderConsts(decls.constEntries), vars, cycles };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Package-level `var` specs in the order their initialisers must be *typed*:
|
|
299
|
+
* a spec that names another package variable (directly, even inside a func
|
|
300
|
+
* literal) comes after it, since `var a = b + 1; var b = f()` infers `a` from
|
|
301
|
+
* `b`. Declaration order is kept otherwise.
|
|
302
|
+
*/
|
|
303
|
+
export function orderVarSpecsForTyping(programs) {
|
|
304
|
+
const entries = [];
|
|
305
|
+
for (const program of programs)
|
|
306
|
+
for (const d of program.decls ?? [])
|
|
307
|
+
if (d.kind === "VarDecl")
|
|
308
|
+
for (const spec of d.decls ?? [])
|
|
309
|
+
entries.push({ program, decl: d, spec });
|
|
310
|
+
const owner = new Map();
|
|
311
|
+
for (const e of entries) for (const n of e.spec.names) owner.set(n, e);
|
|
312
|
+
const ctx = { pkgNames: new Set(owner.keys()), funcs: new Map() };
|
|
313
|
+
const out = [];
|
|
314
|
+
const state = new Set();
|
|
315
|
+
const visit = (e) => {
|
|
316
|
+
if (state.has(e)) return;
|
|
317
|
+
state.add(e);
|
|
318
|
+
if (!e.spec.type) {
|
|
319
|
+
const refs = emptyRefs();
|
|
320
|
+
for (const v of e.spec.value ?? []) collectRefs(v, ctx, refs);
|
|
321
|
+
for (const n of refs.names) {
|
|
322
|
+
const o = owner.get(n);
|
|
323
|
+
if (o && o !== e) visit(o);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
out.push(e);
|
|
327
|
+
};
|
|
328
|
+
for (const e of entries) visit(e);
|
|
329
|
+
return out;
|
|
330
|
+
}
|