@fraczak/k 5.0.1 → 5.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/README.md CHANGED
@@ -54,7 +54,7 @@ the property doesn't exist. E.g.,
54
54
 
55
55
  There are three ways of combining functions:
56
56
 
57
- 1. **composition**: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
57
+ 1. **Composition**: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
58
58
  nested field.
59
59
 
60
60
  ```text
@@ -64,7 +64,7 @@ There are three ways of combining functions:
64
64
  {"other": {}} ... undefined
65
65
  ```
66
66
 
67
- 2. **merge**: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
67
+ 2. **Merge**: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
68
68
  `toto` if present; otherwise extracts `titi`.
69
69
 
70
70
  ```text
@@ -74,7 +74,7 @@ There are three ways of combining functions:
74
74
  {"x":{"o":{}}, "z": {"o":{}}} ... undefined
75
75
  ```
76
76
 
77
- 3. **product**: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
77
+ 3. **Product**: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
78
78
  .titi TITI}` extracts two fields and builds a record out of them.
79
79
 
80
80
  ```text
@@ -86,9 +86,9 @@ There are three ways of combining functions:
86
86
 
87
87
  **QUIZ**: What is:
88
88
 
89
- - empty composition: `()` ?
90
- - empty merge: `<>` ?
91
- - empty product: `{}` ?
89
+ - Empty composition: `()` ?
90
+ - Empty merge: `<>` ?
91
+ - Empty product: `{}` ?
92
92
  - `{{{{} s} s} s}` ?
93
93
  - `({{{() a} b} c} .c .b .a)` ?
94
94
 
@@ -96,9 +96,9 @@ There are three ways of combining functions:
96
96
 
97
97
  ### Syntactic sugar
98
98
 
99
- - parenthesis can be omitted, except for the empty composition `()`,
100
- - dot (`.`) in "projection" acts as a separator so the space around it
101
- can be omitted.
99
+ - Parenthesis can be omitted, except for the empty composition `()`,
100
+ - dot (`.`) in "projection" acts as a separator, so the surrounding
101
+ space can be omitted.
102
102
 
103
103
  For example, `(.toto .titi (.0 .1))` can be written as `.toto.titi.0.1`.
104
104
 
@@ -148,8 +148,8 @@ projection, via (`.`), to the canonical string representation of the value, e.g.
148
148
 
149
149
  #### Vector product
150
150
 
151
- Vector product can be seen as an abbreviation for product whose all fields
152
- have the same type, and the filed names are integers starting from zero.
151
+ The vector product can be seen as a shorthand for a product in which all fields
152
+ have the same type, and the field names are integers starting from zero.
153
153
  E.g., `{.toto 0, .titi 1, 123 2}` can be written as
154
154
  `[.toto, .titi, 123]`.
155
155
 
@@ -531,7 +531,7 @@ For example:
531
531
 
532
532
  ## k-REPL (Read-Evaluate-Print Loop)
533
533
 
534
- Also there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`),
534
+ Also, there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`),
535
535
  which acts like a toy shell for the language.
536
536
 
537
537
  ```k-repl
@@ -552,72 +552,74 @@ Very! experimental repl shell for 'k-language'...
552
552
 
553
553
  ## Using `k` from `javascript`
554
554
 
555
- import k from "@fraczak/k";
555
+ ```javascript
556
+ import k from "@fraczak/k";
556
557
 
557
- let k_expression = '()';
558
- k.run(k_expression,"ANYTHING...");
559
- // RETURNS: "ANYTHING..."
558
+ let k_expression = '()';
559
+ k.run(k_expression,"ANYTHING...");
560
+ // RETURNS: "ANYTHING..."
560
561
 
561
- k_expression = '{"ala" name, 23 age}';
562
- k.run(k_expression,"ANYTHING...");
563
- // RETURNS: {"name":"ala","age":23}
562
+ k_expression = '{"ala" name, 23 age}';
563
+ k.run(k_expression,"ANYTHING...");
564
+ // RETURNS: {"name":"ala","age":23}
564
565
 
565
- k_expression = '[.year, .age]';
566
- k.run(k_expression,{"year":2002,"age":19});
567
- // RETURNS: [2002,19]
566
+ k_expression = '[.year, .age]';
567
+ k.run(k_expression,{"year":2002,"age":19});
568
+ // RETURNS: [2002,19]
568
569
 
569
- k_expression = '[(), ()]';
570
- k.run(k_expression,"duplicate me");
571
- // RETURNS: ["duplicate me","duplicate me"]
570
+ k_expression = '[(), ()]';
571
+ k.run(k_expression,"duplicate me");
572
+ // RETURNS: ["duplicate me","duplicate me"]
572
573
 
573
- k_expression = '[[[()]]]';
574
- k.run(k_expression,"nesting");
575
- // RETURNS: [[["nesting"]]]
574
+ k_expression = '[[[()]]]';
575
+ k.run(k_expression,"nesting");
576
+ // RETURNS: [[["nesting"]]]
576
577
 
577
- k_expression = '[[()]] {() nested, .0.0 val}';
578
- k.run(k_expression,"nesting and accessing");
579
- // RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
578
+ k_expression = '[[()]] {() nested, .0.0 val}';
579
+ k.run(k_expression,"nesting and accessing");
580
+ // RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
580
581
 
581
- k_expression = '0000';
582
- k.run(k_expression,{"test":"parse integer"});
583
- // RETURNS: 0
582
+ k_expression = '0000';
583
+ k.run(k_expression,{"test":"parse integer"});
584
+ // RETURNS: 0
584
585
 
585
- k_expression = '[.y,.x] PLUS';
586
- k.run(k_expression,{"x":3,"y":4});
587
- // RETURNS: 7
586
+ k_expression = '[.y,.x] PLUS';
587
+ k.run(k_expression,{"x":3,"y":4});
588
+ // RETURNS: 7
588
589
 
589
- var k_fn = k.compile('{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}');
590
+ var k_fn = k.compile('{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}');
590
591
 
591
- k_fn({"age":23,"name":"Emily"});
592
- // RETURNS: {"nom":"Emily","age":23}
592
+ k_fn({"age":23,"name":"Emily"});
593
+ // RETURNS: {"nom":"Emily","age":23}
593
594
 
594
- k_fn({"age":16,"name":"Katrina"});
595
- // RETURNS: {"nom":"Katrina","age":"ado"}
595
+ k_fn({"age":16,"name":"Katrina"});
596
+ // RETURNS: {"nom":"Katrina","age":"ado"}
596
597
 
597
- k_fn({"age":2,"name":"Mark"});
598
- // RETURNS: {"nom":"Mark","age":"enfant"}
598
+ k_fn({"age":2,"name":"Mark"});
599
+ // RETURNS: {"nom":"Mark","age":"enfant"}
599
600
 
600
- k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
601
+ k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
601
602
 
602
- k_fn(1);
603
- // RETURNS: 1
603
+ k_fn(1);
604
+ // RETURNS: 1
604
605
 
605
- k_fn({"i":1});
606
- // RETURNS: {"i":1}
606
+ k_fn({"i":1});
607
+ // RETURNS: {"i":1}
607
608
 
608
- k_fn([{"i":2},{"i":3},{"t":[]}]);
609
- // RETURNS: undefined
609
+ k_fn([{"i":2},{"i":3},{"t":[]}]);
610
+ // RETURNS: undefined
610
611
 
611
- k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
612
- // RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
612
+ k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
613
+ // RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
613
614
 
614
- k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
615
+ k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
615
616
 
616
- k_fn({"None":"None"});
617
- // RETURNS: {"None":"None"}
617
+ k_fn({"None":"None"});
618
+ // RETURNS: {"None":"None"}
618
619
 
619
- k_fn({"list":{"ints":[]}});
620
- // RETURNS: {"list":{"ints":[]}}
620
+ k_fn({"list":{"ints":[]}});
621
+ // RETURNS: {"list":{"ints":[]}}
621
622
 
622
- k_fn({"list":{"ints":[1,2,3]}});
623
- // RETURNS: {"list":{"ints":[1,2,3]}}
623
+ k_fn({"list":{"ints":[1,2,3]}});
624
+ // RETURNS: {"list":{"ints":[1,2,3]}}
625
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "5.0.1",
3
+ "version": "5.0.2",
4
4
  "description": "k-language for JSON-like data transformation",
5
5
  "main": "index.mjs",
6
6
  "scripts": {
package/patterns.mjs CHANGED
@@ -8,7 +8,7 @@ import { assignCanonicalNames } from "./export.mjs";
8
8
  const unitCode = codes.unitCode;
9
9
 
10
10
  function relDefToString(relDef) {
11
-
11
+ const start = new Date().getTime();
12
12
  const dumpPatterns = (rel) => {
13
13
  switch (rel.op) {
14
14
  case "product":
@@ -39,14 +39,20 @@ function relDefToString(relDef) {
39
39
  // console.log(relDef.typePatternGraph.edges);
40
40
  // console.log(relDef.def);
41
41
 
42
- return JSON.stringify(dumpPatterns(relDef.def).map((x) => {
42
+ const result = JSON.stringify(dumpPatterns(relDef.def).map((x) => {
43
43
  const { pattern, fields, type} = relDef.typePatternGraph.get_pattern(x);
44
44
  return {pattern, fields, type};
45
45
  }));
46
+
47
+ let took = new Date().getTime() - start;
48
+ if (took > 200) {
49
+ console.log(` -- RelDefToString: size=${result.length}, took ${took} msecs`);
50
+ }
51
+ return result;
46
52
  }
47
53
 
48
54
  function compactRel(relDef, name = "") {
49
- // console.log("********************************* compacting", name);
55
+ const start = new Date().getTime();
50
56
  const {def, typePatternGraph, varRefs } = relDef;
51
57
  // console.log("COMPACTING typePatternGraph of size", typePatternGraph.size());
52
58
 
@@ -92,7 +98,10 @@ function compactRel(relDef, name = "") {
92
98
  outputPatternId: renumbering[x.outputPatternId]
93
99
  }));;
94
100
 
95
- // console.log(`COMPACTED ${name}.typePatternGraph`, typePatternGraph.size(), '->', newTypePatternGraph.size());
101
+ const time = new Date().getTime() - start;
102
+ if (time > 500 ) {
103
+ console.log(`[${new Date().getTime() - start} msecs] - COMPACTED ${name}.typePatternGraph [${typePatternGraph.size()}->${newTypePatternGraph.size()}]`);
104
+ }
96
105
  return {def: newDef, typePatternGraph: newTypePatternGraph, varRefs: newVarRefs};
97
106
  }
98
107
 
@@ -462,7 +471,7 @@ function patterns(representatives, rels) {
462
471
  // we will also generate the canonical representation of the relations in C and replace
463
472
  // the name to the relation by the hash of the canonical representation
464
473
 
465
- // console.log("----- SCC in order", sccInOrder);
474
+ // console.log("- SCC in order", sccInOrder);
466
475
 
467
476
  for (const scc of sccInOrder) {
468
477
 
@@ -470,7 +479,7 @@ function patterns(representatives, rels) {
470
479
 
471
480
  for (let iteration = 1; iteration <= maxNumberOfIterations; iteration++) {
472
481
 
473
- // console.log(`>>>>>> Iteration ${iteration} for SCC: { ${DAGnodes[scc].scc} }`);
482
+ // console.log(`-- Iteration ${iteration}/${maxNumberOfIterations} for SCC: { ${DAGnodes[scc].scc} }`);
474
483
  // 4.1 For every r in C, compute the new typePatternGraph of r, i.e.:
475
484
 
476
485
  let before = JSON.stringify(DAGnodes[scc].scc.map( relName => {
@@ -481,11 +490,11 @@ function patterns(representatives, rels) {
481
490
  // console.log(relDefToString(rels[relName]));
482
491
  return relDefToString(rels[relName]);
483
492
  }));
484
-
493
+
485
494
  let after = JSON.stringify(DAGnodes[scc].scc.map( relName => {
486
495
  const relDef = compactRel(rels[relName],relName);
487
496
  for (let i = 0; i < relDef.varRefs.length; i++) {
488
- // console.log(` ####### Processing ${relName}(..x${i}=${relDef.varRefs[i].varName}..)`);
497
+ const start = new Date().getTime();
489
498
  let varRel = relDef.varRefs[i];
490
499
  let { varName } = varRel;
491
500
  try {
@@ -493,7 +502,6 @@ function patterns(representatives, rels) {
493
502
  let varInputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[0]);
494
503
  let varOutputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[1]);
495
504
  let cloned = varRootDef.typePatternGraph.clone([varInputPatternId, varOutputPatternId], relDef.typePatternGraph);
496
-
497
505
  relDef.typePatternGraph.unify(`ref:input ${relName}(${varName})`, varRel.inputPatternId, cloned[varInputPatternId]);
498
506
  relDef.typePatternGraph.unify("ref:output", varRel.outputPatternId, cloned[varOutputPatternId]);
499
507
  } catch (e) {
@@ -502,6 +510,10 @@ function patterns(representatives, rels) {
502
510
  - ${e.message}`;
503
511
  throw e;
504
512
  }
513
+ const tookMs = new Date().getTime() - start;
514
+ if (tookMs > 200) {
515
+ console.log(` ---- Processing ${relName}(.., x${i}=${relDef.varRefs[i].varName}, ..) took: ${tookMs} ms`);
516
+ }
505
517
  }
506
518
  rels[relName] = compactRel(relDef,relName);
507
519
  return relDefToString(rels[relName]);
package/typing.mjs CHANGED
@@ -3,20 +3,36 @@ import codes from "./codes.mjs";
3
3
 
4
4
  const unitCode = codes.unitCode;
5
5
 
6
- function asMapSet(vector) {
7
- return vector.reduce( (mapSet, x) => ({...mapSet, [JSON.stringify(x)]: x}), {})
6
+ function asSet(vector) {
7
+ return Array.from(new Set(vector));
8
8
  };
9
9
 
10
10
  function setUnion(...vectors) {
11
- return Object.values(asMapSet([].concat(...vectors)));
11
+ const unionSet = new Set();
12
+ for (const v of vectors) {
13
+ for (const element of v) {
14
+ unionSet.add(element);
15
+ }
16
+ }
17
+ return Array.from(unionSet);
12
18
  }
13
19
 
14
20
  function subsetP(v1,v2) {
15
- const v2Set = asMapSet(v2);
16
- return Object.keys(asMapSet(v1)).every(x => x in v2Set);
21
+ const v2Set = new Set(v2);
22
+ return v1.every(x => v2Set.has(x));
17
23
  }
24
+
18
25
  function eqsetP(v1,v2) {
19
- return subsetP(v1,v2) && subsetP(v2,v1);
26
+ const v1Set = new Set(v1);
27
+ const v2Set = new Set(v2);
28
+ if (v1Set.size != v2Set.size) return false;
29
+
30
+ for (const value of v1Set) {
31
+ if (!v2Set.has(value)) {
32
+ return false;
33
+ }
34
+ }
35
+ return true;
20
36
  }
21
37
 
22
38
  const matchAllPattern = {pattern: '(...)', fields: []};
@@ -76,7 +92,7 @@ class TypePatternForest {
76
92
  // const node = {...flatTypePattern, _id: id};
77
93
  const node = {...flatTypePattern}
78
94
  that.nodes.push(node);
79
- Object.values(asMapSet(children.map(that.find.bind(that))))
95
+ Array.from(new Set(children.map(that.find.bind(that))))
80
96
  .forEach(rep => {that.parent[rep] = id});
81
97
  return id;
82
98
  }
@@ -139,7 +155,6 @@ class TypePatternGraph {
139
155
  for( const id of singletonPatterns) {
140
156
  this.unify('singleton', id, this.getTypeId(representatives[`-${id}-`]));
141
157
  }
142
-
143
158
  }
144
159
 
145
160
 
@@ -165,27 +180,29 @@ class TypePatternGraph {
165
180
  )
166
181
  );
167
182
 
168
- const gEdgesWithoutParrallelEdges = Object.values(asMapSet(gEdges));
169
-
183
+ const gEdgesWithoutParrallelEdges = Array.from(new Set(gEdges));
170
184
  const gGraph = new Graph(gEdgesWithoutParrallelEdges);
171
185
  const queue = gNodes.filter(x =>
172
186
  this.patterns.nodes[x].pattern in {'()':1, '(...)':1,'{...}':1, '<...>':1});
173
- const excluded = asMapSet(queue);
187
+ const excludedSet = new Set(queue);
188
+
174
189
  while (queue.length > 0) {
175
190
  const x = queue.pop();
176
191
  for (const e of gGraph.dst[x] || []) {
177
192
  const y = gGraph.edges[e].src;
178
- if (y in excluded) continue;
179
- excluded[y] = y;
193
+ if (excludedSet.has(y)) continue;
194
+ excludedSet.add(y);
180
195
  queue.push(y);
181
196
  }
182
197
  }
183
- return gNodes.filter(x => !((x in excluded) || (this.patterns.nodes[x].pattern == 'type')));
198
+
199
+ return gNodes.filter(x => !((excludedSet.has(x)) || (this.patterns.nodes[x].pattern == 'type')));
184
200
  }
185
201
 
186
202
  getCompressed() {
187
203
  const newTypePatternGraph = new TypePatternGraph();
188
204
  const renamed = this.cloneAll(newTypePatternGraph);
205
+
189
206
  newTypePatternGraph.turnSingletonPatternsIntoCodes();
190
207
 
191
208
  Object.keys(renamed).forEach( id => {
@@ -223,7 +240,9 @@ class TypePatternGraph {
223
240
 
224
241
  // console.log("equivalence", equivalence);
225
242
 
243
+ let count = 0;
226
244
  const areDifferent = (i1,i2) => {
245
+ count++;
227
246
  const p1 = newTypePatternGraph.get_pattern(i1);
228
247
  const p2 = newTypePatternGraph.get_pattern(i2);
229
248
  if (p1.pattern != p2.pattern) return true;
@@ -239,7 +258,9 @@ class TypePatternGraph {
239
258
  return false;
240
259
  }
241
260
  let changed = true;
261
+ let count2 = 0;
242
262
  while (changed) {
263
+ count2++;
243
264
  changed = false;
244
265
  const equivalenceSize = equivalence.length;
245
266
  for (let eqClassPos = 0; eqClassPos < equivalenceSize; eqClassPos++) {
@@ -285,7 +306,7 @@ class TypePatternGraph {
285
306
  for (const oldId in newIds) {
286
307
  const newId = newIds[oldId];
287
308
  const pattern = newTypePatternGraph.get_pattern(oldId);
288
- const newPattern = compressedTypePatternGraph.get_pattern(newId);
309
+ // const newPattern = compressedTypePatternGraph.get_pattern(newId);
289
310
  // console.log("patterns", pattern, newPattern);
290
311
  if (pattern.pattern == 'type')
291
312
  continue;
@@ -294,7 +315,7 @@ class TypePatternGraph {
294
315
  compressedTypePatternGraph.edges[newId] = {};
295
316
  for (const lab in edges) {
296
317
  // console.log("edges[lab]", edges[lab]);
297
- compressedTypePatternGraph.edges[newId][lab] = asMapSet(Object.values(edges[lab]).map(x =>
318
+ compressedTypePatternGraph.edges[newId][lab] = asSet(edges[lab].map(x =>
298
319
  newIds[reps[newTypePatternGraph.find(x)][0]]));
299
320
  }
300
321
  }
@@ -308,6 +329,7 @@ class TypePatternGraph {
308
329
  // console.log("nodes", compressedTypePatternGraph.patterns.nodes);
309
330
  // console.log("edges", compressedTypePatternGraph.edges);
310
331
  // console.log("--------------------")
332
+
311
333
  return {typePatternGraph: compressedTypePatternGraph, remapping};
312
334
  }
313
335
 
@@ -343,7 +365,7 @@ class TypePatternGraph {
343
365
  const new_id = result[i];
344
366
  targetGraph.edges[new_id] = {};
345
367
  for (const lab in edges) {
346
- targetGraph.edges[new_id][lab] = asMapSet(Object.values(edges[lab]).map(x => result[find(x)]));
368
+ targetGraph.edges[new_id][lab] = asSet(edges[lab].map(x => result[find(x)]));
347
369
  }
348
370
  }
349
371
  return result;
@@ -584,7 +606,7 @@ class TypePatternGraph {
584
606
 
585
607
  unify(rule, ...ids) {
586
608
  const find = (x) => this.find(x);
587
- const reps = Object.values(asMapSet(ids.map(find)));
609
+ const reps = Array.from(new Set(ids.map(find)));
588
610
  if (reps.length < 2) return false;
589
611
  const rep_patters = reps.map(x => {
590
612
  const pattern = this.get_pattern(x);
@@ -615,8 +637,8 @@ class TypePatternGraph {
615
637
  for (const lab in edges) {
616
638
  // if the pattern is [] all labels are treated as 'vector-member'
617
639
  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};
640
+ const dests = asSet(edges[lab].map(find));
641
+ this.edges[new_id][new_lab] = asSet([...(this.edges[new_id][new_lab] || []), ...dests]);
620
642
  }
621
643
  }
622
644
  // add stuff for types
@@ -629,7 +651,7 @@ class TypePatternGraph {
629
651
  // all edges are goin to unit type
630
652
  const unit_id = this.getTypeId(unitCode);
631
653
  for (const lab in this.edges[new_id]) {
632
- this.edges[new_id][lab][unit_id] = unit_id;
654
+ this.edges[new_id][lab] = asSet([unit_id]);
633
655
  }
634
656
  }; break;
635
657
  case 'product':
@@ -637,7 +659,7 @@ class TypePatternGraph {
637
659
  const type_fields = Object.keys(code[code.code]);
638
660
  for (const lab of type_fields) {
639
661
  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};
662
+ this.edges[new_id][lab]= asSet([...(this.edges[new_id][lab] ||[]), target_type_id]);
641
663
  }
642
664
  }; break;
643
665
  case 'vector': {
@@ -645,18 +667,13 @@ class TypePatternGraph {
645
667
  const target_type_id = this.getTypeId(code.vector);
646
668
 
647
669
  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
- }
670
+ 'vector-member': asSet([ target_type_id, ...Object.values(this.edges[new_id])])
654
671
  }
655
672
  }
656
673
  }
657
674
  }
658
675
  for ( const lab of Object.keys(this.edges[new_id]) ) {
659
- this.unify(rule+'.', ...Object.values(this.edges[new_id][lab]));
676
+ this.unify(rule+'.', ...this.edges[new_id][lab]);
660
677
  }
661
678
 
662
679
  return true;
@@ -667,7 +684,7 @@ class TypePatternGraph {
667
684
  const find = (x) => this.patterns.find(x);
668
685
  this.edges[id] = {};
669
686
  for (const [lab,dest] of Object.entries(fields)) {
670
- this.edges[id][lab] = asMapSet(dest.map(find));
687
+ this.edges[id][lab] = asSet(dest.map(find));
671
688
  }
672
689
  return id;
673
690
  }