@fraczak/k 2.1.6 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BIG_PICTURE.md +211 -0
- package/Code-derivation-tests/bnat.mjs +10 -0
- package/Code-derivation-tests/built-in.mjs +19 -0
- package/Code-derivation-tests/dot.mjs +15 -0
- package/Code-derivation-tests/index.mjs +20 -0
- package/Code-derivation-tests/loop.mjs +11 -0
- package/Code-derivation-tests/nat.mjs +10 -0
- package/Code-derivation-tests/not-not.mjs +14 -0
- package/Code-derivation-tests/product.mjs +21 -0
- package/Code-derivation-tests/rlz.mjs +14 -0
- package/Code-derivation-tests/vector.mjs +28 -0
- package/INTRO.md +88 -0
- package/README.md +68 -14
- package/TODO.md +54 -0
- package/TYPES.md +32 -0
- package/bug.json +419 -0
- package/chatGPT.md +235 -0
- package/chatGPT.md~ +119 -0
- package/code-resolution.md +121 -0
- package/code-resolution.mjs +51 -0
- package/codes.mjs +59 -4
- package/filters.mjs +59 -0
- package/hash.mjs +39 -0
- package/index.mjs +55 -12
- package/package.json +2 -2
- package/parser.jison +111 -44
- package/parser.mjs +146 -88
- package/patterns.mjs +601 -0
- package/pretty.mjs +27 -0
- package/repl.mjs +35 -6
- package/run.mjs +4 -1
- package/symbol-table.mjs +1 -1
- package/test-filters.mjs +45 -0
- package/test-fingerprint.mjs +38 -0
- package/test.mjs +1 -43
package/BIG_PICTURE.md
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
|
|
2
|
+
`Kernel-code` is an experiment aiming to define a "perfect programming language".
|
|
3
|
+
|
|
4
|
+
## Basics
|
|
5
|
+
|
|
6
|
+
The language describes "partial first-order functions" on "printable" values.
|
|
7
|
+
The printable values can be seen as rooted, edge-labeled trees, intuitively similar to an
|
|
8
|
+
XML or a JSON document. The values are always typed.
|
|
9
|
+
|
|
10
|
+
### Types (codes)
|
|
11
|
+
|
|
12
|
+
Formally, a _type_ is a (subclass of) finite tree automata. The type defines a set of its values,
|
|
13
|
+
which corresponds to the set of trees accepted by the automaton.
|
|
14
|
+
Therefore, every type admits a unique normal form in terms of its minimal deterministic tree automaton.
|
|
15
|
+
|
|
16
|
+
The set of all types can be seen as the universal deterministic (infinite) tree automaton.
|
|
17
|
+
Every node of that automaton defines a type; only a finite set of nodes are reachable from
|
|
18
|
+
any node.
|
|
19
|
+
|
|
20
|
+
Types are defined by two constructs:
|
|
21
|
+
|
|
22
|
+
1. Product
|
|
23
|
+
2. Union (also called tagged or disjoint union)
|
|
24
|
+
|
|
25
|
+
Given a set (possibly empty) of types `t1, t2, ..., tn` and a set of pairwise different tags (labels) `l1, l2, ..., ln`,
|
|
26
|
+
|
|
27
|
+
- by `{ t1 l1, t2 l2, ..., tn ln }`, we define a _product type_ with exactly `n` projection functions `.l1`, `.l2`, `...`, `.ln`,
|
|
28
|
+
mapping a value of type `{t1 l1, t2 l2, ..., tn ln}` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
29
|
+
- by `< t1 l1, t2 l2, ..., tn ln >`, we define a _union type_ with exactly `n` projection functions `.l1`, `.l2`, `...`, `.ln`,
|
|
30
|
+
mapping a value of type `<t1 l1, t2 l2, ..., tn ln>` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
31
|
+
|
|
32
|
+
Intuitively, the projection functions on a product type are all total (meaning, they are defined for every value of the
|
|
33
|
+
input product type), whereas in the case of a union type, exactly one projection function is defined
|
|
34
|
+
for a given value of the union type.
|
|
35
|
+
|
|
36
|
+
Some properties of these types:
|
|
37
|
+
|
|
38
|
+
1. Singleton product and singleton union types, `{ t label }` and `< t label >`, are equivalent as they have
|
|
39
|
+
the same set of values (and the same projection function `.label`). That's why, we not allow singleton product nor
|
|
40
|
+
singleton union types in the syntax of the language. The other benefit is that we will be able to use the same systax
|
|
41
|
+
for values of both product and union types, interpretting every singleton product-like value, e.g., `{ a_value label }`,
|
|
42
|
+
as a union type value (a variant).
|
|
43
|
+
2. The empty product type `{}` has a single value, called the _unit_ and also denoted by `{}`, which has no fields.
|
|
44
|
+
3. The empty union type `<>` has no values.
|
|
45
|
+
4. The product and union types form dual categories.
|
|
46
|
+
|
|
47
|
+
### Partial functions
|
|
48
|
+
|
|
49
|
+
A _partial function_ is a function that is not defined for every value of its domain.
|
|
50
|
+
|
|
51
|
+
There are three ways of combining partial functions:
|
|
52
|
+
|
|
53
|
+
1. __Composition__: we write a composition of `f` and `g` as `(f g)`, meaning that if `f` is defined on valye `x` producing `y`,
|
|
54
|
+
and `g` is defined on value `y` producing `z`, then `(f g)` is defined on value `x` producing `z`.
|
|
55
|
+
2. __Union__: we write `< f1, f2 >` for a union of two functions `f1` and `f2`, meaning that
|
|
56
|
+
if `f1` is defined on value `x` producing `y`, then `< f1, f2 >`
|
|
57
|
+
is defined on value `x` producing `y`. Otherwise, i.e., when `f1` is not defined for `x`, `< f1 f2 >` acts as `f2`.
|
|
58
|
+
3. __Product__: we write `{f1 lab1, f2 lab2 }` for a product of two functions `f1` and `f2`, meaning that if `f1` and `f2`
|
|
59
|
+
are both defined on value `x` producing `y1` and `y2`, respectively, then `{f1 lab1, f2 lab2 }`
|
|
60
|
+
is defined on value `x` producing `{y1 lab1, y2 lab2}`. Otherwise, `{f1 lab1, f2 lab2 }` is not defined for `x`.
|
|
61
|
+
|
|
62
|
+
Actually, all of the above operations take any number of arguments, not only two. For example,
|
|
63
|
+
empty composition, `()`, defines the identity function.
|
|
64
|
+
|
|
65
|
+
### Program
|
|
66
|
+
|
|
67
|
+
A `kernel-core` program is a set of definitions of types and partial functions followed by an _expression_,
|
|
68
|
+
which is the partial function the program defines.
|
|
69
|
+
|
|
70
|
+
The language syntax is defined by:
|
|
71
|
+
|
|
72
|
+
```bnf
|
|
73
|
+
program ::= (type_definition | function_definition) * expression
|
|
74
|
+
|
|
75
|
+
type_definition ::= '$' type_name '=' type ';'
|
|
76
|
+
type ::= '{' type_label_list '}' | '<' type_label_list '>'
|
|
77
|
+
type_label_list ::= /* empty */ | ( type_expression label_name ',' ) + type_expression label_name
|
|
78
|
+
type_expression ::= type_name | type
|
|
79
|
+
|
|
80
|
+
function_definition ::= function_name '=' expression ';'
|
|
81
|
+
expression ::= projection | typing | composition | union | product | function_name
|
|
82
|
+
projection ::= '.' label_name
|
|
83
|
+
typing ::= '$' tape_expression
|
|
84
|
+
composition ::= '(' expression * ')'
|
|
85
|
+
union ::= '<' (expression ',') + expression '>'
|
|
86
|
+
product ::= '{' expression_label_list '}'
|
|
87
|
+
expression_label_list ::= /* empty */ | ( expression label_name ',' ) * expression label
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
We assume that the `type_name`, `function_name`, and `label_name` are identifiers (strings) such that:
|
|
91
|
+
|
|
92
|
+
1. All defined type names are distinct.
|
|
93
|
+
2. All defined function names are distinct.
|
|
94
|
+
3. All label names within a type definition or a `product` expression are locally distinct.
|
|
95
|
+
|
|
96
|
+
The `typing` expression acts as a type annotation and can be seen as a 'filter', i.e., an identity
|
|
97
|
+
function defined only for the values of the corresponding type.
|
|
98
|
+
|
|
99
|
+
That's it.
|
|
100
|
+
No `int`, no `if` statement, no `loop`, no `throw`, no _closure_, no _annotations_, and no macros.
|
|
101
|
+
Just types and partial functions.
|
|
102
|
+
|
|
103
|
+
### Normalization
|
|
104
|
+
|
|
105
|
+
All type names are replaced by the hash of their canonical representation.
|
|
106
|
+
|
|
107
|
+
We will also replace the function name by the hash of the normalized unfolded definition of the function.
|
|
108
|
+
Obviously, two different definitions of the same function may have different hashes.
|
|
109
|
+
|
|
110
|
+
This approach solves the problem of modules and imports and opens the door to a universal
|
|
111
|
+
registry of types (schemas) and functions.
|
|
112
|
+
|
|
113
|
+
## Typing and Polymorphism (codes and filters/patterns)
|
|
114
|
+
|
|
115
|
+
Abstract Syntax Tree of a program after parsing consists of a dictionary of _type definitions_,
|
|
116
|
+
a dictionary of _function definitions_, and the final _main expression_.
|
|
117
|
+
|
|
118
|
+
In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
|
|
119
|
+
the program on an input.
|
|
120
|
+
|
|
121
|
+
Our objective however is to compute canonicals representations for types and normalized
|
|
122
|
+
representations for functions, so they could be effectively reused.
|
|
123
|
+
|
|
124
|
+
The normalization steps are:
|
|
125
|
+
|
|
126
|
+
1. Build the _type graph_ from all types used by the program, i.e., used in _type definitions_
|
|
127
|
+
as well as type expressions used in _function definitions_ and the _main expression_.
|
|
128
|
+
2. Annotate the expressions with types or type patterns; every node of the AST is annotated with a pair of type patterns.
|
|
129
|
+
3. Turn the _singleton_ type patterns into types and add them to the _type graph_. Go to (2) unless no change.
|
|
130
|
+
4. Normalize the function definitions by rewriting them as the fix-point of a single equation.
|
|
131
|
+
|
|
132
|
+
### Example
|
|
133
|
+
|
|
134
|
+
```k
|
|
135
|
+
$ bit = < {} o, {} i >;
|
|
136
|
+
bit0 = {{} o} $bit;
|
|
137
|
+
bit1 = {{} i} $bit;
|
|
138
|
+
|
|
139
|
+
$ byte = { bit b0, bit b1, bit b2, bit b3 };
|
|
140
|
+
zero = bit0 { () b0, () b1, () b2, () b3 } $byte;
|
|
141
|
+
|
|
142
|
+
inc = $byte
|
|
143
|
+
<
|
|
144
|
+
{ .b0.o bit0 overflown, {bit1 b0, .b1 b1, .b2 b2, .b3 b3 } byte },
|
|
145
|
+
{ .b1.o bit0 overflown, {bit0 b0, bit1 b1, .b2 b2, .b3 b3 } byte },
|
|
146
|
+
{ .b2.o bit0 overflown, {bit0 b0, bit0 b1, bit1 b2, .b3 b3 } byte },
|
|
147
|
+
{ .b3.o bit0 overflown, {bit0 b0, bit0 b1, bit0 b2, bit1 b3 } byte },
|
|
148
|
+
{ bit1 overflown, zero byte }
|
|
149
|
+
>
|
|
150
|
+
;
|
|
151
|
+
|
|
152
|
+
-- inc with overflown flag (polymorphism)
|
|
153
|
+
inc_o =
|
|
154
|
+
{ .byte inc inc, .overflown overflown }
|
|
155
|
+
{
|
|
156
|
+
.inc.byte byte,
|
|
157
|
+
<.overflown.i bit1, .inc.overflown > overflown
|
|
158
|
+
}
|
|
159
|
+
;
|
|
160
|
+
|
|
161
|
+
inc3 = inc inc_o inc_o;
|
|
162
|
+
|
|
163
|
+
inc3
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
In the above program, we define a type `bit` a union of two unit types,
|
|
167
|
+
and a type `byte` as a product, for simplicity, of four `bit`s.
|
|
168
|
+
|
|
169
|
+
Functions `bit0`, `bit1`, and `zero` are "constant polymorphic functions", meaning:
|
|
170
|
+
|
|
171
|
+
- constant: if defined, they always return exactly the same value;
|
|
172
|
+
- polymorphic: they are defined for more than one pair of input and output type: functions
|
|
173
|
+
`bit1` and `bit2` are of type `X -> bit`, and `zero` is of type `X -> byte`, where `X`
|
|
174
|
+
denotes an unconstrained type pattern.
|
|
175
|
+
|
|
176
|
+
Functions `inc` and `inc3` are (non-polymorphic) functions of types
|
|
177
|
+
`byte -> { bit overflown, byte byte }` and
|
|
178
|
+
`{ bit overflown, byte byte } -> { bit overflown, byte byte }`, respectively.
|
|
179
|
+
|
|
180
|
+
Function `inc_o` is a polymorphic function of type patterns: `X -> Y` with
|
|
181
|
+
following constraints:
|
|
182
|
+
|
|
183
|
+
- `X` is a product type with at least two fields: `byte` and `overflown`, denoted by:
|
|
184
|
+
> `?{ byte byte, Z overflown, ...}`;
|
|
185
|
+
- `Z` is a type (union or product) with field `i` denoted by:
|
|
186
|
+
> `?( V i, ...)`;
|
|
187
|
+
- `V` is unconstrained;
|
|
188
|
+
- `Y` is a product type with exactly two fields: `byte` and `overflown`, denoted by:
|
|
189
|
+
> `?{ byte byte, bit overflown }`;
|
|
190
|
+
|
|
191
|
+
The type pattern for `Y` is _singleton_, as only one type fits the pattern,
|
|
192
|
+
whereas the other type patterns are not.
|
|
193
|
+
|
|
194
|
+
In this example, we were able to find the most general type pattern for all sub-expressions of the program. However, we still do not have a way to find the most general type patterns in the general case. This is work in progress.
|
|
195
|
+
|
|
196
|
+
## Universal Schema Registry
|
|
197
|
+
|
|
198
|
+
Since the normalization process for types is fast and deterministic, we can build a universal schema registry that will store all invented types. The registry will be a key-value store, where the key is the hash of the normalized type, and the value is the normalized type itself.
|
|
199
|
+
|
|
200
|
+
All functions (non-polymorphic and polymorphic) will be renamed by the hash of their normalized definition and stored in the similar key-value store.
|
|
201
|
+
|
|
202
|
+
Non-polymorphic functions can be easily indexed by the hashes of their input and output types so that we can quickly find the function we need, e.g., for autocomplition. Searching for polymorphic functions is more complicated, as we need to find the function whose input and output type patterns fit given types.
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
## Serialization and Compilation
|
|
206
|
+
|
|
207
|
+
The language is designed to transform _codes_, i.e., serialized typed values, i.e., trees accepted by a tree automaton, into other _codes_. The transformation is done by partial functions as defined in the language. The non-recursive and tail-recursive (and even some non-tail recursive) functions can be compiled into deterministic finite (pushdown) transducers.
|
|
208
|
+
|
|
209
|
+
## Linking with Other Languages
|
|
210
|
+
|
|
211
|
+
## Streams and Arrays (Map-Reduce)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import assert from 'assert';
|
|
4
|
+
|
|
5
|
+
t(fs.readFileSync("./Examples/bnat-patterns.k").toString("utf8"), (annotated) => {
|
|
6
|
+
const {input,output} = in_out(annotated);
|
|
7
|
+
assert.equal(output.code, annotated.representatives["bnat"]);
|
|
8
|
+
assert.equal(input.code, annotated.representatives["pair"]);
|
|
9
|
+
console.log("OK");
|
|
10
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
function is_result_code(code) {
|
|
5
|
+
return function(annotated) {
|
|
6
|
+
const {output} = in_out(annotated);
|
|
7
|
+
assert.strictEqual(output.code, code);
|
|
8
|
+
console.log('OK');
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
t('true', is_result_code('bool'));
|
|
13
|
+
t('false $bool', is_result_code('bool'));
|
|
14
|
+
t('1', is_result_code('int'));
|
|
15
|
+
t('12 $int', is_result_code('int'));
|
|
16
|
+
t('"ala"', is_result_code('string'));
|
|
17
|
+
t('"a" $string', is_result_code('string'));
|
|
18
|
+
|
|
19
|
+
t('${} true $bool 1 $int "a" $string', is_result_code('string'));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
t(`
|
|
5
|
+
$b = < {} true, {} false > ;
|
|
6
|
+
$pair = { b one, b two } ;
|
|
7
|
+
|
|
8
|
+
$b {1 one, () two, () c} .one
|
|
9
|
+
|
|
10
|
+
`, (annotated) => {
|
|
11
|
+
const {input,output} = in_out(annotated);
|
|
12
|
+
assert.equal(input.code, annotated.representatives["b"]);
|
|
13
|
+
assert.equal(output.code, "int");
|
|
14
|
+
console.log("OK");
|
|
15
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { annotate } from "../index.mjs";
|
|
2
|
+
|
|
3
|
+
function in_out(annotated) {
|
|
4
|
+
const [input,output] = annotated.rels.__main__[0].patterns.map( (pat) =>
|
|
5
|
+
annotated.patternNodes[pat]
|
|
6
|
+
);
|
|
7
|
+
return { input, output };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
function t(script, cb = (annotated) => console.log(in_out(annotated))) {
|
|
12
|
+
// console.log("--- Testing Code Derivation ---");
|
|
13
|
+
const annotated = annotate(script);
|
|
14
|
+
// console.log(JSON.stringify(annotated, "", 2));
|
|
15
|
+
return cb(annotated);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default { t, in_out };
|
|
19
|
+
export { t, in_out };
|
|
20
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
t(` < .a, () > `, (annotated) => {
|
|
5
|
+
const {input,output} = in_out(annotated);
|
|
6
|
+
assert.equal(input.type, "union");
|
|
7
|
+
assert.equal(output, input);
|
|
8
|
+
console.log("OK");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Notice that the following are equivalent: '<(), .a>', '<()>', and '()';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import assert from 'assert';
|
|
4
|
+
|
|
5
|
+
t(fs.readFileSync("./Examples/nat.k").toString("utf8"), (annotated) => {
|
|
6
|
+
const {input,output} = in_out(annotated);
|
|
7
|
+
assert.equal(output.code, annotated.representatives["nat"]);
|
|
8
|
+
assert.equal(input.code, annotated.representatives["nat"]);
|
|
9
|
+
console.log("OK");
|
|
10
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
t(`$b = < {} true, {} false > ;
|
|
5
|
+
true = {{} true} $b;
|
|
6
|
+
false = {{} false} $b;
|
|
7
|
+
not = $b < .true false, .false true >;
|
|
8
|
+
not not
|
|
9
|
+
`, (annotated) => {
|
|
10
|
+
const {input,output} = in_out(annotated);
|
|
11
|
+
assert.equal(input.code, annotated.representatives["b"]);
|
|
12
|
+
assert.equal(output.code, annotated.representatives["b"]);
|
|
13
|
+
console.log("OK");
|
|
14
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
t('{}', (annotated) => {
|
|
5
|
+
const {input,output} = in_out(annotated);
|
|
6
|
+
assert.equal(input.type, null);
|
|
7
|
+
assert.equal(output.code, annotated.representatives["{}"]);
|
|
8
|
+
console.log("OK");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
t(`
|
|
12
|
+
$b = < {} true, {} false > ;
|
|
13
|
+
$pair = { b one, b two } ;
|
|
14
|
+
|
|
15
|
+
{() one, () two} $pair
|
|
16
|
+
`, (annotated) => {
|
|
17
|
+
const {input,output} = in_out(annotated);
|
|
18
|
+
assert.equal(input.code, annotated.representatives["b"]);
|
|
19
|
+
assert.equal(output.code, annotated.representatives["pair"]);
|
|
20
|
+
console.log("OK");
|
|
21
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
t(`
|
|
5
|
+
$nat = < {} _, nat 0, nat 1>;
|
|
6
|
+
zero = {{{} _} 0} $nat;
|
|
7
|
+
rlz = <.0._ zero, .0 rlz, ()>;
|
|
8
|
+
rlz
|
|
9
|
+
`, (annotated) => {
|
|
10
|
+
const {input,output} = in_out(annotated);
|
|
11
|
+
assert.equal(output.code, annotated.representatives["nat"]);
|
|
12
|
+
assert.equal(input.code, annotated.representatives["nat"]);
|
|
13
|
+
console.log("OK");
|
|
14
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { t, in_out } from './index.mjs';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
t('[]', (annotated) => {
|
|
5
|
+
const {input,output} = in_out(annotated);
|
|
6
|
+
assert.equal(input.type, null);
|
|
7
|
+
assert.equal(output.type, "vector");
|
|
8
|
+
console.log("OK");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
t(`$int [ $int true, false ]`, (annotated) => {
|
|
12
|
+
const {input,output} = in_out(annotated);
|
|
13
|
+
assert.equal(input.code, "int");
|
|
14
|
+
assert.equal(output.type, "vector");
|
|
15
|
+
console.log("OK");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
t(`$int [ $int true, false ] .0`, (annotated) => {
|
|
19
|
+
const {input,output} = in_out(annotated);
|
|
20
|
+
assert.equal(input.code, "int");
|
|
21
|
+
assert.equal(output.code, "bool");
|
|
22
|
+
console.log("OK");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// assert.throws(() => t(`$int [ $int true, 12 ]`), /Error/);
|
|
26
|
+
// console.log("OK");
|
|
27
|
+
|
|
28
|
+
// t(`[]`);
|
package/INTRO.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
### Introduction to Kernel/Core
|
|
2
|
+
|
|
3
|
+
**Kernel/Core** is a minimal, gradually typed, first-order functional programming language designed for defining and transforming data using partial functions. It is built on the foundation of Algebraic Data Types (ADTs), specifically tagged unions and products.
|
|
4
|
+
|
|
5
|
+
#### Core Concepts
|
|
6
|
+
|
|
7
|
+
1. **Statements**:
|
|
8
|
+
- The program consists of a possibly empty list of statements, each ending with a semicolon (`;`), followed by an expression.
|
|
9
|
+
- There are two types of statements:
|
|
10
|
+
- **Code Definitions**: Define data types and start with a dollar sign (`$`).
|
|
11
|
+
- **Relation Definitions**: Define partial functions (transformations) of data.
|
|
12
|
+
|
|
13
|
+
2. **Codes**:
|
|
14
|
+
- Codes represent data types and can be either unions (`<...>`) or products (`{...}`).
|
|
15
|
+
- Example:
|
|
16
|
+
```kernelcore
|
|
17
|
+
$ bool = <{} true, {} false>;
|
|
18
|
+
$ pair_of_bools = {bool x, bool y};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
3. **Relations**:
|
|
22
|
+
- Relations are partial functions defined to transform data from one type to another.
|
|
23
|
+
- Example:
|
|
24
|
+
```kernelcore
|
|
25
|
+
is_x_true = $pair_of_bools (.x .true);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
4. **Composition of Partial Functions**:
|
|
29
|
+
- **Sequential (Functional) Composition**: Sequential application of partial functions.
|
|
30
|
+
```kernelcore
|
|
31
|
+
(.x .true)
|
|
32
|
+
```
|
|
33
|
+
- **Parallel (Product) Composition**: Combine multiple partial functions to construct a new product type.
|
|
34
|
+
```kernelcore
|
|
35
|
+
{.x x_value, .y y_value}
|
|
36
|
+
```
|
|
37
|
+
- **Concurrent (Union) Composition**: Try different functions in sequence, returning the first successful result.
|
|
38
|
+
```kernelcore
|
|
39
|
+
<{.x .true false_bool}, {} true_bool>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
5. **Constants**:
|
|
43
|
+
- Constants are defined as partial functions that ignore their input.
|
|
44
|
+
- Example:
|
|
45
|
+
```kernelcore
|
|
46
|
+
true_bool = {{} true} $bool;
|
|
47
|
+
false_bool = {{} false} $bool;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
#### Example Program
|
|
51
|
+
|
|
52
|
+
```kernelcore
|
|
53
|
+
// Define a boolean type as a union of true and false
|
|
54
|
+
$ bool = <{} true, {} false>;
|
|
55
|
+
|
|
56
|
+
// Define a pair of booleans as a product of two booleans
|
|
57
|
+
$ pair_of_bools = {bool x, bool y};
|
|
58
|
+
|
|
59
|
+
// Check if the `x` field is `true`
|
|
60
|
+
is_x_true = $pair_of_bools (.x .true);
|
|
61
|
+
|
|
62
|
+
// Extract both `x` and `y` fields into a new product
|
|
63
|
+
extract_both = $pair_of_bools {.x x_value, .y y_value} $pair_of_bools;
|
|
64
|
+
|
|
65
|
+
// Check if either `x` or `y` is `true`
|
|
66
|
+
is_any_true = $pair_of_bools <{.x .true false_bool}, {} true_bool> $bool;
|
|
67
|
+
|
|
68
|
+
// Define constants
|
|
69
|
+
true_bool = {{} true} $bool;
|
|
70
|
+
false_bool = {{} false} $bool;
|
|
71
|
+
|
|
72
|
+
// Create instances of `pair_of_bools`
|
|
73
|
+
pair1 = {true_bool x, true_bool y} $pair_of_bools;
|
|
74
|
+
pair2 = {true_bool x, false_bool y} $pair_of_bools;
|
|
75
|
+
pair3 = {false_bool x, false_bool y} $pair_of_bools;
|
|
76
|
+
|
|
77
|
+
// Example usage of the functions
|
|
78
|
+
result1 = pair1 is_x_true; // Should return true
|
|
79
|
+
result2 = pair2 is_x_true; // Should return true
|
|
80
|
+
|
|
81
|
+
both_values = pair1 extract_both; // Should return {x_value true, y_value true}
|
|
82
|
+
|
|
83
|
+
any_true1 = pair1 is_any_true; // Should return true
|
|
84
|
+
any_true2 = pair2 is_any_true; // Should return true
|
|
85
|
+
any_true3 = pair3 is_any_true; // Should return false
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This brief introduction covers the fundamental concepts of Kernel/Core, enabling you to start defining data types and writing partial functions to transform data.
|
package/README.md
CHANGED
|
@@ -76,9 +76,10 @@ There are three ways of combining functions:
|
|
|
76
76
|
### Syntactic sugar
|
|
77
77
|
|
|
78
78
|
- parenthesis can be omitted, except for the empty composition `()`,
|
|
79
|
-
- dot (`.`) in "projection" acts as a separator so the space around it
|
|
79
|
+
- dot (`.`) in "projection" acts as a separator so the space around it
|
|
80
|
+
can be omitted.
|
|
80
81
|
|
|
81
|
-
For example, `
|
|
82
|
+
For example, `(.toto .titi (.0 .1))` can be written as `.toto.titi.0.1`.
|
|
82
83
|
|
|
83
84
|
Comments can be introduced by `//`, `--`, `%`, or `#` and extends to
|
|
84
85
|
the end of line. Multiline `C`-like comments, `/* ... */`, are also
|
|
@@ -118,8 +119,9 @@ projection to the canonicat string representation of the value, e.g.:
|
|
|
118
119
|
#### Vector product
|
|
119
120
|
|
|
120
121
|
Vector product can be seen as an abbreviation for product whose field
|
|
121
|
-
names are integers starting from zero.
|
|
122
|
-
|
|
122
|
+
names are integers starting from zero.
|
|
123
|
+
E.g., `{.toto 0, .titi 1, 123 2}` can be written as
|
|
124
|
+
`[.toto, .titi, 123]`.
|
|
123
125
|
|
|
124
126
|
[.toto, .titi, 12] :
|
|
125
127
|
{"toto": 5, "titi": 10 } --> [5, 10, 12]
|
|
@@ -128,6 +130,9 @@ can be written as `[.toto, .titi, 123]`.
|
|
|
128
130
|
["A","B","C"] --> "B"
|
|
129
131
|
["a"] ... undefined
|
|
130
132
|
|
|
133
|
+
The vector notation is introduced to satisfy JSON syntax and, for example,
|
|
134
|
+
is not supported in _code derivation_ (explained below).
|
|
135
|
+
|
|
131
136
|
---
|
|
132
137
|
|
|
133
138
|
### Pragmatic extensions, aka "standard library"
|
|
@@ -172,8 +177,8 @@ can be written as `[.toto, .titi, 123]`.
|
|
|
172
177
|
"[1,2.11,0.3e-32]" --> [1,2.11,3e-33]
|
|
173
178
|
|
|
174
179
|
|
|
175
|
-
- other predefined parial functions are: `DIV`, `FDIV`, `CONS`,
|
|
176
|
-
`toDateStr`, and `_log!`.
|
|
180
|
+
- other predefined parial functions are: `DIV`, `FDIV`, `CONS`,
|
|
181
|
+
`SNOC`, `toDateMsec`, `toDateStr`, and `_log!`.
|
|
177
182
|
|
|
178
183
|
---
|
|
179
184
|
|
|
@@ -183,7 +188,8 @@ can be written as `[.toto, .titi, 123]`.
|
|
|
183
188
|
max = <SNOC [.0, .1 max] <GT.0, .1>, .0> ;
|
|
184
189
|
factorial = < [(),0] GT .0 [dec factorial, ()] TIMES, 1 >;
|
|
185
190
|
|
|
186
|
-
|
|
191
|
+
### Codes (schemas, i.e., _non-functional types_)
|
|
192
|
+
_Codes_ (prefixed by `$`) can be defined by tagged union and product. E.g.:
|
|
187
193
|
|
|
188
194
|
|
|
189
195
|
$nat = <nat 1, {} 0>;
|
|
@@ -192,18 +198,67 @@ _Codes_ (prefixed by `$`) can be defined by taged union and product. E.g.:
|
|
|
192
198
|
suc = {$nat 1};
|
|
193
199
|
add = $pair <{.x.1 x, .y suc y} add, .y>;
|
|
194
200
|
|
|
201
|
+
Two codes are considered equal when they are isomorphic (preserving
|
|
202
|
+
union/product and field names). E.g., `$pair`, `${nat y, nat x}`, and
|
|
203
|
+
`${<nat 1, {} 0> x, nat y}` are all equal.
|
|
204
|
+
|
|
195
205
|
#### Basic extension codes
|
|
196
206
|
|
|
197
|
-
Since _basic extension_ introduces integers, booleans, and strings,
|
|
198
|
-
predefined types: `int`, `bool`, and `string`. A
|
|
199
|
-
by `[ codeExp ]`. All members
|
|
207
|
+
Since _basic extension_ introduces integers, booleans, and strings,
|
|
208
|
+
there are three predefined types: `int`, `bool`, and `string`. A
|
|
209
|
+
vector product code can also be defined by `[ codeExp ]`. All members
|
|
210
|
+
of the vector are the same code. E.g.,
|
|
200
211
|
|
|
201
212
|
$intVector = [ int ];
|
|
202
213
|
$boolVector = [ bool ];
|
|
203
214
|
$tree = [ tree ];
|
|
204
215
|
|
|
205
|
-
|
|
206
|
-
|
|
216
|
+
### Code derivation and Patterns
|
|
217
|
+
|
|
218
|
+
Intuitively, a _pattern_ represents some set of contraints on
|
|
219
|
+
codes. For example, expression `.toto` is a projection from a value of
|
|
220
|
+
a product or a union with field `toto`. Therefore, expression `.toto`
|
|
221
|
+
introduces two patterns `p_i` and `p_o` (for input and output codes,
|
|
222
|
+
respectively); `p_o` imposes no constraint on code, however `p_i` is a
|
|
223
|
+
product or union code with field `toto` leading to a code fulfilling
|
|
224
|
+
`p_o`, i.e., something like `< ? toto, ... >` or `{ ? toto, ... }`.
|
|
225
|
+
|
|
226
|
+
.toto
|
|
227
|
+
p_i p_o
|
|
228
|
+
|
|
229
|
+
In a more complex expression each (occurrence of) subexpression will
|
|
230
|
+
introduce some new patterns.
|
|
231
|
+
For example, concider `<.toto, ()>`:
|
|
232
|
+
|
|
233
|
+
< .toto , () >
|
|
234
|
+
p1 p2 p3 p4 p5 p6
|
|
235
|
+
|
|
236
|
+
where `p1` is the pattern for the input code of the whole expression,
|
|
237
|
+
and `p6` is the pattern for the output. We can deduce that patterns
|
|
238
|
+
(`p1`, `p2`, `p4`) defines the same code, as well as (`p6`, `p5`, `p3`),
|
|
239
|
+
because union combines partial functions with the same input and output codes.
|
|
240
|
+
Identity, `()`, implies that (`p4`, `p5`) define the
|
|
241
|
+
same code. That means that in our expression, we deal with only one
|
|
242
|
+
code, `c`, product or union, with field `toto` of type `c`, i.e.,
|
|
243
|
+
|
|
244
|
+
$c = < c toto, ... >;
|
|
245
|
+
|
|
246
|
+
or
|
|
247
|
+
|
|
248
|
+
$c = { c toto, ... };
|
|
249
|
+
|
|
250
|
+
Since the product code pattern (i.e., latter case) doesn't admmit any
|
|
251
|
+
(finite) value, we conclude that the code `c` is a union (i.e.,
|
|
252
|
+
former case).
|
|
253
|
+
|
|
254
|
+
For a given `kScript`, code derivation (as any static analysis) can
|
|
255
|
+
fail, indicating that the script is invalid. In some other cases the
|
|
256
|
+
code derivation can succeed even to the point of reducig every pattern
|
|
257
|
+
to a single code making the program fully annotated by codes.
|
|
258
|
+
|
|
259
|
+
For now, `code derivation` is supported by the core language, i.e., `composition`,
|
|
260
|
+
`projection` and `union` with product and tagged union types.
|
|
261
|
+
|
|
207
262
|
---
|
|
208
263
|
|
|
209
264
|
### List comprehension on vectors (experimental!)
|
|
@@ -259,8 +314,7 @@ times value `x` appears in the list. (see `Examples/list-comprehension.k` for a
|
|
|
259
314
|
."field name"
|
|
260
315
|
.4
|
|
261
316
|
|
|
262
|
-
|
|
263
|
-
the field (or a vector with the index).
|
|
317
|
+
The function is defined if its argument "has" the field.
|
|
264
318
|
|
|
265
319
|
2. _constants_, literals for Strings, Booleans, and
|
|
266
320
|
Integers. Examples:
|
package/TODO.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# TODO list
|
|
2
|
+
|
|
3
|
+
- [ ] Incremental compilation
|
|
4
|
+
- [ ] After `annotation` is done, find fully closed patterns, turn them into codes, then rerun
|
|
5
|
+
the code minimization step
|
|
6
|
+
- [ ] Named relations can be polymorphic:
|
|
7
|
+
-- can we introduce polymorphic patterns? E.g.,: `{ .x if, .y then} .then`.
|
|
8
|
+
Here, the input pattern could be "open" product with two fields `.x` and `.y`, end the ouput pattern could be the pattern of field `.y`.
|
|
9
|
+
-- should we introduce a scope (e.g., syntactic or dynamic?) for all calls to the relation and
|
|
10
|
+
run annotations for all scopes separately?
|
|
11
|
+
- [ ] Implement support for different relations under a same name as long as they have
|
|
12
|
+
"very" different input or output patterns.
|
|
13
|
+
- [ ] In type graphs, we should detect cycles over only product types and reject such programs, e.g.,:
|
|
14
|
+
|
|
15
|
+
```k
|
|
16
|
+
$boo = {{} _, boo b};
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Type system
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
|
|
25
|
+
```Examples/pats.k
|
|
26
|
+
xy = .x y;
|
|
27
|
+
y = .y;
|
|
28
|
+
$A = < {} a, {} other >;
|
|
29
|
+
$B = < {} b, {} other >;
|
|
30
|
+
$ya = < {} a, $A y >;
|
|
31
|
+
$yb = < {} b, $B y >;
|
|
32
|
+
$input = {{ya x, {} y} one, {yb x, {} y} two};
|
|
33
|
+
$input {.one xy one, .two xy two}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
We would like to correctly type the above program as `$input -> { A one, B two }`.
|
|
37
|
+
|
|
38
|
+
filters for typing `__main__`:
|
|
39
|
+
|
|
40
|
+
```?
|
|
41
|
+
__main__
|
|
42
|
+
0 / \ 1
|
|
43
|
+
/ \
|
|
44
|
+
$input {.one xy one, .two xy two}
|
|
45
|
+
2 3 4 / \ 5
|
|
46
|
+
/ \
|
|
47
|
+
(.one xy) (.two xy)
|
|
48
|
+
/ \ / \
|
|
49
|
+
6 / \ 7 8 / \ 9
|
|
50
|
+
/ \ / \
|
|
51
|
+
.one xy .two xy
|
|
52
|
+
10 11 12 13 14 15 16 17
|
|
53
|
+
|
|
54
|
+
```
|