@fraczak/k 5.0.0 → 5.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/BIG_PICTURE.md +87 -36
- package/Code-derivation-tests/bnat.mjs +1 -1
- package/export.mjs +14 -6
- package/package.json +1 -1
- package/parser.jison +1 -1
- package/parser.mjs +1 -1
- package/patterns.mjs +3 -3
- package/repl.mjs +6 -3
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/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
|
@@ -98,8 +98,8 @@ function compactRel(relDef, name = "") {
|
|
|
98
98
|
|
|
99
99
|
function patterns(representatives, rels) {
|
|
100
100
|
// INPUT:
|
|
101
|
-
// codes: {"
|
|
102
|
-
// representatives:{"{}": "
|
|
101
|
+
// codes: {"KL": {"code": "product", "product": {}}, ...}
|
|
102
|
+
// representatives:{"{}": "KL", ...}
|
|
103
103
|
// rels: {"rlz": [{op: comp,...}, ...], ...}
|
|
104
104
|
|
|
105
105
|
const relAlias = {};
|
|
@@ -407,7 +407,7 @@ function patterns(representatives, rels) {
|
|
|
407
407
|
rel.patterns = [rootDef.typePatternGraph.addNewNode(), rootDef.typePatternGraph.addNewNode()];
|
|
408
408
|
break;
|
|
409
409
|
default:
|
|
410
|
-
throw new Error(`No definition found for ${rel.ref}`);
|
|
410
|
+
throw new Error(`No definition found for '${rel.ref}'`);
|
|
411
411
|
}
|
|
412
412
|
}
|
|
413
413
|
|
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);
|