@rsconcept/domain 3.0.0 → 3.0.2
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/{analyzer-fNhkwO-6.d.ts → analyzer-CzFaWR7w.d.ts} +4 -4
- package/dist/{api-Bf0f_uiI.d.ts → api-DgecYHjR.d.ts} +5 -3
- package/dist/{arguments-extractor-ngatANBI.d.ts → arguments-extractor-CXWRT5Ou.d.ts} +4 -4
- package/dist/{ast-C8sIpKdL.d.ts → ast-D-D6Rq3T.d.ts} +2 -3
- package/dist/{ast-annotations-CiIx8qBM.d.ts → ast-annotations-x4PJ_Lhc.d.ts} +4 -4
- package/dist/{calculator-DE89LtIM.d.ts → calculator-uPaTV3hc.d.ts} +5 -5
- package/dist/cctext/language-api.d.ts +0 -1
- package/dist/{diagnostics-ndGYbvgh.js → diagnostics-D1Cp21nP.js} +16 -3
- package/dist/{diagnostics-ndGYbvgh.js.map → diagnostics-D1Cp21nP.js.map} +1 -1
- package/dist/{diagnostics-DsEhmFh0.d.ts → diagnostics-DKyMf6q-.d.ts} +20 -6
- package/dist/{expression-generator-CoucLIkE.d.ts → expression-generator-CADzBw-C.d.ts} +3 -2
- package/dist/graph/index.d.ts +1 -0
- package/dist/index-CnuO4w8e.d.ts +13 -0
- package/dist/index-D5RahF1K.d.ts +2 -0
- package/dist/{index-DpiTybDB.d.ts → index-DCO4mxiO.d.ts} +9 -7
- package/dist/index-DntDUXW1.d.ts +1 -0
- package/dist/index-x0_Y5uPV.d.ts +2 -0
- package/dist/index.d.ts +13 -13
- package/dist/index.js +1 -1
- package/dist/{lezer-tree-iS7LpLBJ.d.ts → lezer-tree-BQZqBT5A.d.ts} +1 -2
- package/dist/library/diagnostics.d.ts +1 -1
- package/dist/library/diagnostics.js +1 -1
- package/dist/library/index.d.ts +2 -3
- package/dist/library/index.js +1 -1
- package/dist/library/oss-api.d.ts +11 -3
- package/dist/library/oss-api.js +45 -4
- package/dist/library/oss-api.js.map +1 -1
- package/dist/library/oss-layout-api.d.ts +0 -1
- package/dist/library/oss.d.ts +21 -16
- package/dist/library/oss.js +19 -13
- package/dist/library/oss.js.map +1 -1
- package/dist/library/rsengine.d.ts +6 -5
- package/dist/library/rsengine.js +1 -1
- package/dist/library/rsform-api/index.d.ts +1 -1
- package/dist/library/rsform-api/index.js +1 -1
- package/dist/library/rsform.d.ts +1 -1
- package/dist/library/rsmodel-api.d.ts +5 -5
- package/dist/library/rsmodel-api.js +3 -3
- package/dist/library/rsmodel-api.js.map +1 -1
- package/dist/library/rsmodel.d.ts +1 -1
- package/dist/library/structure-planner.d.ts +3 -3
- package/dist/library/structure-planner.js +1 -1
- package/dist/{parser-Bwd8LxJ1.d.ts → parser-DkejtiOR.d.ts} +1 -2
- package/dist/parsing/ast.d.ts +1 -1
- package/dist/parsing/index.d.ts +3 -2
- package/dist/parsing/lezer-tree.d.ts +1 -1
- package/dist/rslang/api.d.ts +2 -2
- package/dist/rslang/ast-annotations.d.ts +1 -1
- package/dist/rslang/eval/calculator.d.ts +1 -1
- package/dist/rslang/eval/evaluation-cache.d.ts +3 -3
- package/dist/rslang/eval/evaluator.d.ts +3 -3
- package/dist/rslang/eval/value-api.d.ts +1 -1
- package/dist/rslang/eval/value.d.ts +1 -1
- package/dist/rslang/index.d.ts +11 -10
- package/dist/rslang/labels.d.ts +3 -3
- package/dist/rslang/parser/expression-generator.d.ts +1 -1
- package/dist/rslang/parser/normalize.d.ts +2 -1
- package/dist/rslang/parser/parser.d.ts +1 -1
- package/dist/rslang/parser/parser.terms.d.ts +1 -40
- package/dist/rslang/parser/syntax-errors/index.d.ts +3 -3
- package/dist/rslang/semantic/analyzer.d.ts +1 -1
- package/dist/rslang/semantic/arguments-extractor.d.ts +1 -1
- package/dist/rslang/semantic/type-auditor.d.ts +3 -3
- package/dist/rslang/semantic/typification-api.d.ts +1 -2
- package/dist/rslang/semantic/typification-parser.d.ts +1 -1
- package/dist/rslang/semantic/typification.d.ts +1 -1
- package/dist/rslang/semantic/value-auditor.d.ts +2 -2
- package/dist/rslang/typification-graph.d.ts +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/{typification-graph-BY_oGT82.d.ts → typification-graph-CSxDt18X.d.ts} +2 -3
- package/dist/{typification-9lNu9R4k.d.ts → typification-hWUoWCba.d.ts} +2 -1
- package/dist/{typification-parser-Dytsv9CI.d.ts → typification-parser-DFxj4JiQ.d.ts} +2 -3
- package/dist/{value-BduA_ZGp.d.ts → value-XpJOVzMP.d.ts} +2 -1
- package/dist/{value-api-CXLl_zg0.d.ts → value-api-Dw8UkwCD.d.ts} +3 -4
- package/package.json +20 -21
- package/src/library/diagnostics.ts +16 -2
- package/src/library/oss-api.test.ts +100 -5
- package/src/library/oss-api.ts +71 -5
- package/src/library/oss.ts +22 -16
- package/src/library/rsmodel-api.test.ts +79 -0
- package/src/library/rsmodel-api.ts +3 -2
- package/dist/index-BKZ67WMa.d.ts +0 -1
- package/dist/index-BVVgDSdq.d.ts +0 -1
- package/dist/index-DmtQKWjk.d.ts +0 -1
- package/dist/index-_6s0AX1B.d.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rsconcept/domain",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Shared TypeScript domain for RS language, RSForm, RSModel, RSEngine, OSS, and concept-text helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "IRBorisov",
|
|
@@ -154,37 +154,36 @@
|
|
|
154
154
|
"README.md",
|
|
155
155
|
"LICENSE"
|
|
156
156
|
],
|
|
157
|
-
"scripts": {
|
|
158
|
-
"generate": "lezer-generator src/rslang/parser/rslang.grammar -o src/rslang/parser/parser.ts",
|
|
159
|
-
"build": "tsdown",
|
|
160
|
-
"dev": "tsdown --watch",
|
|
161
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
162
|
-
"test": "vitest run",
|
|
163
|
-
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
|
164
|
-
"lintFix": "eslint . --report-unused-disable-directives --max-warnings 0 --fix",
|
|
165
|
-
"format": "prettier --write \"src/**/*.ts\" \"*.{ts,js}\"",
|
|
166
|
-
"format:check": "prettier --check \"src/**/*.ts\" \"*.{ts,js}\"",
|
|
167
|
-
"prepublishOnly": "pnpm run build"
|
|
168
|
-
},
|
|
169
157
|
"dependencies": {
|
|
170
158
|
"@lezer/common": "^1.5.2",
|
|
171
159
|
"@lezer/lr": "^1.4.10"
|
|
172
160
|
},
|
|
173
161
|
"devDependencies": {
|
|
174
162
|
"@lezer/generator": "^1.8.0",
|
|
175
|
-
"@types/node": "^26.1.
|
|
176
|
-
"@typescript-eslint/parser": "^8.0
|
|
177
|
-
"eslint": "^9.39.
|
|
163
|
+
"@types/node": "^26.1.1",
|
|
164
|
+
"@typescript-eslint/parser": "^8.63.0",
|
|
165
|
+
"eslint": "^9.39.5",
|
|
178
166
|
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
179
167
|
"globals": "^17.7.0",
|
|
180
|
-
"prettier": "^3.9.
|
|
181
|
-
"tsdown": "^0.22.
|
|
168
|
+
"prettier": "^3.9.5",
|
|
169
|
+
"tsdown": "^0.22.4",
|
|
182
170
|
"typescript": "^6.0.3",
|
|
183
|
-
"typescript-eslint": "^8.
|
|
171
|
+
"typescript-eslint": "^8.63.0",
|
|
184
172
|
"unrun": "^0.3.1",
|
|
185
|
-
"vitest": "^4.1.
|
|
173
|
+
"vitest": "^4.1.10"
|
|
186
174
|
},
|
|
187
175
|
"publishConfig": {
|
|
188
176
|
"access": "public"
|
|
177
|
+
},
|
|
178
|
+
"scripts": {
|
|
179
|
+
"generate": "lezer-generator src/rslang/parser/rslang.grammar -o src/rslang/parser/parser.ts",
|
|
180
|
+
"build": "tsdown",
|
|
181
|
+
"dev": "tsdown --watch",
|
|
182
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
183
|
+
"test": "vitest run",
|
|
184
|
+
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
|
185
|
+
"lintFix": "eslint . --report-unused-disable-directives --max-warnings 0 --fix",
|
|
186
|
+
"format": "prettier --write \"src/**/*.ts\" \"*.{ts,js}\"",
|
|
187
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"*.{ts,js}\""
|
|
189
188
|
}
|
|
190
|
-
}
|
|
189
|
+
}
|
|
@@ -19,7 +19,7 @@ export const DiagnosticKind = {
|
|
|
19
19
|
} as const;
|
|
20
20
|
export type DiagnosticKind = (typeof DiagnosticKind)[keyof typeof DiagnosticKind];
|
|
21
21
|
|
|
22
|
-
/** Schema and
|
|
22
|
+
/** Schema, model, and substitution-table diagnostic codes (same numeric format as {@link RSErrorCode}). */
|
|
23
23
|
export const RSDiagnosticCode = {
|
|
24
24
|
schemaHomonym: 0x8901,
|
|
25
25
|
schemaFormalDuplicate: 0x8902,
|
|
@@ -31,7 +31,21 @@ export const RSDiagnosticCode = {
|
|
|
31
31
|
modelEmpty: 0x8910,
|
|
32
32
|
modelAxiomFalse: 0x8911,
|
|
33
33
|
modelInvalidData: 0x8912,
|
|
34
|
-
modelEvalFail: 0x8913
|
|
34
|
+
modelEvalFail: 0x8913,
|
|
35
|
+
|
|
36
|
+
substitutionInvalidIDs: 0x8920,
|
|
37
|
+
substitutionIncorrectCst: 0x8921,
|
|
38
|
+
substitutionInvalidClasses: 0x8922,
|
|
39
|
+
substitutionInvalidBasic: 0x8923,
|
|
40
|
+
substitutionInvalidConstant: 0x8924,
|
|
41
|
+
substitutionTypificationCycle: 0x8925,
|
|
42
|
+
substitutionBaseNotSet: 0x8926,
|
|
43
|
+
substitutionUnequalTypification: 0x8927,
|
|
44
|
+
/** Non-blocking warning: formal definitions differ after mapping. */
|
|
45
|
+
substitutionUnequalExpressions: 0x2928,
|
|
46
|
+
substitutionUnequalArgsCount: 0x8929,
|
|
47
|
+
substitutionUnequalArgs: 0x892a,
|
|
48
|
+
substitutionInvalidNominal: 0x892b
|
|
35
49
|
} as const;
|
|
36
50
|
export type RSDiagnosticCode = (typeof RSDiagnosticCode)[keyof typeof RSDiagnosticCode];
|
|
37
51
|
|
|
@@ -2,14 +2,16 @@ import { describe, expect, it } from 'vitest';
|
|
|
2
2
|
|
|
3
3
|
import { Graph } from '../graph';
|
|
4
4
|
|
|
5
|
+
import { SubstitutionErrorType } from './oss';
|
|
5
6
|
import { SubstitutionValidator } from './oss-api';
|
|
6
7
|
import { type Constituenta, CstClass, CstType, type RSForm } from './rsform';
|
|
7
8
|
|
|
8
9
|
function mockConstituenta(
|
|
9
|
-
partial: Pick<Constituenta, 'id' | 'alias' | 'definition_formal' | 'cst_class' | 'cst_type'>
|
|
10
|
+
partial: Pick<Constituenta, 'id' | 'alias' | 'definition_formal' | 'cst_class' | 'cst_type'> &
|
|
11
|
+
Partial<Pick<Constituenta, 'schema'>>
|
|
10
12
|
): Constituenta {
|
|
11
13
|
return {
|
|
12
|
-
schema: 790,
|
|
14
|
+
schema: partial.schema ?? 790,
|
|
13
15
|
crucial: false,
|
|
14
16
|
convention: '',
|
|
15
17
|
definition_raw: '',
|
|
@@ -37,11 +39,16 @@ function mockConstituenta(
|
|
|
37
39
|
};
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
function mockSchema(items: Constituenta[]): RSForm {
|
|
42
|
+
function mockSchema(items: Constituenta[], edges: number[][] = [], id = 790, alias = 'S1'): RSForm {
|
|
41
43
|
const graph = new Graph(items.map(item => [item.id]));
|
|
44
|
+
for (const edge of edges) {
|
|
45
|
+
if (edge.length === 2) {
|
|
46
|
+
graph.addEdge(edge[0], edge[1]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
42
49
|
return {
|
|
43
|
-
id
|
|
44
|
-
alias
|
|
50
|
+
id,
|
|
51
|
+
alias,
|
|
45
52
|
items,
|
|
46
53
|
graph,
|
|
47
54
|
cstByAlias: new Map(items.map(item => [item.alias, item]))
|
|
@@ -72,4 +79,92 @@ describe('SubstitutionValidator', () => {
|
|
|
72
79
|
expect(() => validator.validate()).not.toThrow();
|
|
73
80
|
expect(validator.validate()).toBe(true);
|
|
74
81
|
});
|
|
82
|
+
|
|
83
|
+
it('reports definitionCycle when identification closes a formal dependency loop', () => {
|
|
84
|
+
// D16 := F2[D15] ⇒ edge D15 → D16; identifying D15 → D16 yields D16 := F2[D16]
|
|
85
|
+
const schema = mockSchema(
|
|
86
|
+
[
|
|
87
|
+
mockConstituenta({
|
|
88
|
+
id: 1,
|
|
89
|
+
alias: 'X1',
|
|
90
|
+
cst_class: CstClass.BASIC,
|
|
91
|
+
cst_type: CstType.BASE,
|
|
92
|
+
definition_formal: ''
|
|
93
|
+
}),
|
|
94
|
+
mockConstituenta({
|
|
95
|
+
id: 2,
|
|
96
|
+
alias: 'F2',
|
|
97
|
+
cst_class: CstClass.DERIVED,
|
|
98
|
+
cst_type: CstType.FUNCTION,
|
|
99
|
+
definition_formal: '[α∈X1] α'
|
|
100
|
+
}),
|
|
101
|
+
mockConstituenta({
|
|
102
|
+
id: 3,
|
|
103
|
+
alias: 'D15',
|
|
104
|
+
cst_class: CstClass.DERIVED,
|
|
105
|
+
cst_type: CstType.TERM,
|
|
106
|
+
definition_formal: 'X1'
|
|
107
|
+
}),
|
|
108
|
+
mockConstituenta({
|
|
109
|
+
id: 4,
|
|
110
|
+
alias: 'D16',
|
|
111
|
+
cst_class: CstClass.DERIVED,
|
|
112
|
+
cst_type: CstType.TERM,
|
|
113
|
+
definition_formal: 'F2[D15]'
|
|
114
|
+
})
|
|
115
|
+
],
|
|
116
|
+
[
|
|
117
|
+
[2, 4],
|
|
118
|
+
[3, 4]
|
|
119
|
+
]
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
const validator = new SubstitutionValidator([schema], [{ original: 3, substitution: 4 }]);
|
|
123
|
+
|
|
124
|
+
expect(validator.validate()).toBe(false);
|
|
125
|
+
expect(validator.errors).toEqual([
|
|
126
|
+
{
|
|
127
|
+
code: SubstitutionErrorType.definitionCycle,
|
|
128
|
+
params: ['D16 → D16']
|
|
129
|
+
}
|
|
130
|
+
]);
|
|
131
|
+
expect(SubstitutionErrorType.definitionCycle).toBe(0x8906);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('allows identification that does not create a definition cycle', () => {
|
|
135
|
+
const schema = mockSchema(
|
|
136
|
+
[
|
|
137
|
+
mockConstituenta({
|
|
138
|
+
id: 1,
|
|
139
|
+
alias: 'X1',
|
|
140
|
+
cst_class: CstClass.BASIC,
|
|
141
|
+
cst_type: CstType.BASE,
|
|
142
|
+
definition_formal: ''
|
|
143
|
+
}),
|
|
144
|
+
mockConstituenta({
|
|
145
|
+
id: 2,
|
|
146
|
+
alias: 'D1',
|
|
147
|
+
cst_class: CstClass.DERIVED,
|
|
148
|
+
cst_type: CstType.TERM,
|
|
149
|
+
definition_formal: 'X1'
|
|
150
|
+
}),
|
|
151
|
+
mockConstituenta({
|
|
152
|
+
id: 3,
|
|
153
|
+
alias: 'D2',
|
|
154
|
+
cst_class: CstClass.DERIVED,
|
|
155
|
+
cst_type: CstType.TERM,
|
|
156
|
+
definition_formal: 'X1'
|
|
157
|
+
})
|
|
158
|
+
],
|
|
159
|
+
[
|
|
160
|
+
[1, 2],
|
|
161
|
+
[1, 3]
|
|
162
|
+
]
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const validator = new SubstitutionValidator([schema], [{ original: 2, substitution: 3 }]);
|
|
166
|
+
|
|
167
|
+
expect(validator.validate()).toBe(true);
|
|
168
|
+
expect(validator.errors.every(error => error.code !== SubstitutionErrorType.definitionCycle)).toBe(true);
|
|
169
|
+
});
|
|
75
170
|
});
|
package/src/library/oss-api.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { type AliasMapping, applyAliasMapping, applyTypificationMapping, isSetTy
|
|
|
7
7
|
import { labelType } from '../rslang/labels';
|
|
8
8
|
import { extractBases } from '../rslang/semantic/typification-api';
|
|
9
9
|
|
|
10
|
+
import { formatDependencyCycle } from './diagnostics';
|
|
10
11
|
import { type LibraryItem } from './library';
|
|
11
12
|
import { NodeType, type OperationSchema, type SubstitutionErrorDescription, SubstitutionErrorType } from './oss';
|
|
12
13
|
import { type Constituenta, CstClass, CstType, type RSForm, type Substitution } from './rsform';
|
|
@@ -101,7 +102,10 @@ export class SubstitutionValidator {
|
|
|
101
102
|
if (!this.checkTypes()) {
|
|
102
103
|
return false;
|
|
103
104
|
}
|
|
104
|
-
if (!this.
|
|
105
|
+
if (!this.checkTypificationCycles()) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (!this.checkDefinitionCycles()) {
|
|
105
109
|
return false;
|
|
106
110
|
}
|
|
107
111
|
if (!this.checkSubstitutions()) {
|
|
@@ -238,7 +242,11 @@ export class SubstitutionValidator {
|
|
|
238
242
|
return true;
|
|
239
243
|
}
|
|
240
244
|
|
|
241
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Detect cycles among base/constant sets implied by typifications of non-base
|
|
247
|
+
* constituents under the substitution table.
|
|
248
|
+
*/
|
|
249
|
+
private checkTypificationCycles(): boolean {
|
|
242
250
|
const graph = new Graph();
|
|
243
251
|
for (const schema of this.schemas) {
|
|
244
252
|
for (const cst of schema.items) {
|
|
@@ -286,6 +294,64 @@ export class SubstitutionValidator {
|
|
|
286
294
|
return true;
|
|
287
295
|
}
|
|
288
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Detect logical cycles in formal definitions after applying the substitution table
|
|
299
|
+
* (e.g. D16 := F2[D15] with identification D15 → D16 yields D16 := F2[D16]).
|
|
300
|
+
*/
|
|
301
|
+
private checkDefinitionCycles(): boolean {
|
|
302
|
+
const graph = new Graph();
|
|
303
|
+
for (const schema of this.schemas) {
|
|
304
|
+
for (const [id, node] of schema.graph.nodes) {
|
|
305
|
+
graph.addNode(id);
|
|
306
|
+
for (const output of node.outputs) {
|
|
307
|
+
graph.addEdge(id, output);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const deleted = new Map<number, number>();
|
|
313
|
+
for (const item of this.substitutions) {
|
|
314
|
+
deleted.set(item.original, item.substitution);
|
|
315
|
+
}
|
|
316
|
+
const resolve = (id: number): number => deleted.get(id) ?? id;
|
|
317
|
+
|
|
318
|
+
const result = new Graph();
|
|
319
|
+
for (const id of graph.nodes.keys()) {
|
|
320
|
+
if (!deleted.has(id)) {
|
|
321
|
+
result.addNode(id);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const substitutionId of deleted.values()) {
|
|
325
|
+
result.addNode(substitutionId);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
for (const [id, node] of graph.nodes) {
|
|
329
|
+
for (const output of node.outputs) {
|
|
330
|
+
if (deleted.has(output)) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
result.addEdge(resolve(id), output);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const cycle = result.findCycle();
|
|
338
|
+
if (cycle !== null) {
|
|
339
|
+
const aliases = cycle.map(id => {
|
|
340
|
+
const cst = this.cstByID.get(id);
|
|
341
|
+
if (!cst) {
|
|
342
|
+
return String(id);
|
|
343
|
+
}
|
|
344
|
+
if (this.schemas.length > 1) {
|
|
345
|
+
const schema = this.schemaByID.get(cst.schema);
|
|
346
|
+
return schema ? `[${schema.alias}]-${cst.alias}` : cst.alias;
|
|
347
|
+
}
|
|
348
|
+
return cst.alias;
|
|
349
|
+
});
|
|
350
|
+
return this.reportError(SubstitutionErrorType.definitionCycle, [formatDependencyCycle(aliases)]);
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
289
355
|
private checkSubstitutions(): boolean {
|
|
290
356
|
const baseMappings = this.prepareBaseMappings();
|
|
291
357
|
const typeMappings = this.calculateSubstituteMappings(baseMappings);
|
|
@@ -407,10 +473,10 @@ export class SubstitutionValidator {
|
|
|
407
473
|
return expression1.replace(/\s+/g, '') === expression2.replace(/\s+/g, '');
|
|
408
474
|
}
|
|
409
475
|
|
|
410
|
-
private reportError(
|
|
476
|
+
private reportError(code: SubstitutionErrorType, params: string[]): boolean {
|
|
411
477
|
this.errors.push({
|
|
412
|
-
|
|
413
|
-
params
|
|
478
|
+
code,
|
|
479
|
+
params
|
|
414
480
|
});
|
|
415
481
|
return false;
|
|
416
482
|
}
|
package/src/library/oss.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Graph } from '../graph';
|
|
6
6
|
|
|
7
|
+
import { RSDiagnosticCode } from './diagnostics';
|
|
7
8
|
import { type LibraryItem } from './library';
|
|
8
9
|
import { type NodePosition, type OssLayout } from './oss-layout';
|
|
9
10
|
|
|
@@ -126,25 +127,30 @@ export interface OperationSchema extends LibraryItem {
|
|
|
126
127
|
itemByNodeID: Map<string, OssItem>;
|
|
127
128
|
}
|
|
128
129
|
|
|
129
|
-
/** Represents substitution error description. */
|
|
130
|
+
/** Represents substitution error description (same code space as {@link RSDiagnosticCode}). */
|
|
130
131
|
export interface SubstitutionErrorDescription {
|
|
131
|
-
|
|
132
|
+
code: SubstitutionErrorType;
|
|
132
133
|
params: string[];
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
/**
|
|
136
|
+
/**
|
|
137
|
+
* Substitution-table error codes — aliases into {@link RSDiagnosticCode}.
|
|
138
|
+
* Logical definition cycles reuse {@link RSDiagnosticCode.schemaDependencyCycle}.
|
|
139
|
+
*/
|
|
136
140
|
export const SubstitutionErrorType = {
|
|
137
|
-
invalidIDs:
|
|
138
|
-
incorrectCst:
|
|
139
|
-
invalidClasses:
|
|
140
|
-
invalidBasic:
|
|
141
|
-
invalidConstant:
|
|
142
|
-
typificationCycle:
|
|
143
|
-
baseSubstitutionNotSet:
|
|
144
|
-
unequalTypification:
|
|
145
|
-
unequalExpressions:
|
|
146
|
-
unequalArgsCount:
|
|
147
|
-
unequalArgs:
|
|
148
|
-
invalidNominal:
|
|
149
|
-
|
|
141
|
+
invalidIDs: RSDiagnosticCode.substitutionInvalidIDs,
|
|
142
|
+
incorrectCst: RSDiagnosticCode.substitutionIncorrectCst,
|
|
143
|
+
invalidClasses: RSDiagnosticCode.substitutionInvalidClasses,
|
|
144
|
+
invalidBasic: RSDiagnosticCode.substitutionInvalidBasic,
|
|
145
|
+
invalidConstant: RSDiagnosticCode.substitutionInvalidConstant,
|
|
146
|
+
typificationCycle: RSDiagnosticCode.substitutionTypificationCycle,
|
|
147
|
+
baseSubstitutionNotSet: RSDiagnosticCode.substitutionBaseNotSet,
|
|
148
|
+
unequalTypification: RSDiagnosticCode.substitutionUnequalTypification,
|
|
149
|
+
unequalExpressions: RSDiagnosticCode.substitutionUnequalExpressions,
|
|
150
|
+
unequalArgsCount: RSDiagnosticCode.substitutionUnequalArgsCount,
|
|
151
|
+
unequalArgs: RSDiagnosticCode.substitutionUnequalArgs,
|
|
152
|
+
invalidNominal: RSDiagnosticCode.substitutionInvalidNominal,
|
|
153
|
+
/** Same code as constituent schema dependency cycle. */
|
|
154
|
+
definitionCycle: RSDiagnosticCode.schemaDependencyCycle
|
|
155
|
+
} as const satisfies Record<string, (typeof RSDiagnosticCode)[keyof typeof RSDiagnosticCode]>;
|
|
150
156
|
export type SubstitutionErrorType = (typeof SubstitutionErrorType)[keyof typeof SubstitutionErrorType];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { type Value } from '../rslang';
|
|
4
|
+
import { compare, TUPLE_ID } from '../rslang/eval/value';
|
|
5
|
+
import { isTupleValue } from '../rslang/eval/value-api';
|
|
6
|
+
import { basic, bool, IntegerT, tuple } from '../rslang/semantic/typification';
|
|
7
|
+
|
|
8
|
+
import { type BasicBinding } from './rsmodel';
|
|
9
|
+
import { generateRandomValue } from './rsmodel-api';
|
|
10
|
+
|
|
11
|
+
function hasUniqueElements(value: Value[]): boolean {
|
|
12
|
+
const sorted = [...value].sort(compare);
|
|
13
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
14
|
+
if (compare(sorted[i - 1], sorted[i]) === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe('generateRandomValue', () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
vi.restoreAllMocks();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns unique elements for collections even when samples collide', () => {
|
|
27
|
+
const basics = new Map<number, BasicBinding>([[10, { 1: 'x1', 2: 'x2' }]]);
|
|
28
|
+
const cstByAlias = new Map([['X1', { id: 10 }]]);
|
|
29
|
+
const collectionType = bool(basic('X1'));
|
|
30
|
+
|
|
31
|
+
// Force: size=5, then always pick the same base element id=1.
|
|
32
|
+
const random = vi.spyOn(Math, 'random');
|
|
33
|
+
random
|
|
34
|
+
.mockReturnValueOnce(0.99) // -> 5 elements
|
|
35
|
+
.mockReturnValue(0); // -> always ids[0] === 1
|
|
36
|
+
|
|
37
|
+
const value = generateRandomValue(collectionType, basics, cstByAlias, 5);
|
|
38
|
+
expect(Array.isArray(value)).toBe(true);
|
|
39
|
+
expect(value).toEqual([1]);
|
|
40
|
+
expect(hasUniqueElements(value as Value[])).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('returns unique nested sets for tuple of collections ℬ(X1)×ℬ(C1)', () => {
|
|
44
|
+
const basics = new Map<number, BasicBinding>([
|
|
45
|
+
[10, { 1: 'x1', 2: 'x2' }],
|
|
46
|
+
[20, { 0: 'c0', 1: 'c1', 2: 'c2' }]
|
|
47
|
+
]);
|
|
48
|
+
const cstByAlias = new Map([
|
|
49
|
+
['X1', { id: 10 }],
|
|
50
|
+
['C1', { id: 20 }]
|
|
51
|
+
]);
|
|
52
|
+
const type = tuple([bool(basic('X1')), bool(basic('C1'))]);
|
|
53
|
+
|
|
54
|
+
for (let attempt = 0; attempt < 30; attempt++) {
|
|
55
|
+
const value = generateRandomValue(type, basics, cstByAlias, 10);
|
|
56
|
+
expect(isTupleValue(value)).toBe(true);
|
|
57
|
+
const tupleValue = value as Value[];
|
|
58
|
+
expect(tupleValue[0]).toBe(TUPLE_ID);
|
|
59
|
+
expect(Array.isArray(tupleValue[1])).toBe(true);
|
|
60
|
+
expect(Array.isArray(tupleValue[2])).toBe(true);
|
|
61
|
+
expect(hasUniqueElements(tupleValue[1] as Value[])).toBe(true);
|
|
62
|
+
expect(hasUniqueElements(tupleValue[2] as Value[])).toBe(true);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('returns null when a basic domain is empty', () => {
|
|
67
|
+
const basics = new Map<number, BasicBinding>();
|
|
68
|
+
const cstByAlias = new Map([['X1', { id: 10 }]]);
|
|
69
|
+
expect(generateRandomValue(basic('X1'), basics, cstByAlias)).toBeNull();
|
|
70
|
+
expect(generateRandomValue(bool(basic('X1')), basics, cstByAlias)).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('generates integers in range', () => {
|
|
74
|
+
const value = generateRandomValue(IntegerT, new Map(), new Map());
|
|
75
|
+
expect(typeof value).toBe('number');
|
|
76
|
+
expect(value as number).toBeGreaterThanOrEqual(-100);
|
|
77
|
+
expect(value as number).toBeLessThanOrEqual(100);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ExpressionType, TypeID, type Typification, type Value } from '../rslang';
|
|
2
|
-
import { compare, tuple, TUPLE_ID, VALUE_TRUE, type ValuePath } from '../rslang/eval/value';
|
|
2
|
+
import { compare, set, tuple, TUPLE_ID, VALUE_TRUE, type ValuePath } from '../rslang/eval/value';
|
|
3
3
|
import { extractValue, isTupleValue, makeDefaultValue, setNestedValue } from '../rslang/eval/value-api';
|
|
4
4
|
import { type EchelonCollection } from '../rslang/semantic/typification';
|
|
5
5
|
|
|
@@ -80,7 +80,8 @@ export function generateRandomValue(
|
|
|
80
80
|
}
|
|
81
81
|
result.push(sample);
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
// Sets must be unique; sampling with replacement can produce duplicates.
|
|
84
|
+
return set(result);
|
|
84
85
|
}
|
|
85
86
|
case TypeID.anyTypification:
|
|
86
87
|
return null;
|
package/dist/index-BKZ67WMa.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
package/dist/index-BVVgDSdq.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
package/dist/index-DmtQKWjk.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
package/dist/index-_6s0AX1B.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|