@rsconcept/domain 2.0.2 → 2.1.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 (43) hide show
  1. package/README.md +1 -1
  2. package/dist/cctext/language-api.js.map +1 -1
  3. package/dist/cctext/language.d.ts +7 -4
  4. package/dist/cctext/language.js.map +1 -1
  5. package/dist/{diagnostics-D7SiHAwW.js → diagnostics-BSryP66e.js} +116 -11
  6. package/dist/diagnostics-BSryP66e.js.map +1 -0
  7. package/dist/{diagnostics-Clo3mkba.d.ts → diagnostics-Dtzgj5Q9.d.ts} +14 -5
  8. package/dist/graph/graph.d.ts +1 -1
  9. package/dist/graph/graph.js +88 -1
  10. package/dist/graph/graph.js.map +1 -1
  11. package/dist/graph/index.d.ts +1 -1
  12. package/dist/{graph-DR8rL2o3.d.ts → graph-DPOvglEm.d.ts} +10 -2
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.js +2 -2
  15. package/dist/library/diagnostics.d.ts +2 -2
  16. package/dist/library/diagnostics.js +2 -2
  17. package/dist/library/index.d.ts +2 -2
  18. package/dist/library/index.js +2 -2
  19. package/dist/library/oss-api.d.ts +1 -1
  20. package/dist/library/oss.d.ts +1 -1
  21. package/dist/library/rsengine.d.ts +1 -1
  22. package/dist/library/rsengine.js +1 -1
  23. package/dist/library/rsform-api.d.ts +13 -2
  24. package/dist/library/rsform-api.js +2 -2
  25. package/dist/library/rsform.d.ts +1 -1
  26. package/dist/library/rsmodel-api.d.ts +1 -1
  27. package/dist/library/rsmodel-api.js +1 -1
  28. package/dist/library/rsmodel.d.ts +1 -1
  29. package/dist/library/structure-planner.d.ts +1 -1
  30. package/dist/library/structure-planner.js +1 -1
  31. package/package.json +14 -13
  32. package/src/cctext/language-api.test.ts +5 -0
  33. package/src/cctext/language-api.ts +6 -6
  34. package/src/cctext/language.ts +3 -4
  35. package/src/graph/graph.test.ts +22 -0
  36. package/src/graph/graph.ts +105 -1
  37. package/src/index.ts +2 -0
  38. package/src/library/diagnostics.test.ts +30 -0
  39. package/src/library/diagnostics.ts +43 -2
  40. package/src/library/index.ts +2 -0
  41. package/src/library/rsform-api.test.ts +99 -3
  42. package/src/library/rsform-api.ts +147 -6
  43. package/dist/diagnostics-D7SiHAwW.js.map +0 -1
@@ -379,7 +379,7 @@ export class Graph<NodeID = number> {
379
379
  * Finds a cycle in the graph.
380
380
  *
381
381
  * @returns {NodeID[] | null} The cycle if found, otherwise `null`.
382
- * Uses non-recursive DFS.
382
+ * Uses non-recursive DFS. The returned path is closed (first id equals last).
383
383
  */
384
384
  findCycle(): NodeID[] | null {
385
385
  const visited = new Set<NodeID>();
@@ -430,4 +430,108 @@ export class Graph<NodeID = number> {
430
430
  }
431
431
  return null;
432
432
  }
433
+
434
+ /**
435
+ * Finds one closed cycle path per cyclic strongly connected component.
436
+ *
437
+ * @returns Closed paths (first id equals last). Empty when the graph is acyclic.
438
+ */
439
+ findCycles(): NodeID[][] {
440
+ const components = this.stronglyConnectedComponents();
441
+ const result: NodeID[][] = [];
442
+ for (const component of components) {
443
+ if (component.length === 1) {
444
+ const id = component[0];
445
+ if (this.hasEdge(id, id)) {
446
+ result.push([id, id]);
447
+ }
448
+ continue;
449
+ }
450
+ const subgraph = new Graph<NodeID>();
451
+ const memberIds = new Set(component);
452
+ for (const id of component) {
453
+ subgraph.addNode(id);
454
+ const node = this.nodes.get(id);
455
+ if (!node) {
456
+ continue;
457
+ }
458
+ for (const child of node.outputs) {
459
+ if (memberIds.has(child)) {
460
+ subgraph.addEdge(id, child);
461
+ }
462
+ }
463
+ }
464
+ const cycle = subgraph.findCycle();
465
+ if (cycle) {
466
+ result.push(cycle);
467
+ }
468
+ }
469
+ return result;
470
+ }
471
+
472
+ /** Tarjan strongly connected components (non-recursive). */
473
+ stronglyConnectedComponents(): NodeID[][] {
474
+ let index = 0;
475
+ const indices = new Map<NodeID, number>();
476
+ const lowlink = new Map<NodeID, number>();
477
+ const onStack = new Set<NodeID>();
478
+ const stack: NodeID[] = [];
479
+ const components: NodeID[][] = [];
480
+
481
+ type Frame = { nodeId: NodeID; edgeIndex: number; entered: boolean };
482
+ for (const startId of this.nodes.keys()) {
483
+ if (indices.has(startId)) {
484
+ continue;
485
+ }
486
+ const callStack: Frame[] = [{ nodeId: startId, edgeIndex: 0, entered: false }];
487
+ while (callStack.length > 0) {
488
+ const frame = callStack[callStack.length - 1];
489
+ const node = this.nodes.get(frame.nodeId);
490
+ if (!node) {
491
+ callStack.pop();
492
+ continue;
493
+ }
494
+
495
+ if (!frame.entered) {
496
+ frame.entered = true;
497
+ indices.set(frame.nodeId, index);
498
+ lowlink.set(frame.nodeId, index);
499
+ index += 1;
500
+ stack.push(frame.nodeId);
501
+ onStack.add(frame.nodeId);
502
+ }
503
+
504
+ if (frame.edgeIndex < node.outputs.length) {
505
+ const child = node.outputs[frame.edgeIndex];
506
+ frame.edgeIndex += 1;
507
+ if (!indices.has(child)) {
508
+ callStack.push({ nodeId: child, edgeIndex: 0, entered: false });
509
+ } else if (onStack.has(child)) {
510
+ lowlink.set(frame.nodeId, Math.min(lowlink.get(frame.nodeId)!, indices.get(child)!));
511
+ }
512
+ continue;
513
+ }
514
+
515
+ callStack.pop();
516
+ if (callStack.length > 0) {
517
+ const parentId = callStack[callStack.length - 1].nodeId;
518
+ lowlink.set(parentId, Math.min(lowlink.get(parentId)!, lowlink.get(frame.nodeId)!));
519
+ }
520
+
521
+ if (lowlink.get(frame.nodeId) === indices.get(frame.nodeId)) {
522
+ const component: NodeID[] = [];
523
+ while (stack.length > 0) {
524
+ const id = stack.pop()!;
525
+ onStack.delete(id);
526
+ component.push(id);
527
+ if (id === frame.nodeId) {
528
+ break;
529
+ }
530
+ }
531
+ components.push(component);
532
+ }
533
+ }
534
+ }
535
+ return components;
536
+ }
433
537
  }
package/src/index.ts CHANGED
@@ -11,7 +11,9 @@ export { Graph } from './graph/graph';
11
11
  export {
12
12
  assignSchemaDiagnostics,
13
13
  type CstDiagnostic,
14
+ detectDependencyCycleDiagnostics,
14
15
  DiagnosticKind,
16
+ formatDependencyCycle,
15
17
  hasCstDiagnostic,
16
18
  modelStatusCstDiagnostic,
17
19
  RSDiagnosticCode
@@ -1,8 +1,11 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
+ import { Graph } from '../graph';
4
+
3
5
  import {
4
6
  assignSchemaDiagnostics,
5
7
  DiagnosticKind,
8
+ formatDependencyCycle,
6
9
  hasCstDiagnostic,
7
10
  modelStatusCstDiagnostic,
8
11
  RSDiagnosticCode
@@ -82,4 +85,31 @@ describe('assignSchemaDiagnostics', () => {
82
85
  'D2'
83
86
  );
84
87
  });
88
+
89
+ it('detects dependency cycles when graph is provided', () => {
90
+ const items = [
91
+ stubCst(1, 'F3', { cst_type: CstType.TERM, definition_formal: 'P1' }),
92
+ stubCst(2, 'P1', { cst_type: CstType.AXIOM, definition_formal: 'F3' }),
93
+ stubCst(3, 'X1', { convention: 'base', term_resolved: 'base' })
94
+ ];
95
+ const graph = new Graph([[1, 2], [2, 1], [3]]);
96
+ const resolveAlias = (id: number) => items.find(item => item.id === id)!.alias;
97
+
98
+ assignSchemaDiagnostics(items, resolveAlias, new Map(), graph);
99
+
100
+ expect(hasCstDiagnostic(items[0], RSDiagnosticCode.schemaDependencyCycle)).toBe(true);
101
+ expect(hasCstDiagnostic(items[1], RSDiagnosticCode.schemaDependencyCycle)).toBe(true);
102
+ expect(hasCstDiagnostic(items[2], RSDiagnosticCode.schemaDependencyCycle)).toBe(false);
103
+ const cycle = items[0].diagnostics.find(item => item.code === RSDiagnosticCode.schemaDependencyCycle)?.params?.[0];
104
+ expect(cycle).toBeDefined();
105
+ expect(cycle!.includes('F3')).toBe(true);
106
+ expect(cycle!.includes('P1')).toBe(true);
107
+ expect(cycle!.startsWith(cycle!.split(' → ').at(-1)!)).toBe(true);
108
+ });
109
+ });
110
+
111
+ describe('formatDependencyCycle', () => {
112
+ it('joins aliases with arrows', () => {
113
+ expect(formatDependencyCycle(['F3', 'P1', 'F3'])).toBe('F3 → P1 → F3');
114
+ });
85
115
  });
@@ -2,6 +2,7 @@
2
2
  * Module: Compact diagnostics for constituents (schema and model issue codes).
3
3
  */
4
4
 
5
+ import { type Graph } from '../graph';
5
6
  import { labelType } from '../rslang/labels';
6
7
 
7
8
  import { type Constituenta, type CstType } from './rsform';
@@ -23,6 +24,7 @@ export const RSDiagnosticCode = {
23
24
  schemaMissingConvention: 0x8903,
24
25
  schemaMissingTerm: 0x8904,
25
26
  schemaTypeMismatch: 0x8905,
27
+ schemaDependencyCycle: 0x8906,
26
28
 
27
29
  modelEmpty: 0x8910,
28
30
  modelAxiomFalse: 0x8911,
@@ -45,20 +47,27 @@ export function hasCstDiagnostic(cst: Constituenta, code: RSDiagnosticCode): boo
45
47
  }
46
48
 
47
49
  /**
48
- * Populate `diagnostics` on each constituent (homonyms, duplicates, metadata, type mismatch).
50
+ * Populate `diagnostics` on each constituent (homonyms, duplicates, metadata, type mismatch, cycles).
49
51
  *
50
52
  * Requires normalized formal definitions for duplicate detection (same as Portal loader).
53
+ * When `graph` is provided, dependency cycles are reported as {@link RSDiagnosticCode.schemaDependencyCycle}.
51
54
  */
52
55
  export function assignSchemaDiagnostics(
53
56
  items: Constituenta[],
54
57
  resolveAlias: (id: number) => string,
55
- normalizedDefinitions: ReadonlyMap<number, string>
58
+ normalizedDefinitions: ReadonlyMap<number, string>,
59
+ graph?: Graph
56
60
  ): void {
57
61
  const homonymById = detectHomonymDiagnostics(items, resolveAlias);
58
62
  const duplicateById = detectFormalDuplicateDiagnostics(items, resolveAlias, normalizedDefinitions);
63
+ const cycleById = graph ? detectDependencyCycleDiagnostics(graph, resolveAlias) : new Map<number, CstDiagnostic>();
59
64
 
60
65
  for (const cst of items) {
61
66
  const diagnostics: CstDiagnostic[] = [];
67
+ const cycle = cycleById.get(cst.id);
68
+ if (cycle) {
69
+ diagnostics.push(cycle);
70
+ }
62
71
  const homonym = homonymById.get(cst.id);
63
72
  if (homonym) {
64
73
  diagnostics.push(homonym);
@@ -72,6 +81,38 @@ export function assignSchemaDiagnostics(
72
81
  }
73
82
  }
74
83
 
84
+ /** Format a closed cycle of aliases as `A → B → A`. */
85
+ export function formatDependencyCycle(aliases: readonly string[]): string {
86
+ return aliases.join(' → ');
87
+ }
88
+
89
+ /**
90
+ * Map each node id that participates in a dependency cycle to a schema diagnostic.
91
+ * `params[0]` is the closed cycle path (`F3 → P1 → F3`).
92
+ */
93
+ export function detectDependencyCycleDiagnostics(
94
+ graph: Graph,
95
+ resolveAlias: (id: number) => string
96
+ ): Map<number, CstDiagnostic> {
97
+ const result = new Map<number, CstDiagnostic>();
98
+ for (const cycle of graph.findCycles()) {
99
+ if (cycle.length < 2) {
100
+ continue;
101
+ }
102
+ const aliases = cycle.map(id => resolveAlias(id));
103
+ const cycleText = formatDependencyCycle(aliases);
104
+ const memberIds = new Set(cycle);
105
+ for (const id of memberIds) {
106
+ result.set(id, {
107
+ kind: DiagnosticKind.SCHEMA,
108
+ code: RSDiagnosticCode.schemaDependencyCycle,
109
+ params: [cycleText]
110
+ });
111
+ }
112
+ }
113
+ return result;
114
+ }
115
+
75
116
  /** Map a model evaluation status to a compact diagnostic, or `null` when not an issue. */
76
117
  export function modelStatusCstDiagnostic(status: EvalStatus, cstType: CstType): CstDiagnostic | null {
77
118
  const code = modelStatusToCode(status, cstType);
@@ -1,7 +1,9 @@
1
1
  export {
2
2
  assignSchemaDiagnostics,
3
3
  type CstDiagnostic,
4
+ detectDependencyCycleDiagnostics,
4
5
  DiagnosticKind,
6
+ formatDependencyCycle,
5
7
  hasCstDiagnostic,
6
8
  modelStatusCstDiagnostic,
7
9
  RSDiagnosticCode
@@ -1,9 +1,18 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
 
3
+ import { Graph } from '../graph';
3
4
  import { RSErrorCode } from '../rslang/error';
4
-
5
- import { CstType, type RSForm } from './rsform';
6
- import { applyMappingToConstituents, getAnalysisFor, normalizeExpression, validateAliasFormat } from './rsform-api';
5
+ import { RSLangAnalyzer } from '../rslang/semantic/analyzer';
6
+
7
+ import { RSDiagnosticCode } from './diagnostics';
8
+ import { type Constituenta, CstType, type RSForm } from './rsform';
9
+ import {
10
+ applyMappingToConstituents,
11
+ findCstByStructure,
12
+ getAnalysisFor,
13
+ normalizeExpression,
14
+ validateAliasFormat
15
+ } from './rsform-api';
7
16
 
8
17
  const emptySchema = {} as RSForm;
9
18
 
@@ -49,6 +58,56 @@ describe('getAnalysisFor', () => {
49
58
  params: [CstType.CONSTANT, 'C1']
50
59
  });
51
60
  });
61
+
62
+ it('upgrades to dependency cycle for unsaved draft that closes a cycle', () => {
63
+ // Saved graph: F3 → P1 only (P1 does not yet reference F3).
64
+ // Draft for P1 introduces F3, closing F3 ↔ P1 before Save.
65
+ const analyzer = new RSLangAnalyzer();
66
+ analyzer.addBase('X1');
67
+ // F3 is untyped in the analyzer (as after a failed cycle parse), so P1's call fails.
68
+ const items = [
69
+ {
70
+ id: 1,
71
+ alias: 'X1',
72
+ cst_type: CstType.BASE,
73
+ definition_formal: '',
74
+ diagnostics: []
75
+ },
76
+ {
77
+ id: 2,
78
+ alias: 'F3',
79
+ cst_type: CstType.FUNCTION,
80
+ definition_formal: '[σ∈X1] P1[σ]',
81
+ diagnostics: []
82
+ },
83
+ {
84
+ id: 3,
85
+ alias: 'P1',
86
+ cst_type: CstType.PREDICATE,
87
+ definition_formal: '[σ∈X1] σ=σ',
88
+ diagnostics: []
89
+ }
90
+ ] as unknown as Constituenta[];
91
+ const graph = new Graph([
92
+ [1, 2],
93
+ [1, 3],
94
+ [3, 2] // P1 → F3 from saved F3 definition
95
+ ]);
96
+ const schema = {
97
+ items,
98
+ graph,
99
+ analyzer,
100
+ cstByAlias: new Map(items.map(item => [item.alias, item])),
101
+ cstByID: new Map(items.map(item => [item.id, item]))
102
+ } as unknown as RSForm;
103
+
104
+ const draft = '[σ∈X1] F3[σ]=F3[σ]';
105
+ const result = getAnalysisFor(draft, CstType.PREDICATE, schema, 'P1');
106
+ const cycleError = result.errors.find(error => error.code === RSDiagnosticCode.schemaDependencyCycle);
107
+ expect(cycleError).toBeDefined();
108
+ expect(cycleError!.params?.[0]).toContain('F3');
109
+ expect(cycleError!.params?.[0]).toContain('P1');
110
+ });
52
111
  });
53
112
 
54
113
  describe('applyMappingToConstituents', () => {
@@ -98,3 +157,40 @@ describe('normalizeExpression', () => {
98
157
  expect(normalizeExpression('ℬ(X1)', CstType.STRUCTURED)).toBeNull();
99
158
  });
100
159
  });
160
+
161
+ describe('findCstByStructure', () => {
162
+ function makeProjectionChainSchema() {
163
+ const s1 = { id: 1, alias: 'S1' };
164
+ const d1 = { id: 2, alias: 'D1', spawner: 1, spawner_path: [0, 1] };
165
+ const d2 = { id: 3, alias: 'D2', spawner: 2, spawner_path: [2] };
166
+ const d3 = { id: 4, alias: 'D3', spawner: 3, spawner_path: [1] };
167
+ const d4 = { id: 5, alias: 'D4', spawner: 4, spawner_path: [2] };
168
+ const schema = {
169
+ items: [s1, d1, d2, d3, d4],
170
+ cstByID: new Map([
171
+ [1, s1],
172
+ [2, d1],
173
+ [3, d2],
174
+ [4, d3],
175
+ [5, d4]
176
+ ])
177
+ } as unknown as RSForm;
178
+ return { schema, s1, d1, d2, d3, d4 };
179
+ }
180
+
181
+ it('resolves chain of relative projection paths to root target', () => {
182
+ const { schema, s1, d2 } = makeProjectionChainSchema();
183
+
184
+ expect(findCstByStructure(schema, s1 as never, [0, 1, 2] as never)).toBe(d2);
185
+ });
186
+
187
+ it('resolves a four-step projection chain from the root structure', () => {
188
+ const { schema, s1, d1, d2, d3, d4 } = makeProjectionChainSchema();
189
+
190
+ expect(findCstByStructure(schema, s1 as never, [0, 1] as never)).toBe(d1);
191
+ expect(findCstByStructure(schema, s1 as never, [0, 1, 2] as never)).toBe(d2);
192
+ expect(findCstByStructure(schema, s1 as never, [0, 1, 2, 1] as never)).toBe(d3);
193
+ expect(findCstByStructure(schema, s1 as never, [0, 1, 2, 1, 2] as never)).toBe(d4);
194
+ expect(findCstByStructure(schema, s1 as never, [0, 1, 2, 2] as never)).toBeNull();
195
+ });
196
+ });
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { applyEntityReferenceMapping } from '../cctext/language-api';
6
+ import { type Graph } from '../graph';
6
7
  import { type AstNode } from '../parsing';
7
8
  import {
8
9
  type AnalysisFull,
@@ -17,13 +18,14 @@ import {
17
18
  type AliasMapping,
18
19
  applyAliasMapping,
19
20
  applyTypificationMapping,
21
+ extractGlobals,
20
22
  generateExpressionFromAst,
21
23
  parseRSLangExpression
22
24
  } from '../rslang/api';
23
25
  import { basic, bool, constant, type EchelonFunctional, isTypification } from '../rslang/semantic/typification';
24
26
  import { applyPath } from '../rslang/semantic/typification-api';
25
27
 
26
- import { hasCstDiagnostic, RSDiagnosticCode } from './diagnostics';
28
+ import { formatDependencyCycle, hasCstDiagnostic, RSDiagnosticCode } from './diagnostics';
27
29
  import { type LibraryItem } from './library';
28
30
  import {
29
31
  type ArgumentValue,
@@ -462,10 +464,113 @@ export function getAnalysisFor(expression: string, cstType: CstType, schema: RSF
462
464
  ast: null
463
465
  };
464
466
  }
465
- return schema.analyzer.checkFull(expression, {
467
+ const analysis = schema.analyzer.checkFull(expression, {
466
468
  expected: typeClassForCstType(cstType),
467
469
  isDomain: cstType === CstType.STRUCTURED
468
470
  });
471
+ return refineAnalysisForDependencyCycles(analysis, expression, schema, alias);
472
+ }
473
+
474
+ /**
475
+ * When an untyped / bare F·P reference names a constituent on a definition cycle with the
476
+ * current expression, replace that error with {@link RSDiagnosticCode.schemaDependencyCycle}.
477
+ *
478
+ * Covers both semantic `globalNotTyped` (e.g. `F3[σ]`) and parser `globalFuncWithoutArgs`
479
+ * (bare `P1` / `F1` treated as a call without arguments).
480
+ *
481
+ * Uses a draft dependency graph: incoming edges of the edited constituent are taken from
482
+ * `expression`, so Check works before Save.
483
+ */
484
+ export function refineAnalysisForDependencyCycles(
485
+ analysis: AnalysisFull,
486
+ expression: string,
487
+ schema: RSForm,
488
+ alias?: string
489
+ ): AnalysisFull {
490
+ if (analysis.errors.length === 0 || !schema.graph) {
491
+ return analysis;
492
+ }
493
+
494
+ const currentId = alias ? schema.cstByAlias.get(alias)?.id : undefined;
495
+ const expressionGlobals = extractGlobals(expression);
496
+ const draftGraph =
497
+ currentId !== undefined ? buildDraftDependencyGraph(schema, currentId, expressionGlobals) : schema.graph;
498
+
499
+ let changed = false;
500
+ const errors = analysis.errors.map(error => {
501
+ if (error.code !== RSErrorCode.globalNotTyped && error.code !== RSErrorCode.globalFuncWithoutArgs) {
502
+ return error;
503
+ }
504
+ const missingAlias = error.params?.[0];
505
+ if (!missingAlias) {
506
+ return error;
507
+ }
508
+ const missing = schema.cstByAlias.get(missingAlias);
509
+ if (!missing) {
510
+ return error;
511
+ }
512
+ const cycleText = resolveDependencyCycleText(schema, draftGraph, currentId, missing.id);
513
+ if (!cycleText) {
514
+ return error;
515
+ }
516
+ changed = true;
517
+ return {
518
+ ...error,
519
+ code: RSDiagnosticCode.schemaDependencyCycle as unknown as RSErrorCode,
520
+ params: [cycleText]
521
+ };
522
+ });
523
+ return changed ? { ...analysis, errors } : analysis;
524
+ }
525
+
526
+ /**
527
+ * Clone the schema graph and replace dependencies of `currentId` with globals from the
528
+ * expression under check (unsaved draft).
529
+ */
530
+ function buildDraftDependencyGraph(schema: RSForm, currentId: number, expressionGlobals: ReadonlySet<string>): Graph {
531
+ const graph = schema.graph.clone();
532
+ const node = graph.at(currentId);
533
+ if (node) {
534
+ for (const parentId of [...node.inputs]) {
535
+ graph.removeEdge(parentId, currentId);
536
+ }
537
+ } else {
538
+ graph.addNode(currentId);
539
+ }
540
+ for (const globalAlias of expressionGlobals) {
541
+ const source = schema.cstByAlias.get(globalAlias);
542
+ if (source) {
543
+ graph.addEdge(source.id, currentId);
544
+ }
545
+ }
546
+ return graph;
547
+ }
548
+
549
+ function resolveDependencyCycleText(
550
+ schema: RSForm,
551
+ graph: Graph,
552
+ currentId: number | undefined,
553
+ missingId: number
554
+ ): string | null {
555
+ for (const cycle of graph.findCycles()) {
556
+ const members = new Set(cycle);
557
+ if (!members.has(missingId)) {
558
+ continue;
559
+ }
560
+ if (currentId !== undefined && !members.has(currentId)) {
561
+ continue;
562
+ }
563
+ return formatDependencyCycle(cycle.map(id => schema.cstByID.get(id)?.alias ?? String(id)));
564
+ }
565
+
566
+ if (currentId === undefined) {
567
+ const cycleDiag = schema.cstByID
568
+ .get(missingId)
569
+ ?.diagnostics.find(item => item.code === RSDiagnosticCode.schemaDependencyCycle);
570
+ return cycleDiag?.params?.[0] ?? null;
571
+ }
572
+
573
+ return null;
469
574
  }
470
575
 
471
576
  /** Calculate statistics for {@link RSForm}. */
@@ -517,10 +622,12 @@ export function calculateSchemaStats(target: RSForm): RSFormStats {
517
622
  /** Finds {@link Constituenta} by structure path. */
518
623
  export function findCstByStructure(schema: RSForm, target: Constituenta, path: TypePath): Constituenta | null {
519
624
  for (const cst of schema.items) {
520
- if (cst.spawner === target.id && cst.spawner_path) {
521
- if (cst.spawner_path.length === path.length && cst.spawner_path.every((v, i) => v === path[i])) {
522
- return cst;
523
- }
625
+ if (!cst.spawner_path) {
626
+ continue;
627
+ }
628
+ const absolutePath = resolveAbsoluteSpawnerPath(schema, cst, target.id);
629
+ if (absolutePath?.length === path.length && absolutePath.every((v, i) => v === path[i])) {
630
+ return cst;
524
631
  }
525
632
  }
526
633
  return null;
@@ -591,6 +698,40 @@ function isMissingConvention(cst: Constituenta): boolean {
591
698
  return false;
592
699
  }
593
700
 
701
+ function resolveAbsoluteSpawnerPath(schema: RSForm, cst: Constituenta, targetId: number): TypePath | null {
702
+ if (!cst.spawner_path || cst.spawner == null) {
703
+ return null;
704
+ }
705
+ if (cst.spawner === targetId) {
706
+ return cst.spawner_path;
707
+ }
708
+
709
+ const byId = schema.cstByID ?? new Map(schema.items.map(item => [item.id, item]));
710
+ const visited = new Set<number>([cst.id]);
711
+ let currentPath: number[] = [...cst.spawner_path];
712
+ let currentSpawnerId: number | null = cst.spawner;
713
+
714
+ while (currentSpawnerId != null) {
715
+ if (currentSpawnerId === targetId) {
716
+ return currentPath as TypePath;
717
+ }
718
+ if (visited.has(currentSpawnerId)) {
719
+ return null;
720
+ }
721
+ visited.add(currentSpawnerId);
722
+
723
+ const parent = byId.get(currentSpawnerId);
724
+ if (!parent?.spawner_path || parent.spawner == null) {
725
+ return null;
726
+ }
727
+
728
+ currentPath = [...parent.spawner_path, ...currentPath];
729
+ currentSpawnerId = parent.spawner;
730
+ }
731
+
732
+ return null;
733
+ }
734
+
594
735
  /** Evaluate if {@link Typification} can be used to produce structure. */
595
736
  function typeCanProduceStructure(type: Typification): boolean {
596
737
  if (type.typeID === TypeID.basic || type.typeID === TypeID.integer || type.typeID === TypeID.anyTypification) {
@@ -1 +0,0 @@
1
- {"version":3,"file":"diagnostics-D7SiHAwW.js","names":[],"sources":["../src/library/rsform-api.ts","../src/library/diagnostics.ts"],"sourcesContent":["/**\n * Module: API for formal representation for systems of concepts.\n */\n\nimport { applyEntityReferenceMapping } from '../cctext/language-api';\nimport { type AstNode } from '../parsing';\nimport {\n type AnalysisFull,\n RSErrorCode,\n TypeClass,\n TypeID,\n type TypePath,\n type Typification,\n ValueClass\n} from '../rslang';\nimport {\n type AliasMapping,\n applyAliasMapping,\n applyTypificationMapping,\n generateExpressionFromAst,\n parseRSLangExpression\n} from '../rslang/api';\nimport { basic, bool, constant, type EchelonFunctional, isTypification } from '../rslang/semantic/typification';\nimport { applyPath } from '../rslang/semantic/typification-api';\n\nimport { hasCstDiagnostic, RSDiagnosticCode } from './diagnostics';\nimport { type LibraryItem } from './library';\nimport {\n type ArgumentValue,\n type Constituenta,\n CstClass,\n CstStatus,\n CstType,\n type RSForm,\n type RSFormStats\n} from './rsform';\n\n/** Record of {@link CstType} prefixes. */\nconst CST_TYPE_PREFIX: Record<CstType, string> = {\n [CstType.NOMINAL]: 'N',\n [CstType.BASE]: 'X',\n [CstType.CONSTANT]: 'C',\n [CstType.STRUCTURED]: 'S',\n [CstType.AXIOM]: 'A',\n [CstType.TERM]: 'D',\n [CstType.FUNCTION]: 'F',\n [CstType.PREDICATE]: 'P',\n [CstType.STATEMENT]: 'T'\n};\n\n/** Record of {@link CstType} to {@link CstClass} mapping. */\nconst CST_TYPE_TO_CLASS: Record<CstType, CstClass> = {\n [CstType.NOMINAL]: CstClass.NOMINAL,\n [CstType.BASE]: CstClass.BASIC,\n [CstType.CONSTANT]: CstClass.BASIC,\n [CstType.STRUCTURED]: CstClass.BASIC,\n [CstType.TERM]: CstClass.DERIVED,\n [CstType.FUNCTION]: CstClass.DERIVED,\n [CstType.AXIOM]: CstClass.STATEMENT,\n [CstType.PREDICATE]: CstClass.DERIVED,\n [CstType.STATEMENT]: CstClass.STATEMENT\n};\n\n/** Checks if {@link Constituenta} is a schema issue. */\nexport function isSchemaIssue(cst: Constituenta): boolean {\n return (\n (cst.diagnostics?.length ?? 0) > 0 || cst.status === CstStatus.INCORRECT || cst.status === CstStatus.INCALCULABLE\n );\n}\n\n/** Infers the status of an expression based on parsing and value information. */\nexport function inferStatus(parse: boolean, value?: ValueClass | null): CstStatus {\n if (!parse) {\n return CstStatus.INCORRECT;\n }\n if (value === null) {\n return CstStatus.INCALCULABLE;\n }\n if (value === ValueClass.PROPERTY) {\n return CstStatus.PROPERTY;\n }\n return CstStatus.VERIFIED;\n}\n\n/** Infers type of constituent for a given template and arguments. */\nexport function inferTemplatedType(templateType: CstType, args: ArgumentValue[]): CstType {\n if (args.length === 0 || args.some(arg => !arg.value)) {\n return templateType;\n } else if (templateType === CstType.PREDICATE) {\n return CstType.AXIOM;\n } else {\n return CstType.TERM;\n }\n}\n\n/** Build alias mapping from template argument bindings. */\nexport function argumentValuesToMapping(args: ArgumentValue[]): AliasMapping {\n const mapping: AliasMapping = {};\n for (const arg of args) {\n if (arg.value) {\n mapping[arg.alias] = arg.value;\n }\n }\n return mapping;\n}\n\n/**\n * Normalize a RSLang expression for duplicate comparison.\n *\n * Returns `null` for basic concepts without formal definitions. Pass a pre-parsed `ast`\n * when the expression was already analyzed to avoid re-parsing.\n */\nexport function normalizeExpression(expression: string, cst_type: CstType, ast?: AstNode | null): string | null {\n if (isBasicConcept(cst_type) && cst_type !== CstType.AXIOM) {\n return null;\n }\n const parsed = ast ?? parseRSLangExpression(expression);\n let normalized = '';\n if (parsed && !parsed.hasError) {\n normalized = generateExpressionFromAst(parsed, { normalize: true });\n } else {\n normalized = expression;\n }\n normalized = normalized.replace(/\\s+/g, '');\n return normalized === '' ? null : normalized;\n}\n\n/** Checks if given expression is a template. */\nexport function inferTemplate(expression: string): boolean {\n const match = expression.match(/R\\d+/g);\n return (match && match?.length > 0) ?? false;\n}\n\n/** Infers the {@link CstClass} based on the provided {@link CstType} and template status. */\nexport function inferClass(type: CstType, isTemplate: boolean = false): CstClass {\n if (isTemplate) {\n return CstClass.TEMPLATE;\n }\n return CST_TYPE_TO_CLASS[type];\n}\n\n/** Check if {@link Constituenta} is a template or a category. */\nexport function isTemplateCst(cst: Constituenta): boolean {\n return cst.cst_type === CstType.FUNCTION || cst.cst_type === CstType.PREDICATE || cst.cst_type === CstType.STATEMENT;\n}\n\n/** Apply filter based on start {@link Constituenta} type. */\nexport function applyFilterCategory(start: Constituenta, items: Constituenta[]): Constituenta[] {\n const startIndex = items.indexOf(start);\n if (startIndex === -1) {\n return [];\n }\n const nextCategoryIndex = items.findIndex((cst, index) => index > startIndex && cst.cst_type === CstType.STATEMENT);\n\n return items.filter((_, index) => index >= startIndex && (nextCategoryIndex === -1 || index < nextCategoryIndex));\n}\n\n/** Prefix for alias indicating {@link CstType}. */\nexport function getCstTypePrefix(type: CstType): string {\n return CST_TYPE_PREFIX[type];\n}\n\n/** Guess {@link CstType} from user input hint. */\nexport function guessCstType(hint: string): CstType | null {\n if (hint.length !== 1) {\n return null;\n }\n for (const [type, prefix] of Object.entries(CST_TYPE_PREFIX)) {\n if (hint === prefix) {\n return type as CstType;\n }\n }\n return null;\n}\n\n/** Evaluate if {@link CstType} is basic concept. */\nexport function isBasicConcept(type: CstType): boolean {\n switch (type) {\n case CstType.NOMINAL:\n case CstType.BASE:\n case CstType.CONSTANT:\n case CstType.STRUCTURED:\n case CstType.AXIOM:\n return true;\n\n case CstType.TERM:\n case CstType.FUNCTION:\n case CstType.PREDICATE:\n case CstType.STATEMENT:\n return false;\n }\n}\n\n/** Evaluate if {@link CstType} is base set or constant set. */\nexport function isBaseSet(type: CstType): boolean {\n switch (type) {\n case CstType.BASE:\n case CstType.CONSTANT:\n return true;\n\n case CstType.NOMINAL:\n case CstType.STRUCTURED:\n case CstType.AXIOM:\n case CstType.TERM:\n case CstType.FUNCTION:\n case CstType.PREDICATE:\n case CstType.STATEMENT:\n return false;\n }\n}\n\n/** Evaluate if {@link CstType} is a function. */\nexport function isFunctional(type: CstType): boolean {\n switch (type) {\n case CstType.FUNCTION:\n case CstType.PREDICATE:\n return true;\n\n case CstType.NOMINAL:\n case CstType.BASE:\n case CstType.CONSTANT:\n case CstType.STRUCTURED:\n case CstType.AXIOM:\n case CstType.TERM:\n case CstType.STATEMENT:\n return false;\n }\n}\n\n/** Evaluate if {@link CstType} is logical. */\nexport function isLogical(type: CstType): boolean {\n switch (type) {\n case CstType.AXIOM:\n case CstType.STATEMENT:\n return true;\n\n case CstType.NOMINAL:\n case CstType.BASE:\n case CstType.CONSTANT:\n case CstType.STRUCTURED:\n case CstType.TERM:\n case CstType.FUNCTION:\n case CstType.PREDICATE:\n return false;\n }\n}\n\n/** Evaluate if {@link Constituenta} can be used produce structure. */\nexport function canProduceStructure(cst: Constituenta): boolean {\n switch (cst.cst_type) {\n case CstType.NOMINAL:\n case CstType.BASE:\n case CstType.CONSTANT:\n case CstType.AXIOM:\n case CstType.STATEMENT:\n case CstType.PREDICATE:\n return false;\n }\n if (!cst.effectiveType) {\n return false;\n }\n if (cst.cst_type === CstType.FUNCTION) {\n const result = (cst.effectiveType as EchelonFunctional).result;\n return typeCanProduceStructure(result);\n }\n if (!isTypification(cst.effectiveType)) {\n return false;\n }\n return typeCanProduceStructure(cst.effectiveType as Typification);\n}\n\n/** Evaluate if {@link CstType} can have manual typification. */\nexport function canHaveManualTypification(type: CstType): boolean {\n switch (type) {\n case CstType.STRUCTURED:\n case CstType.TERM:\n case CstType.FUNCTION:\n case CstType.PREDICATE:\n return true;\n\n case CstType.NOMINAL:\n case CstType.BASE:\n case CstType.CONSTANT:\n case CstType.AXIOM:\n case CstType.STATEMENT:\n return false;\n }\n}\n\n/** Validate alias format for a given {@link CstType} (prefix + digits, min length 2). */\nexport function validateAliasFormat(alias: string, type: CstType): boolean {\n if (alias.length < 2) {\n return false;\n }\n const prefix = getCstTypePrefix(type);\n if (!alias.startsWith(prefix)) {\n return false;\n }\n if (!/^\\d+$/.exec(alias.substring(prefix.length))) {\n return false;\n }\n return true;\n}\n\n/** Validate new alias against {@link CstType} and {@link RSForm}. */\nexport function validateNewAlias(alias: string, type: CstType, schema: RSForm): boolean {\n if (!validateAliasFormat(alias, type)) {\n return false;\n }\n if (schema.cstByAlias.has(alias)) {\n return false;\n }\n return true;\n}\n\n/** Generate alias for new {@link Constituenta} of a given {@link CstType} for current {@link RSForm}. */\nexport function generateAlias(type: CstType, schema: RSForm, takenAliases: string[] = []): string {\n const prefix = getCstTypePrefix(type);\n if (schema.items.length <= 0) {\n return `${prefix}1`;\n }\n let index = schema.items.reduce((prev, cst, index) => {\n if (cst.cst_type !== type) {\n return prev;\n }\n index = Number(cst.alias.slice(1 - cst.alias.length)) + 1;\n return Math.max(prev, index);\n }, 1);\n let alias = `${prefix}${index}`;\n while (takenAliases.includes(alias)) {\n index = index + 1;\n alias = `${prefix}${index}`;\n }\n return alias;\n}\n\n/**\n * Id to pass as `insert_after` when creating a new {@link Constituenta} spawned by `targetId`:\n * after the last existing child of that spawner, or after the spawner itself if none.\n */\nexport function inferNewSpawnPosition(schema: RSForm, targetId: number): number {\n let last: number | null = null;\n for (const cst of schema.items) {\n if (cst.spawner === targetId) {\n last = cst.id;\n }\n }\n return last ?? targetId;\n}\n\n/** Sorts library items relevant for InlineSynthesis with specified {@link RSForm}. */\nexport function sortItemsForInlineSynthesis(receiver: RSForm, items: readonly LibraryItem[]): LibraryItem[] {\n const result = items.filter(item => item.location === receiver.location);\n for (const item of items) {\n if (item.visible && item.owner === item.owner && !result.includes(item)) {\n result.push(item);\n }\n }\n for (const item of items) {\n if (item.visible && !result.includes(item)) {\n result.push(item);\n }\n }\n for (const item of items) {\n if (!result.includes(item)) {\n result.push(item);\n }\n }\n return result;\n}\n\n/** Fields of {@link Constituenta} updated in place by alias mapping. */\nexport interface ConstituentaMappableFields {\n alias: string;\n definition_formal: string;\n typification_manual: string;\n term_raw: string;\n definition_raw: string;\n}\n\n/**\n * Remap formal expressions and terminological entity references in constituent fields.\n * Mutates each element of `items` in place; pass owned or cloned data only.\n */\nexport function applyMappingToConstituents<T extends ConstituentaMappableFields>(\n items: T[],\n mapping: AliasMapping,\n changeAliases: boolean\n): void {\n for (const cst of items) {\n if (changeAliases && cst.alias in mapping) {\n cst.alias = mapping[cst.alias];\n }\n cst.definition_formal = applyAliasMapping(cst.definition_formal, mapping);\n cst.typification_manual = applyTypificationMapping(cst.typification_manual, mapping);\n cst.term_raw = applyEntityReferenceMapping(cst.term_raw, mapping);\n cst.definition_raw = applyEntityReferenceMapping(cst.definition_raw, mapping);\n }\n}\n\n/** Remove alias from expression. */\nexport function removeAliasReference(expression: string, alias: string): string {\n const result = expression.replaceAll(new RegExp(`\\\\b${alias}\\\\b`, 'g'), 'DEL');\n return result === 'DEL' ? '' : result;\n}\n\n/** Add alias to expression. */\nexport function addAliasReference(expression: string, alias: string): string {\n return expression + ' ' + alias;\n}\n\n/** Returns expected {@link TypeClass} of formal definition for {@link CstType}. */\nexport function typeClassForCstType(cstType: CstType): TypeClass {\n switch (cstType) {\n case CstType.NOMINAL:\n case CstType.BASE:\n case CstType.CONSTANT:\n case CstType.STRUCTURED:\n case CstType.TERM:\n return TypeClass.typification;\n case CstType.FUNCTION:\n return TypeClass.function;\n case CstType.PREDICATE:\n return TypeClass.predicate;\n case CstType.AXIOM:\n case CstType.STATEMENT:\n return TypeClass.logic;\n }\n}\n\n/** Checks whether a constituenta type supports formal definitions. */\nexport function canHaveFormalDefinition(cstType: CstType): boolean {\n return cstType !== CstType.BASE && cstType !== CstType.CONSTANT;\n}\n\n/** Analyze expression for {@link RSForm}. */\nexport function getAnalysisFor(expression: string, cstType: CstType, schema: RSForm, alias?: string): AnalysisFull {\n if (!canHaveFormalDefinition(cstType)) {\n if (expression.trim().length === 0) {\n const fallbackAlias = alias && alias.length > 0 ? alias : 'X0';\n const type = cstType === CstType.BASE ? bool(basic(fallbackAlias)) : bool(constant(fallbackAlias));\n return {\n success: true,\n type,\n valueClass: ValueClass.VALUE,\n errors: [],\n ast: null\n };\n }\n return {\n success: false,\n type: null,\n valueClass: null,\n errors: [\n {\n code: RSErrorCode.definitionNotAllowed,\n from: 0,\n to: Math.max(expression.length, 1),\n params: [cstType, alias ?? '']\n }\n ],\n ast: null\n };\n }\n return schema.analyzer.checkFull(expression, {\n expected: typeClassForCstType(cstType),\n isDomain: cstType === CstType.STRUCTURED\n });\n}\n\n/** Calculate statistics for {@link RSForm}. */\nexport function calculateSchemaStats(target: RSForm): RSFormStats {\n const items = target.items;\n return {\n count_all: items.length,\n count_crucial: items.reduce((sum, cst) => sum + (cst.crucial ? 1 : 0), 0),\n\n step_complexity: items.reduce((sum, cst) => sum + calculateStepComplexity(cst), 0),\n\n count_problematic: items.reduce((sum, cst) => sum + (isSchemaIssue(cst) ? 1 : 0), 0),\n count_homonyms: items.reduce(\n (sum, cst) => sum + (hasCstDiagnostic(cst, RSDiagnosticCode.schemaHomonym) ? 1 : 0),\n 0\n ),\n count_formal_duplicates: items.reduce(\n (sum, cst) => sum + (hasCstDiagnostic(cst, RSDiagnosticCode.schemaFormalDuplicate) ? 1 : 0),\n 0\n ),\n count_missing_convention: items.reduce((sum, cst) => sum + (isMissingConvention(cst) ? 1 : 0), 0),\n count_type_mismatch: items.reduce((sum, cst) => sum + (cst.is_type_mismatch ? 1 : 0), 0),\n\n count_incorrect: items.reduce((sum, cst) => sum + (cst.status === CstStatus.INCORRECT ? 1 : 0), 0),\n count_property: items.reduce((sum, cst) => sum + (cst.analysis?.valueClass === ValueClass.PROPERTY ? 1 : 0), 0),\n count_incalculable: items.reduce(\n (sum, cst) => sum + (cst.analysis?.success && cst.analysis.valueClass === null ? 1 : 0),\n 0\n ),\n count_inherited: items.reduce((sum, cst) => sum + (cst.is_inherited ? 1 : 0), 0),\n\n count_text_term: items.reduce((sum, cst) => sum + (cst.term_raw ? 1 : 0), 0),\n count_definition: items.reduce((sum, cst) => sum + (cst.definition_raw ? 1 : 0), 0),\n count_convention: items.reduce((sum, cst) => sum + (isBasicConcept(cst.cst_type) && cst.convention ? 1 : 0), 0),\n count_comment: items.reduce((sum, cst) => sum + (!isBasicConcept(cst.cst_type) && !cst.convention ? 1 : 0), 0),\n\n count_base: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.BASE ? 1 : 0), 0),\n count_constant: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.CONSTANT ? 1 : 0), 0),\n count_structured: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.STRUCTURED ? 1 : 0), 0),\n count_axiom: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.AXIOM ? 1 : 0), 0),\n count_term: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.TERM ? 1 : 0), 0),\n count_function: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.FUNCTION ? 1 : 0), 0),\n count_predicate: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.PREDICATE ? 1 : 0), 0),\n count_statement: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.STATEMENT ? 1 : 0), 0),\n count_nominal: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.NOMINAL ? 1 : 0), 0)\n };\n}\n\n/** Finds {@link Constituenta} by structure path. */\nexport function findCstByStructure(schema: RSForm, target: Constituenta, path: TypePath): Constituenta | null {\n for (const cst of schema.items) {\n if (cst.spawner === target.id && cst.spawner_path) {\n if (cst.spawner_path.length === path.length && cst.spawner_path.every((v, i) => v === path[i])) {\n return cst;\n }\n }\n }\n return null;\n}\n\n/** Retrieves name for piece of target {@link Constituenta} structure. */\nexport function getStructureName(schema: RSForm, target: Constituenta, path: TypePath): string {\n const representation = findCstByStructure(schema, target, path);\n if (representation) {\n return `${representation.alias}: ${representation.term_resolved}`;\n }\n if (!isTypification(target.effectiveType)) {\n return '';\n }\n const type = applyPath(target.effectiveType as Typification, path);\n if (type?.typeID === TypeID.basic) {\n const cst = schema.cstByAlias.get(type.baseID);\n if (cst?.term_resolved) {\n return `${cst.alias}: ${cst.term_resolved}`;\n }\n }\n return '';\n}\n\n// ========= Internals =====\nfunction calculateStepComplexity(cst: Constituenta): number {\n if (cst.cst_type === CstType.AXIOM || cst.cst_type === CstType.NOMINAL || !isBasicConcept(cst.cst_type)) {\n return 0;\n }\n if (cst.cst_type === CstType.BASE || cst.cst_type === CstType.CONSTANT || !cst.effectiveType) {\n return 1;\n }\n\n const type = cst.effectiveType as Typification;\n return calculateTypificationComplexity(type) + 1;\n}\n\nfunction calculateTypificationComplexity(type: Typification): number {\n switch (type.typeID) {\n case TypeID.basic:\n case TypeID.integer:\n case TypeID.anyTypification:\n return 0;\n case TypeID.tuple:\n return (\n type.factors.length + type.factors.reduce((sum, factor) => sum + calculateTypificationComplexity(factor), 0)\n );\n case TypeID.collection:\n if (type.base.typeID === TypeID.tuple) {\n let sum = 0;\n type.base.factors.forEach(factor => {\n sum += calculateTypificationComplexity(factor);\n });\n return sum + type.base.factors.length;\n } else if (type.base.typeID === TypeID.collection) {\n return calculateTypificationComplexity(type.base) + 1;\n }\n return 0;\n }\n}\n\nfunction isMissingConvention(cst: Constituenta): boolean {\n if (isBasicConcept(cst.cst_type) && !isLogical(cst.cst_type)) {\n if (!cst.convention || !cst.term_resolved) {\n return true;\n }\n }\n return false;\n}\n\n/** Evaluate if {@link Typification} can be used to produce structure. */\nfunction typeCanProduceStructure(type: Typification): boolean {\n if (type.typeID === TypeID.basic || type.typeID === TypeID.integer || type.typeID === TypeID.anyTypification) {\n return false;\n } else if (type.typeID === TypeID.tuple) {\n return true;\n } else {\n return (\n type.base.typeID !== TypeID.basic &&\n type.base.typeID !== TypeID.integer &&\n type.base.typeID !== TypeID.anyTypification\n );\n }\n}\n","/**\n * Module: Compact diagnostics for constituents (schema and model issue codes).\n */\n\nimport { labelType } from '../rslang/labels';\n\nimport { type Constituenta, type CstType } from './rsform';\nimport { isBasicConcept, isLogical } from './rsform-api';\nimport { EvalStatus } from './rsmodel';\n\n/** Origin of a diagnostic. */\nexport const DiagnosticKind = {\n EXPRESSION: 'expression',\n SCHEMA: 'schema',\n MODEL: 'model'\n} as const;\nexport type DiagnosticKind = (typeof DiagnosticKind)[keyof typeof DiagnosticKind];\n\n/** Schema and model diagnostic codes (same numeric format as {@link RSErrorCode}). */\nexport const RSDiagnosticCode = {\n schemaHomonym: 0x8901,\n schemaFormalDuplicate: 0x8902,\n schemaMissingConvention: 0x8903,\n schemaMissingTerm: 0x8904,\n schemaTypeMismatch: 0x8905,\n\n modelEmpty: 0x8910,\n modelAxiomFalse: 0x8911,\n modelInvalidData: 0x8912,\n modelEvalFail: 0x8913\n} as const;\nexport type RSDiagnosticCode = (typeof RSDiagnosticCode)[keyof typeof RSDiagnosticCode];\n\n/** Compact diagnostic stored on {@link Constituenta} after schema loading. */\nexport interface CstDiagnostic {\n kind: DiagnosticKind;\n code: number;\n /** Positional message arguments (per-code; homonyms/duplicates use `[aliases]`). */\n params?: readonly string[];\n}\n\n/** Whether a constituent has a schema/model diagnostic with the given code. */\nexport function hasCstDiagnostic(cst: Constituenta, code: RSDiagnosticCode): boolean {\n return cst.diagnostics?.some(item => item.code === code) ?? false;\n}\n\n/**\n * Populate `diagnostics` on each constituent (homonyms, duplicates, metadata, type mismatch).\n *\n * Requires normalized formal definitions for duplicate detection (same as Portal loader).\n */\nexport function assignSchemaDiagnostics(\n items: Constituenta[],\n resolveAlias: (id: number) => string,\n normalizedDefinitions: ReadonlyMap<number, string>\n): void {\n const homonymById = detectHomonymDiagnostics(items, resolveAlias);\n const duplicateById = detectFormalDuplicateDiagnostics(items, resolveAlias, normalizedDefinitions);\n\n for (const cst of items) {\n const diagnostics: CstDiagnostic[] = [];\n const homonym = homonymById.get(cst.id);\n if (homonym) {\n diagnostics.push(homonym);\n }\n const duplicate = duplicateById.get(cst.id);\n if (duplicate) {\n diagnostics.push(duplicate);\n }\n diagnostics.push(...collectLocalSchemaDiagnostics(cst));\n cst.diagnostics = diagnostics;\n }\n}\n\n/** Map a model evaluation status to a compact diagnostic, or `null` when not an issue. */\nexport function modelStatusCstDiagnostic(status: EvalStatus, cstType: CstType): CstDiagnostic | null {\n const code = modelStatusToCode(status, cstType);\n if (code === null) {\n return null;\n }\n return { kind: DiagnosticKind.MODEL, code, params: [String(status)] };\n}\n\nfunction collectLocalSchemaDiagnostics(cst: Constituenta): CstDiagnostic[] {\n const result: CstDiagnostic[] = [];\n if (cst.is_type_mismatch) {\n result.push({\n kind: DiagnosticKind.SCHEMA,\n code: RSDiagnosticCode.schemaTypeMismatch,\n params: [cst.typification_manual, labelType(cst.analysis.type)]\n });\n }\n if (isBasicConcept(cst.cst_type) && !isLogical(cst.cst_type)) {\n if (!cst.convention.trim()) {\n result.push({ kind: DiagnosticKind.SCHEMA, code: RSDiagnosticCode.schemaMissingConvention });\n }\n if (!cst.term_resolved.trim()) {\n result.push({ kind: DiagnosticKind.SCHEMA, code: RSDiagnosticCode.schemaMissingTerm });\n }\n }\n return result;\n}\n\nfunction detectGroupedAliasDiagnostics(\n items: Constituenta[],\n resolveAlias: (id: number) => string,\n groupKey: (cst: Constituenta) => string | undefined,\n code: RSDiagnosticCode\n): Map<number, CstDiagnostic> {\n const groups = new Map<string, Constituenta[]>();\n for (const cst of items) {\n const key = groupKey(cst);\n if (!key) {\n continue;\n }\n const group = groups.get(key) ?? [];\n group.push(cst);\n groups.set(key, group);\n }\n\n const result = new Map<number, CstDiagnostic>();\n for (const group of groups.values()) {\n if (group.length <= 1) {\n continue;\n }\n for (const cst of group) {\n const aliases = group\n .filter(other => other.id !== cst.id)\n .map(other => resolveAlias(other.id))\n .join(', ');\n result.set(cst.id, {\n kind: DiagnosticKind.SCHEMA,\n code,\n params: [aliases]\n });\n }\n }\n return result;\n}\n\nfunction detectHomonymDiagnostics(\n items: Constituenta[],\n resolveAlias: (id: number) => string\n): Map<number, CstDiagnostic> {\n return detectGroupedAliasDiagnostics(\n items,\n resolveAlias,\n cst => {\n const key = cst.term_resolved.trim().toLocaleLowerCase();\n return key === '' ? undefined : key;\n },\n RSDiagnosticCode.schemaHomonym\n );\n}\n\nfunction detectFormalDuplicateDiagnostics(\n items: Constituenta[],\n resolveAlias: (id: number) => string,\n normalizedDefinitions: ReadonlyMap<number, string>\n): Map<number, CstDiagnostic> {\n return detectGroupedAliasDiagnostics(\n items,\n resolveAlias,\n cst => normalizedDefinitions.get(cst.id),\n RSDiagnosticCode.schemaFormalDuplicate\n );\n}\n\nfunction modelStatusToCode(status: EvalStatus, cstType: CstType): RSDiagnosticCode | null {\n switch (status) {\n case EvalStatus.EMPTY:\n return isBasicConcept(cstType) ? RSDiagnosticCode.modelEmpty : null;\n case EvalStatus.AXIOM_FALSE:\n return RSDiagnosticCode.modelAxiomFalse;\n case EvalStatus.INVALID_DATA:\n return RSDiagnosticCode.modelInvalidData;\n case EvalStatus.EVAL_FAIL:\n return RSDiagnosticCode.modelEvalFail;\n default:\n return null;\n }\n}\n"],"mappings":"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"}