@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
package/DOCS/REPL.md
CHANGED
|
@@ -1,189 +1,190 @@
|
|
|
1
1
|
# k Interpreter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`repl2.mjs` is the interactive k interpreter. `k-repl` and `k-repl2` both
|
|
4
|
+
start it.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
its internal state: definitions accumulate incrementally, expressions compile
|
|
7
|
-
in the context of the current library state, and the session can be exported as
|
|
8
|
-
either a reusable `.klib` library or an executable `.ko` object.
|
|
6
|
+
The interpreter keeps a live `.klib`-style state in memory:
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
- registered codes
|
|
9
|
+
- compiled relations
|
|
10
|
+
- human aliases for both
|
|
11
|
+
- the current value flowing through the session
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
Raw k snippets compile on top of that state. You can also export the current
|
|
14
|
+
session as a `.klib` library or compile an executable `.ko` object from an
|
|
15
|
+
expression in the session context.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
┌─────────────────────────────────────────────────┐
|
|
18
|
-
│ codes: { hash → code definition } │ ← registered types
|
|
19
|
-
│ rels: { @hash → typed relation } │ ← compiled relations
|
|
20
|
-
│ aliases: { name → @hash } │ ← human names
|
|
21
|
-
│ value: current value (with .pattern) │ ← last result
|
|
22
|
-
│ lastMain: last relation/expression │ ← default .ko main
|
|
23
|
-
└─────────────────────────────────────────────────┘
|
|
24
|
-
```
|
|
17
|
+
## Prompt Model
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
- `aliases` maps human names to canonical hashes. Redefining a name updates
|
|
28
|
-
the alias; the old definition remains in `rels` (content-addressed, no conflict).
|
|
29
|
-
- `value` is the current pipeline value, initially `{}`.
|
|
19
|
+
There are two kinds of input:
|
|
30
20
|
|
|
31
|
-
|
|
21
|
+
- commands, starting with `:`
|
|
22
|
+
- raw k source
|
|
23
|
+
|
|
24
|
+
Commands execute immediately, but only when there is no open raw snippet in the
|
|
25
|
+
buffer.
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
Raw k source is accumulated until the interpreter can decide the snippet is
|
|
28
|
+
complete.
|
|
35
29
|
|
|
36
|
-
|
|
37
|
-
commands such as `:load` and `:export`, and canonical names that start with
|
|
38
|
-
`@`.
|
|
30
|
+
## Commands
|
|
39
31
|
|
|
40
|
-
| Command |
|
|
41
|
-
|
|
42
|
-
| `:
|
|
43
|
-
| `:
|
|
32
|
+
| Command | Meaning |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| `:help` | Show command summary |
|
|
35
|
+
| `:type name = <...>` | Define a type alias |
|
|
36
|
+
| `:type name` | Show the canonical definition of a type |
|
|
37
|
+
| `:code name` | Alias for `:C name` |
|
|
38
|
+
| `:rel name = expr` | Define a relation alias |
|
|
44
39
|
| `:def name = expr` | Alias for `:rel` |
|
|
45
|
-
| `:run expr` |
|
|
46
|
-
| `:
|
|
47
|
-
| `:
|
|
48
|
-
| `:
|
|
40
|
+
| `:run expr` | Evaluate `expr` on the current value |
|
|
41
|
+
| `:eval expr` | Alias for `:run` |
|
|
42
|
+
| `:t name` | Show relation input/output filters |
|
|
43
|
+
| `:d name` | Show relation definition |
|
|
44
|
+
| `:C name` | Show canonical code definition |
|
|
49
45
|
| `:codes` | List type aliases |
|
|
50
46
|
| `:rels` | List relation aliases |
|
|
51
|
-
| `:
|
|
52
|
-
| `:klib file` | Export current state as `.klib` |
|
|
53
|
-
| `:ko file
|
|
54
|
-
| `:
|
|
55
|
-
| `:
|
|
56
|
-
| `:
|
|
57
|
-
| `:reset` | Clear all state |
|
|
58
|
-
| `:help` | Show available commands |
|
|
59
|
-
| `:quit` | Exit |
|
|
47
|
+
| `:load [--no-alias] file` | Load `.k` source or `.klib` into the current state |
|
|
48
|
+
| `:klib file` | Export current state as a `.klib` library |
|
|
49
|
+
| `:ko file expr` | Export a `.ko` executable with `expr` as main |
|
|
50
|
+
| `:val` | Print current value and JSON form |
|
|
51
|
+
| `:reset` | Reset interpreter state |
|
|
52
|
+
| `:quit` / `:exit` | Exit |
|
|
60
53
|
|
|
61
|
-
|
|
62
|
-
k expression syntax.
|
|
54
|
+
## Raw Snippets
|
|
63
55
|
|
|
64
|
-
|
|
56
|
+
Raw input is compiled as k source on top of the current state.
|
|
65
57
|
|
|
66
|
-
|
|
58
|
+
Examples:
|
|
67
59
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
```k
|
|
61
|
+
> {} |succ
|
|
62
|
+
```
|
|
71
63
|
|
|
72
|
-
|
|
64
|
+
```k
|
|
65
|
+
> $ bool = <{} true, {} false>;
|
|
66
|
+
```
|
|
73
67
|
|
|
74
|
-
|
|
68
|
+
```k
|
|
69
|
+
> $ bool = <{} true, {} false>
|
|
70
|
+
; not = $bool </true | false, {} | true >
|
|
71
|
+
; {} |true not
|
|
72
|
+
```
|
|
75
73
|
|
|
76
|
-
|
|
74
|
+
The interpreter uses these rules, in order:
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
1. If the line ends with `\` followed only by spaces, keep buffering.
|
|
77
|
+
2. If the line ends with `;` followed only by spaces, that line definitively
|
|
78
|
+
closes the snippet.
|
|
79
|
+
3. Otherwise, try to parse the buffered snippet:
|
|
80
|
+
- if it is a complete k program, compile it now
|
|
81
|
+
- if it is a valid prefix, wait for more input
|
|
82
|
+
- otherwise report the parse error immediately
|
|
81
83
|
|
|
82
|
-
|
|
84
|
+
After a snippet is accepted:
|
|
83
85
|
|
|
84
|
-
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
- Print: `name = @hash`
|
|
86
|
+
- if it has a terminal expression, compile it and evaluate it on the current
|
|
87
|
+
value
|
|
88
|
+
- if it is definitions only, compile it into the current state and print
|
|
89
|
+
nothing on success
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
This makes raw snippets useful both for interactive evaluation and for growing
|
|
92
|
+
the live library context.
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
- Compile with current library context.
|
|
94
|
-
- Run on current `value`.
|
|
95
|
-
- Update `value` with result.
|
|
96
|
-
- Print result with envelope (see Output Format).
|
|
94
|
+
## State
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
The interpreter keeps:
|
|
99
97
|
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
- `codes`: canonical code definitions
|
|
99
|
+
- `rels`: canonical compiled relations
|
|
100
|
+
- `typeAliases`: human type names to canonical hashes
|
|
101
|
+
- `relAliases`: human relation names to canonical hashes
|
|
102
|
+
- `value`: current value, initially `{}`
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
<user input>
|
|
106
|
-
```
|
|
104
|
+
Definitions are content-addressed. Rebinding an alias changes the name-to-hash
|
|
105
|
+
mapping, but older canonical definitions remain available by hash.
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
an existing canonical code by hash).
|
|
107
|
+
## Alias Resolution
|
|
110
108
|
|
|
111
|
-
|
|
109
|
+
Before compiling user input, the interpreter injects an alias preamble so human
|
|
110
|
+
names can be reused naturally:
|
|
112
111
|
|
|
113
|
-
|
|
112
|
+
```k
|
|
113
|
+
$ nat = @...;
|
|
114
|
+
succ = @...;
|
|
115
|
+
<user snippet>
|
|
116
|
+
```
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
Type aliases use `$ name = @hash;`. Relation aliases use `name = @hash;`.
|
|
116
119
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
+
Diagnostic locations are remapped back to the visible user snippet, so error
|
|
121
|
+
line numbers do not count the hidden preamble.
|
|
120
122
|
|
|
121
|
-
|
|
123
|
+
## Completion
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
> {}|zero|succ|succ
|
|
125
|
-
{succ: {succ: {zero: {}}}} ?<{} zero, X succ>=X
|
|
125
|
+
Tab completion covers:
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
- command names after `:`
|
|
128
|
+
- file paths for `:load`, `:klib`, and `:ko`
|
|
129
|
+
- type aliases
|
|
130
|
+
- relation aliases
|
|
131
|
+
- canonical names beginning with `@`
|
|
130
132
|
|
|
131
|
-
|
|
132
|
-
`codecs/runtime/show-value.mjs`.
|
|
133
|
+
Type aliases also complete in `$name` position inside raw k input.
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
## Loading
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
... undefined
|
|
138
|
-
```
|
|
137
|
+
### `:load [--no-alias] file.k`
|
|
139
138
|
|
|
140
|
-
|
|
139
|
+
Compiles the source in the current library context, merges the resulting codes
|
|
140
|
+
and relations into the session, and recovers aliases from user-defined names in
|
|
141
|
+
the file unless `--no-alias` is used.
|
|
141
142
|
|
|
142
|
-
### `:load file.
|
|
143
|
+
### `:load [--no-alias] file.klib`
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
Decodes the library file and merges its codes, relations, and metadata into the
|
|
146
|
+
session. Aliases are recovered from library metadata unless `--no-alias` is
|
|
147
|
+
used.
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
## Export
|
|
149
150
|
|
|
150
|
-
|
|
151
|
-
- Merge codes and rels into state.
|
|
152
|
-
- Update aliases from `meta` (recover source names from origins).
|
|
151
|
+
### `:klib file`
|
|
153
152
|
|
|
154
|
-
|
|
153
|
+
Writes the current interpreter state as a `.klib` library.
|
|
155
154
|
|
|
156
|
-
### `:
|
|
155
|
+
### `:ko file expr`
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
Compiles `expr` as the main expression in the current interpreter context and
|
|
158
|
+
writes the resulting executable `.ko` object.
|
|
160
159
|
|
|
161
|
-
|
|
160
|
+
## Output
|
|
162
161
|
|
|
163
|
-
|
|
162
|
+
Evaluated values print in k syntax together with the inferred envelope:
|
|
164
163
|
|
|
165
|
-
|
|
164
|
+
```text
|
|
165
|
+
{succ: {succ: {zero: {}}}} ?<{} zero, X succ>=X
|
|
166
|
+
```
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
- If `expr` is omitted, reuse the last relation definition or expression that
|
|
169
|
-
was evaluated in the interpreter.
|
|
170
|
-
- The current `.klib` state is passed as the compilation library context.
|
|
168
|
+
`undefined` prints as:
|
|
171
169
|
|
|
172
|
-
|
|
170
|
+
```text
|
|
171
|
+
... undefined
|
|
172
|
+
```
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
-
|
|
176
|
-
last main expression.
|
|
174
|
+
`valueToK`, `propertyListToFilter`, and `valueWithEnvelopeToK` from
|
|
175
|
+
`codecs/runtime/show-value.mjs` are used for rendering.
|
|
177
176
|
|
|
178
|
-
## Example
|
|
177
|
+
## Example Session
|
|
179
178
|
|
|
180
|
-
```
|
|
179
|
+
```text
|
|
181
180
|
> :type nat = <{} zero, nat succ>
|
|
182
181
|
$ nat = @...
|
|
183
182
|
> :rel succ = |succ
|
|
184
183
|
succ = @...
|
|
185
|
-
>
|
|
186
|
-
{}
|
|
184
|
+
> {} |zero
|
|
185
|
+
{zero: {}} ?<{} zero, X succ>=X
|
|
186
|
+
> :t succ
|
|
187
|
+
succ : ?X --> ?<{} zero, X succ>=X (@...)
|
|
187
188
|
> :klib nat.klib
|
|
188
189
|
saved nat.klib
|
|
189
190
|
> :ko succ.ko succ
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Textual k Values
|
|
2
|
+
|
|
3
|
+
`k-parse` and `k-print` work with a textual notation for k values.
|
|
4
|
+
|
|
5
|
+
It looks like JSON because products print as JSON objects, but semantically it
|
|
6
|
+
is not a generic JSON format. It is a readable notation for values built from
|
|
7
|
+
the two k data constructors:
|
|
8
|
+
|
|
9
|
+
- product
|
|
10
|
+
- tagged union
|
|
11
|
+
|
|
12
|
+
## The Value Model
|
|
13
|
+
|
|
14
|
+
A k value is a tree.
|
|
15
|
+
|
|
16
|
+
- A product is a mapping from field labels to child values.
|
|
17
|
+
- A union is a single chosen tag together with its payload value.
|
|
18
|
+
|
|
19
|
+
There are no primitive leaves. The base leaf is the empty product:
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
{}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Textual Notation
|
|
26
|
+
|
|
27
|
+
The textual notation used by `k-parse` and `k-print` is:
|
|
28
|
+
|
|
29
|
+
- `{}` for the empty product
|
|
30
|
+
- `{"field": value, ...}` for a product
|
|
31
|
+
- `{"tag": value}` for a union
|
|
32
|
+
|
|
33
|
+
The last form is why the notation only looks like JSON. Syntactically, a
|
|
34
|
+
single-entry object is ambiguous in plain JSON, but in k it is interpreted
|
|
35
|
+
through the inferred or supplied pattern.
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
{}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
{"point":{"x":{},"y":{}}}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
{"succ":{"succ":{"zero":{}}}}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The last example is the k value usually written in native notation as:
|
|
52
|
+
|
|
53
|
+
```k
|
|
54
|
+
{}|zero|succ|succ
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Relation to Native k Notation
|
|
58
|
+
|
|
59
|
+
The native k notation is more explicit for interactive work:
|
|
60
|
+
|
|
61
|
+
- product construction: `{ v field, w other }`
|
|
62
|
+
- union introduction: `v |tag`
|
|
63
|
+
|
|
64
|
+
`k-print` does not emit that native notation. It emits the JSON-like tree
|
|
65
|
+
notation because it is easy to pipe, inspect, and round-trip through standard
|
|
66
|
+
text tooling.
|
|
67
|
+
|
|
68
|
+
## `k-parse`
|
|
69
|
+
|
|
70
|
+
`k-parse`:
|
|
71
|
+
|
|
72
|
+
1. reads textual k values
|
|
73
|
+
2. optionally takes an input type or input pattern
|
|
74
|
+
3. produces the binary `pattern + value` stream
|
|
75
|
+
|
|
76
|
+
When no explicit pattern or type is supplied, `k-parse` derives a witness
|
|
77
|
+
pattern from the textual tree.
|
|
78
|
+
|
|
79
|
+
## `k-print`
|
|
80
|
+
|
|
81
|
+
`k-print`:
|
|
82
|
+
|
|
83
|
+
1. reads the binary `pattern + value` stream
|
|
84
|
+
2. decodes it to a runtime value with its carried pattern
|
|
85
|
+
3. prints the value tree in JSON-like notation
|
|
86
|
+
|
|
87
|
+
So `k-print` is not a JSON decoder in the general sense. It is a renderer from
|
|
88
|
+
k runtime values to a JSON-shaped textual notation.
|
|
89
|
+
|
|
90
|
+
## Ambiguity and Patterns
|
|
91
|
+
|
|
92
|
+
The textual tree alone does not always determine whether a single-child node is
|
|
93
|
+
meant as a union or a singleton product. That is why patterns matter.
|
|
94
|
+
|
|
95
|
+
When `k-parse` has no explicit input pattern, it applies the current witness
|
|
96
|
+
rule:
|
|
97
|
+
|
|
98
|
+
- empty node => closed product
|
|
99
|
+
- node with multiple children => closed product
|
|
100
|
+
- node with one child => open union by default
|
|
101
|
+
|
|
102
|
+
An explicit input pattern or input type can force a different interpretation.
|
|
103
|
+
|
|
104
|
+
## See Also
|
|
105
|
+
|
|
106
|
+
- [codecs/README.md](../codecs/README.md)
|
|
107
|
+
- [DOCS/PATTERNS.md](./PATTERNS.md)
|
|
108
|
+
- [DOCS/OBJECT_FILE_AND_PATTERN.md](./OBJECT_FILE_AND_PATTERN.md)
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Type Derivation Algorithm
|
|
2
|
+
|
|
3
|
+
## 1. Overview
|
|
4
|
+
|
|
5
|
+
This algorithm infers type constraints for programs consisting of mutually recursive partial functions over algebraic data types (products and unions).
|
|
6
|
+
|
|
7
|
+
**Input:** AST of a `k` program, consisting of function definitions (expressions) - can be seen as a set of (recursive) equations.
|
|
8
|
+
**Output:** Annotated AST with type patterns (also called filters)
|
|
9
|
+
|
|
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.
|
|
11
|
+
|
|
12
|
+
## 2. Patterns
|
|
13
|
+
|
|
14
|
+
A **pattern** represents a set of types:
|
|
15
|
+
|
|
16
|
+
| Notation | Meaning | Fields |
|
|
17
|
+
|----------|---------|--------|
|
|
18
|
+
| `(...)` | Open unknown | Any |
|
|
19
|
+
| `{...}` | Open product | At least specified |
|
|
20
|
+
| `<...>` | Open union | At least specified |
|
|
21
|
+
| `{}` | Closed product | Exactly specified |
|
|
22
|
+
| `<>` | Closed union | Exactly specified |
|
|
23
|
+
| `T` | Named type | From type definition |
|
|
24
|
+
|
|
25
|
+
**Fields:** Each pattern has associated field labels. For open patterns, additional fields may exist. For closed patterns, the field set is exact.
|
|
26
|
+
Unknown-kind patterns have only the open form `(...)`; a closed filter must choose product `{...}`/`{}` or union `<...>`/`<>`.
|
|
27
|
+
|
|
28
|
+
A type pattern is a singleton pattern, i.e., only one type is captured by such a pattern.
|
|
29
|
+
|
|
30
|
+
## 3. Pattern Graph - a data structure used during type derivation
|
|
31
|
+
|
|
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
|
+
**Edges:** Labeled by field names, pointing to other patterns
|
|
34
|
+
**Representatives:** Root nodes (called reps) represent equivalence classes
|
|
35
|
+
|
|
36
|
+
**Operations:**
|
|
37
|
+
|
|
38
|
+
- `find(p)` - get representative of pattern `p`
|
|
39
|
+
- `unify(p₁, ..., pₙ)` - merge patterns into equivalence class, potentially adding a new rep (root); the rep gathers all edges of their children
|
|
40
|
+
- `clone(p₁, ..., pₙ)` - generate a copy of the pattern graph connected to the patterns
|
|
41
|
+
- `compact()` - discovers all singleton patterns and replaces them by "Named types"
|
|
42
|
+
|
|
43
|
+
## 4. Unification
|
|
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; the operation does not modify any existing pattern node
|
|
46
|
+
|
|
47
|
+
**Rules:**
|
|
48
|
+
|
|
49
|
+
- **Open + Open:** Merge field sets, stay open
|
|
50
|
+
- **Open + Closed:** Check fields ⊆, become closed
|
|
51
|
+
- **Closed + Closed:** Check fields =, stay closed
|
|
52
|
+
- **Product ⊥ Union:** Error (incompatible)
|
|
53
|
+
- **Type T:** Unique (canonical name)
|
|
54
|
+
|
|
55
|
+
**Algorithm:**
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
1. Find representatives of all patterns
|
|
59
|
+
2. If the set of reps is a singleton, return it as the result
|
|
60
|
+
3. Compute merged pattern descriptor
|
|
61
|
+
4. Create new representative node
|
|
62
|
+
5. Migrate edges from old representatives
|
|
63
|
+
6. Recursively unify edge destinations for each field name
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 5. Local Typing Rules
|
|
67
|
+
|
|
68
|
+
For each expression type, define input/output pattern constraints:
|
|
69
|
+
|
|
70
|
+
### Composition `(e₁ e₂ ... eₙ)`
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
in((e₁ e₂ ... eₙ)) = in(e₁)
|
|
74
|
+
out(eᵢ) = in(eᵢ₊₁) for i = 1..n-1
|
|
75
|
+
out((e₁ e₂ ... eₙ)) = out(eₙ)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
In case when `n=0`, identity, `()`
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
in(()) = out(())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Product `{e₁ l₁, ..., eₙ lₙ}`
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
in(e₁) = ... = in(eₙ) = in({e₁ l₁, ..., eₙ lₙ})
|
|
88
|
+
out({e₁ l₁, ..., eₙ lₙ}) = {} with edges lᵢ → out(eᵢ)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Variant constructor `|l`
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
out(|l) = <...> with edge l → in(|l)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Union `<e₁, ..., eₙ>`
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
in(e₁) = ... = in(eₙ) = in(<e₁, ..., eₙ>)
|
|
101
|
+
out(e₁) = ... = out(eₙ) = out(<e₁, ..., eₙ>)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Projection
|
|
105
|
+
|
|
106
|
+
#### Dot `.l`
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
in(.l) = {...} with edge l → out(.l)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### Div `/t`
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
in(/t) = <...> with edge t → out(/t)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Function reference `f`
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
in(f) = LOOKUP(in(f))
|
|
122
|
+
out(f) = LOOKUP(out(f))
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Once the type derivation for the defining expression for `f` is done, the input and output patterns are stored and will be used.
|
|
126
|
+
|
|
127
|
+
### Type `$ T`
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
in(T) = out(T) = pattern(T)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Filter `? F`
|
|
134
|
+
|
|
135
|
+
```text
|
|
136
|
+
see patterns.filterToPattern(F)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Filter expression is a syntax for describing pattern graphs.
|
|
140
|
+
|
|
141
|
+
## 6. Global Algorithm
|
|
142
|
+
|
|
143
|
+
### Phase 1: Initialization
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
For each function definition:
|
|
147
|
+
1. Create empty pattern graph
|
|
148
|
+
2. Traverse expression AST
|
|
149
|
+
3. Create [input, output] pattern pair per node of the AST
|
|
150
|
+
4. Apply local typing rules
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Phase 2: Dependency Analysis
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
1. Build dependency graph (function → referenced functions)
|
|
157
|
+
2. Compute strongly connected components (SCCs)
|
|
158
|
+
3. Topologically sort SCCs (bottom-up)
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Phase 3: Fixed-Point Iteration
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
For each SCC (in topological order):
|
|
165
|
+
Repeat until convergence (max 10 iterations):
|
|
166
|
+
For each function `f` in SCC:
|
|
167
|
+
1. Compact `f`'s pattern graph
|
|
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
|
+
|
|
170
|
+
If pattern graphs unchanged: break
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## 7. Compaction
|
|
174
|
+
|
|
175
|
+
**Purpose:** Replace all pattern nodes by their reps and replace all singleton patterns by types.
|
|
176
|
+
|
|
177
|
+
**Algorithm:**
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
1. Build a new graph only on reps (keep the mappings from pattern node to its rep)
|
|
181
|
+
2. Identify singleton patterns (closed patterns with no open ancestors)
|
|
182
|
+
3. Register singletons as named types and use them as type pattern nodes
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## 8. Convergence
|
|
186
|
+
|
|
187
|
+
**Criterion:** Pattern graph structure unchanged between iterations
|
|
188
|
+
|
|
189
|
+
**Measured by:**
|
|
190
|
+
1. Representative forest structure (parent pointers)
|
|
191
|
+
2. Edge sets for each representative
|
|
192
|
+
3. Pattern descriptors for each representative
|
|
193
|
+
|
|
194
|
+
**Algorithm:**
|
|
195
|
+
```text
|
|
196
|
+
For each function f in SCC:
|
|
197
|
+
1. Serialize reps forest: {nodeId → parentId}
|
|
198
|
+
2. Serialize edge structure: {repId → {label → [targetReps]}}
|
|
199
|
+
3. Serialize pattern info: {repId → {pattern, fields, type}}
|
|
200
|
+
4. Combine into canonical form
|
|
201
|
+
|
|
202
|
+
Converged = (current_state == previous_state)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Guarantee:** Monotonic refinement + iteration bound ensures termination
|
|
206
|
+
|
|
207
|
+
## 9. Error Handling
|
|
208
|
+
|
|
209
|
+
**Unification errors:**
|
|
210
|
+
|
|
211
|
+
- Product vs Union
|
|
212
|
+
- Closed pattern field mismatch
|
|
213
|
+
- Two distinct type patterns (types with different canonical names) are always incompatible
|
|
214
|
+
|
|
215
|
+
**Other errors:**
|
|
216
|
+
|
|
217
|
+
- Undefined function reference
|
|
218
|
+
- Iteration limit exceeded
|
|
219
|
+
|
|
220
|
+
**Error reporting should include:**
|
|
221
|
+
|
|
222
|
+
- Source location
|
|
223
|
+
- Expression type
|
|
224
|
+
- Conflicting patterns
|
|
225
|
+
- Unification trace
|
|
226
|
+
|
|
227
|
+
## 10. Implementation Notes
|
|
228
|
+
|
|
229
|
+
- Use reps forest for performing and tracing unification
|
|
230
|
+
- Compact only at the end of an iteration to keep unification reasons for error messages
|