@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,301 +0,0 @@
1
- /**
2
- * 结构可赋值:src ≤ tgt(Abs 上的 leq)。
3
- *
4
- * 规则与 TS 结构子类型同构,但作用在推断出的 Abs 上:
5
- * - 对象宽度 + 深度;optional 可缺
6
- * - brand 名义;fn 参数逆变、返回协变
7
- * - pred:src 蕴含 tgt(更严的值可赋给更宽的目标)
8
- *
9
- * 不是完备 checker 的替代——是 Abs 之间的兼容判定。
10
- */
11
-
12
- import type { Abs, Shape } from "./abs.ts";
13
- import { litValue } from "./abs.ts";
14
- import type { Phi, Pred } from "./pred.ts";
15
- import { pTrue, implies } from "./pred.ts";
16
- import { termEquals } from "./term.ts";
17
- import type { AstEnv } from "./ast-eval.ts";
18
- import { getClassChain } from "./language.ts";
19
-
20
- export type LeqResult = {
21
- ok: boolean;
22
- /** Nudo 式原因(actual ⊭ target),不是 TS 文案 */
23
- reason?: string;
24
- };
25
-
26
- function ok(): LeqResult {
27
- return { ok: true };
28
- }
29
-
30
- function fail(reason: string): LeqResult {
31
- return { ok: false, reason };
32
- }
33
-
34
- /**
35
- * src 可赋给 tgt。
36
- * phi:路径前提(可选);env:用于 brand 继承链。
37
- */
38
- export function leqAbs(
39
- src: Abs,
40
- tgt: Abs,
41
- opts: { phi?: Phi; env?: AstEnv } = {},
42
- ): LeqResult {
43
- const phi = opts.phi ?? pTrue;
44
- return leqWithPred(src, tgt, phi, opts.env ?? null, 0);
45
- }
46
-
47
- function leqWithPred(
48
- src: Abs,
49
- tgt: Abs,
50
- phi: Phi,
51
- env: AstEnv | null,
52
- depth: number,
53
- ): LeqResult {
54
- if (depth > 32) return fail("leq depth exceeded");
55
-
56
- // never ≤ 任意
57
- if (src.shape.k === "never") return ok();
58
- // 任意 ≤ any / unknown(目标放宽)
59
- if (tgt.shape.k === "any" || tgt.shape.k === "unknown") return ok();
60
- // any ≤ 任意(源是任意值,目标收窄时不在此判定失败——由 pred/slot 再卡)
61
- if (src.shape.k === "any") return ok();
62
-
63
- // 字面量:先按 lit 值裁定,再走 shape
64
- const sv = litValue(src);
65
- const tv = litValue(tgt);
66
- if (sv !== undefined && tv !== undefined) {
67
- if (sv === tv) return ok();
68
- // 数值字面量可进带 pred 的 number(走 pred 蕴含)
69
- }
70
-
71
- const shapeR = leqShape(src, tgt, phi, env, depth);
72
- if (!shapeR.ok) return shapeR;
73
-
74
- // shape 兼容后检查 pred:src.pred ∧ phi ⇒ tgt.pred
75
- return leqPred(src, tgt, phi, depth);
76
- }
77
-
78
- function leqPred(src: Abs, tgt: Abs, phi: Phi, depth: number): LeqResult {
79
- const sp = src.pred && src.pred.op !== "true" ? src.pred : undefined;
80
- const tp = tgt.pred && tgt.pred.op !== "true" ? tgt.pred : undefined;
81
- if (!tp) return ok(); // 目标无约束
82
- if (!sp) {
83
- // 源无约束但目标有:数值界用 bounds 粗判;否则不蕴含
84
- // 保守:仅当目标 pred 在 phi 下已被蕴含才 ok
85
- if (implies(phi, tp)) return ok();
86
- return fail(`pred ⊭ ${predBrief(tp)}`);
87
- }
88
- const combined: Phi =
89
- phi.op === "true" ? sp : { op: "and", args: [phi, sp] };
90
- if (implies(combined, tp)) return ok();
91
- // 数值界:src 更严可赋给更宽目标(x>5 ≤ x>0)
92
- if (numericBoundsImply(sp, tp)) return ok();
93
- return fail(`pred ⊭ ${predBrief(tp)}`);
94
- }
95
-
96
- function predBrief(p: Pred): string {
97
- switch (p.op) {
98
- case "gt":
99
- case "ge":
100
- case "lt":
101
- case "le":
102
- case "eq":
103
- case "ne":
104
- return p.op;
105
- default:
106
- return p.op;
107
- }
108
- }
109
-
110
- /** 数值界蕴含:src 的界更紧则可赋给更宽 tgt */
111
- function numericBoundsImply(src: Pred, tgt: Pred): boolean {
112
- // 单比较:x > n_src ⇒ x > n_tgt 当 n_src ≥ n_tgt(gt);对称处理 ge
113
- const one = (p: Pred): p is Extract<Pred, { op: "gt" | "ge" | "lt" | "le" }> =>
114
- p.op === "gt" || p.op === "ge" || p.op === "lt" || p.op === "le";
115
- if (!one(src) || !one(tgt)) return false;
116
- if (src.op !== tgt.op) {
117
- // gt ⇒ ge;lt ⇒ le
118
- if (src.op === "gt" && tgt.op === "ge") {
119
- return sameTermSide(src, tgt) && litGE(src.b, tgt.b);
120
- }
121
- if (src.op === "lt" && tgt.op === "le") {
122
- return sameTermSide(src, tgt) && litLE(src.b, tgt.b);
123
- }
124
- return false;
125
- }
126
- if (!sameTermSide(src, tgt)) return false;
127
- const sb = src.b.op === "lit" ? src.b.value : undefined;
128
- const tb = tgt.b.op === "lit" ? tgt.b.value : undefined;
129
- if (typeof sb !== "number" || typeof tb !== "number") return false;
130
- if (src.op === "gt" || src.op === "ge") {
131
- return sb >= tb; // 更大的下界 ⇒ 更小的下界
132
- }
133
- return sb <= tb; // 更小的上界 ⇒ 更大的上界
134
- }
135
-
136
- function sameTermSide(
137
- a: { a: { op: string }; b: { op: string } },
138
- b: { a: { op: string }; b: { op: string } },
139
- ): boolean {
140
- // termEquals 走结构比较,避免 sum×sum 场景 JSON.stringify 爆炸
141
- return a.a.op === b.a.op && termEquals(a.a as never, b.a as never);
142
- }
143
-
144
- function litGE(
145
- a: { op: string; value?: unknown },
146
- b: { op: string; value?: unknown },
147
- ): boolean {
148
- if (a.op !== "lit" || b.op !== "lit") return false;
149
- return typeof a.value === "number" && typeof b.value === "number" && a.value >= b.value;
150
- }
151
-
152
- function litLE(
153
- a: { op: string; value?: unknown },
154
- b: { op: string; value?: unknown },
155
- ): boolean {
156
- if (a.op !== "lit" || b.op !== "lit") return false;
157
- return typeof a.value === "number" && typeof b.value === "number" && a.value <= b.value;
158
- }
159
-
160
- function leqShape(
161
- src: Abs,
162
- tgt: Abs,
163
- phi: Phi,
164
- env: AstEnv | null,
165
- depth: number,
166
- ): LeqResult {
167
- const s = src.shape;
168
- const t = tgt.shape;
169
-
170
- // sum 源:每个成员 ≤ tgt
171
- if (s.k === "sum") {
172
- for (const m of s.members) {
173
- const r = leqWithPred(m, tgt, phi, env, depth + 1);
174
- if (!r.ok) return fail(`sum member: ${r.reason}`);
175
- }
176
- return ok();
177
- }
178
- // sum 目标:src ≤ 某个成员
179
- if (t.k === "sum") {
180
- for (const m of t.members) {
181
- if (leqWithPred(src, m, phi, env, depth + 1).ok) return ok();
182
- }
183
- return fail("src ⊭ sum");
184
- }
185
-
186
- // prim:同型;lit 归入对应 prim
187
- if (t.k === "prim") {
188
- const sp = primOf(src);
189
- if (!sp) return fail(`shape ${s.k} ⊭ prim ${t.type}`);
190
- if (sp !== t.type) return fail(`prim ${sp} ⊭ prim ${t.type}`);
191
- return ok();
192
- }
193
-
194
- // 对象:宽度 + 深度
195
- if (t.k === "obj") {
196
- if (s.k !== "obj" && s.k !== "brand") {
197
- return fail(`shape ${s.k} ⊭ obj`);
198
- }
199
- const srcObj =
200
- s.k === "obj"
201
- ? s
202
- : s.k === "brand"
203
- ? (s.shape.shape.k === "obj" ? s.shape.shape : null)
204
- : null;
205
- if (!srcObj || srcObj.k !== "obj") return fail(`shape ${s.k} ⊭ obj`);
206
- for (const [key, slot] of Object.entries(t.slots)) {
207
- const srcSlot = srcObj.slots[key];
208
- if (!srcSlot) {
209
- if (slot.optional) continue;
210
- return fail(`missing slot ${key}`);
211
- }
212
- if (srcSlot.optional && !slot.optional) {
213
- return fail(`slot ${key}: optional ⊭ required`);
214
- }
215
- const r = leqWithPred(srcSlot.value, slot.value, phi, env, depth + 1);
216
- if (!r.ok) return fail(`slot ${key}: ${r.reason}`);
217
- }
218
- return ok();
219
- }
220
-
221
- // 数组
222
- if (t.k === "arr") {
223
- if (s.k === "arr") {
224
- return leqWithPred(s.element, t.element, phi, env, depth + 1);
225
- }
226
- if (s.k === "tuple") {
227
- return s.elements.every((el) => leqWithPred(el, t.element, phi, env, depth + 1).ok)
228
- ? ok()
229
- : fail("tuple ⊭ arr");
230
- }
231
- return fail(`shape ${s.k} ⊭ arr`);
232
- }
233
-
234
- // 元组
235
- if (t.k === "tuple") {
236
- if (s.k !== "tuple") return fail(`shape ${s.k} ⊭ tuple`);
237
- if (s.elements.length !== t.elements.length) {
238
- return fail(`tuple arity ${s.elements.length} ⊭ ${t.elements.length}`);
239
- }
240
- for (let i = 0; i < t.elements.length; i++) {
241
- const r = leqWithPred(s.elements[i]!, t.elements[i]!, phi, env, depth + 1);
242
- if (!r.ok) return fail(`tuple[${i}]: ${r.reason}`);
243
- }
244
- return ok();
245
- }
246
-
247
- // 函数:参数逆变、返回协变
248
- if (t.k === "fn") {
249
- if (s.k !== "fn") return fail(`shape ${s.k} ⊭ fn`);
250
- if (s.params.length !== t.params.length) {
251
- return fail(`fn arity ${s.params.length} ⊭ ${t.params.length}`);
252
- }
253
- if (t.returnType !== undefined) {
254
- const sr = s.returnType;
255
- if (!sr) return fail("fn return unknown ⊭ target return");
256
- const r = leqWithPred(sr, t.returnType, phi, env, depth + 1);
257
- if (!r.ok) return fail(`fn return: ${r.reason}`);
258
- }
259
- // 参数:tgt 参数 ≤ src 参数(逆变)—— 有 paramTypes 时检查
260
- if (s.paramTypes && t.paramTypes) {
261
- for (let i = 0; i < t.paramTypes.length; i++) {
262
- const sp = s.paramTypes[i];
263
- const tp = t.paramTypes[i];
264
- if (!sp || !tp) continue;
265
- const r = leqWithPred(tp, sp, phi, env, depth + 1);
266
- if (!r.ok) return fail(`fn param[${i}]: ${r.reason}`);
267
- }
268
- }
269
- return ok();
270
- }
271
-
272
- // brand:名义(同名或继承链)
273
- if (t.k === "brand") {
274
- if (s.k !== "brand") return fail(`shape ${s.k} ⊭ brand ${t.name}`);
275
- if (s.name === t.name) return ok();
276
- if (env) {
277
- const chain = getClassChain(env, s.name).map((c) => c.name);
278
- if (chain.includes(t.name)) return ok();
279
- }
280
- return fail(`brand ${s.name} ⊭ brand ${t.name}`);
281
- }
282
-
283
- // eff
284
- if (t.k === "eff") {
285
- if (s.k !== "eff" || s.eff !== t.eff) return fail(`shape ${s.k} ⊭ eff ${t.eff}`);
286
- return leqWithPred(s.inner, t.inner, phi, env, depth + 1);
287
- }
288
-
289
- if (s.k === t.k) return ok();
290
- return fail(`shape ${s.k} ⊭ ${t.k}`);
291
- }
292
-
293
- function primOf(a: Abs): "number" | "string" | "boolean" | "bigint" | "symbol" | undefined {
294
- if (a.shape.k === "prim") return a.shape.type;
295
- const lv = litValue(a);
296
- if (typeof lv === "number") return "number";
297
- if (typeof lv === "string") return "string";
298
- if (typeof lv === "boolean") return "boolean";
299
- if (typeof lv === "bigint") return "bigint";
300
- return undefined;
301
- }
@@ -1,97 +0,0 @@
1
- /**
2
- * loadModule 可达依赖的内容指纹(check 整文件 memo + generalize L0 共用)。
3
- * 覆盖 @nudo:import / ESM from / require / dynamic import;对每条 spec 做
4
- * 传递 BFS(seen 防环),直到节点上限。截断时 truncated=true——调用方必须
5
- * fail-open(不得写入/读取 memo)。
6
- */
7
- import { extractNudoImports } from "./refine.ts";
8
- import { hashSource } from "./hash-source.ts";
9
-
10
- export type LoadDepsFingerprint = {
11
- fp: string;
12
- paths: string[];
13
- /** BFS 撞到节点上限:剩余 dep 未进指纹,键不可信 */
14
- truncated: boolean;
15
- };
16
-
17
- /** 防病态图;seen 已防环,此上限只限制遍历量 */
18
- const MAX_LOAD_DEP_NODES = 64;
19
-
20
- export function normPath(p: string): string {
21
- return p.replace(/\\/g, "/");
22
- }
23
-
24
- /** core 不引 path:相对 spec 纯字符串拼接(与 LSP resolve 对齐时双方 norm) */
25
- export function resolveDepPath(fromFile: string, spec: string): string {
26
- const s = normPath(spec);
27
- if (!s.startsWith(".")) return s;
28
- const from = normPath(fromFile);
29
- const i = from.lastIndexOf("/");
30
- const base = i >= 0 ? from.slice(0, i) : "";
31
- const parts = base ? base.split("/") : [];
32
- for (const seg of s.split("/")) {
33
- if (seg === "" || seg === ".") continue;
34
- if (seg === "..") parts.pop();
35
- else parts.push(seg);
36
- }
37
- return parts.join("/");
38
- }
39
-
40
- /**
41
- * All string module specs that analysis may resolve through loadModule.
42
- * Regex over-approximates (may hit comments) — extra dep fingerprint is safe
43
- * (more misses, never a stale hit).
44
- */
45
- export function extractAllLoadSpecs(source: string): string[] {
46
- const specs = new Set<string>();
47
- for (const imp of extractNudoImports(source)) specs.add(imp.spec);
48
- const patterns = [
49
- /\bfrom\s*['"]([^'"]+)['"]/g,
50
- /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
51
- /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
52
- ];
53
- for (const re of patterns) {
54
- let m: RegExpExecArray | null;
55
- while ((m = re.exec(source))) specs.add(m[1]!);
56
- }
57
- return [...specs];
58
- }
59
-
60
- /**
61
- * Fingerprint every loadModule-reachable dep (not only `*.nudo.js`), including
62
- * one hop of transitive specs from each dep source.
63
- */
64
- export function loadModuleDepsFingerprint(
65
- source: string,
66
- loadModule: (spec: string, fromFile: string) => string | undefined,
67
- fromFile: string,
68
- ): LoadDepsFingerprint {
69
- const parts: string[] = [];
70
- const paths: string[] = [];
71
- const seen = new Set<string>();
72
- const queue: Array<{ spec: string; from: string }> = extractAllLoadSpecs(source).map(
73
- (spec) => ({ spec, from: fromFile }),
74
- );
75
- let n = 0;
76
- while (queue.length > 0) {
77
- if (n >= MAX_LOAD_DEP_NODES) {
78
- parts.sort();
79
- return { fp: `trunc:${parts.join(",")}`, paths, truncated: true };
80
- }
81
- const cur = queue.shift()!;
82
- const path = resolveDepPath(cur.from, cur.spec);
83
- if (seen.has(path)) continue;
84
- seen.add(path);
85
- n++;
86
- const src = loadModule(cur.spec, cur.from);
87
- parts.push(`${path}=${src === undefined ? "miss" : hashSource(src)}`);
88
- paths.push(path);
89
- if (src === undefined) continue;
90
- for (const next of extractAllLoadSpecs(src)) {
91
- const nextPath = resolveDepPath(path, next);
92
- if (!seen.has(nextPath)) queue.push({ spec: next, from: path });
93
- }
94
- }
95
- parts.sort();
96
- return { fp: parts.join(","), paths, truncated: false };
97
- }
@@ -1,128 +0,0 @@
1
- /**
2
- * Abs 方法表:模板字符串 / 结构值上的方法与属性。
3
- * 类型即计算——方法结果仍是 Abs,可继续参与约束推理。
4
- * 宿主 TypeValue 的 dispatchMethod 只服务 IR 兜底,不是真理源。
5
- */
6
-
7
- import type { Abs } from "./abs.ts";
8
- import { abs, litValue, numLit, strLit, boolLit } from "./abs.ts";
9
- import {
10
- isTemplateLike,
11
- templatePartsOf,
12
- concatString,
13
- } from "./template.ts";
14
-
15
- function strPrim(conf: Abs["conf"] = "path"): Abs {
16
- return abs({ k: "prim", type: "string" }, undefined, undefined, conf);
17
- }
18
-
19
- function boolPrim(): Abs {
20
- return abs({ k: "prim", type: "boolean" }, undefined, undefined, "partial");
21
- }
22
-
23
- function knownPrefix(parts: Abs[]): string {
24
- let s = "";
25
- for (const p of parts) {
26
- if (p.term?.op === "lit" && typeof p.term.value === "string") s += p.term.value;
27
- else break;
28
- }
29
- return s;
30
- }
31
-
32
- function knownSuffix(parts: Abs[]): string {
33
- let s = "";
34
- for (let i = parts.length - 1; i >= 0; i--) {
35
- const p = parts[i]!;
36
- if (p.term?.op === "lit" && typeof p.term.value === "string") s = p.term.value + s;
37
- else break;
38
- }
39
- return s;
40
- }
41
-
42
- function fixedLength(parts: Abs[]): number | undefined {
43
- if (parts.some((p) => !(p.term?.op === "lit" && typeof p.term.value === "string"))) {
44
- return undefined;
45
- }
46
- return parts.reduce((n, p) => n + String(p.term && p.term.op === "lit" ? p.term.value : "").length, 0);
47
- }
48
-
49
- /**
50
- * 调用 Abs 方法。返回 undefined = 未接管(调用方走其它路径)。
51
- */
52
- export function callAbsMethod(
53
- recv: Abs,
54
- name: string,
55
- args: Abs[],
56
- ): Abs | undefined {
57
- // 模板 / 字符串
58
- if (isTemplateLike(recv) || (recv.shape.k === "prim" && recv.shape.type === "string")) {
59
- const a0 = args[0] ? litValue(args[0]) : undefined;
60
- if (isTemplateLike(recv)) {
61
- const parts = templatePartsOf(recv);
62
- const prefix = knownPrefix(parts);
63
- const suffix = knownSuffix(parts);
64
- switch (name) {
65
- case "startsWith": {
66
- if (typeof a0 !== "string") return boolPrim();
67
- if (prefix.length >= a0.length) return boolLit(prefix.startsWith(a0));
68
- if (a0.startsWith(prefix)) return boolPrim();
69
- return boolLit(false);
70
- }
71
- case "endsWith": {
72
- if (typeof a0 !== "string") return boolPrim();
73
- if (suffix.length >= a0.length) return boolLit(suffix.endsWith(a0));
74
- if (a0.endsWith(suffix)) return boolPrim();
75
- return boolLit(false);
76
- }
77
- case "includes": {
78
- if (typeof a0 !== "string") return boolPrim();
79
- const fixed = parts
80
- .filter((p) => p.term?.op === "lit" && typeof p.term.value === "string")
81
- .map((p) => String(p.term && p.term.op === "lit" ? p.term.value : ""))
82
- .join("");
83
- if (fixed.includes(a0)) return boolLit(true);
84
- return boolPrim();
85
- }
86
- case "toUpperCase":
87
- case "toLowerCase":
88
- case "trim":
89
- return strPrim("path");
90
- case "slice":
91
- return strPrim("path");
92
- case "concat": {
93
- let acc = recv;
94
- for (const a of args) acc = concatString(acc, a);
95
- return acc;
96
- }
97
- case "toString":
98
- case "valueOf":
99
- return recv;
100
- }
101
- }
102
- }
103
-
104
- // 数组 / 元组 join 等已在 ast-eval;此处只补模板
105
- return undefined;
106
- }
107
-
108
- /** 属性读取:template.length 等 */
109
- export function getAbsProperty(recv: Abs, name: string): Abs | undefined {
110
- if (name === "length") {
111
- if (isTemplateLike(recv)) {
112
- const parts = templatePartsOf(recv);
113
- const n = fixedLength(parts);
114
- if (n !== undefined) return numLit(n);
115
- // 有抽象部件:下界 = 固定前缀+后缀长度
116
- const min = parts
117
- .filter((p) => p.term?.op === "lit" && typeof p.term.value === "string")
118
- .reduce((s, p) => s + String(p.term && p.term.op === "lit" ? p.term.value : "").length, 0);
119
- return abs(
120
- { k: "prim", type: "number" },
121
- undefined,
122
- undefined,
123
- "path",
124
- );
125
- }
126
- }
127
- return undefined;
128
- }