@aster-cloud/aster-lang-ts 1.0.28 → 1.0.30
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/scripts/generate_error_codes.js +49 -6
- package/dist/scripts/generate_error_codes.js.map +1 -1
- package/dist/src/ai/providers/openai.d.ts +10 -0
- package/dist/src/ai/providers/openai.d.ts.map +1 -1
- package/dist/src/ai/providers/openai.js +4 -0
- package/dist/src/ai/providers/openai.js.map +1 -1
- package/dist/src/config/lexicons/regex-guard.d.ts.map +1 -1
- package/dist/src/config/lexicons/regex-guard.js +128 -0
- package/dist/src/config/lexicons/regex-guard.js.map +1 -1
- package/dist/src/core/interpreter.d.ts.map +1 -1
- package/dist/src/core/interpreter.js +221 -27
- package/dist/src/core/interpreter.js.map +1 -1
- package/dist/src/cst/cst_builder.js +0 -14
- package/dist/src/cst/cst_builder.js.map +1 -1
- package/dist/src/cst/cst_printer.d.ts.map +1 -1
- package/dist/src/cst/cst_printer.js +20 -3
- package/dist/src/cst/cst_printer.js.map +1 -1
- package/dist/src/diagnostics/error_codes.d.ts +3 -3
- package/dist/src/diagnostics/error_codes.d.ts.map +1 -1
- package/dist/src/diagnostics/error_codes.js +509 -78
- package/dist/src/diagnostics/error_codes.js.map +1 -1
- package/dist/src/formatter.d.ts +7 -0
- package/dist/src/formatter.d.ts.map +1 -1
- package/dist/src/formatter.js +175 -5
- package/dist/src/formatter.js.map +1 -1
- package/dist/src/frontend/canonicalizer.d.ts.map +1 -1
- package/dist/src/frontend/canonicalizer.js +47 -6
- package/dist/src/frontend/canonicalizer.js.map +1 -1
- package/dist/src/frontend/lexer.d.ts +2 -1
- package/dist/src/frontend/lexer.d.ts.map +1 -1
- package/dist/src/frontend/lexer.js +10 -3
- package/dist/src/frontend/lexer.js.map +1 -1
- package/dist/src/frontend/transformers.d.ts.map +1 -1
- package/dist/src/frontend/transformers.js +50 -3
- package/dist/src/frontend/transformers.js.map +1 -1
- package/dist/src/lower_to_core.js +101 -4
- package/dist/src/lower_to_core.js.map +1 -1
- package/dist/src/lsp/canonicalize-options.d.ts +26 -0
- package/dist/src/lsp/canonicalize-options.d.ts.map +1 -0
- package/dist/src/lsp/canonicalize-options.js +14 -0
- package/dist/src/lsp/canonicalize-options.js.map +1 -0
- package/dist/src/lsp/completion.d.ts.map +1 -1
- package/dist/src/lsp/completion.js +22 -4
- package/dist/src/lsp/completion.js.map +1 -1
- package/dist/src/lsp/server.js +29 -1
- package/dist/src/lsp/server.js.map +1 -1
- package/dist/src/lsp/tenant-init.d.ts +55 -0
- package/dist/src/lsp/tenant-init.d.ts.map +1 -0
- package/dist/src/lsp/tenant-init.js +88 -0
- package/dist/src/lsp/tenant-init.js.map +1 -0
- package/dist/src/mapping/candidate-generator.d.ts +84 -0
- package/dist/src/mapping/candidate-generator.d.ts.map +1 -0
- package/dist/src/mapping/candidate-generator.js +114 -0
- package/dist/src/mapping/candidate-generator.js.map +1 -0
- package/dist/src/mapping/entity-proposer.d.ts +85 -0
- package/dist/src/mapping/entity-proposer.d.ts.map +1 -0
- package/dist/src/mapping/entity-proposer.js +162 -0
- package/dist/src/mapping/entity-proposer.js.map +1 -0
- package/dist/src/mapping/mapping-ir.d.ts +85 -0
- package/dist/src/mapping/mapping-ir.d.ts.map +1 -0
- package/dist/src/mapping/mapping-ir.js +195 -0
- package/dist/src/mapping/mapping-ir.js.map +1 -0
- package/dist/src/mapping/proof-ir.d.ts +125 -0
- package/dist/src/mapping/proof-ir.d.ts.map +1 -0
- package/dist/src/mapping/proof-ir.js +48 -0
- package/dist/src/mapping/proof-ir.js.map +1 -0
- package/dist/src/mapping/quantity-ir.d.ts +93 -0
- package/dist/src/mapping/quantity-ir.d.ts.map +1 -0
- package/dist/src/mapping/quantity-ir.js +151 -0
- package/dist/src/mapping/quantity-ir.js.map +1 -0
- package/dist/src/mapping/source-ir.d.ts +82 -0
- package/dist/src/mapping/source-ir.d.ts.map +1 -0
- package/dist/src/mapping/source-ir.js +231 -0
- package/dist/src/mapping/source-ir.js.map +1 -0
- package/dist/src/nodeid/change-impact.d.ts +77 -0
- package/dist/src/nodeid/change-impact.d.ts.map +1 -0
- package/dist/src/nodeid/change-impact.js +132 -0
- package/dist/src/nodeid/change-impact.js.map +1 -0
- package/dist/src/nodeid/node-id-map.d.ts +69 -0
- package/dist/src/nodeid/node-id-map.d.ts.map +1 -0
- package/dist/src/nodeid/node-id-map.js +193 -0
- package/dist/src/nodeid/node-id-map.js.map +1 -0
- package/dist/src/parser/decl-parser.d.ts.map +1 -1
- package/dist/src/parser/decl-parser.js +10 -4
- package/dist/src/parser/decl-parser.js.map +1 -1
- package/dist/src/parser/expr-stmt-parser.d.ts.map +1 -1
- package/dist/src/parser/expr-stmt-parser.js +36 -24
- package/dist/src/parser/expr-stmt-parser.js.map +1 -1
- package/dist/src/parser/span-utils.d.ts +25 -0
- package/dist/src/parser/span-utils.d.ts.map +1 -1
- package/dist/src/parser/span-utils.js +43 -0
- package/dist/src/parser/span-utils.js.map +1 -1
- package/dist/src/parser/type-parser.d.ts.map +1 -1
- package/dist/src/parser/type-parser.js +21 -0
- package/dist/src/parser/type-parser.js.map +1 -1
- package/dist/src/typecheck/expression.d.ts.map +1 -1
- package/dist/src/typecheck/expression.js +26 -0
- package/dist/src/typecheck/expression.js.map +1 -1
- package/dist/test/fuzz/fuzz.test.js +62 -2
- package/dist/test/fuzz/fuzz.test.js.map +1 -1
- package/dist/test/unit/call-arity.test.d.ts +2 -0
- package/dist/test/unit/call-arity.test.d.ts.map +1 -0
- package/dist/test/unit/call-arity.test.js +71 -0
- package/dist/test/unit/call-arity.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/canonicalizer.test.js +42 -4
- package/dist/test/unit/canonicalizer/canonicalizer.test.js.map +1 -1
- package/dist/test/unit/canonicalizer/line-preservation.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.js +71 -0
- package/dist/test/unit/canonicalizer/line-preservation.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.js +98 -0
- package/dist/test/unit/canonicalizer/marker-restore-linear.test.js.map +1 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts +2 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.d.ts.map +1 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js +101 -0
- package/dist/test/unit/canonicalizer/possessive-anchor.test.js.map +1 -0
- package/dist/test/unit/formatter-redos.test.d.ts +2 -0
- package/dist/test/unit/formatter-redos.test.d.ts.map +1 -0
- package/dist/test/unit/formatter-redos.test.js +196 -0
- package/dist/test/unit/formatter-redos.test.js.map +1 -0
- package/dist/test/unit/interpreter/interpreter.test.js +7 -2
- package/dist/test/unit/interpreter/interpreter.test.js.map +1 -1
- package/dist/test/unit/lexer/bom.test.d.ts +2 -0
- package/dist/test/unit/lexer/bom.test.d.ts.map +1 -0
- package/dist/test/unit/lexer/bom.test.js +49 -0
- package/dist/test/unit/lexer/bom.test.js.map +1 -0
- package/dist/test/unit/list-get-bounds.test.d.ts +2 -0
- package/dist/test/unit/list-get-bounds.test.d.ts.map +1 -0
- package/dist/test/unit/list-get-bounds.test.js +66 -0
- package/dist/test/unit/list-get-bounds.test.js.map +1 -0
- package/dist/test/unit/long-bigint-exact.test.d.ts +2 -0
- package/dist/test/unit/long-bigint-exact.test.d.ts.map +1 -0
- package/dist/test/unit/long-bigint-exact.test.js +79 -0
- package/dist/test/unit/long-bigint-exact.test.js.map +1 -0
- package/dist/test/unit/lsp/completion-lexicon.test.d.ts +2 -0
- package/dist/test/unit/lsp/completion-lexicon.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/completion-lexicon.test.js +83 -0
- package/dist/test/unit/lsp/completion-lexicon.test.js.map +1 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts +2 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.d.ts.map +1 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.js +187 -0
- package/dist/test/unit/lsp/tenant-vocabulary.test.js.map +1 -0
- package/dist/test/unit/map-builtins.test.js +163 -3
- package/dist/test/unit/map-builtins.test.js.map +1 -1
- package/dist/test/unit/map-structural-equality.test.d.ts +2 -0
- package/dist/test/unit/map-structural-equality.test.d.ts.map +1 -0
- package/dist/test/unit/map-structural-equality.test.js +64 -0
- package/dist/test/unit/map-structural-equality.test.js.map +1 -0
- package/dist/test/unit/map-type-guard.test.js +8 -5
- package/dist/test/unit/map-type-guard.test.js.map +1 -1
- package/dist/test/unit/mapping/candidate-generator.test.d.ts +2 -0
- package/dist/test/unit/mapping/candidate-generator.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/candidate-generator.test.js +177 -0
- package/dist/test/unit/mapping/candidate-generator.test.js.map +1 -0
- package/dist/test/unit/mapping/entity-proposer.test.d.ts +2 -0
- package/dist/test/unit/mapping/entity-proposer.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/entity-proposer.test.js +168 -0
- package/dist/test/unit/mapping/entity-proposer.test.js.map +1 -0
- package/dist/test/unit/mapping/mapping-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/mapping-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/mapping-ir.test.js +144 -0
- package/dist/test/unit/mapping/mapping-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/proof-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/proof-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/proof-ir.test.js +119 -0
- package/dist/test/unit/mapping/proof-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/quantity-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/quantity-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/quantity-ir.test.js +144 -0
- package/dist/test/unit/mapping/quantity-ir.test.js.map +1 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.d.ts +2 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.js +182 -0
- package/dist/test/unit/mapping/quantity-redos-property.test.js.map +1 -0
- package/dist/test/unit/mapping/redos-budget.test.d.ts +2 -0
- package/dist/test/unit/mapping/redos-budget.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/redos-budget.test.js +168 -0
- package/dist/test/unit/mapping/redos-budget.test.js.map +1 -0
- package/dist/test/unit/mapping/source-ir.test.d.ts +2 -0
- package/dist/test/unit/mapping/source-ir.test.d.ts.map +1 -0
- package/dist/test/unit/mapping/source-ir.test.js +113 -0
- package/dist/test/unit/mapping/source-ir.test.js.map +1 -0
- package/dist/test/unit/nodeid/change-impact.test.d.ts +2 -0
- package/dist/test/unit/nodeid/change-impact.test.d.ts.map +1 -0
- package/dist/test/unit/nodeid/change-impact.test.js +152 -0
- package/dist/test/unit/nodeid/change-impact.test.js.map +1 -0
- package/dist/test/unit/nodeid/rename-declaration.test.d.ts +2 -0
- package/dist/test/unit/nodeid/rename-declaration.test.d.ts.map +1 -0
- package/dist/test/unit/nodeid/rename-declaration.test.js +75 -0
- package/dist/test/unit/nodeid/rename-declaration.test.js.map +1 -0
- package/dist/test/unit/parse-type-recursion.test.d.ts +2 -0
- package/dist/test/unit/parse-type-recursion.test.d.ts.map +1 -0
- package/dist/test/unit/parse-type-recursion.test.js +58 -0
- package/dist/test/unit/parse-type-recursion.test.js.map +1 -0
- package/dist/test/unit/regex-guard.test.js +64 -0
- package/dist/test/unit/regex-guard.test.js.map +1 -1
- package/dist/test/unit/strict-int-coercion.test.d.ts +2 -0
- package/dist/test/unit/strict-int-coercion.test.d.ts.map +1 -0
- package/dist/test/unit/strict-int-coercion.test.js +96 -0
- package/dist/test/unit/strict-int-coercion.test.js.map +1 -0
- package/dist/test/unit/ufcs-receiver.test.d.ts +2 -0
- package/dist/test/unit/ufcs-receiver.test.d.ts.map +1 -0
- package/dist/test/unit/ufcs-receiver.test.js +75 -0
- package/dist/test/unit/ufcs-receiver.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { NodeIdentity } from './node-id-map.js';
|
|
2
|
+
/**
|
|
3
|
+
* 跨版本 change impact(TS 侧)—— 与 `aster-lang-core` 的 `ChangeImpact` 对等实现。
|
|
4
|
+
*
|
|
5
|
+
* <h2>这是 ADR 0037 §4 的核心能力</h2>
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* $10,000 → $20,000
|
|
9
|
+
* ↓
|
|
10
|
+
* PaymentApproval.threshold 已 stale
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* 之所以能回答,是因为 `NodeIdMap` 把「是哪个节点」(nodeId)与「它变了没有」
|
|
14
|
+
* (contentHash)拆成两个字段:阈值改动时 nodeId 不变、contentHash 变,于是
|
|
15
|
+
* 可以报 `MODIFIED` 而不是「一个节点消失 + 一个节点出现」。
|
|
16
|
+
*
|
|
17
|
+
* <h2>★结果的解读边界</h2>
|
|
18
|
+
*
|
|
19
|
+
* `MODIFIED` 的判定是**内容指纹不同**,不是「语义变了」。两者大多数时候一致,
|
|
20
|
+
* 但并非总是——例如把 `x plus y` 写成 `y plus x`,指纹变而语义可能等价。
|
|
21
|
+
* **本模块只报「变了」,不声称「行为会不同」**;后者需要真正的语义比对。
|
|
22
|
+
*
|
|
23
|
+
* 同理 `REMOVED`/`ADDED` 在**重命名**时会成对出现——那是路径方案的固有代价
|
|
24
|
+
* (ADR 0037 §8.5),需由显式 rename 声明消解,而不是由本模块猜测。
|
|
25
|
+
* 猜错会把两个不同的节点当成同一个,比「识别为新节点」更危险。
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* 一次**显式声明**的重命名:`Rule approve` → `Rule assess`。
|
|
29
|
+
*
|
|
30
|
+
* <p>★为什么必须显式声明、不能自动推断:直觉上可以「按 contentHash 配对」,
|
|
31
|
+
* 但 **contentHash 不唯一**。两条函数体相同的规则,其 `body` / `statements[0]`
|
|
32
|
+
* / `ret` 等各级节点的 hash 全部相同:
|
|
33
|
+
*
|
|
34
|
+
* ```
|
|
35
|
+
* Rule alpha, produce: Rule beta, produce:
|
|
36
|
+
* Return 1. Return 1.
|
|
37
|
+
* → $.decls{alpha}.body 与 $.decls{beta}.body 的 contentHash **完全相同**
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* 于是把 `alpha` 改名为 `gamma` 后,`beta.body` 的 hash 在新版里能匹配到
|
|
41
|
+
* **两个**候选,无法判定谁是谁——自动配对会把两条规则的身份互换,且**不报错**。
|
|
42
|
+
*
|
|
43
|
+
* <p>★把两个不同的节点当成同一个,比「识别为新节点」危险得多:前者给出
|
|
44
|
+
* **错误**的溯源答案,后者只是丢失历史关联。立场是**宁可少认,不可错认**。
|
|
45
|
+
*/
|
|
46
|
+
export interface Rename {
|
|
47
|
+
readonly oldName: string;
|
|
48
|
+
readonly newName: string;
|
|
49
|
+
}
|
|
50
|
+
/** 构造并校验一条重命名声明。空名 / 新旧同名都是调用方的错误,直接暴露。 */
|
|
51
|
+
export declare function rename(oldName: string, newName: string): Rename;
|
|
52
|
+
export type ChangeKind =
|
|
53
|
+
/** nodeId 两侧都在,contentHash 不同 → 同一个节点,内容变了。 */
|
|
54
|
+
'MODIFIED'
|
|
55
|
+
/** nodeId 只在新版出现。 */
|
|
56
|
+
| 'ADDED'
|
|
57
|
+
/** nodeId 只在旧版出现。 */
|
|
58
|
+
| 'REMOVED';
|
|
59
|
+
export interface Change {
|
|
60
|
+
/** 稳定标识(`ADDED`/`REMOVED` 时只在一侧存在)。 */
|
|
61
|
+
readonly nodeId: string;
|
|
62
|
+
readonly kind: ChangeKind;
|
|
63
|
+
/** 节点类型(`Func`/`If`/...)。 */
|
|
64
|
+
readonly nodeKind: string;
|
|
65
|
+
/**
|
|
66
|
+
* 受本次变更波及的祖先 nodeId(由内向外),
|
|
67
|
+
* 即 ADR 0037 §4 里「谁已经 stale」的答案。
|
|
68
|
+
*/
|
|
69
|
+
readonly staleAncestors: readonly string[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 比较两个版本的节点标识表。
|
|
73
|
+
*
|
|
74
|
+
* @returns 变更列表,按 nodeId 字典序(确定顺序,便于比对与快照)
|
|
75
|
+
*/
|
|
76
|
+
export declare function diffNodeIds(before: ReadonlyMap<string, NodeIdentity>, after: ReadonlyMap<string, NodeIdentity>, renames?: readonly Rename[]): readonly Change[];
|
|
77
|
+
//# sourceMappingURL=change-impact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-impact.d.ts","sourceRoot":"","sources":["../../../src/nodeid/change-impact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,2CAA2C;AAC3C,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED,MAAM,MAAM,UAAU;AACpB,+CAA+C;AAC7C,UAAU;AACZ,qBAAqB;GACnB,OAAO;AACT,qBAAqB;GACnB,SAAS,CAAC;AAEd,MAAM,WAAW,MAAM;IACrB,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,6BAA6B;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,EACzC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,EACxC,OAAO,GAAE,SAAS,MAAM,EAAO,GAC9B,SAAS,MAAM,EAAE,CAmBnB"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/** 构造并校验一条重命名声明。空名 / 新旧同名都是调用方的错误,直接暴露。 */
|
|
2
|
+
export function rename(oldName, newName) {
|
|
3
|
+
if (!oldName?.trim() || !newName?.trim()) {
|
|
4
|
+
throw new Error('rename 的新旧名字都不能为空');
|
|
5
|
+
}
|
|
6
|
+
if (oldName === newName) {
|
|
7
|
+
throw new Error(`rename 的新旧名字相同:${oldName}`);
|
|
8
|
+
}
|
|
9
|
+
return { oldName, newName };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 比较两个版本的节点标识表。
|
|
13
|
+
*
|
|
14
|
+
* @returns 变更列表,按 nodeId 字典序(确定顺序,便于比对与快照)
|
|
15
|
+
*/
|
|
16
|
+
export function diffNodeIds(before, after, renames = []) {
|
|
17
|
+
if (renames.length > 0) {
|
|
18
|
+
before = applyRenames(before, renames);
|
|
19
|
+
}
|
|
20
|
+
const all = new Set([...before.keys(), ...after.keys()]);
|
|
21
|
+
const changes = [];
|
|
22
|
+
for (const id of [...all].sort()) {
|
|
23
|
+
const b = before.get(id);
|
|
24
|
+
const a = after.get(id);
|
|
25
|
+
if (b === undefined) {
|
|
26
|
+
changes.push({ nodeId: id, kind: 'ADDED', nodeKind: a.kind, staleAncestors: ancestorsOf(id, after) });
|
|
27
|
+
}
|
|
28
|
+
else if (a === undefined) {
|
|
29
|
+
changes.push({ nodeId: id, kind: 'REMOVED', nodeKind: b.kind, staleAncestors: ancestorsOf(id, before) });
|
|
30
|
+
}
|
|
31
|
+
else if (b.contentHash !== a.contentHash) {
|
|
32
|
+
changes.push({ nodeId: id, kind: 'MODIFIED', nodeKind: a.kind, staleAncestors: ancestorsOf(id, after) });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return changes;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 把旧版的 nodeId 按声明的重命名做**路径段替换**,使整棵子树一次性迁移。
|
|
39
|
+
*
|
|
40
|
+
* <p>★只替换完整的 `{name}` 路径段,不做子串替换——朴素的字符串 replace 会让
|
|
41
|
+
* `approve` 误伤 `approveAll`。
|
|
42
|
+
*
|
|
43
|
+
* <p>★同一个 oldName 不允许被声明成多个不同的新名字:那是自相矛盾的输入,
|
|
44
|
+
* 静默取其一会给出无声错误的溯源结果。
|
|
45
|
+
*/
|
|
46
|
+
function applyRenames(before, renames) {
|
|
47
|
+
const mapping = new Map();
|
|
48
|
+
for (const r of renames) {
|
|
49
|
+
const prev = mapping.get(r.oldName);
|
|
50
|
+
if (prev !== undefined && prev !== r.newName) {
|
|
51
|
+
throw new Error(`同一个名字被声明重命名到多个目标:${r.oldName} → ${prev} / ${r.newName}`);
|
|
52
|
+
}
|
|
53
|
+
mapping.set(r.oldName, r.newName);
|
|
54
|
+
}
|
|
55
|
+
const out = new Map();
|
|
56
|
+
for (const [id, v] of before) {
|
|
57
|
+
const renamed = renamePathSegments(id, mapping);
|
|
58
|
+
out.set(renamed, renamed === id ? v : { ...v, nodeId: renamed });
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/** 逐个 `{name}` 段做整段匹配替换。 */
|
|
63
|
+
function renamePathSegments(path, mapping) {
|
|
64
|
+
let out = '';
|
|
65
|
+
let i = 0;
|
|
66
|
+
while (i < path.length) {
|
|
67
|
+
const c = path[i];
|
|
68
|
+
if (c !== '{') {
|
|
69
|
+
out += c;
|
|
70
|
+
i++;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const close = path.indexOf('}', i);
|
|
74
|
+
if (close < 0) { // 不成对的 '{':原样输出,不猜
|
|
75
|
+
out += path.slice(i);
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
const name = path.slice(i + 1, close);
|
|
79
|
+
out += `{${mapping.get(name) ?? name}}`;
|
|
80
|
+
i = close + 1;
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 一个节点变更后,哪些祖先随之 stale。
|
|
86
|
+
*
|
|
87
|
+
* <p>★注意方向:**祖先 stale,后代不 stale**。改了 if 条件里的阈值,是「包含
|
|
88
|
+
* 它的那条规则」需要重新审视;而阈值节点的子节点(字面量本身)并没有变。
|
|
89
|
+
* 反过来标记会把影响面夸大到整棵子树。
|
|
90
|
+
*/
|
|
91
|
+
function ancestorsOf(nodeId, universe) {
|
|
92
|
+
const out = [];
|
|
93
|
+
let cur = nodeId;
|
|
94
|
+
for (;;) {
|
|
95
|
+
const cut = lastSegmentStart(cur);
|
|
96
|
+
if (cut <= 0)
|
|
97
|
+
break;
|
|
98
|
+
cur = cur.slice(0, cut);
|
|
99
|
+
if (universe.has(cur))
|
|
100
|
+
out.push(cur);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 找到最后一个路径段的起点。
|
|
106
|
+
*
|
|
107
|
+
* <p>路径形如 `$.decls{approve}.body.statements[0].cond`,分隔符是 `.`、`[`、`{`。
|
|
108
|
+
* ★不能简单用 `lastIndexOf('.')`——名字段 `{a.b.c}` 里可能含点(模块路径式的
|
|
109
|
+
* Import 名就是如此),那样会从名字中间切断,产生一个不存在的祖先。
|
|
110
|
+
*/
|
|
111
|
+
function lastSegmentStart(path) {
|
|
112
|
+
let depth = 0;
|
|
113
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
114
|
+
const c = path[i];
|
|
115
|
+
if (c === '}') {
|
|
116
|
+
depth++;
|
|
117
|
+
}
|
|
118
|
+
else if (c === '{') {
|
|
119
|
+
depth--;
|
|
120
|
+
if (depth === 0) {
|
|
121
|
+
// {name} 段:其起点是前面那个 '.'(如 .decls{approve})
|
|
122
|
+
const dot = path.lastIndexOf('.', i);
|
|
123
|
+
return dot > 0 ? dot : i;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else if (depth === 0 && (c === '.' || c === '[')) {
|
|
127
|
+
return i;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return -1;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=change-impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-impact.js","sourceRoot":"","sources":["../../../src/nodeid/change-impact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable IR Node ID(TS 侧)—— 与 `aster-lang-core` 的 `NodeIdMap` 对等实现。
|
|
3
|
+
*
|
|
4
|
+
* <h2>为什么是两个字段而不是一个</h2>
|
|
5
|
+
*
|
|
6
|
+
* ADR 0037 §8 的实测:三种候选方案各有一个**灾难场景**,且互不重叠
|
|
7
|
+
* (基线 16 节点的身份存活率):
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* 编辑 结构hash 结构路径 命名作用域路径
|
|
11
|
+
* 改阈值 10/16 16/16 16/16
|
|
12
|
+
* 前面插入一条规则 16/16 5/16 16/16
|
|
13
|
+
* 重命名规则 14/16 16/16 1/16
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* 结论是**复合键**,两者职责分离:
|
|
17
|
+
*
|
|
18
|
+
* ```
|
|
19
|
+
* nodeId = 命名作用域路径 ← 「是哪个节点」(跨版本稳定)
|
|
20
|
+
* contentHash = 子树内容指纹 ← 「它变了没有」(change impact 的信号)
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* <h2>★跨引擎口径:contentHash 必须对「归一化后」的 IR 取 hash</h2>
|
|
24
|
+
*
|
|
25
|
+
* 两引擎的**原始** Core IR 字段本就不同,且这是 ADR 0016 §B/§C 认定的**合法
|
|
26
|
+
* 分岔**(推导分析层,不是源码结构):
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* Java 独有 annotations / retAnnotations / piiLevel / piiCategories
|
|
30
|
+
* TS 独有 retTypeInferred / constraints / typeInferred
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* 所以直接对原始 IR 取 hash,两侧**必定**不等。调用方必须先用
|
|
34
|
+
* `@aster-cloud/aster-lang-test/ir-normalize` 的 `normalizeIr` 归一化——那是
|
|
35
|
+
* parity 门禁使用的**同一份**规则(单源,避免「门禁说一致、hash 说不一致」
|
|
36
|
+
* 且两边都不报错的漂移)。
|
|
37
|
+
*
|
|
38
|
+
* <h2>与 ADR 0032 anchor 的边界(★不可混用)</h2>
|
|
39
|
+
*
|
|
40
|
+
* ```
|
|
41
|
+
* anchor ("L21C5-L21C22") 位置派生,**同一版本内**稳定 → trace 跨执行聚合
|
|
42
|
+
* nodeId (命名作用域路径) 结构派生,**跨版本**稳定 → 双向导航 / change impact
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* 用其中一个去做另一个的工作,会让「跨版本统计」悄悄退化成「同版本统计」,
|
|
46
|
+
* 且不报错。
|
|
47
|
+
*/
|
|
48
|
+
/** ID 方案版本。变更路径构造规则或 hash 口径必须 bump,否则跨版本比对会静默错配。 */
|
|
49
|
+
export declare const NODE_ID_VERSION = "aster-node-id/v1";
|
|
50
|
+
/** 一个节点的稳定标识。 */
|
|
51
|
+
export interface NodeIdentity {
|
|
52
|
+
/** 命名作用域路径,如 `$.decls{approve}.body.statements[0]`。 */
|
|
53
|
+
readonly nodeId: string;
|
|
54
|
+
/** 该节点子树(剥掉 origin)的 canonical hash。 */
|
|
55
|
+
readonly contentHash: string;
|
|
56
|
+
/** 节点类型(`Func` / `If` / ...),便于调试与过滤。 */
|
|
57
|
+
readonly kind: string;
|
|
58
|
+
}
|
|
59
|
+
/** `sha256(version + "\n" + canonicalJson(value))`,hex。与 Java 同算法同前缀。 */
|
|
60
|
+
export declare function canonicalHash(value: unknown): string;
|
|
61
|
+
/**
|
|
62
|
+
* 为一棵 Core IR 计算全部节点的稳定标识。
|
|
63
|
+
*
|
|
64
|
+
* @param ir 建议先经 `normalizeIr` 归一化——跨引擎比较时**必须**先归一化,
|
|
65
|
+
* 否则两侧的 contentHash 恒不相等(见本文件头部说明)。
|
|
66
|
+
* @returns nodeId → NodeIdentity,按插入顺序(路径的深度优先序,确定)
|
|
67
|
+
*/
|
|
68
|
+
export declare function computeNodeIds(ir: unknown): ReadonlyMap<string, NodeIdentity>;
|
|
69
|
+
//# sourceMappingURL=node-id-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-id-map.d.ts","sourceRoot":"","sources":["../../../src/nodeid/node-id-map.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,qDAAqD;AACrD,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAQlD,iBAAiB;AACjB,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AA2DD,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIpD;AAsCD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAI7E"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Stable IR Node ID(TS 侧)—— 与 `aster-lang-core` 的 `NodeIdMap` 对等实现。
|
|
4
|
+
*
|
|
5
|
+
* <h2>为什么是两个字段而不是一个</h2>
|
|
6
|
+
*
|
|
7
|
+
* ADR 0037 §8 的实测:三种候选方案各有一个**灾难场景**,且互不重叠
|
|
8
|
+
* (基线 16 节点的身份存活率):
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* 编辑 结构hash 结构路径 命名作用域路径
|
|
12
|
+
* 改阈值 10/16 16/16 16/16
|
|
13
|
+
* 前面插入一条规则 16/16 5/16 16/16
|
|
14
|
+
* 重命名规则 14/16 16/16 1/16
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* 结论是**复合键**,两者职责分离:
|
|
18
|
+
*
|
|
19
|
+
* ```
|
|
20
|
+
* nodeId = 命名作用域路径 ← 「是哪个节点」(跨版本稳定)
|
|
21
|
+
* contentHash = 子树内容指纹 ← 「它变了没有」(change impact 的信号)
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* <h2>★跨引擎口径:contentHash 必须对「归一化后」的 IR 取 hash</h2>
|
|
25
|
+
*
|
|
26
|
+
* 两引擎的**原始** Core IR 字段本就不同,且这是 ADR 0016 §B/§C 认定的**合法
|
|
27
|
+
* 分岔**(推导分析层,不是源码结构):
|
|
28
|
+
*
|
|
29
|
+
* ```
|
|
30
|
+
* Java 独有 annotations / retAnnotations / piiLevel / piiCategories
|
|
31
|
+
* TS 独有 retTypeInferred / constraints / typeInferred
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* 所以直接对原始 IR 取 hash,两侧**必定**不等。调用方必须先用
|
|
35
|
+
* `@aster-cloud/aster-lang-test/ir-normalize` 的 `normalizeIr` 归一化——那是
|
|
36
|
+
* parity 门禁使用的**同一份**规则(单源,避免「门禁说一致、hash 说不一致」
|
|
37
|
+
* 且两边都不报错的漂移)。
|
|
38
|
+
*
|
|
39
|
+
* <h2>与 ADR 0032 anchor 的边界(★不可混用)</h2>
|
|
40
|
+
*
|
|
41
|
+
* ```
|
|
42
|
+
* anchor ("L21C5-L21C22") 位置派生,**同一版本内**稳定 → trace 跨执行聚合
|
|
43
|
+
* nodeId (命名作用域路径) 结构派生,**跨版本**稳定 → 双向导航 / change impact
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* 用其中一个去做另一个的工作,会让「跨版本统计」悄悄退化成「同版本统计」,
|
|
47
|
+
* 且不报错。
|
|
48
|
+
*/
|
|
49
|
+
/** ID 方案版本。变更路径构造规则或 hash 口径必须 bump,否则跨版本比对会静默错配。 */
|
|
50
|
+
export const NODE_ID_VERSION = 'aster-node-id/v1';
|
|
51
|
+
/**
|
|
52
|
+
* canonical 算法版本前缀。★必须与 Java `CanonicalJson.CANONICALIZATION_VERSION`
|
|
53
|
+
* 和 aster-cloud `canonical-json.ts` 保持一致,否则同一棵树在两侧产出不同 hash。
|
|
54
|
+
*/
|
|
55
|
+
const CANONICALIZATION_VERSION = 'aster-canonical-json/v1';
|
|
56
|
+
/** 不参与身份与内容判定的字段。位置不是内容。 */
|
|
57
|
+
function isIdentityIrrelevant(field) {
|
|
58
|
+
return field === 'origin';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* canonical JSON 序列化(object key 按 Unicode code point 升序,array 保序)。
|
|
62
|
+
*
|
|
63
|
+
* <p>★这是 Java `CanonicalJson` 的**子集**:归一化后的 Core IR 只含
|
|
64
|
+
* null / boolean / string / 安全整数 / array / object,不含 Decimal 与浮点,
|
|
65
|
+
* 故不需要 aster-cloud 那份实现里的 Decimal 展开与 typeCtx 机制。
|
|
66
|
+
*
|
|
67
|
+
* <p>★遇到浮点或超安全整数范围的数值会**抛错而非静默降级**——跨引擎浮点表示
|
|
68
|
+
* 不一致,静默处理会产出两侧不同的 hash 且无人察觉(与 Java 同口径)。
|
|
69
|
+
*/
|
|
70
|
+
function canonicalJson(value) {
|
|
71
|
+
if (value === null || value === undefined)
|
|
72
|
+
return 'null';
|
|
73
|
+
if (typeof value === 'boolean')
|
|
74
|
+
return value ? 'true' : 'false';
|
|
75
|
+
if (typeof value === 'string')
|
|
76
|
+
return JSON.stringify(value);
|
|
77
|
+
if (typeof value === 'number') {
|
|
78
|
+
if (!Number.isFinite(value)) {
|
|
79
|
+
throw new Error(`canonical JSON 不支持 NaN/Infinity:${value}`);
|
|
80
|
+
}
|
|
81
|
+
if (!Number.isSafeInteger(value)) {
|
|
82
|
+
throw new Error(`canonical JSON 只接受安全整数(跨引擎浮点表示不一致):${value}`);
|
|
83
|
+
}
|
|
84
|
+
return String(value);
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === 'object') {
|
|
90
|
+
const entries = Object.entries(value)
|
|
91
|
+
.filter(([, v]) => v !== undefined)
|
|
92
|
+
.sort(([a], [b]) => compareByCodePoint(a, b));
|
|
93
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
94
|
+
}
|
|
95
|
+
throw new Error(`canonical JSON 不支持的类型:${typeof value}`);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 按 Unicode code point 比较(★不能用默认的 `<`——JS 字符串比较按 UTF-16
|
|
99
|
+
* code unit,对增补平面字符与 Java 的 code point 序不一致,会产出不同的 hash)。
|
|
100
|
+
*/
|
|
101
|
+
function compareByCodePoint(a, b) {
|
|
102
|
+
const ai = [...a];
|
|
103
|
+
const bi = [...b];
|
|
104
|
+
const n = Math.min(ai.length, bi.length);
|
|
105
|
+
for (let i = 0; i < n; i++) {
|
|
106
|
+
const ca = ai[i].codePointAt(0);
|
|
107
|
+
const cb = bi[i].codePointAt(0);
|
|
108
|
+
if (ca !== cb)
|
|
109
|
+
return ca - cb;
|
|
110
|
+
}
|
|
111
|
+
return ai.length - bi.length;
|
|
112
|
+
}
|
|
113
|
+
/** `sha256(version + "\n" + canonicalJson(value))`,hex。与 Java 同算法同前缀。 */
|
|
114
|
+
export function canonicalHash(value) {
|
|
115
|
+
return createHash('sha256')
|
|
116
|
+
.update(`${CANONICALIZATION_VERSION}\n${canonicalJson(value)}`, 'utf8')
|
|
117
|
+
.digest('hex');
|
|
118
|
+
}
|
|
119
|
+
/** 深拷贝并剥掉不参与身份判定的字段。 */
|
|
120
|
+
function stripIdentityIrrelevant(node) {
|
|
121
|
+
if (Array.isArray(node))
|
|
122
|
+
return node.map(stripIdentityIrrelevant);
|
|
123
|
+
if (node !== null && typeof node === 'object') {
|
|
124
|
+
const out = {};
|
|
125
|
+
for (const [k, v] of Object.entries(node)) {
|
|
126
|
+
if (isIdentityIrrelevant(k))
|
|
127
|
+
continue;
|
|
128
|
+
out[k] = stripIdentityIrrelevant(v);
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
return node;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 数组元素的路径段:**有名字就用名字**,否则退回下标。
|
|
136
|
+
*
|
|
137
|
+
* <p>这一行是整个方案的关键——它让「在前面插入一条规则」不再移动后续规则的身份
|
|
138
|
+
* (实测:纯下标路径 5/16 存活,改用名字后 16/16)。
|
|
139
|
+
*/
|
|
140
|
+
function segmentOf(element, index) {
|
|
141
|
+
if (element !== null && typeof element === 'object' && !Array.isArray(element)) {
|
|
142
|
+
const el = element;
|
|
143
|
+
// ★`_` 不是名字,是**占位符**:裸表达式语句一律降为 `Let "_" be expr`
|
|
144
|
+
// (求值并丢弃结果)。若拿它当路径段,同一函数体里的多条裸表达式语句
|
|
145
|
+
// 会全部塌成 `statements{_}` —— nodeId 撞车,Map 只保留最后一条,
|
|
146
|
+
// 其余节点的身份**静默消失**。
|
|
147
|
+
// 实测(tier1 全语料):1 个样本、4 个节点因此丢失身份。
|
|
148
|
+
// 占位名退回下标,回到「按位置区分」——这正是无名节点的正确处理方式。
|
|
149
|
+
if (typeof el.name === 'string' && el.name !== '_')
|
|
150
|
+
return `{${el.name}}`;
|
|
151
|
+
// Import 没有 name,用 path 充当名字:它同样是「重排后仍指同一个导入」的标识。
|
|
152
|
+
if (typeof el.path === 'string')
|
|
153
|
+
return `{${el.path}}`;
|
|
154
|
+
}
|
|
155
|
+
return `[${index}]`;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 为一棵 Core IR 计算全部节点的稳定标识。
|
|
159
|
+
*
|
|
160
|
+
* @param ir 建议先经 `normalizeIr` 归一化——跨引擎比较时**必须**先归一化,
|
|
161
|
+
* 否则两侧的 contentHash 恒不相等(见本文件头部说明)。
|
|
162
|
+
* @returns nodeId → NodeIdentity,按插入顺序(路径的深度优先序,确定)
|
|
163
|
+
*/
|
|
164
|
+
export function computeNodeIds(ir) {
|
|
165
|
+
const out = new Map();
|
|
166
|
+
walk(ir, '$', out);
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
function walk(node, path, out) {
|
|
170
|
+
if (node === null || typeof node !== 'object' || Array.isArray(node))
|
|
171
|
+
return;
|
|
172
|
+
const obj = node;
|
|
173
|
+
// 只有带 kind 的对象才是「IR 节点」;Field/Param 等无 kind 的结构体不单独发 ID,
|
|
174
|
+
// 它们随宿主节点的 contentHash 一起变化(与 Java 侧口径一致)。
|
|
175
|
+
if (typeof obj.kind === 'string') {
|
|
176
|
+
out.set(path, {
|
|
177
|
+
nodeId: path,
|
|
178
|
+
contentHash: canonicalHash(stripIdentityIrrelevant(obj)),
|
|
179
|
+
kind: obj.kind,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
for (const [field, value] of Object.entries(obj)) {
|
|
183
|
+
if (isIdentityIrrelevant(field))
|
|
184
|
+
continue;
|
|
185
|
+
if (Array.isArray(value)) {
|
|
186
|
+
value.forEach((el, i) => walk(el, `${path}.${field}${segmentOf(el, i)}`, out));
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
walk(value, `${path}.${field}`, out);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=node-id-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-id-map.js","sourceRoot":"","sources":["../../../src/nodeid/node-id-map.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decl-parser.d.ts","sourceRoot":"","sources":["../../../src/parser/decl-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAyC,WAAW,EAAE,KAAK,EAAQ,MAAM,aAAa,CAAC;AAC/G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAsIrD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,KAAK,KAAK,EAC1C,SAAS,EAAE,MAAM,IAAI,EACrB,cAAc,EAAE,MAAM,MAAM,GAC3B,WAAW,
|
|
1
|
+
{"version":3,"file":"decl-parser.d.ts","sourceRoot":"","sources":["../../../src/parser/decl-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAyC,WAAW,EAAE,KAAK,EAAQ,MAAM,aAAa,CAAC;AAC/G,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAsIrD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,KAAK,KAAK,EAC1C,SAAS,EAAE,MAAM,IAAI,EACrB,cAAc,EAAE,MAAM,MAAM,GAC3B,WAAW,CA8Bb;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,KAAK,KAAK,EAC1C,SAAS,EAAE,MAAM,IAAI,EACrB,cAAc,EAAE,MAAM,MAAM,GAC3B,WAAW,CAmCb;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,KAAK,KAAK,EAC1C,aAAa,EAAE,MAAM,IAAI,EACzB,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,EAChD,aAAa,EAAE,MAAM,IAAI,EACzB,UAAU,EAAE,MAAM,MAAM,EACxB,WAAW,GAAE,SAAS,UAAU,EAAO,EACvC,YAAY,CAAC,EAAE,KAAK,GACnB,WAAW,CAoSb;AA2FD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,WAAW,GACjB;IAAE,KAAK,EAAE,WAAW,EAAE,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CA4GrD"}
|
|
@@ -10,7 +10,7 @@ import { parseModuleHeader, parseImport } from './import-parser.js';
|
|
|
10
10
|
import { parseType, parseEffectList, separateEffectsAndCaps } from './type-parser.js';
|
|
11
11
|
import { parseBlock, parseExplicitBlock, parseParamList } from './expr-stmt-parser.js';
|
|
12
12
|
import { parseFieldList, parseVariantList } from './field-variant-parser.js';
|
|
13
|
-
import { assignSpan, spanFromSources, lastConsumedToken, spanFromTokens } from './span-utils.js';
|
|
13
|
+
import { assignSpan, spanFromSources, lastConsumedToken, lastNonLayoutToken, spanFromTokens } from './span-utils.js';
|
|
14
14
|
/**
|
|
15
15
|
* R30+ audit P1:原本写满 14 处 {@code (node as any).nameSpan = ...} / variantSpans。
|
|
16
16
|
* 把单一职责的辅助器抽出来:保留 readonly 字段的设计意图(声明阶段不可改),
|
|
@@ -151,7 +151,9 @@ export function parseDataDecl(ctx, error, expectDot, parseTypeIdent) {
|
|
|
151
151
|
const fields = parseFieldList(ctx, error);
|
|
152
152
|
// 期望句点结束
|
|
153
153
|
expectDot();
|
|
154
|
-
|
|
154
|
+
// ★用 lastNonLayoutToken:直接取 ctx.tokens[ctx.index-1] 会把尾随的
|
|
155
|
+
// NEWLINE/DEDENT 算进 span,使声明范围延伸到下一个声明的起始行(span 重叠)。
|
|
156
|
+
const endTok = lastNonLayoutToken(ctx);
|
|
155
157
|
// 创建 Data 节点并注册类型
|
|
156
158
|
const dataDecl = Node.Data(typeName, fields);
|
|
157
159
|
const lastFieldSource = fields.length > 0 ? fields[fields.length - 1] : endTok;
|
|
@@ -184,7 +186,9 @@ export function parseEnumDecl(ctx, error, expectDot, parseTypeIdent) {
|
|
|
184
186
|
const { variants, variantSpans } = parseVariantList(ctx, error);
|
|
185
187
|
// 期望句点结束
|
|
186
188
|
expectDot();
|
|
187
|
-
|
|
189
|
+
// ★用 lastNonLayoutToken:直接取 ctx.tokens[ctx.index-1] 会把尾随的
|
|
190
|
+
// NEWLINE/DEDENT 算进 span,使声明范围延伸到下一个声明的起始行(span 重叠)。
|
|
191
|
+
const endTok = lastNonLayoutToken(ctx);
|
|
188
192
|
// 创建 Enum 节点并附加变体 spans
|
|
189
193
|
const en = Node.Enum(typeName, variants);
|
|
190
194
|
if (variantSpans && variantSpans.length > 0) {
|
|
@@ -446,7 +450,9 @@ export function parseFuncDecl(ctx, error, expectCommaOr, expectKeyword, expectNe
|
|
|
446
450
|
typeParams = Array.from(found);
|
|
447
451
|
}
|
|
448
452
|
}
|
|
449
|
-
|
|
453
|
+
// ★用 lastNonLayoutToken:直接取 ctx.tokens[ctx.index-1] 会把尾随的
|
|
454
|
+
// NEWLINE/DEDENT 算进 span,使声明范围延伸到下一个声明的起始行(span 重叠)。
|
|
455
|
+
const endTok = lastNonLayoutToken(ctx);
|
|
450
456
|
// 合并函数体内收集的效果
|
|
451
457
|
if (Array.isArray(ctx.collectedEffects) && ctx.collectedEffects.length > 0) {
|
|
452
458
|
effects = effects.concat(ctx.collectedEffects);
|