@fraczak/k 5.1.2 → 6.0.1
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/.codex +0 -0
- package/Code-derivation-tests/blowup.mjs +3 -3
- package/Code-derivation-tests/bnat.mjs +2 -2
- package/Code-derivation-tests/codec-witness-pattern.mjs +50 -0
- package/Code-derivation-tests/convergence-strategy.mjs +24 -0
- package/Code-derivation-tests/dot.mjs +12 -9
- package/Code-derivation-tests/filter-syntax.mjs +21 -0
- package/Code-derivation-tests/loop.mjs +8 -2
- package/Code-derivation-tests/nat.mjs +1 -1
- package/Code-derivation-tests/not-not.mjs +3 -3
- package/Code-derivation-tests/pattern-envelope.mjs +68 -0
- package/Code-derivation-tests/rlz.mjs +2 -2
- package/{BIG_PICTURE.md → DOCS/BIG_PICTURE.md} +103 -50
- package/DOCS/REPL.md +191 -0
- package/DOCS/a.html +398 -0
- package/DOCS/book/01-introduction.md +51 -0
- package/DOCS/book/02-syntax-and-values.md +111 -0
- package/DOCS/book/03-types-as-finite-automata.md +156 -0
- package/DOCS/book/04-partial-functions-and-composition.md +146 -0
- package/DOCS/book/05-typing-filters-and-normalization.md +111 -0
- package/DOCS/book-combined.html +3798 -0
- package/DOCS/book-combined.md +3745 -0
- package/DOCS/book-combined.pdf +0 -0
- package/DOCS/book-combined.tex +5682 -0
- package/DOCS/book.html +65 -0
- package/DOCS/book.md +54 -0
- package/DOCS/book.pdf +0 -0
- package/DOCS/book.tex +150 -0
- package/NAT.k~ +452 -0
- package/README.md +204 -508
- package/TypeGraph.mjs +43 -0
- package/TypePatternGraph.mjs +251 -0
- package/Value.mjs +192 -0
- package/augmentation.mjs +198 -0
- package/codecs/BINARY_FORMAT.html +471 -0
- package/codecs/BINARY_FORMAT.md +53 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +355 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.md +303 -0
- package/codecs/README.md +215 -0
- package/codecs/example-pipeline.mjs +149 -0
- package/codecs/ieee.mjs +140 -0
- package/codecs/int.mjs +104 -0
- package/codecs/json-codec.mjs +215 -0
- package/codecs/json.mjs +40 -0
- package/codecs/k-parse.mjs +90 -0
- package/codecs/k-pattern.mjs +75 -0
- package/codecs/k-print.mjs +46 -0
- package/codecs/runtime/codec.mjs +768 -0
- package/codecs/runtime/ieee-pattern.mjs +30 -0
- package/codecs/runtime/pattern-json.mjs +96 -0
- package/codecs/runtime/prefix-codec.mjs +425 -0
- package/codecs/runtime/show-value.mjs +68 -0
- package/codecs/runtime/typeFromValue.mjs +34 -0
- package/codecs/runtime/unicode-string.mjs +220 -0
- package/codecs/show.mjs +41 -0
- package/codecs/string-codec.mjs +80 -0
- package/codecs/unit.mjs +51 -0
- package/codecs/utf16.mjs +66 -0
- package/codecs/utf8.mjs +37 -0
- package/codes.mjs +146 -44
- package/compiler.mjs +111 -0
- package/convergence.mjs +197 -0
- package/core.k +197 -0
- package/export.mjs +22 -32
- package/hash.mjs +29 -31
- package/ieee.klib +0 -0
- package/index.mjs +41 -15
- package/k-vscode/.vscode/launch.json +14 -0
- package/k-vscode/README.md +65 -0
- package/k-vscode/language-configuration.json +42 -0
- package/k-vscode/package-lock.json +4176 -0
- package/k-vscode/package.json +57 -0
- package/k-vscode/snippets/k.json +92 -0
- package/k-vscode/syntaxes/k.tmLanguage.json +243 -0
- package/k.mjs +56 -54
- package/object.mjs +517 -0
- package/objects/README.md +38 -0
- package/objects/compile-lib.mjs +42 -0
- package/objects/compile.mjs +42 -0
- package/objects/decompile.mjs +32 -0
- package/package.json +13 -6
- package/parser.jison +27 -46
- package/parser.mjs +68 -100
- package/pretty.mjs +21 -40
- package/repl2.mjs +662 -0
- package/run.mjs +264 -231
- package/symbol-table.mjs +3 -17
- package/test-fingerprint.mjs +7 -7
- package/test-hash-fuzz.mjs +107 -0
- package/test-hash-normalization.mjs +134 -0
- package/test-k-object.mjs +74 -0
- package/test-repl2.mjs +106 -0
- package/test.mjs +45 -70
- package/tests.sh +10 -1
- package/type_registry/registry.mjs~ +105 -0
- package/type_registry/typeParser.jison~ +132 -0
- package/typing.mjs +129 -676
- package/unification.mjs +157 -0
- package/valueIO.mjs +54 -0
- package/valueParser.jison +143 -0
- package/valueParser.mjs +713 -0
- package/Code-derivation-tests/built-in.mjs +0 -19
- package/Code-derivation-tests/vector.mjs +0 -19
- package/Filters/bug.k +0 -3
- package/Filters/composing.k +0 -15
- package/Filters/filter.k +0 -9
- package/TODO.md +0 -53
- package/TYPING.md +0 -365
- package/k-repl.cast +0 -1913
- package/k.cast +0 -618
- package/my-objects.json +0 -6
- package/patterns.mjs +0 -543
- package/repl.mjs +0 -213
- package/test-filters.mjs +0 -45
- package/typing-test.mjs +0 -40
package/TypeGraph.mjs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export class TypePatternForest {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.nodes = [];
|
|
4
|
+
this.parent = [];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Find the root of the set containing `x`
|
|
8
|
+
find(x) {
|
|
9
|
+
if (this.nodes[x] == undefined)
|
|
10
|
+
throw new Error(`Node of id='${x}' not found in the forest`);
|
|
11
|
+
let result = x;
|
|
12
|
+
let parent = this.parent[result];
|
|
13
|
+
while (parent != undefined) {
|
|
14
|
+
parent = this.parent[result = parent];
|
|
15
|
+
if (parent == result) {
|
|
16
|
+
console.log("Can happen a loop in the forest? It shouldn't!");
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
addChildren(parentId, childrenIds) {
|
|
25
|
+
const parent = this.find(parentId);
|
|
26
|
+
childrenIds.forEach(childId => {
|
|
27
|
+
const child = this.find(childId);
|
|
28
|
+
if (this.nodes[child].pattern != 'type')
|
|
29
|
+
this.parent[child] = parent;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
addNewNode(flatTypePattern, children = []) {
|
|
34
|
+
const that = this;
|
|
35
|
+
const id = this.nodes.length;
|
|
36
|
+
// const node = {...flatTypePattern, _id: id};
|
|
37
|
+
const node = {...flatTypePattern}
|
|
38
|
+
that.nodes.push(node);
|
|
39
|
+
Array.from(new Set(children.map(that.find.bind(that))))
|
|
40
|
+
.forEach(rep => {that.parent[rep] = id});
|
|
41
|
+
return id;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { Graph } from "./Graph.mjs";
|
|
2
|
+
import { unify_two_patterns } from "./unification.mjs";
|
|
3
|
+
import { TypePatternForest } from "./TypeGraph.mjs";
|
|
4
|
+
|
|
5
|
+
function asSet(vector) {
|
|
6
|
+
return Array.from(new Set(vector));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const matchAllPattern = {pattern: '(...)', fields: []};
|
|
10
|
+
/*
|
|
11
|
+
$Pattern = <
|
|
12
|
+
string code,
|
|
13
|
+
open? product,
|
|
14
|
+
open? union,
|
|
15
|
+
open? unknown
|
|
16
|
+
>;
|
|
17
|
+
$open? = < {} open, {} closed >;
|
|
18
|
+
|
|
19
|
+
we use the string notation:
|
|
20
|
+
'(...)' | '{...}' | '<...>' | '{}' | '<>' | 'type'
|
|
21
|
+
e.g.:
|
|
22
|
+
{pattern: '<...>'}
|
|
23
|
+
{pattern: 'type', type: code_name}
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
function assertValidPatternNode(pattern, fields = {}) {
|
|
27
|
+
if (pattern.pattern == '()') {
|
|
28
|
+
throw new Error("Closed unknown pattern '()' is not supported.");
|
|
29
|
+
}
|
|
30
|
+
if (pattern.pattern == '(...)' && Object.keys(fields).length !== 0) {
|
|
31
|
+
throw new Error("Unknown-kind pattern '(...)' cannot have fields.");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class TypePatternGraph {
|
|
36
|
+
constructor(registerCodeDef, findCode) {
|
|
37
|
+
this.patterns = new TypePatternForest();
|
|
38
|
+
this.edges = []; // an edge at index i is a map {lab: asMapSet([i1,...]), ...}
|
|
39
|
+
// representing `patterns.node[i] --[lab]--> {i1:i1,...}, ...
|
|
40
|
+
this.codeId = { }; // type-name -> index
|
|
41
|
+
this.registerCodeDef = registerCodeDef;
|
|
42
|
+
this.findCode = findCode;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
turnSingletonPatternsIntoCodes() {
|
|
46
|
+
const singletonPatterns = this.findSingletonPatterns();
|
|
47
|
+
const newCodeDefs= {}
|
|
48
|
+
for(const patternId of singletonPatterns) {
|
|
49
|
+
const pattern = this.get_pattern(patternId);
|
|
50
|
+
switch (pattern.pattern) {
|
|
51
|
+
case '<>':
|
|
52
|
+
case '{}': {
|
|
53
|
+
const code = (pattern.pattern == '<>') ? 'union' : 'product';
|
|
54
|
+
const fieldsWithDest = this.edges[patternId];
|
|
55
|
+
newCodeDefs[`-${patternId}-`] = {
|
|
56
|
+
code: code,
|
|
57
|
+
[code]: Object.keys(fieldsWithDest).reduce((fields, lab) => {
|
|
58
|
+
const auxDests = Object.values(fieldsWithDest[lab]).map(id => this.find(id));
|
|
59
|
+
const destAsMapSet = auxDests.reduce( (mapSet, x) => ({...mapSet, [JSON.stringify(x)]: x}), {});
|
|
60
|
+
const dests = Object.values(destAsMapSet);
|
|
61
|
+
if (dests.length != 1) {
|
|
62
|
+
throw new Error(`Expected one destination for ${lab} in ${patternId}`);
|
|
63
|
+
}
|
|
64
|
+
const destPattern = this.get_pattern(dests[0]);
|
|
65
|
+
fields[lab] = (destPattern.pattern == 'type') ? destPattern.type : `-${dests[0]}-`;
|
|
66
|
+
return fields;
|
|
67
|
+
}, {})
|
|
68
|
+
};
|
|
69
|
+
}; break;
|
|
70
|
+
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Unexpected pattern ${JSON.stringify(pattern)}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// - add them all to 'codes'
|
|
76
|
+
const representatives = this.registerCodeDef(newCodeDefs);
|
|
77
|
+
|
|
78
|
+
for( const id of singletonPatterns) {
|
|
79
|
+
this.unify('singleton', id, this.getTypeId(representatives[`-${id}-`]));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
get_pattern(id) {
|
|
85
|
+
return this.patterns.nodes[this.find(id)];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
find(id) {
|
|
89
|
+
return this.patterns.find(id);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
findSingletonPatterns() {
|
|
93
|
+
const gNodes = this.patterns.nodes.map((x,i) => i).filter(x => this.patterns.parent[x] == undefined);
|
|
94
|
+
const gEdges = [].concat(
|
|
95
|
+
...gNodes.map(x =>
|
|
96
|
+
[].concat(
|
|
97
|
+
...Object.values(this.edges[x]).map( asMap =>
|
|
98
|
+
[].concat(...Object.values(asMap).map(y =>
|
|
99
|
+
({src: x, dst: this.find(y)}))
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const gEdgesWithoutParrallelEdges = Array.from(new Set(gEdges));
|
|
107
|
+
const gGraph = new Graph(gEdgesWithoutParrallelEdges);
|
|
108
|
+
const queue = gNodes.filter(x =>
|
|
109
|
+
this.patterns.nodes[x].pattern in {'(...)':1,'{...}':1, '<...>':1});
|
|
110
|
+
const excludedSet = new Set(queue);
|
|
111
|
+
|
|
112
|
+
while (queue.length > 0) {
|
|
113
|
+
const x = queue.pop();
|
|
114
|
+
for (const e of gGraph.dst[x] || []) {
|
|
115
|
+
const y = gGraph.edges[e].src;
|
|
116
|
+
if (excludedSet.has(y)) continue;
|
|
117
|
+
excludedSet.add(y);
|
|
118
|
+
queue.push(y);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return gNodes.filter(x => !((excludedSet.has(x)) || (this.patterns.nodes[x].pattern == 'type')));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
clone(roots,targetGraph = this) {
|
|
126
|
+
const result = {}; // mapping from original ids to cloned ids
|
|
127
|
+
const find = (x) => this.find(x);
|
|
128
|
+
let ids = [...roots];
|
|
129
|
+
while (ids.length > 0) {
|
|
130
|
+
const old_id = ids.pop();
|
|
131
|
+
const i = find(old_id);
|
|
132
|
+
if (i in result) {
|
|
133
|
+
result[old_id] = result[i];
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const cloned = (pattern => {
|
|
137
|
+
if (pattern.pattern == 'type') return targetGraph.getTypeId(pattern.type);
|
|
138
|
+
return targetGraph.patterns.addNewNode(pattern);
|
|
139
|
+
})(this.patterns.nodes[i]);
|
|
140
|
+
result[i] = cloned;
|
|
141
|
+
result[old_id] = cloned;
|
|
142
|
+
if (this.patterns.nodes[i].pattern == 'type')
|
|
143
|
+
continue;
|
|
144
|
+
const edges = this.edges[i];
|
|
145
|
+
for (const lab in edges) {
|
|
146
|
+
ids = ids.concat(Object.values(edges[lab]));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const i in result) {
|
|
150
|
+
if (i != find(i)) continue;
|
|
151
|
+
if (this.patterns.nodes[i].pattern == 'type')
|
|
152
|
+
continue;
|
|
153
|
+
const edges = this.edges[i];
|
|
154
|
+
const new_id = result[i];
|
|
155
|
+
targetGraph.edges[new_id] = {};
|
|
156
|
+
for (const lab in edges) {
|
|
157
|
+
targetGraph.edges[new_id][lab] = asSet(edges[lab].map(x => result[find(x)]));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
cloneAll(targetGraph = this) {
|
|
164
|
+
return this.clone(this.patterns.nodes.map((x,i) => i), targetGraph);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
unify_patterns(...patterns) {
|
|
168
|
+
const unifyFn = unify_two_patterns.bind(null, this.findCode);
|
|
169
|
+
return patterns.reduce(unifyFn, {pattern: '(...)', fields: []});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
unify(rule, ...ids) {
|
|
173
|
+
const find = (x) => this.find(x);
|
|
174
|
+
const reps = Array.from(new Set(ids.map(find)));
|
|
175
|
+
if (reps.length < 2) return false;
|
|
176
|
+
const rep_patters = reps.map(x => {
|
|
177
|
+
const pattern = this.get_pattern(x);
|
|
178
|
+
const fields = Object.keys(this.edges[x] || {});
|
|
179
|
+
return {...pattern, fields: fields};
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const new_pattern = (() => { try {
|
|
183
|
+
return {...this.unify_patterns(...rep_patters), rule : rule}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
e.message = `Unification rule "${rule}"\n - ${e.message}`;
|
|
186
|
+
throw e;
|
|
187
|
+
} })();
|
|
188
|
+
const new_id = (() => {
|
|
189
|
+
if (new_pattern.pattern == 'type') {
|
|
190
|
+
const id = this.getTypeId(new_pattern.type);
|
|
191
|
+
this.patterns.addChildren(id, reps);
|
|
192
|
+
return id;
|
|
193
|
+
};
|
|
194
|
+
return this.patterns.addNewNode(new_pattern, reps);
|
|
195
|
+
})();
|
|
196
|
+
// console.log(" * new ", JSON.stringify({new_id, new_pattern}));
|
|
197
|
+
// console.log(" ", JSON.stringify(rep_patters));
|
|
198
|
+
this.edges[new_id] = this.edges[new_id] || {};
|
|
199
|
+
|
|
200
|
+
for (const i of reps) {
|
|
201
|
+
const edges = this.edges[i];
|
|
202
|
+
for (const lab in edges) {
|
|
203
|
+
const dests = asSet(edges[lab].map(find));
|
|
204
|
+
this.edges[new_id][lab] = asSet([...(this.edges[new_id][lab] || []), ...dests]);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for ( const lab of Object.keys(this.edges[new_id]) ) {
|
|
209
|
+
this.unify(rule+'.', ...this.edges[new_id][lab]);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
addNewNode(pattern = matchAllPattern, fields = {}) {
|
|
216
|
+
assertValidPatternNode(pattern, fields);
|
|
217
|
+
const id = this.patterns.addNewNode(pattern);
|
|
218
|
+
const find = (x) => this.patterns.find(x);
|
|
219
|
+
this.edges[id] = {};
|
|
220
|
+
for (const [lab,dest] of Object.entries(fields)) {
|
|
221
|
+
this.edges[id][lab] = asSet(dest.map(find));
|
|
222
|
+
}
|
|
223
|
+
return id;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
getTypeId(type) {
|
|
227
|
+
if (type == undefined) throw new Error("code name cannot be 'undefined'! "); //TODO: remove after testing
|
|
228
|
+
if ( this.codeId[type] == undefined ) {
|
|
229
|
+
const code = this.findCode(type);
|
|
230
|
+
if (code == undefined)
|
|
231
|
+
throw new Error(`Type '${type}' is not defined in codes.`);
|
|
232
|
+
const typeId = this.patterns.addNewNode({pattern: 'type', type: type});
|
|
233
|
+
this.codeId[type] = typeId
|
|
234
|
+
this.edges[typeId] = {};
|
|
235
|
+
for ( const [lab, destType] of Object.entries(code[code.code]) ) {
|
|
236
|
+
const destTypeId = this.getTypeId(destType);
|
|
237
|
+
this.edges[typeId][lab] = asSet([destTypeId]);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return this.codeId[type];
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
size() {
|
|
245
|
+
const typeNodes = this.patterns.nodes.filter(x => x.pattern == 'type').length;
|
|
246
|
+
return [this.patterns.nodes.length,typeNodes];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { TypePatternGraph };
|
|
251
|
+
export default { TypePatternGraph };
|
package/Value.mjs
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
|
|
2
|
+
// const nameRE = /^[a-zA-Z0-9_][a-zA-Z0-9_?!]*$/;
|
|
3
|
+
const nameRE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
4
|
+
|
|
5
|
+
function pLabel(label) {
|
|
6
|
+
return nameRE.test(label) ? `${label}` : `${JSON.stringify(label)}`;
|
|
7
|
+
}
|
|
8
|
+
const getFields = obj => {
|
|
9
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
10
|
+
throw new Error("Not a map!");
|
|
11
|
+
}
|
|
12
|
+
return Object.keys(obj);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function fromObject(obj) {
|
|
16
|
+
const fields = getFields(obj);
|
|
17
|
+
if (fields.length === 1) {
|
|
18
|
+
const field = fields[0];
|
|
19
|
+
return new Variant(field, fromObject(obj[field]));
|
|
20
|
+
}
|
|
21
|
+
return new Product(
|
|
22
|
+
fields.reduce( (result, field) => {
|
|
23
|
+
result[field] = fromObject(obj[field]);
|
|
24
|
+
return result;
|
|
25
|
+
}, {})
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
class Value {
|
|
30
|
+
constructor(type, pattern = null) {
|
|
31
|
+
this.type = type;
|
|
32
|
+
this.pattern = pattern;
|
|
33
|
+
}
|
|
34
|
+
toString() {
|
|
35
|
+
return `${this.constructor.name}(type: ${this.type})`;
|
|
36
|
+
}
|
|
37
|
+
toJSON() {
|
|
38
|
+
return { type: this.type };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function toVector(m) {
|
|
43
|
+
let vector = [];
|
|
44
|
+
for (let i = 0; i < Object.keys(m).length; i++) {
|
|
45
|
+
if (m[i] === undefined) {
|
|
46
|
+
vector = [];
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
vector.push(m[i]);
|
|
50
|
+
}
|
|
51
|
+
return vector;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function cloneSubpattern(pattern, root = 0) {
|
|
55
|
+
if (!pattern || pattern.length === 0) return null;
|
|
56
|
+
const visited = new Map();
|
|
57
|
+
const nodes = [];
|
|
58
|
+
|
|
59
|
+
function visit(nodeId) {
|
|
60
|
+
if (visited.has(nodeId)) return visited.get(nodeId);
|
|
61
|
+
const newId = nodes.length;
|
|
62
|
+
visited.set(nodeId, newId);
|
|
63
|
+
const [kind, edges] = pattern[nodeId];
|
|
64
|
+
nodes.push([kind, []]);
|
|
65
|
+
nodes[newId][1] = edges.map(([label, target]) => [label, visit(target)]);
|
|
66
|
+
return newId;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
visit(root);
|
|
70
|
+
return nodes;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function edgeSubpattern(pattern, label) {
|
|
74
|
+
if (!pattern || pattern.length === 0) return null;
|
|
75
|
+
const [, edges] = pattern[0];
|
|
76
|
+
const edge = edges.find(([edgeLabel]) => edgeLabel === label);
|
|
77
|
+
if (!edge) return null;
|
|
78
|
+
return cloneSubpattern(pattern, edge[1]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function composePattern(kind, entries) {
|
|
82
|
+
if (entries.some(([, childPattern]) => !childPattern)) return null;
|
|
83
|
+
const result = [[kind, []]];
|
|
84
|
+
|
|
85
|
+
for (const [label, childPattern] of entries) {
|
|
86
|
+
const offset = result.length;
|
|
87
|
+
result[0][1].push([label, offset]);
|
|
88
|
+
for (const [childKind, childEdges] of childPattern) {
|
|
89
|
+
result.push([
|
|
90
|
+
childKind,
|
|
91
|
+
childEdges.map(([edgeLabel, target]) => [edgeLabel, target + offset])
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function mergePatterns(declared, observed) {
|
|
100
|
+
if (!declared || declared.length === 0) return observed;
|
|
101
|
+
if (!observed || observed.length === 0) return declared;
|
|
102
|
+
const nodes = [];
|
|
103
|
+
const cloneMemo = new Map();
|
|
104
|
+
const mergeMemo = new Map();
|
|
105
|
+
|
|
106
|
+
function cloneInto(sourceTag, pattern, nodeId) {
|
|
107
|
+
const key = `${sourceTag}:${nodeId}`;
|
|
108
|
+
if (cloneMemo.has(key)) return cloneMemo.get(key);
|
|
109
|
+
const newId = nodes.length;
|
|
110
|
+
cloneMemo.set(key, newId);
|
|
111
|
+
const [kind, edges] = pattern[nodeId];
|
|
112
|
+
nodes.push([kind, []]);
|
|
113
|
+
nodes[newId][1] = edges.map(([label, target]) => [label, cloneInto(sourceTag, pattern, target)]);
|
|
114
|
+
return newId;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function visit(declaredId, observedId) {
|
|
118
|
+
if (declaredId == null) return cloneInto("o", observed, observedId);
|
|
119
|
+
if (observedId == null) return cloneInto("d", declared, declaredId);
|
|
120
|
+
|
|
121
|
+
const key = `${declaredId}|${observedId}`;
|
|
122
|
+
if (mergeMemo.has(key)) return mergeMemo.get(key);
|
|
123
|
+
|
|
124
|
+
const [declaredKind, declaredEdges] = declared[declaredId];
|
|
125
|
+
const [observedKind, observedEdges] = observed[observedId];
|
|
126
|
+
|
|
127
|
+
if (declaredKind === "any") return cloneInto("o", observed, observedId);
|
|
128
|
+
if (observedKind === "any") return cloneInto("d", declared, declaredId);
|
|
129
|
+
if (declaredKind !== observedKind) return cloneInto("d", declared, declaredId);
|
|
130
|
+
|
|
131
|
+
const newId = nodes.length;
|
|
132
|
+
mergeMemo.set(key, newId);
|
|
133
|
+
nodes.push([declaredKind, []]);
|
|
134
|
+
|
|
135
|
+
nodes[newId][1] = declaredEdges.map(([label, declaredTarget]) => {
|
|
136
|
+
const observedEdge = observedEdges.find(([edgeLabel]) => edgeLabel === label);
|
|
137
|
+
return [label, visit(declaredTarget, observedEdge ? observedEdge[1] : null)];
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return newId;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
visit(0, 0);
|
|
144
|
+
return nodes;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function withPattern(value, pattern) {
|
|
148
|
+
if (!pattern) return value;
|
|
149
|
+
if (value.pattern === pattern) return value;
|
|
150
|
+
if (value instanceof Product) return new Product(value.product, pattern);
|
|
151
|
+
if (value instanceof Variant) return new Variant(value.tag, value.value, pattern);
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
class Product extends Value {
|
|
156
|
+
constructor(product, pattern = null) {
|
|
157
|
+
super("{}", pattern);
|
|
158
|
+
this.product = product;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
toString() {
|
|
162
|
+
return `{${Object.entries(this.product).map(([k, v]) => `${JSON.stringify(k)}:${v.toString()}`).join(',')}}`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
toJSON() {
|
|
166
|
+
let vector = toVector(this.product);
|
|
167
|
+
if (vector.length > 0) return vector;
|
|
168
|
+
return this.product;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
class Variant extends Value {
|
|
173
|
+
constructor(tag, value, pattern = null) {
|
|
174
|
+
super("<>", pattern);
|
|
175
|
+
this.tag = tag;
|
|
176
|
+
this.value = value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
toString() {
|
|
180
|
+
// return `{${JSON.stringify(this.tag)}:${this.value.toString()}}`;
|
|
181
|
+
return `${this.value.toString()}|${pLabel(this.tag)}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
toJSON() {
|
|
185
|
+
if (this.value instanceof Product && Object.keys(this.value.product).length === 0) {
|
|
186
|
+
return this.tag;
|
|
187
|
+
}
|
|
188
|
+
return {[this.tag]: this.value.toJSON()};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
export { Value, Product, Variant, fromObject, cloneSubpattern, edgeSubpattern, composePattern, mergePatterns, withPattern };
|
|
192
|
+
export default { Value, Product, Variant, fromObject, cloneSubpattern, edgeSubpattern, composePattern, mergePatterns, withPattern };
|
package/augmentation.mjs
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import codes from "./codes.mjs";
|
|
2
|
+
|
|
3
|
+
const unitCode = codes.unitCode;
|
|
4
|
+
|
|
5
|
+
export function augment(rel, rootDef, rels, representatives) {
|
|
6
|
+
try {
|
|
7
|
+
switch (rel.op) {
|
|
8
|
+
case "product":
|
|
9
|
+
rel["product"].forEach(({label, exp}) => {
|
|
10
|
+
augment(exp, rootDef, rels, representatives)
|
|
11
|
+
});
|
|
12
|
+
augmentProduct(rel,rootDef);
|
|
13
|
+
break;
|
|
14
|
+
case "union":
|
|
15
|
+
rel["union"].forEach((exp) => {
|
|
16
|
+
augment(exp,rootDef, rels, representatives);
|
|
17
|
+
});
|
|
18
|
+
augmentUnion(rel,rootDef);
|
|
19
|
+
break;
|
|
20
|
+
case "comp":
|
|
21
|
+
rel["comp"].forEach((exp) => {
|
|
22
|
+
augment(exp,rootDef, rels, representatives);
|
|
23
|
+
});
|
|
24
|
+
augmentComp(rel,rootDef);
|
|
25
|
+
break;
|
|
26
|
+
case "code":
|
|
27
|
+
rel["code"] = representatives[rel.code] || rel.code;
|
|
28
|
+
rel.patterns = [];
|
|
29
|
+
rel.patterns[0] = rootDef.typePatternGraph.getTypeId(rel["code"]);
|
|
30
|
+
rel.patterns[1] = rel.patterns[0];
|
|
31
|
+
break;
|
|
32
|
+
case "ref":
|
|
33
|
+
augmentRef(rel, rootDef, rels);
|
|
34
|
+
break;
|
|
35
|
+
//----------------
|
|
36
|
+
case "identity":
|
|
37
|
+
rel.patterns = [];
|
|
38
|
+
rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
39
|
+
break;
|
|
40
|
+
case "dot":
|
|
41
|
+
rel.patterns = [];
|
|
42
|
+
rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
43
|
+
rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '{...}', fields: [rel.dot]}, { [rel.dot]: [rel.patterns[1]] });
|
|
44
|
+
break;
|
|
45
|
+
case "div":
|
|
46
|
+
rel.patterns = [];
|
|
47
|
+
rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
48
|
+
rel.patterns[0] = rootDef.typePatternGraph.addNewNode({pattern: '<...>', fields: [rel.div]}, { [rel.div]: [rel.patterns[1]] });
|
|
49
|
+
//console.log(`AUGMENTED DIV: patterns=${JSON.stringify(rel.patterns.map(x => rootDef.typePatternGraph.get_pattern(x)))}`);
|
|
50
|
+
break;
|
|
51
|
+
case "vid":
|
|
52
|
+
rel.patterns = [];
|
|
53
|
+
rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
|
|
54
|
+
rel.patterns[1] = rootDef.typePatternGraph.addNewNode({pattern: '<...>', fields: [rel.vid]}, { [rel.vid]: [rel.patterns[0]] });
|
|
55
|
+
break;
|
|
56
|
+
case "filter":
|
|
57
|
+
rel.patterns = [];
|
|
58
|
+
rel.patterns[1] = rel.patterns[0] = filterToPattern(rel.filter, rootDef, representatives);
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
console.error("NOT EXPECTED OP for augmentation:", JSON.stringify(rel, null, 2));
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
} catch (e) {
|
|
65
|
+
e.message = `Type Error in '${rel.op}' (lines ${rel.start?.line}:${rel.start?.column}...${rel.end?.line}:${rel.end?.column})\n - ${e.message}`;
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function filterToPattern(filter, rootDef, representatives) {
|
|
71
|
+
const context = rootDef.patternVars;
|
|
72
|
+
const getFields = () =>
|
|
73
|
+
Object.keys(filter.fields || {}).reduce( (fields, label) => {
|
|
74
|
+
const patternId = filterToPattern(filter.fields[label], rootDef, representatives);
|
|
75
|
+
fields[label] = [patternId];
|
|
76
|
+
return fields;
|
|
77
|
+
}, {});
|
|
78
|
+
if (filter.name && ! (filter.name in context)) {
|
|
79
|
+
context[filter.name] = rootDef.typePatternGraph.addNewNode();
|
|
80
|
+
}
|
|
81
|
+
let newPatternId = null;
|
|
82
|
+
switch (filter.type) {
|
|
83
|
+
case "name":
|
|
84
|
+
newPatternId = context[filter.name];
|
|
85
|
+
break;
|
|
86
|
+
case "code":
|
|
87
|
+
newPatternId = rootDef.typePatternGraph.getTypeId(representatives[filter.code] || filter.code);
|
|
88
|
+
break;
|
|
89
|
+
case "union":
|
|
90
|
+
newPatternId = rootDef.typePatternGraph.addNewNode(
|
|
91
|
+
{pattern: filter.open ? '<...>' : '<>', fields: Object.keys(filter.fields || {})},
|
|
92
|
+
getFields());
|
|
93
|
+
break;
|
|
94
|
+
case "product":
|
|
95
|
+
newPatternId = rootDef.typePatternGraph.addNewNode(
|
|
96
|
+
{pattern: filter.open ? '{...}' : '{}', fields: Object.keys(filter.fields || {})},
|
|
97
|
+
getFields());
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
if (!filter.open || Object.keys(filter.fields || {}).length !== 0) {
|
|
101
|
+
throw new Error("Unknown-kind filters must be exactly '(...)'. Use '{}'/'<>' for closed product/union or '{...}'/'<...>' for open product/union.");
|
|
102
|
+
}
|
|
103
|
+
newPatternId = rootDef.typePatternGraph.addNewNode({pattern: '(...)'});
|
|
104
|
+
}
|
|
105
|
+
if (filter.name) {
|
|
106
|
+
rootDef.typePatternGraph.unify(`filter: ${filter.name}`, newPatternId, context[filter.name]);
|
|
107
|
+
}
|
|
108
|
+
return newPatternId;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function augmentProduct(rel,rootDef) {
|
|
112
|
+
rel.patterns = [];
|
|
113
|
+
if (rel.product.length == 0) {
|
|
114
|
+
rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
|
|
115
|
+
rel.patterns[1] = rootDef.typePatternGraph.getTypeId(unitCode);
|
|
116
|
+
} else {
|
|
117
|
+
// product constructor %old_i { %exp0_i exp0 %exp0_o field0, ... %expk_i expk %expk_o fieldk } %old_o
|
|
118
|
+
rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
|
|
119
|
+
rootDef.typePatternGraph.unify(
|
|
120
|
+
"product:input",
|
|
121
|
+
rel.patterns[0],
|
|
122
|
+
...rel.product.map(({exp}) => exp.patterns[0]));
|
|
123
|
+
|
|
124
|
+
rel.patterns[1] = rootDef.typePatternGraph.addNewNode(
|
|
125
|
+
{pattern: '{}', fields: rel.product.map(({label}) => label)},
|
|
126
|
+
rel.product.reduce((edges, {label, exp}) => {
|
|
127
|
+
edges[label] = [ rootDef.typePatternGraph.find(exp.patterns[1])];
|
|
128
|
+
return edges;
|
|
129
|
+
}, {})
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function augmentUnion(rel,rootDef) {
|
|
135
|
+
rel.patterns = [];
|
|
136
|
+
if (rel.union.length == 0) {
|
|
137
|
+
rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
|
|
138
|
+
rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
139
|
+
} else {
|
|
140
|
+
rel.patterns[0] = rootDef.typePatternGraph.addNewNode();
|
|
141
|
+
rootDef.typePatternGraph.unify(
|
|
142
|
+
"union:input",
|
|
143
|
+
rel.patterns[0],
|
|
144
|
+
...rel.union.map(exp => exp.patterns[0]));
|
|
145
|
+
|
|
146
|
+
rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
147
|
+
rootDef.typePatternGraph.unify(
|
|
148
|
+
"union:output",
|
|
149
|
+
rel.patterns[1],
|
|
150
|
+
...rel.union.map(exp => exp.patterns[1]));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function augmentComp(rel,rootDef) {
|
|
155
|
+
// console.log(`AUGMENTING COMP: ${JSON.stringify(rel.comp)}`);
|
|
156
|
+
rel.patterns = [];
|
|
157
|
+
if (rel.comp.length == 0) {
|
|
158
|
+
rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
159
|
+
} else {
|
|
160
|
+
for (let i = 0; i < rel.comp.length - 1; i++) {
|
|
161
|
+
rootDef.typePatternGraph.unify(
|
|
162
|
+
"comp:chain",
|
|
163
|
+
rel.comp[i].patterns[1],
|
|
164
|
+
rel.comp[i+1].patterns[0]
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
rel.patterns[0] = rel.comp[0].patterns[0];
|
|
169
|
+
rel.patterns[1] = rel.comp[rel.comp.length - 1].patterns[1];
|
|
170
|
+
}
|
|
171
|
+
// console.log(`AUGMENTED COMP: patterns=${JSON.stringify(rel.patterns.map(i => {
|
|
172
|
+
// const x = rootDef.typePatternGraph.find(i);
|
|
173
|
+
// return [x,rootDef.typePatternGraph.get_pattern(x)];
|
|
174
|
+
// }))}`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function augmentRef(rel,rootDef,rels) {
|
|
178
|
+
rel.patterns = [];
|
|
179
|
+
if (rel.ref in rels) {
|
|
180
|
+
rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
|
|
181
|
+
rootDef.varRefs.push( {
|
|
182
|
+
varName: rel.ref,
|
|
183
|
+
inputPatternId: rel.patterns[0],
|
|
184
|
+
outputPatternId: rel.patterns[1],
|
|
185
|
+
start: rel.start,
|
|
186
|
+
end: rel.end
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
// it is built-in
|
|
191
|
+
switch (rel.ref) {
|
|
192
|
+
case "_log!":
|
|
193
|
+
rel.patterns[0] = rel.patterns[1] = rootDef.typePatternGraph.addNewNode();
|
|
194
|
+
break;
|
|
195
|
+
default:
|
|
196
|
+
throw new Error(`No definition found for '${rel.ref}'`);
|
|
197
|
+
}
|
|
198
|
+
}
|