@rsconcept/domain 1.7.7 → 1.8.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.
@@ -0,0 +1,308 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { type ArgumentValue, type Constituenta, CstClass, CstType, type RSForm } from './rsform';
4
+ import { planTemplateInstantiation } from './template-instantiation';
5
+
6
+ function mockSchema(items: Constituenta[]): RSForm {
7
+ return { items } as RSForm;
8
+ }
9
+
10
+ function mockCst(partial: Pick<Constituenta, 'id' | 'alias' | 'cst_type'> & Partial<Constituenta>): Constituenta {
11
+ return {
12
+ schema: 1,
13
+ crucial: false,
14
+ convention: '',
15
+ definition_raw: '',
16
+ definition_resolved: '',
17
+ term_raw: '',
18
+ term_resolved: '',
19
+ term_forms: [],
20
+ typification_manual: '',
21
+ value_is_property: false,
22
+ homonyms: [],
23
+ formalDuplicates: [],
24
+ analysis: { success: true } as Constituenta['analysis'],
25
+ effectiveType: null,
26
+ is_type_mismatch: false,
27
+ status: 'verified',
28
+ is_template: true,
29
+ is_simple_expression: true,
30
+ parent_schema_index: 0,
31
+ parent_schema: null,
32
+ is_inherited: false,
33
+ has_inherited_children: false,
34
+ attributes: [],
35
+ spawn: [],
36
+ spawn_alias: [],
37
+ cst_class: CstClass.TEMPLATE,
38
+ definition_formal: '',
39
+ ...partial
40
+ };
41
+ }
42
+
43
+ describe('planTemplateInstantiation', () => {
44
+ const f42 = mockCst({
45
+ id: 1,
46
+ alias: 'F42',
47
+ cst_type: CstType.FUNCTION,
48
+ term_raw: 'операция',
49
+ definition_formal: '[α∈R1, β∈R1, σ∈ℬ((R1×R1)×R1)] debool(Pr2(Fi1[{(α,β)}](σ)))'
50
+ });
51
+
52
+ const p29 = mockCst({
53
+ id: 2,
54
+ alias: 'P29',
55
+ cst_type: CstType.PREDICATE,
56
+ term_raw: 'замкнутость',
57
+ definition_formal: '[α∈ℬ(R1), σ∈ℬ((R1×R1)×R1)] Pr1(σ) = α×α & card(σ) = card(Pr1(σ))'
58
+ });
59
+
60
+ const p30 = mockCst({
61
+ id: 3,
62
+ alias: 'P30',
63
+ cst_type: CstType.PREDICATE,
64
+ term_raw: 'ассоциативность',
65
+ definition_formal: '[σ∈ℬ((R1×R1)×R1)] ∀α,β,γ∈Pr1(Pr1(σ)) F42[F42[α,β,σ],γ,σ] = F42[α,F42[β,γ,σ],σ]'
66
+ });
67
+
68
+ const p35 = mockCst({
69
+ id: 4,
70
+ alias: 'P35',
71
+ cst_type: CstType.PREDICATE,
72
+ term_raw: 'полугруппа',
73
+ definition_formal: '[α∈ℬ(R1), σ∈ℬ((R1×R1)×R1)] P29[α, σ] & P30[σ]'
74
+ });
75
+
76
+ const templateItems = [f42, p29, p30, p35];
77
+ const targetSchema = mockSchema([
78
+ mockCst({ id: 10, alias: 'S1', cst_type: CstType.STRUCTURED, definition_formal: 'ℬ(X1)' }),
79
+ mockCst({ id: 11, alias: 'S2', cst_type: CstType.STRUCTURED, definition_formal: 'ℬ((X1×X1)×X1)' })
80
+ ]);
81
+
82
+ it('creates nested template functions with propagated argument bindings', () => {
83
+ const userArgs: ArgumentValue[] = [
84
+ { alias: 'α', typification: 'ℬ(R1)', value: 'S1' },
85
+ { alias: 'σ', typification: 'ℬ((R1×R1)×R1)', value: 'S2' }
86
+ ];
87
+
88
+ const plan = planTemplateInstantiation({
89
+ targetSchema,
90
+ templateItems,
91
+ prototype: p35,
92
+ userArgs,
93
+ mainItem: {
94
+ alias: 'A1',
95
+ cst_type: CstType.PREDICATE,
96
+ crucial: false,
97
+ convention: '',
98
+ definition_formal: p35.definition_formal,
99
+ definition_raw: p35.definition_raw,
100
+ term_raw: p35.term_raw,
101
+ term_forms: [],
102
+ typification_manual: '',
103
+ value_is_property: false
104
+ }
105
+ });
106
+
107
+ expect(plan.items.length).toBe(4);
108
+ expect(plan.items[0].alias).toBe('F1');
109
+ expect(plan.items[0].cst_type).toBe(CstType.FUNCTION);
110
+ expect(plan.items[0].definition_formal).toContain('S2');
111
+ expect(plan.items[0].definition_formal).not.toContain('F42');
112
+
113
+ const p29Item = plan.items.find(item => item.term_raw === 'замкнутость');
114
+ const p30Item = plan.items.find(item => item.term_raw === 'ассоциативность');
115
+ expect(p29Item?.cst_type).toBe(CstType.AXIOM);
116
+ expect(p30Item?.cst_type).toBe(CstType.AXIOM);
117
+ expect(p30Item?.definition_formal).toContain('F1[');
118
+
119
+ const main = plan.items.at(-1)!;
120
+ expect(main.alias).toBe('A1');
121
+ expect(main.cst_type).toBe(CstType.AXIOM);
122
+ expect(main.definition_formal).toContain(`${p29Item?.alias}[S1, S2]`);
123
+ expect(main.definition_formal).toContain(`${p30Item?.alias}[S2]`);
124
+ });
125
+
126
+ it('does not assign dependency aliases that collide with the main constituent alias', () => {
127
+ const userArgs: ArgumentValue[] = [
128
+ { alias: 'α', typification: 'ℬ(R1)', value: 'S1' },
129
+ { alias: 'σ', typification: 'ℬ((R1×R1)×R1)', value: 'S2' }
130
+ ];
131
+
132
+ const plan = planTemplateInstantiation({
133
+ targetSchema: mockSchema([
134
+ ...targetSchema.items,
135
+ mockCst({ id: 12, alias: 'P1', cst_type: CstType.PREDICATE, definition_formal: 'true' }),
136
+ mockCst({ id: 13, alias: 'P2', cst_type: CstType.PREDICATE, definition_formal: 'true' }),
137
+ mockCst({ id: 14, alias: 'P3', cst_type: CstType.PREDICATE, definition_formal: 'true' }),
138
+ mockCst({ id: 15, alias: 'P4', cst_type: CstType.PREDICATE, definition_formal: 'true' }),
139
+ mockCst({ id: 16, alias: 'P5', cst_type: CstType.PREDICATE, definition_formal: 'true' })
140
+ ]),
141
+ templateItems,
142
+ prototype: p35,
143
+ userArgs,
144
+ mainItem: {
145
+ alias: 'P6',
146
+ cst_type: CstType.PREDICATE,
147
+ crucial: false,
148
+ convention: '',
149
+ definition_formal: p35.definition_formal,
150
+ definition_raw: p35.definition_raw,
151
+ term_raw: p35.term_raw,
152
+ term_forms: [],
153
+ typification_manual: '',
154
+ value_is_property: false
155
+ }
156
+ });
157
+
158
+ const aliases = plan.items.map(item => item.alias);
159
+ expect(new Set(aliases).size).toBe(aliases.length);
160
+ expect(aliases.at(-1)).toBe('P6');
161
+ expect(aliases.slice(0, -1).every(alias => alias !== 'P6')).toBe(true);
162
+ });
163
+
164
+ it('reuses schema constituent matched by normalized substituted expression', () => {
165
+ const userArgs: ArgumentValue[] = [{ alias: 'σ', typification: 'ℬ((R1×R1)×R1)', value: 'S2' }];
166
+
167
+ const baseline = planTemplateInstantiation({
168
+ targetSchema,
169
+ templateItems,
170
+ prototype: p30,
171
+ userArgs,
172
+ mainItem: {
173
+ alias: 'A2',
174
+ cst_type: CstType.PREDICATE,
175
+ crucial: false,
176
+ convention: '',
177
+ definition_formal: p30.definition_formal,
178
+ definition_raw: '',
179
+ term_raw: p30.term_raw,
180
+ term_forms: [],
181
+ typification_manual: '',
182
+ value_is_property: false
183
+ }
184
+ });
185
+
186
+ const existingFunction = baseline.items[0];
187
+ const schemaWithEquivalentFunction = mockSchema([
188
+ ...targetSchema.items,
189
+ mockCst({
190
+ id: 12,
191
+ alias: 'F9',
192
+ cst_type: existingFunction.cst_type,
193
+ definition_formal: existingFunction.definition_formal
194
+ })
195
+ ]);
196
+
197
+ const plan = planTemplateInstantiation({
198
+ targetSchema: schemaWithEquivalentFunction,
199
+ templateItems,
200
+ prototype: p30,
201
+ userArgs,
202
+ mainItem: {
203
+ alias: 'A2',
204
+ cst_type: CstType.PREDICATE,
205
+ crucial: false,
206
+ convention: '',
207
+ definition_formal: p30.definition_formal,
208
+ definition_raw: '',
209
+ term_raw: p30.term_raw,
210
+ term_forms: [],
211
+ typification_manual: '',
212
+ value_is_property: false
213
+ }
214
+ });
215
+
216
+ expect(plan.items).toHaveLength(1);
217
+ expect(plan.items[0].alias).toBe('A2');
218
+ expect(plan.items[0].definition_formal).toContain('F9[');
219
+ expect(plan.items[0].definition_formal).not.toContain('F42[');
220
+ });
221
+
222
+ it('skips template constituents that already exist in the target schema', () => {
223
+ const schemaWithF42 = mockSchema([
224
+ ...targetSchema.items,
225
+ mockCst({ id: 12, alias: 'F42', cst_type: CstType.FUNCTION, definition_formal: f42.definition_formal })
226
+ ]);
227
+
228
+ const plan = planTemplateInstantiation({
229
+ targetSchema: schemaWithF42,
230
+ templateItems,
231
+ prototype: p30,
232
+ userArgs: [{ alias: 'σ', typification: 'ℬ((R1×R1)×R1)', value: 'S2' }],
233
+ mainItem: {
234
+ alias: 'A2',
235
+ cst_type: CstType.PREDICATE,
236
+ crucial: false,
237
+ convention: '',
238
+ definition_formal: p30.definition_formal,
239
+ definition_raw: '',
240
+ term_raw: p30.term_raw,
241
+ term_forms: [],
242
+ typification_manual: '',
243
+ value_is_property: false
244
+ }
245
+ });
246
+
247
+ expect(plan.items).toHaveLength(1);
248
+ expect(plan.items[0].alias).toBe('A2');
249
+ expect(plan.items[0].definition_formal).toContain('F42[');
250
+ });
251
+
252
+ it('reports duplicate alias when the selected expression already exists in the schema', () => {
253
+ const userArgs: ArgumentValue[] = [
254
+ { alias: 'α', typification: 'ℬ(R1)', value: 'S1' },
255
+ { alias: 'σ', typification: 'ℬ((R1×R1)×R1)', value: 'S2' }
256
+ ];
257
+
258
+ const baseline = planTemplateInstantiation({
259
+ targetSchema,
260
+ templateItems,
261
+ prototype: p35,
262
+ userArgs,
263
+ mainItem: {
264
+ alias: 'A1',
265
+ cst_type: CstType.PREDICATE,
266
+ crucial: false,
267
+ convention: '',
268
+ definition_formal: p35.definition_formal,
269
+ definition_raw: p35.definition_raw,
270
+ term_raw: p35.term_raw,
271
+ term_forms: [],
272
+ typification_manual: '',
273
+ value_is_property: false
274
+ }
275
+ });
276
+
277
+ const existingMain = baseline.items.at(-1)!;
278
+ const plan = planTemplateInstantiation({
279
+ targetSchema: mockSchema([
280
+ ...targetSchema.items,
281
+ mockCst({
282
+ id: 20,
283
+ alias: 'A9',
284
+ cst_type: existingMain.cst_type,
285
+ definition_formal: existingMain.definition_formal
286
+ })
287
+ ]),
288
+ templateItems,
289
+ prototype: p35,
290
+ userArgs,
291
+ mainItem: {
292
+ alias: 'A1',
293
+ cst_type: CstType.PREDICATE,
294
+ crucial: false,
295
+ convention: '',
296
+ definition_formal: p35.definition_formal,
297
+ definition_raw: p35.definition_raw,
298
+ term_raw: p35.term_raw,
299
+ term_forms: [],
300
+ typification_manual: '',
301
+ value_is_property: false
302
+ }
303
+ });
304
+
305
+ expect(plan.mainDuplicateAlias).toBe('A9');
306
+ expect(plan.items.some(item => item.term_raw === p35.term_raw)).toBe(false);
307
+ });
308
+ });