@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/patterns.mjs CHANGED
@@ -1,601 +1,512 @@
1
1
 
2
- // import { prettyCode, prettyRel } from "./pretty.mjs";
3
-
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";
4
6
 
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] }
7
+ const unitCode = codes.unitCode;
11
8
 
12
- import hash from "./hash.mjs";
9
+ function relDefToString(relDef) {
13
10
 
14
- const unitCode = hash('$C0={};'); // it was '{}'
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
+ case "filter":
31
+ return [...rel.patterns];
32
+ }
33
+ throw new Error("NOT EXPECTED OP:", JSON.stringify(rel, null, 2));
34
+ };
15
35
 
16
- function patterns(codes, representatives, rels) {
17
- // INPUT:
18
- // codes: {"BG": {"code": "product", "product": {}}, ...}
19
- // representatives:{"{}": "BG", ...}
20
- // rels: {"rlz": [{op: comp,...}, ...], ...}
36
+ // console.log("SIZE", relDef.typePatternGraph.size());
37
+ // console.log(relDef.typePatternGraph.patterns.nodes);
38
+ // console.log(relDef.typePatternGraph.edges);
39
+ // console.log(relDef.def);
21
40
 
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
+ return JSON.stringify(dumpPatterns(relDef.def).map((x) => {
42
+ const { pattern, fields, type} = relDef.typePatternGraph.get_pattern(x);
43
+ return {pattern, fields, type};
44
+ }));
45
+ }
26
46
 
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
47
+ function compactRel(relDef, name = "") {
48
+ // console.log("********************************* compacting", name);
49
+ const {def, typePatternGraph, varRefs } = relDef;
50
+ // console.log("COMPACTING typePatternGraph of size", typePatternGraph.size());
31
51
 
32
- // 1 INITIALIZATION
33
- // 1.1 initialize patternNodes and rels
52
+ const {typePatternGraph : newTypePatternGraph, remapping: renumbering} = typePatternGraph.getCompressed();
34
53
 
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);
54
+ const copyRel = (rel) => {
55
+ const newRel = {...rel, patterns:[renumbering[rel.patterns[0]], renumbering[rel.patterns[1]]]};
39
56
  switch (rel.op) {
40
57
  case "product":
41
- rel["product"].forEach(({label, exp}) => augment(exp));
58
+ newRel.product = rel.product.map(({label, exp}) => ({label, exp: copyRel(exp)}));
42
59
  break;
43
60
  case "union":
61
+ newRel.union = rel.union.map(exp => copyRel(exp));
62
+ break;
44
63
  case "comp":
45
- case "vector":
46
- rel[rel.op].forEach((exp) => augment(exp));
64
+ newRel.comp = rel.comp.map(exp => copyRel(exp));
47
65
  break;
48
- case "code":
49
- rel["code"] = representatives[rel.code] || rel.code;
66
+ case "vector":
67
+ newRel.vector = rel.vector.map(exp => copyRel(exp));
50
68
  break;
51
69
  case "caret":
52
- augment(rel.caret);
53
- break;
54
- case "filter":
55
- // do nothing for now
56
- console.log("filter ignored", rel);
70
+ newRel.caret = copyRel(rel.caret);
57
71
  break;
58
72
  case "ref":
59
- // do nothing
60
- break;
73
+ case "int":
74
+ case "str":
75
+ case "identity":
76
+ case "dot":
77
+ case "code":
61
78
  case "pipe":
79
+ case "filter":
62
80
  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
- }
81
+ default:
82
+ console.error("NOT EXPECTED OP:", JSON.stringify(rel, null, 2));
83
+ break;
84
+ };
85
+ return newRel;
86
+ };
87
+ const newDef = copyRel(def);
88
+ const newVarRefs = varRefs.map(x => ({
89
+ ...x,
90
+ inputPatternId: renumbering[x.inputPatternId],
91
+ outputPatternId: renumbering[x.outputPatternId]
92
+ }));;
93
+
94
+ // console.log(`COMPACTED ${name}.typePatternGraph`, typePatternGraph.size(), '->', newTypePatternGraph.size());
95
+ return {def: newDef, typePatternGraph: newTypePatternGraph, varRefs: newVarRefs};
96
+ }
75
97
 
76
- // console.log(JSON.stringify(rels,"",2));
98
+ function patterns(representatives, rels) {
99
+ // INPUT:
100
+ // codes: {"BG": {"code": "product", "product": {}}, ...}
101
+ // representatives:{"{}": "BG", ...}
102
+ // rels: {"rlz": [{op: comp,...}, ...], ...}
103
+
77
104
 
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 --------
105
+ // 1 INITIALIZATION
106
+ // 1.1 initialize patternNodes and rels
90
107
 
91
- function inspect(rel) {
92
- const op = rel.op;
93
- // console.log(`Inspecting ${op} ${JSON.stringify(rel)}`);
108
+ function augment(rel, rootDef) {
94
109
  try {
95
- switch (op) {
110
+ switch (rel.op) {
96
111
  case "product":
97
- return inspectProduct(rel);
112
+ rel["product"].forEach(({label, exp}) => {
113
+ augment(exp, rootDef)
114
+ });
115
+ augmentProduct(rel,rootDef);
116
+ break;
98
117
  case "union":
99
- return inspectUnion(rel);
100
- case "comp":
101
- return inspectComp(rel);
102
- case "identity":
103
- return inspectIdentity(rel);
118
+ rel["union"].forEach((exp) => {
119
+ augment(exp,rootDef);
120
+ });
121
+ augmentUnion(rel,rootDef);
122
+ break;
123
+ case "comp":
124
+ rel["comp"].forEach((exp) => {
125
+ augment(exp,rootDef);
126
+ });
127
+ augmentComp(rel,rootDef);
128
+ break;
104
129
  case "vector":
105
- return inspectVector(rel);
106
- case "dot":
107
- return inspectDot(rel);
108
- case "code":
109
- return inspectCode(rel);
130
+ rel["vector"].forEach((exp) => {
131
+ augment(exp,rootDef);
132
+ });
133
+ augmentVector(rel,rootDef);
134
+ break;
135
+ case "code":
136
+ rel["code"] = representatives[rel.code] || rel.code;
137
+ rel.patterns = [];
138
+ rel.patterns[0] = rootDef.typePatternGraph.getTypeId(rel["code"]);
139
+ rel.patterns[1] = rel.patterns[0];
140
+ break;
141
+ case "caret":
142
+ augment(rel.caret,rootDef);
143
+ rel.patterns = [
144
+ rel.caret.patterns[0],
145
+ rootDef.typePatternGraph.addNewNode(
146
+ {pattern: '[]'},
147
+ {"vector-member": [rel.caret.patterns[1]]})
148
+ ];
149
+ break;
150
+ case "pipe":
151
+ rel.patterns = [];
152
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
153
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode(
154
+ {pattern: '[]'},
155
+ {"vector-member": [rel.patterns[1]]});
156
+ break;
157
+
110
158
  case "ref":
111
- return inspectRef(rel);
159
+ augmentRef(rel, rootDef);
160
+ break;
161
+ //----------------
162
+
112
163
  case "int":
113
- return inspectInt(rel);
164
+ rel.patterns = [];
165
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
166
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('int');
167
+ break;
114
168
  case "str":
115
- return inspectStr(rel);
116
- case "caret":
117
- return inspectCaret(rel);
118
- case "pipe":
119
- return inspectPipe(rel);
169
+ rel.patterns = [];
170
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
171
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
172
+ break;
173
+ case "identity":
174
+ rel.patterns = [];
175
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
176
+ break;
177
+ case "dot":
178
+ rel.patterns = [];
179
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
180
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '(...)'}, { [rel.dot]: [rel.patterns[1]] });
181
+ break;
120
182
  case "filter":
121
- return false;
183
+ rel.patterns = [];
184
+ rel.patterns[1] = rel.patterns[0] = filterToPattern(rel.filter, rootDef);
185
+ break;
186
+ default:
187
+ console.error("NOT EXPECTED OP for aumentation:", JSON.stringify(rel, null, 2));
188
+ break;
122
189
  }
123
- throw new Error(`Unknown op: ${op}`);
124
190
  } 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}.`);
191
+ e.message = `Type Error in '${rel.op}' (lines ${rel.start?.line}:${rel.start?.column}...${rel.end?.line}:${rel.end?.column})\n - ${e.message}`;
126
192
  throw e;
127
193
  }
128
194
  }
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
195
 
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)}`);
196
+ function filterToPattern(filter, rootDef) {
197
+ const context = rootDef.patternVars;
198
+ const getFields = () =>
199
+ Object.keys(filter.fields || {}).reduce( (fields, label) => {
200
+ const patternId = filterToPattern(filter.fields[label], rootDef);
201
+ fields[label] = [patternId];
202
+ return fields;
203
+ }, {});
204
+ if (filter.name && ! (filter.name in context)) {
205
+ context[filter.name] = rootDef.typePatternGraph.addNewNode();
224
206
  }
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)}`);
207
+ let newPatternId = null;
208
+ switch (filter.type) {
209
+ case "name":
210
+ newPatternId = context[filter.name];
211
+ break;
212
+ case "code":
213
+ newPatternId = rootDef.typePatternGraph.getTypeId(filter.code);
214
+ break;
215
+ case "union":
216
+ newPatternId = rootDef.typePatternGraph.addNewNode(
217
+ {pattern: filter.open ? '<...>' : '<>', fields:Object.keys(filter.fields || {})},
218
+ getFields());
219
+ break;
220
+ case "product":
221
+ newPatternId = rootDef.typePatternGraph.addNewNode(
222
+ {pattern: filter.open ? '{...}' : '{}', fields:Object.keys(filter.fields || {})},
223
+ getFields());
224
+ break;
225
+ case "vector": {
226
+ const vector = filterToPattern(filter.vector, rootDef);
227
+ newPatternId = rootDef.typePatternGraph.addNewNode(
228
+ {pattern: '[]', fields: ["vector-member"]},
229
+ {"vector-member": [vector]});
230
+ };
231
+ break;
232
+ default:
233
+ newPatternId = rootDef.typePatternGraph.addNewNode(
234
+ {pattern: filter.open ? '(...)' : '()'},
235
+ getFields());
240
236
  }
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;
237
+ if (filter.name) {
238
+ rootDef.typePatternGraph.unify(`filter: ${filter.name}`, newPatternId, context[filter.name]);
249
239
  }
250
240
 
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});
241
+ return newPatternId;
266
242
  }
267
243
 
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
-
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) {
244
+ function augmentProduct(rel,rootDef) {
245
+ rel.patterns = [];
333
246
  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]);
247
+ case 0: 
248
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
249
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId(unitCode);
250
+ break;
251
+ case 1:
252
+ // union/variant constructor: %old_i { %exp_i exp %exp_o field } %old_o
253
+ rel.patterns[0] = rel.product[0].exp.patterns[0];
254
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '<...>'},
255
+ {[rel.product[0].label]: [rel.product[0].exp.patterns[1]]});
256
+ break;
257
+ default:
258
+ // product constructor %old_i { %exp0_i exp0 %exp0_o field0, ... %expk_i expk %expk_o fieldk } %old_o
259
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
260
+ rootDef.typePatternGraph.unify(
261
+ "product:input",
262
+ rel.patterns[0],
263
+ ...rel.product.map(({exp}) => exp.patterns[0]));
356
264
 
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;
265
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '{}'},
266
+ rel.product.reduce((edges, {label, exp}) => {
267
+ edges[label] = [exp.patterns[1]];
268
+ return edges;
269
+ }, {})
270
+ );
271
+ };
392
272
  }
393
273
 
394
- function inspectUnion(rel) {
395
-
396
- const old_o = getRep(rel.patterns[1]);
274
+ function augmentUnion(rel,rootDef) {
275
+ rel.patterns = [];
397
276
  if (rel.union.length == 0) {
398
- return updatePattern(patternNodes[old_o], {type: "union", closed: true});
277
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
278
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '<>'});
279
+ } else {
280
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
281
+ rootDef.typePatternGraph.unify(
282
+ "union:input",
283
+ rel.patterns[0],
284
+ ...rel.union.map(exp => exp.patterns[0]));
285
+
286
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
287
+ rootDef.typePatternGraph.unify(
288
+ "union:output",
289
+ rel.patterns[1],
290
+ ...rel.union.map(exp => exp.patterns[1]));
399
291
  }
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
292
  }
410
293
 
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"]);
294
+ function augmentComp(rel,rootDef) {
295
+ rel.patterns = [];
296
+ if (rel.comp.length == 0) {
297
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
439
298
  } 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
299
 
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])]
300
+ for (let i = 0; i < rel.comp.length - 1; i++) {
301
+ rootDef.typePatternGraph.unify(
302
+ "comp:chain",
303
+ rel.comp[i].patterns[1],
304
+ rel.comp[i+1].patterns[0]
506
305
  );
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
306
  }
307
+
308
+ rel.patterns[0] = rel.comp[0].patterns[0];
309
+ rel.patterns[1] = rel.comp[rel.comp.length - 1].patterns[1];
533
310
  }
534
311
  }
535
312
 
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);
313
+ function augmentVector(rel,rootDef) {
314
+ rel.patterns = [];
315
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
316
+ rootDef.typePatternGraph.unify(
317
+ "vector:input",
318
+ rel.patterns[0],
319
+ ...rel.vector.map(exp => exp.patterns[0]));
320
+
321
+ let member = rootDef.typePatternGraph.addNewNode();
322
+ rootDef.typePatternGraph.unify(
323
+ "vector:output",
324
+ member,
325
+ ...rel.vector.map(exp => exp.patterns[1])
326
+ );
327
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [member]});
328
+ }
545
329
 
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));
330
+ function augmentRef(rel,rootDef) {
331
+ rel.patterns = [];
332
+ if (rel.ref in rels) {
333
+ rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
334
+ rootDef.varRefs.push( {
335
+ varName: rel.ref,
336
+ inputPatternId: rel.patterns[0],
337
+ outputPatternId: rel.patterns[1],
338
+ start: rel.start,
339
+ end: rel.end
340
+ });
341
+ return;
342
+ }
343
+ // it is built-in
344
+ switch (rel.ref) {
345
+ case "_log!":
346
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
552
347
  break;
553
- case "union":
554
- case "comp":
555
- case "vector":
556
- rel[rel.op].forEach((exp) => updatePatterns(exp));
348
+ case "true":
349
+ case "false":
350
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
351
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('bool');
352
+ break;
353
+ case "PLUS":
354
+ case "TIMES":
355
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('int');
356
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rel.patterns[1]]});
357
+ break;
358
+ case "CONCAT":
359
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
360
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rel.patterns[1]]});
361
+ break;
362
+ case "toVEC":
363
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
364
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rel.patterns[0]]});
365
+ break;
366
+ case "toDateMsec":
367
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
368
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('int');
369
+ break;
370
+ case "toJSON":
371
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
372
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
373
+ break;
374
+ case "toDateStr":
375
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
376
+ rel.patterns[1] = rootDef.typePatternGraph.getTypeId('string');
377
+ break;
378
+ case "GT":
379
+ case "EQ":
380
+ rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [rootDef.typePatternGraph.addNewNode()]});
381
+ break;
382
+ case "fromJSON":
383
+ rel.patterns[0] = rootDef.typePatternGraph.getTypeId('string');
384
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
385
+ break;
386
+ case "CONS": {
387
+ let member = rootDef.typePatternGraph.addNewNode();
388
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [member]});
389
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '{}'}, {"car": [member], "cdr": [rel.patterns[1]]});
390
+ }; break;
391
+ case "SNOC": {
392
+ let member = rootDef.typePatternGraph.addNewNode();
393
+ rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '[]'}, {"vector-member": [member]});
394
+ rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '{}'}, {"car": [member], "cdr": [rel.patterns[0]]});
395
+ }; break;
396
+ // TO DO
397
+ case "null":
398
+ case "DIV":
399
+ case "FDIV":
400
+ rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
401
+ break;
402
+ default:
403
+ throw new Error(`No definition found for ${rel.ref}`);
557
404
  }
558
- }
405
+ }
559
406
 
560
- for (const relName in rels) {
561
- const defs = rels[relName];
562
- for (const def of defs) { updatePatterns(def); }
563
- }
407
+ // ==================================================================================
408
+ // ----------------------------------------------------------------------------------
409
+ // 1. Initialize the typePatternGraph and varRefs for each relation
564
410
 
565
- const newPatternNodes = [];
566
- for (const i in pNodes) {
567
- newPatternNodes[pNodes[i]] = patternNodes[i];
411
+ for (const relName in rels) {
412
+ const rootDef = rels[relName];
413
+ rootDef.typePatternGraph = new TypePatternGraph();
414
+ rootDef.varRefs = []; // the list of non-built references as pointers to AST nodes
415
+ rootDef.patternVars = {};
416
+ augment(rootDef.def, rootDef);
417
+ delete rootDef.patternVars;
568
418
  }
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
- }, {});
419
+
420
+
421
+ // 2. Compute DAG of strongly connected components in varRefs
422
+ // A rel_1 -> rel_2 if rel1,varRefs contains a reference to {op: "ref", ref: rel_2}
423
+
424
+ const graph = new Graph(
425
+ // 1st argument: edges
426
+ [].concat(...Object.keys(rels).map( relName =>
427
+ rels[relName].varRefs.map( ({varName}) =>
428
+ ({src: relName, dst: varName})))),
429
+ // 2nd argument: vertices
430
+ rels
431
+ );
432
+
433
+ const sccs_ = sccs(graph);
434
+
435
+ const DAGnodes = sccs_.reduce ((nodes, scc) =>
436
+ ({...nodes, [scc[0]]: {scc}}), {});
437
+ const fromRelNameToDAGnode = Object.keys(DAGnodes).reduce((map, node) =>
438
+ DAGnodes[node].scc.reduce((map, relName) => ({...map, [relName]: node}), map), {});
439
+ const DAGedges = [].concat(
440
+ ...Object.keys(rels).map( x =>
441
+ rels[x].varRefs.map(({ varName }) =>
442
+ ({src: fromRelNameToDAGnode[x], dst: fromRelNameToDAGnode[varName]})
443
+ ) // don't forget to remove loops
444
+ .filter(({src, dst}) => src !== dst )
445
+ )
446
+ );
447
+ const DAG = new Graph(DAGedges, DAGnodes);
448
+
449
+ // 3. Topological sort of the DAG
450
+
451
+ const sccInOrder = topoOrder(DAG);
452
+
453
+ // 4. For each strongly connected component C in a bottom-up order
454
+
455
+ for (const scc of sccInOrder.reverse()) {
456
+ // console.log(`SCC: { ${DAGnodes[scc].scc} }`);
457
+
458
+ const maxNumberOfIterations = 10;
459
+
460
+ for (let iteration = 1; iteration <= maxNumberOfIterations; iteration++) {
461
+
462
+ // console.log(`>>>>>> Iteration ${iteration} for SCC: { ${DAGnodes[scc].scc} }`);
463
+ // 4.1 For every r in C, compute the new typePatternGraph of r, i.e.:
464
+
465
+ let before = JSON.stringify(DAGnodes[scc].scc.map( relName => {
466
+ const relDef = rels[relName];
467
+ // console.log("-----------------------------------");
468
+ // console.log(relDefToString(relDef));
469
+ rels[relName] = compactRel(relDef,relName);
470
+ // console.log(relDefToString(rels[relName]));
471
+ return relDefToString(rels[relName]);
472
+ }));
473
+
474
+ let after = JSON.stringify(DAGnodes[scc].scc.map( relName => {
475
+ const relDef = compactRel(rels[relName],relName);
476
+ for (let i = 0; i < relDef.varRefs.length; i++) {
477
+ // console.log(` ####### Processing ${relName}(..x${i}=${relDef.varRefs[i].varName}..)`);
478
+ let varRel = relDef.varRefs[i];
479
+ let { varName } = varRel;
480
+ try {
481
+ let varRootDef = rels[varName];
482
+ let varInputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[0]);
483
+ let varOutputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[1]);
484
+ let cloned = varRootDef.typePatternGraph.clone([varInputPatternId, varOutputPatternId], relDef.typePatternGraph);
485
+
486
+ relDef.typePatternGraph.unify(`ref:input ${relName}(${varName})`, varRel.inputPatternId, cloned[varInputPatternId]);
487
+ relDef.typePatternGraph.unify("ref:output", varRel.outputPatternId, cloned[varOutputPatternId]);
488
+ } catch (e) {
489
+ e.message =
490
+ `Type Error in definition of '${relName}' at call to '${varName}': lines ${varRel.start?.line}:${varRel.start?.column}...${varRel.end?.line}:${varRel.end?.column}):
491
+ - ${e.message}`;
492
+ throw e;
493
+ }
494
+ }
579
495
 
580
- // loops in the pattern graph are forcing the pattern to be union!
496
+ rels[relName] = compactRel(relDef,relName);
497
+
498
+ return relDefToString(rels[relName]);
499
+ }));
581
500
 
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";
501
+ if (before == after) {
502
+ // console.log("SUCCESS!");
503
+ break;
590
504
  }
591
- }
505
+ } // while
506
+
592
507
  }
593
-
594
- return { patternNodes: newPatternNodes, patternEdges: newPatternEdges };
595
508
  }
596
509
 
597
-
598
-
599
510
  export default { patterns };
600
511
  export { patterns };
601
512