@fraczak/k 2.1.6 → 3.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/BIG_PICTURE.md +211 -0
- package/Code-derivation-tests/bnat.mjs +10 -0
- package/Code-derivation-tests/built-in.mjs +19 -0
- package/Code-derivation-tests/dot.mjs +15 -0
- package/Code-derivation-tests/index.mjs +20 -0
- package/Code-derivation-tests/loop.mjs +11 -0
- package/Code-derivation-tests/nat.mjs +10 -0
- package/Code-derivation-tests/not-not.mjs +14 -0
- package/Code-derivation-tests/product.mjs +21 -0
- package/Code-derivation-tests/rlz.mjs +14 -0
- package/Code-derivation-tests/vector.mjs +28 -0
- package/INTRO.md +88 -0
- package/README.md +68 -14
- package/TODO.md +54 -0
- package/TYPES.md +32 -0
- package/bug.json +419 -0
- package/chatGPT.md +235 -0
- package/chatGPT.md~ +119 -0
- package/code-resolution.md +121 -0
- package/code-resolution.mjs +51 -0
- package/codes.mjs +59 -4
- package/filters.mjs +59 -0
- package/hash.mjs +39 -0
- package/index.mjs +55 -12
- package/package.json +2 -2
- package/parser.jison +111 -44
- package/parser.mjs +146 -88
- package/patterns.mjs +601 -0
- package/pretty.mjs +27 -0
- package/repl.mjs +35 -6
- package/run.mjs +4 -1
- package/symbol-table.mjs +1 -1
- package/test-filters.mjs +45 -0
- package/test-fingerprint.mjs +38 -0
- package/test.mjs +1 -43
package/patterns.mjs
ADDED
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
|
|
2
|
+
// import { prettyCode, prettyRel } from "./pretty.mjs";
|
|
3
|
+
|
|
4
|
+
|
|
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] }
|
|
11
|
+
|
|
12
|
+
import hash from "./hash.mjs";
|
|
13
|
+
|
|
14
|
+
const unitCode = hash('$C0={};'); // it was '{}'
|
|
15
|
+
|
|
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]"
|
|
26
|
+
|
|
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
|
|
31
|
+
|
|
32
|
+
// 1 INITIALIZATION
|
|
33
|
+
// 1.1 initialize patternNodes and rels
|
|
34
|
+
|
|
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);
|
|
39
|
+
switch (rel.op) {
|
|
40
|
+
case "product":
|
|
41
|
+
rel["product"].forEach(({label, exp}) => augment(exp));
|
|
42
|
+
break;
|
|
43
|
+
case "union":
|
|
44
|
+
case "comp":
|
|
45
|
+
case "vector":
|
|
46
|
+
rel[rel.op].forEach((exp) => augment(exp));
|
|
47
|
+
break;
|
|
48
|
+
case "code":
|
|
49
|
+
rel["code"] = representatives[rel.code] || rel.code;
|
|
50
|
+
break;
|
|
51
|
+
case "caret":
|
|
52
|
+
augment(rel.caret);
|
|
53
|
+
break;
|
|
54
|
+
case "filter":
|
|
55
|
+
// do nothing for now
|
|
56
|
+
console.log("filter ignored", rel);
|
|
57
|
+
break;
|
|
58
|
+
case "ref":
|
|
59
|
+
// do nothing
|
|
60
|
+
break;
|
|
61
|
+
case "pipe":
|
|
62
|
+
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
|
+
}
|
|
75
|
+
|
|
76
|
+
// console.log(JSON.stringify(rels,"",2));
|
|
77
|
+
|
|
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 --------
|
|
90
|
+
|
|
91
|
+
function inspect(rel) {
|
|
92
|
+
const op = rel.op;
|
|
93
|
+
// console.log(`Inspecting ${op} ${JSON.stringify(rel)}`);
|
|
94
|
+
try {
|
|
95
|
+
switch (op) {
|
|
96
|
+
case "product":
|
|
97
|
+
return inspectProduct(rel);
|
|
98
|
+
case "union":
|
|
99
|
+
return inspectUnion(rel);
|
|
100
|
+
case "comp":
|
|
101
|
+
return inspectComp(rel);
|
|
102
|
+
case "identity":
|
|
103
|
+
return inspectIdentity(rel);
|
|
104
|
+
case "vector":
|
|
105
|
+
return inspectVector(rel);
|
|
106
|
+
case "dot":
|
|
107
|
+
return inspectDot(rel);
|
|
108
|
+
case "code":
|
|
109
|
+
return inspectCode(rel);
|
|
110
|
+
case "ref":
|
|
111
|
+
return inspectRef(rel);
|
|
112
|
+
case "int":
|
|
113
|
+
return inspectInt(rel);
|
|
114
|
+
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;
|
|
122
|
+
}
|
|
123
|
+
throw new Error(`Unknown op: ${op}`);
|
|
124
|
+
} 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}.`);
|
|
126
|
+
throw e;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
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
|
+
|
|
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) {
|
|
333
|
+
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]);
|
|
356
|
+
|
|
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;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function inspectUnion(rel) {
|
|
395
|
+
|
|
396
|
+
const old_o = getRep(rel.patterns[1]);
|
|
397
|
+
if (rel.union.length == 0) {
|
|
398
|
+
return updatePattern(patternNodes[old_o], {type: "union", closed: true});
|
|
399
|
+
}
|
|
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
|
+
}
|
|
410
|
+
|
|
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"]);
|
|
439
|
+
} 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
|
+
|
|
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])]
|
|
506
|
+
);
|
|
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
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
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);
|
|
545
|
+
|
|
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));
|
|
552
|
+
break;
|
|
553
|
+
case "union":
|
|
554
|
+
case "comp":
|
|
555
|
+
case "vector":
|
|
556
|
+
rel[rel.op].forEach((exp) => updatePatterns(exp));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
for (const relName in rels) {
|
|
561
|
+
const defs = rels[relName];
|
|
562
|
+
for (const def of defs) { updatePatterns(def); }
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const newPatternNodes = [];
|
|
566
|
+
for (const i in pNodes) {
|
|
567
|
+
newPatternNodes[pNodes[i]] = patternNodes[i];
|
|
568
|
+
}
|
|
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
|
+
}, {});
|
|
579
|
+
|
|
580
|
+
// loops in the pattern graph are forcing the pattern to be union!
|
|
581
|
+
|
|
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";
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return { patternNodes: newPatternNodes, patternEdges: newPatternEdges };
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
export default { patterns };
|
|
600
|
+
export { patterns };
|
|
601
|
+
|
package/pretty.mjs
CHANGED
|
@@ -43,10 +43,37 @@ function prettyCode (codes, representatives, codeExp) {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
+
function prettyFilter (prettyCode, filter) {
|
|
47
|
+
const fieldsStr = (f) =>
|
|
48
|
+
Object.keys(f.fields).map( (key) =>
|
|
49
|
+
`${key}: ${prettyFilter(prettyCode, f.fields[key])}`
|
|
50
|
+
).join(", ") + (f.open ? ", ..." : "");
|
|
51
|
+
switch (filter.type) {
|
|
52
|
+
case "name":
|
|
53
|
+
return filter.name;
|
|
54
|
+
case "code":
|
|
55
|
+
return `$${prettyCode({
|
|
56
|
+
code: "ref",
|
|
57
|
+
ref: filter.code,
|
|
58
|
+
})}`;
|
|
59
|
+
case "vector":
|
|
60
|
+
return `[${prettyFilter(prettyCode, filter.vector)}]`;
|
|
61
|
+
case null:
|
|
62
|
+
return `(${fieldsStr(filter)})`;
|
|
63
|
+
case "union":
|
|
64
|
+
return `<${fieldsStr(filter)}>`;
|
|
65
|
+
case "product":
|
|
66
|
+
return `{${fieldsStr(filter)}}`;
|
|
67
|
+
}
|
|
68
|
+
throw new Error("unreachable");
|
|
69
|
+
}
|
|
70
|
+
|
|
46
71
|
function prettyRel (prettyCode, exp) {
|
|
47
72
|
"use strict";
|
|
48
73
|
const pretty = (exp) => {
|
|
49
74
|
switch (exp.op) {
|
|
75
|
+
case "filter":
|
|
76
|
+
return `?${prettyFilter(prettyCode, exp.filter)}`;
|
|
50
77
|
case "pipe":
|
|
51
78
|
return `|`;
|
|
52
79
|
case "caret":
|