@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,691 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 算术核:单调性 + 常量折叠 + 约束传播。
|
|
3
|
-
* 这是「类型即计算」的主武器——x>0 时 x+1 必须得到 >1。
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Term } from "./term.ts";
|
|
7
|
-
import { app, lit, simplifyTerm, termToString } from "./term.ts";
|
|
8
|
-
import type { Pred } from "./pred.ts";
|
|
9
|
-
import {
|
|
10
|
-
and,
|
|
11
|
-
gt,
|
|
12
|
-
ge,
|
|
13
|
-
lt,
|
|
14
|
-
le,
|
|
15
|
-
implies,
|
|
16
|
-
predToString,
|
|
17
|
-
pTrue,
|
|
18
|
-
ptypeof,
|
|
19
|
-
type Phi,
|
|
20
|
-
} from "./pred.ts";
|
|
21
|
-
import type { Abs } from "./abs.ts";
|
|
22
|
-
import {
|
|
23
|
-
abs,
|
|
24
|
-
confJoin,
|
|
25
|
-
isNumPrim,
|
|
26
|
-
isStrPrim,
|
|
27
|
-
litValue,
|
|
28
|
-
num,
|
|
29
|
-
numLit,
|
|
30
|
-
str,
|
|
31
|
-
bool,
|
|
32
|
-
boolLit,
|
|
33
|
-
never,
|
|
34
|
-
} from "./abs.ts";
|
|
35
|
-
import { concatString, isTemplateLike } from "./template.ts";
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 抽象加法:eval(a + b) —— 跟真实 JS,不无根据地假定 number。
|
|
39
|
-
* 1. 双方字面量 → 直接求值
|
|
40
|
-
* 2. 双方 number prim → term + 单调性 pred,shape=number
|
|
41
|
-
* 3. 含 string/template → 拼接
|
|
42
|
-
* 4. any / type-var 参与 → 按 JS + 的可能结果取并集:number | string
|
|
43
|
-
* (bool/null 走 ToNumber→number;object 默认 ToPrimitive→string;
|
|
44
|
-
* bigint/symbol 边角不在默认并集里,由调用点实例化再收窄)
|
|
45
|
-
* 无契约的 score(x){return x+1}:score("x") 合法,不得钉成 number。
|
|
46
|
-
*/
|
|
47
|
-
export function add(a: Abs, b: Abs, phi: Phi = pTrue): Abs {
|
|
48
|
-
// 字面量快速路径
|
|
49
|
-
const va = litValue(a);
|
|
50
|
-
const vb = litValue(b);
|
|
51
|
-
if (va !== undefined && vb !== undefined) {
|
|
52
|
-
if (typeof va === "number" && typeof vb === "number") {
|
|
53
|
-
return numLit(va + vb);
|
|
54
|
-
}
|
|
55
|
-
if (typeof va === "string" || typeof vb === "string") {
|
|
56
|
-
return strLitResult(String(va) + String(vb));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// 字符串拼接(含 template parts)—— JS + 优先走 string
|
|
61
|
-
if (isStrPrim(a) || isStrPrim(b) || isTemplateLike(a) || isTemplateLike(b)) {
|
|
62
|
-
return concatString(a, b);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// 双方 number prim:数值加法 + 约束传播
|
|
66
|
-
if (isNumPrim(a) && isNumPrim(b)) {
|
|
67
|
-
if (!a.term || !b.term) {
|
|
68
|
-
return abs(num().shape, undefined, undefined, confJoin(a.conf, "widened"));
|
|
69
|
-
}
|
|
70
|
-
const term = simplifyTerm(app("+", [a.term, b.term]));
|
|
71
|
-
const pred = addPred(a, b, term, phi);
|
|
72
|
-
const conf =
|
|
73
|
-
term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), "path");
|
|
74
|
-
return abs({ k: "prim", type: "number" }, term, pred, conf);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// any / type-var:JS + 的并集,不是 unknown,也不是 number
|
|
78
|
-
if (isAnyLike(a) || isAnyLike(b)) {
|
|
79
|
-
const term =
|
|
80
|
-
a.term && b.term ? simplifyTerm(app("+", [a.term, b.term])) : undefined;
|
|
81
|
-
return abs(
|
|
82
|
-
{ k: "sum", members: [num(), str()] },
|
|
83
|
-
term,
|
|
84
|
-
undefined,
|
|
85
|
-
"partial",
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// sum 分发:对每个成员做 +,再 join((x+1)+1 在 any 上仍是 number|string)
|
|
90
|
-
if (a.shape.k === "sum" || b.shape.k === "sum") {
|
|
91
|
-
const parts: Abs[] = [];
|
|
92
|
-
const as = a.shape.k === "sum" ? a.shape.members : [a];
|
|
93
|
-
const bs = b.shape.k === "sum" ? b.shape.members : [b];
|
|
94
|
-
for (const x of as) {
|
|
95
|
-
for (const y of bs) {
|
|
96
|
-
const r = add(x, y, phi);
|
|
97
|
-
if (r.shape.k === "never") continue;
|
|
98
|
-
if (r.shape.k === "sum") parts.push(...r.shape.members);
|
|
99
|
-
else parts.push(r);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
const uniq = dedupAbsMembers(parts);
|
|
103
|
-
const term =
|
|
104
|
-
a.term && b.term ? simplifyTerm(app("+", [a.term, b.term])) : undefined;
|
|
105
|
-
if (uniq.length === 0) {
|
|
106
|
-
return abs({ k: "unknown" }, term, undefined, "partial");
|
|
107
|
-
}
|
|
108
|
-
if (uniq.length === 1) {
|
|
109
|
-
return abs(uniq[0]!.shape, term, uniq[0]!.pred, confJoin(a.conf, b.conf));
|
|
110
|
-
}
|
|
111
|
-
return abs({ k: "sum", members: uniq }, term, undefined, "partial");
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return abs({ k: "unknown" }, undefined, undefined, "partial");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function dedupAbsMembers(ms: Abs[]): Abs[] {
|
|
118
|
-
const seen = new Set<string>();
|
|
119
|
-
const out: Abs[] = [];
|
|
120
|
-
for (const m of ms) {
|
|
121
|
-
const key =
|
|
122
|
-
m.shape.k === "prim"
|
|
123
|
-
? `prim:${m.shape.type}`
|
|
124
|
-
: m.shape.k === "sum"
|
|
125
|
-
? `sum:${m.shape.members.length}`
|
|
126
|
-
: m.shape.k;
|
|
127
|
-
if (seen.has(key)) continue;
|
|
128
|
-
seen.add(key);
|
|
129
|
-
out.push(m);
|
|
130
|
-
}
|
|
131
|
-
return out;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function strLitResult(s: string): Abs {
|
|
135
|
-
return {
|
|
136
|
-
shape: { k: "prim", type: "string" },
|
|
137
|
-
term: lit(s),
|
|
138
|
-
conf: "exact",
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/** -0 与 0 在约束里等价 */
|
|
143
|
-
function normalizeNegZero(n: number): number {
|
|
144
|
-
return n === 0 ? 0 : n;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** any / unknown(含无 term 的裸 unknown):JS ToNumber 语义用于 - * / % */
|
|
148
|
-
function isAnyLike(a: Abs): boolean {
|
|
149
|
-
if (a.shape.k === "any") return true;
|
|
150
|
-
if (a.shape.k === "unknown") return true;
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* 减乘除模在 any 上走 JS ToNumber:结果恒为 number(可能 NaN)。
|
|
156
|
-
* 与 + 不同——+ 可能拼接;减乘除模不会。
|
|
157
|
-
*/
|
|
158
|
-
function toNumberResult(
|
|
159
|
-
a: Abs,
|
|
160
|
-
b: Abs,
|
|
161
|
-
op: "-" | "*" | "/" | "%",
|
|
162
|
-
): Abs {
|
|
163
|
-
const term =
|
|
164
|
-
a.term && b.term ? simplifyTerm(app(op, [a.term, b.term])) : undefined;
|
|
165
|
-
return abs(
|
|
166
|
-
{ k: "prim", type: "number" },
|
|
167
|
-
term,
|
|
168
|
-
undefined,
|
|
169
|
-
term ? confJoin(confJoin(a.conf, b.conf), "partial") : "partial",
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* 可参与数值运算(- * / %):number prim。
|
|
175
|
-
* any 不算数——那会把 score("x") 误判成 number 路径。
|
|
176
|
-
*/
|
|
177
|
-
function isNumericLike(a: Abs): boolean {
|
|
178
|
-
return isNumPrim(a);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* 加法约束单调性:
|
|
183
|
-
* a > p ∧ b > q ⇒ a+b > p+q
|
|
184
|
-
* a > p ∧ b = lit(q) ⇒ a+b > p+q
|
|
185
|
-
* a = var(x) ∧ x>0 ∧ b=lit(1) ⇒ term=x+1, pred: term>1
|
|
186
|
-
*/
|
|
187
|
-
function addPred(a: Abs, b: Abs, sumTerm: Term, phi: Phi): Pred | undefined {
|
|
188
|
-
// 双方都已有「相对自身 term」的数值下界/上界,且能从 Φ 或自身 pred 得到
|
|
189
|
-
const aBounds = numericBounds(a, phi);
|
|
190
|
-
const bBounds = numericBounds(b, phi);
|
|
191
|
-
if (!aBounds && !bBounds) return undefined;
|
|
192
|
-
|
|
193
|
-
const facts: Pred[] = [];
|
|
194
|
-
|
|
195
|
-
// (a.lo + b.lo) < sum 或 ≤
|
|
196
|
-
if (aBounds?.lo !== undefined && bBounds?.lo !== undefined) {
|
|
197
|
-
const loSum = aBounds.lo.value + bBounds.lo.value;
|
|
198
|
-
const strict = aBounds.lo.strict || bBounds.lo.strict;
|
|
199
|
-
facts.push(strict ? gt(sumTerm, lit(loSum)) : ge(sumTerm, lit(loSum)));
|
|
200
|
-
}
|
|
201
|
-
if (aBounds?.hi !== undefined && bBounds?.hi !== undefined) {
|
|
202
|
-
const hiSum = aBounds.hi.value + bBounds.hi.value;
|
|
203
|
-
const strict = aBounds.hi.strict || bBounds.hi.strict;
|
|
204
|
-
facts.push(strict ? lt(sumTerm, lit(hiSum)) : le(sumTerm, lit(hiSum)));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// 保留原 Φ 中可平移的事实:若 a 是 var(x) 且 Φ ⊢ x>0,则 sum=x+b 时
|
|
208
|
-
// numericBounds 已处理。此处直接返回 and(facts)。
|
|
209
|
-
if (facts.length === 0) return undefined;
|
|
210
|
-
return and(...facts);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
type NumBounds = {
|
|
214
|
-
lo?: { value: number; strict: boolean };
|
|
215
|
-
hi?: { value: number; strict: boolean };
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* 提取 Abs 上相对 term 的数值界。
|
|
220
|
-
* 优先用自身 pred;否则查 Φ(针对 var id)。
|
|
221
|
-
*/
|
|
222
|
-
export function numericBounds(a: Abs, phi: Phi = pTrue): NumBounds | undefined {
|
|
223
|
-
if (!isNumPrim(a)) return undefined;
|
|
224
|
-
const result: NumBounds = {};
|
|
225
|
-
|
|
226
|
-
// 自身 pred 中关于 term 的界
|
|
227
|
-
if (a.pred && a.term) {
|
|
228
|
-
collectBoundsFromPred(a.pred, a.term, result);
|
|
229
|
-
}
|
|
230
|
-
// Φ 中关于 var 的界
|
|
231
|
-
if (a.term?.op === "var") {
|
|
232
|
-
collectBoundsFromPhi(phi, a.term.id, result);
|
|
233
|
-
}
|
|
234
|
-
// 字面量:上下界都是自身
|
|
235
|
-
if (a.term?.op === "lit" && typeof a.term.value === "number") {
|
|
236
|
-
result.lo = { value: a.term.value, strict: false };
|
|
237
|
-
result.hi = { value: a.term.value, strict: false };
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (result.lo === undefined && result.hi === undefined) return undefined;
|
|
241
|
-
return result;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function collectBoundsFromPred(pred: Pred, term: Term, acc: NumBounds): void {
|
|
245
|
-
const match = (t: Term): boolean => termToString(t) === termToString(term);
|
|
246
|
-
const apply = (p: Pred): void => {
|
|
247
|
-
if (p.op === "and") {
|
|
248
|
-
p.args.forEach(apply);
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
if (p.op === "gt" && match(p.a) && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
252
|
-
const n = p.b.value;
|
|
253
|
-
if (acc.lo === undefined || n > acc.lo.value || (n === acc.lo.value && p.op === "gt")) {
|
|
254
|
-
acc.lo = { value: n, strict: true };
|
|
255
|
-
}
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
if (p.op === "ge" && match(p.a) && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
259
|
-
const n = p.b.value;
|
|
260
|
-
if (acc.lo === undefined || n > acc.lo.value) {
|
|
261
|
-
acc.lo = { value: n, strict: false };
|
|
262
|
-
}
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
if (p.op === "lt" && match(p.a) && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
266
|
-
const n = p.b.value;
|
|
267
|
-
if (acc.hi === undefined || n < acc.hi.value) {
|
|
268
|
-
acc.hi = { value: n, strict: true };
|
|
269
|
-
}
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
if (p.op === "le" && match(p.a) && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
273
|
-
const n = p.b.value;
|
|
274
|
-
if (acc.hi === undefined || n < acc.hi.value) {
|
|
275
|
-
acc.hi = { value: n, strict: false };
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
apply(pred);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function collectBoundsFromPhi(phi: Phi, id: string, acc: NumBounds): void {
|
|
283
|
-
const conjs = phi.op === "and" ? phi.args : [phi];
|
|
284
|
-
for (const p of conjs) {
|
|
285
|
-
if (p.op === "gt" && p.a.op === "var" && p.a.id === id && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
286
|
-
const n = p.b.value;
|
|
287
|
-
if (acc.lo === undefined || n > acc.lo.value) {
|
|
288
|
-
acc.lo = { value: n, strict: true };
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
if (p.op === "ge" && p.a.op === "var" && p.a.id === id && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
292
|
-
const n = p.b.value;
|
|
293
|
-
if (acc.lo === undefined || n > acc.lo.value) {
|
|
294
|
-
acc.lo = { value: n, strict: false };
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
if (p.op === "lt" && p.a.op === "var" && p.a.id === id && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
298
|
-
const n = p.b.value;
|
|
299
|
-
if (acc.hi === undefined || n < acc.hi.value) {
|
|
300
|
-
acc.hi = { value: n, strict: true };
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (p.op === "le" && p.a.op === "var" && p.a.id === id && p.b.op === "lit" && typeof p.b.value === "number") {
|
|
304
|
-
const n = p.b.value;
|
|
305
|
-
if (acc.hi === undefined || n < acc.hi.value) {
|
|
306
|
-
acc.hi = { value: n, strict: false };
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/** 减法:a - b = a + (-b),数值上做单调性 */
|
|
313
|
-
export function sub(a: Abs, b: Abs, phi: Phi = pTrue): Abs {
|
|
314
|
-
const va = litValue(a);
|
|
315
|
-
const vb = litValue(b);
|
|
316
|
-
if (typeof va === "number" && typeof vb === "number") {
|
|
317
|
-
return numLit(va - vb);
|
|
318
|
-
}
|
|
319
|
-
if (isNumericLike(a) && isNumericLike(b) && a.term && b.term) {
|
|
320
|
-
const term = simplifyTerm(app("-", [a.term, b.term]));
|
|
321
|
-
// a.lo - b.hi < a-b < a.hi - b.lo
|
|
322
|
-
const ab = numericBounds(a, phi);
|
|
323
|
-
const bb = numericBounds(b, phi);
|
|
324
|
-
const facts: Pred[] = [];
|
|
325
|
-
if (ab?.lo !== undefined && bb?.hi !== undefined) {
|
|
326
|
-
const lo = ab.lo.value - bb.hi.value;
|
|
327
|
-
const strict = ab.lo.strict || bb.hi.strict;
|
|
328
|
-
facts.push(strict ? gt(term, lit(lo)) : ge(term, lit(lo)));
|
|
329
|
-
}
|
|
330
|
-
if (ab?.hi !== undefined && bb?.lo !== undefined) {
|
|
331
|
-
const hi = ab.hi.value - bb.lo.value;
|
|
332
|
-
const strict = ab.hi.strict || bb.lo.strict;
|
|
333
|
-
facts.push(strict ? lt(term, lit(hi)) : le(term, lit(hi)));
|
|
334
|
-
}
|
|
335
|
-
const conf =
|
|
336
|
-
term.op === "lit"
|
|
337
|
-
? "exact"
|
|
338
|
-
: confJoin(confJoin(a.conf, b.conf), facts.length ? "path" : "path");
|
|
339
|
-
return abs({ k: "prim", type: "number" }, term, facts.length ? and(...facts) : undefined, conf);
|
|
340
|
-
}
|
|
341
|
-
if (isNumericLike(a) && isNumericLike(b)) {
|
|
342
|
-
return abs({ k: "prim", type: "number" }, undefined, undefined, "partial");
|
|
343
|
-
}
|
|
344
|
-
// any:JS ToNumber → number(可能 NaN)
|
|
345
|
-
if (isAnyLike(a) || isAnyLike(b)) {
|
|
346
|
-
return toNumberResult(a, b, "-");
|
|
347
|
-
}
|
|
348
|
-
return abs({ k: "unknown" }, undefined, undefined, "partial");
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/** 乘法:字面量直接求值;×正数同向缩放;×负数翻转不等式;×0 归零 */
|
|
352
|
-
export function mul(a: Abs, b: Abs, phi: Phi = pTrue): Abs {
|
|
353
|
-
const va = litValue(a);
|
|
354
|
-
const vb = litValue(b);
|
|
355
|
-
if (typeof va === "number" && typeof vb === "number") {
|
|
356
|
-
return numLit(va * vb);
|
|
357
|
-
}
|
|
358
|
-
if (isNumericLike(a) && isNumericLike(b) && a.term && b.term) {
|
|
359
|
-
const term = simplifyTerm(app("*", [a.term, b.term]));
|
|
360
|
-
// × 0
|
|
361
|
-
if (
|
|
362
|
-
(b.term.op === "lit" && b.term.value === 0) ||
|
|
363
|
-
(a.term.op === "lit" && a.term.value === 0)
|
|
364
|
-
) {
|
|
365
|
-
return numLit(0);
|
|
366
|
-
}
|
|
367
|
-
// × 常数 k
|
|
368
|
-
let k: number | undefined;
|
|
369
|
-
let base: Abs | undefined;
|
|
370
|
-
if (b.term.op === "lit" && typeof b.term.value === "number") {
|
|
371
|
-
k = b.term.value;
|
|
372
|
-
base = a;
|
|
373
|
-
} else if (a.term.op === "lit" && typeof a.term.value === "number") {
|
|
374
|
-
k = a.term.value;
|
|
375
|
-
base = b;
|
|
376
|
-
}
|
|
377
|
-
if (k !== undefined && base !== undefined && k !== 0) {
|
|
378
|
-
const ab = numericBounds(base, phi);
|
|
379
|
-
const facts: Pred[] = [];
|
|
380
|
-
if (ab?.lo !== undefined) {
|
|
381
|
-
if (k > 0) {
|
|
382
|
-
const lo = ab.lo.value * k;
|
|
383
|
-
facts.push(ab.lo.strict ? gt(term, lit(lo)) : ge(term, lit(lo)));
|
|
384
|
-
} else {
|
|
385
|
-
// 负数:lo * k 变成上界
|
|
386
|
-
const hi = ab.lo.value * k;
|
|
387
|
-
facts.push(ab.lo.strict ? lt(term, lit(normalizeNegZero(hi))) : le(term, lit(normalizeNegZero(hi))));
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (ab?.hi !== undefined) {
|
|
391
|
-
if (k > 0) {
|
|
392
|
-
const hi = ab.hi.value * k;
|
|
393
|
-
facts.push(ab.hi.strict ? lt(term, lit(hi)) : le(term, lit(hi)));
|
|
394
|
-
} else {
|
|
395
|
-
const lo = ab.hi.value * k;
|
|
396
|
-
facts.push(ab.hi.strict ? gt(term, lit(normalizeNegZero(lo))) : ge(term, lit(normalizeNegZero(lo))));
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return abs(
|
|
400
|
-
{ k: "prim", type: "number" },
|
|
401
|
-
term,
|
|
402
|
-
facts.length ? and(...facts) : undefined,
|
|
403
|
-
term.op === "lit" ? "exact" : confJoin(a.conf, "path"),
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
return abs(
|
|
407
|
-
{ k: "prim", type: "number" },
|
|
408
|
-
term,
|
|
409
|
-
undefined,
|
|
410
|
-
term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), "path"),
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
// number prim(无 term)× number prim → number(与 TypeValue 对齐)
|
|
414
|
-
if (isNumericLike(a) && isNumericLike(b)) {
|
|
415
|
-
return abs({ k: "prim", type: "number" }, undefined, undefined, "partial");
|
|
416
|
-
}
|
|
417
|
-
if (isAnyLike(a) || isAnyLike(b)) {
|
|
418
|
-
return toNumberResult(a, b, "*");
|
|
419
|
-
}
|
|
420
|
-
return abs({ k: "unknown" }, undefined, undefined, "partial");
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* 除法:字面量折叠;除以正/负常数时按单调性推界。
|
|
425
|
-
* 除以 0:JS 语义为 ±Infinity / NaN,shape 仍 number(不报违例)。
|
|
426
|
-
*/
|
|
427
|
-
export function div(a: Abs, b: Abs, phi: Phi = pTrue): Abs {
|
|
428
|
-
const va = litValue(a);
|
|
429
|
-
const vb = litValue(b);
|
|
430
|
-
if (typeof va === "number" && typeof vb === "number") {
|
|
431
|
-
if (vb === 0) {
|
|
432
|
-
// JS:0/0=NaN,n/0=±Infinity —— 保留字面量语义
|
|
433
|
-
return numLit(va / vb);
|
|
434
|
-
}
|
|
435
|
-
return numLit(va / vb);
|
|
436
|
-
}
|
|
437
|
-
if (isNumericLike(a) && isNumericLike(b) && a.term && b.term) {
|
|
438
|
-
const term = simplifyTerm(app("/", [a.term, b.term]));
|
|
439
|
-
if (b.term.op === "lit" && typeof b.term.value === "number" && b.term.value !== 0) {
|
|
440
|
-
const k = b.term.value;
|
|
441
|
-
const ab = numericBounds(a, phi);
|
|
442
|
-
const facts: Pred[] = [];
|
|
443
|
-
if (ab?.lo !== undefined) {
|
|
444
|
-
const lo = ab.lo.value / k;
|
|
445
|
-
if (k > 0) facts.push(ab.lo.strict ? gt(term, lit(lo)) : ge(term, lit(lo)));
|
|
446
|
-
else facts.push(ab.lo.strict ? lt(term, lit(lo)) : le(term, lit(lo)));
|
|
447
|
-
}
|
|
448
|
-
if (ab?.hi !== undefined) {
|
|
449
|
-
const hi = ab.hi.value / k;
|
|
450
|
-
if (k > 0) facts.push(ab.hi.strict ? lt(term, lit(hi)) : le(term, lit(hi)));
|
|
451
|
-
else facts.push(ab.hi.strict ? gt(term, lit(hi)) : ge(term, lit(hi)));
|
|
452
|
-
}
|
|
453
|
-
return abs(
|
|
454
|
-
num().shape,
|
|
455
|
-
term,
|
|
456
|
-
facts.length ? and(...facts) : undefined,
|
|
457
|
-
term.op === "lit" ? "exact" : confJoin(confJoin(a.conf, b.conf), "path"),
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
return abs(num().shape, term, undefined, confJoin(confJoin(a.conf, b.conf), "path"));
|
|
461
|
-
}
|
|
462
|
-
if (isNumericLike(a) && isNumericLike(b)) {
|
|
463
|
-
return abs(num().shape, undefined, undefined, "partial");
|
|
464
|
-
}
|
|
465
|
-
if (isAnyLike(a) || isAnyLike(b)) {
|
|
466
|
-
return toNumberResult(a, b, "/");
|
|
467
|
-
}
|
|
468
|
-
return abs({ k: "unknown" }, undefined, undefined, "partial");
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* 取模:字面量折叠;`x % k`(k>0 字面量)结果界在 (−|k|, |k|)。
|
|
473
|
-
* 整数模可收紧到 [0, k),此处先做保守实数界。
|
|
474
|
-
*/
|
|
475
|
-
export function mod(a: Abs, b: Abs, phi: Phi = pTrue): Abs {
|
|
476
|
-
const va = litValue(a);
|
|
477
|
-
const vb = litValue(b);
|
|
478
|
-
if (typeof va === "number" && typeof vb === "number") {
|
|
479
|
-
if (vb === 0) {
|
|
480
|
-
return abs(num().shape, undefined, undefined, "path");
|
|
481
|
-
}
|
|
482
|
-
return numLit(va % vb);
|
|
483
|
-
}
|
|
484
|
-
if (isNumericLike(a) && isNumericLike(b) && a.term && b.term) {
|
|
485
|
-
const term = simplifyTerm(app("%", [a.term, b.term]));
|
|
486
|
-
if (b.term.op === "lit" && typeof b.term.value === "number" && b.term.value !== 0) {
|
|
487
|
-
const k = Math.abs(b.term.value);
|
|
488
|
-
// 余数始终落在 (−k, k)
|
|
489
|
-
return abs(
|
|
490
|
-
num().shape,
|
|
491
|
-
term,
|
|
492
|
-
and(gt(term, lit(-k)), lt(term, lit(k))),
|
|
493
|
-
confJoin(confJoin(a.conf, b.conf), "path"),
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
return abs(num().shape, term, undefined, confJoin(confJoin(a.conf, b.conf), "path"));
|
|
497
|
-
}
|
|
498
|
-
if (isNumericLike(a) && isNumericLike(b)) {
|
|
499
|
-
return abs(num().shape, undefined, undefined, "partial");
|
|
500
|
-
}
|
|
501
|
-
if (isAnyLike(a) || isAnyLike(b)) {
|
|
502
|
-
return toNumberResult(a, b, "%");
|
|
503
|
-
}
|
|
504
|
-
return abs({ k: "unknown" }, undefined, undefined, "partial");
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/** 比较:返回 boolean Abs;若双字面量则 exact */
|
|
508
|
-
export function cmp(
|
|
509
|
-
op: "lt" | "le" | "gt" | "ge" | "eq" | "ne",
|
|
510
|
-
a: Abs,
|
|
511
|
-
b: Abs,
|
|
512
|
-
phi: Phi = pTrue,
|
|
513
|
-
): Abs {
|
|
514
|
-
const va = litValue(a);
|
|
515
|
-
const vb = litValue(b);
|
|
516
|
-
if (va !== undefined && vb !== undefined) {
|
|
517
|
-
const result = compareLits(op, va, vb);
|
|
518
|
-
if (result !== undefined) return boolLit(result);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// 符号比较:构造 pred 挂在 boolean 上,供 if 分支消费
|
|
522
|
-
if (a.term && b.term) {
|
|
523
|
-
const pred: Pred =
|
|
524
|
-
op === "lt"
|
|
525
|
-
? lt(a.term, b.term)
|
|
526
|
-
: op === "le"
|
|
527
|
-
? le(a.term, b.term)
|
|
528
|
-
: op === "gt"
|
|
529
|
-
? gt(a.term, b.term)
|
|
530
|
-
: op === "ge"
|
|
531
|
-
? ge(a.term, b.term)
|
|
532
|
-
: op === "eq"
|
|
533
|
-
? { op: "eq", a: a.term, b: b.term }
|
|
534
|
-
: { op: "ne", a: a.term, b: b.term };
|
|
535
|
-
|
|
536
|
-
// 若 Φ 已蕴含该比较 → true;若蕴含否定 → false
|
|
537
|
-
if (implies(phi, pred)) return boolLit(true);
|
|
538
|
-
const neg: Pred =
|
|
539
|
-
op === "lt"
|
|
540
|
-
? ge(a.term, b.term)
|
|
541
|
-
: op === "le"
|
|
542
|
-
? gt(a.term, b.term)
|
|
543
|
-
: op === "gt"
|
|
544
|
-
? le(a.term, b.term)
|
|
545
|
-
: op === "ge"
|
|
546
|
-
? lt(a.term, b.term)
|
|
547
|
-
: op === "eq"
|
|
548
|
-
? { op: "ne", a: a.term, b: b.term }
|
|
549
|
-
: { op: "eq", a: a.term, b: b.term };
|
|
550
|
-
if (implies(phi, neg)) return boolLit(false);
|
|
551
|
-
|
|
552
|
-
// 数值界判定:range pred / Φ 中的 min·max 足以决定字面比较
|
|
553
|
-
const decided = decideByBounds(op, a, b, phi);
|
|
554
|
-
if (decided !== undefined) return boolLit(decided);
|
|
555
|
-
|
|
556
|
-
return {
|
|
557
|
-
shape: { k: "prim", type: "boolean" },
|
|
558
|
-
term: undefined,
|
|
559
|
-
pred,
|
|
560
|
-
conf: confJoin(confJoin(a.conf, b.conf), "path"),
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
return abs({ k: "prim", type: "boolean" }, undefined, undefined, "partial");
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
function compareLits(
|
|
568
|
-
op: "lt" | "le" | "gt" | "ge" | "eq" | "ne",
|
|
569
|
-
a: string | number | boolean | null | undefined,
|
|
570
|
-
b: string | number | boolean | null | undefined,
|
|
571
|
-
): boolean | undefined {
|
|
572
|
-
if (op === "eq") return a === b;
|
|
573
|
-
if (op === "ne") return a !== b;
|
|
574
|
-
if (typeof a === "number" && typeof b === "number") {
|
|
575
|
-
switch (op) {
|
|
576
|
-
case "lt":
|
|
577
|
-
return a < b;
|
|
578
|
-
case "le":
|
|
579
|
-
return a <= b;
|
|
580
|
-
case "gt":
|
|
581
|
-
return a > b;
|
|
582
|
-
case "ge":
|
|
583
|
-
return a >= b;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
// 字符串字面量比较(JS 词法序)
|
|
587
|
-
if (typeof a === "string" && typeof b === "string") {
|
|
588
|
-
switch (op) {
|
|
589
|
-
case "lt":
|
|
590
|
-
return a < b;
|
|
591
|
-
case "le":
|
|
592
|
-
return a <= b;
|
|
593
|
-
case "gt":
|
|
594
|
-
return a > b;
|
|
595
|
-
case "ge":
|
|
596
|
-
return a >= b;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
return undefined;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* 用数值界判定比较(range Pred / Φ)。
|
|
604
|
-
* 仅当界足以推出结果时返回 true/false,否则 undefined。
|
|
605
|
-
*/
|
|
606
|
-
function decideByBounds(
|
|
607
|
-
op: "lt" | "le" | "gt" | "ge" | "eq" | "ne",
|
|
608
|
-
a: Abs,
|
|
609
|
-
b: Abs,
|
|
610
|
-
phi: Phi,
|
|
611
|
-
): boolean | undefined {
|
|
612
|
-
if (op === "eq" || op === "ne") return undefined;
|
|
613
|
-
const aB = numericBounds(a, phi);
|
|
614
|
-
const bB = numericBounds(b, phi);
|
|
615
|
-
if (!aB && !bB) return undefined;
|
|
616
|
-
|
|
617
|
-
// 一侧/两侧字面量界已含在 numericBounds;用 lo/hi 推
|
|
618
|
-
const aLo = aB?.lo;
|
|
619
|
-
const aHi = aB?.hi;
|
|
620
|
-
const bLo = bB?.lo;
|
|
621
|
-
const bHi = bB?.hi;
|
|
622
|
-
|
|
623
|
-
// a < b:a.hi < b.lo ⇒ true;a.lo ≥ b.hi ⇒ false
|
|
624
|
-
if (op === "lt") {
|
|
625
|
-
if (aHi && bLo && aHi.value < bLo.value) return true;
|
|
626
|
-
if (aLo && bHi && aLo.value >= bHi.value) return false;
|
|
627
|
-
return undefined;
|
|
628
|
-
}
|
|
629
|
-
if (op === "le") {
|
|
630
|
-
if (aHi && bLo && aHi.value < bLo.value) return true;
|
|
631
|
-
if (aLo && bHi && aLo.value > bHi.value) return false;
|
|
632
|
-
return undefined;
|
|
633
|
-
}
|
|
634
|
-
if (op === "gt") {
|
|
635
|
-
if (aLo && bHi && aLo.value > bHi.value) return true;
|
|
636
|
-
if (aHi && bLo && aHi.value <= bLo.value) return false;
|
|
637
|
-
return undefined;
|
|
638
|
-
}
|
|
639
|
-
if (op === "ge") {
|
|
640
|
-
if (aLo && bHi && aLo.value >= bHi.value) return true;
|
|
641
|
-
if (aHi && bLo && aHi.value < bLo.value) return false;
|
|
642
|
-
return undefined;
|
|
643
|
-
}
|
|
644
|
-
return undefined;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/** 从比较结果 Abs 提取「若为真」的额外约束(供 if 使用) */
|
|
648
|
-
export function trueConstraint(c: Abs): Pred | undefined {
|
|
649
|
-
if (c.term?.op === "lit" && c.term.value === true) return pTrue;
|
|
650
|
-
if (c.term?.op === "lit" && c.term.value === false) return undefined;
|
|
651
|
-
return c.pred;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
export function falseConstraint(c: Abs): Pred | undefined {
|
|
655
|
-
if (c.term?.op === "lit" && c.term.value === true) return undefined;
|
|
656
|
-
if (c.term?.op === "lit" && c.term.value === false) return pTrue;
|
|
657
|
-
if (!c.pred) return undefined;
|
|
658
|
-
// 否定 pred
|
|
659
|
-
return negatePred(c.pred);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
function negatePred(p: Pred): Pred {
|
|
663
|
-
switch (p.op) {
|
|
664
|
-
case "true":
|
|
665
|
-
return { op: "false" };
|
|
666
|
-
case "false":
|
|
667
|
-
return { op: "true" };
|
|
668
|
-
case "eq":
|
|
669
|
-
return { op: "ne", a: p.a, b: p.b };
|
|
670
|
-
case "ne":
|
|
671
|
-
return { op: "eq", a: p.a, b: p.b };
|
|
672
|
-
case "lt":
|
|
673
|
-
return ge(p.a, p.b);
|
|
674
|
-
case "le":
|
|
675
|
-
return gt(p.a, p.b);
|
|
676
|
-
case "gt":
|
|
677
|
-
return le(p.a, p.b);
|
|
678
|
-
case "ge":
|
|
679
|
-
return lt(p.a, p.b);
|
|
680
|
-
case "and":
|
|
681
|
-
// De Morgan:¬(A∧B) = ¬A ∨ ¬B —— Phase A 不展开 or,退回 unknown-ish
|
|
682
|
-
// 保守:返回一个 not 节点(implies 暂不处理)
|
|
683
|
-
return { op: "not", arg: p };
|
|
684
|
-
case "or":
|
|
685
|
-
return { op: "not", arg: p };
|
|
686
|
-
case "not":
|
|
687
|
-
return p.arg;
|
|
688
|
-
case "typeof":
|
|
689
|
-
return { op: "not", arg: p };
|
|
690
|
-
}
|
|
691
|
-
}
|