@nudojs/core 0.3.0 → 0.4.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/dist/chunk-N7E2A7IK.js +7216 -0
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +174 -0
- package/dist/index-DgyudAkw.d.ts +911 -0
- package/dist/index.d.ts +64 -710
- package/dist/index.js +3269 -8426
- package/package.json +18 -5
- package/CHANGELOG.md +0 -34
- package/src/__tests__/environment.test.ts +0 -48
- package/src/__tests__/fn-sig-impl.test.ts +0 -57
- package/src/__tests__/ops-refined.test.ts +0 -196
- package/src/__tests__/ops.test.ts +0 -84
- package/src/__tests__/type-value-phase2.test.ts +0 -117
- package/src/__tests__/type-value-phase3.test.ts +0 -94
- package/src/__tests__/type-value-refined.test.ts +0 -210
- package/src/__tests__/type-value.test.ts +0 -319
- package/src/algebra/__tests__/abs-native.test.ts +0 -176
- package/src/algebra/__tests__/arithmetic.test.ts +0 -170
- package/src/algebra/__tests__/ast-eval.test.ts +0 -154
- package/src/algebra/__tests__/bridge.test.ts +0 -160
- package/src/algebra/__tests__/call-budget.test.ts +0 -117
- package/src/algebra/__tests__/check-case-consistency.test.ts +0 -69
- package/src/algebra/__tests__/check-gold.test.ts +0 -290
- package/src/algebra/__tests__/check-json.test.ts +0 -57
- package/src/algebra/__tests__/check-mini-repo.test.ts +0 -49
- package/src/algebra/__tests__/check-real-commander.test.ts +0 -43
- package/src/algebra/__tests__/check-real-packages.test.ts +0 -165
- package/src/algebra/__tests__/check-recall-gold.test.ts +0 -1024
- package/src/algebra/__tests__/check-require-dep-memo.test.ts +0 -45
- package/src/algebra/__tests__/check-return-contract.test.ts +0 -90
- package/src/algebra/__tests__/check-shape-gold.test.ts +0 -303
- package/src/algebra/__tests__/check-source-memo.test.ts +0 -204
- package/src/algebra/__tests__/check.test.ts +0 -69
- package/src/algebra/__tests__/denote.test.ts +0 -77
- package/src/algebra/__tests__/diagnostics.test.ts +0 -64
- package/src/algebra/__tests__/differential.test.ts +0 -124
- package/src/algebra/__tests__/div-mod.test.ts +0 -38
- package/src/algebra/__tests__/example0.test.ts +0 -103
- package/src/algebra/__tests__/exec-analyze-throws.test.ts +0 -60
- package/src/algebra/__tests__/exec-async.test.ts +0 -46
- package/src/algebra/__tests__/exec-bpath.test.ts +0 -193
- package/src/algebra/__tests__/exec-class.test.ts +0 -56
- package/src/algebra/__tests__/exec-e2e.test.ts +0 -97
- package/src/algebra/__tests__/exec-generator.test.ts +0 -53
- package/src/algebra/__tests__/exec-inherit-default.test.ts +0 -111
- package/src/algebra/__tests__/exec-optional-nested.test.ts +0 -87
- package/src/algebra/__tests__/exec-rest.test.ts +0 -55
- package/src/algebra/__tests__/exec-spread-forof.test.ts +0 -85
- package/src/algebra/__tests__/exec-static-computed.test.ts +0 -61
- package/src/algebra/__tests__/exec-switch.test.ts +0 -74
- package/src/algebra/__tests__/exec-trycatch.test.ts +0 -71
- package/src/algebra/__tests__/fn-fp-memo.test.ts +0 -166
- package/src/algebra/__tests__/fn-fp-multidecl.test.ts +0 -49
- package/src/algebra/__tests__/format.test.ts +0 -34
- package/src/algebra/__tests__/generalize-memo.test.ts +0 -285
- package/src/algebra/__tests__/generalize.test.ts +0 -113
- package/src/algebra/__tests__/hash-source.test.ts +0 -19
- package/src/algebra/__tests__/hof.test.ts +0 -80
- package/src/algebra/__tests__/inlay.test.ts +0 -30
- package/src/algebra/__tests__/language.test.ts +0 -77
- package/src/algebra/__tests__/leq.test.ts +0 -109
- package/src/algebra/__tests__/load-deps-fp-memo.test.ts +0 -132
- package/src/algebra/__tests__/methods.test.ts +0 -37
- package/src/algebra/__tests__/node-types.test.ts +0 -26
- package/src/algebra/__tests__/nudo-constraints.ts +0 -46
- package/src/algebra/__tests__/objects.test.ts +0 -117
- package/src/algebra/__tests__/parse-source-cache.test.ts +0 -42
- package/src/algebra/__tests__/refine-import.test.ts +0 -109
- package/src/algebra/__tests__/stable-source-key-block.test.ts +0 -23
- package/src/algebra/__tests__/stable-source-key.test.ts +0 -31
- package/src/algebra/__tests__/surface.test.ts +0 -41
- package/src/algebra/__tests__/this-inherit.test.ts +0 -130
- package/src/algebra/abs-fn.ts +0 -49
- package/src/algebra/abs-modules.ts +0 -139
- package/src/algebra/abs.ts +0 -206
- package/src/algebra/arithmetic.ts +0 -691
- package/src/algebra/ast-eval.ts +0 -1763
- package/src/algebra/bridge.ts +0 -350
- package/src/algebra/builtins.ts +0 -373
- package/src/algebra/check-report.ts +0 -150
- package/src/algebra/check.ts +0 -1867
- package/src/algebra/constraint.ts +0 -304
- package/src/algebra/denote.ts +0 -166
- package/src/algebra/diagnostics.ts +0 -210
- package/src/algebra/exec/call.ts +0 -29
- package/src/algebra/exec/calls.ts +0 -96
- package/src/algebra/exec/class-registry.ts +0 -29
- package/src/algebra/exec/class.ts +0 -329
- package/src/algebra/exec/index.ts +0 -6
- package/src/algebra/exec/member-diag.ts +0 -141
- package/src/algebra/exec/run.ts +0 -341
- package/src/algebra/exec/runtime.ts +0 -583
- package/src/algebra/exec/transpile.ts +0 -993
- package/src/algebra/fn-fp.ts +0 -294
- package/src/algebra/format.ts +0 -91
- package/src/algebra/generalize.ts +0 -776
- package/src/algebra/hash-source.ts +0 -27
- package/src/algebra/index.ts +0 -34
- package/src/algebra/inlay.ts +0 -198
- package/src/algebra/language.ts +0 -289
- package/src/algebra/leak.ts +0 -68
- package/src/algebra/leq.ts +0 -301
- package/src/algebra/load-deps-fp.ts +0 -97
- package/src/algebra/methods.ts +0 -128
- package/src/algebra/objects.ts +0 -251
- package/src/algebra/parse-source.ts +0 -64
- package/src/algebra/phi.ts +0 -42
- package/src/algebra/pred.ts +0 -395
- package/src/algebra/refine.ts +0 -227
- package/src/algebra/stable-source-key.ts +0 -81
- package/src/algebra/surface.ts +0 -206
- package/src/algebra/template.ts +0 -143
- package/src/algebra/term.ts +0 -119
- package/src/environment.ts +0 -74
- package/src/index.ts +0 -73
- package/src/mock-helpers.ts +0 -166
- package/src/ops.ts +0 -191
- package/src/refinements/range.ts +0 -86
- package/src/refinements/template-predicates.ts +0 -11
- package/src/refinements/template.ts +0 -176
- package/src/strip-types.ts +0 -236
- package/src/type-value.ts +0 -513
- package/tsconfig.json +0 -8
package/src/algebra/fn-fp.ts
DELETED
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* per-function fingerprint: after-edit only dirty fn + callers miss L0.
|
|
3
|
-
*
|
|
4
|
-
* - own: function declaration slice (incl. leading comments / refine)
|
|
5
|
-
* - deps: transitively referenced sibling own hashes + non-fn top-level context
|
|
6
|
-
* - context change (import/class/const...) -> all miss (conservative)
|
|
7
|
-
*
|
|
8
|
-
* Multi-declarator `const a = fn, b = fn` yields one fingerprint per function;
|
|
9
|
-
* each own-slice is its own declarator (first also carries statement leading comments).
|
|
10
|
-
*/
|
|
11
|
-
import type { Node, File } from "@babel/types";
|
|
12
|
-
import { hashSource } from "./hash-source.ts";
|
|
13
|
-
|
|
14
|
-
export type FnFp = { own: string; deps: string; nRefs: number };
|
|
15
|
-
|
|
16
|
-
/** source → per-fn fingerprints. Key is full source (collision-safe); cap keeps
|
|
17
|
-
* retained text bounded: 24 × 1MB ≈ 24MB worst-case key strings. */
|
|
18
|
-
const fnFpCache = new Map<string, Map<string, FnFp>>();
|
|
19
|
-
const MAX_FN_FP_CACHE = 24;
|
|
20
|
-
/** Cap on cached whole-source keys so large monorepo sessions stay bounded. */
|
|
21
|
-
const MAX_FN_FP_SOURCE_CHARS = 1_000_000;
|
|
22
|
-
|
|
23
|
-
export function resetFnFpCache(): void {
|
|
24
|
-
fnFpCache.clear();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
type FnDeclInfo = {
|
|
28
|
-
name: string;
|
|
29
|
-
body: Node;
|
|
30
|
-
/** Function/arrow node (own-slice end; also scanned for sibling refs incl. defaults). */
|
|
31
|
-
fnNode: Node;
|
|
32
|
-
/** Declarator start when from a VariableDeclaration; else undefined. */
|
|
33
|
-
declaratorStart?: number;
|
|
34
|
-
/** First leading-comment start on this declarator (multi-decl refine/@nudo). */
|
|
35
|
-
declaratorLeadStart?: number;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/** Every top-level function binding in a declaration node (not just the first). */
|
|
39
|
-
export function getFnNameAndBodies(decl: Node): FnDeclInfo[] {
|
|
40
|
-
const out: FnDeclInfo[] = [];
|
|
41
|
-
if (decl.type === "FunctionDeclaration" && decl.id) {
|
|
42
|
-
out.push({ name: decl.id.name, body: decl.body, fnNode: decl });
|
|
43
|
-
return out;
|
|
44
|
-
}
|
|
45
|
-
if (decl.type === "VariableDeclaration") {
|
|
46
|
-
for (const d of decl.declarations) {
|
|
47
|
-
if (
|
|
48
|
-
d.id.type === "Identifier" &&
|
|
49
|
-
d.init &&
|
|
50
|
-
(d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression")
|
|
51
|
-
) {
|
|
52
|
-
const comments = (d as { leadingComments?: Array<{ start?: number }> }).leadingComments;
|
|
53
|
-
const first = comments?.[0];
|
|
54
|
-
out.push({
|
|
55
|
-
name: d.id.name,
|
|
56
|
-
body: d.init.body,
|
|
57
|
-
fnNode: d.init,
|
|
58
|
-
declaratorStart: d.start ?? undefined,
|
|
59
|
-
declaratorLeadStart: typeof first?.start === "number" ? first.start : undefined,
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return out;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function leadingSliceStart(stmt: Node, decl: Node): number {
|
|
68
|
-
const comments =
|
|
69
|
-
(stmt as { leadingComments?: Array<{ start?: number }> }).leadingComments ??
|
|
70
|
-
(decl as { leadingComments?: Array<{ start?: number }> }).leadingComments;
|
|
71
|
-
const first = comments?.[0];
|
|
72
|
-
if (first && typeof first.start === "number") return first.start;
|
|
73
|
-
return stmt.start ?? decl.start ?? 0;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Conservative: any top-level sibling name appearing in the body is a dep. */
|
|
77
|
-
function collectSiblingRefs(node: unknown, known: Set<string>, out: Set<string>): void {
|
|
78
|
-
if (!node || typeof node !== "object") return;
|
|
79
|
-
const obj = node as Record<string, unknown> & { type?: string };
|
|
80
|
-
if (obj.type === "Identifier" && typeof obj.name === "string" && known.has(obj.name)) {
|
|
81
|
-
out.add(obj.name);
|
|
82
|
-
}
|
|
83
|
-
for (const key of Object.keys(obj)) {
|
|
84
|
-
if (
|
|
85
|
-
key === "loc" ||
|
|
86
|
-
key === "start" ||
|
|
87
|
-
key === "end" ||
|
|
88
|
-
key === "leadingComments" ||
|
|
89
|
-
key === "trailingComments" ||
|
|
90
|
-
key === "innerComments"
|
|
91
|
-
) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (
|
|
95
|
-
(key === "key" || key === "property") &&
|
|
96
|
-
(obj.type === "MemberExpression" ||
|
|
97
|
-
obj.type === "ObjectProperty" ||
|
|
98
|
-
obj.type === "ObjectMethod" ||
|
|
99
|
-
obj.type === "ClassMethod" ||
|
|
100
|
-
obj.type === "ClassProperty") &&
|
|
101
|
-
obj.computed !== true
|
|
102
|
-
) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
// Binding name, not a use: `function id() {}` / `class id {}` must not
|
|
106
|
-
// count as a sibling ref (would make nRefs≥1 for every named decl and
|
|
107
|
-
// permanently disable canSkipLiteralCallScan).
|
|
108
|
-
if (
|
|
109
|
-
key === "id" &&
|
|
110
|
-
(obj.type === "FunctionDeclaration" ||
|
|
111
|
-
obj.type === "FunctionExpression" ||
|
|
112
|
-
obj.type === "ClassDeclaration" ||
|
|
113
|
-
obj.type === "ClassExpression")
|
|
114
|
-
) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
const v = obj[key];
|
|
118
|
-
if (Array.isArray(v)) {
|
|
119
|
-
for (const x of v) collectSiblingRefs(x, known, out);
|
|
120
|
-
} else if (v && typeof v === "object") {
|
|
121
|
-
collectSiblingRefs(v, known, out);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function computeFnFingerprints(source: string, file: File): Map<string, FnFp> {
|
|
127
|
-
const top: Array<{ name: string; slice: string; fnNode: Node }> = [];
|
|
128
|
-
const contextParts: string[] = [];
|
|
129
|
-
|
|
130
|
-
for (const stmt of file.program.body) {
|
|
131
|
-
let decl: Node = stmt;
|
|
132
|
-
if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
|
|
133
|
-
decl = stmt.declaration;
|
|
134
|
-
} else if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration) {
|
|
135
|
-
decl = stmt.declaration;
|
|
136
|
-
}
|
|
137
|
-
const fnInfos = getFnNameAndBodies(decl);
|
|
138
|
-
if (fnInfos.length > 0) {
|
|
139
|
-
const lead = leadingSliceStart(stmt, decl);
|
|
140
|
-
for (let i = 0; i < fnInfos.length; i++) {
|
|
141
|
-
const info = fnInfos[i]!;
|
|
142
|
-
// First declarator carries statement leading comments; later ones start
|
|
143
|
-
// at their own leading comments so sibling edits do not dirty this own-hash.
|
|
144
|
-
const sliceStart =
|
|
145
|
-
i === 0
|
|
146
|
-
? lead
|
|
147
|
-
: (info.declaratorLeadStart ??
|
|
148
|
-
info.declaratorStart ??
|
|
149
|
-
info.fnNode.start ??
|
|
150
|
-
lead);
|
|
151
|
-
const end = info.fnNode.end ?? info.declaratorStart ?? decl.end ?? stmt.end ?? sliceStart;
|
|
152
|
-
top.push({
|
|
153
|
-
name: info.name,
|
|
154
|
-
slice: source.slice(sliceStart, Math.max(end, sliceStart)),
|
|
155
|
-
fnNode: info.fnNode,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
} else if (typeof stmt.start === "number" && typeof stmt.end === "number") {
|
|
159
|
-
contextParts.push(source.slice(stmt.start, stmt.end));
|
|
160
|
-
} else {
|
|
161
|
-
contextParts.push(stmt.type);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const context = hashSource(contextParts.join("\n "));
|
|
166
|
-
const names = new Set(top.map((t) => t.name));
|
|
167
|
-
const own = new Map<string, string>();
|
|
168
|
-
for (const t of top) own.set(t.name, hashSource(t.slice));
|
|
169
|
-
|
|
170
|
-
const edges = new Map<string, Set<string>>();
|
|
171
|
-
for (const t of top) {
|
|
172
|
-
const refs = new Set<string>();
|
|
173
|
-
// Whole fn node (params defaults + body), not body alone
|
|
174
|
-
collectSiblingRefs(t.fnNode, names, refs);
|
|
175
|
-
edges.set(t.name, refs);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const result = new Map<string, FnFp>();
|
|
179
|
-
for (const t of top) {
|
|
180
|
-
const seen = new Set<string>();
|
|
181
|
-
const stack = [...(edges.get(t.name) ?? [])];
|
|
182
|
-
const depParts: string[] = [];
|
|
183
|
-
while (stack.length > 0) {
|
|
184
|
-
const cur = stack.pop()!;
|
|
185
|
-
if (seen.has(cur)) continue;
|
|
186
|
-
seen.add(cur);
|
|
187
|
-
depParts.push(`${cur}=${own.get(cur) ?? "?"}`);
|
|
188
|
-
for (const next of edges.get(cur) ?? []) {
|
|
189
|
-
if (!seen.has(next)) stack.push(next);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
depParts.sort();
|
|
193
|
-
result.set(t.name, {
|
|
194
|
-
own: own.get(t.name)!,
|
|
195
|
-
deps: hashSource(`${depParts.join(",")} ${context}`),
|
|
196
|
-
nRefs: seen.size,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
return result;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/** source -> (fnName -> own/deps); LRU */
|
|
203
|
-
export function fnFingerprints(source: string, file: File): Map<string, FnFp> {
|
|
204
|
-
if (source.length > MAX_FN_FP_SOURCE_CHARS) {
|
|
205
|
-
return computeFnFingerprints(source, file);
|
|
206
|
-
}
|
|
207
|
-
const hit = fnFpCache.get(source);
|
|
208
|
-
if (hit !== undefined) {
|
|
209
|
-
fnFpCache.delete(source);
|
|
210
|
-
fnFpCache.set(source, hit);
|
|
211
|
-
return hit;
|
|
212
|
-
}
|
|
213
|
-
const fp = computeFnFingerprints(source, file);
|
|
214
|
-
if (fnFpCache.size >= MAX_FN_FP_CACHE) {
|
|
215
|
-
const oldest = fnFpCache.keys().next().value;
|
|
216
|
-
if (oldest !== undefined) fnFpCache.delete(oldest);
|
|
217
|
-
}
|
|
218
|
-
fnFpCache.set(source, fp);
|
|
219
|
-
return fp;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** L0 source part: per-fn fingerprint when AST available; else full-file hash */
|
|
223
|
-
export function generalizeSourceKeyPart(
|
|
224
|
-
source: string,
|
|
225
|
-
fnName: string,
|
|
226
|
-
file?: File,
|
|
227
|
-
): string {
|
|
228
|
-
if (!file) return `w:${hashSource(source)}`;
|
|
229
|
-
const fp = fnFingerprints(source, file).get(fnName);
|
|
230
|
-
if (fp) return `f:${fp.own}:${fp.deps}`;
|
|
231
|
-
return `u:${hashSource(source)}`;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/** Any CallExpression / NewExpression / dynamic import in the AST. */
|
|
235
|
-
function hasAnyCallLike(node: unknown, depth = 0): boolean {
|
|
236
|
-
if (!node || typeof node !== "object") return false;
|
|
237
|
-
// Depth cap: unexplored subtree must be treated as "may contain a call"
|
|
238
|
-
// (return true). Returning false would let canSkipLiteralCallScan skip
|
|
239
|
-
// scanLiteralCalls and miss constraint violations in deep expressions.
|
|
240
|
-
if (depth > 80) return true;
|
|
241
|
-
const obj = node as Record<string, unknown> & { type?: string };
|
|
242
|
-
if (
|
|
243
|
-
obj.type === "CallExpression" ||
|
|
244
|
-
obj.type === "NewExpression" ||
|
|
245
|
-
obj.type === "OptionalCallExpression" ||
|
|
246
|
-
obj.type === "ImportExpression"
|
|
247
|
-
) {
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
|
-
for (const key of Object.keys(obj)) {
|
|
251
|
-
if (
|
|
252
|
-
key === "loc" ||
|
|
253
|
-
key === "start" ||
|
|
254
|
-
key === "end" ||
|
|
255
|
-
key === "leadingComments" ||
|
|
256
|
-
key === "trailingComments" ||
|
|
257
|
-
key === "innerComments"
|
|
258
|
-
) {
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
const v = obj[key];
|
|
262
|
-
if (Array.isArray(v)) {
|
|
263
|
-
for (const x of v) if (hasAnyCallLike(x, depth + 1)) return true;
|
|
264
|
-
} else if (v && typeof v === "object") {
|
|
265
|
-
if (hasAnyCallLike(v, depth + 1)) return true;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function hasModuleSyntax(file: File): boolean {
|
|
272
|
-
for (const stmt of file.program.body) {
|
|
273
|
-
if (stmt.type === "ImportDeclaration") return true;
|
|
274
|
-
if (stmt.type === "ExportAllDeclaration") return true;
|
|
275
|
-
if (stmt.type === "ExportNamedDeclaration" && stmt.source) return true;
|
|
276
|
-
}
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* 无 refine、无模块语法、无兄弟调用、无任何调用表达式 → scanLiteralCalls 可整段跳过。
|
|
282
|
-
* 必须保守:`const x = f(-1)` / `if (f(-1))` / `require("x")` 都含 CallExpression,
|
|
283
|
-
* 只认顶层 ExpressionStatement 会漏报约束违规。
|
|
284
|
-
*/
|
|
285
|
-
export function canSkipLiteralCallScan(source: string, file: File): boolean {
|
|
286
|
-
if (source.includes("@nudo:refine")) return false;
|
|
287
|
-
if (hasModuleSyntax(file)) return false;
|
|
288
|
-
if (hasAnyCallLike(file)) return false;
|
|
289
|
-
const fps = fnFingerprints(source, file);
|
|
290
|
-
for (const fp of fps.values()) {
|
|
291
|
-
if (fp.nRefs > 0) return false;
|
|
292
|
-
}
|
|
293
|
-
return true;
|
|
294
|
-
}
|
package/src/algebra/format.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 格式化:把 Abs 渲染成人类可读 / CLI 友好的字符串。
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { Abs } from "./abs.ts";
|
|
6
|
-
import { litValue } from "./abs.ts";
|
|
7
|
-
import type { Term } from "./term.ts";
|
|
8
|
-
import { termToString } from "./term.ts";
|
|
9
|
-
import type { Pred } from "./pred.ts";
|
|
10
|
-
import { predToString } from "./pred.ts";
|
|
11
|
-
|
|
12
|
-
export type FormatOptions = {
|
|
13
|
-
/** 是否显示 term= */
|
|
14
|
-
showTerm?: boolean;
|
|
15
|
-
/** 是否显示 pred */
|
|
16
|
-
showPred?: boolean;
|
|
17
|
-
indent?: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export function formatAbs(a: Abs, opts: FormatOptions = {}): string {
|
|
21
|
-
const showTerm = opts.showTerm !== false;
|
|
22
|
-
const showPred = opts.showPred !== false;
|
|
23
|
-
const parts: string[] = [];
|
|
24
|
-
|
|
25
|
-
parts.push(formatShape(a));
|
|
26
|
-
|
|
27
|
-
if (showTerm && a.term && a.term.op !== "lit") {
|
|
28
|
-
parts.push(`= ${termToString(a.term)}`);
|
|
29
|
-
}
|
|
30
|
-
if (showPred && a.pred && a.pred.op !== "true") {
|
|
31
|
-
parts.push(`where ${predToString(a.pred)}`);
|
|
32
|
-
}
|
|
33
|
-
parts.push(`#${a.conf}`);
|
|
34
|
-
return parts.join(" ");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function formatShape(a: Abs): string {
|
|
38
|
-
const s = a.shape;
|
|
39
|
-
switch (s.k) {
|
|
40
|
-
case "never":
|
|
41
|
-
return "never";
|
|
42
|
-
case "any":
|
|
43
|
-
return "any";
|
|
44
|
-
case "unknown":
|
|
45
|
-
return "unknown";
|
|
46
|
-
case "prim": {
|
|
47
|
-
const lv = litValue(a);
|
|
48
|
-
if (lv !== undefined) return JSON.stringify(lv);
|
|
49
|
-
return s.type;
|
|
50
|
-
}
|
|
51
|
-
case "obj": {
|
|
52
|
-
const entries = Object.entries(s.slots).map(([k, slot]) => {
|
|
53
|
-
const opt = slot.optional ? "?" : "";
|
|
54
|
-
return `${k}${opt}: ${formatShape(slot.value)}`;
|
|
55
|
-
});
|
|
56
|
-
return `{ ${entries.join(", ")} }`;
|
|
57
|
-
}
|
|
58
|
-
case "arr":
|
|
59
|
-
return `${formatShape(s.element)}[]`;
|
|
60
|
-
case "tuple":
|
|
61
|
-
return `[${s.elements.map(formatShape).join(", ")}]`;
|
|
62
|
-
case "fn": {
|
|
63
|
-
const ret =
|
|
64
|
-
s.returnType !== undefined ? formatShape(s.returnType) : "?";
|
|
65
|
-
return `(${s.params.join(", ")}) => ${ret}`;
|
|
66
|
-
}
|
|
67
|
-
case "brand":
|
|
68
|
-
return `${s.name}`;
|
|
69
|
-
case "eff":
|
|
70
|
-
return `${s.eff}<${formatShape(s.inner)}>`;
|
|
71
|
-
case "sum":
|
|
72
|
-
return s.members.map(formatShape).join(" | ");
|
|
73
|
-
default:
|
|
74
|
-
return "·";
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** 多行展示,CLI 用 */
|
|
79
|
-
export function formatAbsMultiline(a: Abs, label?: string): string {
|
|
80
|
-
const lines: string[] = [];
|
|
81
|
-
if (label) lines.push(label);
|
|
82
|
-
lines.push(` ${formatShape(a)}`);
|
|
83
|
-
if (a.term && a.term.op !== "lit") {
|
|
84
|
-
lines.push(` term: ${termToString(a.term)}`);
|
|
85
|
-
}
|
|
86
|
-
if (a.pred && a.pred.op !== "true") {
|
|
87
|
-
lines.push(` pred: ${predToString(a.pred)}`);
|
|
88
|
-
}
|
|
89
|
-
lines.push(` conf: ${a.conf}`);
|
|
90
|
-
return lines.join("\n");
|
|
91
|
-
}
|