@fraczak/k 3.0.2 → 4.0.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.
package/typing.mjs ADDED
@@ -0,0 +1,689 @@
1
+ import { Graph } from "./Graph.mjs";
2
+ import codes from "./codes.mjs";
3
+
4
+ const unitCode = codes.unitCode;
5
+
6
+ function asMapSet(vector) {
7
+ return vector.reduce( (mapSet, x) => ({...mapSet, [JSON.stringify(x)]: x}), {})
8
+ };
9
+
10
+ function setUnion(...vectors) {
11
+ return Object.values(asMapSet([].concat(...vectors)));
12
+ }
13
+
14
+ function subsetP(v1,v2) {
15
+ const v2Set = asMapSet(v2);
16
+ return Object.keys(asMapSet(v1)).every(x => x in v2Set);
17
+ }
18
+ function eqsetP(v1,v2) {
19
+ return subsetP(v1,v2) && subsetP(v2,v1);
20
+ }
21
+
22
+ const matchAllPattern = {pattern: '(...)', fields: []};
23
+ /*
24
+ $Pattern = <
25
+ string code,
26
+ open? product,
27
+ open? union,
28
+ {} vector,
29
+ open? unknown
30
+ >;
31
+ $open? = < {} open, {} closed >;
32
+
33
+ we use the string notation:
34
+ '(...)' | '{...}' | '<...>' | '()' | '[]' | '{}' | '<>' | 'type'
35
+ e.g.:
36
+ {pattern: '()'},
37
+ {pattern: '<...>'}
38
+ {pattern: 'type', type: code_name}
39
+ */
40
+
41
+ class TypePatternForest {
42
+ constructor() {
43
+ this.nodes = [];
44
+ this.parent = [];
45
+ }
46
+
47
+ // Find the root of the set containing `x`
48
+ find(x) {
49
+ if (this.nodes[x] == undefined)
50
+ throw new Error(`Node of id='${x}' not found in the forest`);
51
+ let result = x;
52
+ let parent = this.parent[result];
53
+ while (parent != undefined) {
54
+ parent = this.parent[result = parent];
55
+ if (parent == result) {
56
+ console.log("Can happen a loop in the forest? It shouldn't!");
57
+ break;
58
+ }
59
+
60
+ }
61
+ return result;
62
+ }
63
+
64
+ addChildren(parentId, childrenIds) {
65
+ const parent = this.find(parentId);
66
+ childrenIds.forEach(childId => {
67
+ const child = this.find(childId);
68
+ if (this.nodes[child].pattern != 'type')
69
+ this.parent[child] = parent;
70
+ });
71
+ }
72
+
73
+ addNewNode(flatTypePattern, children = []) {
74
+ const that = this;
75
+ const id = this.nodes.length;
76
+ // const node = {...flatTypePattern, _id: id};
77
+ const node = {...flatTypePattern}
78
+ that.nodes.push(node);
79
+ Object.values(asMapSet(children.map(that.find.bind(that))))
80
+ .forEach(rep => {that.parent[rep] = id});
81
+ return id;
82
+ }
83
+ }
84
+
85
+ class TypePatternGraph {
86
+ constructor() {
87
+ this.patterns = new TypePatternForest();
88
+ this.edges = []; // an edge at index i is a map {lab: asMapSet([i1,...]), ...}
89
+ // representing `patterns.node[i] --[lab]--> {i1:i1,...}, ...
90
+ this.codeId = { }; // type-name -> index
91
+ }
92
+
93
+ turnSingletonPatternsIntoCodes() {
94
+ const singletonPatterns = this.findSingletonPatterns();
95
+ const newCodeDefs= {}
96
+ for(const patternId of singletonPatterns) {
97
+ const pattern = this.get_pattern(patternId);
98
+ switch (pattern.pattern) {
99
+ case '<>':
100
+ case '{}': {
101
+ const code = (pattern.pattern == '<>') ? 'union' : 'product';
102
+ const fieldsWithDest = this.edges[patternId];
103
+ newCodeDefs[`-${patternId}-`] = {
104
+ code: code,
105
+ [code]: Object.keys(fieldsWithDest).reduce((fields, lab) => {
106
+ const auxDests = Object.values(fieldsWithDest[lab]).map(id => this.find(id));
107
+ const destAsMapSet = auxDests.reduce( (mapSet, x) => ({...mapSet, [JSON.stringify(x)]: x}), {});
108
+ const dests = Object.values(destAsMapSet);
109
+ if (dests.length != 1) {
110
+ throw new Error(`Expected one destination for ${lab} in ${patternId}`);
111
+ }
112
+ const destPattern = this.get_pattern(dests[0]);
113
+ fields[lab] = (destPattern.pattern == 'type') ? destPattern.type : `-${dests[0]}-`;
114
+ return fields;
115
+ }, {})
116
+ };
117
+ }; break;
118
+ case '[]': {
119
+ const auxDests = Object.values(this.edges[patternId]["vector-member"]).map(id => this.find(id));
120
+ const destAsMapSet = auxDests.reduce( (mapSet, x) => ({...mapSet, [JSON.stringify(x)]: x}), {});
121
+ const dests = Object.values(destAsMapSet);
122
+ if (dests.length != 1) {
123
+ // console.log(dests.map(d => this.get_pattern(d)));
124
+ throw new Error(`Expected one destination 'vector-member' in ${JSON.stringify(pattern)}, but got ${JSON.stringify(dests)}`);
125
+ }
126
+ const destPattern = this.get_pattern(dests[0]);
127
+ newCodeDefs[`-${patternId}-`] = {
128
+ code: 'vector',
129
+ vector: (destPattern.pattern == 'type') ? destPattern.type : `-${dests[0]}-`
130
+ };
131
+ }; break;
132
+ default:
133
+ throw new Error(`Unexpected pattern ${JSON.stringify(pattern)}`);
134
+ }
135
+ }
136
+ // - add them all to 'codes'
137
+ const representatives = codes.register(newCodeDefs);
138
+
139
+ for( const id of singletonPatterns) {
140
+ this.unify('singleton', id, this.getTypeId(representatives[`-${id}-`]));
141
+ }
142
+
143
+ }
144
+
145
+
146
+ get_pattern(id) {
147
+ return this.patterns.nodes[this.find(id)];
148
+ }
149
+
150
+ find(id) {
151
+ return this.patterns.find(id);
152
+ }
153
+
154
+ findSingletonPatterns() {
155
+ const gNodes = this.patterns.nodes.map((x,i) => i).filter(x => this.patterns.parent[x] == undefined);
156
+ const gEdges = [].concat(
157
+ ...gNodes.map(x =>
158
+ [].concat(
159
+ ...Object.values(this.edges[x]).map( asMap =>
160
+ [].concat(...Object.values(asMap).map(y =>
161
+ ({src: x, dst: this.find(y)}))
162
+ )
163
+ )
164
+ )
165
+ )
166
+ );
167
+
168
+ const gEdgesWithoutParrallelEdges = Object.values(asMapSet(gEdges));
169
+
170
+ const gGraph = new Graph(gEdgesWithoutParrallelEdges);
171
+ const queue = gNodes.filter(x =>
172
+ this.patterns.nodes[x].pattern in {'()':1, '(...)':1,'{...}':1, '<...>':1});
173
+ const excluded = asMapSet(queue);
174
+ while (queue.length > 0) {
175
+ const x = queue.pop();
176
+ for (const e of gGraph.dst[x] || []) {
177
+ const y = gGraph.edges[e].src;
178
+ if (y in excluded) continue;
179
+ excluded[y] = y;
180
+ queue.push(y);
181
+ }
182
+ }
183
+ return gNodes.filter(x => !((x in excluded) || (this.patterns.nodes[x].pattern == 'type')));
184
+ }
185
+
186
+ getCompressed() {
187
+ const newTypePatternGraph = new TypePatternGraph();
188
+ const renamed = this.cloneAll(newTypePatternGraph);
189
+ newTypePatternGraph.turnSingletonPatternsIntoCodes();
190
+
191
+ Object.keys(renamed).forEach( id => {
192
+ renamed[id] = newTypePatternGraph.find(renamed[id]);
193
+ });
194
+
195
+ // console.log("newTypePatternGraph", newTypePatternGraph);
196
+ // console.log("nodes", newTypePatternGraph.patterns.nodes);
197
+ // console.log("edges", newTypePatternGraph.edges);
198
+
199
+ const rootPatternIds = newTypePatternGraph.patterns.nodes.map((x,i) => i)
200
+ .filter(x => newTypePatternGraph.find(x) == x);
201
+
202
+ // console.log("rootPatternIds", rootPatternIds);
203
+
204
+ const equivalence = rootPatternIds.reduce( (equivalence,i) => {
205
+ const pattern = newTypePatternGraph.get_pattern(i);
206
+ switch (pattern.pattern) {
207
+ case 'type':
208
+ case '(...)':
209
+ case '{...}':
210
+ case '<...>':
211
+ equivalence.push([i]);
212
+ break;
213
+ default:
214
+ equivalence[0].push(i);
215
+ };
216
+ return equivalence;
217
+ }, [[]]).filter(x => x.length > 0);
218
+
219
+ const reps = equivalence.reduce( (reps,eqClass) => {
220
+ eqClass.forEach( x => reps[x] = eqClass);
221
+ return reps;
222
+ }, {});
223
+
224
+ // console.log("equivalence", equivalence);
225
+
226
+ const areDifferent = (i1,i2) => {
227
+ const p1 = newTypePatternGraph.get_pattern(i1);
228
+ const p2 = newTypePatternGraph.get_pattern(i2);
229
+ if (p1.pattern != p2.pattern) return true;
230
+ const e1 = newTypePatternGraph.edges[i1];
231
+ const e2 = newTypePatternGraph.edges[i2];
232
+ if (Object.keys(e1).length != Object.keys(e2).length) return true;
233
+ for (const lab in e1) {
234
+ if (e2[lab] == undefined) return true;
235
+ const dests1 = Object.values(e1[lab])[0];
236
+ const dests2 = Object.values(e2[lab])[0];
237
+ if (reps[dests1] != reps[dests2]) return true;
238
+ }
239
+ return false;
240
+ }
241
+ let changed = true;
242
+ while (changed) {
243
+ changed = false;
244
+ const equivalenceSize = equivalence.length;
245
+ for (let eqClassPos = 0; eqClassPos < equivalenceSize; eqClassPos++) {
246
+ const eqClass = equivalence[eqClassPos];
247
+ const oldClass = [eqClass[0]];
248
+ const newClass = [];
249
+ for (let i = 1; i < eqClass.length; i++) {
250
+ if (areDifferent(oldClass[0],eqClass[i])) {
251
+ newClass.push(eqClass[i]);
252
+ } else {
253
+ oldClass.push(eqClass[i]);
254
+ }
255
+ }
256
+ if (newClass.length > 0) {
257
+ changed = true;
258
+ equivalence.push(newClass);
259
+ equivalence[eqClassPos] = oldClass;
260
+ for (const i of newClass) {
261
+ reps[i] = newClass;
262
+ }
263
+ for (const i of oldClass) {
264
+ reps[i] = oldClass;
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ // console.log("equivalence (final)", equivalence);
271
+ // console.log("reps", reps);
272
+
273
+ const compressedTypePatternGraph = new TypePatternGraph();
274
+ const newIds = {};
275
+ for (const eqClass of equivalence) {
276
+ // console.log("eqClass", eqClass);
277
+ const oldId = eqClass[0];
278
+ const pattern = newTypePatternGraph.get_pattern(oldId);
279
+ const newId = (() => {
280
+ if (pattern.pattern == 'type') return compressedTypePatternGraph.getTypeId(pattern.type);
281
+ return compressedTypePatternGraph.patterns.addNewNode(pattern);
282
+ })();
283
+ newIds[oldId] = newId;
284
+ }
285
+ for (const oldId in newIds) {
286
+ const newId = newIds[oldId];
287
+ const pattern = newTypePatternGraph.get_pattern(oldId);
288
+ const newPattern = compressedTypePatternGraph.get_pattern(newId);
289
+ // console.log("patterns", pattern, newPattern);
290
+ if (pattern.pattern == 'type')
291
+ continue;
292
+ const edges = newTypePatternGraph.edges[oldId];
293
+ // console.log("edges", edges);
294
+ compressedTypePatternGraph.edges[newId] = {};
295
+ for (const lab in edges) {
296
+ // console.log("edges[lab]", edges[lab]);
297
+ compressedTypePatternGraph.edges[newId][lab] = asMapSet(Object.values(edges[lab]).map(x =>
298
+ newIds[reps[newTypePatternGraph.find(x)][0]]));
299
+ }
300
+ }
301
+
302
+ const remapping = Object.keys(renamed).reduce( (remapping,id) => {
303
+ return {...remapping, [id]: newIds[ reps[renamed[id]][0] ]};
304
+ }, {});
305
+
306
+ // console.log("remapping", remapping);
307
+ // console.log("compressedTypePatternGraph", compressedTypePatternGraph);
308
+ // console.log("nodes", compressedTypePatternGraph.patterns.nodes);
309
+ // console.log("edges", compressedTypePatternGraph.edges);
310
+ // console.log("--------------------")
311
+ return {typePatternGraph: compressedTypePatternGraph, remapping};
312
+ }
313
+
314
+ clone(roots,targetGraph = this) {
315
+ const result = {}; // mapping from original ids to cloned ids
316
+ const find = (x) => this.find(x);
317
+ let ids = [...roots];
318
+ while (ids.length > 0) {
319
+ const old_id = ids.pop();
320
+ const i = find(old_id);
321
+ if (i in result) {
322
+ result[old_id] = result[i];
323
+ continue;
324
+ }
325
+ const cloned = (pattern => {
326
+ if (pattern.pattern == 'type') return targetGraph.getTypeId(pattern.type);
327
+ return targetGraph.patterns.addNewNode(pattern);
328
+ })(this.patterns.nodes[i]);
329
+ result[i] = cloned;
330
+ result[old_id] = cloned;
331
+ if (this.patterns.nodes[i].pattern == 'type')
332
+ continue;
333
+ const edges = this.edges[i];
334
+ for (const lab in edges) {
335
+ ids = ids.concat(Object.values(edges[lab]));
336
+ }
337
+ }
338
+ for (const i in result) {
339
+ if (i != find(i)) continue;
340
+ if (this.patterns.nodes[i].pattern == 'type')
341
+ continue;
342
+ const edges = this.edges[i];
343
+ const new_id = result[i];
344
+ targetGraph.edges[new_id] = {};
345
+ for (const lab in edges) {
346
+ targetGraph.edges[new_id][lab] = asMapSet(Object.values(edges[lab]).map(x => result[find(x)]));
347
+ }
348
+ }
349
+ return result;
350
+ }
351
+
352
+ cloneAll(targetGraph = this) {
353
+ return this.clone(this.patterns.nodes.map((x,i) => i), targetGraph);
354
+ }
355
+
356
+ unify_two_patterns(p1, p2) {
357
+ // console.log(`unify_two_patterns(${JSON.stringify(p1)}, ${JSON.stringify(p2)})`);
358
+ switch (p1.pattern) {
359
+ case '(...)':
360
+ switch (p2.pattern) {
361
+ case '(...)':
362
+ return {...p2, fields: setUnion(p1.fields, p2.fields)};
363
+ case '{...}':
364
+ return {...p2, fields: setUnion(p1.fields, p2.fields)};
365
+ case '<...>':
366
+ return {...p2, fields: setUnion(p1.fields, p2.fields)};
367
+ case '()':
368
+ if (subsetP(p1.fields, p2.fields)) return p2;
369
+ throw new Error(`Cannot unify (${p1.fields},...) with (${p2.fields})`);
370
+ case '[]':
371
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/))) return p2;
372
+ throw new Error(`Cannot unify [] with (${p1.fields},...)`);
373
+ case '{}':
374
+ if (subsetP(p1.fields, p2.fields)) return p2;
375
+ throw new Error(`Cannot unify (${p1.fields},...) with {${p2.fields}}`);
376
+ case '<>':
377
+ if (subsetP(p1.fields, p2.fields)) return p2;
378
+ throw new Error(`Cannot unify (${p1.fields},...) with <${p2.fields}>`);
379
+ case 'type': {
380
+ const code = codes.find(p2.type);
381
+ switch (code.code) {
382
+ case 'int':
383
+ // supports (2 .2) which returns unit
384
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/)))
385
+ return p2;
386
+ break;
387
+ case 'string':
388
+ return p2;
389
+ case 'bool':
390
+ if (p1.fields.every(x => `${x}`.match(/^true|false$/)))
391
+ return p2;
392
+ break;
393
+ case 'product':
394
+ case 'union': {
395
+ let p2_fields = Object.keys(code[code.code]);
396
+ if (subsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
397
+ }; break;
398
+ case 'vector':
399
+ return {...p2, fields: ['vector-member']};
400
+ }
401
+ throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
402
+ }
403
+ } ;
404
+ break;
405
+ case '{...}':
406
+ switch (p2.pattern) {
407
+ case '{...}':
408
+ return {...p2, fields: setUnion(p1.fields, p2.fields)};
409
+ case '<...>':
410
+ throw new Error('Cannot unify {...} with <...>');
411
+ case '()':
412
+ if (subsetP(p1.fields, p2.fields))
413
+ return {pattern: '{}', fields: p2.fields};
414
+ case '[]':
415
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/))) return p2;
416
+ throw new Error(`Cannot unify [] with {${p1.fields},...}`);
417
+ case '{}':
418
+ if (subsetP(p1.fields, p2.fields)) return p2;
419
+ throw new Error(`Cannot unify {${p1.fields},...} with {${p2.fields}}`);
420
+ case '<>':
421
+ throw new Error('Cannot unify {...} with <>');
422
+ case 'type': {
423
+ const code = codes.find(p2.type);
424
+ switch (code.code) {
425
+ case 'product':{
426
+ let p2_fields = Object.keys(code[code.code]);
427
+ if (subsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
428
+ };
429
+ }
430
+ throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
431
+ }
432
+ };
433
+ break;
434
+ case '<...>':
435
+ switch (p2.pattern) {
436
+ case '<...>':
437
+ return {pattern: '<...>', fields: setUnion(p1.fields, p2.fields)};
438
+ case '()':
439
+ if (subsetP(p1.fields, p2.fields))
440
+ return {pattern: '<>', fields: p2.fields};
441
+ throw new Error(`Cannot unify <${p1.fields},...> with (${p2.fields})`);
442
+ case '[]':
443
+ throw new Error('Cannot unify <...> with []');
444
+ case '{}':
445
+ throw new Error('Cannot unify <...> with {}');
446
+ case '<>':
447
+ if (subsetP(p1.fields, p2.fields)) return p2;
448
+ throw new Error(`Cannot unify <${p1.fields},...> with <${p2.fields}>`);
449
+ case 'type':{
450
+ const code = codes.find(p2.type);
451
+ switch (code.code) {
452
+ case 'int':
453
+ // supports (2 .2) which returns unit
454
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/)))
455
+ return p2;
456
+ break;
457
+ case 'string':
458
+ return p2;
459
+ case 'bool':
460
+ if (p1.fields.every(x => `${x}`.match(/^true|false$/)))
461
+ return p2;
462
+ break;
463
+ case 'union': {
464
+ let p2_fields = Object.keys(code[code.code]);
465
+ if (subsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
466
+ }
467
+ }
468
+ throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
469
+ };
470
+ };
471
+ break;
472
+ case '()': // Pattern '()' can be introduced by filter e.g., ?(X x, Y x)
473
+ switch (p2.pattern) {
474
+ case '()':
475
+ if (eqsetP(p1.fields, p2.fields)) return p2;
476
+ throw new Error(`Cannot unify (${p1.fields}) with (${p2.fields})`);
477
+ case '{}':
478
+ if (eqsetP(p1.fields, p2.fields)) return p2;
479
+ throw new Error(`Cannot unify (${p1.fields}) with {${p2.fields}}`);
480
+ case '[]':
481
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/))) return p2;
482
+ throw new Error(`Cannot unify [] with (${p1.fields})`);
483
+ case '<>':
484
+ if (eqsetP(p1.fields, p2.fields)) return p2;
485
+ throw new Error(`Cannot unify (${p1.fields}) with <${p2.fields}>`);
486
+ case 'type':{
487
+ const code = codes.find(p2.type);
488
+ switch (code.code) {
489
+ case 'int':
490
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/)))
491
+ return p2;
492
+ break;
493
+ case 'string':
494
+ return p2;
495
+ case 'bool':
496
+ if (p1.fields.every(x => `${x}`.match(/^true|false$/)))
497
+ return p2;
498
+ break;
499
+ case 'product':
500
+ case 'union': {
501
+ let p2_fields = Object.keys(code[code.code]);
502
+ if (eqsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
503
+ }; break;
504
+ case 'vector':
505
+ if (p1.fields.every(x => `${x}`.match(/^[0-9]+$/)))
506
+ return {...p2, fields: ['vector-member']};
507
+ }
508
+ throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
509
+ };
510
+ };
511
+ break;
512
+ case '[]':
513
+ switch (p2.pattern) {
514
+ case '{}':
515
+ // if (p2.fields.every(x => `${x}`.match(/^[0-9]+$/)))
516
+ // return p1;
517
+ throw new Error(`Cannot unify [] with {${p2.fields}}`);
518
+ case '[]':
519
+ return p1;
520
+ case '<>':
521
+ throw new Error('Cannot unify [] with <>');
522
+ case 'type': {
523
+ const code = codes.find(p2.type);
524
+ switch (code.code) {
525
+ case 'vector':
526
+ return {...p2, fields: ['vector-member']};
527
+ }
528
+ throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
529
+ };
530
+ };
531
+ break;
532
+ case '{}':
533
+ switch (p2.pattern) {
534
+ case '{}':
535
+ if (eqsetP(p1.fields, p2.fields)) return p2;
536
+ throw new Error(`Cannot unify {${p1.fields}} with {${p2.fields}}`);
537
+ case '<>':
538
+ throw new Error('Cannot unify {} with <>');
539
+ case 'type': {
540
+ const code = codes.find(p2.type);
541
+ switch (code.code) {
542
+ case 'product':{
543
+ let p2_fields = Object.keys(code[code.code]);
544
+ if (eqsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
545
+ };
546
+ }
547
+ throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
548
+ }
549
+ };
550
+ break;
551
+ case '<>':
552
+ switch (p2.pattern) {
553
+ case '<>':
554
+ if (eqsetP(p1.fields, p2.fields)) return p2;
555
+ throw new Error(`Cannot unify <${p1.fields}> with <${p2.fields}>`);
556
+ case 'type': {
557
+ const code = codes.find(p2.type);
558
+ switch (code.code) {
559
+ case 'union':{
560
+ let p2_fields = Object.keys(code[code.code]);
561
+ if (eqsetP(p1.fields, p2_fields)) return {...p2, fields: p2_fields};
562
+ };
563
+ }
564
+ throw new Error(`Cannot unify ${JSON.stringify(p1)} with code ${p2.type}:${code.def}`);
565
+ }
566
+ };
567
+ break;
568
+ case 'type':
569
+ switch (p2.pattern) {
570
+ case 'type':
571
+ if (p1.type != p2.type)
572
+ throw new Error(`Cannot unify different types: ${p1.type}:${codes.find(p1.type).def} and ${p2.type}:${codes.find(p2.type).def}`);
573
+ return p2;
574
+ };
575
+ break;
576
+ }
577
+ return this.unify_two_patterns(p2, p1);
578
+ }
579
+
580
+ unify_patterns(...patterns) {
581
+ const unify_two_patterns = this.unify_two_patterns.bind(this);
582
+ return patterns.reduce(unify_two_patterns, {pattern: '(...)', fields: []});
583
+ }
584
+
585
+ unify(rule, ...ids) {
586
+ const find = (x) => this.find(x);
587
+ const reps = Object.values(asMapSet(ids.map(find)));
588
+ if (reps.length < 2) return false;
589
+ const rep_patters = reps.map(x => {
590
+ const pattern = this.get_pattern(x);
591
+ const fields = Object.keys(this.edges[x] || {});
592
+ return {...pattern, fields: fields};
593
+ });
594
+
595
+ const new_pattern = (() => { try {
596
+ return {...this.unify_patterns(...rep_patters), rule : rule}
597
+ } catch (e) {
598
+ e.message = `Unification rule "${rule}"\n - ${e.message}`;
599
+ throw e;
600
+ } })();
601
+ const new_id = (() => {
602
+ if (new_pattern.pattern == 'type') {
603
+ const id = this.getTypeId(new_pattern.type);
604
+ this.patterns.addChildren(id, reps);
605
+ return id;
606
+ };
607
+ return this.patterns.addNewNode(new_pattern, reps);
608
+ })();
609
+ // console.log(" * new ", JSON.stringify({new_id, new_pattern}));
610
+ // console.log(" ", JSON.stringify(rep_patters));
611
+ this.edges[new_id] = {};
612
+
613
+ for (const i of reps) {
614
+ const edges = this.edges[i];
615
+ for (const lab in edges) {
616
+ // if the pattern is [] all labels are treated as 'vector-member'
617
+ const new_lab = new_pattern.pattern == '[]' ? 'vector-member' : lab;
618
+ const dests = asMapSet(Object.values(edges[lab]).map(find));
619
+ this.edges[new_id][new_lab] = {...this.edges[new_id][new_lab], ...dests};
620
+ }
621
+ }
622
+ // add stuff for types
623
+ if (new_pattern.pattern == 'type') {
624
+ const code = codes.find(new_pattern.type);
625
+ switch (code.code) {
626
+ case 'int':
627
+ case 'string':
628
+ case 'bool': {
629
+ // all edges are goin to unit type
630
+ const unit_id = this.getTypeId(unitCode);
631
+ for (const lab in this.edges[new_id]) {
632
+ this.edges[new_id][lab][unit_id] = unit_id;
633
+ }
634
+ }; break;
635
+ case 'product':
636
+ case 'union':{
637
+ const type_fields = Object.keys(code[code.code]);
638
+ for (const lab of type_fields) {
639
+ const target_type_id = this.getTypeId(code[code.code][lab]);
640
+ this.edges[new_id][lab]= {...this.edges[new_id][lab], [target_type_id]: target_type_id};
641
+ }
642
+ }; break;
643
+ case 'vector': {
644
+
645
+ const target_type_id = this.getTypeId(code.vector);
646
+
647
+ this.edges[new_id] = {
648
+ 'vector-member': {
649
+ [target_type_id]: target_type_id,
650
+ ...asMapSet(
651
+ setUnion(...Object.values(this.edges[new_id]).map(aSet => Object.values(aSet)))
652
+ )
653
+ }
654
+ }
655
+ }
656
+ }
657
+ }
658
+ for ( const lab of Object.keys(this.edges[new_id]) ) {
659
+ this.unify(rule+'.', ...Object.values(this.edges[new_id][lab]));
660
+ }
661
+
662
+ return true;
663
+ }
664
+
665
+ addNewNode(pattern = matchAllPattern, fields = {}) {
666
+ const id = this.patterns.addNewNode(pattern);
667
+ const find = (x) => this.patterns.find(x);
668
+ this.edges[id] = {};
669
+ for (const [lab,dest] of Object.entries(fields)) {
670
+ this.edges[id][lab] = asMapSet(dest.map(find));
671
+ }
672
+ return id;
673
+ }
674
+
675
+ getTypeId(type) {
676
+ if (type == undefined) throw new Error("code name cannot be 'undefined'! "); //TODO: remove after testing
677
+ if ( this.codeId[type] == undefined )
678
+ this.codeId[type] = this.addNewNode({pattern: 'type', type: type});
679
+ return this.codeId[type];
680
+ }
681
+
682
+ size() {
683
+ const typeNodes = this.patterns.nodes.filter(x => x.pattern == 'type').length;
684
+ return [this.patterns.nodes.length,typeNodes];
685
+ }
686
+ }
687
+
688
+ export { TypePatternGraph };
689
+ export default { TypePatternGraph };