@fraczak/k 6.1.2 → 6.2.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/AGENTS.md +4 -4
- package/CLAUDE.md +1 -1
- package/DOCS/BIG_PICTURE.md +10 -10
- package/DOCS/DIVERGENCE_DETECTION.md +1 -1
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +6 -0
- package/DOCS/REPL.md +22 -17
- package/DOCS/TYPE_DERIVATION.md +12 -12
- package/DOCS/book/01-introduction.md +2 -2
- package/DOCS/book/02-syntax-and-values.md +8 -8
- package/DOCS/book/03-types-as-finite-automata.md +2 -2
- package/DOCS/book/04-partial-functions-and-composition.md +2 -2
- package/DOCS/book/05-typing-filters-and-normalization.md +3 -3
- package/DOCS/book/06-values-in-memory.md +3 -3
- package/DOCS/book/07-the-partial-function-abi.md +1 -1
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +1 -0
- package/README.md +13 -13
- package/codecs/BINARY_FORMAT.md +1 -1
- package/codecs/POLYMORPHIC_BINARY_FORMAT.md +1 -1
- package/codes.mjs +14 -33
- package/export.mjs +13 -3
- package/k-vscode/README.md +10 -1
- package/k-vscode/extension.js +273 -0
- package/k-vscode/package.json +22 -0
- package/k-vscode/syntaxes/k.tmLanguage.json +493 -137
- package/k-vscode/test/background-ranges.test.js +50 -0
- package/k-vscode/themes/k-contrast-color-theme.json +104 -0
- package/object.mjs +20 -7
- package/package.json +3 -2
- package/repl.mjs +43 -2
- package/scripts/benchmark-idempotency.mjs +72 -0
- package/scripts/run-tests.mjs +54 -0
- package/{Code-derivation-tests → tests/code-derivation}/codec-witness-pattern.mjs +3 -3
- package/{Code-derivation-tests → tests/code-derivation}/convergence-strategy.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/dot.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/filter-syntax.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/index.mjs +1 -1
- package/{Code-derivation-tests → tests/code-derivation}/pattern-envelope.mjs +5 -5
- package/{Code-derivation-tests → tests/code-derivation}/product.mjs +1 -1
- package/{tests.sh → tests/integration.sh} +3 -1
- package/{test-fingerprint.mjs → tests/test-fingerprint.mjs} +3 -3
- package/{test-hash-fuzz.mjs → tests/test-hash-fuzz.mjs} +1 -1
- package/{test-hash-normalization.mjs → tests/test-hash-normalization.mjs} +2 -2
- package/{test-ieee-arithmetic.mjs → tests/test-ieee-arithmetic.mjs} +14 -27
- package/{test-k-object.mjs → tests/test-k-object.mjs} +26 -7
- package/{test-repl.mjs → tests/test-repl.mjs} +22 -4
- package/{test.mjs → tests/test.mjs} +1 -1
- package/.codex +0 -0
- package/DOCS/a.html +0 -398
- package/DOCS/dependency-graph.html +0 -140
- package/DOCS/dependency-graph.md +0 -144
- package/NAT.k~ +0 -452
- package/UU/ta.k +0 -1
- package/UU/ta.k~ +0 -0
- package/UU/tb.k +0 -0
- package/UU/tc.k +0 -0
- package/codecs/BINARY_FORMAT.html +0 -471
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +0 -355
- package/core.json +0 -1
- package/core.klib +0 -1
- package/core.klib~ +0 -0
- package/ieee.klib +0 -1
- package/k-vscode/package-lock.json +0 -4176
- package/kkk.k +0 -197
- package/kkk.klib +0 -0
- package/kkk.klib~ +0 -0
- package/p.json +0 -5475
- package/type_registry/registry.mjs~ +0 -105
- package/type_registry/typeParser.jison~ +0 -132
- /package/{Code-derivation-tests → tests/code-derivation}/blowup.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/bnat.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/loop.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/nat.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/not-not.mjs +0 -0
- /package/{Code-derivation-tests → tests/code-derivation}/rlz.mjs +0 -0
package/AGENTS.md
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
- `k_compiler/` contains the Python prototype compiler and supporting modules.
|
|
6
6
|
- `type_registry/` houses registry experiments and grammar artifacts.
|
|
7
7
|
- `Examples/` and `DOCS/` provide sample programs and design notes.
|
|
8
|
-
- Tests live in
|
|
8
|
+
- Tests live in `tests/`, with type-derivation cases in `tests/code-derivation/`.
|
|
9
9
|
|
|
10
10
|
## Build, Test, and Development Commands
|
|
11
11
|
- `npm install` installs dev dependencies and runs `prepare` to generate parsers.
|
|
12
12
|
- `npm run prepare` regenerates `parser.mjs` and `valueParser.mjs` from `.jison` grammars.
|
|
13
|
-
- `npm test` runs the full suite
|
|
13
|
+
- `npm test` runs the full suite through `scripts/run-tests.mjs`.
|
|
14
14
|
- `node repl.mjs` (or `k-repl`) starts the interactive REPL.
|
|
15
15
|
- `./k.mjs -k <file.k>` executes a k script on binary pattern+value stream input from stdin or a file; use `k-parse` and `k-print` at the boundaries.
|
|
16
16
|
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
- Grammar sources are `parser.jison` and `valueParser.jison`; generated files should not be hand-edited.
|
|
22
22
|
|
|
23
23
|
## Testing Guidelines
|
|
24
|
-
- Add runtime tests alongside `test.mjs` or new `test-*.mjs` files.
|
|
25
|
-
- Add type-derivation coverage in `
|
|
24
|
+
- Add runtime tests alongside `tests/test.mjs` or new `tests/test-*.mjs` files.
|
|
25
|
+
- Add type-derivation coverage in `tests/code-derivation/` (one case per file is typical).
|
|
26
26
|
- Verify locally with `npm test` before opening a PR.
|
|
27
27
|
|
|
28
28
|
## Commit & Pull Request Guidelines
|
package/CLAUDE.md
CHANGED
|
@@ -60,5 +60,5 @@ npm test # Run comprehensive test suite (unit tests, derivation tests, shell te
|
|
|
60
60
|
|
|
61
61
|
- No external runtime dependencies (only jiwson for parser generation)
|
|
62
62
|
- Uses ES6 modules (.mjs files) throughout
|
|
63
|
-
- Extensive test suite in `
|
|
63
|
+
- Extensive test suite in `tests/code-derivation/` for type system validation
|
|
64
64
|
- REPL supports commands like `:C name` (show canonical type), `:t name` (show relation type), and `:d name` (show relation definition)
|
package/DOCS/BIG_PICTURE.md
CHANGED
|
@@ -12,7 +12,7 @@ with that tree.
|
|
|
12
12
|
|
|
13
13
|
### Types (codes)
|
|
14
14
|
|
|
15
|
-
Formally, a _type_ is a
|
|
15
|
+
Formally, a _type_ is a finite tree automaton. The type defines a set of values,
|
|
16
16
|
which corresponds to the set of trees accepted by the automaton.
|
|
17
17
|
Therefore, every type admits a unique normal form in terms of its minimal deterministic tree automaton.
|
|
18
18
|
|
|
@@ -38,7 +38,7 @@ Given a finite (possibly empty) set of types `t1, t2, ..., tn` and a set of pair
|
|
|
38
38
|
Each projection function maps a value of type `{t1 l1, t2 l2, ..., tn ln}`
|
|
39
39
|
into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
40
40
|
- by `< t1 l1, t2 l2, ..., tn ln >`, we define a _union type_ with exactly `n` projection functions `/l1`, `/l2`, `...`, `/ln`,
|
|
41
|
-
|
|
41
|
+
mapping a value of type `<t1 l1, t2 l2, ..., tn ln>` into a value of type `t1`, `t2`, `...`, `tn`, respectively.
|
|
42
42
|
|
|
43
43
|
Intuitively, the projection functions on a product type are all total (meaning, they are defined for every value of the
|
|
44
44
|
input product type), whereas in the case of a union type, exactly one projection function is defined
|
|
@@ -68,7 +68,7 @@ Two (possibly recursive) codes are equivalent iff they are bisimilar over their
|
|
|
68
68
|
|
|
69
69
|
### Filters (patterns)
|
|
70
70
|
|
|
71
|
-
A _filter_ is a way of representing a _pattern_, i.e
|
|
71
|
+
A _filter_ is a way of representing a _pattern_, i.e., a set of types sharing some structure.
|
|
72
72
|
For example, if I wanted to refer to all product types that have a field `f` of type `T`,
|
|
73
73
|
I would write: `?{ T f, ... }`. In general, a filter is defined by:
|
|
74
74
|
|
|
@@ -158,14 +158,14 @@ registry of types (schemas) and functions.
|
|
|
158
158
|
|
|
159
159
|
## Typing and Polymorphism (codes and patterns)
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
a dictionary
|
|
161
|
+
The abstract syntax tree of a program consists of a dictionary of _type definitions_,
|
|
162
|
+
a dictionary of _function definitions_, and the final _main expression_.
|
|
163
163
|
|
|
164
164
|
In principle, the initial form of the Abstract Syntax Tree, _raw_ AST, is enough to evaluate
|
|
165
165
|
the program on an input.
|
|
166
166
|
|
|
167
|
-
Our objective however is to compute canonical representations for types and normalized
|
|
168
|
-
representations for functions
|
|
167
|
+
Our objective, however, is to compute canonical representations for types and normalized
|
|
168
|
+
representations for functions so they can be reused.
|
|
169
169
|
|
|
170
170
|
The normalization steps are:
|
|
171
171
|
|
|
@@ -225,7 +225,7 @@ Functions `inc` and `inc3` are (non-polymorphic) functions of types
|
|
|
225
225
|
`${ bit overflown, byte byte } -> ${ bit overflown, byte byte }`, respectively.
|
|
226
226
|
|
|
227
227
|
Function `inc_o` is a polymorphic function of type patterns: `?X -> ${ byte byte, bit overflown }`
|
|
228
|
-
with following constraints:
|
|
228
|
+
with the following constraints:
|
|
229
229
|
|
|
230
230
|
- `?X` is a product type with at least two fields: `byte` and `overflown`, denoted by:
|
|
231
231
|
> `?{ $byte byte, Z overflown, ...}`;
|
|
@@ -269,7 +269,7 @@ The registry will be a key-value store, where the key is the hash of the normali
|
|
|
269
269
|
and the value is the normalized type itself.
|
|
270
270
|
|
|
271
271
|
All functions (non-polymorphic and polymorphic) will be renamed by the hash of their normalized
|
|
272
|
-
definition and stored in
|
|
272
|
+
definition and stored in a similar key-value store.
|
|
273
273
|
|
|
274
274
|
Non-polymorphic functions can be easily indexed by the hashes of their input and output types
|
|
275
275
|
so that we can quickly find the function we need.
|
|
@@ -280,7 +280,7 @@ Indexing polymorphic functions seems complicated (TODO).
|
|
|
280
280
|
The language is designed to transform _codes_, i.e., serialized typed values, i.e., trees
|
|
281
281
|
accepted by a tree automaton, into other _codes_.
|
|
282
282
|
The transformation is done by partial functions as defined in the language.
|
|
283
|
-
The non-recursive and tail-recursive (and even some non-tail
|
|
283
|
+
The non-recursive and tail-recursive (and even some non-tail-recursive) functions can
|
|
284
284
|
be compiled into deterministic finite (pushdown) transducers.
|
|
285
285
|
|
|
286
286
|
### AND-OR graphs for encoding and decoding codes
|
|
@@ -19,6 +19,6 @@ HEAD(?F E) = HEAD(E)
|
|
|
19
19
|
HEAD($T E) = HEAD(E)
|
|
20
20
|
HEAD((E1 E2 ...)) = HEAD(E1)
|
|
21
21
|
HEAD({ E1 f1, E2 f2, ... }) = HEAD(E1) union HEAD(E2) union ...
|
|
22
|
-
HEAD(<E1, E2, ...>) = {} --
|
|
22
|
+
HEAD(<E1, E2, ...>) = {} -- the head of a union is the empty set
|
|
23
23
|
HEAD(/ tag) = HEAD(. lab) = HEAD(| tag) = {}
|
|
24
24
|
```
|
|
@@ -305,3 +305,9 @@ shape is the same object payload shape with `main: null`:
|
|
|
305
305
|
When a library is compiled with `--lib`, the output is the merged library
|
|
306
306
|
closure. Imported codes, relations, aliases, and metadata are preserved, and the
|
|
307
307
|
new source adds its own origins.
|
|
308
|
+
|
|
309
|
+
The REPL `:klib` command uses active relation aliases as roots. It stores their
|
|
310
|
+
transitive relation closure and omits historical content-addressed relations
|
|
311
|
+
that remain in the live session after alias rebinding but are no longer
|
|
312
|
+
reachable from an active alias. Its `codes` field remains the registered code
|
|
313
|
+
snapshot needed to reload the library.
|
package/DOCS/REPL.md
CHANGED
|
@@ -11,9 +11,9 @@ The interpreter keeps a live `.klib`-style state in memory:
|
|
|
11
11
|
- metadata origins used to recover aliases
|
|
12
12
|
- the current value flowing through the session
|
|
13
13
|
|
|
14
|
-
Raw k snippets compile on top of that state. You can also export the
|
|
15
|
-
|
|
16
|
-
expression in the session context.
|
|
14
|
+
Raw k snippets compile on top of that state. You can also export the active
|
|
15
|
+
library closure as a `.klib` file or compile an executable `.ko` object from
|
|
16
|
+
an expression in the session context.
|
|
17
17
|
|
|
18
18
|
## Prompt Model
|
|
19
19
|
|
|
@@ -49,7 +49,7 @@ complete.
|
|
|
49
49
|
| `:codec list` | List loaded REPL codecs |
|
|
50
50
|
| `:input type [codec]` | Read the next line as codec input for a singleton type |
|
|
51
51
|
| `:load [--no-alias] file` | Load `.k` source or `.klib` into the current state |
|
|
52
|
-
| `:klib file` | Export
|
|
52
|
+
| `:klib file` | Export the active relation closure as a `.klib` library |
|
|
53
53
|
| `:ko file expr` | Export a `.ko` executable with `expr` as main |
|
|
54
54
|
| `:val` | Print current value and JSON form |
|
|
55
55
|
| `:reset` | Reset interpreter state |
|
|
@@ -62,7 +62,7 @@ Raw input is compiled as k source on top of the current state.
|
|
|
62
62
|
Examples:
|
|
63
63
|
|
|
64
64
|
```k
|
|
65
|
-
> {} |
|
|
65
|
+
> {} |ok
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
```k
|
|
@@ -107,6 +107,8 @@ The interpreter keeps:
|
|
|
107
107
|
|
|
108
108
|
Definitions are content-addressed. Rebinding an alias changes the name-to-hash
|
|
109
109
|
mapping, but older canonical definitions remain available by hash.
|
|
110
|
+
They stay available in the live session, but `:klib` omits historical
|
|
111
|
+
relations that are no longer reachable from an active relation alias.
|
|
110
112
|
|
|
111
113
|
## Alias Resolution
|
|
112
114
|
|
|
@@ -184,8 +186,11 @@ against that code before becoming the current value.
|
|
|
184
186
|
|
|
185
187
|
### `:klib file`
|
|
186
188
|
|
|
187
|
-
Writes
|
|
188
|
-
|
|
189
|
+
Writes a plain-JSON `.klib` library rooted at the current relation aliases.
|
|
190
|
+
Relations referenced by those aliases are included transitively. Historical
|
|
191
|
+
relations retained in the live session are omitted when no active alias
|
|
192
|
+
depends on them. The registered code snapshot is still included. The library
|
|
193
|
+
has `main: null`; it has no binary header and no object payload version.
|
|
189
194
|
|
|
190
195
|
### `:ko file expr`
|
|
191
196
|
|
|
@@ -197,7 +202,7 @@ writes the resulting executable `.ko` object.
|
|
|
197
202
|
Evaluated values print in k syntax together with the inferred envelope:
|
|
198
203
|
|
|
199
204
|
```text
|
|
200
|
-
{
|
|
205
|
+
{}|0|+1 ?<<{} 0, ...> +1, ...>
|
|
201
206
|
```
|
|
202
207
|
|
|
203
208
|
`undefined` prints as:
|
|
@@ -212,16 +217,16 @@ Evaluated values print in k syntax together with the inferred envelope:
|
|
|
212
217
|
## Example Session
|
|
213
218
|
|
|
214
219
|
```text
|
|
215
|
-
> :type nat = <{}
|
|
220
|
+
> :type nat = <{} 0, nat +1>
|
|
216
221
|
$ nat = @...
|
|
217
|
-
> :rel
|
|
218
|
-
|
|
219
|
-
> {} |
|
|
220
|
-
{
|
|
221
|
-
> :t
|
|
222
|
-
|
|
222
|
+
> :rel inc = |+1
|
|
223
|
+
inc = @...
|
|
224
|
+
> {} |0
|
|
225
|
+
{}|0 ?<{} 0, ...>
|
|
226
|
+
> :t inc
|
|
227
|
+
inc : ?X0 --> ?<X0 +1, ...> (@...)
|
|
223
228
|
> :klib nat.klib
|
|
224
229
|
saved nat.klib
|
|
225
|
-
> :ko
|
|
226
|
-
saved
|
|
230
|
+
> :ko inc.ko inc
|
|
231
|
+
saved inc.ko (inc)
|
|
227
232
|
```
|
package/DOCS/TYPE_DERIVATION.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
This algorithm infers type constraints for programs consisting of mutually recursive partial functions over algebraic data types (products and unions).
|
|
6
6
|
|
|
7
|
-
**Input:** AST of a `k` program, consisting of function definitions (expressions)
|
|
7
|
+
**Input:** AST of a `k` program, consisting of function definitions (expressions), which can be seen as a set of recursive equations.
|
|
8
8
|
**Output:** Annotated AST with type patterns (also called filters)
|
|
9
9
|
|
|
10
10
|
It is assumed that we have access (through APIs) to a universal "type registry", which provides us with unique (canonical) names for each type.
|
|
@@ -22,12 +22,12 @@ A **pattern** represents a set of types:
|
|
|
22
22
|
| `<>` | Closed union | Exactly specified |
|
|
23
23
|
| `T` | Named type | From type definition |
|
|
24
24
|
|
|
25
|
-
**Fields:** Each pattern has associated field labels. For open patterns, additional fields may exist. For closed patterns, the field set is exact.
|
|
25
|
+
**Fields:** Each pattern has associated field labels. For open patterns, additional fields may exist. For closed patterns, the field set is exact.
|
|
26
26
|
Unknown-kind patterns have only the open form `(...)`; a closed filter must choose product `{...}`/`{}` or union `<...>`/`<>`.
|
|
27
27
|
|
|
28
28
|
A type pattern is a singleton pattern, i.e., only one type is captured by such a pattern.
|
|
29
29
|
|
|
30
|
-
## 3. Pattern Graph -
|
|
30
|
+
## 3. Pattern Graph - A Data Structure Used During Type Derivation
|
|
31
31
|
|
|
32
32
|
**Nodes:** Patterns organized in a "reps" (representatives) forest, similar to union-find. However, a union of two (or more) different trees always produces a new "rep" (root) node, joining the trees.
|
|
33
33
|
**Edges:** Labeled by field names, pointing to other patterns
|
|
@@ -42,13 +42,13 @@ A type pattern is a singleton pattern, i.e., only one type is captured by such a
|
|
|
42
42
|
|
|
43
43
|
## 4. Unification
|
|
44
44
|
|
|
45
|
-
`unify(p₁, ..., pₙ)` computes the least upper bound (most specific common pattern); it may add a new node (rep) into the reps forest
|
|
45
|
+
`unify(p₁, ..., pₙ)` computes the least upper bound (most specific common pattern); it may add a new node (rep) into the reps forest, but it does not modify any existing pattern node.
|
|
46
46
|
|
|
47
47
|
**Rules:**
|
|
48
48
|
|
|
49
49
|
- **Open + Open:** Merge field sets, stay open
|
|
50
|
-
- **Open + Closed:** Check
|
|
51
|
-
- **Closed + Closed:** Check
|
|
50
|
+
- **Open + Closed:** Check field inclusion, then become closed
|
|
51
|
+
- **Closed + Closed:** Check field equality, then stay closed
|
|
52
52
|
- **Product ⊥ Union:** Error (incompatible)
|
|
53
53
|
- **Type T:** Unique (canonical name)
|
|
54
54
|
|
|
@@ -75,7 +75,7 @@ out(eᵢ) = in(eᵢ₊₁) for i = 1..n-1
|
|
|
75
75
|
out((e₁ e₂ ... eₙ)) = out(eₙ)
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
When `n=0`, the composition is the identity, `()`.
|
|
79
79
|
|
|
80
80
|
```text
|
|
81
81
|
in(()) = out(())
|
|
@@ -136,7 +136,7 @@ in(T) = out(T) = pattern(T)
|
|
|
136
136
|
see patterns.filterToPattern(F)
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
A filter expression is syntax for describing pattern graphs.
|
|
140
140
|
|
|
141
141
|
## 6. Global Algorithm
|
|
142
142
|
|
|
@@ -166,13 +166,13 @@ For each SCC (in topological order):
|
|
|
166
166
|
For each function `f` in SCC:
|
|
167
167
|
1. Compact `f`'s pattern graph
|
|
168
168
|
2. For each occurrence `o` of `g` in `f`, clone(in(g), out(g)) and unify input and output patterns of `o` with the corresponding cloned patterns
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
If pattern graphs unchanged: break
|
|
171
171
|
```
|
|
172
172
|
|
|
173
173
|
## 7. Compaction
|
|
174
174
|
|
|
175
|
-
**Purpose:** Replace all pattern nodes
|
|
175
|
+
**Purpose:** Replace all pattern nodes with their reps and replace all singleton patterns with types.
|
|
176
176
|
|
|
177
177
|
**Algorithm:**
|
|
178
178
|
|
|
@@ -186,7 +186,7 @@ For each SCC (in topological order):
|
|
|
186
186
|
|
|
187
187
|
**Criterion:** Pattern graph structure unchanged between iterations
|
|
188
188
|
|
|
189
|
-
**Measured by:**
|
|
189
|
+
**Measured by:**
|
|
190
190
|
1. Representative forest structure (parent pointers)
|
|
191
191
|
2. Edge sets for each representative
|
|
192
192
|
3. Pattern descriptors for each representative
|
|
@@ -206,7 +206,7 @@ Converged = (current_state == previous_state)
|
|
|
206
206
|
|
|
207
207
|
## 9. Error Handling
|
|
208
208
|
|
|
209
|
-
**Unification errors:**
|
|
209
|
+
**Unification errors include:**
|
|
210
210
|
|
|
211
211
|
- Product vs Union
|
|
212
212
|
- Closed pattern field mismatch
|
|
@@ -44,8 +44,8 @@ computation in a concise and understandable way.
|
|
|
44
44
|
|
|
45
45
|
There is another concept in the language, called **filters**.
|
|
46
46
|
Filters play a role in the process of reasoning about types of partial functions.
|
|
47
|
-
Filters have been added to the syntax of the language
|
|
48
|
-
of "polymorphic" expressions, i.e., expressions
|
|
47
|
+
Filters have been added to the syntax of the language because they allow explicit annotations
|
|
48
|
+
of "polymorphic" expressions, i.e., expressions that make sense in different contexts.
|
|
49
49
|
Filters are similar to *type classes* in other languages.
|
|
50
50
|
|
|
51
51
|
---
|
|
@@ -10,7 +10,7 @@ $ type_name = ...type_expression... ;
|
|
|
10
10
|
function_name = ...expression... ;
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The definitions are followed in the file by the *main expression
|
|
13
|
+
The definitions are followed in the file by the *main expression*, which represents the partial function defined by the program as a whole.
|
|
14
14
|
|
|
15
15
|
There is no syntax for values.
|
|
16
16
|
Every syntactic form in an expression describes a **function** -- possibly a constant function that always returns the same value.
|
|
@@ -48,22 +48,22 @@ Every type denotes a finite tree automaton whose accepted trees are the possible
|
|
|
48
48
|
|
|
49
49
|
## **2.4 Projections**
|
|
50
50
|
|
|
51
|
-
**Dot** notation, e.g.,
|
|
51
|
+
**Dot** notation, e.g.,
|
|
52
52
|
|
|
53
53
|
```k
|
|
54
54
|
. field_name
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
is used for extracting the value of a field from a record (product type value).
|
|
58
|
-
**Div** notation, e.g.,
|
|
58
|
+
**Div** notation, e.g.,
|
|
59
59
|
|
|
60
60
|
```k
|
|
61
61
|
/ variant_name
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
is used for asserting a particular variant of a union and extracting its value.
|
|
65
|
-
|
|
66
|
-
and union type `< T₁ l₁, T₂ l₂, … , Tₙ lₙ >` naturally defines `n` partial functions `/l₁, /l₂, … , /lₙ`.
|
|
65
|
+
A product type `{ T₁ l₁, T₂ l₂, … , Tₙ lₙ }` naturally defines `n` partial functions: `.l₁, .l₂, … , .lₙ`,
|
|
66
|
+
and a union type `< T₁ l₁, T₂ l₂, … , Tₙ lₙ >` naturally defines `n` partial functions `/l₁, /l₂, … , /lₙ`.
|
|
67
67
|
Those partial functions are called **projections** and they constitute the basis for the definition
|
|
68
68
|
of all other partial functions in the `k` language.
|
|
69
69
|
|
|
@@ -81,10 +81,10 @@ Expressions combine partial functions using the following operators:
|
|
|
81
81
|
Parentheses around a composition may be omitted except for the empty composition `()` — the identity function.
|
|
82
82
|
Empty product, `{ }`, defines a constant function (ignoring its input) returning *unit*.
|
|
83
83
|
|
|
84
|
-
It is important to notice that after the three markers
|
|
84
|
+
It is important to notice that after the three markers `dot (.)`, `div (/)`, or `pipe (|)`, there must be a constant label literal. For that reason, it is possible and recommended not to leave any blanks after the marker, e.g.:
|
|
85
85
|
|
|
86
86
|
```k-lang
|
|
87
|
-
.field /'a-tag' |"strange and long tag name ✅"
|
|
87
|
+
.field /'a-tag' |"strange and long tag name ✅"
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
---
|
|
@@ -105,7 +105,7 @@ This defines a "two-variant" union type and three functions: two constants and o
|
|
|
105
105
|
## **2.7 Summary**
|
|
106
106
|
|
|
107
107
|
* All types describe tree shapes.
|
|
108
|
-
* As type expression, `{}` is the empty product type (with one value, called *unit*).
|
|
108
|
+
* As a type expression, `{}` is the empty product type (with one value, called *unit*).
|
|
109
109
|
* Functions, not values, are the only expressions in `k`.
|
|
110
110
|
|
|
111
111
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## **3.1 Motivation**
|
|
4
4
|
|
|
5
|
-
A type in `k` describes a set of finite labeled trees, which can be recognized by a finite tree automaton (FTA). Equivalently, a type definition can be seen as a
|
|
5
|
+
A type in `k` describes a set of finite labeled trees, which can be recognized by a finite tree automaton (FTA). Equivalently, a type definition can be seen as a context-free grammar (CFG), where the values of the type are the derivation trees of the grammar.
|
|
6
6
|
|
|
7
7
|
For example, the recursive type definition of a `list`:
|
|
8
8
|
|
|
@@ -31,7 +31,7 @@ A value of type `list` is a derivation tree of this grammar. For instance, a lis
|
|
|
31
31
|
-- (rule 1) --> {} |true
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
This derivation tree corresponds to the value generated by constant function
|
|
34
|
+
This derivation tree corresponds to the value generated by the constant function `{ {} |true head, {} |nil tail } |cons`.
|
|
35
35
|
|
|
36
36
|
Representing types as automata (or CFGs) provides each type with a well-defined structure, independent of the names used in the source code. This allows for canonical normal forms and for comparing types for equality by structure alone.
|
|
37
37
|
|
|
@@ -91,7 +91,7 @@ Here, `true_bool` and `false_bool` are constant functions. They are also **total
|
|
|
91
91
|
|
|
92
92
|
---
|
|
93
93
|
|
|
94
|
-
## **4.6 Projection and
|
|
94
|
+
## **4.6 Projection and variant value constructors**
|
|
95
95
|
|
|
96
96
|
Projection selects a field or a variant from a product or union.
|
|
97
97
|
It is written with a leading dot `.` or a leading slash `/`, respectively:
|
|
@@ -141,6 +141,6 @@ The empty product `{}` is the constant function returning the unit value.
|
|
|
141
141
|
* **Product composition** `{...}` runs functions in parallel and gathers their results.
|
|
142
142
|
* **Union composition** `<...>` tries functions in order, returning the first successful result.
|
|
143
143
|
* **Projections** (`.x`, `/x`) are fundamental building blocks for accessing data structures.
|
|
144
|
-
* **Variant
|
|
144
|
+
* **Variant constructors** (`|tag`) allow lifting a value into a variant type value.
|
|
145
145
|
|
|
146
146
|
---
|
|
@@ -87,13 +87,13 @@ Here:
|
|
|
87
87
|
|
|
88
88
|
## **5.6 Type inference and normalization**
|
|
89
89
|
|
|
90
|
-
Every `k` program can be analyzed to assign an input and an output filter to every sub-expression. This process is called type inference
|
|
90
|
+
Every `k` program can be analyzed to assign an input and an output filter to every sub-expression. This process is called type inference and normalization.
|
|
91
91
|
|
|
92
|
-
Type inference and normalization
|
|
92
|
+
Type inference and normalization proceed as follows:
|
|
93
93
|
|
|
94
94
|
1. Build a graph of all type references appearing explicitly and implicitly in the program.
|
|
95
95
|
2. Annotate each expression node with a pair of (input filter, output filter).
|
|
96
|
-
3. Replace filters that match a single, concrete type with that type expression, adding any newly
|
|
96
|
+
3. Replace filters that match a single, concrete type with that type expression, adding any newly discovered types to the graph.
|
|
97
97
|
4. Repeat until a fixed point is reached and no more changes occur.
|
|
98
98
|
5. Compute canonical forms for all newly introduced types.
|
|
99
99
|
|
|
@@ -138,7 +138,7 @@ The value `{}|_|0|1` or in JSON-like notation `{1:{0:{_:{}}}}` (binary 10₂ = d
|
|
|
138
138
|
01 00 10
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
This represents the
|
|
141
|
+
This represents the leftmost derivation of `{}|_|0|1` from `C0`.
|
|
142
142
|
|
|
143
143
|
#### **Prefix-free property**
|
|
144
144
|
|
|
@@ -304,7 +304,7 @@ Without DAG: Each "Engineering" string encoded separately
|
|
|
304
304
|
- 1000 records × 30 bytes/record = 30KB
|
|
305
305
|
|
|
306
306
|
With DAG: "Engineering" string shared via back-references
|
|
307
|
-
- 1 × "Engineering" (11 bytes) + 800 × back-reference (3 bytes) = 2.
|
|
307
|
+
- 1 × "Engineering" (11 bytes) + 800 × back-reference (3 bytes) = 2.4 KB
|
|
308
308
|
- Compression ratio: ~12.5×
|
|
309
309
|
```
|
|
310
310
|
|
|
@@ -1061,7 +1061,7 @@ The final encoding strategy balances theoretical optimality with practical hardw
|
|
|
1061
1061
|
- **Decode performance**: 5-10× faster than bit-level encoding
|
|
1062
1062
|
- **Cache behavior**: Excellent (word-aligned, sequential access)
|
|
1063
1063
|
- **SIMD compatibility**: Yes (bulk discriminator processing)
|
|
1064
|
-
- **Architecture
|
|
1064
|
+
- **Architecture-agnostic**: Optimized for x86-64 and ARM64
|
|
1065
1065
|
|
|
1066
1066
|
**Performance characteristics**:
|
|
1067
1067
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## **7.1 Purpose**
|
|
4
4
|
|
|
5
5
|
The **application binary interface (ABI)** defines how partial functions in `k` are represented and invoked at runtime.
|
|
6
|
-
Its goal is to make all functions
|
|
6
|
+
Its goal is to make all functions, whether user-defined or compiled, compatible with the same calling convention and data layout.
|
|
7
7
|
|
|
8
8
|
Every compiled `k` function receives a single argument (a runtime value) and may either produce a result or remain undefined.
|
|
9
9
|
The ABI provides a uniform way to express both outcomes.
|
|
@@ -133,7 +133,7 @@ otherwise it is an identity
|
|
|
133
133
|
⟨ ?F , v ⟩ ⇓ v
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
-
Filters therefore act as compile
|
|
136
|
+
Filters therefore act as compile-time annotations for type checking only.
|
|
137
137
|
As operations they are identities at run time, unless they lead to fully typed
|
|
138
138
|
expressions (i.e., can be replaced by a type). Their pattern information may
|
|
139
139
|
still be present on the runtime value because values can carry codec patterns.
|
|
@@ -44,7 +44,7 @@ Each stage transforms one representation of the program into another, simpler on
|
|
|
44
44
|
|
|
45
45
|
A minimal compiler can be viewed as three connected modules:
|
|
46
46
|
|
|
47
|
-
* **Front end** — reads source code, builds the AST, performs type analysis.
|
|
47
|
+
* **Front end** — reads source code, builds the AST, and performs type analysis.
|
|
48
48
|
* **Middle** — resolves all references, normalizes types, and simplifies expressions.
|
|
49
49
|
* **Back end** — produces the executable form.
|
|
50
50
|
|
|
@@ -107,7 +107,7 @@ For `k`, the IR mirrors the structure of partial functions:
|
|
|
107
107
|
|
|
108
108
|
| Concept in k | IR operation |
|
|
109
109
|
| --------------------- | -------------------------------- |
|
|
110
|
-
| Projection `.x`
|
|
110
|
+
| Projection `.x` `/x` | `PROJECT label_id` |
|
|
111
111
|
| Composition `(f g)` | `CALL f; CALL g` |
|
|
112
112
|
| Union `<f,g>` | `TRY f; IF undefined THEN TRY g` |
|
|
113
113
|
| Product `{.x f, .y g}` | `PRODUCT_CALL (f, g); COMBINE` |
|
package/README.md
CHANGED
|
@@ -40,13 +40,12 @@ numbers are either `0` or one more than another natural number:
|
|
|
40
40
|
|
|
41
41
|
```k
|
|
42
42
|
$ nat = < {} 0, nat +1 >;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
add
|
|
48
|
-
|
|
49
|
-
.y
|
|
43
|
+
0 = {} | 0 $ nat ;
|
|
44
|
+
inc = | +1 $ nat ;
|
|
45
|
+
dec = $ nat / +1 ; # undefined for '0'
|
|
46
|
+
add = $ { nat x, nat y } <
|
|
47
|
+
{ . x dec x, . y inc y } add, # defined if 'x > 0'
|
|
48
|
+
. y # else, return 'y'
|
|
50
49
|
>;
|
|
51
50
|
```
|
|
52
51
|
|
|
@@ -56,8 +55,8 @@ from `x` to `y` and recurses. When that no longer applies, it returns `y`.
|
|
|
56
55
|
In the REPL:
|
|
57
56
|
|
|
58
57
|
```text
|
|
59
|
-
> {
|
|
60
|
-
{}|0|+1|+1|+1 ?<X0 +1, {} 0
|
|
58
|
+
> { 0 inc inc x, 0 inc y } add
|
|
59
|
+
{}|0|+1|+1|+1 ?<X0 +1, {} 0>=X0
|
|
61
60
|
```
|
|
62
61
|
|
|
63
62
|
That evaluates `2 + 1` to `3`: a value starts at `0`, and each `+1` tag adds
|
|
@@ -240,7 +239,7 @@ Still evolving:
|
|
|
240
239
|
- optimization and backend experiments
|
|
241
240
|
- larger real-world examples
|
|
242
241
|
|
|
243
|
-
## Good Areas
|
|
242
|
+
## Good Areas for Contributors
|
|
244
243
|
|
|
245
244
|
k is small enough to study, but there are several useful directions:
|
|
246
245
|
|
|
@@ -278,13 +277,14 @@ compose with `.result` when only the floating-point value is needed.
|
|
|
278
277
|
|
|
279
278
|
```bash
|
|
280
279
|
npm run prepare # regenerate parsers from .jison grammars
|
|
281
|
-
npm test # run the full suite
|
|
280
|
+
npm test # run the fail-fast full suite with per-test timings
|
|
282
281
|
```
|
|
283
282
|
|
|
284
|
-
The test
|
|
283
|
+
The test runner prints each test before execution and reports its elapsed time
|
|
284
|
+
afterward. It stops immediately when a test fails. The suite covers:
|
|
285
285
|
|
|
286
286
|
- core runtime and parser behavior
|
|
287
|
-
- type derivation cases in `
|
|
287
|
+
- type derivation cases in `tests/code-derivation/`
|
|
288
288
|
- hash/fingerprint stability
|
|
289
289
|
- object file round-trips
|
|
290
290
|
- REPL scripted interaction
|
package/codecs/BINARY_FORMAT.md
CHANGED
|
@@ -9,7 +9,7 @@ in [`POLYMORPHIC_BINARY_FORMAT.md`](./POLYMORPHIC_BINARY_FORMAT.md):
|
|
|
9
9
|
encode($pattern_value : $pattern) encode(value : decoded_pattern)
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
## Purpose
|
|
12
|
+
## Purpose of This File
|
|
13
13
|
|
|
14
14
|
This file records constraints for any future optimized container layered above
|
|
15
15
|
the canonical stream.
|
|
@@ -288,7 +288,7 @@ Decoding the stream yields `Value(pattern, tree)`. Encoding a runtime `Value`
|
|
|
288
288
|
uses its carried pattern by default, or an explicit caller-supplied pattern when
|
|
289
289
|
one is provided.
|
|
290
290
|
|
|
291
|
-
## Out
|
|
291
|
+
## Out of Scope for the Base Codec
|
|
292
292
|
|
|
293
293
|
The following are intentionally outside the primitive codec:
|
|
294
294
|
|
package/codes.mjs
CHANGED
|
@@ -248,43 +248,24 @@ function finalizeIncremental(newCodes) {
|
|
|
248
248
|
}
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
}
|
|
251
|
+
const reachableCodes = {};
|
|
252
|
+
const queue = Object.keys(resolvedCodes);
|
|
253
|
+
while (queue.length > 0) {
|
|
254
|
+
const name = queue.shift();
|
|
255
|
+
if (reachableCodes[name]) continue;
|
|
258
256
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
257
|
+
const code = resolvedCodes[name] || theRepository.codes[name];
|
|
258
|
+
if (code == null) {
|
|
259
|
+
throw new Error(`Type references undefined type: ${name}`);
|
|
260
|
+
}
|
|
261
|
+
reachableCodes[name] = code;
|
|
262
262
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
globalNames[name] = hash(globalDef);
|
|
263
|
+
for (const dest of Object.values(code[code.code] || {})) {
|
|
264
|
+
if (!reachableCodes[dest]) queue.push(dest);
|
|
265
|
+
}
|
|
267
266
|
}
|
|
268
267
|
|
|
269
|
-
|
|
270
|
-
result[globalNames[name]] = normalizedCodes[name];
|
|
271
|
-
return result;
|
|
272
|
-
}, {});
|
|
273
|
-
|
|
274
|
-
const extendedRepresentatives = Object.keys(representatives).reduce((result, name) => {
|
|
275
|
-
result[name] = globalNames[representatives[name]] || representatives[name] || name;
|
|
276
|
-
return result;
|
|
277
|
-
}, {});
|
|
278
|
-
|
|
279
|
-
Object.values(globalNames).forEach((name) => {
|
|
280
|
-
extendedRepresentatives[name] = name;
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const normalizedGlobalCodes = normalizeAll(globalCodes, extendedRepresentatives);
|
|
284
|
-
return {
|
|
285
|
-
codes: normalizedGlobalCodes,
|
|
286
|
-
representatives: extendedRepresentatives
|
|
287
|
-
};
|
|
268
|
+
return finalize(reachableCodes);
|
|
288
269
|
}
|
|
289
270
|
|
|
290
271
|
function register(newCodes) {
|