@nudojs/core 0.3.0 → 0.3.1
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-BU4A6XJ3.js +6856 -0
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +168 -0
- package/dist/index-CVYjXnwU.d.ts +902 -0
- package/dist/index.d.ts +64 -710
- package/dist/index.js +3258 -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,583 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* B 路径运行时:transpile 后的程序在 Node 上执行时,值就是 Abs。
|
|
3
|
-
* 与 AST 解释器语义同构;TypeValue 不再是求值载体。
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Abs } from "../abs.ts";
|
|
7
|
-
import { abs, bool, boolLit, confJoin, litValue, unknown } from "../abs.ts";
|
|
8
|
-
import { absFunction } from "../abs-fn.ts";
|
|
9
|
-
import { add, sub, mul, div, mod, cmp } from "../arithmetic.ts";
|
|
10
|
-
import { typeofAbs, negAbs, notAbs, strictEqAbs } from "../surface.ts";
|
|
11
|
-
import { joinAbs, objOf, isObj, spread as spreadObj, type ObjShape } from "../objects.ts";
|
|
12
|
-
import { leqAbs } from "../leq.ts";
|
|
13
|
-
import type { Phi } from "../pred.ts";
|
|
14
|
-
import { pTrue } from "../pred.ts";
|
|
15
|
-
import { noteUnknownMemberMissing } from "./calls.ts";
|
|
16
|
-
|
|
17
|
-
/** 当前路径前提 Φ(transpile 后的 fork 会压栈) */
|
|
18
|
-
let phi: Phi = pTrue;
|
|
19
|
-
|
|
20
|
-
export function currentExecPhi(): Phi {
|
|
21
|
-
return phi;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function withExecPhi<T>(p: Phi, body: () => T): T {
|
|
25
|
-
const prev = phi;
|
|
26
|
-
phi = p;
|
|
27
|
-
try {
|
|
28
|
-
return body();
|
|
29
|
-
} finally {
|
|
30
|
-
phi = prev;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// --- 运算符重载面(transpile 目标)---
|
|
35
|
-
|
|
36
|
-
export function $add(a: Abs, b: Abs): Abs {
|
|
37
|
-
return add(a, b, phi);
|
|
38
|
-
}
|
|
39
|
-
export function $sub(a: Abs, b: Abs): Abs {
|
|
40
|
-
return sub(a, b, phi);
|
|
41
|
-
}
|
|
42
|
-
export function $mul(a: Abs, b: Abs): Abs {
|
|
43
|
-
return mul(a, b, phi);
|
|
44
|
-
}
|
|
45
|
-
export function $div(a: Abs, b: Abs): Abs {
|
|
46
|
-
return div(a, b, phi);
|
|
47
|
-
}
|
|
48
|
-
export function $mod(a: Abs, b: Abs): Abs {
|
|
49
|
-
return mod(a, b, phi);
|
|
50
|
-
}
|
|
51
|
-
export function $neg(a: Abs): Abs {
|
|
52
|
-
return negAbs(a);
|
|
53
|
-
}
|
|
54
|
-
export function $typeof(a: Abs): Abs {
|
|
55
|
-
return typeofAbs(a);
|
|
56
|
-
}
|
|
57
|
-
export function $not(a: Abs): Abs {
|
|
58
|
-
return notAbs(a);
|
|
59
|
-
}
|
|
60
|
-
export function $eq(a: Abs, b: Abs): Abs {
|
|
61
|
-
const r = strictEqAbs(a, b);
|
|
62
|
-
return r === undefined ? bool() : boolLit(r);
|
|
63
|
-
}
|
|
64
|
-
export function $ne(a: Abs, b: Abs): Abs {
|
|
65
|
-
const r = strictEqAbs(a, b);
|
|
66
|
-
return r === undefined ? bool() : boolLit(!r);
|
|
67
|
-
}
|
|
68
|
-
export function $lt(a: Abs, b: Abs): Abs {
|
|
69
|
-
return cmp("lt", a, b, phi);
|
|
70
|
-
}
|
|
71
|
-
export function $le(a: Abs, b: Abs): Abs {
|
|
72
|
-
return cmp("le", a, b, phi);
|
|
73
|
-
}
|
|
74
|
-
export function $gt(a: Abs, b: Abs): Abs {
|
|
75
|
-
return cmp("gt", a, b, phi);
|
|
76
|
-
}
|
|
77
|
-
export function $ge(a: Abs, b: Abs): Abs {
|
|
78
|
-
return cmp("ge", a, b, phi);
|
|
79
|
-
}
|
|
80
|
-
export function $join(a: Abs, b: Abs): Abs {
|
|
81
|
-
return joinAbs(asAbsVal(a), asAbsVal(b));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** apply 型 impl 的占位 body(AbsFnImpl.body 必需;$call 走 apply 不经 body) */
|
|
85
|
-
const noBody = { type: "BlockStatement", body: [], directives: [] } as never;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* transpile 泄漏的 JS 函数值 → 一等 fn Abs。
|
|
89
|
-
* B 路径把函数声明/表达式编译成真实 JS 函数;它们流进对象槽、
|
|
90
|
-
* 元组、join 等 Abs 结构时不能裸存——下游(bridge/leq/join)读 `.shape`。
|
|
91
|
-
* 参数名无法从运行时函数恢复(用 fn.length → argN,与 analyzer 的
|
|
92
|
-
* extractParamNames 回退口径一致);带真实参数名走 $fnVal(transpile 侧)。
|
|
93
|
-
*/
|
|
94
|
-
export function asAbsVal(v: unknown): Abs {
|
|
95
|
-
if (v && typeof v === "object" && "shape" in (v as object)) return v as Abs;
|
|
96
|
-
if (typeof v === "function") {
|
|
97
|
-
const n = Math.max(0, v.length);
|
|
98
|
-
const params = Array.from({ length: n }, (_, i) => `arg${i}`);
|
|
99
|
-
return absFunction(params, {
|
|
100
|
-
body: noBody,
|
|
101
|
-
apply: (args) => (v as (...a: Abs[]) => Abs)(...args),
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return $lit(v as never);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** 函数表达式 → 一等 fn Abs(transpile 侧带真实参数名;异步 body 包 $async) */
|
|
108
|
-
export function $fnVal(params: string[], impl: (...args: Abs[]) => Abs): Abs {
|
|
109
|
-
return absFunction(params, { body: noBody, apply: (args) => impl(...args) });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** 字面量 → Abs(transpile 侧数字/字符串/布尔/null/undefined) */
|
|
113
|
-
export function $lit(v: unknown): Abs {
|
|
114
|
-
if (v && typeof v === "object" && "shape" in (v as object) && "conf" in (v as object)) {
|
|
115
|
-
return v as Abs;
|
|
116
|
-
}
|
|
117
|
-
return litAbsFromJs(v);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function litAbsFromJs(v: unknown): Abs {
|
|
121
|
-
if (v === null || v === undefined) {
|
|
122
|
-
return abs({ k: "unknown" }, { op: "lit", value: v as never }, pTrue, "exact");
|
|
123
|
-
}
|
|
124
|
-
const t = typeof v;
|
|
125
|
-
if (t === "number" || t === "string" || t === "boolean" || t === "bigint") {
|
|
126
|
-
return abs(
|
|
127
|
-
{ k: "prim", type: t as "number" | "string" | "boolean" | "bigint" },
|
|
128
|
-
{ op: "lit", value: v as never },
|
|
129
|
-
pTrue,
|
|
130
|
-
"exact",
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
return unknown;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export function isDefinitelyTrue(a: Abs): boolean {
|
|
137
|
-
return litValue(a) === true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function isDefinitelyFalse(a: Abs): boolean {
|
|
141
|
-
const lv = litValue(a);
|
|
142
|
-
if (lv === false) return true;
|
|
143
|
-
if (a.shape.k === "never") return true;
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function undef(): Abs {
|
|
148
|
-
return abs({ k: "unknown" }, { op: "lit", value: undefined as never }, pTrue, "exact");
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* if:两侧都探索(抽象条件),具体条件短路。
|
|
153
|
-
*/
|
|
154
|
-
export function $fork(test: Abs, consequent: () => Abs, alternate?: () => Abs): Abs {
|
|
155
|
-
if (isDefinitelyTrue(test)) return asAbsVal(consequent());
|
|
156
|
-
if (isDefinitelyFalse(test)) return alternate ? asAbsVal(alternate()) : undef();
|
|
157
|
-
|
|
158
|
-
const a = asAbsVal(consequent());
|
|
159
|
-
const b = alternate ? asAbsVal(alternate()) : undef();
|
|
160
|
-
return joinAbs(a, b);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export const DEFAULT_MAX_LOOP_ITERS = 8;
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* for 的惰性展开:生成器只负责「按上限吐状态」。
|
|
167
|
-
* 抽象条件无法诚实终止——消费者必须自带 maxIters。
|
|
168
|
-
*/
|
|
169
|
-
export function* $forIter(
|
|
170
|
-
init: Abs,
|
|
171
|
-
test: (s: Abs) => Abs,
|
|
172
|
-
step: (s: Abs) => Abs,
|
|
173
|
-
body: (s: Abs) => Abs,
|
|
174
|
-
maxIters: number = DEFAULT_MAX_LOOP_ITERS,
|
|
175
|
-
): Generator<{ state: Abs; test: Abs; afterBody: Abs }, void, void> {
|
|
176
|
-
let s = init;
|
|
177
|
-
for (let i = 0; i < maxIters; i++) {
|
|
178
|
-
const t = test(s);
|
|
179
|
-
if (isDefinitelyFalse(t)) {
|
|
180
|
-
yield { state: s, test: t, afterBody: s };
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
const afterBody = body(s);
|
|
184
|
-
yield { state: s, test: t, afterBody };
|
|
185
|
-
s = step(afterBody);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* 有界 for:unroll ≤ maxIters,每步「可能退出」的态 join;
|
|
191
|
-
* 相邻态 leq 视为不动点提前停。
|
|
192
|
-
*/
|
|
193
|
-
export function $for(
|
|
194
|
-
init: Abs,
|
|
195
|
-
test: (s: Abs) => Abs,
|
|
196
|
-
step: (s: Abs) => Abs,
|
|
197
|
-
body: (s: Abs) => Abs,
|
|
198
|
-
maxIters: number = DEFAULT_MAX_LOOP_ITERS,
|
|
199
|
-
): Abs {
|
|
200
|
-
let state = init;
|
|
201
|
-
let exitJoin: Abs | undefined;
|
|
202
|
-
|
|
203
|
-
for (let i = 0; i < maxIters; i++) {
|
|
204
|
-
const t = test(state);
|
|
205
|
-
if (isDefinitelyFalse(t)) {
|
|
206
|
-
return exitJoin ? joinAbs(exitJoin, state) : state;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (!isDefinitelyTrue(t)) {
|
|
210
|
-
exitJoin = exitJoin ? joinAbs(exitJoin, state) : state;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const afterBody = body(state);
|
|
214
|
-
const next = step(afterBody);
|
|
215
|
-
|
|
216
|
-
// 不动点:字面量不变,或两侧皆非字面量且 next ≤ state
|
|
217
|
-
if (i > 0) {
|
|
218
|
-
const nv = litValue(next);
|
|
219
|
-
const sv = litValue(state);
|
|
220
|
-
const stuck =
|
|
221
|
-
(nv !== undefined && sv !== undefined && nv === sv) ||
|
|
222
|
-
(nv === undefined && sv === undefined && leqAbs(next, state).ok);
|
|
223
|
-
if (stuck) {
|
|
224
|
-
return exitJoin ? joinAbs(exitJoin, next) : next;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
state = next;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return exitJoin ? joinAbs(exitJoin, state) : state;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// --- 数组 ---
|
|
234
|
-
|
|
235
|
-
/** 数组字面量 → Abs tuple(长度已知) */
|
|
236
|
-
export function $arr(items: Abs[]): Abs {
|
|
237
|
-
return abs({ k: "tuple", elements: items.map(asAbsVal) }, undefined, undefined, "exact");
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/** 下标读 a[i];字面量 i 走 tuple 精确投影,否则并所有元素 */
|
|
241
|
-
export function $idx(a: Abs, i: Abs): Abs {
|
|
242
|
-
const iv = litValue(i);
|
|
243
|
-
if (a.shape.k === "tuple") {
|
|
244
|
-
const els = a.shape.elements;
|
|
245
|
-
if (typeof iv === "number" && Number.isInteger(iv)) {
|
|
246
|
-
if (iv >= 0 && iv < els.length) return els[iv]!;
|
|
247
|
-
return undef();
|
|
248
|
-
}
|
|
249
|
-
if (els.length === 0) return undef();
|
|
250
|
-
return els.reduce((x, y) => joinAbs(x, y));
|
|
251
|
-
}
|
|
252
|
-
if (a.shape.k === "arr") return a.shape.element;
|
|
253
|
-
if (a.shape.k === "sum") {
|
|
254
|
-
return a.shape.members.map((m) => $idx(m, i)).reduce((x, y) => joinAbs(x, y));
|
|
255
|
-
}
|
|
256
|
-
return unknown;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/** 下标写 a[i]=v → 新 tuple */
|
|
260
|
-
export function $idxSet(a: Abs, i: Abs, value: Abs): Abs {
|
|
261
|
-
const iv = litValue(i);
|
|
262
|
-
if (a.shape.k === "tuple" && typeof iv === "number" && Number.isInteger(iv)) {
|
|
263
|
-
const els = [...a.shape.elements];
|
|
264
|
-
if (iv >= 0 && iv < els.length) {
|
|
265
|
-
els[iv] = asAbsVal(value);
|
|
266
|
-
const next = abs({ k: "tuple", elements: els }, undefined, undefined, a.conf);
|
|
267
|
-
return next;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
return a;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/** 数组长度 */
|
|
274
|
-
export function $len(a: Abs): Abs {
|
|
275
|
-
if (a.shape.k === "tuple") {
|
|
276
|
-
return abs(
|
|
277
|
-
{ k: "prim", type: "number" },
|
|
278
|
-
{ op: "lit", value: a.shape.elements.length },
|
|
279
|
-
pTrue,
|
|
280
|
-
"exact",
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
if (a.shape.k === "arr") {
|
|
284
|
-
return abs({ k: "prim", type: "number" }, undefined, undefined, "path");
|
|
285
|
-
}
|
|
286
|
-
return unknown;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// --- 对象 / 成员 ---
|
|
290
|
-
|
|
291
|
-
/** 对象字面量 → Abs obj */
|
|
292
|
-
export function $obj(slots: Record<string, Abs>): Abs {
|
|
293
|
-
const s: Record<string, { value: Abs }> = {};
|
|
294
|
-
for (const [k, v] of Object.entries(slots)) s[k] = { value: asAbsVal(v) };
|
|
295
|
-
return objOf(s);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/** 对象展开 { ...a, b } */
|
|
299
|
-
export function $spread(a: Abs, b: Abs): Abs {
|
|
300
|
-
return spreadObj(asAbsVal(a), asAbsVal(b));
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/** 数组连接 [...a, ...b] / [...a, x] */
|
|
304
|
-
export function $concat(a: Abs, b: Abs): Abs {
|
|
305
|
-
a = asAbsVal(a);
|
|
306
|
-
b = asAbsVal(b);
|
|
307
|
-
if (a.shape.k === "tuple" && b.shape.k === "tuple") {
|
|
308
|
-
return abs(
|
|
309
|
-
{ k: "tuple", elements: [...a.shape.elements, ...b.shape.elements] },
|
|
310
|
-
undefined,
|
|
311
|
-
undefined,
|
|
312
|
-
confJoin(a.conf, b.conf),
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
if (a.shape.k === "tuple" && b.shape.k !== "tuple") {
|
|
316
|
-
// [...a, x]:x 作单元素
|
|
317
|
-
return abs(
|
|
318
|
-
{ k: "tuple", elements: [...a.shape.elements, b] },
|
|
319
|
-
undefined,
|
|
320
|
-
undefined,
|
|
321
|
-
confJoin(a.conf, b.conf),
|
|
322
|
-
);
|
|
323
|
-
}
|
|
324
|
-
if (a.shape.k !== "tuple" && b.shape.k === "tuple") {
|
|
325
|
-
return abs(
|
|
326
|
-
{ k: "tuple", elements: [a, ...b.shape.elements] },
|
|
327
|
-
undefined,
|
|
328
|
-
undefined,
|
|
329
|
-
confJoin(a.conf, b.conf),
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
// 抽象数组:元素类型 join
|
|
333
|
-
const ea = a.shape.k === "arr" ? a.shape.element : a;
|
|
334
|
-
const eb = b.shape.k === "arr" ? b.shape.element : b;
|
|
335
|
-
return abs({ k: "arr", element: joinAbs(ea, eb) }, undefined, undefined, "path");
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/** 元素列表(tuple 展开;arr 抽象) */
|
|
339
|
-
export function $elems(a: Abs): Abs[] {
|
|
340
|
-
if (a.shape.k === "tuple") return [...a.shape.elements];
|
|
341
|
-
if (a.shape.k === "arr") return [a.shape.element];
|
|
342
|
-
return [unknown];
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* for-of:对 iterable 每个元素跑 body;有界展开。
|
|
347
|
-
* body(item, i) 可返回 void;状态由外部 JS 变量承接。
|
|
348
|
-
*/
|
|
349
|
-
export function $forOf(
|
|
350
|
-
iterable: Abs,
|
|
351
|
-
body: (item: Abs, index: Abs) => void,
|
|
352
|
-
maxIters: number = DEFAULT_MAX_LOOP_ITERS,
|
|
353
|
-
): void {
|
|
354
|
-
const items = $elems(iterable);
|
|
355
|
-
const n = Math.min(items.length || maxIters, maxIters);
|
|
356
|
-
for (let i = 0; i < n; i++) {
|
|
357
|
-
const item =
|
|
358
|
-
items.length > 0
|
|
359
|
-
? items[Math.min(i, items.length - 1)]!
|
|
360
|
-
: unknown;
|
|
361
|
-
body(item, abs(
|
|
362
|
-
{ k: "prim", type: "number" },
|
|
363
|
-
{ op: "lit", value: i },
|
|
364
|
-
pTrue,
|
|
365
|
-
"exact",
|
|
366
|
-
));
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
/** 成员读:obj.slots[key];缺失 → undefined 字面量;brand 解包内层 */
|
|
371
|
-
export function $get(
|
|
372
|
-
o: Abs,
|
|
373
|
-
key: string,
|
|
374
|
-
opts?: { /** 调用方已负责诊断(如 $invoke) */ silent?: boolean },
|
|
375
|
-
): Abs {
|
|
376
|
-
if (o.shape.k === "brand") return $get(o.shape.shape, key, opts);
|
|
377
|
-
if (isObj(o)) {
|
|
378
|
-
const slot = (o.shape as ObjShape).slots[key];
|
|
379
|
-
if (slot) return slot.value;
|
|
380
|
-
if ((o.shape as ObjShape).open) return unknown;
|
|
381
|
-
return undef();
|
|
382
|
-
}
|
|
383
|
-
if (o.shape.k === "sum") {
|
|
384
|
-
const parts = o.shape.members.map((m) => $get(m, key, opts));
|
|
385
|
-
return parts.reduce((a, b) => joinAbs(a, b));
|
|
386
|
-
}
|
|
387
|
-
if (!opts?.silent) {
|
|
388
|
-
// 裸属性访问落在 unknown 上 → unknown-recv($invoke 自己报 method)
|
|
389
|
-
noteUnknownMemberMissing(o, key, "property");
|
|
390
|
-
}
|
|
391
|
-
return unknown;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/** 成员写:返回新 obj/brand(不可变更新) */
|
|
395
|
-
export function $set(o: Abs, key: string, value: Abs): Abs {
|
|
396
|
-
if (o.shape.k === "brand") {
|
|
397
|
-
const inner = $set(o.shape.shape, key, value);
|
|
398
|
-
return abs(
|
|
399
|
-
{ k: "brand", name: o.shape.name, shape: inner },
|
|
400
|
-
o.term,
|
|
401
|
-
o.pred,
|
|
402
|
-
confJoin(o.conf, value.conf),
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
if (!isObj(o)) return $obj({ [key]: value });
|
|
406
|
-
const shape = o.shape as ObjShape;
|
|
407
|
-
const slots = { ...shape.slots, [key]: { value: asAbsVal(value) } };
|
|
408
|
-
const next = objOf(slots, {
|
|
409
|
-
index: shape.index,
|
|
410
|
-
open: shape.open,
|
|
411
|
-
});
|
|
412
|
-
next.conf = confJoin(o.conf, value.conf);
|
|
413
|
-
return next;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* 有界 while:state 线程穿 test/step(与 $for 同折叠语义)。
|
|
418
|
-
* 抽象条件无法诚实终止——必须 maxIters。
|
|
419
|
-
*/
|
|
420
|
-
export function $while(
|
|
421
|
-
init: Abs,
|
|
422
|
-
test: (s: Abs) => Abs,
|
|
423
|
-
step: (s: Abs) => Abs,
|
|
424
|
-
maxIters: number = DEFAULT_MAX_LOOP_ITERS,
|
|
425
|
-
): Abs {
|
|
426
|
-
let state = init;
|
|
427
|
-
let exitJoin: Abs | undefined;
|
|
428
|
-
|
|
429
|
-
for (let i = 0; i < maxIters; i++) {
|
|
430
|
-
const t = test(state);
|
|
431
|
-
if (isDefinitelyFalse(t)) {
|
|
432
|
-
return exitJoin ? joinAbs(exitJoin, state) : state;
|
|
433
|
-
}
|
|
434
|
-
if (!isDefinitelyTrue(t)) {
|
|
435
|
-
exitJoin = exitJoin ? joinAbs(exitJoin, state) : state;
|
|
436
|
-
}
|
|
437
|
-
const next = step(state);
|
|
438
|
-
if (i > 0) {
|
|
439
|
-
const nv = litValue(next);
|
|
440
|
-
const sv = litValue(state);
|
|
441
|
-
const stuck =
|
|
442
|
-
(nv !== undefined && sv !== undefined && nv === sv) ||
|
|
443
|
-
(nv === undefined && sv === undefined && leqAbs(next, state).ok);
|
|
444
|
-
if (stuck) {
|
|
445
|
-
return exitJoin ? joinAbs(exitJoin, next) : next;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
state = next;
|
|
449
|
-
}
|
|
450
|
-
return exitJoin ? joinAbs(exitJoin, state) : state;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* 顺序 while(具体/可变闭包):body 内对 JS 变量赋值。
|
|
455
|
-
* 适合 transpile `let i; while (…) { i = … }`;抽象条件仍靠预算截断,
|
|
456
|
-
* 不保证 exit-join 健全——健全路径用 $while/$for + 状态对象。
|
|
457
|
-
*/
|
|
458
|
-
export function $whileSeq(
|
|
459
|
-
test: () => Abs,
|
|
460
|
-
body: () => void,
|
|
461
|
-
maxIters: number = DEFAULT_MAX_LOOP_ITERS,
|
|
462
|
-
): void {
|
|
463
|
-
for (let i = 0; i < maxIters; i++) {
|
|
464
|
-
const t = test();
|
|
465
|
-
if (isDefinitelyFalse(t)) return;
|
|
466
|
-
body();
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// --- throws ---
|
|
471
|
-
|
|
472
|
-
/** B 路径 throw 载荷:携带 Abs 抛出值 */
|
|
473
|
-
export class NudoThrow extends Error {
|
|
474
|
-
readonly absValue: Abs;
|
|
475
|
-
constructor(absValue: Abs) {
|
|
476
|
-
super("nudo:throw");
|
|
477
|
-
this.name = "NudoThrow";
|
|
478
|
-
this.absValue = absValue;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/** transpile `throw x` → `$throw(x)` */
|
|
483
|
-
export function $throw(v: Abs): never {
|
|
484
|
-
throw new NudoThrow(v);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
export function isNudoThrow(e: unknown): e is NudoThrow {
|
|
488
|
-
return e instanceof NudoThrow;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/** catch 参数:从 NudoThrow 取出 Abs,否则 unknown */
|
|
492
|
-
export function $catchVal(e: unknown): Abs {
|
|
493
|
-
if (isNudoThrow(e)) return e.absValue;
|
|
494
|
-
if (e instanceof Error) {
|
|
495
|
-
return abs(
|
|
496
|
-
{ k: "brand", name: e.name || "Error", shape: objOf({}) },
|
|
497
|
-
undefined,
|
|
498
|
-
undefined,
|
|
499
|
-
"path",
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
return unknown;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
// --- async / await ---
|
|
506
|
-
|
|
507
|
-
function wrapPromiseAbs(inner: Abs): Abs {
|
|
508
|
-
inner = asAbsVal(inner);
|
|
509
|
-
return abs(
|
|
510
|
-
{ k: "eff", eff: "promise", inner },
|
|
511
|
-
undefined,
|
|
512
|
-
undefined,
|
|
513
|
-
confJoin(inner.conf, "path"),
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
function awaitAbsVal(v: Abs): Abs {
|
|
518
|
-
if (v.shape.k === "eff" && v.shape.eff === "promise") return v.shape.inner;
|
|
519
|
-
return v;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* async 函数体包进 thunk,返回值经 wrapPromise。
|
|
524
|
-
*/
|
|
525
|
-
export function $async(thunk: () => Abs): Abs {
|
|
526
|
-
return wrapPromiseAbs(thunk());
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/** await → 解包 eff("promise") */
|
|
530
|
-
export function $await(v: Abs): Abs {
|
|
531
|
-
return awaitAbsVal(v);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
/** async 直接 return 的 coerce */
|
|
535
|
-
export function $asyncReturn(v: Abs): Abs {
|
|
536
|
-
if (v.shape.k === "eff") return v;
|
|
537
|
-
return wrapPromiseAbs(v);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// --- 生成器 ---
|
|
541
|
-
|
|
542
|
-
let yieldStack: Abs[][] = [];
|
|
543
|
-
|
|
544
|
-
/** function* 体:收集所有 yield 值为 tuple Abs */
|
|
545
|
-
export function $gen(body: () => void): Abs {
|
|
546
|
-
const ys: Abs[] = [];
|
|
547
|
-
yieldStack.push(ys);
|
|
548
|
-
try {
|
|
549
|
-
body();
|
|
550
|
-
} finally {
|
|
551
|
-
yieldStack.pop();
|
|
552
|
-
}
|
|
553
|
-
return $arr(ys);
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/** yield v:压入当前生成器收集器;表达式值用 unknown */
|
|
557
|
-
export function $yield(v: Abs): Abs {
|
|
558
|
-
const top = yieldStack[yieldStack.length - 1];
|
|
559
|
-
if (top) top.push(v);
|
|
560
|
-
return unknown;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* switch:具体 disc 选中匹配 case;抽象 disc 并所有分支。
|
|
565
|
-
*/
|
|
566
|
-
export function $switch(
|
|
567
|
-
disc: Abs,
|
|
568
|
-
cases: Array<{ test: Abs; run: () => Abs }>,
|
|
569
|
-
dflt?: () => Abs,
|
|
570
|
-
): Abs {
|
|
571
|
-
const dv = litValue(disc);
|
|
572
|
-
if (dv !== undefined) {
|
|
573
|
-
for (const c of cases) {
|
|
574
|
-
const tv = litValue(c.test);
|
|
575
|
-
if (tv !== undefined && Object.is(tv, dv)) return asAbsVal(c.run());
|
|
576
|
-
}
|
|
577
|
-
return dflt ? asAbsVal(dflt()) : undef();
|
|
578
|
-
}
|
|
579
|
-
const parts = cases.map((c) => asAbsVal(c.run()));
|
|
580
|
-
if (dflt) parts.push(asAbsVal(dflt()));
|
|
581
|
-
if (parts.length === 0) return undef();
|
|
582
|
-
return parts.reduce((a, b) => joinAbs(a, b));
|
|
583
|
-
}
|