@rsconcept/domain 2.1.2 → 2.2.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 (51) hide show
  1. package/dist/api-Bf0f_uiI.d.ts +57 -0
  2. package/dist/{diagnostics-CY9sKggB.js → diagnostics-B1OfMra1.js} +412 -174
  3. package/dist/diagnostics-B1OfMra1.js.map +1 -0
  4. package/dist/{diagnostics-EaIt3zIw.d.ts → diagnostics-Db1F7ruF.d.ts} +3 -3
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +2 -2
  7. package/dist/library/diagnostics.d.ts +1 -1
  8. package/dist/library/diagnostics.js +1 -1
  9. package/dist/library/index.d.ts +1 -1
  10. package/dist/library/index.js +1 -1
  11. package/dist/library/oss-api.d.ts +1 -1
  12. package/dist/library/oss-api.js +3 -3
  13. package/dist/library/oss-api.js.map +1 -1
  14. package/dist/library/rsengine.d.ts +1 -1
  15. package/dist/library/rsengine.js +1 -1
  16. package/dist/library/{rsform-api.d.ts → rsform-api/index.d.ts} +109 -66
  17. package/dist/library/rsform-api/index.js +2 -0
  18. package/dist/library/rsform.d.ts +1 -1
  19. package/dist/library/rsform.js.map +1 -1
  20. package/dist/library/rsmodel-api.d.ts +1 -1
  21. package/dist/library/rsmodel-api.js +3 -1
  22. package/dist/library/rsmodel-api.js.map +1 -1
  23. package/dist/library/rsmodel.d.ts +1 -1
  24. package/dist/library/structure-planner.d.ts +1 -1
  25. package/dist/library/structure-planner.js +1 -1
  26. package/dist/rslang/api.d.ts +2 -58
  27. package/dist/rslang/api.js +1 -1
  28. package/dist/rslang/index.js +1 -1
  29. package/dist/rslang/parser/syntax-errors/index.js +1 -1
  30. package/dist/rslang/semantic/analyzer.js +2 -2
  31. package/dist/{syntax-errors-DwPXhpVe.js → syntax-errors-BN_bqTaX.js} +2 -2
  32. package/dist/{syntax-errors-DwPXhpVe.js.map → syntax-errors-BN_bqTaX.js.map} +1 -1
  33. package/package.json +3 -3
  34. package/src/library/oss-api.ts +5 -2
  35. package/src/library/rsform-api/alias.ts +104 -0
  36. package/src/library/rsform-api/analysis.ts +157 -0
  37. package/src/library/rsform-api/cst-type.ts +216 -0
  38. package/src/library/rsform-api/expression.ts +73 -0
  39. package/src/library/rsform-api/formal-graph.test.ts +72 -0
  40. package/src/library/rsform-api/formal-graph.ts +195 -0
  41. package/src/library/rsform-api/index.ts +32 -0
  42. package/src/library/rsform-api/semantic-relations.ts +117 -0
  43. package/src/library/rsform-api/stats.ts +170 -0
  44. package/src/library/rsform-api/structure.ts +93 -0
  45. package/src/library/rsform-api/synthesis.ts +27 -0
  46. package/src/library/rsform-api/types.ts +21 -0
  47. package/src/library/rsform.ts +2 -2
  48. package/src/library/rsmodel-api.ts +7 -1
  49. package/dist/diagnostics-CY9sKggB.js.map +0 -1
  50. package/dist/library/rsform-api.js +0 -2
  51. package/src/library/rsform-api.ts +0 -755
@@ -0,0 +1,216 @@
1
+ /**
2
+ * CstType classification, prefixes, and capability predicates.
3
+ */
4
+
5
+ import { TypeClass, TypeID, type Typification } from '../../rslang';
6
+ import { type EchelonFunctional, isTypification } from '../../rslang/semantic/typification';
7
+ import { type Constituenta, CstClass, CstType } from '../rsform';
8
+
9
+ /** Record of {@link CstType} prefixes. */
10
+ const CST_TYPE_PREFIX: Record<CstType, string> = {
11
+ [CstType.NOMINAL]: 'N',
12
+ [CstType.BASE]: 'X',
13
+ [CstType.CONSTANT]: 'C',
14
+ [CstType.STRUCTURED]: 'S',
15
+ [CstType.AXIOM]: 'A',
16
+ [CstType.TERM]: 'D',
17
+ [CstType.FUNCTION]: 'F',
18
+ [CstType.PREDICATE]: 'P',
19
+ [CstType.STATEMENT]: 'T'
20
+ };
21
+
22
+ /** Record of {@link CstType} to {@link CstClass} mapping. */
23
+ const CST_TYPE_TO_CLASS: Record<CstType, CstClass> = {
24
+ [CstType.NOMINAL]: CstClass.NOMINAL,
25
+ [CstType.BASE]: CstClass.BASIC,
26
+ [CstType.CONSTANT]: CstClass.BASIC,
27
+ [CstType.STRUCTURED]: CstClass.BASIC,
28
+ [CstType.TERM]: CstClass.DERIVED,
29
+ [CstType.FUNCTION]: CstClass.DERIVED,
30
+ [CstType.AXIOM]: CstClass.STATEMENT,
31
+ [CstType.PREDICATE]: CstClass.DERIVED,
32
+ [CstType.STATEMENT]: CstClass.STATEMENT
33
+ };
34
+
35
+ /** Prefix for alias indicating {@link CstType}. */
36
+ export function getCstTypePrefix(type: CstType): string {
37
+ return CST_TYPE_PREFIX[type];
38
+ }
39
+
40
+ /** Guess {@link CstType} from user input hint. */
41
+ export function guessCstType(hint: string): CstType | null {
42
+ if (hint.length !== 1) {
43
+ return null;
44
+ }
45
+ for (const [type, prefix] of Object.entries(CST_TYPE_PREFIX)) {
46
+ if (hint === prefix) {
47
+ return type as CstType;
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+
53
+ /** Infers the {@link CstClass} based on the provided {@link CstType} and template status. */
54
+ export function inferClass(type: CstType, isTemplate: boolean = false): CstClass {
55
+ if (isTemplate) {
56
+ return CstClass.TEMPLATE;
57
+ }
58
+ return CST_TYPE_TO_CLASS[type];
59
+ }
60
+
61
+ /** Check if {@link Constituenta} is a template or a category. */
62
+ export function isTemplateCst(cst: Constituenta): boolean {
63
+ return cst.cst_type === CstType.FUNCTION || cst.cst_type === CstType.PREDICATE || cst.cst_type === CstType.STATEMENT;
64
+ }
65
+
66
+ /** Evaluate if {@link CstType} is basic concept. */
67
+ export function isBasicConcept(type: CstType): boolean {
68
+ switch (type) {
69
+ case CstType.NOMINAL:
70
+ case CstType.BASE:
71
+ case CstType.CONSTANT:
72
+ case CstType.STRUCTURED:
73
+ case CstType.AXIOM:
74
+ return true;
75
+
76
+ case CstType.TERM:
77
+ case CstType.FUNCTION:
78
+ case CstType.PREDICATE:
79
+ case CstType.STATEMENT:
80
+ return false;
81
+ }
82
+ }
83
+
84
+ /** Evaluate if {@link CstType} is base set or constant set. */
85
+ export function isBaseSet(type: CstType): boolean {
86
+ switch (type) {
87
+ case CstType.BASE:
88
+ case CstType.CONSTANT:
89
+ return true;
90
+
91
+ case CstType.NOMINAL:
92
+ case CstType.STRUCTURED:
93
+ case CstType.AXIOM:
94
+ case CstType.TERM:
95
+ case CstType.FUNCTION:
96
+ case CstType.PREDICATE:
97
+ case CstType.STATEMENT:
98
+ return false;
99
+ }
100
+ }
101
+
102
+ /** Evaluate if {@link CstType} is a function. */
103
+ export function isFunctional(type: CstType): boolean {
104
+ switch (type) {
105
+ case CstType.FUNCTION:
106
+ case CstType.PREDICATE:
107
+ return true;
108
+
109
+ case CstType.NOMINAL:
110
+ case CstType.BASE:
111
+ case CstType.CONSTANT:
112
+ case CstType.STRUCTURED:
113
+ case CstType.AXIOM:
114
+ case CstType.TERM:
115
+ case CstType.STATEMENT:
116
+ return false;
117
+ }
118
+ }
119
+
120
+ /** Evaluate if {@link CstType} is logical. */
121
+ export function isLogical(type: CstType): boolean {
122
+ switch (type) {
123
+ case CstType.AXIOM:
124
+ case CstType.STATEMENT:
125
+ return true;
126
+
127
+ case CstType.NOMINAL:
128
+ case CstType.BASE:
129
+ case CstType.CONSTANT:
130
+ case CstType.STRUCTURED:
131
+ case CstType.TERM:
132
+ case CstType.FUNCTION:
133
+ case CstType.PREDICATE:
134
+ return false;
135
+ }
136
+ }
137
+
138
+ /** Evaluate if {@link CstType} can have manual typification. */
139
+ export function canHaveManualTypification(type: CstType): boolean {
140
+ switch (type) {
141
+ case CstType.STRUCTURED:
142
+ case CstType.TERM:
143
+ case CstType.FUNCTION:
144
+ case CstType.PREDICATE:
145
+ return true;
146
+
147
+ case CstType.NOMINAL:
148
+ case CstType.BASE:
149
+ case CstType.CONSTANT:
150
+ case CstType.AXIOM:
151
+ case CstType.STATEMENT:
152
+ return false;
153
+ }
154
+ }
155
+
156
+ /** Checks whether a constituenta type supports formal definitions. */
157
+ export function canHaveFormalDefinition(cstType: CstType): boolean {
158
+ return cstType !== CstType.BASE && cstType !== CstType.CONSTANT;
159
+ }
160
+
161
+ /** Returns expected {@link TypeClass} of formal definition for {@link CstType}. */
162
+ export function typeClassForCstType(cstType: CstType): TypeClass {
163
+ switch (cstType) {
164
+ case CstType.NOMINAL:
165
+ case CstType.BASE:
166
+ case CstType.CONSTANT:
167
+ case CstType.STRUCTURED:
168
+ case CstType.TERM:
169
+ return TypeClass.typification;
170
+ case CstType.FUNCTION:
171
+ return TypeClass.function;
172
+ case CstType.PREDICATE:
173
+ return TypeClass.predicate;
174
+ case CstType.AXIOM:
175
+ case CstType.STATEMENT:
176
+ return TypeClass.logic;
177
+ }
178
+ }
179
+
180
+ /** Evaluate if {@link Constituenta} can be used produce structure. */
181
+ export function canProduceStructure(cst: Constituenta): boolean {
182
+ switch (cst.cst_type) {
183
+ case CstType.NOMINAL:
184
+ case CstType.BASE:
185
+ case CstType.CONSTANT:
186
+ case CstType.AXIOM:
187
+ case CstType.STATEMENT:
188
+ case CstType.PREDICATE:
189
+ return false;
190
+ }
191
+ if (!cst.effectiveType) {
192
+ return false;
193
+ }
194
+ if (cst.cst_type === CstType.FUNCTION) {
195
+ const result = (cst.effectiveType as EchelonFunctional).result;
196
+ return typeCanProduceStructure(result);
197
+ }
198
+ if (!isTypification(cst.effectiveType)) {
199
+ return false;
200
+ }
201
+ return typeCanProduceStructure(cst.effectiveType as Typification);
202
+ }
203
+
204
+ function typeCanProduceStructure(type: Typification): boolean {
205
+ if (type.typeID === TypeID.basic || type.typeID === TypeID.integer || type.typeID === TypeID.anyTypification) {
206
+ return false;
207
+ } else if (type.typeID === TypeID.tuple) {
208
+ return true;
209
+ } else {
210
+ return (
211
+ type.base.typeID !== TypeID.basic &&
212
+ type.base.typeID !== TypeID.integer &&
213
+ type.base.typeID !== TypeID.anyTypification
214
+ );
215
+ }
216
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Formal expression helpers: normalization, templates, status.
3
+ */
4
+
5
+ import { type AstNode } from '../../parsing';
6
+ import { ValueClass } from '../../rslang';
7
+ import { generateExpressionFromAst, parseRSLangExpression } from '../../rslang/api';
8
+ import { type ArgumentValue, type Constituenta, CstStatus, CstType } from '../rsform';
9
+
10
+ import { isBasicConcept } from './cst-type';
11
+
12
+ /** Infers the status of an expression based on parsing and value information. */
13
+ export function inferStatus(parse: boolean, value?: ValueClass | null): CstStatus {
14
+ if (!parse) {
15
+ return CstStatus.INCORRECT;
16
+ }
17
+ if (value === null) {
18
+ return CstStatus.INCALCULABLE;
19
+ }
20
+ if (value === ValueClass.PROPERTY) {
21
+ return CstStatus.PROPERTY;
22
+ }
23
+ return CstStatus.VERIFIED;
24
+ }
25
+
26
+ /** Infers type of constituent for a given template and arguments. */
27
+ export function inferTemplatedType(templateType: CstType, args: ArgumentValue[]): CstType {
28
+ if (args.length === 0 || args.some(arg => !arg.value)) {
29
+ return templateType;
30
+ } else if (templateType === CstType.PREDICATE) {
31
+ return CstType.AXIOM;
32
+ } else {
33
+ return CstType.TERM;
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Normalize a RSLang expression for duplicate comparison.
39
+ *
40
+ * Returns `null` for basic concepts without formal definitions. Pass a pre-parsed `ast`
41
+ * when the expression was already analyzed to avoid re-parsing.
42
+ */
43
+ export function normalizeExpression(expression: string, cst_type: CstType, ast?: AstNode | null): string | null {
44
+ if (isBasicConcept(cst_type) && cst_type !== CstType.AXIOM) {
45
+ return null;
46
+ }
47
+ const parsed = ast ?? parseRSLangExpression(expression);
48
+ let normalized = '';
49
+ if (parsed && !parsed.hasError) {
50
+ normalized = generateExpressionFromAst(parsed, { normalize: true });
51
+ } else {
52
+ normalized = expression;
53
+ }
54
+ normalized = normalized.replace(/\s+/g, '');
55
+ return normalized === '' ? null : normalized;
56
+ }
57
+
58
+ /** Checks if given expression is a template. */
59
+ export function inferTemplate(expression: string): boolean {
60
+ const match = expression.match(/R\d+/g);
61
+ return (match && match?.length > 0) ?? false;
62
+ }
63
+
64
+ /** Apply filter based on start {@link Constituenta} type. */
65
+ export function applyFilterCategory(start: Constituenta, items: Constituenta[]): Constituenta[] {
66
+ const startIndex = items.indexOf(start);
67
+ if (startIndex === -1) {
68
+ return [];
69
+ }
70
+ const nextCategoryIndex = items.findIndex((cst, index) => index > startIndex && cst.cst_type === CstType.STATEMENT);
71
+
72
+ return items.filter((_, index) => index >= startIndex && (nextCategoryIndex === -1 || index < nextCategoryIndex));
73
+ }
@@ -0,0 +1,72 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { CstType } from '../rsform';
4
+
5
+ import { buildFormalDependencyGraph, restoreConstituentOrder } from './formal-graph';
6
+ import { computeSemanticRelations } from './semantic-relations';
7
+ import { type OrderableConstituenta } from './types';
8
+
9
+ function cst(id: number, alias: string, cst_type: CstType, definition_formal = ''): OrderableConstituenta {
10
+ return { id, alias, cst_type, definition_formal };
11
+ }
12
+
13
+ describe('restoreConstituentOrder', () => {
14
+ it('keeps short lists unchanged', () => {
15
+ expect(restoreConstituentOrder([])).toEqual([]);
16
+ const only = [cst(1, 'X1', CstType.BASE)];
17
+ expect(restoreConstituentOrder(only)).toEqual(only);
18
+ });
19
+
20
+ it('matches classic kernel order with sticky semantic child', () => {
21
+ const items = [
22
+ cst(1, 'D2', CstType.TERM, 'D{ξ∈S1 | 1=1}'),
23
+ cst(2, 'D1', CstType.TERM, 'Pr1(S1)\\X1'),
24
+ cst(3, 'X1', CstType.BASE),
25
+ cst(4, 'X2', CstType.BASE),
26
+ cst(5, 'S1', CstType.STRUCTURED, 'ℬ(X1×X1)'),
27
+ cst(6, 'C1', CstType.CONSTANT),
28
+ cst(7, 'S2', CstType.STRUCTURED, 'ℬ(X2×D1)'),
29
+ cst(8, 'A1', CstType.AXIOM, 'D3=∅'),
30
+ cst(9, 'A2', CstType.AXIOM, 'P1[S1]'),
31
+ cst(10, 'D3', CstType.TERM, 'Pr2(S2)'),
32
+ cst(11, 'F1', CstType.FUNCTION, '[α∈ℬ(X1)] D{σ∈S1 | α⊆pr1(σ)}'),
33
+ cst(12, 'D4', CstType.TERM, 'Pr2(D3)'),
34
+ cst(13, 'F2', CstType.FUNCTION, '[α∈ℬ(X1)] X1\\α'),
35
+ cst(14, 'P1', CstType.PREDICATE, '[α∈ℬ(R1)] card(α)=0')
36
+ ];
37
+
38
+ const ordered = restoreConstituentOrder(items).map(item => item.alias);
39
+ expect(ordered).toEqual(['X1', 'X2', 'C1', 'P1', 'S1', 'A2', 'D1', 'S2', 'D3', 'A1', 'D4', 'D2', 'F1', 'F2']);
40
+ });
41
+
42
+ it('keeps formal supplier before semantic child (schema 875 pattern)', () => {
43
+ const items = [
44
+ cst(1, 'X1', CstType.BASE),
45
+ cst(2, 'C1', CstType.CONSTANT),
46
+ cst(3, 'P2', CstType.PREDICATE, '[σ∈ℬ(C1)] card(σ)=0'),
47
+ cst(4, 'P6', CstType.PREDICATE, '[σ∈ℬℬ(C1)] ∀ξ∈σ P2[ξ]'),
48
+ cst(5, 'F7', CstType.FUNCTION, '[σ∈ℬ(R1×R2)] Pr1(σ)'),
49
+ cst(6, 'F9', CstType.FUNCTION, '[β∈ℬ(R1×R2), α∈F7[β]] pr2(debool(Fi1[{α}](β)))'),
50
+ cst(7, 'F3', CstType.FUNCTION, '[σ∈D{σ0∈ℬ(ℬ(R1)×ℬ(C1)) | P6[pr2(σ0)]}] F7[σ]'),
51
+ cst(8, 'F4', CstType.FUNCTION, '[β∈D{β0∈ℬ(ℬ(R1)×ℬ(C1)) | P6[pr2(β0)]}, α∈F3[β]] F9[β,α]'),
52
+ cst(9, 'S1', CstType.STRUCTURED, 'ℬ(ℬ(X1)×ℬ(C1))'),
53
+ cst(10, 'A1', CstType.AXIOM, 'P6[ℬ(C1)]')
54
+ ];
55
+
56
+ const ordered = restoreConstituentOrder(items);
57
+ const aliases = ordered.map(item => item.alias);
58
+ expect(aliases.indexOf('P6')).toBeLessThan(aliases.indexOf('F3'));
59
+ expect(aliases.indexOf('F9')).toBeLessThan(aliases.indexOf('F4'));
60
+
61
+ const relations = computeSemanticRelations(items, buildFormalDependencyGraph(items));
62
+ expect(relations.parentOf.get(8)).toBe(4); // F4 → P6
63
+ expect(relations.childrenOf.get(4)).toEqual(expect.arrayContaining([7, 8]));
64
+ });
65
+
66
+ it('builds supplier → dependent formal edges', () => {
67
+ const items = [cst(1, 'X1', CstType.BASE), cst(2, 'D1', CstType.TERM, 'ℬ(X1)')];
68
+ const graph = buildFormalDependencyGraph(items);
69
+ expect(graph.hasEdge(1, 2)).toBe(true);
70
+ expect(graph.hasEdge(2, 1)).toBe(false);
71
+ });
72
+ });
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Formal dependency graph and declaration-order restore for RSForm constituents.
3
+ */
4
+
5
+ import { Graph } from '../../graph';
6
+ import { extractGlobals } from '../../rslang/api';
7
+ import { CstType } from '../rsform';
8
+
9
+ import { computeSemanticRelations } from './semantic-relations';
10
+ import { type OrderableConstituenta, type SemanticRelations } from './types';
11
+
12
+ /**
13
+ * Build the formal dependency graph: supplier → dependent.
14
+ * Matches backend {@code RSForm.graph_formal}.
15
+ */
16
+ export function buildFormalDependencyGraph(items: readonly OrderableConstituenta[]): Graph<number> {
17
+ const graph = new Graph<number>();
18
+ const byAlias = new Map(items.map(cst => [cst.alias, cst]));
19
+ for (const cst of items) {
20
+ graph.addNode(cst.id);
21
+ }
22
+ for (const cst of items) {
23
+ for (const alias of extractGlobals(cst.definition_formal)) {
24
+ const supplier = byAlias.get(alias);
25
+ if (supplier) {
26
+ graph.addEdge(supplier.id, cst.id);
27
+ }
28
+ }
29
+ }
30
+ return graph;
31
+ }
32
+
33
+ /**
34
+ * Reorder constituents: formal topology hard, semantic children sticky when ready,
35
+ * otherwise stable against type/kernel preferred ranks.
36
+ *
37
+ * One Kahn pass over the formal dependency graph.
38
+ */
39
+ export function restoreConstituentOrder<T extends OrderableConstituenta>(
40
+ items: readonly T[],
41
+ graph: Graph<number> = buildFormalDependencyGraph(items),
42
+ relations: SemanticRelations = computeSemanticRelations(items, graph)
43
+ ): T[] {
44
+ if (items.length <= 1) {
45
+ return [...items];
46
+ }
47
+
48
+ const byId = new Map(items.map(cst => [cst.id, cst]));
49
+ const preferred = kernelPriority(items, graph, relations.parentOf);
50
+ const rank = new Map(preferred.map((cst, index) => [cst.id, index]));
51
+
52
+ const remainingInputs = new Map<number, number>();
53
+ for (const cst of preferred) {
54
+ const inputs = graph.at(cst.id)?.inputs ?? [];
55
+ remainingInputs.set(
56
+ cst.id,
57
+ inputs.reduce((count, src) => count + (rank.has(src) ? 1 : 0), 0)
58
+ );
59
+ }
60
+
61
+ const ready = new Set<number>();
62
+ for (const cst of preferred) {
63
+ if ((remainingInputs.get(cst.id) ?? 0) === 0) {
64
+ ready.add(cst.id);
65
+ }
66
+ }
67
+
68
+ const children = new Map<number, number[]>();
69
+ for (const cst of preferred) {
70
+ children.set(
71
+ cst.id,
72
+ (relations.childrenOf.get(cst.id) ?? []).filter(childId => rank.has(childId))
73
+ );
74
+ }
75
+
76
+ const result: T[] = [];
77
+ const pendingChildren: number[] = [];
78
+
79
+ function enqueueSticky(candidateIds: readonly number[]): void {
80
+ const sticky = candidateIds.filter(
81
+ childId => ready.has(childId) && !pendingChildren.includes(childId)
82
+ );
83
+ sticky.sort((left, right) => (rank.get(left) ?? 0) - (rank.get(right) ?? 0));
84
+ pendingChildren.push(...sticky);
85
+ }
86
+
87
+ function pickReady(): number {
88
+ while (pendingChildren.length > 0) {
89
+ const childId = pendingChildren.shift()!;
90
+ if (ready.has(childId)) {
91
+ return childId;
92
+ }
93
+ }
94
+ let best = -1;
95
+ let bestRank = Number.POSITIVE_INFINITY;
96
+ for (const nodeId of ready) {
97
+ const nodeRank = rank.get(nodeId) ?? Number.POSITIVE_INFINITY;
98
+ if (nodeRank < bestRank) {
99
+ best = nodeId;
100
+ bestRank = nodeRank;
101
+ }
102
+ }
103
+ return best;
104
+ }
105
+
106
+ while (ready.size > 0 || pendingChildren.length > 0) {
107
+ if (ready.size === 0) {
108
+ pendingChildren.length = 0;
109
+ continue;
110
+ }
111
+ const nodeId = pickReady();
112
+ ready.delete(nodeId);
113
+ result.push(byId.get(nodeId)!);
114
+
115
+ for (const dependentId of graph.at(nodeId)?.outputs ?? []) {
116
+ if (!remainingInputs.has(dependentId)) {
117
+ continue;
118
+ }
119
+ const next = (remainingInputs.get(dependentId) ?? 0) - 1;
120
+ remainingInputs.set(dependentId, next);
121
+ if (next === 0) {
122
+ ready.add(dependentId);
123
+ }
124
+ }
125
+
126
+ enqueueSticky(children.get(nodeId) ?? []);
127
+ }
128
+
129
+ if (result.length < preferred.length) {
130
+ const placed = new Set(result.map(cst => cst.id));
131
+ for (const cst of preferred) {
132
+ if (!placed.has(cst.id)) {
133
+ result.push(cst);
134
+ }
135
+ }
136
+ }
137
+
138
+ return result;
139
+ }
140
+
141
+ function kernelPriority<T extends OrderableConstituenta>(
142
+ items: readonly T[],
143
+ graph: Graph<number>,
144
+ parentOf: Map<number, number>
145
+ ): T[] {
146
+ const byId = new Map(items.map(cst => [cst.id, cst]));
147
+ const seen = new Set<number>();
148
+ const result: T[] = [];
149
+
150
+ function push(cst: T): void {
151
+ if (seen.has(cst.id)) {
152
+ return;
153
+ }
154
+ seen.add(cst.id);
155
+ result.push(cst);
156
+ }
157
+
158
+ for (const cst of items) {
159
+ if (cst.cst_type === CstType.BASE) {
160
+ push(cst);
161
+ }
162
+ }
163
+ for (const cst of items) {
164
+ if (cst.cst_type === CstType.CONSTANT) {
165
+ push(cst);
166
+ }
167
+ }
168
+ for (const cst of items) {
169
+ if ((graph.at(cst.id)?.inputs.length ?? 0) === 0) {
170
+ push(cst);
171
+ }
172
+ }
173
+
174
+ const kernelSeeds = items
175
+ .filter(cst => {
176
+ const parentId = parentOf.get(cst.id) ?? cst.id;
177
+ const parent = byId.get(parentId);
178
+ return (
179
+ cst.cst_type === CstType.STRUCTURED ||
180
+ cst.cst_type === CstType.AXIOM ||
181
+ parent?.cst_type === CstType.STRUCTURED
182
+ );
183
+ })
184
+ .map(cst => cst.id);
185
+ const kernel = new Set<number>([...kernelSeeds, ...graph.expandAllInputs(kernelSeeds)]);
186
+ for (const cst of items) {
187
+ if (kernel.has(cst.id)) {
188
+ push(cst);
189
+ }
190
+ }
191
+ for (const cst of items) {
192
+ push(cst);
193
+ }
194
+ return result;
195
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Module: API for formal representation for systems of concepts.
3
+ */
4
+
5
+ export {
6
+ addAliasReference,
7
+ applyMappingToConstituents,
8
+ argumentValuesToMapping,
9
+ generateAlias,
10
+ removeAliasReference,
11
+ validateAliasFormat,
12
+ validateNewAlias
13
+ } from './alias';
14
+ export { getAnalysisFor, refineAnalysisForDependencyCycles } from './analysis';
15
+ export {
16
+ canHaveManualTypification,
17
+ canProduceStructure,
18
+ getCstTypePrefix,
19
+ guessCstType,
20
+ inferClass,
21
+ isBaseSet,
22
+ isBasicConcept,
23
+ isFunctional,
24
+ isLogical,
25
+ isTemplateCst,
26
+ typeClassForCstType
27
+ } from './cst-type';
28
+ export { applyFilterCategory, inferStatus, inferTemplate, inferTemplatedType, normalizeExpression } from './expression';
29
+ export { restoreConstituentOrder } from './formal-graph';
30
+ export { calculateSchemaStats, isSchemaIssue } from './stats';
31
+ export { findCstByStructure, getStructureName, inferNewSpawnPosition } from './structure';
32
+ export { sortItemsForInlineSynthesis } from './synthesis';
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Semantic parent/children used when restoring constituent order.
3
+ */
4
+
5
+ import { type Graph } from '../../graph';
6
+ import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from '../../rslang/api';
7
+ import { CstType } from '../rsform';
8
+
9
+ import { isBaseSet, isFunctional } from './cst-type';
10
+ import { inferTemplate } from './expression';
11
+ import { type OrderableConstituenta, type SemanticRelations } from './types';
12
+
13
+ /**
14
+ * Infer semantic parent/children used for clustering during restore order.
15
+ * Port of backend {@code SemanticInfo}.
16
+ */
17
+ export function computeSemanticRelations(
18
+ items: readonly OrderableConstituenta[],
19
+ graph: Graph<number>
20
+ ): SemanticRelations {
21
+ const byId = new Map(items.map(cst => [cst.id, cst]));
22
+ const byAlias = new Map(items.map(cst => [cst.alias, cst]));
23
+ const isTemplate = new Map<number, boolean>();
24
+ const isSimple = new Map<number, boolean>();
25
+ const parentOf = new Map<number, number>();
26
+ const childrenOf = new Map<number, number[]>();
27
+
28
+ for (const cst of items) {
29
+ parentOf.set(cst.id, cst.id);
30
+ childrenOf.set(cst.id, []);
31
+ }
32
+
33
+ function needCheckHead(sources: Set<number>, head: string): boolean {
34
+ if (sources.size === 0) {
35
+ return true;
36
+ }
37
+ if (sources.size !== 1) {
38
+ return false;
39
+ }
40
+ const base = byId.get([...sources][0])!;
41
+ return !isFunctional(base.cst_type) || splitTemplateDefinition(base.definition_formal).head !== head;
42
+ }
43
+
44
+ function extractSources(target: OrderableConstituenta): Set<number> {
45
+ const sources = new Set<number>();
46
+ if (!isFunctional(target.cst_type)) {
47
+ for (const parentId of graph.at(target.id)?.inputs ?? []) {
48
+ if (!(isTemplate.get(parentId) && isSimple.get(parentId))) {
49
+ sources.add(parentOf.get(parentId) ?? parentId);
50
+ }
51
+ }
52
+ return sources;
53
+ }
54
+
55
+ const expression = splitTemplateDefinition(target.definition_formal);
56
+ for (const alias of extractGlobals(expression.body)) {
57
+ const parent = byAlias.get(alias);
58
+ if (!parent) {
59
+ continue;
60
+ }
61
+ if (!(isTemplate.get(parent.id) && isSimple.get(parent.id))) {
62
+ sources.add(parentOf.get(parent.id) ?? parent.id);
63
+ }
64
+ }
65
+ if (needCheckHead(sources, expression.head)) {
66
+ for (const alias of extractGlobals(expression.head)) {
67
+ const parent = byAlias.get(alias);
68
+ if (!parent) {
69
+ continue;
70
+ }
71
+ if (!isBaseSet(parent.cst_type) && !(isTemplate.get(parent.id) && isSimple.get(parent.id))) {
72
+ sources.add(parentOf.get(parent.id) ?? parent.id);
73
+ }
74
+ }
75
+ }
76
+ return sources;
77
+ }
78
+
79
+ function inferSimple(target: OrderableConstituenta): boolean {
80
+ if (target.cst_type === CstType.STRUCTURED || isBaseSet(target.cst_type)) {
81
+ return false;
82
+ }
83
+ const dependencies = graph.at(target.id)?.inputs ?? [];
84
+ if (dependencies.some(id => isTemplate.get(id) && !isSimple.get(id))) {
85
+ return false;
86
+ }
87
+ const expression = isFunctional(target.cst_type)
88
+ ? splitTemplateDefinition(target.definition_formal).body
89
+ : target.definition_formal;
90
+ return isSimpleExpression(expression);
91
+ }
92
+
93
+ for (const cstId of graph.topologicalOrder()) {
94
+ const cst = byId.get(cstId);
95
+ if (!cst) {
96
+ continue;
97
+ }
98
+ isTemplate.set(cstId, inferTemplate(cst.definition_formal));
99
+ isSimple.set(cstId, inferSimple(cst));
100
+ if (!isSimple.get(cstId) || cst.cst_type === CstType.STRUCTURED) {
101
+ continue;
102
+ }
103
+ const sources = extractSources(cst);
104
+ if (sources.size !== 1) {
105
+ continue;
106
+ }
107
+ const parentId = [...sources][0];
108
+ const parent = byId.get(parentId);
109
+ if (!parent || isBaseSet(parent.cst_type)) {
110
+ continue;
111
+ }
112
+ parentOf.set(cstId, parentId);
113
+ childrenOf.get(parentId)!.push(cstId);
114
+ }
115
+
116
+ return { parentOf, childrenOf };
117
+ }