@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.
Files changed (123) hide show
  1. package/dist/chunk-N7E2A7IK.js +7216 -0
  2. package/dist/exec.d.ts +2 -0
  3. package/dist/exec.js +174 -0
  4. package/dist/index-DgyudAkw.d.ts +911 -0
  5. package/dist/index.d.ts +64 -710
  6. package/dist/index.js +3269 -8426
  7. package/package.json +18 -5
  8. package/CHANGELOG.md +0 -34
  9. package/src/__tests__/environment.test.ts +0 -48
  10. package/src/__tests__/fn-sig-impl.test.ts +0 -57
  11. package/src/__tests__/ops-refined.test.ts +0 -196
  12. package/src/__tests__/ops.test.ts +0 -84
  13. package/src/__tests__/type-value-phase2.test.ts +0 -117
  14. package/src/__tests__/type-value-phase3.test.ts +0 -94
  15. package/src/__tests__/type-value-refined.test.ts +0 -210
  16. package/src/__tests__/type-value.test.ts +0 -319
  17. package/src/algebra/__tests__/abs-native.test.ts +0 -176
  18. package/src/algebra/__tests__/arithmetic.test.ts +0 -170
  19. package/src/algebra/__tests__/ast-eval.test.ts +0 -154
  20. package/src/algebra/__tests__/bridge.test.ts +0 -160
  21. package/src/algebra/__tests__/call-budget.test.ts +0 -117
  22. package/src/algebra/__tests__/check-case-consistency.test.ts +0 -69
  23. package/src/algebra/__tests__/check-gold.test.ts +0 -290
  24. package/src/algebra/__tests__/check-json.test.ts +0 -57
  25. package/src/algebra/__tests__/check-mini-repo.test.ts +0 -49
  26. package/src/algebra/__tests__/check-real-commander.test.ts +0 -43
  27. package/src/algebra/__tests__/check-real-packages.test.ts +0 -165
  28. package/src/algebra/__tests__/check-recall-gold.test.ts +0 -1024
  29. package/src/algebra/__tests__/check-require-dep-memo.test.ts +0 -45
  30. package/src/algebra/__tests__/check-return-contract.test.ts +0 -90
  31. package/src/algebra/__tests__/check-shape-gold.test.ts +0 -303
  32. package/src/algebra/__tests__/check-source-memo.test.ts +0 -204
  33. package/src/algebra/__tests__/check.test.ts +0 -69
  34. package/src/algebra/__tests__/denote.test.ts +0 -77
  35. package/src/algebra/__tests__/diagnostics.test.ts +0 -64
  36. package/src/algebra/__tests__/differential.test.ts +0 -124
  37. package/src/algebra/__tests__/div-mod.test.ts +0 -38
  38. package/src/algebra/__tests__/example0.test.ts +0 -103
  39. package/src/algebra/__tests__/exec-analyze-throws.test.ts +0 -60
  40. package/src/algebra/__tests__/exec-async.test.ts +0 -46
  41. package/src/algebra/__tests__/exec-bpath.test.ts +0 -193
  42. package/src/algebra/__tests__/exec-class.test.ts +0 -56
  43. package/src/algebra/__tests__/exec-e2e.test.ts +0 -97
  44. package/src/algebra/__tests__/exec-generator.test.ts +0 -53
  45. package/src/algebra/__tests__/exec-inherit-default.test.ts +0 -111
  46. package/src/algebra/__tests__/exec-optional-nested.test.ts +0 -87
  47. package/src/algebra/__tests__/exec-rest.test.ts +0 -55
  48. package/src/algebra/__tests__/exec-spread-forof.test.ts +0 -85
  49. package/src/algebra/__tests__/exec-static-computed.test.ts +0 -61
  50. package/src/algebra/__tests__/exec-switch.test.ts +0 -74
  51. package/src/algebra/__tests__/exec-trycatch.test.ts +0 -71
  52. package/src/algebra/__tests__/fn-fp-memo.test.ts +0 -166
  53. package/src/algebra/__tests__/fn-fp-multidecl.test.ts +0 -49
  54. package/src/algebra/__tests__/format.test.ts +0 -34
  55. package/src/algebra/__tests__/generalize-memo.test.ts +0 -285
  56. package/src/algebra/__tests__/generalize.test.ts +0 -113
  57. package/src/algebra/__tests__/hash-source.test.ts +0 -19
  58. package/src/algebra/__tests__/hof.test.ts +0 -80
  59. package/src/algebra/__tests__/inlay.test.ts +0 -30
  60. package/src/algebra/__tests__/language.test.ts +0 -77
  61. package/src/algebra/__tests__/leq.test.ts +0 -109
  62. package/src/algebra/__tests__/load-deps-fp-memo.test.ts +0 -132
  63. package/src/algebra/__tests__/methods.test.ts +0 -37
  64. package/src/algebra/__tests__/node-types.test.ts +0 -26
  65. package/src/algebra/__tests__/nudo-constraints.ts +0 -46
  66. package/src/algebra/__tests__/objects.test.ts +0 -117
  67. package/src/algebra/__tests__/parse-source-cache.test.ts +0 -42
  68. package/src/algebra/__tests__/refine-import.test.ts +0 -109
  69. package/src/algebra/__tests__/stable-source-key-block.test.ts +0 -23
  70. package/src/algebra/__tests__/stable-source-key.test.ts +0 -31
  71. package/src/algebra/__tests__/surface.test.ts +0 -41
  72. package/src/algebra/__tests__/this-inherit.test.ts +0 -130
  73. package/src/algebra/abs-fn.ts +0 -49
  74. package/src/algebra/abs-modules.ts +0 -139
  75. package/src/algebra/abs.ts +0 -206
  76. package/src/algebra/arithmetic.ts +0 -691
  77. package/src/algebra/ast-eval.ts +0 -1763
  78. package/src/algebra/bridge.ts +0 -350
  79. package/src/algebra/builtins.ts +0 -373
  80. package/src/algebra/check-report.ts +0 -150
  81. package/src/algebra/check.ts +0 -1867
  82. package/src/algebra/constraint.ts +0 -304
  83. package/src/algebra/denote.ts +0 -166
  84. package/src/algebra/diagnostics.ts +0 -210
  85. package/src/algebra/exec/call.ts +0 -29
  86. package/src/algebra/exec/calls.ts +0 -96
  87. package/src/algebra/exec/class-registry.ts +0 -29
  88. package/src/algebra/exec/class.ts +0 -329
  89. package/src/algebra/exec/index.ts +0 -6
  90. package/src/algebra/exec/member-diag.ts +0 -141
  91. package/src/algebra/exec/run.ts +0 -341
  92. package/src/algebra/exec/runtime.ts +0 -583
  93. package/src/algebra/exec/transpile.ts +0 -993
  94. package/src/algebra/fn-fp.ts +0 -294
  95. package/src/algebra/format.ts +0 -91
  96. package/src/algebra/generalize.ts +0 -776
  97. package/src/algebra/hash-source.ts +0 -27
  98. package/src/algebra/index.ts +0 -34
  99. package/src/algebra/inlay.ts +0 -198
  100. package/src/algebra/language.ts +0 -289
  101. package/src/algebra/leak.ts +0 -68
  102. package/src/algebra/leq.ts +0 -301
  103. package/src/algebra/load-deps-fp.ts +0 -97
  104. package/src/algebra/methods.ts +0 -128
  105. package/src/algebra/objects.ts +0 -251
  106. package/src/algebra/parse-source.ts +0 -64
  107. package/src/algebra/phi.ts +0 -42
  108. package/src/algebra/pred.ts +0 -395
  109. package/src/algebra/refine.ts +0 -227
  110. package/src/algebra/stable-source-key.ts +0 -81
  111. package/src/algebra/surface.ts +0 -206
  112. package/src/algebra/template.ts +0 -143
  113. package/src/algebra/term.ts +0 -119
  114. package/src/environment.ts +0 -74
  115. package/src/index.ts +0 -73
  116. package/src/mock-helpers.ts +0 -166
  117. package/src/ops.ts +0 -191
  118. package/src/refinements/range.ts +0 -86
  119. package/src/refinements/template-predicates.ts +0 -11
  120. package/src/refinements/template.ts +0 -176
  121. package/src/strip-types.ts +0 -236
  122. package/src/type-value.ts +0 -513
  123. 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
- }