@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
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
generalizeFromAst,
|
|
4
|
-
resetGeneralizeMemo,
|
|
5
|
-
getGeneralizeMemoSize,
|
|
6
|
-
evictGeneralizeMemoForPaths,
|
|
7
|
-
formatAbs,
|
|
8
|
-
checkSource,
|
|
9
|
-
pTrue,
|
|
10
|
-
numLit,
|
|
11
|
-
numVar,
|
|
12
|
-
gtNum,
|
|
13
|
-
and,
|
|
14
|
-
v,
|
|
15
|
-
litValue,
|
|
16
|
-
strLit,
|
|
17
|
-
termToString,
|
|
18
|
-
} from "../index.ts";
|
|
19
|
-
|
|
20
|
-
const SRC = `
|
|
21
|
-
function add(a, b) {
|
|
22
|
-
return a + b;
|
|
23
|
-
}
|
|
24
|
-
`;
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
resetGeneralizeMemo();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
describe("generalizeFromAst L0 memo", () => {
|
|
31
|
-
it("returns the same PolyFn instance for identical (source, name) queries", () => {
|
|
32
|
-
const a = generalizeFromAst("add", SRC);
|
|
33
|
-
const b = generalizeFromAst("add", SRC);
|
|
34
|
-
expect(a).toBeDefined();
|
|
35
|
-
expect(b).toBe(a);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("misses when source content changes", () => {
|
|
39
|
-
const a = generalizeFromAst("add", SRC);
|
|
40
|
-
const b = generalizeFromAst("add", SRC + "\n// touch\n");
|
|
41
|
-
expect(a).toBeDefined();
|
|
42
|
-
expect(b).toBeDefined();
|
|
43
|
-
expect(b).not.toBe(a);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("misses for a different function name", () => {
|
|
47
|
-
const src = `
|
|
48
|
-
function add(a, b) { return a + b; }
|
|
49
|
-
function sub(a, b) { return a - b; }
|
|
50
|
-
`;
|
|
51
|
-
const a = generalizeFromAst("add", src);
|
|
52
|
-
const b = generalizeFromAst("sub", src);
|
|
53
|
-
expect(a).toBeDefined();
|
|
54
|
-
expect(b).toBeDefined();
|
|
55
|
-
expect(b).not.toBe(a);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("caches undefined for missing functions", () => {
|
|
59
|
-
expect(generalizeFromAst("nope", SRC)).toBeUndefined();
|
|
60
|
-
expect(generalizeFromAst("nope", SRC)).toBeUndefined();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("misses when label changes", () => {
|
|
64
|
-
const a = generalizeFromAst("add", SRC, { label: "A" });
|
|
65
|
-
const b = generalizeFromAst("add", SRC, { label: "B" });
|
|
66
|
-
expect(a).toBeDefined();
|
|
67
|
-
expect(b).toBeDefined();
|
|
68
|
-
expect(b).not.toBe(a);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("misses when refine loadModule identity changes", () => {
|
|
72
|
-
const load1 = () => undefined;
|
|
73
|
-
const load2 = () => undefined;
|
|
74
|
-
const a = generalizeFromAst("add", SRC, { refine: { loadModule: load1, fromFile: "/t.js" } });
|
|
75
|
-
const b = generalizeFromAst("add", SRC, { refine: { loadModule: load2, fromFile: "/t.js" } });
|
|
76
|
-
expect(a).toBeDefined();
|
|
77
|
-
expect(b).toBeDefined();
|
|
78
|
-
expect(b).not.toBe(a);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("hit is consistent with checkSource signatures", () => {
|
|
82
|
-
const g1 = generalizeFromAst("add", SRC);
|
|
83
|
-
const g2 = generalizeFromAst("add", SRC);
|
|
84
|
-
expect(formatAbs(g1!.symbolic)).toBe(formatAbs(g2!.symbolic));
|
|
85
|
-
const report = checkSource("t.js", SRC, pTrue, {});
|
|
86
|
-
expect(report.signatures.map((s) => s.name)).toContain("add");
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe("instantiate L1 memo", () => {
|
|
91
|
-
const SRC_SCALE = `
|
|
92
|
-
function scale(x) {
|
|
93
|
-
return x + 1;
|
|
94
|
-
}
|
|
95
|
-
function pair(a, b) {
|
|
96
|
-
return { a, b };
|
|
97
|
-
}
|
|
98
|
-
`;
|
|
99
|
-
|
|
100
|
-
it("returns the same Abs instance for identical args + Φ", () => {
|
|
101
|
-
const g = generalizeFromAst("scale", SRC_SCALE)!;
|
|
102
|
-
const a1 = numVar("x", gtNum(v("x"), 0));
|
|
103
|
-
const phi = gtNum(v("x"), 0);
|
|
104
|
-
const r1 = g.instantiate([a1], phi);
|
|
105
|
-
const r2 = g.instantiate([numVar("x", gtNum(v("x"), 0))], phi);
|
|
106
|
-
expect(r2).toBe(r1);
|
|
107
|
-
expect(r1.term).toBeDefined();
|
|
108
|
-
expect(termToString(r1.term!)).toBe("(x + 1)");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("hits for repeated literal args", () => {
|
|
112
|
-
const g = generalizeFromAst("scale", SRC_SCALE)!;
|
|
113
|
-
const r1 = g.instantiate([numLit(5)]);
|
|
114
|
-
const r2 = g.instantiate([numLit(5)]);
|
|
115
|
-
expect(litValue(r1)).toBe(6);
|
|
116
|
-
expect(r2).toBe(r1);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it("misses for different args", () => {
|
|
120
|
-
const g = generalizeFromAst("scale", SRC_SCALE)!;
|
|
121
|
-
const r1 = g.instantiate([numLit(5)]);
|
|
122
|
-
const r2 = g.instantiate([numLit(7)]);
|
|
123
|
-
expect(litValue(r1)).toBe(6);
|
|
124
|
-
expect(litValue(r2)).toBe(8);
|
|
125
|
-
expect(r2).not.toBe(r1);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("misses when Φ differs under same arg shape", () => {
|
|
129
|
-
const g = generalizeFromAst("scale", SRC_SCALE)!;
|
|
130
|
-
const arg = numVar("x", gtNum(v("x"), 0));
|
|
131
|
-
const r1 = g.instantiate([arg], gtNum(v("x"), 0));
|
|
132
|
-
const r2 = g.instantiate([arg], pTrue);
|
|
133
|
-
// 结果可能同构,但键不同不得错误复用为同一对象(允许 equal,禁止错缓存)
|
|
134
|
-
expect(formatAbs(r1)).toBeTypeOf("string");
|
|
135
|
-
expect(formatAbs(r2)).toBeTypeOf("string");
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("shares L1 hits across L0 PolyFn identity", () => {
|
|
139
|
-
const g1 = generalizeFromAst("scale", SRC_SCALE)!;
|
|
140
|
-
const g2 = generalizeFromAst("scale", SRC_SCALE)!;
|
|
141
|
-
expect(g2).toBe(g1);
|
|
142
|
-
const r = g1.instantiate([numLit(2)]);
|
|
143
|
-
expect(g2.instantiate([numLit(2)])).toBe(r);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("object args key by sorted slots (order-insensitive)", () => {
|
|
147
|
-
const g = generalizeFromAst("pair", SRC_SCALE)!;
|
|
148
|
-
const o1 = {
|
|
149
|
-
shape: {
|
|
150
|
-
k: "obj" as const,
|
|
151
|
-
slots: {
|
|
152
|
-
a: { value: numLit(1) },
|
|
153
|
-
b: { value: strLit("x") },
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
conf: "exact" as const,
|
|
157
|
-
};
|
|
158
|
-
const o2 = {
|
|
159
|
-
shape: {
|
|
160
|
-
k: "obj" as const,
|
|
161
|
-
slots: {
|
|
162
|
-
b: { value: strLit("x") },
|
|
163
|
-
a: { value: numLit(1) },
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
conf: "exact" as const,
|
|
167
|
-
};
|
|
168
|
-
const r1 = g.instantiate([o1, numLit(9)]);
|
|
169
|
-
// 第二参不同 → miss;同结构第一参应能命中同一对第一个参数的缓存路径
|
|
170
|
-
const r2 = g.instantiate([o2, numLit(9)]);
|
|
171
|
-
expect(formatAbs(r1)).toContain("a");
|
|
172
|
-
// 同键(slot 顺序无关)应返回同一缓存对象
|
|
173
|
-
expect(r2).toBe(r1);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it("resetGeneralizeMemo drops L0 and thus L1 tables", () => {
|
|
177
|
-
const g1 = generalizeFromAst("scale", SRC_SCALE)!;
|
|
178
|
-
const r1 = g1.instantiate([numLit(5)]);
|
|
179
|
-
resetGeneralizeMemo();
|
|
180
|
-
const g2 = generalizeFromAst("scale", SRC_SCALE)!;
|
|
181
|
-
expect(g2).not.toBe(g1);
|
|
182
|
-
const r2 = g2.instantiate([numLit(5)]);
|
|
183
|
-
expect(litValue(r2)).toBe(6);
|
|
184
|
-
expect(r2).not.toBe(r1);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
describe("L2 α-equivalence + pred canonicalization", () => {
|
|
189
|
-
const SRC_ID = `
|
|
190
|
-
function id(x) {
|
|
191
|
-
return x;
|
|
192
|
-
}
|
|
193
|
-
function inc(x) {
|
|
194
|
-
return x + 1;
|
|
195
|
-
}
|
|
196
|
-
`;
|
|
197
|
-
|
|
198
|
-
it("hits across renamed free vars and rewrites result vars", () => {
|
|
199
|
-
const g = generalizeFromAst("inc", SRC_ID)!;
|
|
200
|
-
const r1 = g.instantiate([numVar("x", gtNum(v("x"), 0))], gtNum(v("x"), 0));
|
|
201
|
-
const r2 = g.instantiate([numVar("y", gtNum(v("y"), 0))], gtNum(v("y"), 0));
|
|
202
|
-
expect(termToString(r1.term!)).toBe("(x + 1)");
|
|
203
|
-
expect(termToString(r2.term!)).toBe("(y + 1)");
|
|
204
|
-
// 不是同一对象(α 改写后的新 Abs),但结构同构
|
|
205
|
-
expect(r2).not.toBe(r1);
|
|
206
|
-
expect(formatAbs(r2).replace(/y/g, "x")).toBe(formatAbs(r1).replace(/x/g, "x"));
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("and-order does not cause miss", () => {
|
|
210
|
-
const g = generalizeFromAst("id", SRC_ID)!;
|
|
211
|
-
const ax = numVar("x", and(gtNum(v("x"), 0), gtNum(v("x"), 1)));
|
|
212
|
-
const phiA = and(gtNum(v("x"), 0), gtNum(v("x"), 1));
|
|
213
|
-
const phiB = and(gtNum(v("x"), 1), gtNum(v("x"), 0));
|
|
214
|
-
const r1 = g.instantiate([ax], phiA);
|
|
215
|
-
const r2 = g.instantiate([ax], phiB);
|
|
216
|
-
expect(formatAbs(r1)).toBe(formatAbs(r2));
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
describe("L3 deps fingerprint + LRU", () => {
|
|
221
|
-
const SRC_REFINE = `
|
|
222
|
-
/// @nudo:import { positive } from "./shapes.nudo.js"
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* @nudo:refine x positive
|
|
226
|
-
*/
|
|
227
|
-
function needsPositive(x) {
|
|
228
|
-
return x;
|
|
229
|
-
}
|
|
230
|
-
`;
|
|
231
|
-
|
|
232
|
-
it("misses L0 when imported nudo module content changes", () => {
|
|
233
|
-
let dep = "export const positive = number().gt(0);\n";
|
|
234
|
-
const loadModule = (spec: string) => (spec.includes("shapes") ? dep : undefined);
|
|
235
|
-
const refine = { loadModule, fromFile: "/proj/a.js" };
|
|
236
|
-
const g1 = generalizeFromAst("needsPositive", SRC_REFINE, { refine });
|
|
237
|
-
expect(g1).toBeDefined();
|
|
238
|
-
dep = "export const positive = number().gt(10);\n";
|
|
239
|
-
const g2 = generalizeFromAst("needsPositive", SRC_REFINE, { refine });
|
|
240
|
-
expect(g2).toBeDefined();
|
|
241
|
-
expect(g2).not.toBe(g1);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it("hits L0 when dep is unchanged", () => {
|
|
245
|
-
const loadModule = () => "export const positive = number().gt(0);\n";
|
|
246
|
-
const refine = { loadModule, fromFile: "/proj/a.js" };
|
|
247
|
-
const g1 = generalizeFromAst("needsPositive", SRC_REFINE, { refine });
|
|
248
|
-
const g2 = generalizeFromAst("needsPositive", SRC_REFINE, { refine });
|
|
249
|
-
expect(g2).toBe(g1);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("tracks memo size and can reset", () => {
|
|
253
|
-
resetGeneralizeMemo();
|
|
254
|
-
expect(getGeneralizeMemoSize()).toBe(0);
|
|
255
|
-
generalizeFromAst("add", SRC);
|
|
256
|
-
expect(getGeneralizeMemoSize()).toBeGreaterThan(0);
|
|
257
|
-
resetGeneralizeMemo();
|
|
258
|
-
expect(getGeneralizeMemoSize()).toBe(0);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
it("evictGeneralizeMemoForPaths drops only dependents of that dep", () => {
|
|
262
|
-
resetGeneralizeMemo();
|
|
263
|
-
const loadModule = () => "export const positive = number().gt(0);\n";
|
|
264
|
-
const refine = { loadModule, fromFile: "/proj/a.js" };
|
|
265
|
-
const g1 = generalizeFromAst("needsPositive", SRC_REFINE, { refine });
|
|
266
|
-
const gOther = generalizeFromAst("add", SRC);
|
|
267
|
-
const sizeAfter = getGeneralizeMemoSize();
|
|
268
|
-
const n = evictGeneralizeMemoForPaths(["/proj/shapes.nudo.js"]);
|
|
269
|
-
expect(n).toBeGreaterThan(0);
|
|
270
|
-
expect(getGeneralizeMemoSize()).toBeLessThan(sizeAfter);
|
|
271
|
-
// 无关函数仍在
|
|
272
|
-
expect(generalizeFromAst("add", SRC)).toBe(gOther);
|
|
273
|
-
// 依赖被逐出 → 重新 generalize 得到新 PolyFn
|
|
274
|
-
const g2 = generalizeFromAst("needsPositive", SRC_REFINE, { refine });
|
|
275
|
-
expect(g2).not.toBe(g1);
|
|
276
|
-
expect(g2).toBeDefined();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it("evict is a no-op for unknown paths", () => {
|
|
280
|
-
resetGeneralizeMemo();
|
|
281
|
-
generalizeFromAst("add", SRC);
|
|
282
|
-
expect(evictGeneralizeMemoForPaths(["/nope/missing.nudo.js"])).toBe(0);
|
|
283
|
-
expect(getGeneralizeMemoSize()).toBeGreaterThan(0);
|
|
284
|
-
});
|
|
285
|
-
});
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
generalizeFromAst,
|
|
4
|
-
generalizeAll,
|
|
5
|
-
numLit,
|
|
6
|
-
numVar,
|
|
7
|
-
gtNum,
|
|
8
|
-
v,
|
|
9
|
-
litValue,
|
|
10
|
-
termToString,
|
|
11
|
-
formatAbs,
|
|
12
|
-
} from "../index.ts";
|
|
13
|
-
|
|
14
|
-
const SRC = `
|
|
15
|
-
const add = (a, b) => a + b;
|
|
16
|
-
function scale(x) {
|
|
17
|
-
return add(x, 1);
|
|
18
|
-
}
|
|
19
|
-
function twice(x) {
|
|
20
|
-
const c = add(x, 1);
|
|
21
|
-
return add(c, 1);
|
|
22
|
-
}
|
|
23
|
-
function id(x) { return x; }
|
|
24
|
-
function pair(a, b) { return { a, b }; }
|
|
25
|
-
`;
|
|
26
|
-
|
|
27
|
-
describe("generalize", () => {
|
|
28
|
-
it("scale generalizes to term = A1+1", () => {
|
|
29
|
-
const g = generalizeFromAst("scale", SRC);
|
|
30
|
-
expect(g).toBeDefined();
|
|
31
|
-
if (!g) return;
|
|
32
|
-
expect(g.params).toEqual(["x"]);
|
|
33
|
-
expect(g.typeParams.length).toBe(1);
|
|
34
|
-
expect(g.symbolic.term).toBeDefined();
|
|
35
|
-
expect(termToString(g.symbolic.term!)).toBe("(A1 + 1)");
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it("instantiate scale with x>0 gives (x+1)>1", () => {
|
|
39
|
-
const g = generalizeFromAst("scale", SRC);
|
|
40
|
-
expect(g).toBeDefined();
|
|
41
|
-
if (!g) return;
|
|
42
|
-
const phi = gtNum(v("x"), 0);
|
|
43
|
-
const r = g.instantiate([numVar("x", gtNum(v("x"), 0))], phi);
|
|
44
|
-
expect(termToString(r.term!)).toBe("(x + 1)");
|
|
45
|
-
expect(r.pred!.op).toBe("gt");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("instantiate scale with literal 5 gives 6", () => {
|
|
49
|
-
const g = generalizeFromAst("scale", SRC);
|
|
50
|
-
if (!g) return;
|
|
51
|
-
const r = g.instantiate([numLit(5)]);
|
|
52
|
-
expect(litValue(r)).toBe(6);
|
|
53
|
-
expect(r.conf).toBe("exact");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("id is identity: term stays A1", () => {
|
|
57
|
-
const g = generalizeFromAst("id", SRC);
|
|
58
|
-
if (!g) return;
|
|
59
|
-
expect(g.symbolic.term?.op).toBe("var");
|
|
60
|
-
if (g.symbolic.term?.op === "var") {
|
|
61
|
-
expect(g.symbolic.term.id).toBe("A1");
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it("twice nests to (A1+1)+1", () => {
|
|
66
|
-
const g = generalizeFromAst("twice", SRC);
|
|
67
|
-
if (!g) return;
|
|
68
|
-
const ts = termToString(g.symbolic.term!);
|
|
69
|
-
expect(ts === "((A1 + 1) + 1)" || ts === "(A1 + 2)").toBe(true);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("generalizeAll finds all functions", () => {
|
|
73
|
-
const all = generalizeAll(SRC);
|
|
74
|
-
const names = all.map((g) => g.name).sort();
|
|
75
|
-
expect(names).toContain("add");
|
|
76
|
-
expect(names).toContain("scale");
|
|
77
|
-
expect(names).toContain("twice");
|
|
78
|
-
expect(names).toContain("id");
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("display mentions type params", () => {
|
|
82
|
-
const g = generalizeFromAst("scale", SRC);
|
|
83
|
-
if (!g) return;
|
|
84
|
-
expect(g.display).toContain("scale");
|
|
85
|
-
expect(g.display).toContain("A1");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("attaches @nudo:refine to entry param Abs", () => {
|
|
89
|
-
const src = `
|
|
90
|
-
/// @nudo:import { positive } from "./x.nudo.js"
|
|
91
|
-
/**
|
|
92
|
-
* @nudo:refine x positive
|
|
93
|
-
*/
|
|
94
|
-
function scale(x) { return x + 1; }
|
|
95
|
-
function bare(x) { return x + 1; }
|
|
96
|
-
`;
|
|
97
|
-
const loadModule = () => `export const positive = number().gt(0);`;
|
|
98
|
-
const g = generalizeFromAst("scale", src, {
|
|
99
|
-
refine: { loadModule, fromFile: "/t/a.js" },
|
|
100
|
-
});
|
|
101
|
-
expect(g).toBeDefined();
|
|
102
|
-
expect(g!.typeParams[0]!.value.shape).toEqual({ k: "prim", type: "number" });
|
|
103
|
-
expect(g!.entryReqs?.[0]?.param).toBe("x");
|
|
104
|
-
expect(g!.display).toContain("x > 0");
|
|
105
|
-
expect(formatAbs(g!.symbolic)).toContain("number");
|
|
106
|
-
|
|
107
|
-
const b = generalizeFromAst("bare", src, {
|
|
108
|
-
refine: { loadModule, fromFile: "/t/a.js" },
|
|
109
|
-
});
|
|
110
|
-
expect(b!.typeParams[0]!.value.shape.k).toBe("any");
|
|
111
|
-
expect(b!.display).toContain("number | string");
|
|
112
|
-
});
|
|
113
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { hashSource, resetHashSourceCache } from "../hash-source.ts";
|
|
3
|
-
|
|
4
|
-
describe("hashSource", () => {
|
|
5
|
-
it("is stable for identical input and differs on length/content", () => {
|
|
6
|
-
resetHashSourceCache();
|
|
7
|
-
const a = hashSource("hello world");
|
|
8
|
-
expect(hashSource("hello world")).toBe(a);
|
|
9
|
-
expect(hashSource("hello world!")).not.toBe(a);
|
|
10
|
-
// pure 32-bit FNV can collide on crafted pairs; length+second mix should not
|
|
11
|
-
expect(hashSource("a")).not.toBe(hashSource("b"));
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("includes length so truncated prefixes do not alias", () => {
|
|
15
|
-
resetHashSourceCache();
|
|
16
|
-
// classic FNV pitfalls often involve short suffixes; length disambiguates
|
|
17
|
-
expect(hashSource("abc")).not.toBe(hashSource("abcabc"));
|
|
18
|
-
});
|
|
19
|
-
});
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
analyzeFn,
|
|
4
|
-
numLit,
|
|
5
|
-
strLit,
|
|
6
|
-
numVar,
|
|
7
|
-
gtNum,
|
|
8
|
-
v,
|
|
9
|
-
litValue,
|
|
10
|
-
termToString,
|
|
11
|
-
formatShape,
|
|
12
|
-
abs,
|
|
13
|
-
unknown,
|
|
14
|
-
} from "../index.ts";
|
|
15
|
-
|
|
16
|
-
describe("HOF map/reduce from real source", () => {
|
|
17
|
-
it("map doubles literals: [1,2,3].map(x=>x*2) → number[] with joined elem", () => {
|
|
18
|
-
const src = `
|
|
19
|
-
function doubleAll(xs) {
|
|
20
|
-
return xs.map((x) => x * 2);
|
|
21
|
-
}
|
|
22
|
-
`;
|
|
23
|
-
// 实参数组:arr(number)
|
|
24
|
-
const arr = abs({ k: "arr", element: numLit(1) }, undefined, undefined, "exact");
|
|
25
|
-
const r = analyzeFn(src, "doubleAll", [arr]);
|
|
26
|
-
expect(r.shape.k).toBe("arr");
|
|
27
|
-
if (r.shape.k !== "arr") return;
|
|
28
|
-
// x=1, x*2=2
|
|
29
|
-
expect(litValue(r.shape.element)).toBe(2);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("map on symbolic element: Arr(number) map x=>x+1 under Φ elem>0", () => {
|
|
33
|
-
// 元素带约束的数组:用 numVar 作为 element 的 term/pred
|
|
34
|
-
const elem = {
|
|
35
|
-
shape: { k: "prim" as const, type: "number" as const },
|
|
36
|
-
term: { op: "var" as const, id: "e" },
|
|
37
|
-
pred: { op: "gt" as const, a: { op: "var" as const, id: "e" }, b: { op: "lit" as const, value: 0 } },
|
|
38
|
-
conf: "path" as const,
|
|
39
|
-
};
|
|
40
|
-
const arr = abs({ k: "arr", element: elem }, undefined, undefined, "path");
|
|
41
|
-
const src = `
|
|
42
|
-
function incAll(xs) {
|
|
43
|
-
return xs.map((x) => x + 1);
|
|
44
|
-
}
|
|
45
|
-
`;
|
|
46
|
-
const r = analyzeFn(src, "incAll", [arr]);
|
|
47
|
-
expect(r.shape.k).toBe("arr");
|
|
48
|
-
if (r.shape.k !== "arr") return;
|
|
49
|
-
const el = r.shape.element;
|
|
50
|
-
expect(termToString(el.term!)).toBe("(e + 1)");
|
|
51
|
-
expect(el.pred!.op).toBe("gt");
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("reduce sum of literals: [1,2,3].reduce((a,n)=>a+n,0) → 6", () => {
|
|
55
|
-
const src = `
|
|
56
|
-
function sum(xs) {
|
|
57
|
-
return xs.reduce((acc, n) => acc + n, 0);
|
|
58
|
-
}
|
|
59
|
-
`;
|
|
60
|
-
const arr = abs({ k: "arr", element: numLit(1) }, undefined, undefined, "exact");
|
|
61
|
-
// 用字面量元素 1 反复加不够 —— 用 tuple 语义:我们用 arr(number) 时元素是 1
|
|
62
|
-
// 更好:传 tuple
|
|
63
|
-
// Phase A:reduce 对 arr(element) 做不动点:0+1=1, 1+1=2, ... 会收敛到 number
|
|
64
|
-
const r = analyzeFn(src, "sum", [arr]);
|
|
65
|
-
// 0+1=1 exact first iter; join(0,1) 丢 term → number path
|
|
66
|
-
// 或者若 first next=1, join(0,1) → number
|
|
67
|
-
expect(r.shape).toEqual({ k: "prim", type: "number" });
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("filter preserves element type", () => {
|
|
71
|
-
const src = `
|
|
72
|
-
function keep(xs) {
|
|
73
|
-
return xs.filter((x) => x > 0);
|
|
74
|
-
}
|
|
75
|
-
`;
|
|
76
|
-
const arr = abs({ k: "arr", element: numLit(5) }, undefined, undefined, "exact");
|
|
77
|
-
const r = analyzeFn(src, "keep", [arr]);
|
|
78
|
-
expect(r.shape.k).toBe("arr");
|
|
79
|
-
});
|
|
80
|
-
});
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { collectAbsInlays } from "../inlay.ts";
|
|
3
|
-
|
|
4
|
-
describe("collectAbsInlays", () => {
|
|
5
|
-
it("shows param constraint and return Abs", () => {
|
|
6
|
-
const src = `
|
|
7
|
-
function needsPositive(x) {
|
|
8
|
-
if (x > 0) return x;
|
|
9
|
-
return 0;
|
|
10
|
-
}
|
|
11
|
-
`;
|
|
12
|
-
const inlays = collectAbsInlays(src);
|
|
13
|
-
expect(inlays.length).toBeGreaterThan(0);
|
|
14
|
-
const param = inlays.find((i) => i.kind === "parameter");
|
|
15
|
-
expect(param).toBeDefined();
|
|
16
|
-
expect(param!.label).toContain(">");
|
|
17
|
-
const ret = inlays.find((i) => i.kind === "type");
|
|
18
|
-
expect(ret).toBeDefined();
|
|
19
|
-
expect(ret!.label).toContain(":");
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("shows scale return term", () => {
|
|
23
|
-
const src = `function scale(x) { return x + 1; }\n`;
|
|
24
|
-
const inlays = collectAbsInlays(src);
|
|
25
|
-
const ret = inlays.find((i) => i.kind === "type");
|
|
26
|
-
expect(ret).toBeDefined();
|
|
27
|
-
// generalize 后是符号项 A1
|
|
28
|
-
expect(ret!.label).toContain("A1 + 1");
|
|
29
|
-
});
|
|
30
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
analyzeFn,
|
|
4
|
-
numLit,
|
|
5
|
-
litValue,
|
|
6
|
-
} from "../index.ts";
|
|
7
|
-
|
|
8
|
-
describe("language: class / instanceof / async", () => {
|
|
9
|
-
it("class + new → brand with methods", () => {
|
|
10
|
-
const src = `
|
|
11
|
-
class Point {
|
|
12
|
-
constructor(x) { this.x = x; }
|
|
13
|
-
dist() { return this.x; }
|
|
14
|
-
}
|
|
15
|
-
function main() {
|
|
16
|
-
const p = new Point(3);
|
|
17
|
-
return p;
|
|
18
|
-
}
|
|
19
|
-
`;
|
|
20
|
-
const r = analyzeFn(src, "main", []);
|
|
21
|
-
expect(r.shape.k).toBe("brand");
|
|
22
|
-
if (r.shape.k === "brand") {
|
|
23
|
-
expect(r.shape.name).toBe("Point");
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("instanceof brand name → true/false exact", () => {
|
|
28
|
-
const src = `
|
|
29
|
-
class Point { dist() { return 1; } }
|
|
30
|
-
class Other { dist() { return 2; } }
|
|
31
|
-
function check() {
|
|
32
|
-
const p = new Point();
|
|
33
|
-
return p instanceof Point;
|
|
34
|
-
}
|
|
35
|
-
function checkOther() {
|
|
36
|
-
const p = new Point();
|
|
37
|
-
return p instanceof Other;
|
|
38
|
-
}
|
|
39
|
-
`;
|
|
40
|
-
const r1 = analyzeFn(src, "check", []);
|
|
41
|
-
expect(litValue(r1)).toBe(true);
|
|
42
|
-
expect(r1.conf).toBe("exact");
|
|
43
|
-
const r2 = analyzeFn(src, "checkOther", []);
|
|
44
|
-
expect(litValue(r2)).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("async function returns eff promise", () => {
|
|
48
|
-
const src = `
|
|
49
|
-
async function load(id) {
|
|
50
|
-
return id + 1;
|
|
51
|
-
}
|
|
52
|
-
`;
|
|
53
|
-
const r = analyzeFn(src, "load", [numLit(5)]);
|
|
54
|
-
expect(r.shape.k).toBe("eff");
|
|
55
|
-
if (r.shape.k === "eff") {
|
|
56
|
-
expect(r.shape.eff).toBe("promise");
|
|
57
|
-
expect(litValue(r.shape.inner)).toBe(6);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it("await unwraps promise", () => {
|
|
62
|
-
const src = `
|
|
63
|
-
async function load(id) {
|
|
64
|
-
return id + 1;
|
|
65
|
-
}
|
|
66
|
-
async function twice(id) {
|
|
67
|
-
const a = await load(id);
|
|
68
|
-
return a + 1;
|
|
69
|
-
}
|
|
70
|
-
`;
|
|
71
|
-
const r = analyzeFn(src, "twice", [numLit(5)]);
|
|
72
|
-
expect(r.shape.k).toBe("eff");
|
|
73
|
-
if (r.shape.k === "eff") {
|
|
74
|
-
expect(litValue(r.shape.inner)).toBe(7);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
});
|