@fraczak/k 5.0.0 → 5.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 +87 -36
- package/Code-derivation-tests/bnat.mjs +1 -1
- package/README.md +62 -60
- package/export.mjs +14 -6
- package/package.json +1 -1
- package/parser.jison +1 -1
- package/parser.mjs +1 -1
- package/patterns.mjs +24 -12
- package/repl.mjs +6 -3
- package/typing.mjs +47 -30
package/BIG_PICTURE.md
CHANGED
|
@@ -14,15 +14,18 @@ Formally, a _type_ is a (subclass of) finite tree automata. The type defines a s
|
|
|
14
14
|
which corresponds to the set of trees accepted by the automaton.
|
|
15
15
|
Therefore, every type admits a unique normal form in terms of its minimal deterministic tree automaton.
|
|
16
16
|
|
|
17
|
-
The set of all types can be seen as the universal deterministic (infinite) tree automaton.
|
|
18
|
-
Every node of that automaton defines a type; only a finite set of nodes are reachable from
|
|
19
|
-
any node.
|
|
20
|
-
|
|
21
17
|
Types are defined by two constructs:
|
|
22
18
|
|
|
23
19
|
1. Product
|
|
24
20
|
2. Union (also called tagged or disjoint union)
|
|
25
21
|
|
|
22
|
+
```bnf
|
|
23
|
+
type ::= name | '{' type_label_list '}' | '<' type_label_list '>'
|
|
24
|
+
type_label_list ::= /* empty */ | ( type label ',' )* type label
|
|
25
|
+
name ::= IDENTIFIER
|
|
26
|
+
label ::= STRING
|
|
27
|
+
```
|
|
28
|
+
|
|
26
29
|
Given a finite (possibly empty) set of types `t1, t2, ..., tn` and a set of pairwise different tags (labels) `l1, l2, ..., ln`,
|
|
27
30
|
|
|
28
31
|
- by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n`
|
|
@@ -43,8 +46,34 @@ Some properties of these types:
|
|
|
43
46
|
singleton union types in the syntax of the language. The other benefit is that we will be able to use the same syntax
|
|
44
47
|
for writing out values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
|
|
45
48
|
as a union type value (a variant).
|
|
46
|
-
2. The empty product type `{}
|
|
47
|
-
|
|
49
|
+
2. The empty product type, `{}`, has a single value, called the _unit_
|
|
50
|
+
and also denoted by `{}`, which has no fields.
|
|
51
|
+
3. The empty union type, `<>`, has no values.
|
|
52
|
+
|
|
53
|
+
### Filters (patterns)
|
|
54
|
+
|
|
55
|
+
A _filter_ is a way of representing a _type class_, i.e, a set of types sharing some struture.
|
|
56
|
+
For example, if I wanted to refer to all product types that have a field `f` of type `T`,
|
|
57
|
+
I would write a pattern: `?{ T f, ... }`. In general, a filter is defined by:
|
|
58
|
+
|
|
59
|
+
```bnf
|
|
60
|
+
filter ::= name | '$' type_expr | '{' filter_label_list '}'
|
|
61
|
+
| '<' filter_label_list '>' | '(' filter_label_list ')'
|
|
62
|
+
| filter '=' name
|
|
63
|
+
filter_label_list ::= /* empty */ | '...'
|
|
64
|
+
| ( filter label ',' ) * filter label
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
68
|
+
|
|
69
|
+
```k-repl
|
|
70
|
+
X -- any type
|
|
71
|
+
( ... ) -- any type
|
|
72
|
+
{ ... } -- any product type
|
|
73
|
+
( X x, X y ) -- any type with two fields of the same type
|
|
74
|
+
< X x, ... > = X -- a (recursive) type, 'X', with variant 'x'
|
|
75
|
+
-- of the same type 'X'
|
|
76
|
+
```
|
|
48
77
|
|
|
49
78
|
### Partial functions
|
|
50
79
|
|
|
@@ -52,14 +81,17 @@ A _partial function_ is a function that is not defined for every value of its do
|
|
|
52
81
|
|
|
53
82
|
There are three ways of combining partial functions:
|
|
54
83
|
|
|
55
|
-
1. __Composition__: we write a composition of `f` and `g` as `(f g)`,
|
|
84
|
+
1. __Composition__: we write a composition of `f` and `g` as `(f g)`,
|
|
85
|
+
meaning that if `f` is defined on value `x` producing `y`,
|
|
56
86
|
and `g` is defined on value `y` producing `z`, then `(f g)` is defined on value `x` producing `z`.
|
|
57
87
|
2. __Union__: we write `< f1, f2 >` for a union of two functions `f1` and `f2`, meaning that
|
|
58
88
|
if `f1` is defined on value `x` producing `y`, then `< f1, f2 >`
|
|
59
89
|
is defined on value `x` producing `y`. Otherwise, i.e., when `f1` is not defined for `x`, `< f1 f2 >` acts as `f2`.
|
|
60
|
-
3. __Product__: we write `{f1 lab1, f2 lab2 }` for a product of two functions `f1` and `f2`,
|
|
61
|
-
are both defined on value `x` producing `y1` and `y2`,
|
|
62
|
-
|
|
90
|
+
3. __Product__: we write `{f1 lab1, f2 lab2 }` for a product of two functions `f1` and `f2`,
|
|
91
|
+
meaning that if `f1` and `f2` are both defined on value `x` producing `y1` and `y2`,
|
|
92
|
+
respectively, then `{f1 lab1, f2 lab2 }` is defined on value `x` producing
|
|
93
|
+
`{y1 lab1, y2 lab2}`.
|
|
94
|
+
Otherwise, `{f1 lab1, f2 lab2 }` is not defined for `x`.
|
|
63
95
|
|
|
64
96
|
Actually, composition, union, and product combine any number of partial functions, not only two. For example,
|
|
65
97
|
empty composition, `()`, defines the identity function.
|
|
@@ -72,41 +104,32 @@ which is the partial function the program defines.
|
|
|
72
104
|
The language syntax is defined by:
|
|
73
105
|
|
|
74
106
|
```bnf
|
|
75
|
-
program ::= (type_definition | function_definition)
|
|
76
|
-
|
|
107
|
+
program ::= (type_definition | function_definition)* expression
|
|
77
108
|
type_definition ::= '$' type_name '=' type ';'
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
function_definition ::= function_name '=' expression ';'
|
|
83
|
-
expression ::= projection | typing | filter | composition | union | product | function_name
|
|
84
|
-
projection ::= '.' label_name
|
|
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
|
|
109
|
+
function_definition ::= name '=' expression ';'
|
|
110
|
+
expression ::= name | '$' type | '?' filter
|
|
111
|
+
| projection | composition | union | product
|
|
112
|
+
projection ::= '.' label
|
|
91
113
|
composition ::= '(' expression * ')'
|
|
92
114
|
union ::= '<' (expression ',') + expression '>'
|
|
93
|
-
product ::= '{'
|
|
94
|
-
|
|
115
|
+
product ::= '{' expr_label_list '}'
|
|
116
|
+
expr_label_list ::= /* empty */
|
|
117
|
+
| (expression label_name ',')* expression label
|
|
95
118
|
```
|
|
96
119
|
|
|
97
|
-
We assume that in a `kernel-core` program the
|
|
120
|
+
We assume that in a `kernel-core` program the names (for types, functions, and labels) are identifiers (strings) such that:
|
|
98
121
|
|
|
99
122
|
1. All defined type names are distinct.
|
|
100
123
|
2. All defined function names are distinct.
|
|
101
124
|
3. All label names within a type definition or a `product` expression are locally distinct.
|
|
102
125
|
|
|
103
|
-
|
|
104
|
-
function defined only for the values of the corresponding
|
|
126
|
+
`Filter` or `type` expressions act as a type annotation, i.e., an identity
|
|
127
|
+
function defined only for the values of the corresponding types.
|
|
105
128
|
|
|
106
129
|
That's it.
|
|
107
130
|
|
|
108
131
|
No `builtin` types, no `if` statement, no `loop`, no `throw`, no _closure_, no _annotations_, and no macros.
|
|
109
|
-
Just
|
|
132
|
+
Just _types_, _filters_, and _functions_.
|
|
110
133
|
|
|
111
134
|
### Normalization
|
|
112
135
|
|
|
@@ -126,20 +149,21 @@ a dictionary of _function definitions_, and the final _main expression_.
|
|
|
126
149
|
In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
|
|
127
150
|
the program on an input.
|
|
128
151
|
|
|
129
|
-
Our objective however is to compute
|
|
152
|
+
Our objective however is to compute canonical representations for types and normalized
|
|
130
153
|
representations for functions, so they could be reused.
|
|
131
154
|
|
|
132
155
|
The normalization steps are:
|
|
133
156
|
|
|
134
157
|
1. Build the _type graph_ from all types used by the program, i.e., used in _type definitions_
|
|
135
158
|
as well as type expressions used in _function definitions_ and the _main expression_.
|
|
136
|
-
2. Annotate the expressions with
|
|
137
|
-
3. Turn the _singleton_
|
|
159
|
+
2. Annotate the expressions with filters; every node of the AST is annotated with a pair of filters.
|
|
160
|
+
3. Turn the _singleton_ filters into types and add them to the _type graph_. Go to (2) unless no change.
|
|
138
161
|
|
|
162
|
+
### Examples
|
|
139
163
|
|
|
140
|
-
|
|
164
|
+
#### Example 1
|
|
141
165
|
|
|
142
|
-
```k
|
|
166
|
+
```k-repl
|
|
143
167
|
$ bit = < {} o, {} i >;
|
|
144
168
|
bit0 = {{} o} $bit;
|
|
145
169
|
bit1 = {{} i} $bit;
|
|
@@ -199,6 +223,33 @@ We can write it as: `?($byte byte, ((...) i, ...) overflown, ...)`.
|
|
|
199
223
|
The target type `${ byte byte, bit overflown }` was derived from pattern `?{ $byte byte, $bit overflown }`.
|
|
200
224
|
Such a pattern is called _singleton pattern_, as only one type fits the pattern.
|
|
201
225
|
|
|
226
|
+
#### Example 2
|
|
227
|
+
|
|
228
|
+
Polymorphic list functions:
|
|
229
|
+
|
|
230
|
+
```k-repl
|
|
231
|
+
list? = ?< {} nil, {X car, Y cdr} cons > = Y;
|
|
232
|
+
nil = {{} nil} list?;
|
|
233
|
+
singleton = {{() car, nil cdr} cons} list?;
|
|
234
|
+
cons = {{.car car, .cdr cdr} cons} list?;
|
|
235
|
+
nil? = list? .nil nil;
|
|
236
|
+
car = list? .cons .car;
|
|
237
|
+
cdr = list? .cons .cdr;
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
For example, the filter annotation for `car` is:
|
|
241
|
+
|
|
242
|
+
```k-repl
|
|
243
|
+
--R car
|
|
244
|
+
{
|
|
245
|
+
name: 'yRXGSJrIWCuKV',
|
|
246
|
+
type: '?<{X1 car, X0 cdr} cons, $KL nil>=X0 --> ?X1',
|
|
247
|
+
def: '?<{X1 car, X0 cdr} cons, $KL nil>=X0 .cons .car ?X1'
|
|
248
|
+
}
|
|
249
|
+
--C KL
|
|
250
|
+
$ KL = {}; -- $C0={};
|
|
251
|
+
```
|
|
252
|
+
|
|
202
253
|
## Universal Schema Registry
|
|
203
254
|
|
|
204
255
|
Since the normalization process for types is fast and deterministic, we can build a universal
|
|
@@ -5,7 +5,7 @@ import assert from 'assert';
|
|
|
5
5
|
t(fs.readFileSync("./Examples/bnat-patterns.k").toString("utf8"), (annotated) => {
|
|
6
6
|
// console.log(JSON.stringify(annotated,null,2));
|
|
7
7
|
const {input,output} = in_out(annotated);
|
|
8
|
-
|
|
8
|
+
console.log(output);
|
|
9
9
|
assert.equal(output.type, annotated.representatives["bnat"]);
|
|
10
10
|
assert.equal(input.type, annotated.representatives["pair"]);
|
|
11
11
|
console.log("OK");
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ the property doesn't exist. E.g.,
|
|
|
54
54
|
|
|
55
55
|
There are three ways of combining functions:
|
|
56
56
|
|
|
57
|
-
1. **
|
|
57
|
+
1. **Composition**: `(f1 f2 ...)`, e.g. `(.toto .titi)` extracts
|
|
58
58
|
nested field.
|
|
59
59
|
|
|
60
60
|
```text
|
|
@@ -64,7 +64,7 @@ There are three ways of combining functions:
|
|
|
64
64
|
{"other": {}} ... undefined
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
2. **
|
|
67
|
+
2. **Merge**: `< f1, f2,... >`, e.g., `<.toto, .titi>` extracts field
|
|
68
68
|
`toto` if present; otherwise extracts `titi`.
|
|
69
69
|
|
|
70
70
|
```text
|
|
@@ -74,7 +74,7 @@ There are three ways of combining functions:
|
|
|
74
74
|
{"x":{"o":{}}, "z": {"o":{}}} ... undefined
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
3. **
|
|
77
|
+
3. **Product**: `{ f1 label1, f2 label2, ...}`, e.g., `{.toto TOTO,
|
|
78
78
|
.titi TITI}` extracts two fields and builds a record out of them.
|
|
79
79
|
|
|
80
80
|
```text
|
|
@@ -86,9 +86,9 @@ There are three ways of combining functions:
|
|
|
86
86
|
|
|
87
87
|
**QUIZ**: What is:
|
|
88
88
|
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
89
|
+
- Empty composition: `()` ?
|
|
90
|
+
- Empty merge: `<>` ?
|
|
91
|
+
- Empty product: `{}` ?
|
|
92
92
|
- `{{{{} s} s} s}` ?
|
|
93
93
|
- `({{{() a} b} c} .c .b .a)` ?
|
|
94
94
|
|
|
@@ -96,9 +96,9 @@ There are three ways of combining functions:
|
|
|
96
96
|
|
|
97
97
|
### Syntactic sugar
|
|
98
98
|
|
|
99
|
-
-
|
|
100
|
-
- dot (`.`) in "projection" acts as a separator so the
|
|
101
|
-
can be omitted.
|
|
99
|
+
- Parenthesis can be omitted, except for the empty composition `()`,
|
|
100
|
+
- dot (`.`) in "projection" acts as a separator, so the surrounding
|
|
101
|
+
space can be omitted.
|
|
102
102
|
|
|
103
103
|
For example, `(.toto .titi (.0 .1))` can be written as `.toto.titi.0.1`.
|
|
104
104
|
|
|
@@ -148,8 +148,8 @@ projection, via (`.`), to the canonical string representation of the value, e.g.
|
|
|
148
148
|
|
|
149
149
|
#### Vector product
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
have the same type, and the
|
|
151
|
+
The vector product can be seen as a shorthand for a product in which all fields
|
|
152
|
+
have the same type, and the field names are integers starting from zero.
|
|
153
153
|
E.g., `{.toto 0, .titi 1, 123 2}` can be written as
|
|
154
154
|
`[.toto, .titi, 123]`.
|
|
155
155
|
|
|
@@ -531,7 +531,7 @@ For example:
|
|
|
531
531
|
|
|
532
532
|
## k-REPL (Read-Evaluate-Print Loop)
|
|
533
533
|
|
|
534
|
-
Also there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`),
|
|
534
|
+
Also, there is a REPL, `k-repl` (`./node_modules/.bin/k-repl`),
|
|
535
535
|
which acts like a toy shell for the language.
|
|
536
536
|
|
|
537
537
|
```k-repl
|
|
@@ -552,72 +552,74 @@ Very! experimental repl shell for 'k-language'...
|
|
|
552
552
|
|
|
553
553
|
## Using `k` from `javascript`
|
|
554
554
|
|
|
555
|
-
|
|
555
|
+
```javascript
|
|
556
|
+
import k from "@fraczak/k";
|
|
556
557
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
558
|
+
let k_expression = '()';
|
|
559
|
+
k.run(k_expression,"ANYTHING...");
|
|
560
|
+
// RETURNS: "ANYTHING..."
|
|
560
561
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
562
|
+
k_expression = '{"ala" name, 23 age}';
|
|
563
|
+
k.run(k_expression,"ANYTHING...");
|
|
564
|
+
// RETURNS: {"name":"ala","age":23}
|
|
564
565
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
566
|
+
k_expression = '[.year, .age]';
|
|
567
|
+
k.run(k_expression,{"year":2002,"age":19});
|
|
568
|
+
// RETURNS: [2002,19]
|
|
568
569
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
570
|
+
k_expression = '[(), ()]';
|
|
571
|
+
k.run(k_expression,"duplicate me");
|
|
572
|
+
// RETURNS: ["duplicate me","duplicate me"]
|
|
572
573
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
574
|
+
k_expression = '[[[()]]]';
|
|
575
|
+
k.run(k_expression,"nesting");
|
|
576
|
+
// RETURNS: [[["nesting"]]]
|
|
576
577
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
578
|
+
k_expression = '[[()]] {() nested, .0.0 val}';
|
|
579
|
+
k.run(k_expression,"nesting and accessing");
|
|
580
|
+
// RETURNS: {"nested":[["nesting and accessing"]],"val":"nesting and accessing"}
|
|
580
581
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
582
|
+
k_expression = '0000';
|
|
583
|
+
k.run(k_expression,{"test":"parse integer"});
|
|
584
|
+
// RETURNS: 0
|
|
584
585
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
586
|
+
k_expression = '[.y,.x] PLUS';
|
|
587
|
+
k.run(k_expression,{"x":3,"y":4});
|
|
588
|
+
// RETURNS: 7
|
|
588
589
|
|
|
589
|
-
|
|
590
|
+
var k_fn = k.compile('{.name nom, <[.age, 18] GT .0, [.age, 12] GT "ado", "enfant"> age}');
|
|
590
591
|
|
|
591
|
-
|
|
592
|
-
|
|
592
|
+
k_fn({"age":23,"name":"Emily"});
|
|
593
|
+
// RETURNS: {"nom":"Emily","age":23}
|
|
593
594
|
|
|
594
|
-
|
|
595
|
-
|
|
595
|
+
k_fn({"age":16,"name":"Katrina"});
|
|
596
|
+
// RETURNS: {"nom":"Katrina","age":"ado"}
|
|
596
597
|
|
|
597
|
-
|
|
598
|
-
|
|
598
|
+
k_fn({"age":2,"name":"Mark"});
|
|
599
|
+
// RETURNS: {"nom":"Mark","age":"enfant"}
|
|
599
600
|
|
|
600
|
-
|
|
601
|
+
k_fn = k.compile('$t = < i: int, t: [ t ] > ; <$t, $int>');
|
|
601
602
|
|
|
602
|
-
|
|
603
|
-
|
|
603
|
+
k_fn(1);
|
|
604
|
+
// RETURNS: 1
|
|
604
605
|
|
|
605
|
-
|
|
606
|
-
|
|
606
|
+
k_fn({"i":1});
|
|
607
|
+
// RETURNS: {"i":1}
|
|
607
608
|
|
|
608
|
-
|
|
609
|
-
|
|
609
|
+
k_fn([{"i":2},{"i":3},{"t":[]}]);
|
|
610
|
+
// RETURNS: undefined
|
|
610
611
|
|
|
611
|
-
|
|
612
|
-
|
|
612
|
+
k_fn({"t":[{"i":2},{"i":3},{"t":[]}]});
|
|
613
|
+
// RETURNS: {"t":[{"i":2},{"i":3},{"t":[]}]}
|
|
613
614
|
|
|
614
|
-
|
|
615
|
+
k_fn = k.compile('$ < < [ int ] ints, [ bool ] bools > list, string None>');
|
|
615
616
|
|
|
616
|
-
|
|
617
|
-
|
|
617
|
+
k_fn({"None":"None"});
|
|
618
|
+
// RETURNS: {"None":"None"}
|
|
618
619
|
|
|
619
|
-
|
|
620
|
-
|
|
620
|
+
k_fn({"list":{"ints":[]}});
|
|
621
|
+
// RETURNS: {"list":{"ints":[]}}
|
|
621
622
|
|
|
622
|
-
|
|
623
|
-
|
|
623
|
+
k_fn({"list":{"ints":[1,2,3]}});
|
|
624
|
+
// RETURNS: {"list":{"ints":[1,2,3]}}
|
|
625
|
+
```
|
package/export.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { prettyRel, patterns2filters } from "./pretty.mjs";
|
|
3
3
|
import { hash } from "./hash.mjs";
|
|
4
4
|
|
|
5
|
-
function simplifyRel(relDef) {
|
|
5
|
+
function simplifyRel(relDef,rels) {
|
|
6
6
|
// remove filters and codes
|
|
7
7
|
const prune = (rel) => {
|
|
8
8
|
const newRel = {...rel};
|
|
@@ -32,6 +32,13 @@ function simplifyRel(relDef) {
|
|
|
32
32
|
case 'code':
|
|
33
33
|
case 'filter':
|
|
34
34
|
newRel.op = "identity";
|
|
35
|
+
break;
|
|
36
|
+
case 'ref':
|
|
37
|
+
// console.log("ref", rel.ref, rels[rel.ref]);
|
|
38
|
+
if (["filter","code","identity"].includes(rels[rel.ref]?.def.op)){
|
|
39
|
+
newRel.op = "identity";
|
|
40
|
+
};
|
|
41
|
+
break;
|
|
35
42
|
default:
|
|
36
43
|
break;
|
|
37
44
|
};
|
|
@@ -44,13 +51,13 @@ function simplifyRel(relDef) {
|
|
|
44
51
|
const newRel = prune(rel);
|
|
45
52
|
let resultRel = {...newRel};
|
|
46
53
|
if (newRel.op == "identity")
|
|
47
|
-
return filters[0];
|
|
54
|
+
return {...resultRel, ...filters[0]};
|
|
48
55
|
|
|
49
56
|
if (newRel.op == "comp") {
|
|
50
57
|
resultRel.comp = [ filters[0], ...newRel.comp, filters[1] ];
|
|
51
58
|
return resultRel;
|
|
52
59
|
}
|
|
53
|
-
return {
|
|
60
|
+
return {...resultRel,
|
|
54
61
|
op: "comp",
|
|
55
62
|
comp: [filters[0], newRel, filters[1] ]
|
|
56
63
|
};
|
|
@@ -108,11 +115,12 @@ function assignCanonicalNames(scc, rels, relAlias) {
|
|
|
108
115
|
const relDef = rels[relName];
|
|
109
116
|
if (relDef.def.op == "ref" && rels[relDef.def.ref] != undefined) {
|
|
110
117
|
// inlining if direct alias to a non built-in relation
|
|
111
|
-
relDef.
|
|
118
|
+
// relDef.def = rels[relDef.def.ref]; // simplifyRel(rels[relDef.def.ref],rels);
|
|
119
|
+
rels[relName] = rels[relDef.def.ref];
|
|
112
120
|
} else {
|
|
113
|
-
relDef.
|
|
121
|
+
relDef.def = simplifyRel(relDef,rels);
|
|
114
122
|
}
|
|
115
|
-
newAlias[relName] = theID(relAlias,
|
|
123
|
+
newAlias[relName] = theID(relAlias, rels[relName].def, scc, relName);
|
|
116
124
|
return newAlias;
|
|
117
125
|
}, {});
|
|
118
126
|
const newNames = [...new Set(Object.values(newAlias))];
|
package/package.json
CHANGED
package/parser.jison
CHANGED
|
@@ -43,7 +43,7 @@ function fromEscString(escString) {
|
|
|
43
43
|
|
|
44
44
|
%%
|
|
45
45
|
[/][*]([^*]*[*]+[^*/])*[^*]*[*]+[/] /* c-comment */
|
|
46
|
-
("//"|"#"|"%"|"--")[^\n]*
|
|
46
|
+
("//"|"#"|"%"|"--"|"\\")[^\n]* /* one line comment */
|
|
47
47
|
\s+ /* blanks */
|
|
48
48
|
"<" return 'LA';
|
|
49
49
|
"{" return 'LC';
|
package/parser.mjs
CHANGED
|
@@ -846,7 +846,7 @@ case 25:return 47;
|
|
|
846
846
|
break;
|
|
847
847
|
}
|
|
848
848
|
},
|
|
849
|
-
rules: [/^(?:[/][*]([^*]*[*]+[^*/])*[^*]*[*]+[/])/,/^(?:(
|
|
849
|
+
rules: [/^(?:[/][*]([^*]*[*]+[^*/])*[^*]*[*]+[/])/,/^(?:(\/\/|#|%|--|\\)[^\n]*)/,/^(?:\s+)/,/^(?:<)/,/^(?:\{)/,/^(?:\[)/,/^(?:\()/,/^(?:>)/,/^(?:\})/,/^(?:\])/,/^(?:\))/,/^(?:=)/,/^(?:\.\.\.)/,/^(?:\.)/,/^(?:,)/,/^(?:;)/,/^(?::)/,/^(?:\$)/,/^(?:\?)/,/^(?:\|)/,/^(?:\^)/,/^(?:"([^"\\]|\\(.|\n))*"|'([^'\\]|\\(.|\n))*')/,/^(?:[a-zA-Z_][a-zA-Z0-9_?!]*)/,/^(?:0|[-]?[1-9][0-9]*)/,/^(?:\+\+\+)/,/^(?:$)/],
|
|
850
850
|
conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],"inclusive":true}}
|
|
851
851
|
});
|
|
852
852
|
return lexer;
|
package/patterns.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { assignCanonicalNames } from "./export.mjs";
|
|
|
8
8
|
const unitCode = codes.unitCode;
|
|
9
9
|
|
|
10
10
|
function relDefToString(relDef) {
|
|
11
|
-
|
|
11
|
+
const start = new Date().getTime();
|
|
12
12
|
const dumpPatterns = (rel) => {
|
|
13
13
|
switch (rel.op) {
|
|
14
14
|
case "product":
|
|
@@ -39,14 +39,20 @@ function relDefToString(relDef) {
|
|
|
39
39
|
// console.log(relDef.typePatternGraph.edges);
|
|
40
40
|
// console.log(relDef.def);
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const result = JSON.stringify(dumpPatterns(relDef.def).map((x) => {
|
|
43
43
|
const { pattern, fields, type} = relDef.typePatternGraph.get_pattern(x);
|
|
44
44
|
return {pattern, fields, type};
|
|
45
45
|
}));
|
|
46
|
+
|
|
47
|
+
let took = new Date().getTime() - start;
|
|
48
|
+
if (took > 200) {
|
|
49
|
+
console.log(` -- RelDefToString: size=${result.length}, took ${took} msecs`);
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
function compactRel(relDef, name = "") {
|
|
49
|
-
|
|
55
|
+
const start = new Date().getTime();
|
|
50
56
|
const {def, typePatternGraph, varRefs } = relDef;
|
|
51
57
|
// console.log("COMPACTING typePatternGraph of size", typePatternGraph.size());
|
|
52
58
|
|
|
@@ -92,14 +98,17 @@ function compactRel(relDef, name = "") {
|
|
|
92
98
|
outputPatternId: renumbering[x.outputPatternId]
|
|
93
99
|
}));;
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
const time = new Date().getTime() - start;
|
|
102
|
+
if (time > 500 ) {
|
|
103
|
+
console.log(`[${new Date().getTime() - start} msecs] - COMPACTED ${name}.typePatternGraph [${typePatternGraph.size()}->${newTypePatternGraph.size()}]`);
|
|
104
|
+
}
|
|
96
105
|
return {def: newDef, typePatternGraph: newTypePatternGraph, varRefs: newVarRefs};
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
function patterns(representatives, rels) {
|
|
100
109
|
// INPUT:
|
|
101
|
-
// codes: {"
|
|
102
|
-
// representatives:{"{}": "
|
|
110
|
+
// codes: {"KL": {"code": "product", "product": {}}, ...}
|
|
111
|
+
// representatives:{"{}": "KL", ...}
|
|
103
112
|
// rels: {"rlz": [{op: comp,...}, ...], ...}
|
|
104
113
|
|
|
105
114
|
const relAlias = {};
|
|
@@ -407,7 +416,7 @@ function patterns(representatives, rels) {
|
|
|
407
416
|
rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
|
|
408
417
|
break;
|
|
409
418
|
default:
|
|
410
|
-
throw new Error(`No definition found for ${rel.ref}`);
|
|
419
|
+
throw new Error(`No definition found for '${rel.ref}'`);
|
|
411
420
|
}
|
|
412
421
|
}
|
|
413
422
|
|
|
@@ -462,7 +471,7 @@ function patterns(representatives, rels) {
|
|
|
462
471
|
// we will also generate the canonical representation of the relations in C and replace
|
|
463
472
|
// the name to the relation by the hash of the canonical representation
|
|
464
473
|
|
|
465
|
-
// console.log("
|
|
474
|
+
// console.log("- SCC in order", sccInOrder);
|
|
466
475
|
|
|
467
476
|
for (const scc of sccInOrder) {
|
|
468
477
|
|
|
@@ -470,7 +479,7 @@ function patterns(representatives, rels) {
|
|
|
470
479
|
|
|
471
480
|
for (let iteration = 1; iteration <= maxNumberOfIterations; iteration++) {
|
|
472
481
|
|
|
473
|
-
// console.log(
|
|
482
|
+
// console.log(`-- Iteration ${iteration}/${maxNumberOfIterations} for SCC: { ${DAGnodes[scc].scc} }`);
|
|
474
483
|
// 4.1 For every r in C, compute the new typePatternGraph of r, i.e.:
|
|
475
484
|
|
|
476
485
|
let before = JSON.stringify(DAGnodes[scc].scc.map( relName => {
|
|
@@ -481,11 +490,11 @@ function patterns(representatives, rels) {
|
|
|
481
490
|
// console.log(relDefToString(rels[relName]));
|
|
482
491
|
return relDefToString(rels[relName]);
|
|
483
492
|
}));
|
|
484
|
-
|
|
493
|
+
|
|
485
494
|
let after = JSON.stringify(DAGnodes[scc].scc.map( relName => {
|
|
486
495
|
const relDef = compactRel(rels[relName],relName);
|
|
487
496
|
for (let i = 0; i < relDef.varRefs.length; i++) {
|
|
488
|
-
|
|
497
|
+
const start = new Date().getTime();
|
|
489
498
|
let varRel = relDef.varRefs[i];
|
|
490
499
|
let { varName } = varRel;
|
|
491
500
|
try {
|
|
@@ -493,7 +502,6 @@ function patterns(representatives, rels) {
|
|
|
493
502
|
let varInputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[0]);
|
|
494
503
|
let varOutputPatternId = varRootDef.typePatternGraph.find(varRootDef.def.patterns[1]);
|
|
495
504
|
let cloned = varRootDef.typePatternGraph.clone([varInputPatternId, varOutputPatternId], relDef.typePatternGraph);
|
|
496
|
-
|
|
497
505
|
relDef.typePatternGraph.unify(`ref:input ${relName}(${varName})`, varRel.inputPatternId, cloned[varInputPatternId]);
|
|
498
506
|
relDef.typePatternGraph.unify("ref:output", varRel.outputPatternId, cloned[varOutputPatternId]);
|
|
499
507
|
} catch (e) {
|
|
@@ -502,6 +510,10 @@ function patterns(representatives, rels) {
|
|
|
502
510
|
- ${e.message}`;
|
|
503
511
|
throw e;
|
|
504
512
|
}
|
|
513
|
+
const tookMs = new Date().getTime() - start;
|
|
514
|
+
if (tookMs > 200) {
|
|
515
|
+
console.log(` ---- Processing ${relName}(.., x${i}=${relDef.varRefs[i].varName}, ..) took: ${tookMs} ms`);
|
|
516
|
+
}
|
|
505
517
|
}
|
|
506
518
|
rels[relName] = compactRel(relDef,relName);
|
|
507
519
|
return relDefToString(rels[relName]);
|
package/repl.mjs
CHANGED
|
@@ -51,8 +51,8 @@ rl.on('line', (line) => {
|
|
|
51
51
|
process.exit(0);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
let old_val = {};
|
|
55
|
-
let val =
|
|
54
|
+
let old_val = k.compile("{}")({});
|
|
55
|
+
let val = old_val;
|
|
56
56
|
const printVal = function (v = val) {
|
|
57
57
|
if (v === undefined)
|
|
58
58
|
console.log("...",v);
|
|
@@ -140,6 +140,9 @@ function evaluate(line) {
|
|
|
140
140
|
codeName = codeName.slice(1);
|
|
141
141
|
const canonicalName = run.defs.representatives[codeName] || codeName;
|
|
142
142
|
const codeExp = find( canonicalName );
|
|
143
|
+
if (codeExp.def == "builtin") {
|
|
144
|
+
return console.log(` $ ${canonicalName} = :builtin;`);
|
|
145
|
+
}
|
|
143
146
|
console.log(` $ ${canonicalName} = ${prettyCode(run.defs.representatives, codeExp)}; -- ${codeExp.def}`);
|
|
144
147
|
// --pp
|
|
145
148
|
} else if (line.match(/^[ \n\t]*(?:--pp)?$/)) {
|
|
@@ -187,7 +190,7 @@ function evaluate(line) {
|
|
|
187
190
|
console.log({
|
|
188
191
|
name: canonicalRelName,
|
|
189
192
|
type: `${filtersStr[0]} --> ${filtersStr[1]}`,
|
|
190
|
-
def: prettyRel(rel.
|
|
193
|
+
def: prettyRel(rel.def)
|
|
191
194
|
});
|
|
192
195
|
if (DEBUG_FLAG) {
|
|
193
196
|
val = exportRelation(run.defs.rels, run.defs.relAlias, relName);
|
package/typing.mjs
CHANGED
|
@@ -3,20 +3,36 @@ import codes from "./codes.mjs";
|
|
|
3
3
|
|
|
4
4
|
const unitCode = codes.unitCode;
|
|
5
5
|
|
|
6
|
-
function
|
|
7
|
-
return
|
|
6
|
+
function asSet(vector) {
|
|
7
|
+
return Array.from(new Set(vector));
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
function setUnion(...vectors) {
|
|
11
|
-
|
|
11
|
+
const unionSet = new Set();
|
|
12
|
+
for (const v of vectors) {
|
|
13
|
+
for (const element of v) {
|
|
14
|
+
unionSet.add(element);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return Array.from(unionSet);
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
function subsetP(v1,v2) {
|
|
15
|
-
const v2Set =
|
|
16
|
-
return
|
|
21
|
+
const v2Set = new Set(v2);
|
|
22
|
+
return v1.every(x => v2Set.has(x));
|
|
17
23
|
}
|
|
24
|
+
|
|
18
25
|
function eqsetP(v1,v2) {
|
|
19
|
-
|
|
26
|
+
const v1Set = new Set(v1);
|
|
27
|
+
const v2Set = new Set(v2);
|
|
28
|
+
if (v1Set.size != v2Set.size) return false;
|
|
29
|
+
|
|
30
|
+
for (const value of v1Set) {
|
|
31
|
+
if (!v2Set.has(value)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
const matchAllPattern = {pattern: '(...)', fields: []};
|
|
@@ -76,7 +92,7 @@ class TypePatternForest {
|
|
|
76
92
|
// const node = {...flatTypePattern, _id: id};
|
|
77
93
|
const node = {...flatTypePattern}
|
|
78
94
|
that.nodes.push(node);
|
|
79
|
-
|
|
95
|
+
Array.from(new Set(children.map(that.find.bind(that))))
|
|
80
96
|
.forEach(rep => {that.parent[rep] = id});
|
|
81
97
|
return id;
|
|
82
98
|
}
|
|
@@ -139,7 +155,6 @@ class TypePatternGraph {
|
|
|
139
155
|
for( const id of singletonPatterns) {
|
|
140
156
|
this.unify('singleton', id, this.getTypeId(representatives[`-${id}-`]));
|
|
141
157
|
}
|
|
142
|
-
|
|
143
158
|
}
|
|
144
159
|
|
|
145
160
|
|
|
@@ -165,27 +180,29 @@ class TypePatternGraph {
|
|
|
165
180
|
)
|
|
166
181
|
);
|
|
167
182
|
|
|
168
|
-
const gEdgesWithoutParrallelEdges =
|
|
169
|
-
|
|
183
|
+
const gEdgesWithoutParrallelEdges = Array.from(new Set(gEdges));
|
|
170
184
|
const gGraph = new Graph(gEdgesWithoutParrallelEdges);
|
|
171
185
|
const queue = gNodes.filter(x =>
|
|
172
186
|
this.patterns.nodes[x].pattern in {'()':1, '(...)':1,'{...}':1, '<...>':1});
|
|
173
|
-
const
|
|
187
|
+
const excludedSet = new Set(queue);
|
|
188
|
+
|
|
174
189
|
while (queue.length > 0) {
|
|
175
190
|
const x = queue.pop();
|
|
176
191
|
for (const e of gGraph.dst[x] || []) {
|
|
177
192
|
const y = gGraph.edges[e].src;
|
|
178
|
-
if (y
|
|
179
|
-
|
|
193
|
+
if (excludedSet.has(y)) continue;
|
|
194
|
+
excludedSet.add(y);
|
|
180
195
|
queue.push(y);
|
|
181
196
|
}
|
|
182
197
|
}
|
|
183
|
-
|
|
198
|
+
|
|
199
|
+
return gNodes.filter(x => !((excludedSet.has(x)) || (this.patterns.nodes[x].pattern == 'type')));
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
getCompressed() {
|
|
187
203
|
const newTypePatternGraph = new TypePatternGraph();
|
|
188
204
|
const renamed = this.cloneAll(newTypePatternGraph);
|
|
205
|
+
|
|
189
206
|
newTypePatternGraph.turnSingletonPatternsIntoCodes();
|
|
190
207
|
|
|
191
208
|
Object.keys(renamed).forEach( id => {
|
|
@@ -223,7 +240,9 @@ class TypePatternGraph {
|
|
|
223
240
|
|
|
224
241
|
// console.log("equivalence", equivalence);
|
|
225
242
|
|
|
243
|
+
let count = 0;
|
|
226
244
|
const areDifferent = (i1,i2) => {
|
|
245
|
+
count++;
|
|
227
246
|
const p1 = newTypePatternGraph.get_pattern(i1);
|
|
228
247
|
const p2 = newTypePatternGraph.get_pattern(i2);
|
|
229
248
|
if (p1.pattern != p2.pattern) return true;
|
|
@@ -239,7 +258,9 @@ class TypePatternGraph {
|
|
|
239
258
|
return false;
|
|
240
259
|
}
|
|
241
260
|
let changed = true;
|
|
261
|
+
let count2 = 0;
|
|
242
262
|
while (changed) {
|
|
263
|
+
count2++;
|
|
243
264
|
changed = false;
|
|
244
265
|
const equivalenceSize = equivalence.length;
|
|
245
266
|
for (let eqClassPos = 0; eqClassPos < equivalenceSize; eqClassPos++) {
|
|
@@ -285,7 +306,7 @@ class TypePatternGraph {
|
|
|
285
306
|
for (const oldId in newIds) {
|
|
286
307
|
const newId = newIds[oldId];
|
|
287
308
|
const pattern = newTypePatternGraph.get_pattern(oldId);
|
|
288
|
-
const newPattern = compressedTypePatternGraph.get_pattern(newId);
|
|
309
|
+
// const newPattern = compressedTypePatternGraph.get_pattern(newId);
|
|
289
310
|
// console.log("patterns", pattern, newPattern);
|
|
290
311
|
if (pattern.pattern == 'type')
|
|
291
312
|
continue;
|
|
@@ -294,7 +315,7 @@ class TypePatternGraph {
|
|
|
294
315
|
compressedTypePatternGraph.edges[newId] = {};
|
|
295
316
|
for (const lab in edges) {
|
|
296
317
|
// console.log("edges[lab]", edges[lab]);
|
|
297
|
-
compressedTypePatternGraph.edges[newId][lab] =
|
|
318
|
+
compressedTypePatternGraph.edges[newId][lab] = asSet(edges[lab].map(x =>
|
|
298
319
|
newIds[reps[newTypePatternGraph.find(x)][0]]));
|
|
299
320
|
}
|
|
300
321
|
}
|
|
@@ -308,6 +329,7 @@ class TypePatternGraph {
|
|
|
308
329
|
// console.log("nodes", compressedTypePatternGraph.patterns.nodes);
|
|
309
330
|
// console.log("edges", compressedTypePatternGraph.edges);
|
|
310
331
|
// console.log("--------------------")
|
|
332
|
+
|
|
311
333
|
return {typePatternGraph: compressedTypePatternGraph, remapping};
|
|
312
334
|
}
|
|
313
335
|
|
|
@@ -343,7 +365,7 @@ class TypePatternGraph {
|
|
|
343
365
|
const new_id = result[i];
|
|
344
366
|
targetGraph.edges[new_id] = {};
|
|
345
367
|
for (const lab in edges) {
|
|
346
|
-
targetGraph.edges[new_id][lab] =
|
|
368
|
+
targetGraph.edges[new_id][lab] = asSet(edges[lab].map(x => result[find(x)]));
|
|
347
369
|
}
|
|
348
370
|
}
|
|
349
371
|
return result;
|
|
@@ -584,7 +606,7 @@ class TypePatternGraph {
|
|
|
584
606
|
|
|
585
607
|
unify(rule, ...ids) {
|
|
586
608
|
const find = (x) => this.find(x);
|
|
587
|
-
const reps =
|
|
609
|
+
const reps = Array.from(new Set(ids.map(find)));
|
|
588
610
|
if (reps.length < 2) return false;
|
|
589
611
|
const rep_patters = reps.map(x => {
|
|
590
612
|
const pattern = this.get_pattern(x);
|
|
@@ -615,8 +637,8 @@ class TypePatternGraph {
|
|
|
615
637
|
for (const lab in edges) {
|
|
616
638
|
// if the pattern is [] all labels are treated as 'vector-member'
|
|
617
639
|
const new_lab = new_pattern.pattern == '[]' ? 'vector-member' : lab;
|
|
618
|
-
const dests =
|
|
619
|
-
this.edges[new_id][new_lab] =
|
|
640
|
+
const dests = asSet(edges[lab].map(find));
|
|
641
|
+
this.edges[new_id][new_lab] = asSet([...(this.edges[new_id][new_lab] || []), ...dests]);
|
|
620
642
|
}
|
|
621
643
|
}
|
|
622
644
|
// add stuff for types
|
|
@@ -629,7 +651,7 @@ class TypePatternGraph {
|
|
|
629
651
|
// all edges are goin to unit type
|
|
630
652
|
const unit_id = this.getTypeId(unitCode);
|
|
631
653
|
for (const lab in this.edges[new_id]) {
|
|
632
|
-
this.edges[new_id][lab]
|
|
654
|
+
this.edges[new_id][lab] = asSet([unit_id]);
|
|
633
655
|
}
|
|
634
656
|
}; break;
|
|
635
657
|
case 'product':
|
|
@@ -637,7 +659,7 @@ class TypePatternGraph {
|
|
|
637
659
|
const type_fields = Object.keys(code[code.code]);
|
|
638
660
|
for (const lab of type_fields) {
|
|
639
661
|
const target_type_id = this.getTypeId(code[code.code][lab]);
|
|
640
|
-
this.edges[new_id][lab]=
|
|
662
|
+
this.edges[new_id][lab]= asSet([...(this.edges[new_id][lab] ||[]), target_type_id]);
|
|
641
663
|
}
|
|
642
664
|
}; break;
|
|
643
665
|
case 'vector': {
|
|
@@ -645,18 +667,13 @@ class TypePatternGraph {
|
|
|
645
667
|
const target_type_id = this.getTypeId(code.vector);
|
|
646
668
|
|
|
647
669
|
this.edges[new_id] = {
|
|
648
|
-
'vector-member':
|
|
649
|
-
[target_type_id]: target_type_id,
|
|
650
|
-
...asMapSet(
|
|
651
|
-
setUnion(...Object.values(this.edges[new_id]).map(aSet => Object.values(aSet)))
|
|
652
|
-
)
|
|
653
|
-
}
|
|
670
|
+
'vector-member': asSet([ target_type_id, ...Object.values(this.edges[new_id])])
|
|
654
671
|
}
|
|
655
672
|
}
|
|
656
673
|
}
|
|
657
674
|
}
|
|
658
675
|
for ( const lab of Object.keys(this.edges[new_id]) ) {
|
|
659
|
-
this.unify(rule+'.', ...
|
|
676
|
+
this.unify(rule+'.', ...this.edges[new_id][lab]);
|
|
660
677
|
}
|
|
661
678
|
|
|
662
679
|
return true;
|
|
@@ -667,7 +684,7 @@ class TypePatternGraph {
|
|
|
667
684
|
const find = (x) => this.patterns.find(x);
|
|
668
685
|
this.edges[id] = {};
|
|
669
686
|
for (const [lab,dest] of Object.entries(fields)) {
|
|
670
|
-
this.edges[id][lab] =
|
|
687
|
+
this.edges[id][lab] = asSet(dest.map(find));
|
|
671
688
|
}
|
|
672
689
|
return id;
|
|
673
690
|
}
|