@fraczak/k 4.0.4 → 5.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/BIG_PICTURE.md +28 -21
- package/TODO.md +18 -0
- package/TYPING.md +2 -2
- package/export.mjs +177 -0
- package/index.mjs +3 -15
- package/package.json +1 -1
- package/patterns.mjs +22 -8
- package/pretty.mjs +26 -41
- package/repl.mjs +45 -25
- package/run.mjs +2 -0
- package/k-repl.cast~ +0 -1939
package/BIG_PICTURE.md
CHANGED
|
@@ -25,8 +25,10 @@ Types are defined by two constructs:
|
|
|
25
25
|
|
|
26
26
|
Given a finite (possibly empty) set of types `t1, t2, ..., tn` and a set of pairwise different tags (labels) `l1, l2, ..., ln`,
|
|
27
27
|
|
|
28
|
-
- by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n`
|
|
29
|
-
|
|
28
|
+
- by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n`
|
|
29
|
+
_projection_ functions: `.l1`, `.l2`, `...`, `.ln`.
|
|
30
|
+
Each projection function is mapping a value of type `{t1 l1, t2 l2, ..., tn ln}`
|
|
31
|
+
into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
30
32
|
- by `< t1 l1, t2 l2, ..., tn ln >`, we define a _union type_ with exactly `n` projection functions `.l1`, `.l2`, `...`, `.ln`,
|
|
31
33
|
mapping a value of type `<t1 l1, t2 l2, ..., tn ln>` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
32
34
|
|
|
@@ -38,8 +40,8 @@ Some properties of these types:
|
|
|
38
40
|
|
|
39
41
|
1. Singleton product and singleton union types, `{ t label }` and `< t label >`, are equivalent as they have
|
|
40
42
|
the same set of values (and the same projection function `.label`). That's why, we not allow singleton product nor
|
|
41
|
-
singleton union types in the syntax of the language. The other benefit is that we will be able to use the same
|
|
42
|
-
for values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
|
|
43
|
+
singleton union types in the syntax of the language. The other benefit is that we will be able to use the same syntax
|
|
44
|
+
for writing out values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
|
|
43
45
|
as a union type value (a variant).
|
|
44
46
|
2. The empty product type `{}` has a single value, called the _unit_ and also denoted by `{}`, which has no fields.
|
|
45
47
|
3. The empty union type `<>` has no values.
|
|
@@ -50,7 +52,7 @@ A _partial function_ is a function that is not defined for every value of its do
|
|
|
50
52
|
|
|
51
53
|
There are three ways of combining partial functions:
|
|
52
54
|
|
|
53
|
-
1. __Composition__: we write a composition of `f` and `g` as `(f g)`, meaning that if `f` is defined on
|
|
55
|
+
1. __Composition__: we write a composition of `f` and `g` as `(f g)`, meaning that if `f` is defined on value `x` producing `y`,
|
|
54
56
|
and `g` is defined on value `y` producing `z`, then `(f g)` is defined on value `x` producing `z`.
|
|
55
57
|
2. __Union__: we write `< f1, f2 >` for a union of two functions `f1` and `f2`, meaning that
|
|
56
58
|
if `f1` is defined on value `x` producing `y`, then `< f1, f2 >`
|
|
@@ -59,7 +61,7 @@ There are three ways of combining partial functions:
|
|
|
59
61
|
are both defined on value `x` producing `y1` and `y2`, respectively, then `{f1 lab1, f2 lab2 }`
|
|
60
62
|
is defined on value `x` producing `{y1 lab1, y2 lab2}`. Otherwise, `{f1 lab1, f2 lab2 }` is not defined for `x`.
|
|
61
63
|
|
|
62
|
-
Actually,
|
|
64
|
+
Actually, composition, union, and product combine any number of partial functions, not only two. For example,
|
|
63
65
|
empty composition, `()`, defines the identity function.
|
|
64
66
|
|
|
65
67
|
### Program
|
|
@@ -78,16 +80,21 @@ type_label_list ::= /* empty */ | ( type_expression label_name ',' ) + type_expr
|
|
|
78
80
|
type_expression ::= type_name | type
|
|
79
81
|
|
|
80
82
|
function_definition ::= function_name '=' expression ';'
|
|
81
|
-
expression ::= projection | typing | composition | union | product | function_name
|
|
83
|
+
expression ::= projection | typing | filter | composition | union | product | function_name
|
|
82
84
|
projection ::= '.' label_name
|
|
83
|
-
typing ::= '$'
|
|
85
|
+
typing ::= '$' type_expression
|
|
86
|
+
filter ::= '?' filter_expression
|
|
87
|
+
filter_expression ::= $ type_expression | filter_name
|
|
88
|
+
| '{' filter_label_list '}' | '<' filter_label_list '>' | '(' filter_label_list ')'
|
|
89
|
+
| filter_expression '=' filter_name
|
|
90
|
+
filter_label_list ::= /* empty */ | ( filter_expression label_name ',' ) * filter_expression label
|
|
84
91
|
composition ::= '(' expression * ')'
|
|
85
92
|
union ::= '<' (expression ',') + expression '>'
|
|
86
93
|
product ::= '{' expression_label_list '}'
|
|
87
94
|
expression_label_list ::= /* empty */ | ( expression label_name ',' ) * expression label
|
|
88
95
|
```
|
|
89
96
|
|
|
90
|
-
We assume that the `type_name`, `function_name`, and `label_name` are identifiers (strings) such that:
|
|
97
|
+
We assume that in a `kernel-core` program the `type_name`, `function_name`, and `label_name` are identifiers (strings) such that:
|
|
91
98
|
|
|
92
99
|
1. All defined type names are distinct.
|
|
93
100
|
2. All defined function names are distinct.
|
|
@@ -97,6 +104,7 @@ The `typing` expression acts as a type annotation and can be seen as a 'filter',
|
|
|
97
104
|
function defined only for the values of the corresponding type.
|
|
98
105
|
|
|
99
106
|
That's it.
|
|
107
|
+
|
|
100
108
|
No `builtin` types, no `if` statement, no `loop`, no `throw`, no _closure_, no _annotations_, and no macros.
|
|
101
109
|
Just types and partial functions.
|
|
102
110
|
|
|
@@ -104,22 +112,22 @@ Just types and partial functions.
|
|
|
104
112
|
|
|
105
113
|
All type names are replaced by the hash of their canonical representation.
|
|
106
114
|
|
|
107
|
-
We
|
|
108
|
-
Obviously,
|
|
115
|
+
We replace the function name by the hash of the normalized definition of the function as provided by the program defining the function.
|
|
116
|
+
Obviously, a function may have many different definitions so the hash-based names are not canonical. By using hash-based function names we avoid the problem of name clashes when bringing together many `kernel-core` programs.
|
|
109
117
|
|
|
110
|
-
This approach solves the problem of modules
|
|
118
|
+
This approach solves the problem of modules, imports, etc., and opens the door to a universal
|
|
111
119
|
registry of types (schemas) and functions.
|
|
112
120
|
|
|
113
121
|
## Typing and Polymorphism (codes and filters/patterns)
|
|
114
122
|
|
|
115
|
-
Abstract Syntax Tree of a program
|
|
123
|
+
Abstract Syntax Tree of a program consists of a dictionary of _type definitions_,
|
|
116
124
|
a dictionary of _function definitions_, and the final _main expression_.
|
|
117
125
|
|
|
118
126
|
In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
|
|
119
127
|
the program on an input.
|
|
120
128
|
|
|
121
129
|
Our objective however is to compute canonicals representations for types and normalized
|
|
122
|
-
representations for functions, so they could be
|
|
130
|
+
representations for functions, so they could be reused.
|
|
123
131
|
|
|
124
132
|
The normalization steps are:
|
|
125
133
|
|
|
@@ -127,7 +135,7 @@ The normalization steps are:
|
|
|
127
135
|
as well as type expressions used in _function definitions_ and the _main expression_.
|
|
128
136
|
2. Annotate the expressions with types or type patterns; every node of the AST is annotated with a pair of type patterns.
|
|
129
137
|
3. Turn the _singleton_ type patterns into types and add them to the _type graph_. Go to (2) unless no change.
|
|
130
|
-
|
|
138
|
+
|
|
131
139
|
|
|
132
140
|
### Example
|
|
133
141
|
|
|
@@ -169,7 +177,7 @@ and a type `byte` as a product, for simplicity, of four `bit`s.
|
|
|
169
177
|
Functions `bit0`, `bit1`, and `zero` are "constant polymorphic functions", meaning:
|
|
170
178
|
|
|
171
179
|
- constant: if defined, they always return exactly the same value;
|
|
172
|
-
- polymorphic: they are defined for more than one pair of input and output
|
|
180
|
+
- polymorphic: they are defined for more than one pair of input and output types: functions
|
|
173
181
|
`bit0` and `bit1` are of type `?X -> $bit`, and `zero` is of type `?X -> $byte`, where `?X`
|
|
174
182
|
denotes an unconstrained type pattern, also denoted as `?(...)`.
|
|
175
183
|
|
|
@@ -186,15 +194,15 @@ with following constraints:
|
|
|
186
194
|
> `?( V i, ...)`;
|
|
187
195
|
- `V` is unconstrained, denoted by `(...)`;
|
|
188
196
|
|
|
189
|
-
We
|
|
197
|
+
We can write it as: `?($byte byte, ((...) i, ...) overflown, ...)`.
|
|
190
198
|
|
|
191
199
|
The target type `${ byte byte, bit overflown }` was derived from pattern `?{ $byte byte, $bit overflown }`.
|
|
192
|
-
Such a
|
|
200
|
+
Such a pattern is called _singleton pattern_, as only one type fits the pattern.
|
|
193
201
|
|
|
194
202
|
## Universal Schema Registry
|
|
195
203
|
|
|
196
204
|
Since the normalization process for types is fast and deterministic, we can build a universal
|
|
197
|
-
schema registry that will store all invented types.
|
|
205
|
+
schema registry that will store all invented types.
|
|
198
206
|
The registry will be a key-value store, where the key is the hash of the normalized type,
|
|
199
207
|
and the value is the normalized type itself.
|
|
200
208
|
|
|
@@ -203,8 +211,7 @@ definition and stored in the similar key-value store.
|
|
|
203
211
|
|
|
204
212
|
Non-polymorphic functions can be easily indexed by the hashes of their input and output types
|
|
205
213
|
so that we can quickly find the function we need.
|
|
206
|
-
|
|
207
|
-
whose input and output type patterns fit given types.
|
|
214
|
+
Indexing polymorphic functions seems complicated (TODO).
|
|
208
215
|
|
|
209
216
|
## Serialization and Compilation
|
|
210
217
|
|
package/TODO.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# To do list for canonical names
|
|
2
|
+
|
|
3
|
+
- [ ] Write a script to search for conflicting names
|
|
4
|
+
- [ ] Export a relation. i.e., rewrite it as a list of all
|
|
5
|
+
dependency definitions using canonical names
|
|
6
|
+
- [ ] Find a way of adding 'comments' or some way of 'tagging' the definitions
|
|
7
|
+
- [X] The definitions should be simplied by removing useless filters
|
|
8
|
+
- [X] Definitions, which are just aliases to non built-in rels should not introduce a new canonical name
|
|
9
|
+
- [ ] Can we inline some references w/o changing the canonical name?
|
|
10
|
+
|
|
11
|
+
## Simplification rules
|
|
12
|
+
|
|
13
|
+
It seems to me that after calculating input and output patterns, all
|
|
14
|
+
filters and code expressions could be replaced by just input and output
|
|
15
|
+
filters corresponding to the patterns. [DONE]
|
|
16
|
+
|
|
17
|
+
The filer variables have to be renamed for normalization if they take part of
|
|
18
|
+
the canonical name calculation. [DONE]
|
package/TYPING.md
CHANGED
|
@@ -360,6 +360,6 @@ Repeat the procedure until the type pattern graph is not changing anymore.
|
|
|
360
360
|
- [X] Redefine built-in CONS and SNOC to allign with no support for non-homogeneous lists
|
|
361
361
|
- [X] Integrate filters into typePatters (execution is not affected)
|
|
362
362
|
- [/] better type error repporting - the info exists in the pattern parent tree ... in progress
|
|
363
|
-
- [
|
|
363
|
+
- [X] Introduce a space/time bounds for type derivation
|
|
364
364
|
- [ ] Check for loops, e.g., `$x = < x f, ...>`, `$x = [x]`, `$x = {x f, ...}` -- this
|
|
365
|
-
looks a little arbitrary, but maybe this is actually an important part
|
|
365
|
+
looks a little arbitrary, but maybe this is actually an important part
|
package/export.mjs
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
|
|
2
|
+
import { prettyRel, patterns2filters } from "./pretty.mjs";
|
|
3
|
+
import { hash } from "./hash.mjs";
|
|
4
|
+
|
|
5
|
+
function simplifyRel(relDef) {
|
|
6
|
+
// remove filters and codes
|
|
7
|
+
const prune = (rel) => {
|
|
8
|
+
const newRel = {...rel};
|
|
9
|
+
switch (rel.op) {
|
|
10
|
+
case "product":
|
|
11
|
+
newRel.product = rel.product.map(({label, exp}) => ({label, exp: prune(exp)}));
|
|
12
|
+
break;
|
|
13
|
+
case "union":
|
|
14
|
+
newRel.union = rel.union.map(exp => prune(exp));
|
|
15
|
+
break;
|
|
16
|
+
case "comp":
|
|
17
|
+
newRel.comp = rel.comp.reduce((list, exp) => {
|
|
18
|
+
const newExp = prune(exp);
|
|
19
|
+
if (newExp.op != "identity")
|
|
20
|
+
list.push(newExp);
|
|
21
|
+
return list;
|
|
22
|
+
},[]);
|
|
23
|
+
if (newRel.comp.length == 0)
|
|
24
|
+
newRel.op = "identity";
|
|
25
|
+
break;
|
|
26
|
+
case "vector":
|
|
27
|
+
newRel.vector = rel.vector.map(exp => prune(exp));
|
|
28
|
+
break;
|
|
29
|
+
case "caret":
|
|
30
|
+
newRel.caret = prune(rel.caret);
|
|
31
|
+
break;
|
|
32
|
+
case 'code':
|
|
33
|
+
case 'filter':
|
|
34
|
+
newRel.op = "identity";
|
|
35
|
+
default:
|
|
36
|
+
break;
|
|
37
|
+
};
|
|
38
|
+
return newRel;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const rel = relDef.def;
|
|
42
|
+
const filters = patterns2filters(relDef.typePatternGraph, ...rel.patterns).map( filter =>
|
|
43
|
+
({op: "filter", filter: filter}));
|
|
44
|
+
const newRel = prune(rel);
|
|
45
|
+
let resultRel = {...newRel};
|
|
46
|
+
if (newRel.op == "identity")
|
|
47
|
+
return filters[0];
|
|
48
|
+
|
|
49
|
+
if (newRel.op == "comp") {
|
|
50
|
+
resultRel.comp = [ filters[0], ...newRel.comp, filters[1] ];
|
|
51
|
+
return resultRel;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
op: "comp",
|
|
55
|
+
comp: [filters[0], newRel, filters[1] ]
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function theID(alias, rel, scc, name) {
|
|
61
|
+
const sccNames = new Set(scc);
|
|
62
|
+
const auxNames = {[name]: "X0"};
|
|
63
|
+
// rename and remove filters and codes
|
|
64
|
+
const reNameX = (rel) => {
|
|
65
|
+
const newRel = {...rel};
|
|
66
|
+
switch (rel.op) {
|
|
67
|
+
case "product":
|
|
68
|
+
newRel.product = rel.product.map(({label, exp}) => ({label, exp: reNameX(exp)}));
|
|
69
|
+
break;
|
|
70
|
+
case "union":
|
|
71
|
+
newRel.union = rel.union.map(exp => reNameX(exp));
|
|
72
|
+
break;
|
|
73
|
+
case "comp":
|
|
74
|
+
newRel.comp = rel.comp.map(exp => reNameX(exp));
|
|
75
|
+
break;
|
|
76
|
+
case "vector":
|
|
77
|
+
newRel.vector = rel.vector.map(exp => reNameX(exp));
|
|
78
|
+
break;
|
|
79
|
+
case "caret":
|
|
80
|
+
newRel.caret = reNameX(rel.caret);
|
|
81
|
+
break;
|
|
82
|
+
case "ref": {
|
|
83
|
+
const n = rel.ref;
|
|
84
|
+
if (alias[n] != undefined) {
|
|
85
|
+
newRel.ref = alias[n];
|
|
86
|
+
} else if (auxNames[n] != undefined) {
|
|
87
|
+
newRel.ref = auxNames[n];
|
|
88
|
+
} else if (sccNames.has(n)) {
|
|
89
|
+
auxNames[n] = `X${Object.keys(auxNames).length}`;
|
|
90
|
+
newRel.ref = auxNames[n];
|
|
91
|
+
};
|
|
92
|
+
}; break;
|
|
93
|
+
default:
|
|
94
|
+
break;
|
|
95
|
+
};
|
|
96
|
+
return newRel;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const newRel = reNameX(rel);
|
|
100
|
+
const resultRelStr = prettyRel(newRel);
|
|
101
|
+
// console.log(` ${name} = ${resultRelStr}`);
|
|
102
|
+
const newName = hash(resultRelStr);
|
|
103
|
+
return newName;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function assignCanonicalNames(scc, rels, relAlias) {
|
|
107
|
+
const newAlias = scc.reduce( (newAlias,relName) => {
|
|
108
|
+
const relDef = rels[relName];
|
|
109
|
+
if (relDef.def.op == "ref" && rels[relDef.def.ref] != undefined) {
|
|
110
|
+
// inlining if direct alias to a non built-in relation
|
|
111
|
+
relDef.simplified = simplifyRel(rels[relDef.def.ref]);
|
|
112
|
+
} else {
|
|
113
|
+
relDef.simplified = simplifyRel(relDef);
|
|
114
|
+
}
|
|
115
|
+
newAlias[relName] = theID(relAlias, relDef.simplified, scc, relName);
|
|
116
|
+
return newAlias;
|
|
117
|
+
}, {});
|
|
118
|
+
const newNames = [...new Set(Object.values(newAlias))];
|
|
119
|
+
// console.log(` --- SCC: {${scc.join(",")}} ---`);
|
|
120
|
+
|
|
121
|
+
const sccCanonicalName = newNames.sort().join(":");
|
|
122
|
+
for (let relName in newAlias) {
|
|
123
|
+
if (newNames.length > 1)
|
|
124
|
+
relAlias[relName] = hash(newAlias[relName]+":"+sccCanonicalName);
|
|
125
|
+
else
|
|
126
|
+
relAlias[relName] = newAlias[relName];
|
|
127
|
+
// console.log(` ${relName} -> ${relAlias[relName]}`);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function exportRelation(rels, alias, name) {
|
|
132
|
+
const queue = [name];
|
|
133
|
+
const inQueue = { [name]: true };
|
|
134
|
+
const result = {};
|
|
135
|
+
const reName = (rel) => {
|
|
136
|
+
const newRel = {...rel};
|
|
137
|
+
switch (rel.op) {
|
|
138
|
+
case "product":
|
|
139
|
+
newRel.product = rel.product.map(({label, exp}) => ({label, exp: reName(exp)}));
|
|
140
|
+
break;
|
|
141
|
+
case "union":
|
|
142
|
+
newRel.union = rel.union.map(exp => reName(exp));
|
|
143
|
+
break;
|
|
144
|
+
case "comp":
|
|
145
|
+
newRel.comp = rel.comp.map(exp => reName(exp));
|
|
146
|
+
break;
|
|
147
|
+
case "vector":
|
|
148
|
+
newRel.vector = rel.vector.map(exp => reName(exp));
|
|
149
|
+
break;
|
|
150
|
+
case "caret":
|
|
151
|
+
newRel.caret = reName(rel.caret);
|
|
152
|
+
break;
|
|
153
|
+
case "ref": {
|
|
154
|
+
const n = rel.ref;
|
|
155
|
+
if (alias[n] == undefined) // built-in
|
|
156
|
+
return newRel;
|
|
157
|
+
newRel.ref = alias[n];
|
|
158
|
+
if (! inQueue[n]) {
|
|
159
|
+
inQueue[n] = true;
|
|
160
|
+
queue.push(n);
|
|
161
|
+
}
|
|
162
|
+
}; break;
|
|
163
|
+
default:
|
|
164
|
+
break;
|
|
165
|
+
};
|
|
166
|
+
return newRel;
|
|
167
|
+
};
|
|
168
|
+
while (queue.length > 0 ) {
|
|
169
|
+
const name = queue.shift();
|
|
170
|
+
const canonicalName = alias[name];
|
|
171
|
+
result[canonicalName] = {...rels[name], def: reName(rels[name].def), name: name};
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export default { assignCanonicalNames, exportRelation };
|
|
177
|
+
export { assignCanonicalNames, exportRelation };
|
package/index.mjs
CHANGED
|
@@ -4,19 +4,7 @@ import run from "./run.mjs";
|
|
|
4
4
|
import t from "./codes.mjs";
|
|
5
5
|
|
|
6
6
|
function compile(script) {
|
|
7
|
-
|
|
8
|
-
run.defs = annotate(script);
|
|
9
|
-
} catch (e) {
|
|
10
|
-
console.error(e.message);
|
|
11
|
-
console.error("WARN: Recompiling without type reconciliation due to the type error above.");
|
|
12
|
-
const { defs, exp } = parse(script);
|
|
13
|
-
const { codes, representatives } = t.finalize(defs.codes);
|
|
14
|
-
run.defs = {
|
|
15
|
-
rels: {...defs.rels, "__main__": {def: exp}},
|
|
16
|
-
codes, representatives
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
}
|
|
7
|
+
run.defs = annotate(script);
|
|
20
8
|
return run.bind(null, run.defs.rels.__main__.def);
|
|
21
9
|
}
|
|
22
10
|
|
|
@@ -35,9 +23,9 @@ function annotate(script) {
|
|
|
35
23
|
const representatives = t.register(defs.codes);
|
|
36
24
|
const rels = {...defs.rels, "__main__": {def: exp}};
|
|
37
25
|
|
|
38
|
-
patterns(representatives, rels);
|
|
26
|
+
const relAlias = patterns(representatives, rels);
|
|
39
27
|
|
|
40
|
-
return {rels, representatives}
|
|
28
|
+
return {rels, representatives, relAlias};
|
|
41
29
|
}
|
|
42
30
|
annotate.doc = "Annotate all the script expressions with patterns";
|
|
43
31
|
|
package/package.json
CHANGED
package/patterns.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import hash from "./hash.mjs";
|
|
|
3
3
|
import { TypePatternGraph } from "./typing.mjs";
|
|
4
4
|
import { Graph, sccs, topoOrder } from "./Graph.mjs";
|
|
5
5
|
import codes from "./codes.mjs";
|
|
6
|
+
import { assignCanonicalNames } from "./export.mjs";
|
|
6
7
|
|
|
7
8
|
const unitCode = codes.unitCode;
|
|
8
9
|
|
|
@@ -101,6 +102,7 @@ function patterns(representatives, rels) {
|
|
|
101
102
|
// representatives:{"{}": "BG", ...}
|
|
102
103
|
// rels: {"rlz": [{op: comp,...}, ...], ...}
|
|
103
104
|
|
|
105
|
+
const relAlias = {};
|
|
104
106
|
|
|
105
107
|
// 1 INITIALIZATION
|
|
106
108
|
// 1.1 initialize patternNodes and rels
|
|
@@ -416,7 +418,7 @@ function patterns(representatives, rels) {
|
|
|
416
418
|
for (const relName in rels) {
|
|
417
419
|
const rootDef = rels[relName];
|
|
418
420
|
rootDef.typePatternGraph = new TypePatternGraph();
|
|
419
|
-
rootDef.varRefs = []; // the list of non-
|
|
421
|
+
rootDef.varRefs = []; // the list of references to non-builtin rels as pointers to AST nodes
|
|
420
422
|
rootDef.patternVars = {};
|
|
421
423
|
augment(rootDef.def, rootDef);
|
|
422
424
|
delete rootDef.patternVars;
|
|
@@ -453,12 +455,16 @@ function patterns(representatives, rels) {
|
|
|
453
455
|
|
|
454
456
|
// 3. Topological sort of the DAG
|
|
455
457
|
|
|
456
|
-
const sccInOrder = topoOrder(DAG);
|
|
458
|
+
const sccInOrder = topoOrder(DAG).reverse();
|
|
457
459
|
|
|
458
460
|
// 4. For each strongly connected component C in a bottom-up order
|
|
461
|
+
|
|
462
|
+
// we will also generate the canonical representation of the relations in C and replace
|
|
463
|
+
// the name to the relation by the hash of the canonical representation
|
|
464
|
+
|
|
465
|
+
// console.log("----- SCC in order", sccInOrder);
|
|
459
466
|
|
|
460
|
-
for (const scc of sccInOrder
|
|
461
|
-
// console.log(`SCC: { ${DAGnodes[scc].scc} }`);
|
|
467
|
+
for (const scc of sccInOrder) {
|
|
462
468
|
|
|
463
469
|
const maxNumberOfIterations = 10;
|
|
464
470
|
|
|
@@ -497,9 +503,7 @@ function patterns(representatives, rels) {
|
|
|
497
503
|
throw e;
|
|
498
504
|
}
|
|
499
505
|
}
|
|
500
|
-
|
|
501
506
|
rels[relName] = compactRel(relDef,relName);
|
|
502
|
-
|
|
503
507
|
return relDefToString(rels[relName]);
|
|
504
508
|
}));
|
|
505
509
|
|
|
@@ -507,9 +511,19 @@ function patterns(representatives, rels) {
|
|
|
507
511
|
// console.log("SUCCESS!");
|
|
508
512
|
break;
|
|
509
513
|
}
|
|
510
|
-
} //
|
|
511
|
-
|
|
514
|
+
} // end of iteration loop
|
|
515
|
+
|
|
516
|
+
DAGnodes[scc].scc.forEach( relName => {
|
|
517
|
+
const relDef = rels[relName];
|
|
518
|
+
const canonical = relDefToString(relDef);
|
|
519
|
+
const h = hash(canonical);
|
|
520
|
+
relAlias[relName] = h;
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
assignCanonicalNames(DAGnodes[scc].scc, rels, relAlias);
|
|
524
|
+
|
|
512
525
|
}
|
|
526
|
+
return relAlias;
|
|
513
527
|
}
|
|
514
528
|
|
|
515
529
|
export default { patterns };
|
package/pretty.mjs
CHANGED
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import { find } from "./codes.mjs";
|
|
2
2
|
import { TypePatternGraph } from "./typing.mjs";
|
|
3
3
|
|
|
4
|
-
function compareAs(fn) {
|
|
5
|
-
return function (a, b) {
|
|
6
|
-
[a, b] = [a, b].map(fn);
|
|
7
|
-
if (a < b) return -1;
|
|
8
|
-
if (a > b) return 1;
|
|
9
|
-
return 0;
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
|
|
13
4
|
const nameRE = /^[a-zA-Z0-9_][a-zA-Z0-9_?!]*$/;
|
|
5
|
+
function pLabel_(label) {
|
|
6
|
+
return nameRE.test(label) ? ` ${label}` : `${JSON.stringify(label)}`;
|
|
7
|
+
}
|
|
8
|
+
function pLabel(label) {
|
|
9
|
+
return nameRE.test(label) ? `${label}` : `${JSON.stringify(label)}`;
|
|
10
|
+
}
|
|
14
11
|
|
|
15
12
|
function prettyCode_labels (representatives, label_ref_map) {
|
|
16
13
|
return Object.keys(label_ref_map)
|
|
17
|
-
.sort(
|
|
14
|
+
.sort()
|
|
18
15
|
.map( (label) => {
|
|
19
|
-
const plabel = nameRE.test(label) ? label : `'${label}'`;
|
|
20
16
|
return `${prettyCode(representatives, {
|
|
21
17
|
code: "ref",
|
|
22
18
|
ref: label_ref_map[label],
|
|
23
|
-
})}
|
|
19
|
+
})}${pLabel_(label)}`;
|
|
24
20
|
})
|
|
25
21
|
.join(", ");
|
|
26
22
|
};
|
|
@@ -46,10 +42,10 @@ function prettyCode (representatives, codeExp) {
|
|
|
46
42
|
}
|
|
47
43
|
};
|
|
48
44
|
|
|
49
|
-
function prettyFilter (
|
|
45
|
+
function prettyFilter ( filter ) {
|
|
50
46
|
const fieldsStr = () => {
|
|
51
47
|
const fields = Object.keys(filter.fields).map( (key) =>
|
|
52
|
-
`${prettyFilter(
|
|
48
|
+
`${prettyFilter(filter.fields[key])}${pLabel_(key)}`
|
|
53
49
|
);
|
|
54
50
|
if (filter.open) fields.push("...");
|
|
55
51
|
return fields.join(", ");
|
|
@@ -57,11 +53,11 @@ function prettyFilter (prettyCode, filter) {
|
|
|
57
53
|
switch (filter.type) {
|
|
58
54
|
|
|
59
55
|
case "code":
|
|
60
|
-
return `$${
|
|
56
|
+
return `$${filter.code}`;
|
|
61
57
|
case "name":
|
|
62
58
|
return filter.name;
|
|
63
59
|
case "vector":
|
|
64
|
-
return `[${prettyFilter(
|
|
60
|
+
return `[${prettyFilter(filter.vector)}]${filter.name ? "=" + filter.name : ""}`;
|
|
65
61
|
case null:
|
|
66
62
|
if (filter.name && Object.keys(filter.fields).length == 0)
|
|
67
63
|
return filter.name;
|
|
@@ -74,12 +70,13 @@ function prettyFilter (prettyCode, filter) {
|
|
|
74
70
|
throw new Error(`Unknown filter type ${filter.type} in ${JSON.stringify(filter)}`);
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
function prettyRel (
|
|
73
|
+
function prettyRel (exp) {
|
|
78
74
|
"use strict";
|
|
79
75
|
const pretty = (exp) => {
|
|
80
76
|
switch (exp.op) {
|
|
81
77
|
case "filter":
|
|
82
|
-
return
|
|
78
|
+
if (exp.filter.type == 'code') return `$${exp.filter.code}`;
|
|
79
|
+
return `?${prettyFilter(exp.filter)}`;
|
|
83
80
|
case "pipe":
|
|
84
81
|
return `|`;
|
|
85
82
|
case "caret":
|
|
@@ -101,27 +98,16 @@ function prettyRel (prettyCode, exp) {
|
|
|
101
98
|
case "dot":
|
|
102
99
|
if ("number" === typeof exp.dot) {
|
|
103
100
|
return `.${exp.dot}`;
|
|
104
|
-
} else
|
|
105
|
-
return `.${exp.dot}`;
|
|
106
|
-
} else {
|
|
107
|
-
return `'${exp.dot}'`;
|
|
108
|
-
}
|
|
109
|
-
break;
|
|
101
|
+
} else
|
|
102
|
+
return `.${pLabel(exp.dot)}`;
|
|
110
103
|
case "code":
|
|
111
|
-
return `$${
|
|
112
|
-
code: "ref",
|
|
113
|
-
ref: exp.code,
|
|
114
|
-
})}`;
|
|
104
|
+
return `$${exp.code}`;
|
|
115
105
|
case "product":
|
|
116
106
|
return (function (labelled) {
|
|
117
107
|
return `{${labelled.join(", ")}}`;
|
|
118
108
|
})(
|
|
119
109
|
exp.product.map(function ({ label, exp }) {
|
|
120
|
-
|
|
121
|
-
return `${pretty(exp)} ${label}`;
|
|
122
|
-
} else {
|
|
123
|
-
return `${pretty(exp)} '${label}'`;
|
|
124
|
-
}
|
|
110
|
+
return `${pretty(exp)}${pLabel_(label)}`;
|
|
125
111
|
})
|
|
126
112
|
);
|
|
127
113
|
}
|
|
@@ -148,25 +134,24 @@ function patterns2filters(typePatternGraph, ...patternIds) {
|
|
|
148
134
|
|
|
149
135
|
// variables - variables which will have to be added as extra filters
|
|
150
136
|
const variables = inDegree.reduce( (variables, degree, i) => {
|
|
151
|
-
if (degree > 1) variables[i] =
|
|
137
|
+
if (degree > 1) variables[i] = true;
|
|
152
138
|
return variables;
|
|
153
139
|
}, {});
|
|
154
140
|
|
|
141
|
+
const filterVars = {};
|
|
155
142
|
const buildFilter = (path, patternId, i) => {
|
|
156
143
|
let named_filter = {};
|
|
157
144
|
const pattern = newTypePatternGraph.get_pattern(patternId);
|
|
158
145
|
if (variables[patternId] && pattern.pattern != 'type') {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
named_filter = { name: name };
|
|
146
|
+
if (filterVars[patternId])
|
|
147
|
+
return { type: 'name', name: filterVars[patternId] };
|
|
148
|
+
filterVars[patternId] = `X${Object.keys(filterVars).length}`;
|
|
149
|
+
named_filter = { name: filterVars[patternId] };
|
|
164
150
|
}
|
|
165
151
|
|
|
166
|
-
|
|
167
152
|
const edges = newTypePatternGraph.edges[patternId];
|
|
168
153
|
const fields = () =>
|
|
169
|
-
Object.keys(edges).reduce( (fields, key) =>{
|
|
154
|
+
Object.keys(edges).sort().reduce( (fields, key) =>{
|
|
170
155
|
fields[key] = buildFilter([...path, key], Object.values(edges[key])[0], i);
|
|
171
156
|
return fields;
|
|
172
157
|
}, {});
|