@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,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Test the binary codec with basic k types
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { deriveClosedPattern } from './codec.mjs';
|
|
7
|
+
import { encodeToWire, decodeWire } from './prefix-codec.mjs';
|
|
8
|
+
import { patternToPropertyList } from './pattern-json.mjs';
|
|
9
|
+
import { Product, Variant } from '../../Value.mjs';
|
|
10
|
+
import hash from '../../hash.mjs';
|
|
11
|
+
|
|
12
|
+
// Type repository
|
|
13
|
+
const types = {};
|
|
14
|
+
|
|
15
|
+
// Define unit type: {}
|
|
16
|
+
const unitTypeDef = { code: 'product', product: {} };
|
|
17
|
+
const unitTypeStr = '$C0={};';
|
|
18
|
+
const unitTypeName = hash(unitTypeStr);
|
|
19
|
+
types[unitTypeName] = unitTypeDef;
|
|
20
|
+
|
|
21
|
+
console.log('Unit type name:', unitTypeName);
|
|
22
|
+
|
|
23
|
+
// Define bool type: <{} false, {} true>
|
|
24
|
+
const boolTypeDef = {
|
|
25
|
+
code: 'union',
|
|
26
|
+
union: {
|
|
27
|
+
false: unitTypeName,
|
|
28
|
+
true: unitTypeName
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const boolTypeStr = `$C0=<${unitTypeName}"false",${unitTypeName}"true">;`;
|
|
32
|
+
const boolTypeName = hash(boolTypeStr);
|
|
33
|
+
types[boolTypeName] = boolTypeDef;
|
|
34
|
+
|
|
35
|
+
console.log('Bool type name:', boolTypeName);
|
|
36
|
+
|
|
37
|
+
// Resolver function
|
|
38
|
+
function resolveType(typeName) {
|
|
39
|
+
if (!types[typeName]) {
|
|
40
|
+
throw new Error(`Unknown type: ${typeName}`);
|
|
41
|
+
}
|
|
42
|
+
return types[typeName];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Test 1: Unit value
|
|
46
|
+
console.log('\n=== Test 1: Unit ===');
|
|
47
|
+
const unitValue = new Product({});
|
|
48
|
+
console.log('Encoding unit:', unitValue.toString());
|
|
49
|
+
|
|
50
|
+
const unitPattern = patternToPropertyList(deriveClosedPattern(unitTypeName, unitTypeDef, resolveType));
|
|
51
|
+
const unitEncoded = encodeToWire(unitValue, unitPattern);
|
|
52
|
+
console.log('Encoded bytes:', unitEncoded.length, 'bytes');
|
|
53
|
+
console.log('Hex:', unitEncoded.toString('hex'));
|
|
54
|
+
|
|
55
|
+
const unitDecoded = decodeWire(unitEncoded);
|
|
56
|
+
console.log('Decoded value:', unitDecoded.value.toString());
|
|
57
|
+
console.log('Match:', unitDecoded.value instanceof Product &&
|
|
58
|
+
Object.keys(unitDecoded.value.product).length === 0);
|
|
59
|
+
|
|
60
|
+
// Test 2: Bool false
|
|
61
|
+
console.log('\n=== Test 2: Bool (false) ===');
|
|
62
|
+
const falseValue = new Variant('false', new Product({}));
|
|
63
|
+
console.log('Encoding false:', falseValue.toString());
|
|
64
|
+
|
|
65
|
+
const boolPattern = patternToPropertyList(deriveClosedPattern(boolTypeName, boolTypeDef, resolveType));
|
|
66
|
+
const falseEncoded = encodeToWire(falseValue, boolPattern);
|
|
67
|
+
console.log('Encoded bytes:', falseEncoded.length, 'bytes');
|
|
68
|
+
console.log('Hex:', falseEncoded.toString('hex'));
|
|
69
|
+
|
|
70
|
+
const falseDecoded = decodeWire(falseEncoded);
|
|
71
|
+
console.log('Decoded value:', falseDecoded.value.toString());
|
|
72
|
+
console.log('Match:', falseDecoded.value instanceof Variant &&
|
|
73
|
+
falseDecoded.value.tag === 'false');
|
|
74
|
+
|
|
75
|
+
// Test 3: Bool true
|
|
76
|
+
console.log('\n=== Test 3: Bool (true) ===');
|
|
77
|
+
const trueValue = new Variant('true', new Product({}));
|
|
78
|
+
console.log('Encoding true:', trueValue.toString());
|
|
79
|
+
|
|
80
|
+
const trueEncoded = encodeToWire(trueValue, boolPattern);
|
|
81
|
+
console.log('Encoded bytes:', trueEncoded.length, 'bytes');
|
|
82
|
+
console.log('Hex:', trueEncoded.toString('hex'));
|
|
83
|
+
|
|
84
|
+
const trueDecoded = decodeWire(trueEncoded);
|
|
85
|
+
console.log('Decoded value:', trueDecoded.value.toString());
|
|
86
|
+
console.log('Match:', trueDecoded.value instanceof Variant &&
|
|
87
|
+
trueDecoded.value.tag === 'true');
|
|
88
|
+
|
|
89
|
+
console.log('\n=== All tests complete ===');
|
package/core.klib
ADDED
|
Binary file
|
package/core.klib~
ADDED
|
Binary file
|
package/ieee.klib
CHANGED
|
Binary file
|
package/kkk.k
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
-- core.k — the k standard core library
|
|
2
|
+
--
|
|
3
|
+
-- Defines:
|
|
4
|
+
-- 1. $bits — the canonical binary number / bit-path type
|
|
5
|
+
-- 2. $string — unicode strings (used for labels)
|
|
6
|
+
-- 3. $pattern — the type of pattern graphs
|
|
7
|
+
-- 4. Basic $bits arithmetic: succ, plus, times
|
|
8
|
+
|
|
9
|
+
---------------------------------------------------------------
|
|
10
|
+
-- §1. Bits
|
|
11
|
+
---------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
$ bit = < {} 0, {} 1 >;
|
|
14
|
+
b0 = {}|0 $bit;
|
|
15
|
+
b1 = {}|1 $bit;
|
|
16
|
+
|
|
17
|
+
$ bits = < {} _, bits 0, bits 1 >;
|
|
18
|
+
|
|
19
|
+
_ = {}|_;
|
|
20
|
+
|
|
21
|
+
0 = _ |0;
|
|
22
|
+
1 = _ |1;
|
|
23
|
+
|
|
24
|
+
inv = {() x, _ r} _inv;
|
|
25
|
+
_inv = ${bits x, bits r} < {.x /0 x, .r |0 r} _inv, {.x /1 x, .r |1 r} _inv, .r >;
|
|
26
|
+
|
|
27
|
+
concat = ${bits x, bits y} {.x inv x, .y y} concat_;
|
|
28
|
+
concat_ = ${bits x, bits y}
|
|
29
|
+
<
|
|
30
|
+
{.x /0 x, .y |0 y} concat_,
|
|
31
|
+
{.x /1 x, .y |1 y} concat_,
|
|
32
|
+
.y
|
|
33
|
+
>;
|
|
34
|
+
|
|
35
|
+
---------------------------------------------------------------
|
|
36
|
+
-- §2. Bits arithmetic
|
|
37
|
+
---------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
-- successor (increment by 1)
|
|
40
|
+
succ = {inv x, _ y} _inc;
|
|
41
|
+
|
|
42
|
+
_inc = ${bits x, bits y} <
|
|
43
|
+
{.x /0 x, .y |1 y} concat_,
|
|
44
|
+
{.x /1 x, .y |0 y} _inc,
|
|
45
|
+
.y |1
|
|
46
|
+
>;
|
|
47
|
+
|
|
48
|
+
-- constants
|
|
49
|
+
2 = 1 succ;
|
|
50
|
+
3 = 2 succ;
|
|
51
|
+
4 = 3 succ;
|
|
52
|
+
5 = 4 succ;
|
|
53
|
+
6 = 5 succ;
|
|
54
|
+
7 = 6 succ;
|
|
55
|
+
8 = 7 succ;
|
|
56
|
+
9 = 8 succ;
|
|
57
|
+
10 = 9 succ;
|
|
58
|
+
|
|
59
|
+
-- addition on $bits (unsigned, LSB-first with carry)
|
|
60
|
+
plus = ${bits x, bits y} {.x inv x, .y inv y, b0 c} _plus;
|
|
61
|
+
_plus = ${bits x, bits y, bit c}
|
|
62
|
+
<
|
|
63
|
+
{.x /_ if, .y /_ and, <.c/0 0, 1> then} .then,
|
|
64
|
+
{.x /_ if, .c/0 and, .y then} .then,
|
|
65
|
+
{.x y, .y /_|_ x, .c c} _plus,
|
|
66
|
+
{<.c/0 0, 1> x, .y y, .x /_ b0 c} _plus,
|
|
67
|
+
{.x /0 x, .y /0 y, .c/0 b0 c} _plus |0,
|
|
68
|
+
{.x /0 x, .y /0 y, .c/1 b0 c} _plus |1,
|
|
69
|
+
{.x /0 x, .y /1 y, .c/0 b0 c} _plus |1,
|
|
70
|
+
{.x /0 x, .y /1 y, .c/1 b1 c} _plus |0,
|
|
71
|
+
{.x /1 x, .y /0 y, .c/0 b0 c} _plus |1,
|
|
72
|
+
{.x /1 x, .y /0 y, .c/1 b1 c} _plus |0,
|
|
73
|
+
{.x /1 x, .y /1 y, .c/0 b1 c} _plus |0,
|
|
74
|
+
{.x /1 x, .y /1 y, .c/1 b1 c} _plus |1
|
|
75
|
+
>;
|
|
76
|
+
|
|
77
|
+
-- multiplication on $bits (unsigned, shift-and-add)
|
|
78
|
+
times = ${bits x, bits y} {0 result, .x inv _x, .y y} _times;
|
|
79
|
+
_times = ${bits result, bits _x, bits y}
|
|
80
|
+
<
|
|
81
|
+
{ .y /_ if, .result then } .then,
|
|
82
|
+
{
|
|
83
|
+
.result |0 result,
|
|
84
|
+
._x _x,
|
|
85
|
+
.y /0 y
|
|
86
|
+
} _times,
|
|
87
|
+
{
|
|
88
|
+
{._x x, .result |0 y, b0 c} _plus result,
|
|
89
|
+
._x _x,
|
|
90
|
+
.y /1 y
|
|
91
|
+
} _times
|
|
92
|
+
>;
|
|
93
|
+
|
|
94
|
+
---------------------------------------------------------------
|
|
95
|
+
-- §3. Unicode and String
|
|
96
|
+
---------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
-- 8-bit octet used as a building block for UTF-8 payload pieces.
|
|
99
|
+
$ byte = {
|
|
100
|
+
bit 0, bit 1, bit 2, bit 3,
|
|
101
|
+
bit 4, bit 5, bit 6, bit 7
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
-- 7-bit ASCII code point payload (U+0000..U+007F).
|
|
105
|
+
$ ascii = { bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 };
|
|
106
|
+
|
|
107
|
+
-- 11-bit payload used for Plane 0 code points U+0080..U+07FF.
|
|
108
|
+
-- Plane 0 is the Basic Multilingual Plane (BMP): U+0000..U+FFFF.
|
|
109
|
+
$ u0080_07FF = { bit 10, bit 9, bit 8, bit 7, bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 };
|
|
110
|
+
|
|
111
|
+
-- High-byte classes for common BMP ranges, excluding surrogate/private-use partitions.
|
|
112
|
+
-- BMP (Basic Multilingual Plane) is Plane 0: the first 65,536 code points.
|
|
113
|
+
$ bmp_hi_common = <
|
|
114
|
+
{ bit 2, bit 1, bit 0 } h08_0F,
|
|
115
|
+
{ bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 } h10_7F,
|
|
116
|
+
{ bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 } h80_CF,
|
|
117
|
+
{ bit 2, bit 1, bit 0 } hD0_D7,
|
|
118
|
+
{} hF9,
|
|
119
|
+
{ bit 0 } hFA_FB,
|
|
120
|
+
{ bit 0 } hFC_FD,
|
|
121
|
+
{ bit 0 } hFE_FF
|
|
122
|
+
>;
|
|
123
|
+
|
|
124
|
+
-- High-byte classes for BMP private-use/high-reserved slices.
|
|
125
|
+
-- These partitions keep Plane 0 subsets explicit in the type encoding.
|
|
126
|
+
$ bmp_hi_private_use = <
|
|
127
|
+
{ bit 3, bit 2, bit 1, bit 0 } hE0_EF,
|
|
128
|
+
{ bit 2, bit 1, bit 0 } hF0_F7,
|
|
129
|
+
{} hF8
|
|
130
|
+
>;
|
|
131
|
+
|
|
132
|
+
-- Plane selector for supplementary code points in Planes 2..16.
|
|
133
|
+
-- Supplementary planes are all planes above BMP (Plane 0).
|
|
134
|
+
$ plane_02_10 = <
|
|
135
|
+
{ bit 0 } p02_03,
|
|
136
|
+
{ bit 1, bit 0 } p04_07,
|
|
137
|
+
{ bit 2, bit 1, bit 0 } p08_0F,
|
|
138
|
+
{} p10
|
|
139
|
+
>;
|
|
140
|
+
|
|
141
|
+
-- Canonical Unicode scalar-value partition used by string encoding.
|
|
142
|
+
-- Branch meaning:
|
|
143
|
+
-- - ascii: U+0000..U+007F (subset of BMP / Plane 0)
|
|
144
|
+
-- - plane0: U+0080..U+07FF (still Plane 0)
|
|
145
|
+
-- - bmp_common, bmp_private_use: U+0800..U+FFFF partitions (Plane 0)
|
|
146
|
+
-- - supplementary_plane1: U+10000..U+1FFFF (Plane 1)
|
|
147
|
+
-- - supplementary_planes2_16: U+20000..U+10FFFF (Planes 2..16)
|
|
148
|
+
$ unicode = <
|
|
149
|
+
ascii ascii,
|
|
150
|
+
u0080_07FF plane0,
|
|
151
|
+
{ bmp_hi_common hi, byte lo } bmp_common,
|
|
152
|
+
{ bmp_hi_private_use hi, byte lo } bmp_private_use,
|
|
153
|
+
{ byte mid, byte lo } supplementary_plane1,
|
|
154
|
+
{ plane_02_10 plane, byte mid, byte lo } supplementary_planes2_16
|
|
155
|
+
>;
|
|
156
|
+
|
|
157
|
+
-- Linked-list string: nil or (unicode car, string cdr).
|
|
158
|
+
$ string = < {} nil, { unicode car, string cdr } cons >;
|
|
159
|
+
|
|
160
|
+
---------------------------------------------------------------
|
|
161
|
+
-- §4. Pattern
|
|
162
|
+
---------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
-- Edge: a labeled edge in a pattern graph.
|
|
165
|
+
-- 'label' is the field name or tag name.
|
|
166
|
+
-- 'target' is the index (as $bits) of the target node in the pattern list.
|
|
167
|
+
|
|
168
|
+
$ edge = { string label, bits target };
|
|
169
|
+
|
|
170
|
+
-- Edge list: the edges of a pattern node, sorted by label.
|
|
171
|
+
|
|
172
|
+
$ edges = < {} nil, { edge car, edges cdr } cons >;
|
|
173
|
+
|
|
174
|
+
-- Pattern node: one of five kinds.
|
|
175
|
+
-- "any" is the unconstrained position (...), carrying no edges.
|
|
176
|
+
-- The four others carry their edge list directly.
|
|
177
|
+
|
|
178
|
+
$ pattern-node = <
|
|
179
|
+
{} any,
|
|
180
|
+
edges open-product,
|
|
181
|
+
edges open-union,
|
|
182
|
+
edges closed-product,
|
|
183
|
+
edges closed-union
|
|
184
|
+
>;
|
|
185
|
+
|
|
186
|
+
-- Pattern: a cons-list of pattern nodes.
|
|
187
|
+
-- Node 0 (first element) is the root.
|
|
188
|
+
-- Edges reference nodes by their index in this list, encoded as $bits.
|
|
189
|
+
--
|
|
190
|
+
-- The singleton pattern of $pattern itself is a fixed constant.
|
|
191
|
+
-- That constant is the implicit framing for all k wire formats:
|
|
192
|
+
-- to decode any value, first decode a $pattern under that constant,
|
|
193
|
+
-- then decode the value under the pattern just obtained.
|
|
194
|
+
|
|
195
|
+
$ pattern = < {} nil, { pattern-node car, pattern cdr } cons >;
|
|
196
|
+
|
|
197
|
+
$pattern
|
package/kkk.klib
ADDED
|
Binary file
|
package/kkk.klib~
ADDED
|
Binary file
|
package/object.mjs
CHANGED
|
@@ -61,19 +61,86 @@ function hydrateAnnotated(serialized) {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
const NAME_RE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
65
|
+
|
|
66
|
+
function isSourceName(name) {
|
|
67
|
+
return NAME_RE.test(name) && !name.startsWith("@") && !name.startsWith(":");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function addOrigin(meta, hash, type, origin) {
|
|
71
|
+
if (!hash) return;
|
|
72
|
+
if (!meta[hash]) {
|
|
73
|
+
meta[hash] = { type, origins: [] };
|
|
74
|
+
} else if (meta[hash].type == null) {
|
|
75
|
+
meta[hash].type = type;
|
|
76
|
+
} else if (meta[hash].type !== type) {
|
|
77
|
+
throw new Error(`Metadata type conflict for ${hash}: ${meta[hash].type} vs ${type}`);
|
|
78
|
+
}
|
|
79
|
+
if (meta[hash].origins.some((existing) =>
|
|
80
|
+
existing.source === origin.source &&
|
|
81
|
+
existing.name === origin.name &&
|
|
82
|
+
existing.compiledAt === origin.compiledAt
|
|
83
|
+
)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
meta[hash].origins.push(origin);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function mergeMetaEntries(...metas) {
|
|
90
|
+
const merged = {};
|
|
91
|
+
for (const meta of metas) {
|
|
92
|
+
for (const [hash, entry] of Object.entries(meta || {})) {
|
|
93
|
+
const type = entry?.type;
|
|
94
|
+
if (type !== "code" && type !== "rel") continue;
|
|
95
|
+
for (const origin of entry.origins || []) {
|
|
96
|
+
if (!origin?.name) continue;
|
|
97
|
+
addOrigin(merged, hash, type, {
|
|
98
|
+
...(origin.source == null ? {} : { source: origin.source }),
|
|
99
|
+
name: origin.name,
|
|
100
|
+
...(origin.compiledAt == null ? {} : { compiledAt: origin.compiledAt })
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return merged;
|
|
106
|
+
}
|
|
107
|
+
|
|
64
108
|
function buildMeta(annotated, source) {
|
|
65
109
|
const meta = {};
|
|
110
|
+
const representatives = annotated.representatives || {};
|
|
66
111
|
const relAlias = annotated.relAlias || {};
|
|
67
112
|
const now = new Date().toISOString();
|
|
68
|
-
|
|
113
|
+
|
|
114
|
+
for (const [name, hash] of Object.entries(representatives)) {
|
|
115
|
+
if (!isSourceName(name)) continue;
|
|
116
|
+
addOrigin(meta, hash, "code", { source, name, compiledAt: now });
|
|
117
|
+
}
|
|
118
|
+
|
|
69
119
|
for (const [name, hash] of Object.entries(relAlias)) {
|
|
70
120
|
if (name === "__main__") continue;
|
|
71
|
-
if (!
|
|
72
|
-
meta
|
|
121
|
+
if (!isSourceName(name)) continue;
|
|
122
|
+
addOrigin(meta, hash, "rel", { source, name, compiledAt: now });
|
|
73
123
|
}
|
|
74
124
|
return meta;
|
|
75
125
|
}
|
|
76
126
|
|
|
127
|
+
function importedLibraryMeta(libraries = []) {
|
|
128
|
+
return mergeMetaEntries(...libraries.map((library) => library.meta || {}));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function isEofParseError(error) {
|
|
132
|
+
return error?.hash?.token === "EOF" || /got 'EOF'|Expecting/.test(error?.message || "");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function annotateLibrary(script, options = {}) {
|
|
136
|
+
try {
|
|
137
|
+
return annotate(script, options);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (!isEofParseError(error)) throw error;
|
|
140
|
+
return annotate(`${script}\n()`, options);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
77
144
|
function collectReachable(mainName, rels, allCodes) {
|
|
78
145
|
const reachableRels = new Set();
|
|
79
146
|
const reachableCodes = new Set();
|
|
@@ -140,7 +207,10 @@ function compileObject(script, options = {}) {
|
|
|
140
207
|
const annotated = annotate(script, options);
|
|
141
208
|
const allCodes = codes.dump();
|
|
142
209
|
const serialized = serializeAnnotated(annotated);
|
|
143
|
-
const meta =
|
|
210
|
+
const meta = mergeMetaEntries(
|
|
211
|
+
importedLibraryMeta(options.libraries),
|
|
212
|
+
buildMeta(annotated, options.source || null)
|
|
213
|
+
);
|
|
144
214
|
|
|
145
215
|
// Prune to reachable from main
|
|
146
216
|
const { reachableRels, reachableCodes } = collectReachable("__main__", serialized.rels, allCodes);
|
|
@@ -189,9 +259,12 @@ function rewriteRefsToCanonical(exp, relAlias) {
|
|
|
189
259
|
}
|
|
190
260
|
|
|
191
261
|
function compileLibrary(script, options = {}) {
|
|
192
|
-
const annotated =
|
|
262
|
+
const annotated = annotateLibrary(script, options);
|
|
193
263
|
const serialized = serializeAnnotated(annotated);
|
|
194
|
-
const meta =
|
|
264
|
+
const meta = mergeMetaEntries(
|
|
265
|
+
importedLibraryMeta(options.libraries),
|
|
266
|
+
buildMeta(annotated, options.source || null)
|
|
267
|
+
);
|
|
195
268
|
const relAlias = serialized.relAlias || {};
|
|
196
269
|
|
|
197
270
|
// Key rels by canonical hash, rewrite internal refs to canonical hashes
|
|
@@ -482,6 +555,83 @@ function decompileObjectBuffer(buffer) {
|
|
|
482
555
|
return decompileObject(decodeObject(buffer));
|
|
483
556
|
}
|
|
484
557
|
|
|
558
|
+
function hashBody(hash) {
|
|
559
|
+
return hash.startsWith("@") ? hash.slice(1) : hash;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function compareAliasEntries(left, right) {
|
|
563
|
+
return (
|
|
564
|
+
left.typeOrder - right.typeOrder ||
|
|
565
|
+
left.name.localeCompare(right.name) ||
|
|
566
|
+
compareMaybeTimestamp(left.compiledAt, right.compiledAt) ||
|
|
567
|
+
left.hash.localeCompare(right.hash) ||
|
|
568
|
+
left.source.localeCompare(right.source)
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function compareMaybeTimestamp(left, right) {
|
|
573
|
+
if (left == null && right == null) return 0;
|
|
574
|
+
if (left == null) return 1;
|
|
575
|
+
if (right == null) return -1;
|
|
576
|
+
return left.localeCompare(right);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function originComment(origin) {
|
|
580
|
+
return JSON.stringify({
|
|
581
|
+
...(origin.source == null ? {} : { source: origin.source }),
|
|
582
|
+
...(origin.compiledAt == null ? {} : { compiledAt: origin.compiledAt })
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function extractAliasesFromObject(object) {
|
|
587
|
+
const hydrated = isHydratedObject(object) ? object : hydrateObject(object);
|
|
588
|
+
const entries = [];
|
|
589
|
+
|
|
590
|
+
for (const [hash, entry] of Object.entries(hydrated.meta || {})) {
|
|
591
|
+
const type = entry?.type;
|
|
592
|
+
if (type !== "code" && type !== "rel") continue;
|
|
593
|
+
const typeOrder = type === "code" ? 0 : 1;
|
|
594
|
+
for (const origin of entry.origins || []) {
|
|
595
|
+
if (!origin?.name || !isSourceName(origin.name)) continue;
|
|
596
|
+
entries.push({
|
|
597
|
+
type,
|
|
598
|
+
typeOrder,
|
|
599
|
+
hash,
|
|
600
|
+
name: origin.name,
|
|
601
|
+
source: origin.source || "",
|
|
602
|
+
compiledAt: origin.compiledAt,
|
|
603
|
+
comment: originComment(origin)
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
entries.sort(compareAliasEntries);
|
|
609
|
+
|
|
610
|
+
const activeEntries = new Set();
|
|
611
|
+
for (let i = 0; i < entries.length; i++) {
|
|
612
|
+
const next = entries[i + 1];
|
|
613
|
+
if (!next || next.type !== entries[i].type || next.name !== entries[i].name) {
|
|
614
|
+
activeEntries.add(entries[i]);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const lines = [];
|
|
619
|
+
let lastType = null;
|
|
620
|
+
for (const entry of entries) {
|
|
621
|
+
if (lastType != null && lastType !== entry.type) lines.push("");
|
|
622
|
+
lastType = entry.type;
|
|
623
|
+
const prefix = entry.type === "code" ? "$ " : "";
|
|
624
|
+
const line = `${prefix}${entry.name} = @${hashBody(entry.hash)}; # ${entry.comment}`;
|
|
625
|
+
lines.push(activeEntries.has(entry) ? line : `# ${line}`);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return `${lines.join("\n")}${lines.length === 0 ? "" : "\n"}`;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function extractAliasesFromObjectBuffer(buffer) {
|
|
632
|
+
return extractAliasesFromObject(decodeObject(buffer));
|
|
633
|
+
}
|
|
634
|
+
|
|
485
635
|
export {
|
|
486
636
|
compileObject,
|
|
487
637
|
compileObjectBuffer,
|
|
@@ -496,7 +646,9 @@ export {
|
|
|
496
646
|
runObject,
|
|
497
647
|
objectToFunction,
|
|
498
648
|
decompileObject,
|
|
499
|
-
decompileObjectBuffer
|
|
649
|
+
decompileObjectBuffer,
|
|
650
|
+
extractAliasesFromObject,
|
|
651
|
+
extractAliasesFromObjectBuffer
|
|
500
652
|
};
|
|
501
653
|
|
|
502
654
|
export default {
|
|
@@ -513,5 +665,7 @@ export default {
|
|
|
513
665
|
runObject,
|
|
514
666
|
objectToFunction,
|
|
515
667
|
decompileObject,
|
|
516
|
-
decompileObjectBuffer
|
|
668
|
+
decompileObjectBuffer,
|
|
669
|
+
extractAliasesFromObject,
|
|
670
|
+
extractAliasesFromObjectBuffer
|
|
517
671
|
};
|
package/objects/README.md
CHANGED
|
@@ -31,6 +31,16 @@ Decompiles a `.ko` or `.klib` back into human-readable k source.
|
|
|
31
31
|
./objects/decompile.mjs [object-file [k-file]]
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
### extract-aliases.mjs
|
|
35
|
+
|
|
36
|
+
Extracts metadata aliases from a `.ko` or `.klib` as a valid k definition
|
|
37
|
+
snippet. Output is grouped by metadata type (`code`, then `rel`), then sorted by
|
|
38
|
+
alias name and `compiledAt`.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
./objects/extract-aliases.mjs [object-file [k-file]]
|
|
42
|
+
```
|
|
43
|
+
|
|
34
44
|
## Further reading
|
|
35
45
|
|
|
36
46
|
- [DOCS/OBJECT_FILE_AND_PATTERN.md](../DOCS/OBJECT_FILE_AND_PATTERN.md) — object file format and pattern encoding
|
package/objects/compile-lib.mjs
CHANGED
|
@@ -4,8 +4,27 @@ import fs from "node:fs";
|
|
|
4
4
|
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
5
|
import { compileLibraryBuffer, decodeObject, loadLibrary } from "../object.mjs";
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
|
|
7
|
+
function helpText() {
|
|
8
|
+
return [
|
|
9
|
+
"Compile a k source file into a .klib library object.",
|
|
10
|
+
"",
|
|
11
|
+
`Usage: ${argv[1]} [ --lib lib-file ]... [ k-file [ lib-file ] ]`,
|
|
12
|
+
"",
|
|
13
|
+
"Arguments:",
|
|
14
|
+
" k-file Source .k file to compile. Reads UTF-8 source from stdin when omitted.",
|
|
15
|
+
" lib-file Output .klib path. Writes binary library data to stdout when omitted.",
|
|
16
|
+
"",
|
|
17
|
+
"Options:",
|
|
18
|
+
" --lib file Load a .klib dependency before compiling. May be repeated.",
|
|
19
|
+
" -h, --help Show this help.",
|
|
20
|
+
"",
|
|
21
|
+
"Example:",
|
|
22
|
+
` ${argv[1]} --lib core.klib library.k library.klib`
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function usage(stream = console.error) {
|
|
27
|
+
stream(helpText());
|
|
9
28
|
}
|
|
10
29
|
|
|
11
30
|
async function readStdinText() {
|
|
@@ -18,6 +37,11 @@ async function readStdinText() {
|
|
|
18
37
|
|
|
19
38
|
try {
|
|
20
39
|
const args = argv.slice(2);
|
|
40
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
41
|
+
usage(console.log);
|
|
42
|
+
exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
21
45
|
const libraries = [];
|
|
22
46
|
while (args.length > 0 && args[0] === "--lib") {
|
|
23
47
|
args.shift();
|
package/objects/compile.mjs
CHANGED
|
@@ -4,8 +4,27 @@ import fs from "node:fs";
|
|
|
4
4
|
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
5
|
import { compileObjectBuffer, decodeObject, loadLibrary } from "../object.mjs";
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
|
|
7
|
+
function helpText() {
|
|
8
|
+
return [
|
|
9
|
+
"Compile a k source file into an executable .ko object.",
|
|
10
|
+
"",
|
|
11
|
+
`Usage: ${argv[1]} [ --lib lib-file ]... [ k-file [ object-file ] ]`,
|
|
12
|
+
"",
|
|
13
|
+
"Arguments:",
|
|
14
|
+
" k-file Source .k file to compile. Reads UTF-8 source from stdin when omitted.",
|
|
15
|
+
" object-file Output .ko path. Writes binary object data to stdout when omitted.",
|
|
16
|
+
"",
|
|
17
|
+
"Options:",
|
|
18
|
+
" --lib file Load a .klib dependency before compiling. May be repeated.",
|
|
19
|
+
" -h, --help Show this help.",
|
|
20
|
+
"",
|
|
21
|
+
"Example:",
|
|
22
|
+
` ${argv[1]} --lib core.klib program.k program.ko`
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function usage(stream = console.error) {
|
|
27
|
+
stream(helpText());
|
|
9
28
|
}
|
|
10
29
|
|
|
11
30
|
async function readStdinText() {
|
|
@@ -18,6 +37,11 @@ async function readStdinText() {
|
|
|
18
37
|
|
|
19
38
|
try {
|
|
20
39
|
const args = argv.slice(2);
|
|
40
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
41
|
+
usage(console.log);
|
|
42
|
+
exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
21
45
|
const libraries = [];
|
|
22
46
|
while (args.length > 0 && args[0] === "--lib") {
|
|
23
47
|
args.shift();
|
package/objects/decompile.mjs
CHANGED
|
@@ -4,8 +4,26 @@ import fs from "node:fs";
|
|
|
4
4
|
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
5
|
import { decompileObjectBuffer } from "../object.mjs";
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
|
|
7
|
+
function helpText() {
|
|
8
|
+
return [
|
|
9
|
+
"Decompile a .ko or .klib object into readable k source.",
|
|
10
|
+
"",
|
|
11
|
+
`Usage: ${argv[1]} [ object-file [ k-file ] ]`,
|
|
12
|
+
"",
|
|
13
|
+
"Arguments:",
|
|
14
|
+
" object-file Input .ko or .klib path. Reads binary object data from stdin when omitted.",
|
|
15
|
+
" k-file Output .k path. Writes source to stdout when omitted.",
|
|
16
|
+
"",
|
|
17
|
+
"Options:",
|
|
18
|
+
" -h, --help Show this help.",
|
|
19
|
+
"",
|
|
20
|
+
"Example:",
|
|
21
|
+
` ${argv[1]} program.ko program.decompiled.k`
|
|
22
|
+
].join("\n");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function usage(stream = console.error) {
|
|
26
|
+
stream(helpText());
|
|
9
27
|
}
|
|
10
28
|
|
|
11
29
|
async function readStdinBuffer() {
|
|
@@ -18,6 +36,11 @@ async function readStdinBuffer() {
|
|
|
18
36
|
|
|
19
37
|
try {
|
|
20
38
|
const [, , objectPath, outputPath] = argv;
|
|
39
|
+
if (objectPath === "-h" || objectPath === "--help") {
|
|
40
|
+
usage(console.log);
|
|
41
|
+
exit(0);
|
|
42
|
+
}
|
|
43
|
+
|
|
21
44
|
const objectBuffer = objectPath == null ? await readStdinBuffer() : fs.readFileSync(objectPath);
|
|
22
45
|
const source = decompileObjectBuffer(objectBuffer);
|
|
23
46
|
if (outputPath == null) {
|