@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,176 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { analyzeFn, evalSource } from "../ast-eval.ts";
|
|
3
|
-
import { abs, num, numLit, unknown } from "../abs.ts";
|
|
4
|
-
import { v, lit } from "../term.ts";
|
|
5
|
-
import { gt, pTrue } from "../pred.ts";
|
|
6
|
-
import { typeValueToString } from "../../type-value.ts";
|
|
7
|
-
import { absToTypeValue } from "../bridge.ts";
|
|
8
|
-
import { formatAbs } from "../format.ts";
|
|
9
|
-
|
|
10
|
-
function show(a: ReturnType<typeof analyzeFn>): string {
|
|
11
|
-
return typeValueToString(absToTypeValue(a));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe("abs-native ast-eval", () => {
|
|
15
|
-
it("literal arith", () => {
|
|
16
|
-
const r = analyzeFn(`function f(){ return 1+2*3; }`, "f", []);
|
|
17
|
-
expect(show(r)).toBe("7");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("param + 1 with constraint via phi in source", () => {
|
|
21
|
-
const src = `
|
|
22
|
-
function f(x) {
|
|
23
|
-
if (x > 0) return x + 1;
|
|
24
|
-
return 0;
|
|
25
|
-
}
|
|
26
|
-
`;
|
|
27
|
-
const r = analyzeFn(src, "f", [abs(num().shape, v("x"), gt(v("x"), lit(0)), "path")]);
|
|
28
|
-
// x>0 时 x+1 应带约束
|
|
29
|
-
expect(r.shape.k === "prim" || r.shape.k === "sum").toBe(true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("logical && short-circuit", () => {
|
|
33
|
-
const r = analyzeFn(`function f(){ return false && 1; }`, "f", []);
|
|
34
|
-
expect(show(r)).toBe("false");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("typeof", () => {
|
|
38
|
-
const r = analyzeFn(`function f(){ return typeof 1; }`, "f", []);
|
|
39
|
-
expect(show(r)).toBe('"number"');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("template literal", () => {
|
|
43
|
-
const r = analyzeFn(`function f(x){ return \`n=\${x}\`; }`, "f", [numLit(3)]);
|
|
44
|
-
expect(show(r)).toBe('"n=3"');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("first-class arrow", () => {
|
|
48
|
-
const src = `
|
|
49
|
-
function f() {
|
|
50
|
-
const add = (a, b) => a + b;
|
|
51
|
-
return add(2, 3);
|
|
52
|
-
}
|
|
53
|
-
`;
|
|
54
|
-
const r = analyzeFn(src, "f", []);
|
|
55
|
-
expect(show(r)).toBe("5");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("div and mod", () => {
|
|
59
|
-
expect(show(analyzeFn(`function f(){ return 10/2; }`, "f", []))).toBe("5");
|
|
60
|
-
expect(show(analyzeFn(`function f(){ return 10%3; }`, "f", []))).toBe("1");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("nullish === decided", () => {
|
|
64
|
-
const r = analyzeFn(`function f(x){ return x === null; }`, "f", [numLit(1)]);
|
|
65
|
-
expect(show(r)).toBe("false");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("for loop runs with update", () => {
|
|
69
|
-
const src = `
|
|
70
|
-
function f() {
|
|
71
|
-
let s = 0;
|
|
72
|
-
for (let i = 0; i < 3; i++) s = s + i;
|
|
73
|
-
return s;
|
|
74
|
-
}
|
|
75
|
-
`;
|
|
76
|
-
const r = analyzeFn(src, "f", []);
|
|
77
|
-
expect(r.shape.k === "prim" || r.shape.k === "unknown").toBe(true);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it("for-of over tuple", () => {
|
|
81
|
-
const src = `
|
|
82
|
-
function f(xs) {
|
|
83
|
-
let s = 0;
|
|
84
|
-
for (const x of xs) s = s + x;
|
|
85
|
-
return s;
|
|
86
|
-
}
|
|
87
|
-
`;
|
|
88
|
-
const tup = abs({ k: "tuple", elements: [numLit(1), numLit(2)] }, undefined, undefined, "exact");
|
|
89
|
-
const r = analyzeFn(src, "f", [tup]);
|
|
90
|
-
expect(r.shape.k === "prim" || r.shape.k === "unknown").toBe(true);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("while with break", () => {
|
|
94
|
-
const src = `
|
|
95
|
-
function f() {
|
|
96
|
-
let i = 0;
|
|
97
|
-
while (true) {
|
|
98
|
-
i = i + 1;
|
|
99
|
-
if (i > 2) break;
|
|
100
|
-
}
|
|
101
|
-
return i;
|
|
102
|
-
}
|
|
103
|
-
`;
|
|
104
|
-
const r = analyzeFn(src, "f", []);
|
|
105
|
-
expect(r.shape.k === "prim" || r.shape.k === "unknown").toBe(true);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it("try/catch binds thrown value", () => {
|
|
109
|
-
const src = `
|
|
110
|
-
function f() {
|
|
111
|
-
try {
|
|
112
|
-
throw 1;
|
|
113
|
-
} catch (e) {
|
|
114
|
-
return e;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
`;
|
|
118
|
-
const r = analyzeFn(src, "f", []);
|
|
119
|
-
expect(show(r)).toBe("1");
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("string methods fold", () => {
|
|
123
|
-
expect(show(analyzeFn(`function f(){ return "abc".startsWith("a"); }`, "f", []))).toBe("true");
|
|
124
|
-
expect(show(analyzeFn(`function f(){ return "abc".toUpperCase(); }`, "f", []))).toBe('"ABC"');
|
|
125
|
-
expect(show(analyzeFn(`function f(){ return "hello".length; }`, "f", []))).toBe("5");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("array join returns string", () => {
|
|
129
|
-
const r = analyzeFn(`function f(xs){ return xs.join(","); }`, "f", [
|
|
130
|
-
abs({ k: "arr", element: abs({ k: "prim", type: "string" }, undefined, undefined, "exact") }, undefined, undefined, "exact"),
|
|
131
|
-
]);
|
|
132
|
-
expect(r.shape).toEqual({ k: "prim", type: "string" });
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it("Math and global builtins on Abs", () => {
|
|
136
|
-
expect(show(analyzeFn(`function f(){ return Math.floor(3.7); }`, "f", []))).toBe("3");
|
|
137
|
-
expect(show(analyzeFn(`function f(){ return Math.max(1, 5); }`, "f", []))).toBe("5");
|
|
138
|
-
expect(show(analyzeFn(`function f(){ return Array.isArray([]); }`, "f", []))).toBe("true");
|
|
139
|
-
expect(show(analyzeFn(`function f(){ return parseInt("42"); }`, "f", []))).toBe("42");
|
|
140
|
-
expect(show(analyzeFn(`function f(){ return Number.isInteger(3); }`, "f", []))).toBe("true");
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it("Object.keys of object shape", () => {
|
|
144
|
-
const src = `function f(){ return Object.keys({ a: 1, b: "x" }); }`;
|
|
145
|
-
const r = analyzeFn(src, "f", []);
|
|
146
|
-
expect(r.shape.k === "tuple" || r.shape.k === "arr").toBe(true);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it("Promise.resolve and then", () => {
|
|
150
|
-
const src = `
|
|
151
|
-
function f() {
|
|
152
|
-
return Promise.resolve(1).then((x) => x + 1);
|
|
153
|
-
}
|
|
154
|
-
`;
|
|
155
|
-
const r = analyzeFn(src, "f", []);
|
|
156
|
-
expect(r.shape.k).toBe("eff");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("Date.now and RegExp.test", () => {
|
|
160
|
-
const n = analyzeFn(`function f(){ return Date.now(); }`, "f", []);
|
|
161
|
-
expect(n.shape.k === "prim" && (n.shape as { type: string }).type === "number").toBe(true);
|
|
162
|
-
const t = analyzeFn(`function f(){ return /a/.test("a"); }`, "f", []);
|
|
163
|
-
// 字面正则:.test 可能走 member 路径;至少 boolean 或 unknown
|
|
164
|
-
expect(t.shape.k === "prim" || t.shape.k === "unknown").toBe(true);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("evalProgramAbs executes top-level", async () => {
|
|
168
|
-
const { evalProgramAbs } = await import("../ast-eval.ts");
|
|
169
|
-
const { env, last } = evalProgramAbs(`
|
|
170
|
-
const x = 1 + 2;
|
|
171
|
-
function inc(n) { return n + 1; }
|
|
172
|
-
`);
|
|
173
|
-
expect(env.vars.get("x")).toBeDefined();
|
|
174
|
-
expect(env.fns.has("inc")).toBe(true);
|
|
175
|
-
});
|
|
176
|
-
});
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
add,
|
|
4
|
-
sub,
|
|
5
|
-
mul,
|
|
6
|
-
div,
|
|
7
|
-
mod,
|
|
8
|
-
cmp,
|
|
9
|
-
numLit,
|
|
10
|
-
numVar,
|
|
11
|
-
strLit,
|
|
12
|
-
gtNum,
|
|
13
|
-
geNum,
|
|
14
|
-
ltNum,
|
|
15
|
-
lit,
|
|
16
|
-
v,
|
|
17
|
-
termToString,
|
|
18
|
-
predToString,
|
|
19
|
-
litValue,
|
|
20
|
-
implies,
|
|
21
|
-
simplifyTerm,
|
|
22
|
-
app,
|
|
23
|
-
} from "../index.ts";
|
|
24
|
-
|
|
25
|
-
describe("term simplify", () => {
|
|
26
|
-
it("folds 1+3", () => {
|
|
27
|
-
expect(simplifyTerm(app("+", [lit(1), lit(3)]))).toEqual(lit(4));
|
|
28
|
-
});
|
|
29
|
-
it("x+0 = x", () => {
|
|
30
|
-
expect(simplifyTerm(app("+", [v("x"), lit(0)]))).toEqual(v("x"));
|
|
31
|
-
});
|
|
32
|
-
it("0+x = x", () => {
|
|
33
|
-
expect(simplifyTerm(app("+", [lit(0), v("x")]))).toEqual(v("x"));
|
|
34
|
-
});
|
|
35
|
-
it("x*0 = 0", () => {
|
|
36
|
-
expect(simplifyTerm(app("*", [v("x"), lit(0)]))).toEqual(lit(0));
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe("add monotonicity", () => {
|
|
41
|
-
it("unconstrained any + 1: number|string 并集,term kept(real JS)", () => {
|
|
42
|
-
// 无契约时 x 是 any;score('x') 合法,+ 可能是拼接也可能是加法
|
|
43
|
-
const anyA = {
|
|
44
|
-
shape: { k: "any" as const },
|
|
45
|
-
term: v("A1"),
|
|
46
|
-
conf: "path" as const,
|
|
47
|
-
};
|
|
48
|
-
const r = add(anyA, numLit(1));
|
|
49
|
-
expect(r.shape.k).toBe("sum");
|
|
50
|
-
if (r.shape.k === "sum") {
|
|
51
|
-
const kinds = r.shape.members.map((m) =>
|
|
52
|
-
m.shape.k === "prim" ? (m.shape as { type: string }).type : m.shape.k,
|
|
53
|
-
);
|
|
54
|
-
expect(kinds.sort()).toEqual(["number", "string"]);
|
|
55
|
-
}
|
|
56
|
-
expect(termToString(r.term!)).toBe("(A1 + 1)");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it("unconstrained any - * / % → number (JS ToNumber)", () => {
|
|
60
|
-
const anyA = {
|
|
61
|
-
shape: { k: "any" as const },
|
|
62
|
-
term: v("A1"),
|
|
63
|
-
conf: "path" as const,
|
|
64
|
-
};
|
|
65
|
-
const one = numLit(1);
|
|
66
|
-
for (const [name, r] of [
|
|
67
|
-
["sub", sub(anyA, one)],
|
|
68
|
-
["mul", mul(anyA, one)],
|
|
69
|
-
["div", div(anyA, one)],
|
|
70
|
-
["mod", mod(anyA, one)],
|
|
71
|
-
] as const) {
|
|
72
|
-
expect(r.shape, name).toEqual({ k: "prim", type: "number" });
|
|
73
|
-
expect(r.term, name).toBeDefined();
|
|
74
|
-
}
|
|
75
|
-
expect(termToString(sub(anyA, one).term!)).toBe("(A1 - 1)");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("x>0 + 1 ⇒ term>1", () => {
|
|
79
|
-
const phi = gtNum(v("x"), 0);
|
|
80
|
-
const r = add(numVar("x", gtNum(v("x"), 0)), numLit(1), phi);
|
|
81
|
-
expect(termToString(r.term!)).toBe("(x + 1)");
|
|
82
|
-
expect(r.pred!.op).toBe("gt");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("x>2 + y>3 ⇒ (x+y)>5", () => {
|
|
86
|
-
const phi = { op: "and" as const, args: [gtNum(v("x"), 2), gtNum(v("y"), 3)] };
|
|
87
|
-
const r = add(
|
|
88
|
-
numVar("x", gtNum(v("x"), 2)),
|
|
89
|
-
numVar("y", gtNum(v("y"), 3)),
|
|
90
|
-
phi,
|
|
91
|
-
);
|
|
92
|
-
expect(r.pred!.op).toBe("gt");
|
|
93
|
-
if (r.pred!.op === "gt") {
|
|
94
|
-
expect(r.pred!.b).toEqual(lit(5));
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("x≥1 + 0 literal keeps ≥ via simplify to x", () => {
|
|
99
|
-
const phi = geNum(v("x"), 1);
|
|
100
|
-
const r = add(numVar("x", geNum(v("x"), 1)), numLit(0), phi);
|
|
101
|
-
// x+0 simplifies to x
|
|
102
|
-
expect(termToString(r.term!)).toBe("x");
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("literal 2+3 = 5 exact", () => {
|
|
106
|
-
const r = add(numLit(2), numLit(3));
|
|
107
|
-
expect(litValue(r)).toBe(5);
|
|
108
|
-
expect(r.conf).toBe("exact");
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("sub / mul", () => {
|
|
113
|
-
it("5-2 = 3", () => {
|
|
114
|
-
expect(litValue(sub(numLit(5), numLit(2)))).toBe(3);
|
|
115
|
-
});
|
|
116
|
-
it("x>0 - 1 ⇒ (x-1)>-1? x>0 ⇒ x-1 > -1", () => {
|
|
117
|
-
const phi = gtNum(v("x"), 0);
|
|
118
|
-
const r = sub(numVar("x", gtNum(v("x"), 0)), numLit(1), phi);
|
|
119
|
-
expect(r.term).toBeDefined();
|
|
120
|
-
expect(r.pred!.op).toBe("gt");
|
|
121
|
-
if (r.pred!.op === "gt") {
|
|
122
|
-
expect(r.pred!.b).toEqual(lit(-1));
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
it("3*4 = 12", () => {
|
|
126
|
-
expect(litValue(mul(numLit(3), numLit(4)))).toBe(12);
|
|
127
|
-
});
|
|
128
|
-
it("x>0 * 2 ⇒ (x*2)>0", () => {
|
|
129
|
-
const phi = gtNum(v("x"), 0);
|
|
130
|
-
const r = mul(numVar("x", gtNum(v("x"), 0)), numLit(2), phi);
|
|
131
|
-
expect(r.pred!.op).toBe("gt");
|
|
132
|
-
if (r.pred!.op === "gt") {
|
|
133
|
-
expect(r.pred!.b).toEqual(lit(0));
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe("implies", () => {
|
|
139
|
-
it("x>0 ⊢ x>0", () => {
|
|
140
|
-
expect(implies(gtNum(v("x"), 0), gtNum(v("x"), 0))).toBe(true);
|
|
141
|
-
});
|
|
142
|
-
it("x>0 ⊢ x≥0", () => {
|
|
143
|
-
expect(implies(gtNum(v("x"), 0), geNum(v("x"), 0))).toBe(true);
|
|
144
|
-
});
|
|
145
|
-
it("x>0 ⊬ x>5", () => {
|
|
146
|
-
expect(implies(gtNum(v("x"), 0), gtNum(v("x"), 5))).toBe(false);
|
|
147
|
-
});
|
|
148
|
-
it("x>5 ⊢ x>0", () => {
|
|
149
|
-
expect(implies(gtNum(v("x"), 5), gtNum(v("x"), 0))).toBe(true);
|
|
150
|
-
});
|
|
151
|
-
it("x>0 ⊢ x+1>1", () => {
|
|
152
|
-
const phi = gtNum(v("x"), 0);
|
|
153
|
-
expect(implies(phi, gtNum(app("+", [v("x"), lit(1)]), 1))).toBe(true);
|
|
154
|
-
});
|
|
155
|
-
it("x>0 ⊬ x+1>2", () => {
|
|
156
|
-
const phi = gtNum(v("x"), 0);
|
|
157
|
-
expect(implies(phi, gtNum(app("+", [v("x"), lit(1)]), 2))).toBe(false);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe("cmp under phi", () => {
|
|
162
|
-
it("x>0, cmp(x, 0, >) → true", () => {
|
|
163
|
-
const phi = gtNum(v("x"), 0);
|
|
164
|
-
const r = cmp("gt", numVar("x"), numLit(0), phi);
|
|
165
|
-
expect(litValue(r)).toBe(true);
|
|
166
|
-
});
|
|
167
|
-
it("1<3 → true", () => {
|
|
168
|
-
expect(litValue(cmp("lt", numLit(1), numLit(3)))).toBe(true);
|
|
169
|
-
});
|
|
170
|
-
});
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
analyzeFn,
|
|
4
|
-
numLit,
|
|
5
|
-
numVar,
|
|
6
|
-
gtNum,
|
|
7
|
-
geNum,
|
|
8
|
-
v,
|
|
9
|
-
lit,
|
|
10
|
-
termToString,
|
|
11
|
-
predToString,
|
|
12
|
-
litValue,
|
|
13
|
-
leakIfNeeded,
|
|
14
|
-
maybeLeak,
|
|
15
|
-
exceedsBudget,
|
|
16
|
-
termDepth,
|
|
17
|
-
termNodes,
|
|
18
|
-
app,
|
|
19
|
-
resetLeakCounter,
|
|
20
|
-
mul,
|
|
21
|
-
add,
|
|
22
|
-
} from "../index.ts";
|
|
23
|
-
|
|
24
|
-
describe("AST eval: source → Abs", () => {
|
|
25
|
-
it("add(1,3) from real source → 4", () => {
|
|
26
|
-
const src = `
|
|
27
|
-
const add = (a, b) => a + b;
|
|
28
|
-
function main() { return add(1, 3); }
|
|
29
|
-
`;
|
|
30
|
-
const r = analyzeFn(src, "main", []);
|
|
31
|
-
expect(litValue(r)).toBe(4);
|
|
32
|
-
expect(r.conf).toBe("exact");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("scale(x) = add(x,1) under x>0 ⇒ (x+1)>1", () => {
|
|
36
|
-
const src = `
|
|
37
|
-
const add = (a, b) => a + b;
|
|
38
|
-
function scale(x) { return add(x, 1); }
|
|
39
|
-
`;
|
|
40
|
-
const phi = gtNum(v("x"), 0);
|
|
41
|
-
const r = analyzeFn(src, "scale", [numVar("x", gtNum(v("x"), 0))], phi);
|
|
42
|
-
expect(r.term).toBeDefined();
|
|
43
|
-
expect(termToString(r.term!)).toBe("(x + 1)");
|
|
44
|
-
expect(r.pred!.op).toBe("gt");
|
|
45
|
-
if (r.pred!.op === "gt") {
|
|
46
|
-
expect(r.pred!.b).toEqual(lit(1));
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("twice(x) nested add under x>0 ⇒ >2", () => {
|
|
51
|
-
const src = `
|
|
52
|
-
const add = (a, b) => a + b;
|
|
53
|
-
function twice(x) {
|
|
54
|
-
const c = add(x, 1);
|
|
55
|
-
return add(c, 1);
|
|
56
|
-
}
|
|
57
|
-
`;
|
|
58
|
-
const phi = gtNum(v("x"), 0);
|
|
59
|
-
const r = analyzeFn(src, "twice", [numVar("x", gtNum(v("x"), 0))], phi);
|
|
60
|
-
expect(r.term).toBeDefined();
|
|
61
|
-
expect(r.pred).toBeDefined();
|
|
62
|
-
// ((x+1)+1) > 2 或 (x+2) > 2
|
|
63
|
-
const ps = predToString(r.pred!);
|
|
64
|
-
expect(ps).toContain("2");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("if (x > 5) return x; return 0 with x>10 ⇒ x", () => {
|
|
68
|
-
const src = `
|
|
69
|
-
function pick(x) {
|
|
70
|
-
if (x > 5) return x;
|
|
71
|
-
return 0;
|
|
72
|
-
}
|
|
73
|
-
`;
|
|
74
|
-
const phi = gtNum(v("x"), 10);
|
|
75
|
-
const r = analyzeFn(src, "pick", [numVar("x", gtNum(v("x"), 10))], phi);
|
|
76
|
-
// true 分支返回 x(term=x),false 分支 0;join 后丢 term
|
|
77
|
-
// 但 true 分支在 Φ 下 x>5 成立,我们至少得到 number
|
|
78
|
-
expect(r.shape).toEqual({ k: "prim", type: "number" });
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("if (x > 0) return x + 1; return 0 with Φ x>0 ⇒ true branch", () => {
|
|
82
|
-
const src = `
|
|
83
|
-
function addOneIfPositive(x) {
|
|
84
|
-
if (x > 0) return x + 1;
|
|
85
|
-
return 0;
|
|
86
|
-
}
|
|
87
|
-
`;
|
|
88
|
-
const phi = gtNum(v("x"), 0);
|
|
89
|
-
const r = analyzeFn(src, "addOneIfPositive", [numVar("x", gtNum(v("x"), 0))], phi);
|
|
90
|
-
expect(termToString(r.term!)).toBe("(x + 1)");
|
|
91
|
-
expect(r.pred!.op).toBe("gt");
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it("literal only function", () => {
|
|
95
|
-
const src = `
|
|
96
|
-
function f() { return 1 + 2 * 3; }
|
|
97
|
-
`;
|
|
98
|
-
// 注意:JS 里 1+2*3 = 7,我们的 BinaryExpression 是左结合遍历
|
|
99
|
-
// Babel AST: (1 + (2 * 3)) —— 乘法优先,AST 已带优先级
|
|
100
|
-
const r = analyzeFn(src, "f", []);
|
|
101
|
-
expect(litValue(r)).toBe(7);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it("string concat from source", () => {
|
|
105
|
-
const src = `
|
|
106
|
-
function greet(name) { return "hi, " + name; }
|
|
107
|
-
`;
|
|
108
|
-
const r = analyzeFn(src, "greet", [numLit(1)]);
|
|
109
|
-
// number + string 在 JS 里是字符串,我们 isStrPrim 一侧即可
|
|
110
|
-
// 实参是 number,左侧是 string → 走 string 拼接
|
|
111
|
-
expect(r.shape).toEqual({ k: "prim", type: "string" });
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
describe("leak", () => {
|
|
116
|
-
it("small term does not leak", () => {
|
|
117
|
-
resetLeakCounter();
|
|
118
|
-
const t = app("+", [v("x"), lit(1)]);
|
|
119
|
-
expect(exceedsBudget(t)).toBe(false);
|
|
120
|
-
const a = add(numVar("x"), numLit(1));
|
|
121
|
-
const leaked = leakIfNeeded(a);
|
|
122
|
-
expect(leaked.term).toEqual(a.term);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("deep term leaks to fresh var + eq", () => {
|
|
126
|
-
resetLeakCounter();
|
|
127
|
-
let t = v("x");
|
|
128
|
-
for (let i = 0; i < 10; i++) {
|
|
129
|
-
t = app("+", [t, lit(1)]);
|
|
130
|
-
}
|
|
131
|
-
expect(termDepth(t)).toBeGreaterThan(6);
|
|
132
|
-
expect(exceedsBudget(t)).toBe(true);
|
|
133
|
-
|
|
134
|
-
const a = { shape: { k: "prim" as const, type: "number" as const }, term: t, conf: "path" as const };
|
|
135
|
-
const leaked = maybeLeak(a, { maxDepth: 4, maxNodes: 20 }, "acc");
|
|
136
|
-
expect(leaked.term?.op).toBe("var");
|
|
137
|
-
if (leaked.term?.op === "var") {
|
|
138
|
-
expect(leaked.term.id.startsWith("acc$")).toBe(true);
|
|
139
|
-
}
|
|
140
|
-
expect(leaked.pred?.op).toBe("eq");
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe("mul negative flips bounds", () => {
|
|
145
|
-
it("x>0 * -2 ⇒ (x*-2) < 0", () => {
|
|
146
|
-
const phi = gtNum(v("x"), 0);
|
|
147
|
-
const r = mul(numVar("x", gtNum(v("x"), 0)), numLit(-2), phi);
|
|
148
|
-
expect(r.pred).toBeDefined();
|
|
149
|
-
expect(r.pred!.op).toBe("lt");
|
|
150
|
-
if (r.pred!.op === "lt") {
|
|
151
|
-
expect(r.pred!.b).toEqual(lit(0));
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
});
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
absToTypeValue,
|
|
4
|
-
typeValueToAbs,
|
|
5
|
-
bridgeIsLossy,
|
|
6
|
-
getTvConfidence,
|
|
7
|
-
numLit,
|
|
8
|
-
numVar,
|
|
9
|
-
gtNum,
|
|
10
|
-
v,
|
|
11
|
-
litValue,
|
|
12
|
-
formatShape,
|
|
13
|
-
analyzeFn,
|
|
14
|
-
objOf,
|
|
15
|
-
strLit,
|
|
16
|
-
boolLit,
|
|
17
|
-
} from "../index.ts";
|
|
18
|
-
import { typeValueToString, T } from "../../type-value.ts";
|
|
19
|
-
|
|
20
|
-
describe("bridge Abs → TypeValue", () => {
|
|
21
|
-
it("literal 4 → literal 4 #exact", () => {
|
|
22
|
-
const tv = absToTypeValue(numLit(4));
|
|
23
|
-
expect(tv.kind).toBe("literal");
|
|
24
|
-
if (tv.kind === "literal") expect(tv.value).toBe(4);
|
|
25
|
-
expect(getTvConfidence(tv)).toBe("exact");
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("plain number prim → primitive number", () => {
|
|
29
|
-
const tv = absToTypeValue({
|
|
30
|
-
shape: { k: "prim", type: "number" },
|
|
31
|
-
conf: "exact",
|
|
32
|
-
});
|
|
33
|
-
expect(typeValueToString(tv)).toBe("number");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("x>0 with pred → refined number", () => {
|
|
37
|
-
const a = numVar("x", gtNum(v("x"), 0));
|
|
38
|
-
const tv = absToTypeValue(a);
|
|
39
|
-
// term 是 var,会丢 term;pred 可 encode 为 refined
|
|
40
|
-
expect(tv.kind === "refined" || tv.kind === "primitive").toBe(true);
|
|
41
|
-
const lossy = bridgeIsLossy(a);
|
|
42
|
-
// 丢 term 是预期的
|
|
43
|
-
expect(lossy.reasons.some((r) => r.includes("term"))).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it("object slots map to object TypeValue", () => {
|
|
47
|
-
const o = objOf({
|
|
48
|
-
host: { value: strLit("localhost") },
|
|
49
|
-
port: { value: numLit(8080) },
|
|
50
|
-
debug: { value: boolLit(false) },
|
|
51
|
-
});
|
|
52
|
-
const tv = absToTypeValue(o);
|
|
53
|
-
expect(tv.kind).toBe("object");
|
|
54
|
-
expect(typeValueToString(tv)).toContain("host");
|
|
55
|
-
expect(typeValueToString(tv)).toContain("8080");
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe("bridge TypeValue → Abs", () => {
|
|
60
|
-
it("literal 6 → Abs exact 6", () => {
|
|
61
|
-
const abs = typeValueToAbs(T.literal(6));
|
|
62
|
-
expect(litValue(abs)).toBe(6);
|
|
63
|
-
expect(abs.conf).toBe("exact");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("primitive string → prim string", () => {
|
|
67
|
-
const abs = typeValueToAbs(T.string);
|
|
68
|
-
expect(formatShape(abs)).toBe("string");
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("object → obj slots", () => {
|
|
72
|
-
const abs = typeValueToAbs(
|
|
73
|
-
T.object({ a: T.literal(1), b: T.string }),
|
|
74
|
-
);
|
|
75
|
-
expect(abs.shape.k).toBe("obj");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it("round-trip literal", () => {
|
|
79
|
-
const tv = absToTypeValue(numLit(42));
|
|
80
|
-
const back = typeValueToAbs(tv);
|
|
81
|
-
expect(litValue(back)).toBe(42);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("round-trip primitive (lossless)", () => {
|
|
85
|
-
const abs0 = {
|
|
86
|
-
shape: { k: "prim" as const, type: "number" as const },
|
|
87
|
-
conf: "exact" as const,
|
|
88
|
-
};
|
|
89
|
-
const tv = absToTypeValue(abs0);
|
|
90
|
-
const back = typeValueToAbs(tv);
|
|
91
|
-
expect(formatShape(back)).toBe("number");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe("bridgeIsLossy", () => {
|
|
96
|
-
it("literal is not lossy", () => {
|
|
97
|
-
expect(bridgeIsLossy(numLit(1)).lossy).toBe(false);
|
|
98
|
-
});
|
|
99
|
-
it("symbolic term is lossy", () => {
|
|
100
|
-
const src = `
|
|
101
|
-
function scale(x) { return x + 1; }
|
|
102
|
-
`;
|
|
103
|
-
const r = analyzeFn(src, "scale", [numVar("x")]);
|
|
104
|
-
expect(bridgeIsLossy(r).lossy).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe("bridge any / conf / refined reverse (rewrite closeout)", () => {
|
|
109
|
-
it("Abs any projects as unknown but keeps path conf (not partial)", () => {
|
|
110
|
-
const a: import("../abs.ts").Abs = { shape: { k: "any" }, conf: "path" };
|
|
111
|
-
const tv = absToTypeValue(a);
|
|
112
|
-
expect(tv.kind).toBe("unknown");
|
|
113
|
-
expect(getTvConfidence(tv)).toBe("path");
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("Abs analysis-fail unknown keeps partial conf", () => {
|
|
117
|
-
const a: import("../abs.ts").Abs = { shape: { k: "unknown" }, conf: "partial" };
|
|
118
|
-
const tv = absToTypeValue(a);
|
|
119
|
-
expect(tv.kind).toBe("unknown");
|
|
120
|
-
expect(getTvConfidence(tv)).toBe("partial");
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("widened TypeValue does not round-trip as exact", () => {
|
|
124
|
-
const src = `
|
|
125
|
-
function scale(x) { return x + 1; }
|
|
126
|
-
`;
|
|
127
|
-
const r = analyzeFn(src, "scale", [numVar("x")]);
|
|
128
|
-
const tv = absToTypeValue(r);
|
|
129
|
-
const back = typeValueToAbs(tv);
|
|
130
|
-
expect(back.conf === "exact").toBe(false);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("refined numeric pred reverse-encodes into Abs pred", () => {
|
|
134
|
-
const a = numVar("x", gtNum(v("x"), 0));
|
|
135
|
-
const tv = absToTypeValue(a);
|
|
136
|
-
expect(tv.kind).toBe("refined");
|
|
137
|
-
const back = typeValueToAbs(tv);
|
|
138
|
-
expect(back.shape.k).toBe("prim");
|
|
139
|
-
expect(back.pred).toBeDefined();
|
|
140
|
-
expect(back.pred!.op).toBe("gt");
|
|
141
|
-
// pred 右端恢复为字面量 0
|
|
142
|
-
if (back.pred!.op === "gt") {
|
|
143
|
-
expect(back.pred!.b.op).toBe("lit");
|
|
144
|
-
if (back.pred!.b.op === "lit") expect(back.pred!.b.value).toBe(0);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("refined le reverse-encodes", () => {
|
|
149
|
-
const a: import("../abs.ts").Abs = {
|
|
150
|
-
shape: { k: "prim", type: "number" },
|
|
151
|
-
term: v("n"),
|
|
152
|
-
pred: { op: "le", a: v("n"), b: { op: "lit", value: 10 } },
|
|
153
|
-
conf: "path",
|
|
154
|
-
};
|
|
155
|
-
const tv = absToTypeValue(a);
|
|
156
|
-
expect(tv.kind).toBe("refined");
|
|
157
|
-
const back = typeValueToAbs(tv);
|
|
158
|
-
expect(back.pred?.op).toBe("le");
|
|
159
|
-
});
|
|
160
|
-
});
|