@fraczak/k 5.1.2 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex +0 -0
- package/Code-derivation-tests/blowup.mjs +3 -3
- package/Code-derivation-tests/bnat.mjs +2 -2
- package/Code-derivation-tests/codec-witness-pattern.mjs +50 -0
- package/Code-derivation-tests/convergence-strategy.mjs +24 -0
- package/Code-derivation-tests/dot.mjs +12 -9
- package/Code-derivation-tests/filter-syntax.mjs +21 -0
- package/Code-derivation-tests/loop.mjs +8 -2
- package/Code-derivation-tests/nat.mjs +1 -1
- package/Code-derivation-tests/not-not.mjs +3 -3
- package/Code-derivation-tests/pattern-envelope.mjs +68 -0
- package/Code-derivation-tests/rlz.mjs +2 -2
- package/{BIG_PICTURE.md → DOCS/BIG_PICTURE.md} +103 -50
- package/DOCS/REPL.md +191 -0
- package/DOCS/a.html +398 -0
- package/DOCS/book/01-introduction.md +51 -0
- package/DOCS/book/02-syntax-and-values.md +111 -0
- package/DOCS/book/03-types-as-finite-automata.md +156 -0
- package/DOCS/book/04-partial-functions-and-composition.md +146 -0
- package/DOCS/book/05-typing-filters-and-normalization.md +111 -0
- package/DOCS/book-combined.html +3798 -0
- package/DOCS/book-combined.md +3745 -0
- package/DOCS/book-combined.pdf +0 -0
- package/DOCS/book-combined.tex +5682 -0
- package/DOCS/book.html +65 -0
- package/DOCS/book.md +54 -0
- package/DOCS/book.pdf +0 -0
- package/DOCS/book.tex +150 -0
- package/NAT.k~ +452 -0
- package/README.md +204 -508
- package/TypeGraph.mjs +43 -0
- package/TypePatternGraph.mjs +251 -0
- package/Value.mjs +192 -0
- package/augmentation.mjs +198 -0
- package/codecs/BINARY_FORMAT.html +471 -0
- package/codecs/BINARY_FORMAT.md +53 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.html +355 -0
- package/codecs/POLYMORPHIC_BINARY_FORMAT.md +303 -0
- package/codecs/README.md +215 -0
- package/codecs/example-pipeline.mjs +149 -0
- package/codecs/ieee.mjs +140 -0
- package/codecs/int.mjs +104 -0
- package/codecs/json-codec.mjs +215 -0
- package/codecs/json.mjs +40 -0
- package/codecs/k-parse.mjs +90 -0
- package/codecs/k-pattern.mjs +75 -0
- package/codecs/k-print.mjs +46 -0
- package/codecs/runtime/codec.mjs +768 -0
- package/codecs/runtime/ieee-pattern.mjs +30 -0
- package/codecs/runtime/pattern-json.mjs +96 -0
- package/codecs/runtime/prefix-codec.mjs +425 -0
- package/codecs/runtime/show-value.mjs +68 -0
- package/codecs/runtime/typeFromValue.mjs +34 -0
- package/codecs/runtime/unicode-string.mjs +220 -0
- package/codecs/show.mjs +41 -0
- package/codecs/string-codec.mjs +80 -0
- package/codecs/unit.mjs +51 -0
- package/codecs/utf16.mjs +66 -0
- package/codecs/utf8.mjs +37 -0
- package/codes.mjs +146 -44
- package/compiler.mjs +111 -0
- package/convergence.mjs +197 -0
- package/core.k +197 -0
- package/export.mjs +22 -32
- package/hash.mjs +29 -31
- package/ieee.klib +0 -0
- package/index.mjs +41 -15
- package/k-vscode/.vscode/launch.json +14 -0
- package/k-vscode/README.md +65 -0
- package/k-vscode/language-configuration.json +42 -0
- package/k-vscode/package-lock.json +4176 -0
- package/k-vscode/package.json +57 -0
- package/k-vscode/snippets/k.json +92 -0
- package/k-vscode/syntaxes/k.tmLanguage.json +243 -0
- package/k.mjs +56 -54
- package/object.mjs +517 -0
- package/objects/README.md +38 -0
- package/objects/compile-lib.mjs +42 -0
- package/objects/compile.mjs +42 -0
- package/objects/decompile.mjs +32 -0
- package/package.json +13 -6
- package/parser.jison +27 -46
- package/parser.mjs +68 -100
- package/pretty.mjs +21 -40
- package/repl2.mjs +662 -0
- package/run.mjs +264 -231
- package/symbol-table.mjs +3 -17
- package/test-fingerprint.mjs +7 -7
- package/test-hash-fuzz.mjs +107 -0
- package/test-hash-normalization.mjs +134 -0
- package/test-k-object.mjs +74 -0
- package/test-repl2.mjs +106 -0
- package/test.mjs +45 -70
- package/tests.sh +10 -1
- package/type_registry/registry.mjs~ +105 -0
- package/type_registry/typeParser.jison~ +132 -0
- package/typing.mjs +129 -676
- package/unification.mjs +157 -0
- package/valueIO.mjs +54 -0
- package/valueParser.jison +143 -0
- package/valueParser.mjs +713 -0
- package/Code-derivation-tests/built-in.mjs +0 -19
- package/Code-derivation-tests/vector.mjs +0 -19
- package/Filters/bug.k +0 -3
- package/Filters/composing.k +0 -15
- package/Filters/filter.k +0 -9
- package/TODO.md +0 -53
- package/TYPING.md +0 -365
- package/k-repl.cast +0 -1913
- package/k.cast +0 -618
- package/my-objects.json +0 -6
- package/patterns.mjs +0 -543
- package/repl.mjs +0 -213
- package/test-filters.mjs +0 -45
- package/typing-test.mjs +0 -40
package/codecs/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# K Codecs
|
|
2
|
+
|
|
3
|
+
This directory is being rewritten around a new codec design:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
abstract pattern graph + prefix-free tree encoding
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The default concrete format is now the binary encoding of a `$pattern` value,
|
|
10
|
+
using the `$pattern` type from [`../core.k`](../core.k), immediately followed by
|
|
11
|
+
the binary encoding of the value under that decoded pattern.
|
|
12
|
+
|
|
13
|
+
There is no separate JSON container format.
|
|
14
|
+
|
|
15
|
+
## Objectives
|
|
16
|
+
|
|
17
|
+
The rewrite is guided by two hard requirements:
|
|
18
|
+
|
|
19
|
+
1. compactness close to the structural lower bound,
|
|
20
|
+
2. efficient `k` projections, especially `.field` and `/tag`.
|
|
21
|
+
|
|
22
|
+
The current target architecture is the pattern graph plus prefix-free value-tree
|
|
23
|
+
stream described here and in
|
|
24
|
+
[`POLYMORPHIC_BINARY_FORMAT.md`](./POLYMORPHIC_BINARY_FORMAT.md).
|
|
25
|
+
|
|
26
|
+
## Core Idea
|
|
27
|
+
|
|
28
|
+
The semantic object serialized by the new codec is:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
(P, v)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
where:
|
|
35
|
+
|
|
36
|
+
- `P` is an abstract rooted pattern graph,
|
|
37
|
+
- `v` is a value tree compatible with `P`.
|
|
38
|
+
|
|
39
|
+
The same pair is now the in-memory runtime model as well. Decoding a stream
|
|
40
|
+
attaches `P` to the resulting `Value` as `value.pattern`; evaluation propagates
|
|
41
|
+
that pattern through projections and constructors; encoding a `Value` uses its
|
|
42
|
+
carried pattern unless an explicit pattern is supplied.
|
|
43
|
+
|
|
44
|
+
The base value encoding is:
|
|
45
|
+
|
|
46
|
+
- tree-based, not DAG-based,
|
|
47
|
+
- prefix-free,
|
|
48
|
+
- interpreted relative to the pattern graph,
|
|
49
|
+
- free of repeated field names and tag names in the value payload.
|
|
50
|
+
|
|
51
|
+
The base codec does **not** perform DAG compression. If sharing is desired, it
|
|
52
|
+
should happen as a higher-level transform, not inside the primitive value
|
|
53
|
+
format.
|
|
54
|
+
|
|
55
|
+
## Wire Format
|
|
56
|
+
|
|
57
|
+
The default transport format is:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
encode($pattern_value : $pattern) encode(value : decoded_pattern)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`$pattern_value` is a normal k value whose shape is defined in `core.k`. After
|
|
64
|
+
that value is decoded, it becomes the pattern used to decode the remaining
|
|
65
|
+
value payload.
|
|
66
|
+
|
|
67
|
+
The runtime boundary is therefore:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
binary pattern+value stream <-> Value(pattern, tree)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`k.mjs` is only the command-line adapter for that boundary. The operational
|
|
74
|
+
runtime sees and preserves the pattern on the `Value` itself.
|
|
75
|
+
|
|
76
|
+
## Pattern Graph Representation
|
|
77
|
+
|
|
78
|
+
For documentation and tests, a pattern graph may be shown as a property-list
|
|
79
|
+
style vector of nodes:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
[kind, edges]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
where:
|
|
86
|
+
|
|
87
|
+
- `kind` is one of:
|
|
88
|
+
- `"any"`
|
|
89
|
+
- `"open-product"`
|
|
90
|
+
- `"open-union"`
|
|
91
|
+
- `"closed-product"`
|
|
92
|
+
- `"closed-union"`
|
|
93
|
+
- `edges` is a list of:
|
|
94
|
+
- `[label, target_node_id]`
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
[
|
|
100
|
+
["closed-union", [["nil", 1], ["cons", 2]]],
|
|
101
|
+
["closed-product", []],
|
|
102
|
+
["closed-product", [["car", 3], ["cdr", 0]]],
|
|
103
|
+
["closed-union", [["_", 1], ["0", 3], ["1", 3]]]
|
|
104
|
+
]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Canonical rules:
|
|
108
|
+
|
|
109
|
+
- root node is node `0`,
|
|
110
|
+
- node list order is canonical graph-discovery order,
|
|
111
|
+
- edges are sorted by label,
|
|
112
|
+
- edge labels are unique within a node,
|
|
113
|
+
- `"any"` must have no outgoing edges.
|
|
114
|
+
|
|
115
|
+
This JSON-like graph is only a readable notation. The wire representation is
|
|
116
|
+
the ordinary k `$pattern` value from `core.k`.
|
|
117
|
+
|
|
118
|
+
## Witness-Derived Patterns
|
|
119
|
+
|
|
120
|
+
When `k-parse` is used without an explicit input pattern or type, the wire
|
|
121
|
+
pattern is derived from the parsed value tree.
|
|
122
|
+
|
|
123
|
+
- An empty textual node is a closed product: `{}`.
|
|
124
|
+
- A textual node with multiple children is a closed product.
|
|
125
|
+
- A textual node with one child is interpreted as an open union by default.
|
|
126
|
+
- An explicit product input pattern may force such a node to be treated as a
|
|
127
|
+
singleton product instead.
|
|
128
|
+
|
|
129
|
+
For example, parsing:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
{a:{b:x,c:{}}}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
without an input pattern derives:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
[
|
|
139
|
+
["open-union", [["a", 1]]],
|
|
140
|
+
["closed-product", [["b", 2], ["c", 3]]],
|
|
141
|
+
["open-union", [["x", 3]]],
|
|
142
|
+
["closed-product", []]
|
|
143
|
+
]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
During this construction, finite closed pattern subtrees are hash-consed from
|
|
147
|
+
the leaves upward. The starting closed leaf is `["closed-product", []]`. Two
|
|
148
|
+
closed nodes collapse only when they have the same closed kind, the same labels,
|
|
149
|
+
and the same already-collapsed child targets. Open nodes keep their identity,
|
|
150
|
+
and recursive closed nodes are not collapsed by this witness-tree rule.
|
|
151
|
+
|
|
152
|
+
This is canonicalization of the pattern graph carried in the wire stream. It is
|
|
153
|
+
not DAG compression of the value payload.
|
|
154
|
+
|
|
155
|
+
## Prefix-Free Value Encoding
|
|
156
|
+
|
|
157
|
+
The value payload is a bitstream interpreted relative to the pattern graph.
|
|
158
|
+
|
|
159
|
+
Base rules:
|
|
160
|
+
|
|
161
|
+
- closed or open product:
|
|
162
|
+
- encode child values in canonical edge order,
|
|
163
|
+
- closed or open union:
|
|
164
|
+
- encode the selected tag position,
|
|
165
|
+
- then encode the selected payload,
|
|
166
|
+
- `any`:
|
|
167
|
+
- not directly value-carrying in the base format.
|
|
168
|
+
|
|
169
|
+
The initial concrete union encoding rule is:
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
width = ceil(log2(cardinality))
|
|
173
|
+
emit tag ordinal in width bits
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
This is the simplest canonical prefix-free choice encoding. It is not the final
|
|
177
|
+
word on compression, but it gives a regular and compact base encoding that can
|
|
178
|
+
later be refined without changing the abstract pattern/value model.
|
|
179
|
+
|
|
180
|
+
## Projections
|
|
181
|
+
|
|
182
|
+
The base tree encoding is chosen so that:
|
|
183
|
+
|
|
184
|
+
- `/tag` only needs to read the union choice at the current node,
|
|
185
|
+
- `.field` follows canonical product order and may later be accelerated by
|
|
186
|
+
higher-level indexes if necessary.
|
|
187
|
+
|
|
188
|
+
In the materialized runtime, projections also project the carried pattern:
|
|
189
|
+
`.field` returns the subpattern at that product field, and `/tag` returns the
|
|
190
|
+
subpattern at that union tag. If no carried subpattern is available, later
|
|
191
|
+
encoding can still derive a witness pattern from the result tree.
|
|
192
|
+
|
|
193
|
+
The primitive codec stays minimal. Projection indexes, framing, or sharing
|
|
194
|
+
schemes are explicitly separate concerns.
|
|
195
|
+
|
|
196
|
+
## Design Boundary
|
|
197
|
+
|
|
198
|
+
The new codec work is split into layers:
|
|
199
|
+
|
|
200
|
+
1. abstract pattern graph semantics,
|
|
201
|
+
2. prefix-free value-tree semantics relative to that graph,
|
|
202
|
+
3. self-hosted `k` representation of the pattern graph,
|
|
203
|
+
4. optional higher-level optimizations.
|
|
204
|
+
|
|
205
|
+
## Files
|
|
206
|
+
|
|
207
|
+
- [`POLYMORPHIC_BINARY_FORMAT.md`](./POLYMORPHIC_BINARY_FORMAT.md):
|
|
208
|
+
main design document for the new pattern-plus-prefix-tree codec.
|
|
209
|
+
- [`BINARY_FORMAT.md`](./BINARY_FORMAT.md):
|
|
210
|
+
notes on future binary packaging of the same abstract semantics.
|
|
211
|
+
|
|
212
|
+
## Status
|
|
213
|
+
|
|
214
|
+
The active command-line pipeline emits and consumes the self-hosted binary
|
|
215
|
+
pattern+value stream.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Example: Demonstrating the codec pipeline
|
|
4
|
+
*
|
|
5
|
+
* Shows how to:
|
|
6
|
+
* 1. Define types with canonical names
|
|
7
|
+
* 2. Create values
|
|
8
|
+
* 3. Encode to binary
|
|
9
|
+
* 4. Pass through "compiled program" (identity)
|
|
10
|
+
* 5. Decode from binary
|
|
11
|
+
* 6. Print result
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { deriveClosedPattern } from './runtime/codec.mjs';
|
|
15
|
+
import { encodeToWire, decodeWire } from './runtime/prefix-codec.mjs';
|
|
16
|
+
import { patternToPropertyList } from './runtime/pattern-json.mjs';
|
|
17
|
+
import { Product, Variant } from '../Value.mjs';
|
|
18
|
+
import hash from '../hash.mjs';
|
|
19
|
+
|
|
20
|
+
console.log('=== K Codec Pipeline Example ===\n');
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Step 1: Define types (normally from compiler)
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
const types = {};
|
|
27
|
+
|
|
28
|
+
// Unit: {}
|
|
29
|
+
const unitDef = { code: 'product', product: {} };
|
|
30
|
+
const unitName = hash('$C0={};');
|
|
31
|
+
types[unitName] = unitDef;
|
|
32
|
+
|
|
33
|
+
// Bool: <{} false, {} true>
|
|
34
|
+
const boolDef = {
|
|
35
|
+
code: 'union',
|
|
36
|
+
union: { false: unitName, true: unitName }
|
|
37
|
+
};
|
|
38
|
+
const boolName = hash(`$C0=<${unitName}"false",${unitName}"true">;`);
|
|
39
|
+
types[boolName] = boolDef;
|
|
40
|
+
|
|
41
|
+
// Maybe: <{} nothing, Bool something>
|
|
42
|
+
const maybeDef = {
|
|
43
|
+
code: 'union',
|
|
44
|
+
union: { nothing: unitName, something: boolName }
|
|
45
|
+
};
|
|
46
|
+
const maybeName = hash(
|
|
47
|
+
`$C0=<${unitName}"nothing",${boolName}"something">;`
|
|
48
|
+
);
|
|
49
|
+
types[maybeName] = maybeDef;
|
|
50
|
+
|
|
51
|
+
console.log('Types defined:');
|
|
52
|
+
console.log(' unit:', unitName);
|
|
53
|
+
console.log(' bool:', boolName);
|
|
54
|
+
console.log(' maybe:', maybeName);
|
|
55
|
+
console.log();
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Step 2: Type resolver
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
function resolveType(typeName) {
|
|
62
|
+
const def = types[typeName];
|
|
63
|
+
if (!def) {
|
|
64
|
+
throw new Error(`Unknown type: ${typeName}`);
|
|
65
|
+
}
|
|
66
|
+
return def;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Step 3: Create input value: Maybe Bool = something(true)
|
|
71
|
+
// ============================================================================
|
|
72
|
+
|
|
73
|
+
const inputValue = new Variant(
|
|
74
|
+
'something',
|
|
75
|
+
new Variant('true', new Product({}))
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
console.log('Input value:', inputValue.toString());
|
|
79
|
+
console.log();
|
|
80
|
+
|
|
81
|
+
const maybePattern = patternToPropertyList(
|
|
82
|
+
deriveClosedPattern(maybeName, maybeDef, resolveType)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Step 4: Parse (TEXT → BINARY)
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
console.log('--- PARSE (text → binary) ---');
|
|
90
|
+
const binaryInput = encodeToWire(inputValue, maybePattern);
|
|
91
|
+
console.log('Binary size:', binaryInput.length, 'bytes');
|
|
92
|
+
console.log(' Bytes:', binaryInput.toString('hex'));
|
|
93
|
+
console.log();
|
|
94
|
+
|
|
95
|
+
// ============================================================================
|
|
96
|
+
// Step 5: Compiled k-program (BINARY → BINARY)
|
|
97
|
+
// ============================================================================
|
|
98
|
+
|
|
99
|
+
console.log('--- COMPILED PROGRAM (binary → binary) ---');
|
|
100
|
+
|
|
101
|
+
// Simulate a compiled k-program: decode → transform → encode
|
|
102
|
+
const { value: programInput } = decodeWire(binaryInput);
|
|
103
|
+
|
|
104
|
+
console.log('Program sees value:', programInput.toString());
|
|
105
|
+
|
|
106
|
+
// Example transformation: NOT operation (flip bool inside Maybe)
|
|
107
|
+
let programOutput;
|
|
108
|
+
if (programInput instanceof Variant && programInput.tag === 'something') {
|
|
109
|
+
const innerBool = programInput.value;
|
|
110
|
+
if (innerBool instanceof Variant) {
|
|
111
|
+
const flipped = new Variant(
|
|
112
|
+
innerBool.tag === 'true' ? 'false' : 'true',
|
|
113
|
+
innerBool.value
|
|
114
|
+
);
|
|
115
|
+
programOutput = new Variant('something', flipped);
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
programOutput = programInput; // nothing → nothing
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log('Program output value:', programOutput.toString());
|
|
122
|
+
|
|
123
|
+
// Encode output
|
|
124
|
+
const binaryOutput = encodeToWire(programOutput, maybePattern);
|
|
125
|
+
|
|
126
|
+
console.log('Binary output size:', binaryOutput.length, 'bytes');
|
|
127
|
+
console.log(' Bytes:', binaryOutput.toString('hex'));
|
|
128
|
+
console.log();
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Step 6: Print (BINARY → TEXT)
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
console.log('--- PRINT (binary → text) ---');
|
|
135
|
+
const { value: finalValue } = decodeWire(binaryOutput);
|
|
136
|
+
|
|
137
|
+
console.log('Output value:', finalValue.toString());
|
|
138
|
+
console.log();
|
|
139
|
+
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// Summary
|
|
142
|
+
// ============================================================================
|
|
143
|
+
|
|
144
|
+
console.log('=== Pipeline Summary ===');
|
|
145
|
+
console.log('Input: ', inputValue.toString(), '(true inside Maybe)');
|
|
146
|
+
console.log('Transform: NOT operation on inner bool');
|
|
147
|
+
console.log('Output: ', finalValue.toString(), '(false inside Maybe)');
|
|
148
|
+
console.log();
|
|
149
|
+
console.log('Binary representations are canonical and type-safe!');
|
package/codecs/ieee.mjs
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { stdin, stdout, argv, exit } from "node:process";
|
|
4
|
+
import { Product, Variant } from "../Value.mjs";
|
|
5
|
+
import { decodeWire, encodeToWire } from "./runtime/prefix-codec.mjs";
|
|
6
|
+
import { FLOAT64_PATTERN } from "./runtime/ieee-pattern.mjs";
|
|
7
|
+
const UNIT = new Product({});
|
|
8
|
+
|
|
9
|
+
function bitValue(bit) {
|
|
10
|
+
return new Variant(bit === 0 ? "0" : "1", UNIT);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function bitsProduct(width, value) {
|
|
14
|
+
const big = BigInt(value);
|
|
15
|
+
const product = {};
|
|
16
|
+
for (let i = width - 1; i >= 0; i--) {
|
|
17
|
+
product[String(i)] = bitValue(Number((big >> BigInt(i)) & 1n));
|
|
18
|
+
}
|
|
19
|
+
return new Product(product);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function encodeNumberToValue(number) {
|
|
23
|
+
const buf = Buffer.alloc(8);
|
|
24
|
+
buf.writeDoubleBE(number, 0);
|
|
25
|
+
const bits = buf.readBigUInt64BE(0);
|
|
26
|
+
const sign = Number((bits >> 63n) & 1n);
|
|
27
|
+
const exponent = Number((bits >> 52n) & 0x7ffn);
|
|
28
|
+
const fraction = bits & ((1n << 52n) - 1n);
|
|
29
|
+
|
|
30
|
+
return new Product({
|
|
31
|
+
sign: new Variant(sign === 0 ? "+" : "-", UNIT),
|
|
32
|
+
exponent: bitsProduct(11, exponent),
|
|
33
|
+
fraction: bitsProduct(52, fraction)
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function requireProduct(value, where) {
|
|
38
|
+
if (!(value instanceof Product)) {
|
|
39
|
+
throw new Error(`${where}: expected Product`);
|
|
40
|
+
}
|
|
41
|
+
return value.product;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function requireVariant(value, where) {
|
|
45
|
+
if (!(value instanceof Variant)) {
|
|
46
|
+
throw new Error(`${where}: expected Variant`);
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseBitsProduct(value, maxBit, where) {
|
|
52
|
+
const product = requireProduct(value, where);
|
|
53
|
+
let n = 0n;
|
|
54
|
+
for (let i = maxBit; i >= 0; i--) {
|
|
55
|
+
const entry = requireVariant(product[String(i)], `${where}.bit${i}`);
|
|
56
|
+
if (entry.tag !== "0" && entry.tag !== "1") {
|
|
57
|
+
throw new Error(`${where}.bit${i}: expected tag 0 or 1`);
|
|
58
|
+
}
|
|
59
|
+
n = (n << 1n) | BigInt(entry.tag === "1" ? 1 : 0);
|
|
60
|
+
}
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function decodeValueToNumber(value) {
|
|
65
|
+
const product = requireProduct(value, "float64");
|
|
66
|
+
const sign = requireVariant(product.sign, "float64.sign");
|
|
67
|
+
if (sign.tag !== "+" && sign.tag !== "-") {
|
|
68
|
+
throw new Error(`float64.sign: expected + or -, got ${sign.tag}`);
|
|
69
|
+
}
|
|
70
|
+
const exponent = parseBitsProduct(product.exponent, 10, "float64.exponent");
|
|
71
|
+
const fraction = parseBitsProduct(product.fraction, 51, "float64.fraction");
|
|
72
|
+
|
|
73
|
+
const bits =
|
|
74
|
+
(BigInt(sign.tag === "-" ? 1 : 0) << 63n) |
|
|
75
|
+
(exponent << 52n) |
|
|
76
|
+
fraction;
|
|
77
|
+
|
|
78
|
+
const buf = Buffer.alloc(8);
|
|
79
|
+
buf.writeBigUInt64BE(bits, 0);
|
|
80
|
+
return buf.readDoubleBE(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function parseFloatText(text) {
|
|
84
|
+
const trimmed = text.trim();
|
|
85
|
+
if (trimmed === "") {
|
|
86
|
+
throw new Error("Expected a floating-point literal");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (/^[+-]?nan$/i.test(trimmed)) return NaN;
|
|
90
|
+
if (/^[+]?inf(inity)?$/i.test(trimmed)) return Infinity;
|
|
91
|
+
if (/^-inf(inity)?$/i.test(trimmed)) return -Infinity;
|
|
92
|
+
|
|
93
|
+
const value = Number(trimmed);
|
|
94
|
+
if (Number.isNaN(value)) {
|
|
95
|
+
throw new Error(`Invalid floating-point literal: ${JSON.stringify(trimmed)}`);
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function printFloatText(number) {
|
|
101
|
+
if (Number.isNaN(number)) return "NaN";
|
|
102
|
+
if (number === Infinity) return "Infinity";
|
|
103
|
+
if (number === -Infinity) return "-Infinity";
|
|
104
|
+
if (Object.is(number, -0)) return "-0";
|
|
105
|
+
return String(number);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function readAll(stream) {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
const chunks = [];
|
|
111
|
+
stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
112
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
113
|
+
stream.on("error", reject);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function main() {
|
|
118
|
+
const args = argv.slice(2);
|
|
119
|
+
if (args.length !== 1 || (args[0] !== "--parse" && args[0] !== "--print")) {
|
|
120
|
+
console.error("Usage: ieee.mjs --parse | --print");
|
|
121
|
+
console.error(" --parse read a float literal from stdin, write binary pattern+value stream");
|
|
122
|
+
console.error(" --print read binary pattern+value stream from stdin, write a float literal");
|
|
123
|
+
exit(1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const buf = await readAll(stdin);
|
|
127
|
+
|
|
128
|
+
if (args[0] === "--parse") {
|
|
129
|
+
const value = encodeNumberToValue(parseFloatText(buf.toString("utf8")));
|
|
130
|
+
stdout.write(encodeToWire(value, FLOAT64_PATTERN));
|
|
131
|
+
} else {
|
|
132
|
+
const { value } = decodeWire(buf);
|
|
133
|
+
stdout.write(`${printFloatText(decodeValueToNumber(value))}\n`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
main().catch((error) => {
|
|
138
|
+
console.error(error.message || String(error));
|
|
139
|
+
exit(1);
|
|
140
|
+
});
|
package/codecs/int.mjs
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* int: parse/print integers in the k 'int' binary pattern+value stream.
|
|
5
|
+
*
|
|
6
|
+
* $ bits = < {} _, bits 0, bits 1 >;
|
|
7
|
+
* $ int = < bits '+', bits '-' >;
|
|
8
|
+
*
|
|
9
|
+
* Bits are stored MSB-outermost (remove_leading_zeros strips outermost 0s).
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* echo "-21" | int.mjs --parse # decimal → binary pattern+value stream
|
|
13
|
+
* <wire> | int.mjs --print # binary pattern+value stream → decimal
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { stdin, stdout, argv, exit } from "node:process";
|
|
17
|
+
import { Product, Variant } from "../Value.mjs";
|
|
18
|
+
import { decodeWire, encodeToWire } from "./runtime/prefix-codec.mjs";
|
|
19
|
+
|
|
20
|
+
// Closed pattern for $ int = < bits '+', bits '-' >
|
|
21
|
+
// with $ bits = < {} _, bits 0, bits 1 >
|
|
22
|
+
const INT_PATTERN = [
|
|
23
|
+
["closed-union", [["+", 1], ["-", 1]]],
|
|
24
|
+
["closed-union", [["0", 1], ["1", 1], ["_", 2]]],
|
|
25
|
+
["closed-product", []]
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function readAll(stream) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
const chunks = [];
|
|
31
|
+
stream.on("data", c => chunks.push(Buffer.isBuffer(c) ? c : Buffer.from(c)));
|
|
32
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
33
|
+
stream.on("error", reject);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Build bits Value (MSB outermost) from a non-negative BigInt.
|
|
38
|
+
function buildBits(n) {
|
|
39
|
+
const bitChars = n === 0n ? ["0"] : n.toString(2).split("");
|
|
40
|
+
let v = new Variant("_", new Product({}));
|
|
41
|
+
for (let i = bitChars.length - 1; i >= 0; i--) {
|
|
42
|
+
v = new Variant(bitChars[i], v);
|
|
43
|
+
}
|
|
44
|
+
return v;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse a decimal integer string ([-+]?[ ]*[1-9][0-9]*|0) into a k int Value.
|
|
48
|
+
function parseIntStr(str) {
|
|
49
|
+
str = str.trim();
|
|
50
|
+
let sign = "+";
|
|
51
|
+
if (str[0] === "-") { sign = "-"; str = str.slice(1).replace(/\s+/g, ""); }
|
|
52
|
+
else if (str[0] === "+") { str = str.slice(1).replace(/\s+/g, ""); }
|
|
53
|
+
if (!/^(0|[1-9][0-9]*)$/.test(str)) {
|
|
54
|
+
throw new Error(`Invalid integer syntax: ${JSON.stringify(str)}`);
|
|
55
|
+
}
|
|
56
|
+
const n = BigInt(str);
|
|
57
|
+
if (n === 0n) sign = "+"; // zero is always '+'
|
|
58
|
+
return new Variant(sign, buildBits(n));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Walk a decoded int Value (Variant sign → bits) and return a decimal string.
|
|
62
|
+
function printIntValue(value) {
|
|
63
|
+
if (!(value instanceof Variant) || (value.tag !== "+" && value.tag !== "-")) {
|
|
64
|
+
throw new Error("Not a valid k int value");
|
|
65
|
+
}
|
|
66
|
+
const sign = value.tag;
|
|
67
|
+
let bits = "";
|
|
68
|
+
let node = value.value;
|
|
69
|
+
while (node instanceof Variant && node.tag !== "_") {
|
|
70
|
+
bits += node.tag;
|
|
71
|
+
node = node.value;
|
|
72
|
+
}
|
|
73
|
+
// bits is MSB-first binary string (may be empty for zero represented as "0")
|
|
74
|
+
const n = bits === "" ? 0n : BigInt("0b" + bits);
|
|
75
|
+
const digits = n.toString(10);
|
|
76
|
+
if (n === 0n) return "0";
|
|
77
|
+
return (sign === "-" ? "-" : "") + digits;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function main() {
|
|
81
|
+
const args = argv.slice(2);
|
|
82
|
+
if (args.length !== 1 || (args[0] !== "--parse" && args[0] !== "--print")) {
|
|
83
|
+
console.error("Usage: int.mjs --parse | --print");
|
|
84
|
+
console.error(" --parse read a decimal integer from stdin, write binary pattern+value stream");
|
|
85
|
+
console.error(" --print read binary pattern+value stream from stdin, write decimal integer");
|
|
86
|
+
exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const buf = await readAll(stdin);
|
|
90
|
+
|
|
91
|
+
if (args[0] === "--parse") {
|
|
92
|
+
const text = buf.toString("utf8").trim();
|
|
93
|
+
const value = parseIntStr(text);
|
|
94
|
+
stdout.write(encodeToWire(value, INT_PATTERN));
|
|
95
|
+
} else {
|
|
96
|
+
const { value } = decodeWire(buf);
|
|
97
|
+
stdout.write(printIntValue(value) + "\n");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main().catch(err => {
|
|
102
|
+
console.error(err.message || String(err));
|
|
103
|
+
exit(1);
|
|
104
|
+
});
|