@fraczak/k 3.0.2 → 3.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.
package/patterns.mjs CHANGED
@@ -1,601 +1,453 @@
1
1
 
2
- // import { prettyCode, prettyRel } from "./pretty.mjs";
2
+ import hash from "./hash.mjs";
3
+ import { TypePatternGraph } from "./typing.mjs";
4
+ import { Graph, sccs, topoOrder } from "./Graph.mjs";
5
+ import codes from "./codes.mjs";
3
6
 
7
+ const unitCode = codes.unitCode;
4
8
 
5
- // Assumptions:
6
- // 1. Normalized code graph (i.e., codes and representatives)
7
- // 2. a pattern is defined by triples:
8
- // { code: [null, codeId],
9
- // type: [null, product, union, vector, code],
10
- // closed: [true, false] }
9
+ function relDefToString(relDef) {
11
10
 
12
- import hash from "./hash.mjs";
11
+ const dumpPatterns = (rel) => {
12
+ switch (rel.op) {
13
+ case "product":
14
+ return [].concat(rel.patterns, ...rel.product.map(({exp}) => dumpPatterns(exp)));
15
+ case "union":
16
+ return [].concat(rel.patterns, ...rel.union.map(exp => dumpPatterns(exp)));
17
+ case "comp":
18
+ return [].concat(rel.patterns, ...rel.comp.map(exp => dumpPatterns(exp)));
19
+ case "vector":
20
+ return [].concat(rel.patterns, ...rel.vector.map(exp => dumpPatterns(exp)));
21
+ case "caret":
22
+ return [].concat(rel.patterns, dumpPatterns(rel.caret));
23
+ case "ref":
24
+ case "int":
25
+ case "str":
26
+ case "identity":
27
+ case "dot":
28
+ case "code":
29
+ case "pipe":
30
+ return [...rel.patterns];
31
+ }
32
+ throw new Error("NOT EXPECTED OP:", rel);
33
+ };
13
34
 
14
- const unitCode = hash('$C0={};'); // it was '{}'
35
+ return JSON.stringify(dumpPatterns(relDef.def).map((x) => {
36
+ const { pattern, fields, type} = relDef.typePatternGraph.get_pattern(x);
37
+ return {pattern, fields, type};
38
+ }));
39
+ }
15
40
 
16
- function patterns(codes, representatives, rels) {
17
- // INPUT:
18
- // codes: {"BG": {"code": "product", "product": {}}, ...}
19
- // representatives:{"{}": "BG", ...}
20
- // rels: {"rlz": [{op: comp,...}, ...], ...}
21
-
22
- // OUTPUT: returns pattern graph:
23
- // { patternNodes: [{code,type,closed},...],
24
- // patternEdges: {src: {label: dst, ...}}...}
25
- // as well as it will annotate rels; each rel node will have property "patterns: [i, o]"
41
+ function compactRel(relDef, name = "") {
42
+ const {def, typePatternGraph, varRefs } = relDef;
43
+ // console.log("COMPACTING typePatternGraph of size", typePatternGraph.size());
26
44
 
27
- const patternNodes = []; // e.g.: [{ code: null, type: null, closed" false}],
28
- const patternEdges = {}; // e.g.: {0: {label: 2}, ...},
29
-
30
- const codeToPattern = {}; // e.g., {"{}": 0, ...} - in order to collapse patterns resolved to the same code
45
+ const newTypePatternGraph = new TypePatternGraph();
46
+ const renumbering = typePatternGraph.cloneAll(newTypePatternGraph);
31
47
 
32
- // 1 INITIALIZATION
33
- // 1.1 initialize patternNodes and rels
48
+ newTypePatternGraph.turnSingletonPatternsIntoCodes();
34
49
 
35
- function augment(rel) {
36
- const i = patternNodes.length;
37
- const pattern_i = { _id: i, code: null, type: null, closed: false}
38
- patternNodes.push(pattern_i);
50
+ const copyRel = (rel) => {
51
+ const newRel = {...rel, patterns:[renumbering[rel.patterns[0]], renumbering[rel.patterns[1]]]};
39
52
  switch (rel.op) {
40
53
  case "product":
41
- rel["product"].forEach(({label, exp}) => augment(exp));
54
+ newRel.product = rel.product.map(({label, exp}) => ({label, exp: copyRel(exp)}));
42
55
  break;
43
56
  case "union":
57
+ newRel.union = rel.union.map(exp => copyRel(exp));
58
+ break;
44
59
  case "comp":
45
- case "vector":
46
- rel[rel.op].forEach((exp) => augment(exp));
60
+ newRel.comp = rel.comp.map(exp => copyRel(exp));
47
61
  break;
48
- case "code":
49
- rel["code"] = representatives[rel.code] || rel.code;
62
+ case "vector":
63
+ newRel.vector = rel.vector.map(exp => copyRel(exp));
50
64
  break;
51
65
  case "caret":
52
- augment(rel.caret);
53
- break;
54
- case "filter":
55
- // do nothing for now
56
- console.log("filter ignored", rel);
66
+ newRel.caret = copyRel(rel.caret);
57
67
  break;
58
68
  case "ref":
59
- // do nothing
60
- break;
69
+ case "int":
70
+ case "str":
71
+ case "identity":
72
+ case "dot":
73
+ case "code":
61
74
  case "pipe":
62
75
  break;
63
- }
64
-
65
- const o = patternNodes.length;
66
- const pattern_o = { _id: o, code: null, type: null, closed: false}
67
- patternNodes.push(pattern_o);
68
- rel.patterns = [i, o];
69
- }
70
-
71
- for (const relName in rels) {
72
- const defs = rels[relName];
73
- for (const def of defs) { augment(def); }
74
- }
76
+ default:
77
+ console.error("NOT EXPECTED OP:", rel);
78
+ break;
79
+ };
80
+ return newRel;
81
+ };
82
+ const newDef = copyRel(def);
83
+ const newVarRefs = varRefs.map(x => ({
84
+ ...x,
85
+ inputPatternId: renumbering[x.inputPatternId],
86
+ outputPatternId: renumbering[x.outputPatternId]
87
+ }));;
88
+
89
+ // console.log(`COMPACTED ${name}.typePatternGraph`, typePatternGraph.size(), '->', newTypePatternGraph.size());
90
+ return {def: newDef, typePatternGraph: newTypePatternGraph, varRefs: newVarRefs};
91
+ }
75
92
 
76
- // console.log(JSON.stringify(rels,"",2));
93
+ function patterns(usedCodes, representatives, rels) {
94
+ codes.register(usedCodes);
95
+ // INPUT:
96
+ // codes: {"BG": {"code": "product", "product": {}}, ...}
97
+ // representatives:{"{}": "BG", ...}
98
+ // rels: {"rlz": [{op: comp,...}, ...], ...}
99
+
77
100
 
78
- // 1.2 initialize 'eq' (equivalence classes over patternNodes) and 'patternEdges'
79
-
80
- const eq = patternNodes.map((_, i) => i); // equivalence classes over patternNodes, e.g., [0, 1, 0, ...]}
81
-
82
- function getRep(i) {
83
- while (eq[i] !== i) { i = eq[i]; }
84
- return i;
85
- }
86
-
87
- for (const node_id in eq) { patternEdges[node_id]={}; }
88
-
89
- // -------- helper functions --------
101
+ // 1 INITIALIZATION
102
+ // 1.1 initialize patternNodes and rels
90
103
 
91
- function inspect(rel) {
92
- const op = rel.op;
93
- // console.log(`Inspecting ${op} ${JSON.stringify(rel)}`);
104
+ function augment(rel, rootDef) {
94
105
  try {
95
- switch (op) {
106
+ switch (rel.op) {
96
107
  case "product":
97
- return inspectProduct(rel);
108
+ rel["product"].forEach(({label, exp}) => {
109
+ augment(exp, rootDef)
110
+ });
111
+ augmentProduct(rel,rootDef);
112
+ break;
98
113
  case "union":
99
- return inspectUnion(rel);
100
- case "comp":
101
- return inspectComp(rel);
102
- case "identity":
103
- return inspectIdentity(rel);
114
+ rel["union"].forEach((exp) => {
115
+ augment(exp,rootDef);
116
+ });
117
+ augmentUnion(rel,rootDef);
118
+ break;
119
+ case "comp":
120
+ rel["comp"].forEach((exp) => {
121
+ augment(exp,rootDef);
122
+ });
123
+ augmentComp(rel,rootDef);
124
+ break;
104
125
  case "vector":
105
- return inspectVector(rel);
106
- case "dot":
107
- return inspectDot(rel);
108
- case "code":
109
- return inspectCode(rel);
126
+ rel["vector"].forEach((exp) => {
127
+ augment(exp,rootDef);
128
+ });
129
+ augmentVector(rel,rootDef);
130
+ break;
131
+ case "code":
132
+ rel["code"] = representatives[rel.code] || rel.code;
133
+ rel.patterns = [];
134
+ rel.patterns[0] = rootDef.typePatternGraph.getTypeId(rel["code"]);
135
+ rel.patterns[1] = rel.patterns[0];
136
+ break;
137
+ case "caret":
138
+ augment(rel.caret,rootDef);
139
+ rel.patterns = [
140
+ rel.caret.patterns[0],
141
+ rootDef.typePatternGraph.addNewNode(
142
+ {pattern: '[]'},
143
+ {"vector-member": [rel.caret.patterns[1]]})
144
+ ];
145
+ break;
146
+ case "pipe":
147
+ rel.patterns = [];
148
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
149
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode(
150
+ {pattern: '[]'},
151
+ {"vector-member": [rel.patterns[1]]});
152
+ break;
153
+
110
154
  case "ref":
111
- return inspectRef(rel);
155
+ augmentRef(rel, rootDef);
156
+ break;
157
+ //----------------
158
+
112
159
  case "int":
113
- return inspectInt(rel);
160
+ rel.patterns = [];
161
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
162
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('int');
163
+ break;
114
164
  case "str":
115
- return inspectStr(rel);
116
- case "caret":
117
- return inspectCaret(rel);
118
- case "pipe":
119
- return inspectPipe(rel);
120
- case "filter":
121
- return false;
165
+ rel.patterns = [];
166
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
167
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
168
+ break;
169
+ case "identity":
170
+ rel.patterns = [];
171
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
172
+ break;
173
+ case "dot":
174
+ rel.patterns = [];
175
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
176
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '(...)'}, { [rel.dot]: [rel.patterns[1]] });
177
+ break;
178
+
179
+ default:
180
+ console.error("NOT EXPECTED OP:", rel);
181
+ break;
122
182
  }
123
- throw new Error(`Unknown op: ${op}`);
124
183
  } catch (e) {
125
- console.error(`Code Derivation Error for '${op}' (lines ${rel.start?.line}:${rel.start?.column}...${rel.end?.line}:${rel.end?.column}): ${e.message}.`);
184
+ console.error(`Code Derivation Error for '${rel.op}' (lines ${rel.start?.line}:${rel.start?.column}...${rel.end?.line}:${rel.end?.column}): ${e.message}.`);
126
185
  throw e;
127
186
  }
128
187
  }
129
-
130
- function updatePattern(pattern, {code, type, closed}) {
131
- if (code) {
132
- type = "code";
133
- closed = true;
134
- if (pattern.code == code && pattern.type == type && pattern.closed == closed) {
135
- return false;
136
- }
137
- if (pattern.code) {
138
- throw new Error(`Cannot update pattern with code: ${pattern.code} -> ${code}`);
139
- }
140
- if (pattern.type && pattern.type != codes[code]?.code) {
141
- throw new Error(`Cannot update pattern with code '${code}': pattern type: ${pattern.type} -> code type: ${codes[code]?.code || 'built-in'}`);
142
- }
143
- pattern.code = code;
144
- pattern.closed = true;
145
- pattern.type = "code";
146
- return true;
147
- }
148
-
149
- if (pattern.code) {
150
- // TODO: check for potential type mismatch between 'type' and 'code.code'
151
- return false;
152
- }
153
-
154
- let changed = false;
155
-
156
- if (type) {
157
- if (! pattern.type) {
158
- pattern.type = type;
159
- changed = true;
160
- } else if (pattern.type != type) {
161
- throw new Error(`Cannot update pattern ${JSON.stringify(pattern)} with ${JSON.stringify({code, type, closed})}`);
162
- }
163
- }
164
-
165
- if (closed && (! pattern.closed)) {
166
- pattern.closed = closed;
167
- return true;
168
- }
169
-
170
- return changed;
171
- }
172
-
173
- function join(i, j) {
174
- const iRep = getRep(i);
175
- const jRep = getRep(j);
176
- if (iRep == jRep) { return false; }
177
- // create new representative
178
- const iPattern = patternNodes[iRep];
179
- const jPattern = patternNodes[jRep];
180
-
181
- const newRep = patternNodes.length;
182
- const newPattern = {
183
- _id: newRep,
184
- code: null, type: null, closed: false,
185
- _from: `(${iPattern._from || iRep},${jPattern._from || jRep})`};
186
- patternNodes[newRep] = newPattern;
187
- patternEdges[newRep] = {};
188
- eq[iRep] = eq[jRep] = eq[newRep] = newRep;
189
- // we still have to merge patterns and migrate edges
190
- updatePattern(newPattern, iPattern);
191
- updatePattern(newPattern, jPattern);
192
- migrateEdges(jRep, newRep);
193
- migrateEdges(iRep, newRep);
194
-
195
- return newRep;
196
- }
197
-
198
- function migrateEdges(from_i, to_j) {
199
- const iEdges = patternEdges[from_i];
200
- const jEdges = patternEdges[to_j];
201
- for (const label in iEdges) {
202
- const iTarget = iEdges[label];
203
- const jTarget = jEdges[label];
204
- if (jTarget) {
205
- const newTarget = join(iTarget, jTarget);
206
- if (newTarget) {
207
- jEdges[label] = newTarget;
208
- }
209
- } else {
210
- jEdges[label] = iTarget;
211
- }
212
- }
213
- }
214
-
215
- function addEdge(src, label, target) {
216
- // console.log("addEdge", src, label, target, "old target", patternEdges[src][label] || "none");
217
- const srcPattern = patternNodes[getRep(src)];
218
- if (srcPattern.type == "code") {
219
- const code = codes[representatives[srcPattern.code] || srcPattern.code];
220
- if (code.code == "product" || code.code == "union" ) {
221
- return updatePattern(patternNodes[getRep(target)], {code: code[code.code][label]})
222
- }
223
- throw new Error(`Cannot add edge ${label} to code ${JSON.stringify(code)}`);
224
- }
225
- if (srcPattern.type == 'vector') {
226
- if (/^(0|[1-9][0-9]*)$/.test(label)) {
227
- const old_target = patternEdges[src]['vector-member'];
228
- if (old_target) {
229
- const newTarget = join(old_target, target);
230
- if (newTarget) {
231
- patternEdges[src][label] = newTarget;
232
- return true;
233
- }
234
- return false;
235
- }
236
- patternEdges[src]['vector-member'] = target;
237
- return true;
238
- }
239
- throw new Error(`Cannot add edge ${label} to code ${JSON.stringify(code)}`);
240
- }
241
- const old_target = patternEdges[src][label];
242
- if (old_target) {
243
- const newTarget = join(old_target, target);
244
- if (newTarget) {
245
- patternEdges[src][label] = newTarget;
246
- return true;
247
- }
248
- return false;
249
- }
250
-
251
- if (srcPattern.closed) {
252
- throw new Error(`Cannot add edge ${label} to pattern ${JSON.stringify(srcPattern)}`);
253
- }
254
- patternEdges[src][label] = target;
255
- return true;
256
- }
257
-
258
- function inspectInt(rel) {
259
- const o_pattern = patternNodes[rel.patterns[1]];
260
- return updatePattern(o_pattern, {code: "int", type: "code", closed: true});
261
- }
262
-
263
- function inspectStr(rel) {
264
- const o_pattern = patternNodes[rel.patterns[1]];
265
- return updatePattern(o_pattern, {code: "string", type: "code", closed: true});
266
- }
267
-
268
- function inspectPipe(rel) {
269
- const old_i = getRep(rel.patterns[0]);
270
- const old_o = getRep(rel.patterns[1]);
271
- const old_i_pattern = patternNodes[old_i];
272
-
273
- let modified = updatePattern(old_i_pattern, {type: "vector", closed: true});
274
-
275
- let member_i = patternEdges[old_i]["vector-member"];
276
- if (member_i == undefined ) {
277
- modified = true;
278
- member_i = patternNodes.length;
279
- patternNodes.push({ _id: member_i, code: null, type: null, closed: false})
280
- patternEdges[old_i]["vector-member"] = member_i;
281
- eq[member_i] = member_i;
282
- }
283
- member_i = getRep(member_i);
284
- modified = join(member_i, old_o) || modified;
285
- console.log([old_i,old_o,member_i].map( x => [x,patternNodes[x]]));
286
- return modified;
287
- }
288
-
289
- function inspectCaret(rel) {
290
- let modified = join(rel.patterns[0], rel.caret.patterns[0]);
291
- const old_o = getRep(rel.patterns[1]);
292
- const old_o_pattern = patternNodes[old_o];
293
-
294
- modified = updatePattern(old_o_pattern, {type: "vector", closed: true});
295
-
296
- let member_o = patternEdges[old_o]["vector-member"];
297
- if (member_o == undefined ) {
298
- modified = true;
299
- member_o = patternNodes.length;
300
- patternNodes.push({ _id: member_o, code: null, type: null, closed: false})
301
- patternEdges[old_o]["vector-member"] = member_o;
302
- eq[member_o] = member_o;
303
- }
304
- member_o = getRep(member_o);
305
- modified = join(rel.caret.patterns[1], member_o) || modified;
306
- return modified;
307
- }
308
188
 
309
-
310
- function inspectDot(rel) {
311
- const old_i = getRep(rel.patterns[0]);
312
- const old_o = getRep(rel.patterns[1]);
313
- const old_i_pattern = patternNodes[old_i];
314
- const old_o_pattern = patternNodes[old_o];
315
- const field = rel.dot;
316
- if (old_i_pattern.type == "code") {
317
- if (old_i_pattern.code == "int" || old_i_pattern.code == "bool" || old_i_pattern.code == "string") {
318
- return updatePattern(old_o_pattern, {code: "product", type: "code", closed: true, product: []});
319
- }
320
- const code = codes[representatives[old_i_pattern.code] || old_i_pattern.code];
321
- if (code.code == "product" || code.code == "union" || code.code == "vector") {
322
- const target_code = code[code.code][field];
323
- if (target_code)
324
- return updatePattern(old_o_pattern, {code: target_code});
325
- }
326
- throw new Error(`Cannot find field ${field} in code ${JSON.stringify(code)}`);
327
- }
328
-
329
- return addEdge(old_i, field, old_o);
330
- }
331
-
332
- function inspectProduct(rel) {
189
+ function augmentProduct(rel,rootDef) {
190
+ rel.patterns = [];
333
191
  switch (rel.product.length) {
334
- case 0: {
335
- // unit
336
- const old_o = getRep(rel.patterns[1]);
337
- const old_o_pattern = patternNodes[old_o];
338
- // check that there is no patternEdge from old_o
339
- for (const label in patternEdges[old_o]) {
340
- throw new Error(`Label ${label} in code '{}'.`);
341
- }
342
- return updatePattern(old_o_pattern, {code: unitCode, type: "code", closed: true});
343
- }
344
- case 1: {
345
- // union constructor: %old_i { %exp_i exp %exp_o field } %old_o
346
- const field = rel.product[0].label;
347
- const exp = rel.product[0].exp;
348
- var modified = inspect(exp);
349
-
350
- const old_i = getRep(rel.patterns[0]);
351
- const old_o = getRep(rel.patterns[1]);
352
- const old_o_pattern = patternNodes[old_o];
353
-
354
- const exp_i = getRep(exp.patterns[0]);
355
- const exp_o = getRep(exp.patterns[1]);
192
+ case 0: 
193
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
194
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId(unitCode);
195
+ break;
196
+ case 1:
197
+ // union/variant constructor: %old_i { %exp_i exp %exp_o field } %old_o
198
+ rel.patterns[0] = rel.product[0].exp.patterns[0];
199
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '<...>'},
200
+ {[rel.product[0].label]: [rel.product[0].exp.patterns[1]]});
201
+ break;
202
+ default:
203
+ // product constructor %old_i { %exp0_i exp0 %exp0_o field0, ... %expk_i expk %expk_o fieldk } %old_o
204
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
205
+ rootDef.typePatternGraph.unify(
206
+ "product:input",
207
+ rel.patterns[0],
208
+ ...rel.product.map(({exp}) => exp.patterns[0]));
356
209
 
357
- var modified = join(exp_i, old_i);
358
-
359
- modified = updatePattern(old_o_pattern, {code: null, type: "union", closed: false}) || modified;
360
- modified = addEdge(old_o, field, exp_o) || modified;
361
-
362
- return modified;
363
- }
364
- }
365
- // product constructor %old_i { %exp0_i exp0 %exp0_o field0, ... %expk_i expk %expk_o fieldk } %old_o
366
- var modified = rel.product.reduce((modified, {label,exp}) =>
367
- inspect(exp) || modified, false);
368
-
369
- const fields = rel.product.map(({label, exp}) =>
370
- ({label, exp_i: getRep(exp.patterns[0]), exp_o: getRep(exp.patterns[1])}));
371
-
372
- const old_i = getRep(rel.patterns[0]);
373
- const old_o = getRep(rel.patterns[1]);
374
- const old_o_pattern = patternNodes[old_o];
375
-
376
- for (const {label, exp_i, exp_o} of fields) {
377
- modified = join(exp_i, old_i) || modified;
378
- modified = addEdge(old_o, label, exp_o) || modified;
379
- }
380
- modified = updatePattern(old_o_pattern, {type: "product", closed: true}) || modified;
381
- // check that patternEdges coincide with field labels
382
- const patternEdgeLabels = Object.keys(patternEdges[old_o]);
383
- const labels = fields.reduce( (labels,{label}) =>
384
- ({[label]: true, ...labels}), {});
385
-
386
- for (const label of patternEdgeLabels) {
387
- if (!labels[label]) {
388
- throw new Error(`Label '${label}' not allowed for pattern ${JSON.stringify({...old_o_pattern, fields})}`);
389
- }
390
- }
391
- return modified;
210
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '{}'},
211
+ rel.product.reduce((edges, {label, exp}) => {
212
+ edges[label] = [exp.patterns[1]];
213
+ return edges;
214
+ }, {})
215
+ );
216
+ };
392
217
  }
393
218
 
394
- function inspectUnion(rel) {
395
-
396
- const old_o = getRep(rel.patterns[1]);
219
+ function augmentUnion(rel,rootDef) {
220
+ rel.patterns = [];
397
221
  if (rel.union.length == 0) {
398
- return updatePattern(patternNodes[old_o], {type: "union", closed: true});
222
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
223
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '<>'});
224
+ } else {
225
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
226
+ rootDef.typePatternGraph.unify(
227
+ "union:input",
228
+ rel.patterns[0],
229
+ ...rel.union.map(exp => exp.patterns[0]));
230
+
231
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
232
+ rootDef.typePatternGraph.unify(
233
+ "union:output",
234
+ rel.patterns[1],
235
+ ...rel.union.map(exp => exp.patterns[1]));
399
236
  }
400
- // union: %old_i < %exp0_i exp0 %exp0_o, ... %expk_i expk %expk_o > %old_o
401
-
402
- const modified = rel.union.reduce((modified, exp) => {
403
- var new_modified = inspect(exp) || modified;
404
- new_modified = join(rel.patterns[0], exp.patterns[0]) || modified;
405
- new_modified = join(rel.patterns[1], exp.patterns[1]) || modified;
406
- return new_modified;
407
- }, false);
408
- return modified;
409
237
  }
410
238
 
411
- function inspectComp(rel) {
412
- if (rel.comp.length == 0) { return inspectIdentity(rel); }
413
-
414
- // comp: %old_i ( %exp0_i exp0 %exp0_o, ... %expk_i expk %expk_o ) %old_o
415
- const old_i = getRep(rel.patterns[0]);
416
- const [modified,pattern_o] = rel.comp.reduce(([modified,pattern_i], exp) => {
417
- var new_modified = inspect(exp) || modified;
418
- new_modified = join(pattern_i, exp.patterns[0]) || modified;
419
- return [new_modified, exp.patterns[1]];
420
- }, [false, old_i]);
421
- return join(pattern_o, rel.patterns[1]) || modified;
422
- }
423
-
424
- function inspectIdentity(rel) {
425
- return join(rel.patterns[0], rel.patterns[1]);
426
- }
427
-
428
- function inspectVector(rel) {
429
- const old_o = getRep(rel.patterns[1]);
430
- var modified = updatePattern(patternNodes[old_o], {type: "vector", closed: true});
431
- if (rel.vector.length == 0) {
432
- return modified;
433
- }
434
- // vector: %old_i [ %exp0_i exp0 %exp0_o, ... %expk_i expk %expk_o] %old_o
435
-
436
- let member_o = patternEdges[old_o]["vector-member"];
437
- if (member_o) {
438
- member_o = getRep(patternEdges[old_o]["vector-member"]);
239
+ function augmentComp(rel,rootDef) {
240
+ rel.patterns = [];
241
+ if (rel.comp.length == 0) {
242
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
439
243
  } else {
440
- modified = true;
441
- member_o = patternNodes.length;
442
- patternNodes.push({ _id: member_o, code: null, type: null, closed: false})
443
- patternEdges[old_o]["vector-member"] = member_o;
444
- eq[member_o] = member_o;
445
- }
446
244
 
447
- modified = rel.vector.reduce((modified, exp) => {
448
- modified = inspect(exp) || modified;
449
- modified = join(rel.patterns[0], exp.patterns[0]) || modified;
450
- modified = join(member_o, exp.patterns[1]) || modified;
451
- return modified;
452
- }, modified);
453
- return modified;
454
- }
455
-
456
- function inspectCode(rel) {
457
- const old_i = getRep(rel.patterns[0]);
458
- var modified = updatePattern(patternNodes[old_i], {code: representatives[rel.code] || rel.code});
459
- return join(old_i, rel.patterns[1]) || modified;
460
- }
461
-
462
- function inspectRef(rel) {
463
- const relDefs = rels[rel.ref];
464
- if (!relDefs) {
465
- switch (rel.ref) {
466
- case "_log!":
467
- return inspectIdentity(rel);
468
- case "true":
469
- case "false": return updatePattern(patternNodes[rel.patterns[1]], {code: "bool"});
470
-
471
- case "PLUS":
472
- case "TIMES":
473
- // should add updating the input parrent to code `$[int]`
474
- // console.log("PLUS/TIMES", rel);
475
- return updatePattern(patternNodes[rel.patterns[1]], {code: "int"});
476
- case "CONCAT":
477
- return updatePattern(patternNodes[rel.patterns[1]], {code: "string"});
478
- case "toDateMsec":
479
- return updatePattern(patternNodes[rel.patterns[1]], {code: "int"});
480
- case "toJSON":
481
- return updatePattern(patternNodes[rel.patterns[1]], {code: "string"});
482
- case "toDateStr":
483
- return updatePattern(patternNodes[rel.patterns[1]], {code: "string"});
484
-
485
- // TO DO
486
- case "GT":
487
- case "EQ":
488
- case "null":
489
- case "DIV":
490
- case "FDIV":
491
- case "fromJSON":
492
- case "CONS":
493
- case "SNOC":
494
- return false;
495
- }
496
- throw new Error(`No definition found for ${rel.ref}`);
497
- }
498
- // For now we assume only the first def which does not throw!!!!
499
- // That's good enough if all defs are typed by different input codes.
500
- for (const def of relDefs) {
501
- try {
502
- // var modified = join(def.patterns[0], rel.patterns[0]);
503
- var modified = updatePattern(
504
- patternNodes[getRep(rel.patterns[0])],
505
- patternNodes[getRep(def.patterns[0])]
245
+ for (let i = 0; i < rel.comp.length - 1; i++) {
246
+ rootDef.typePatternGraph.unify(
247
+ "comp:chain",
248
+ rel.comp[i].patterns[1],
249
+ rel.comp[i+1].patterns[0]
506
250
  );
507
-
508
- // modified = join(def.patterns[1], rel.patterns[1]) || modified;
509
- modified = updatePattern(
510
- patternNodes[getRep(rel.patterns[1])],
511
- patternNodes[getRep(def.patterns[1])]
512
- ) || modified;
513
-
514
- return modified;
515
- } catch (e) {}
516
- }
517
- throw new Error(`No matching definition found for ${rel.ref}`);
518
- }
519
-
520
-
521
- // --------- main loop --------------
522
-
523
- // 2. loop until no more changes
524
-
525
- let changed = true;
526
- while (changed) {
527
- changed = false;
528
- for (const relName in rels) {
529
- const defs = rels[relName];
530
- for (const def of defs) {
531
- changed = inspect(def) || changed;
532
251
  }
252
+
253
+ rel.patterns[0] = rel.comp[0].patterns[0];
254
+ rel.patterns[1] = rel.comp[rel.comp.length - 1].patterns[1];
533
255
  }
534
256
  }
535
257
 
536
- // 3. compress the pattern graph
537
-
538
- // 3.1. get representative nodes for each pattern
539
- // pNode = {repFromPatternNodes: 0, repFromPatternNodes: 1, ...}
540
- const pNodes = {};
541
- eq.map(getRep).forEach((p) => {
542
- pNodes[p] = p;
543
- });
544
- Object.keys(pNodes).map((p,i) => pNodes[p] = i);
258
+ function augmentVector(rel,rootDef) {
259
+ rel.patterns = [];
260
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
261
+ rootDef.typePatternGraph.unify(
262
+ "vector:input",
263
+ rel.patterns[0],
264
+ ...rel.vector.map(exp => exp.patterns[0]));
265
+
266
+ let member = rootDef.typePatternGraph.addNewNode();
267
+ rootDef.typePatternGraph.unify(
268
+ "vector:output",
269
+ member,
270
+ ...rel.vector.map(exp => exp.patterns[1])
271
+ );
272
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [member]});
273
+ }
545
274
 
546
- function updatePatterns(rel) {
547
- rel.patterns[0] = pNodes[getRep(rel.patterns[0])];
548
- rel.patterns[1] = pNodes[getRep(rel.patterns[1])];
549
- switch (rel.op) {
550
- case "product":
551
- rel["product"].forEach(({label, exp}) => updatePatterns(exp));
275
+ function augmentRef(rel,rootDef) {
276
+ rel.patterns = [];
277
+ if (rel.ref in rels) {
278
+ rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
279
+ rootDef.varRefs.push( {
280
+ varName: rel.ref,
281
+ inputPatternId: rel.patterns[0],
282
+ outputPatternId: rel.patterns[1],
283
+ start: rel.start,
284
+ end: rel.end
285
+ });
286
+ return;
287
+ }
288
+ // it is built-in
289
+ switch (rel.ref) {
290
+ case "_log!":
291
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
552
292
  break;
553
- case "union":
554
- case "comp":
555
- case "vector":
556
- rel[rel.op].forEach((exp) => updatePatterns(exp));
293
+ case "true":
294
+ case "false":
295
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
296
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('bool');
297
+ break;
298
+ case "PLUS":
299
+ case "TIMES":
300
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('int');
301
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rel.patterns[1]]});
302
+ break;
303
+ case "CONCAT":
304
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
305
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rel.patterns[1]]});
306
+ break;
307
+ case "toVEC":
308
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
309
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rel.patterns[0]]});
310
+ break;
311
+ case "toDateMsec":
312
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
313
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('int');
314
+ break;
315
+ case "toJSON":
316
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
317
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
318
+ break;
319
+ case "toDateStr":
320
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
321
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
322
+ break;
323
+ case "GT":
324
+ case "EQ":
325
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rootDef.typePatternGraph.addNewNode()]});
326
+ break;
327
+ case "fromJSON":
328
+ rel.patterns[0] = rootDef.typePatternGraph.getTypeId('string');
329
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
330
+ break;
331
+ case "CONS": {
332
+ let member = rootDef.typePatternGraph.addNewNode();
333
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [member]});
334
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '{}'}, {"car": [member], "cdr": [rel.patterns[1]]});
335
+ }; break;
336
+ case "SNOC": {
337
+ let member = rootDef.typePatternGraph.addNewNode();
338
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [member]});
339
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '{}'}, {"car": [member], "cdr": [rel.patterns[0]]});
340
+ }; break;
341
+ // TO DO
342
+ case "null":
343
+ case "DIV":
344
+ case "FDIV":
345
+ rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
346
+ break;
347
+ default:
348
+ throw new Error(`No definition found for ${rel.ref}`);
557
349
  }
558
- }
350
+ }
559
351
 
560
- for (const relName in rels) {
561
- const defs = rels[relName];
562
- for (const def of defs) { updatePatterns(def); }
563
- }
352
+ // ==================================================================================
353
+ // ----------------------------------------------------------------------------------
354
+ // 1. Initialize the typePatternGraph and varRefs for each relation
564
355
 
565
- const newPatternNodes = [];
566
- for (const i in pNodes) {
567
- newPatternNodes[pNodes[i]] = patternNodes[i];
356
+ for (const relName in rels) {
357
+ const rootDef = rels[relName];
358
+ rootDef.typePatternGraph = new TypePatternGraph();
359
+ rootDef.varRefs = []; // the list of non-built references as pointers to AST nodes
360
+ augment(rootDef.def, rootDef);
568
361
  }
569
- const newPatternEdges = Object.keys(patternEdges).reduce(function (newPatternEdges, src) {
570
- const newSrc = pNodes[src];
571
- if (newSrc != undefined) {
572
- newPatternEdges[newSrc] = Object.keys(patternEdges[src]).reduce(function (newPatternEdges, label) {
573
- newPatternEdges[label] = pNodes[getRep(patternEdges[src][label])];
574
- return newPatternEdges;
575
- }, {});
576
- }
577
- return newPatternEdges;
578
- }, {});
362
+
363
+
364
+ // 2. Compute DAG of strongly connected components in varRefs
365
+ // A rel_1 -> rel_2 if rel1,varRefs contains a reference to {op: "ref", ref: rel_2}
366
+
367
+ const graph = new Graph(
368
+ // 1st argument: edges
369
+ [].concat(...Object.keys(rels).map( relName =>
370
+ rels[relName].varRefs.map( ({varName}) =>
371
+ ({src: relName, dst: varName})))),
372
+ // 2nd argument: vertices
373
+ rels
374
+ );
375
+
376
+ const sccs_ = sccs(graph);
377
+
378
+ const DAGnodes = sccs_.reduce ((nodes, scc) =>
379
+ ({...nodes, [scc[0]]: {scc}}), {});
380
+ const fromRelNameToDAGnode = Object.keys(DAGnodes).reduce((map, node) =>
381
+ DAGnodes[node].scc.reduce((map, relName) => ({...map, [relName]: node}), map), {});
382
+ const DAGedges = [].concat(
383
+ ...Object.keys(rels).map( x =>
384
+ rels[x].varRefs.map(({ varName }) =>
385
+ ({src: fromRelNameToDAGnode[x], dst: fromRelNameToDAGnode[varName]})
386
+ ) // don't forget to remove loops
387
+ .filter(({src, dst}) => src !== dst )
388
+ )
389
+ );
390
+ const DAG = new Graph(DAGedges, DAGnodes);
391
+
392
+ // 3. Topological sort of the DAG
393
+
394
+ const sccInOrder = topoOrder(DAG);
395
+
396
+ // 4. For each strongly connected component C in a bottom-up order
397
+
398
+ for (const scc of sccInOrder.reverse()) {
399
+ // console.log(`SCC: { ${DAGnodes[scc].scc} }`);
400
+
401
+ const maxNumberOfIterations = 10;
402
+
403
+ for (let iteration = 1; iteration <= maxNumberOfIterations; iteration++) {
404
+
405
+ // console.log(`>>>>>> Iteration ${iteration} for SCC: { ${DAGnodes[scc].scc} }`);
406
+ // 4.1 For every r in C, compute the new typePatternGraph of r, i.e.:
407
+
408
+ let before = JSON.stringify(DAGnodes[scc].scc.map( relName => {
409
+ const relDef = rels[relName];
410
+ // console.log("-----------------------------------");
411
+ // console.log(relDefToString(relDef));
412
+ rels[relName] = compactRel(relDef,relName);
413
+ // console.log(relDefToString(rels[relName]));
414
+ return relDefToString(rels[relName]);
415
+ }));
416
+
417
+ let after = JSON.stringify(DAGnodes[scc].scc.map( relName => {
418
+ const relDef = compactRel(rels[relName],relName);
419
+ for (let i = 0; i < relDef.varRefs.length; i++) {
420
+ // console.log(` ####### Processing ${relName}(..x${i}=${relDef.varRefs[i].varName}..)`);
421
+ let varRel = relDef.varRefs[i];
422
+ let { varName } = varRel;
423
+ try {
424
+ let varRootDef = rels[varName];
425
+ let varInputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[0]);
426
+ let varOutputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[1]);
427
+ let cloned = varRootDef.typePatternGraph.clone([varInputPatternId, varOutputPatternId], relDef.typePatternGraph);
428
+
429
+ relDef.typePatternGraph.unify(`ref:input ${relName}(${varName})`, varRel.inputPatternId, cloned[varInputPatternId]);
430
+ relDef.typePatternGraph.unify("ref:output", varRel.outputPatternId, cloned[varOutputPatternId]);
431
+ } catch (e) {
432
+ console.error(`Type Error in call to ${varName} in definition of ${relName}: lines ${varRel.start?.line}:${varRel.start?.column}...${varRel.end?.line}:${varRel.end?.column}): ${e.message}.`);
433
+ throw e;
434
+ }
435
+ }
579
436
 
580
- // loops in the pattern graph are forcing the pattern to be union!
437
+ rels[relName] = compactRel(relDef,relName);
438
+
439
+ return relDefToString(rels[relName]);
440
+ }));
581
441
 
582
- for( const i in newPatternNodes) {
583
- const node = newPatternNodes[i];
584
- for (const label in newPatternEdges[i]) {
585
- if (newPatternEdges[i][label] == i) {
586
- if (node.type == "product") {
587
- throw new Error("Loop in product code!");
588
- }
589
- node.type = "union";
442
+ if (before == after) {
443
+ // console.log("SUCCESS!");
444
+ break;
590
445
  }
591
- }
446
+ } // while
447
+
592
448
  }
593
-
594
- return { patternNodes: newPatternNodes, patternEdges: newPatternEdges };
595
449
  }
596
450
 
597
-
598
-
599
451
  export default { patterns };
600
452
  export { patterns };
601
453