@fraczak/k 6.0.1 → 6.0.3
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 +35 -0
- package/CHANGELOG.md +48 -0
- package/CLAUDE.md +64 -0
- package/DOCS/BIG_PICTURE.md +4 -8
- package/DOCS/BINARY_CODEC_STATUS_AND_TODO.md +74 -0
- package/DOCS/CONVERGENCE.md +92 -0
- package/DOCS/DIVERGENCE_DETECTION.md +24 -0
- package/DOCS/Makefile +99 -0
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +221 -0
- package/DOCS/PATTERNS.md +114 -0
- package/DOCS/PATTERNS_AND_TYPED_VALUES.md +665 -0
- package/DOCS/REPL.md +127 -126
- package/DOCS/TEXTUAL_VALUES.md +108 -0
- package/DOCS/TYPE_DERIVATION.md +230 -0
- package/DOCS/book/06-values-in-memory.md +1085 -0
- package/DOCS/book/07-the-partial-function-abi.md +143 -0
- package/DOCS/book/08-operational-semantics-and-execution.md +201 -0
- package/DOCS/book/09-compiler-architecture.md +160 -0
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +167 -0
- package/DOCS/book/14-canonical-serialization.md +176 -0
- package/DOCS/book/appendix-b-incorporating-external-predefined-types-and-functions.md +155 -0
- package/DOCS/book/overview.txt +68 -0
- package/README.md +78 -30
- package/UU/ta.k +1 -0
- package/UU/ta.k~ +0 -0
- package/UU/tb.k +0 -0
- package/UU/tc.k +0 -0
- package/codecs/README.md +12 -0
- package/codecs/pandoc.css +71 -0
- package/codecs/runtime/test-codec.mjs +89 -0
- package/core.klib +0 -0
- package/core.klib~ +0 -0
- package/ieee.klib +0 -0
- package/kkk.k +197 -0
- package/kkk.klib +0 -0
- package/kkk.klib~ +0 -0
- package/object.mjs +162 -8
- package/objects/README.md +10 -0
- package/objects/compile-lib.mjs +26 -2
- package/objects/compile.mjs +26 -2
- package/objects/decompile.mjs +25 -2
- package/objects/extract-aliases.mjs +59 -0
- package/package.json +3 -2
- package/{codecs/k-pattern.mjs → patterns/from-k.mjs} +3 -3
- package/repl2.mjs +345 -83
- package/scripts/generate-dependency-graph.mjs +121 -0
- package/test-k-object.mjs +77 -1
- package/test-repl2.mjs +65 -8
- package/DOCS/book-combined.html +0 -3798
- package/DOCS/book-combined.md +0 -3745
- package/DOCS/book-combined.pdf +0 -0
- package/DOCS/book-combined.tex +0 -5682
- package/DOCS/book.html +0 -65
- package/DOCS/book.pdf +0 -0
- package/DOCS/book.tex +0 -150
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Chapter 7 — The Partial Function ABI
|
|
2
|
+
|
|
3
|
+
## **7.1 Purpose**
|
|
4
|
+
|
|
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 - whether user-defined or compiled - compatible with the same calling convention and data layout.
|
|
7
|
+
|
|
8
|
+
Every compiled `k` function receives a single argument (a runtime value) and may either produce a result or remain undefined.
|
|
9
|
+
The ABI provides a uniform way to express both outcomes.
|
|
10
|
+
|
|
11
|
+
Conceptually, a runtime value is a materialized product/union tree plus its
|
|
12
|
+
current pattern context. The older bare-node ABI below describes the tree part;
|
|
13
|
+
a pattern-aware ABI must carry the pattern beside the node pointer.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## **7.2 Function result representation**
|
|
18
|
+
|
|
19
|
+
The result of every function call is a pair of fields:
|
|
20
|
+
|
|
21
|
+
```c
|
|
22
|
+
struct KOpt {
|
|
23
|
+
bool ok; // 1 if function is defined for the given input
|
|
24
|
+
struct KNode* val; // valid only if ok == true
|
|
25
|
+
struct KPattern* pattern; // optional root pattern for val
|
|
26
|
+
};
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If `ok == 1`, the function succeeded and `val` points to the root node of the resulting value.
|
|
30
|
+
If `ok == 0`, the function is undefined for the input, and `val` is meaningless.
|
|
31
|
+
|
|
32
|
+
This structure carries both the success flag and the value pointer, allowing partiality to be expressed directly in generated code.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## **7.3 Function parameter representation**
|
|
37
|
+
|
|
38
|
+
Each function takes exactly one parameter:
|
|
39
|
+
|
|
40
|
+
```c
|
|
41
|
+
struct KValue* input;
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The parameter points to the input runtime value, including both the root node
|
|
45
|
+
and any carried pattern.
|
|
46
|
+
Since all values are immutable, the function must not modify this structure.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## **7.4 Calling convention**
|
|
51
|
+
|
|
52
|
+
Every `k` function compiled to machine code follows this C-style signature:
|
|
53
|
+
|
|
54
|
+
```c
|
|
55
|
+
struct KOpt k_function(struct KValue* input);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The return value indicates success or failure.
|
|
59
|
+
The convention is uniform for all functions, regardless of the specific types involved.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## **7.5 Core runtime operations**
|
|
64
|
+
|
|
65
|
+
The runtime provides a small set of primitive functions implementing the fundamental operations of `k`:
|
|
66
|
+
|
|
67
|
+
| Operation | Purpose | Result |
|
|
68
|
+
| -------------------------------------- | ----------------------------- | ------------------------------------------------- |
|
|
69
|
+
| `k_project(input, label_id)` | projection `.label` or `/label` | defined if the field or variant `label_id` exists; result carries the selected subpattern when available |
|
|
70
|
+
| `k_make_product(children[], labels[], n)` | construct a product value | always defined; result carries a closed product pattern when child patterns are known |
|
|
71
|
+
| `k_make_union(tag, child)` | construct a union value | always defined; result carries a closed union pattern for `tag` when the child pattern is known |
|
|
72
|
+
| `k_fail()` | represent undefined result | returns `{ ok = 0 }` |
|
|
73
|
+
| `k_unit()` | return the constant unit node | returns `{ ok = 1, val = &unit_node }` |
|
|
74
|
+
|
|
75
|
+
All user-defined functions can be compiled entirely in terms of these operations.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## **7.6 Metadata tables**
|
|
80
|
+
|
|
81
|
+
To interpret or construct nodes correctly, the runtime holds constant tables derived from canonical type definitions:
|
|
82
|
+
|
|
83
|
+
* `state_kind[state]` — `0` for product, `1` for union,
|
|
84
|
+
* `state_arity[state]` — number of child fields for products (always 1 for unions),
|
|
85
|
+
* `field_index[state][label_id]` — index of field in product (or `-1` if absent),
|
|
86
|
+
* `variant_index[state][label_id]` — index of variant in union (or `-1` if absent).
|
|
87
|
+
|
|
88
|
+
These tables are read-only and known at compile time for each canonical type.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## **7.7 Example: projection**
|
|
93
|
+
|
|
94
|
+
To implement the projection `.x`:
|
|
95
|
+
|
|
96
|
+
```c
|
|
97
|
+
struct KOpt k_project_x(struct KValue* input) {
|
|
98
|
+
struct KNode* node = input->root;
|
|
99
|
+
int state = node->state;
|
|
100
|
+
int kind = state_kind[state];
|
|
101
|
+
|
|
102
|
+
if (kind == PRODUCT) {
|
|
103
|
+
int idx = field_index[state]["x"];
|
|
104
|
+
if (idx < 0 || idx >= node->arity)
|
|
105
|
+
return (struct KOpt){0, NULL, NULL};
|
|
106
|
+
return (struct KOpt){1, node->child[idx], project_pattern(input->pattern, "x")};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (kind == UNION) {
|
|
110
|
+
int tag = variant_index[state]["x"];
|
|
111
|
+
if (tag < 0 || node->tag != tag)
|
|
112
|
+
return (struct KOpt){0, NULL, NULL};
|
|
113
|
+
return (struct KOpt){1, node->child[0], project_pattern(input->pattern, "x")};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return (struct KOpt){0, NULL, NULL};
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
This code demonstrates the generic principle:
|
|
121
|
+
the projection is defined if and only if the input has a field or variant named `x`.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## **7.8 Error propagation**
|
|
126
|
+
|
|
127
|
+
When one function calls another, the success flag `ok` propagates forward.
|
|
128
|
+
If a subcall returns `ok == 0`, the caller must return `{0, NULL}` immediately.
|
|
129
|
+
This makes undefinedness explicit and local—no exceptions, no global flags.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## **7.9 Summary**
|
|
134
|
+
|
|
135
|
+
* Every function has the form `KOpt f(KValue*)`.
|
|
136
|
+
* Partiality is expressed by the `ok` flag.
|
|
137
|
+
* Runtime operations handle projection and construction.
|
|
138
|
+
* Runtime values carry pattern context when available.
|
|
139
|
+
* Metadata tables describe field positions and variant indices.
|
|
140
|
+
* By inspecting `arity`, `state`, and `tag`, the runtime can interpret any value correctly.
|
|
141
|
+
* This ABI ensures all generated and runtime functions can interoperate safely.
|
|
142
|
+
|
|
143
|
+
---
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Chapter 8 — Operational Semantics and Execution
|
|
2
|
+
|
|
3
|
+
## **8.1 Purpose**
|
|
4
|
+
|
|
5
|
+
The **operational semantics** of `k` describe how expressions are evaluated step by step on runtime values.
|
|
6
|
+
It defines when a function is *defined* for a particular input and what value it returns.
|
|
7
|
+
All execution—interpretive or compiled—follows these rules.
|
|
8
|
+
|
|
9
|
+
In the current runtime, a value is a materialized tree together with an optional
|
|
10
|
+
root pattern. The tree determines the ordinary product/union computation; the
|
|
11
|
+
pattern records the polymorphic type context used by the codec stream and is
|
|
12
|
+
propagated by structural operations.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## **8.2 Evaluation relation**
|
|
17
|
+
|
|
18
|
+
Evaluation is written as:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
⟨ e , v ⟩ ⇓ r
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
meaning that expression `e` applied to value `v` yields result `r`.
|
|
25
|
+
If `e` is undefined for `v`, the relation does not hold.
|
|
26
|
+
|
|
27
|
+
`r` is a runtime value in memory, represented as described in Chapter 6.
|
|
28
|
+
Undefined results are expressed by the absence of any rule that produces `r`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## **8.3 Rules for base expressions**
|
|
33
|
+
|
|
34
|
+
### **Identity**
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
⟨ () , v ⟩ ⇓ v
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The empty composition `()` returns its argument unchanged.
|
|
41
|
+
|
|
42
|
+
### **Projection**
|
|
43
|
+
|
|
44
|
+
Let `label` be a field or variant name.
|
|
45
|
+
If `v` has a child under `label`,
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
⟨ .label , v ⟩ ⇓ v.label
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Otherwise the projection is undefined.
|
|
52
|
+
|
|
53
|
+
If `v` carries a pattern, the result carries the subpattern reached by `label`.
|
|
54
|
+
For a union projection `/label`, the same rule applies to the selected tag's
|
|
55
|
+
payload.
|
|
56
|
+
|
|
57
|
+
### **Type expression**
|
|
58
|
+
|
|
59
|
+
For a type `T`,
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
⟨ $T , v ⟩ ⇓ v if v ∈ T
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
and undefined otherwise.
|
|
66
|
+
Type expressions thus act as identity functions restricted to their type.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## **8.4 Rules for composition**
|
|
71
|
+
|
|
72
|
+
### **Sequential composition**
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
⟨ (f g) , v ⟩ ⇓ r
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
if there exists `u` such that
|
|
79
|
+
`⟨ f , v ⟩ ⇓ u` and `⟨ g , u ⟩ ⇓ r`.
|
|
80
|
+
|
|
81
|
+
If either step is undefined, the composition is undefined.
|
|
82
|
+
Because composition is associative,
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
(f (g h)) ≡ ((f g) h) ≡ (f g h)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Parentheses are needed only for `()`.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## **8.5 Rules for product composition**
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
⟨ { f₁ l₁ , f₂ l₂ , … , fₙ lₙ } , v ⟩ ⇓ { r₁ l₁ , r₂ l₂ , … , rₙ lₙ }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
if and only if all subfunctions `fᵢ` are defined on `v` and yield `rᵢ`.
|
|
99
|
+
If any subfunction is undefined, the whole product composition is undefined.
|
|
100
|
+
|
|
101
|
+
A product composition constructs a new product node;
|
|
102
|
+
each result `rᵢ` becomes one child in canonical field order.
|
|
103
|
+
If the component results carry patterns, the constructed value carries the
|
|
104
|
+
closed product pattern with field `lᵢ` pointing to the corresponding result
|
|
105
|
+
pattern.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## **8.6 Rules for union composition**
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
⟨ < f₁ , f₂ , … , fₙ > , v ⟩ ⇓ rⱼ
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
if there exists the smallest index `j` such that
|
|
116
|
+
`⟨ fⱼ , v ⟩ ⇓ rⱼ`.
|
|
117
|
+
|
|
118
|
+
If no subfunction is defined, the union composition is undefined.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## **8.7 Rules for filters**
|
|
123
|
+
|
|
124
|
+
If a filter `F` matches a single type `T`
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
⟨ ?F , v ⟩ ⇓ ⟨ $T , v ⟩
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
otherwise it is an identity
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
⟨ ?F , v ⟩ ⇓ v
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Filters therefore act as compile time annotations for type checking only.
|
|
137
|
+
As operations they are identities at run time, unless they lead to fully typed
|
|
138
|
+
expressions (i.e., can be replaced by a type). Their pattern information may
|
|
139
|
+
still be present on the runtime value because values can carry codec patterns.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## **8.8 Evaluation order**
|
|
144
|
+
|
|
145
|
+
Evaluation proceeds left to right.
|
|
146
|
+
In products, all subfunctions receive the same input;
|
|
147
|
+
in unions, later functions are evaluated only if earlier ones fail.
|
|
148
|
+
|
|
149
|
+
The semantics are deterministic:
|
|
150
|
+
for any given input, at most one result tree can be produced.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## **8.9 Example**
|
|
155
|
+
|
|
156
|
+
Given:
|
|
157
|
+
|
|
158
|
+
```k-lang
|
|
159
|
+
$bool = < {} true, {} false >;
|
|
160
|
+
neg = $bool < .true {{ } false}, .false {{ } true} > $bool;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
and input value `{ {} true }` of type `bool`,
|
|
164
|
+
evaluation steps are:
|
|
165
|
+
|
|
166
|
+
1. `⟨ $bool , { {} true } ⟩ ⇓ { {} true }`
|
|
167
|
+
2. `⟨ < .true {{ } false}, .false {{ } true} > , { {} true } ⟩ ⇓ {{ } false}`
|
|
168
|
+
3. `⟨ $bool , {{ } false} ⟩ ⇓ {{ } false}`
|
|
169
|
+
|
|
170
|
+
Final result: `{{ } false}`.
|
|
171
|
+
If the input were of another type, step 1 would be undefined.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## **8.10 Implementation correspondence**
|
|
176
|
+
|
|
177
|
+
The evaluation rules map directly onto the runtime ABI:
|
|
178
|
+
|
|
179
|
+
| Semantic rule | Runtime operation |
|
|
180
|
+
| ------------------- | ------------------------------------- |
|
|
181
|
+
| Projection | `k_project` |
|
|
182
|
+
| Product composition | multiple subcalls + `k_make_product` |
|
|
183
|
+
| Union composition | sequential subcalls with early return |
|
|
184
|
+
| Type | runtime check of `state` |
|
|
185
|
+
| Composition | function call chain |
|
|
186
|
+
|
|
187
|
+
In compiled form, the `ok` flag of `KOpt` represents whether a rule applies;
|
|
188
|
+
the node pointer represents the result value.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## **8.11 Summary**
|
|
193
|
+
|
|
194
|
+
* Execution follows deterministic, left-to-right rules.
|
|
195
|
+
* All expressions denote partial functions on runtime values.
|
|
196
|
+
* Runtime values may carry patterns, and structural operations propagate them.
|
|
197
|
+
* Type expressions act as restricted identities.
|
|
198
|
+
* Composition is associative; undefined propagates automatically.
|
|
199
|
+
* Runtime semantics match the formal evaluation relation exactly.
|
|
200
|
+
|
|
201
|
+
---
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Chapter 9 — Compiler Architecture
|
|
2
|
+
|
|
3
|
+
## **9.1 What the compiler does**
|
|
4
|
+
|
|
5
|
+
A *compiler* for the `k` language is a program that reads another program (written in `k`) and produces a lower-level version of it.
|
|
6
|
+
The lower-level version can then be executed efficiently by a computer.
|
|
7
|
+
|
|
8
|
+
For `k`, the compiler’s task is simple in concept:
|
|
9
|
+
|
|
10
|
+
1. **Read** the definitions of types and functions.
|
|
11
|
+
2. **Understand** their structure and the relationships between them.
|
|
12
|
+
3. **Translate** them into equivalent instructions that follow the runtime conventions described in Chapters 6–8.
|
|
13
|
+
|
|
14
|
+
The compiler must ensure that every function in the source program becomes a machine-level function that behaves in exactly the same way:
|
|
15
|
+
it takes a value as input, may or may not be defined for it, and, if defined, produces another value.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## **9.2 The main stages**
|
|
20
|
+
|
|
21
|
+
A `k` compiler is organized as a sequence of well-defined stages.
|
|
22
|
+
Each stage transforms one representation of the program into another, simpler one.
|
|
23
|
+
|
|
24
|
+
1. **Reading and parsing** — The compiler converts the source text into a tree of syntactic objects.
|
|
25
|
+
This tree is called the *abstract syntax tree* (AST).
|
|
26
|
+
It records the structure of expressions such as `{ .x a, .y b }` or `< .x, .y >`.
|
|
27
|
+
|
|
28
|
+
2. **Type analysis and normalization** —
|
|
29
|
+
The compiler expands type names, checks their correctness, and replaces all filters and meta-variables by concrete type information, following the procedure from Chapter 5.
|
|
30
|
+
The result is a fully typed AST.
|
|
31
|
+
|
|
32
|
+
3. **Preparation for execution** —
|
|
33
|
+
The compiler builds a *type table* for all canonical type states (C0, C1, …).
|
|
34
|
+
For each type, it records whether it is a product or a union and the number and names of its fields or variants.
|
|
35
|
+
These tables become the metadata used at runtime.
|
|
36
|
+
|
|
37
|
+
4. **Code generation** —
|
|
38
|
+
Finally, the compiler walks through the typed AST and produces an equivalent description in a lower-level form that follows the runtime conventions: functions returning a success flag and a value pointer.
|
|
39
|
+
This form can be expressed directly in C or in the *LLVM intermediate representation*, which is a platform-independent format understood by many compilers.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## **9.3 Internal structure**
|
|
44
|
+
|
|
45
|
+
A minimal compiler can be viewed as three connected modules:
|
|
46
|
+
|
|
47
|
+
* **Front end** — reads source code, builds the AST, performs type analysis.
|
|
48
|
+
* **Middle** — resolves all references, normalizes types, and simplifies expressions.
|
|
49
|
+
* **Back end** — produces the executable form.
|
|
50
|
+
|
|
51
|
+
Although these terms are traditional, the distinction is simple:
|
|
52
|
+
the front end *understands* the program, the back end *emits* it, and the middle part connects the two.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## **9.4 Data kept by the compiler**
|
|
57
|
+
|
|
58
|
+
During translation the compiler maintains several tables:
|
|
59
|
+
|
|
60
|
+
| Name | Purpose |
|
|
61
|
+
| ------------------ | ----------------------------------------------------------------- |
|
|
62
|
+
| **Type table** | Maps type names to canonical forms and assigns numeric state IDs. |
|
|
63
|
+
| **Field table** | For each product type, records the order and index of its fields. |
|
|
64
|
+
| **Variant table** | For each union type, records the tags and their numeric indices. |
|
|
65
|
+
| **Function table** | Associates function names with their input and output types. |
|
|
66
|
+
|
|
67
|
+
These tables ensure that generated code can find fields and variants by number rather than by name, which simplifies execution.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## **9.5 Example of transformation**
|
|
72
|
+
|
|
73
|
+
Consider a simple k program:
|
|
74
|
+
|
|
75
|
+
```k-lang
|
|
76
|
+
$bool = < {} true, {} false >;
|
|
77
|
+
neg = $bool < /true | false, /false | true > $bool;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
1. **Parsing:**
|
|
81
|
+
The compiler recognizes a type definition `$bool` and a function `neg`.
|
|
82
|
+
|
|
83
|
+
2. **Type analysis:**
|
|
84
|
+
Both the input and output of `neg` are of type `bool`.
|
|
85
|
+
|
|
86
|
+
3. **Internal representation:**
|
|
87
|
+
The compiler constructs an internal tree describing that
|
|
88
|
+
`< /true f₁, /false f₂ >` means:
|
|
89
|
+
try the projection `/true`, then `/false`, each producing a constant value.
|
|
90
|
+
|
|
91
|
+
4. **Generated form:**
|
|
92
|
+
In the target language, `neg` becomes a small function that:
|
|
93
|
+
|
|
94
|
+
* calls `k_project` to check which variant the input has,
|
|
95
|
+
* creates a new node with the opposite variant using `k_make_union`, and
|
|
96
|
+
* returns the result with `ok = 1`.
|
|
97
|
+
|
|
98
|
+
This sequence of steps is fully automatic and follows directly from the semantic rules in Chapter 8.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## **9.6 Intermediate representation**
|
|
103
|
+
|
|
104
|
+
Before producing final code, many compilers use an *intermediate representation* (IR).
|
|
105
|
+
It is a language designed to be easy to generate and easy to translate further into real machine code.
|
|
106
|
+
For `k`, the IR mirrors the structure of partial functions:
|
|
107
|
+
|
|
108
|
+
| Concept in k | IR operation |
|
|
109
|
+
| --------------------- | -------------------------------- |
|
|
110
|
+
| Projection `.x` '/x' | `PROJECT label_id` |
|
|
111
|
+
| Composition `(f g)` | `CALL f; CALL g` |
|
|
112
|
+
| Union `<f,g>` | `TRY f; IF undefined THEN TRY g` |
|
|
113
|
+
| Product `{.x f, .y g}` | `PRODUCT_CALL (f, g); COMBINE` |
|
|
114
|
+
| Type restriction `$T` | `CHECK_TYPE state_id` |
|
|
115
|
+
|
|
116
|
+
The compiler converts each AST node into one or more IR instructions.
|
|
117
|
+
The `PRODUCT_CALL(f, g)` instruction is a higher-level concept that implies applying both `f` and `g` to the same input value.
|
|
118
|
+
The low-level implementation of this would use stack operations to duplicate the input, but the IR itself remains more abstract.
|
|
119
|
+
Later, these instructions are translated into LLVM or C code using the runtime ABI.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## **9.7 Linking with the runtime**
|
|
124
|
+
|
|
125
|
+
The generated code does not contain memory allocation or type tables itself.
|
|
126
|
+
Instead, it relies on the runtime library (`krt`), which provides:
|
|
127
|
+
|
|
128
|
+
* functions such as `k_project` and `k_make_union`,
|
|
129
|
+
* metadata tables for each canonical type, and
|
|
130
|
+
* the unit value `{}` shared by all programs.
|
|
131
|
+
|
|
132
|
+
When the compiler finishes, it outputs a text file in the target language plus references to this runtime library.
|
|
133
|
+
The two together form an executable program.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## **9.8 Simplicity of the model**
|
|
138
|
+
|
|
139
|
+
Because every expression in `k` takes exactly one input and may return one output or be undefined,
|
|
140
|
+
the compiler never has to deal with variable environments, loops, or side effects.
|
|
141
|
+
This makes the structure of the generated program very regular:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
input → [series of calls and checks] → output or undefined
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Each compiled function is independent and stateless.
|
|
148
|
+
This regularity is what allows `k` to map cleanly to low-level code.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## **9.9 Summary**
|
|
153
|
+
|
|
154
|
+
* The compiler transforms `k` programs into executable form through a sequence of simple, deterministic steps.
|
|
155
|
+
* Type information and structure are resolved before code generation.
|
|
156
|
+
* All generated functions follow the same calling convention (`ok`, `val`).
|
|
157
|
+
* The runtime library provides the shared operations needed by every program.
|
|
158
|
+
* The entire process mirrors the formal semantics defined earlier but expresses it as concrete machine operations.
|
|
159
|
+
|
|
160
|
+
---
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Chapter 10 — From AST to Intermediate Representation
|
|
2
|
+
|
|
3
|
+
## **10.1 The purpose of an intermediate form**
|
|
4
|
+
|
|
5
|
+
The abstract syntax tree (AST) of a `k` program directly reflects how the source code was written.
|
|
6
|
+
It is easy to read but not convenient for generating machine code.
|
|
7
|
+
Before code can be produced, the compiler rewrites this tree into a **simpler, regular structure** called the *intermediate representation* (IR).
|
|
8
|
+
|
|
9
|
+
The IR is close to what the machine will execute, but it still looks like a structured program rather than raw binary instructions.
|
|
10
|
+
Each node in the IR corresponds to a single, well-defined operation such as *project a field*, *apply a function*, or *combine results*.
|
|
11
|
+
|
|
12
|
+
Using an intermediate form helps in three ways:
|
|
13
|
+
|
|
14
|
+
1. It separates understanding of the language from machine details.
|
|
15
|
+
2. It allows the compiler to verify that every function is well formed.
|
|
16
|
+
3. It provides a uniform surface for optimization and for later translation into C or LLVM.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## **10.2 Shape of the intermediate representation**
|
|
21
|
+
|
|
22
|
+
Each IR instruction corresponds to one semantic rule from Chapter 8.
|
|
23
|
+
The IR does not need to represent filters or meta-variables, since those are resolved earlier.
|
|
24
|
+
Only concrete actions remain.
|
|
25
|
+
|
|
26
|
+
| Operation | Meaning |
|
|
27
|
+
| ----------------------------- | ---------------------------------------------------------------- |
|
|
28
|
+
| **PROJECT label_id** | Apply `.label` to the input value. |
|
|
29
|
+
| **TYPECHECK state_id** | Accept the input only if its root has the given canonical state. |
|
|
30
|
+
| **CALL function_id** | Invoke another function following the ABI. |
|
|
31
|
+
| **UNION [f₁, f₂, …]** | Try several functions in order until one succeeds. |
|
|
32
|
+
| **PRODUCT [l₁=f₁, l₂=f₂, …]** | Apply each function to the same input and combine results. |
|
|
33
|
+
| **FAIL** | Always undefined. |
|
|
34
|
+
| **SEQ [f₁, f₂, …]** | Apply functions in sequence (composition). |
|
|
35
|
+
|
|
36
|
+
Each function in the program becomes a small list or tree of such IR instructions.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## **10.3 Example**
|
|
41
|
+
|
|
42
|
+
Source program:
|
|
43
|
+
|
|
44
|
+
```k-lang
|
|
45
|
+
$bool = < {} true, {} false >;
|
|
46
|
+
neg = $bool < .true {{ } false}, .false {{ } true} > $bool;
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The IR for `neg` can be written informally as:
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
neg:
|
|
53
|
+
TYPECHECK bool
|
|
54
|
+
UNION [
|
|
55
|
+
SEQ [ PROJECT true, CONST false ],
|
|
56
|
+
SEQ [ PROJECT false, CONST true ]
|
|
57
|
+
]
|
|
58
|
+
TYPECHECK bool
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This IR expresses the same logic as the source:
|
|
62
|
+
check the type, then try the first projection `.true`, otherwise the second, and finally ensure the result is again of type `$bool`.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## **10.4 Relation to evaluation**
|
|
67
|
+
|
|
68
|
+
Every IR operation corresponds to a runtime action:
|
|
69
|
+
|
|
70
|
+
| IR operation | Runtime function or behavior |
|
|
71
|
+
| ------------ | --------------------------------------------------- |
|
|
72
|
+
| PROJECT | `k_project(input, label_id)` |
|
|
73
|
+
| TYPECHECK | verify `input->state == state_id` |
|
|
74
|
+
| CALL | call another generated function |
|
|
75
|
+
| UNION | sequential `if`–`else` test on success flags |
|
|
76
|
+
| PRODUCT | multiple subcalls + `k_make_product` |
|
|
77
|
+
| FAIL | return `{ ok = 0 }` immediately |
|
|
78
|
+
| SEQ | connect the output of one step as input to the next |
|
|
79
|
+
|
|
80
|
+
By describing computation in these terms, the compiler can generate executable code simply by replacing each IR instruction with its runtime equivalent.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## **10.5 Building the IR**
|
|
85
|
+
|
|
86
|
+
The process of converting the AST to IR follows a recursive pattern:
|
|
87
|
+
|
|
88
|
+
1. **Projections** become single instructions `PROJECT`.
|
|
89
|
+
2. **Compositions** `(f g h)` become a `SEQ` list of their components.
|
|
90
|
+
3. **Unions** `<f,g>` become a `UNION` list.
|
|
91
|
+
4. **Products** `{f l₁, g l₂}` become a `PRODUCT` list with labeled entries.
|
|
92
|
+
5. **Type expressions** `$T` become `TYPECHECK` instructions.
|
|
93
|
+
6. **Filters** disappear; their meaning is already captured by typechecks.
|
|
94
|
+
|
|
95
|
+
Each transformation step produces IR nodes with fixed behavior and explicit order.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## **10.6 Verifying the IR**
|
|
100
|
+
|
|
101
|
+
Because k programs are defined by simple composition, a small set of checks ensures correctness:
|
|
102
|
+
|
|
103
|
+
1. Each `UNION` list must have at least one element.
|
|
104
|
+
2. Every `PROJECT` must have a valid label.
|
|
105
|
+
3. Input and output states of consecutive steps must match.
|
|
106
|
+
4. A function must end in an operation that produces a result (`PROJECT`, `CALL`, or combination).
|
|
107
|
+
|
|
108
|
+
After verification, the IR is guaranteed to represent a valid partial function according to the semantics in Chapter 8.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## **10.7 Intermediate form as a small language**
|
|
113
|
+
|
|
114
|
+
The IR can be viewed as a minimal language on its own.
|
|
115
|
+
It has:
|
|
116
|
+
|
|
117
|
+
* one data type (`KNode*`),
|
|
118
|
+
* one universal value format (trees),
|
|
119
|
+
* and a fixed set of operators.
|
|
120
|
+
|
|
121
|
+
Its execution model is the same as the runtime ABI.
|
|
122
|
+
This means an interpreter for the IR can serve as a reference implementation of the k semantics before any machine code generation is added.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## **10.8 Example: product composition**
|
|
127
|
+
|
|
128
|
+
For input function `{ .x a, .y b }`,
|
|
129
|
+
the IR is:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
PRODUCT [
|
|
133
|
+
a = PROJECT x,
|
|
134
|
+
b = PROJECT y
|
|
135
|
+
]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The runtime sequence is:
|
|
139
|
+
|
|
140
|
+
1. Apply each projection.
|
|
141
|
+
2. If both succeed, build a new node with two children in order `(a,b)`.
|
|
142
|
+
|
|
143
|
+
This pattern covers all record-building operations in the language.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## **10.9 Why this representation is useful**
|
|
148
|
+
|
|
149
|
+
This intermediate form provides three practical benefits:
|
|
150
|
+
|
|
151
|
+
1. **Clarity** – every step is explicit and local.
|
|
152
|
+
2. **Verifiability** – type consistency and definedness can be checked mechanically.
|
|
153
|
+
3. **Flexibility** – the same IR can be translated to many targets (LLVM IR, C, or even interpreted directly).
|
|
154
|
+
|
|
155
|
+
Because of these advantages, most compilers for declarative languages use an intermediate form of this kind.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## **10.10 Summary**
|
|
160
|
+
|
|
161
|
+
* The abstract syntax tree is rewritten into a simpler, machine-oriented form.
|
|
162
|
+
* Each IR instruction represents one operation defined by the `k` semantics.
|
|
163
|
+
* Type and filter information appear as explicit checks.
|
|
164
|
+
* The IR is easy to verify and to translate into executable code.
|
|
165
|
+
* The IR can also serve as a precise description of how `k` functions behave step by step.
|
|
166
|
+
|
|
167
|
+
---
|