@fraczak/k 6.2.2 → 6.2.4
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 +1 -1
- package/CHANGELOG.md +11 -0
- package/CLAUDE.md +1 -1
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +10 -7
- package/README.md +6 -5
- package/a.klib +1 -0
- package/k.mjs +62 -16
- package/kvm.mjs +2 -1
- package/objects/README.md +7 -17
- package/objects/compile.mjs +214 -24
- package/package.json +1 -2
- package/run.mjs +25 -26
- package/scripts/benchmark-idempotency.mjs +3 -3
- package/tests/integration.sh +44 -2
- package/objects/compile-lib.mjs +0 -67
package/k.mjs
CHANGED
|
@@ -12,19 +12,29 @@ const prog = argv[1];
|
|
|
12
12
|
let kScript, inputStream;
|
|
13
13
|
|
|
14
14
|
function usage() {
|
|
15
|
-
console.error(`Usage: ${prog} [ --lib lib-file ]... ( k-expr |
|
|
15
|
+
console.error(`Usage: ${prog} [ --lib lib-file ] [ --export spec ]... ( k-expr | file ) [ input-file ]`);
|
|
16
16
|
console.error(` E.g., echo '["zebara","ela"]' | k-parse --input-type '$x=<{} zebara, {} ela>; $v={x 0, x 1}; $v' | ${prog} '{.1 0}'`);
|
|
17
17
|
console.error("Options:");
|
|
18
|
-
console.error(" --lib file
|
|
19
|
-
console.error("
|
|
18
|
+
console.error(" --lib file Load one .klib dependency before compiling.");
|
|
19
|
+
console.error(" --export spec Export a library alias into scope. 'name' or 'libname:localname'. May be repeated.");
|
|
20
|
+
console.error(" -h, --help Show this help.");
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function
|
|
23
|
-
|
|
23
|
+
function isFile(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
return fs.statSync(filePath).isFile();
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error?.code === "ENOENT" || error?.code === "ENOTDIR") return false;
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function compileFile(filePath, libraries, preamble) {
|
|
33
|
+
const buffer = fs.readFileSync(filePath);
|
|
24
34
|
try {
|
|
25
35
|
return objectToFunction(decodeObject(buffer));
|
|
26
36
|
} catch {
|
|
27
|
-
return k.compile(buffer.toString("utf8"), { libraries });
|
|
37
|
+
return k.compile(preamble + buffer.toString("utf8"), { libraries });
|
|
28
38
|
}
|
|
29
39
|
}
|
|
30
40
|
|
|
@@ -36,13 +46,49 @@ function compileFile(path, libraries) {
|
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
const libraries = [];
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
const exports = [];
|
|
50
|
+
// Parse --lib and --export flags
|
|
51
|
+
while (args.length > 0 && args[0].startsWith("--")) {
|
|
52
|
+
if (args[0] === "--lib") {
|
|
53
|
+
args.shift();
|
|
54
|
+
const libPath = args.shift();
|
|
55
|
+
if (!libPath) throw new Error("--lib requires a file argument");
|
|
56
|
+
if (libraries.length > 0) throw new Error("--lib may be specified at most once");
|
|
57
|
+
const libBuffer = fs.readFileSync(libPath);
|
|
58
|
+
libraries.push(loadLibrary(decodeObject(libBuffer)));
|
|
59
|
+
} else if (args[0] === "--export") {
|
|
60
|
+
args.shift();
|
|
61
|
+
const spec = args.shift();
|
|
62
|
+
if (!spec) throw new Error("--export requires a spec argument");
|
|
63
|
+
exports.push(spec);
|
|
64
|
+
} else {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function buildExportPreamble() {
|
|
70
|
+
if (exports.length === 0) return "";
|
|
71
|
+
const aliasMap = {};
|
|
72
|
+
for (const lib of libraries) {
|
|
73
|
+
for (const [name, hash] of Object.entries(lib.relAlias || {})) {
|
|
74
|
+
if (name !== "__main__") aliasMap[name] = hash;
|
|
75
|
+
}
|
|
76
|
+
for (const [hash, entry] of Object.entries(lib.meta || {})) {
|
|
77
|
+
if (entry?.type !== "rel") continue;
|
|
78
|
+
for (const origin of entry?.origins || []) {
|
|
79
|
+
if (origin?.name && origin.name !== "__main__") aliasMap[origin.name] = hash;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const lines = [];
|
|
84
|
+
for (const spec of exports) {
|
|
85
|
+
const [libName, localName] = spec.includes(":") ? spec.split(":", 2) : [spec, spec];
|
|
86
|
+
const hash = aliasMap[libName];
|
|
87
|
+
if (!hash) throw new Error(`--export: '${libName}' not found in loaded libraries`);
|
|
88
|
+
const body = hash.startsWith("@") ? hash.slice(1) : hash;
|
|
89
|
+
lines.push(`${localName} = @${body};`);
|
|
90
|
+
}
|
|
91
|
+
return lines.join("\n") + "\n";
|
|
46
92
|
}
|
|
47
93
|
|
|
48
94
|
let kScriptStr = (function (arg) {
|
|
@@ -50,10 +96,10 @@ function compileFile(path, libraries) {
|
|
|
50
96
|
throw new Error("Missing script argument");
|
|
51
97
|
}
|
|
52
98
|
if (arg === "-k") {
|
|
53
|
-
|
|
54
|
-
} else {
|
|
55
|
-
return arg;
|
|
99
|
+
throw new Error("-k is no longer supported; pass the source/object file path directly");
|
|
56
100
|
}
|
|
101
|
+
if (isFile(arg)) return compileFile(arg, libraries, buildExportPreamble());
|
|
102
|
+
return buildExportPreamble() + arg;
|
|
57
103
|
})(args.shift());
|
|
58
104
|
let kScript = typeof kScriptStr === "function" ? kScriptStr : k.compile(kScriptStr, { libraries });
|
|
59
105
|
inputStream = (function (arg) {
|
package/kvm.mjs
CHANGED
|
@@ -430,7 +430,7 @@ function usage() {
|
|
|
430
430
|
const prog = argv[1] || "kvm.mjs";
|
|
431
431
|
console.error(`Usage: node ${prog} [ options ] ( k-expr | -k file ) [ input-file ]`);
|
|
432
432
|
console.error("Options:");
|
|
433
|
-
console.error(" --lib file Load
|
|
433
|
+
console.error(" --lib file Load one .klib dependency before compiling.");
|
|
434
434
|
console.error(" --envelope-free Run the interpreter in envelope-free mode.");
|
|
435
435
|
console.error(" -h, --help Show this help.");
|
|
436
436
|
}
|
|
@@ -454,6 +454,7 @@ async function main() {
|
|
|
454
454
|
args.shift();
|
|
455
455
|
const libPath = args.shift();
|
|
456
456
|
if (!libPath) throw new Error("--lib requires a file argument");
|
|
457
|
+
if (libraries.length > 0) throw new Error("--lib may be specified at most once");
|
|
457
458
|
const libBuffer = fs.readFileSync(libPath);
|
|
458
459
|
libraries.push(loadLibrary(decodeObject(libBuffer)));
|
|
459
460
|
} else {
|
package/objects/README.md
CHANGED
|
@@ -6,27 +6,17 @@ CLI tools for compiling and inspecting k object files (`.ko`) and library files
|
|
|
6
6
|
|
|
7
7
|
### `k-compile`
|
|
8
8
|
|
|
9
|
-
Compiles
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Compiles `.k` source into an executable object file (`.ko`), library file
|
|
10
|
+
(`.klib`), or kVM JSON (`.kvm`). Output format is inferred from the output
|
|
11
|
+
extension, or from `--format`.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
k-compile [--lib lib-file]
|
|
14
|
+
k-compile [--lib lib-file] [--format ko|klib|kvm] [source-snippet | input-file [output-file]]
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
The output is plain JSON with no binary header. It contains the library closure
|
|
21
|
-
formed from loaded `--lib` dependencies plus the compiled source definitions,
|
|
22
|
-
with `main: null`.
|
|
23
|
-
Use `--lib` to build on top of existing libraries.
|
|
24
|
-
Each stored relation includes `typeDerivation.status`; libraries may contain a
|
|
25
|
-
mix of converged and non-converged relations.
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
k-compile-lib [--lib lib-file]... [k-file [lib-file]]
|
|
29
|
-
```
|
|
17
|
+
Existing input paths are read as files. A non-existing input with `.k`, `.ko`,
|
|
18
|
+
or `.klib` extension is reported as a missing file; otherwise it is compiled as
|
|
19
|
+
inline k source, in the same style as `k.mjs`.
|
|
30
20
|
|
|
31
21
|
### `k-decompile`
|
|
32
22
|
|
package/objects/compile.mjs
CHANGED
|
@@ -1,26 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
4
5
|
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
compileObjectBuffer,
|
|
8
|
+
compileLibraryBuffer,
|
|
9
|
+
decodeObject,
|
|
10
|
+
loadLibrary
|
|
11
|
+
} from "../object.mjs";
|
|
12
|
+
import { lowerToKVM } from "../kvm.mjs";
|
|
13
|
+
import { annotate } from "../index.mjs";
|
|
6
14
|
|
|
7
15
|
function helpText() {
|
|
8
16
|
return [
|
|
9
|
-
"Compile a k source
|
|
10
|
-
"
|
|
17
|
+
"Compile a k source, .ko, or .klib into .ko, .klib, or .kvm output.",
|
|
18
|
+
"Output format is inferred from the output file extension, or from --format.",
|
|
11
19
|
"",
|
|
12
|
-
`Usage: ${argv[1]} [
|
|
20
|
+
`Usage: ${argv[1]} [options] [source-snippet | input-file [output-file]]`,
|
|
13
21
|
"",
|
|
14
22
|
"Arguments:",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
23
|
+
" source-snippet Inline k source, in the same style as k.mjs.",
|
|
24
|
+
" input-file Source .k, .ko, or .klib file. Reads UTF-8 source from stdin when omitted.",
|
|
25
|
+
" output-file Output path (.ko, .klib, or .kvm). Writes to stdout when omitted.",
|
|
17
26
|
"",
|
|
18
27
|
"Options:",
|
|
19
|
-
" --lib file
|
|
20
|
-
"
|
|
28
|
+
" --lib file Load one .klib dependency before compiling.",
|
|
29
|
+
" --export spec Export a library alias into the source scope. May be repeated.",
|
|
30
|
+
" spec is 'name' or 'libname:localname'.",
|
|
31
|
+
" --format fmt Output format: ko, klib, or kvm. Overrides extension detection.",
|
|
32
|
+
" -h, --help Show this help.",
|
|
21
33
|
"",
|
|
22
|
-
"
|
|
23
|
-
|
|
34
|
+
"Existing input paths are read as files. A non-existing input with .k, .ko,",
|
|
35
|
+
"or .klib extension is reported as a missing file; otherwise it is compiled",
|
|
36
|
+
"as inline k source.",
|
|
37
|
+
"",
|
|
38
|
+
"When input is .ko and output is .kvm, the object's relations are lowered",
|
|
39
|
+
"to kVM. A .klib input can only be copied to .klib output.",
|
|
40
|
+
"",
|
|
41
|
+
"Examples:",
|
|
42
|
+
` ${argv[1]} 'x = |x; x x'`,
|
|
43
|
+
` ${argv[1]} program.k program.ko`,
|
|
44
|
+
` ${argv[1]} program.k program.klib`,
|
|
45
|
+
` ${argv[1]} program.k program.kvm`,
|
|
46
|
+
` ${argv[1]} program.ko program.kvm`,
|
|
47
|
+
` ${argv[1]} --lib core.klib --export add --export mul:times program.k program.ko`
|
|
24
48
|
].join("\n");
|
|
25
49
|
}
|
|
26
50
|
|
|
@@ -28,12 +52,100 @@ function usage(stream = console.error) {
|
|
|
28
52
|
stream(helpText());
|
|
29
53
|
}
|
|
30
54
|
|
|
31
|
-
async function
|
|
55
|
+
async function readStdinBytes() {
|
|
32
56
|
const chunks = [];
|
|
33
57
|
for await (const chunk of stdin) {
|
|
34
58
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
35
59
|
}
|
|
36
|
-
return Buffer.concat(chunks)
|
|
60
|
+
return Buffer.concat(chunks);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function inferFormat(outputPath) {
|
|
64
|
+
if (!outputPath) return "ko";
|
|
65
|
+
const ext = path.extname(outputPath).toLowerCase();
|
|
66
|
+
if (ext === ".klib") return "klib";
|
|
67
|
+
if (ext === ".kvm") return "kvm";
|
|
68
|
+
return "ko";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function inferInputType(inputPath) {
|
|
72
|
+
if (!inputPath) return "k";
|
|
73
|
+
const ext = path.extname(inputPath).toLowerCase();
|
|
74
|
+
if (ext === ".ko") return "ko";
|
|
75
|
+
if (ext === ".klib") return "klib";
|
|
76
|
+
return "k";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveInput(inputArg) {
|
|
80
|
+
if (inputArg == null) {
|
|
81
|
+
return { kind: "stdin", type: "k", sourceName: "<stdin>" };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ext = path.extname(inputArg).toLowerCase();
|
|
85
|
+
if (fs.existsSync(inputArg)) {
|
|
86
|
+
return {
|
|
87
|
+
kind: "file",
|
|
88
|
+
type: inferInputType(inputArg),
|
|
89
|
+
path: inputArg,
|
|
90
|
+
sourceName: inputArg
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (ext === ".k" || ext === ".ko" || ext === ".klib") {
|
|
95
|
+
throw new Error(`Input file not found: ${inputArg}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
kind: "inline",
|
|
100
|
+
type: "k",
|
|
101
|
+
source: inputArg,
|
|
102
|
+
sourceName: "<inline>"
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function buildExportPreamble(exports, libraries) {
|
|
107
|
+
if (exports.length === 0) return "";
|
|
108
|
+
// Build a combined alias map from all libraries: name -> @hash
|
|
109
|
+
const aliasMap = {};
|
|
110
|
+
for (const lib of libraries) {
|
|
111
|
+
for (const [name, hash] of Object.entries(lib.relAlias || {})) {
|
|
112
|
+
if (name !== "__main__") aliasMap[name] = hash;
|
|
113
|
+
}
|
|
114
|
+
for (const [hash, entry] of Object.entries(lib.meta || {})) {
|
|
115
|
+
if (entry?.type !== "rel") continue;
|
|
116
|
+
for (const origin of entry?.origins || []) {
|
|
117
|
+
if (origin?.name && origin.name !== "__main__") {
|
|
118
|
+
aliasMap[origin.name] = hash;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const lines = [];
|
|
124
|
+
for (const spec of exports) {
|
|
125
|
+
const [libName, localName] = spec.includes(":") ? spec.split(":", 2) : [spec, spec];
|
|
126
|
+
const hash = aliasMap[libName];
|
|
127
|
+
if (!hash) throw new Error(`--export: '${libName}' not found in loaded libraries`);
|
|
128
|
+
const body = hash.startsWith("@") ? hash.slice(1) : hash;
|
|
129
|
+
lines.push(`${localName} = @${body};`);
|
|
130
|
+
}
|
|
131
|
+
return lines.join("\n") + "\n";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function compileToKVM(source, options) {
|
|
135
|
+
const defs = annotate(source, options);
|
|
136
|
+
const kvmProgram = {};
|
|
137
|
+
for (const [name, relDef] of Object.entries(defs.rels)) {
|
|
138
|
+
kvmProgram[name] = lowerToKVM(relDef, name);
|
|
139
|
+
}
|
|
140
|
+
return JSON.stringify(kvmProgram, null, 2) + "\n";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function objectToKVM(object) {
|
|
144
|
+
const kvmProgram = {};
|
|
145
|
+
for (const [name, relDef] of Object.entries(object.rels)) {
|
|
146
|
+
kvmProgram[name] = lowerToKVM(relDef, name);
|
|
147
|
+
}
|
|
148
|
+
return JSON.stringify(kvmProgram, null, 2) + "\n";
|
|
37
149
|
}
|
|
38
150
|
|
|
39
151
|
try {
|
|
@@ -44,24 +156,102 @@ try {
|
|
|
44
156
|
}
|
|
45
157
|
|
|
46
158
|
const libraries = [];
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
159
|
+
let format = null;
|
|
160
|
+
const exports = [];
|
|
161
|
+
|
|
162
|
+
while (args.length > 0) {
|
|
163
|
+
if (args[0] === "--") {
|
|
164
|
+
args.shift();
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
if (args[0] === "--lib") {
|
|
168
|
+
args.shift();
|
|
169
|
+
const libPath = args.shift();
|
|
170
|
+
if (!libPath) throw new Error("--lib requires a file argument");
|
|
171
|
+
if (libraries.length > 0) throw new Error("--lib may be specified at most once");
|
|
172
|
+
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
173
|
+
} else if (args[0] === "--export") {
|
|
174
|
+
args.shift();
|
|
175
|
+
const spec = args.shift();
|
|
176
|
+
if (!spec) throw new Error("--export requires a spec argument (name or libname:localname)");
|
|
177
|
+
exports.push(spec);
|
|
178
|
+
} else if (args[0] === "--format") {
|
|
179
|
+
args.shift();
|
|
180
|
+
format = args.shift();
|
|
181
|
+
if (!["ko", "klib", "kvm"].includes(format))
|
|
182
|
+
throw new Error(`Unknown format: ${format}. Use ko, klib, or kvm.`);
|
|
183
|
+
} else if (args[0].startsWith("--")) {
|
|
184
|
+
throw new Error(`Unknown option: ${args[0]}`);
|
|
185
|
+
} else {
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const inputArg = args.shift() || null;
|
|
191
|
+
const outputPath = args.shift() || null;
|
|
192
|
+
if (args.length > 0) {
|
|
193
|
+
throw new Error(`Unexpected argument: ${args[0]}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const outputFormat = format || inferFormat(outputPath);
|
|
197
|
+
const input = resolveInput(inputArg);
|
|
198
|
+
const inputType = input.type;
|
|
199
|
+
|
|
200
|
+
let output;
|
|
201
|
+
|
|
202
|
+
if (inputType === "ko" || inputType === "klib") {
|
|
203
|
+
const buffer = input.kind === "stdin" ? await readStdinBytes() : fs.readFileSync(input.path);
|
|
204
|
+
const object = decodeObject(buffer);
|
|
205
|
+
if (inputType === "klib") {
|
|
206
|
+
libraries.push(loadLibrary(object));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (outputFormat === "kvm") {
|
|
210
|
+
if (inputType === "klib") {
|
|
211
|
+
throw new Error("Cannot produce .kvm from .klib alone; use a .ko or .k input.");
|
|
212
|
+
}
|
|
213
|
+
output = objectToKVM(object);
|
|
214
|
+
} else if (outputFormat === "ko") {
|
|
215
|
+
if (inputType === "ko") {
|
|
216
|
+
// already a .ko, just copy
|
|
217
|
+
output = buffer;
|
|
218
|
+
} else {
|
|
219
|
+
throw new Error("Cannot produce .ko from .klib without a main expression; provide .k source instead.");
|
|
220
|
+
}
|
|
221
|
+
} else if (outputFormat === "klib") {
|
|
222
|
+
if (inputType === "klib") {
|
|
223
|
+
output = buffer;
|
|
224
|
+
} else {
|
|
225
|
+
throw new Error("Cannot produce .klib from .ko; provide .k source instead.");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
// Input is .k source
|
|
230
|
+
const rawSource = input.kind === "stdin"
|
|
231
|
+
? (await readStdinBytes()).toString("utf8")
|
|
232
|
+
: input.kind === "inline"
|
|
233
|
+
? input.source
|
|
234
|
+
: fs.readFileSync(input.path, "utf8");
|
|
235
|
+
const preamble = buildExportPreamble(exports, libraries);
|
|
236
|
+
const source = preamble + rawSource;
|
|
237
|
+
const opts = { source: input.sourceName, libraries };
|
|
238
|
+
|
|
239
|
+
if (outputFormat === "ko") {
|
|
240
|
+
output = compileObjectBuffer(source, opts);
|
|
241
|
+
} else if (outputFormat === "klib") {
|
|
242
|
+
output = compileLibraryBuffer(source, opts);
|
|
243
|
+
} else if (outputFormat === "kvm") {
|
|
244
|
+
output = compileToKVM(source, opts);
|
|
245
|
+
}
|
|
52
246
|
}
|
|
53
247
|
|
|
54
|
-
const sourcePath = args.shift();
|
|
55
|
-
const outputPath = args.shift();
|
|
56
|
-
const source = sourcePath == null ? await readStdinText() : fs.readFileSync(sourcePath, "utf8");
|
|
57
|
-
const objectBuffer = compileObjectBuffer(source, { source: sourcePath || "<stdin>", libraries });
|
|
58
248
|
if (outputPath == null) {
|
|
59
|
-
stdout.write(
|
|
249
|
+
stdout.write(output);
|
|
60
250
|
} else {
|
|
61
|
-
fs.writeFileSync(outputPath,
|
|
251
|
+
fs.writeFileSync(outputPath, output);
|
|
62
252
|
}
|
|
63
253
|
} catch (error) {
|
|
64
|
-
console.error(error);
|
|
254
|
+
console.error(error.message || String(error));
|
|
65
255
|
usage();
|
|
66
256
|
exit(1);
|
|
67
257
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.4",
|
|
4
4
|
"description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"scripts": {
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"bin": {
|
|
19
19
|
"k": "./k.mjs",
|
|
20
20
|
"k-compile": "./objects/compile.mjs",
|
|
21
|
-
"k-compile-lib": "./objects/compile-lib.mjs",
|
|
22
21
|
"k-decompile": "./objects/decompile.mjs",
|
|
23
22
|
"k-extract-aliases": "./objects/extract-aliases.mjs",
|
|
24
23
|
"k-ieee": "./codecs/ieee.mjs",
|
package/run.mjs
CHANGED
|
@@ -238,32 +238,31 @@ function compiledExp(findCode, exp, typePatternGraph) {
|
|
|
238
238
|
case "product": {
|
|
239
239
|
const labels = exp.product.map(({ label }) => label);
|
|
240
240
|
const fieldFns = exp.product.map(({ exp: e }) => compiledExp(findCode, e, typePatternGraph));
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
241
|
+
fn = (value) => {
|
|
242
|
+
if (value === undefined) return undefined;
|
|
243
|
+
const result = {};
|
|
244
|
+
const patternEntries = [];
|
|
245
|
+
for (let i = 0, len = labels.length; i < len; i++) {
|
|
246
|
+
const r = fieldFns[i](value);
|
|
247
|
+
if (r === undefined) return undefined;
|
|
248
|
+
const label = labels[i];
|
|
249
|
+
result[label] = r;
|
|
250
|
+
patternEntries.push([label, r.pattern]);
|
|
251
|
+
}
|
|
252
|
+
const dynamicOutputPattern = composePattern("closed-product", patternEntries);
|
|
253
|
+
const pattern = staticOutputPattern && dynamicOutputPattern
|
|
254
|
+
? intersectPatterns(staticOutputPattern, dynamicOutputPattern)
|
|
255
|
+
: staticOutputPattern || dynamicOutputPattern;
|
|
256
|
+
if (!pattern && staticOutputPattern && dynamicOutputPattern) {
|
|
257
|
+
throw new TypeError(
|
|
258
|
+
`Type Error in '${exp.op}'${expLocation(exp)}\n` +
|
|
259
|
+
` - Product value envelope does not intersect expression output pattern.\n` +
|
|
260
|
+
` - output pattern: ${patternPreview(staticOutputPattern)}\n` +
|
|
261
|
+
` - product envelope: ${patternPreview(dynamicOutputPattern)}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return new Product(result, pattern);
|
|
265
|
+
};
|
|
267
266
|
break;
|
|
268
267
|
}
|
|
269
268
|
case "vid":
|
|
@@ -44,7 +44,7 @@ function formatBytes(value) {
|
|
|
44
44
|
return `${value} B`;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
console.log(`compile
|
|
47
|
+
console.log(`compile/decompile .klib round-trip benchmark (${iterations} iterations)`);
|
|
48
48
|
console.log("example, median, min, max, object");
|
|
49
49
|
|
|
50
50
|
for (const example of benchmarkExamples) {
|
|
@@ -54,9 +54,9 @@ for (const example of benchmarkExamples) {
|
|
|
54
54
|
for (let i = 0; i < iterations; i++) {
|
|
55
55
|
const out = mkdtempSync(join(tmpdir(), `k-idempotency-bench-${example.replaceAll("/", "_")}-`));
|
|
56
56
|
const t0 = performance.now();
|
|
57
|
-
run(["objects/compile
|
|
57
|
+
run(["objects/compile.mjs", example, join(out, "1.klib")]);
|
|
58
58
|
run(["objects/decompile.mjs", join(out, "1.klib"), join(out, "1.k")]);
|
|
59
|
-
run(["objects/compile
|
|
59
|
+
run(["objects/compile.mjs", join(out, "1.k"), join(out, "2.klib")]);
|
|
60
60
|
run(["objects/decompile.mjs", join(out, "2.klib"), join(out, "2.k")]);
|
|
61
61
|
times.push(performance.now() - t0);
|
|
62
62
|
size = statSync(join(out, "1.klib")).size;
|
package/tests/integration.sh
CHANGED
|
@@ -8,15 +8,57 @@ echo "Running './tests/integration.sh' ..."
|
|
|
8
8
|
TMP_DIR=`mktemp -d`
|
|
9
9
|
trap 'rm -rf "$TMP_DIR"' EXIT
|
|
10
10
|
|
|
11
|
+
./objects/compile.mjs 'x = |x; x x' "$TMP_DIR/inline.ko"
|
|
12
|
+
./objects/decompile.mjs "$TMP_DIR/inline.ko" | grep -q '^----- main -----$'
|
|
13
|
+
printf '()' | ./objects/compile.mjs > "$TMP_DIR/stdin.ko"
|
|
14
|
+
./objects/decompile.mjs "$TMP_DIR/stdin.ko" | grep -q '^----- main -----$'
|
|
15
|
+
./objects/compile.mjs --format kvm '()' | grep -q '"__main__"'
|
|
16
|
+
./objects/compile.mjs "$TMP_DIR/stdin.ko" "$TMP_DIR/stdin.kvm"
|
|
17
|
+
grep -q '"__main__"' "$TMP_DIR/stdin.kvm"
|
|
18
|
+
./objects/compile.mjs 'id = ();' "$TMP_DIR/export.klib"
|
|
19
|
+
./objects/compile.mjs --lib "$TMP_DIR/export.klib" --export id:alias 'alias' "$TMP_DIR/export.ko"
|
|
20
|
+
./objects/decompile.mjs "$TMP_DIR/export.ko" | grep -q '^----- main -----$'
|
|
21
|
+
node ./codecs/unit.mjs --parse | ./k.mjs --lib "$TMP_DIR/export.klib" --export id:alias 'alias' | node ./codecs/unit.mjs --print | grep -qx '{}'
|
|
22
|
+
node ./codecs/unit.mjs --parse | ./k.mjs "$TMP_DIR/export.ko" | node ./codecs/unit.mjs --print | grep -qx '{}'
|
|
23
|
+
if ./k.mjs --lib "$TMP_DIR/export.klib" --lib "$TMP_DIR/export.klib" '()' > "$TMP_DIR/two-lib.out" 2> "$TMP_DIR/two-lib.err"; then
|
|
24
|
+
echo "expected repeated --lib to fail" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
grep -q -- '--lib may be specified at most once' "$TMP_DIR/two-lib.err"
|
|
28
|
+
if ./objects/compile.mjs --lib "$TMP_DIR/export.klib" --lib "$TMP_DIR/export.klib" '()' "$TMP_DIR/two-lib.ko" > "$TMP_DIR/two-lib-compile.out" 2> "$TMP_DIR/two-lib-compile.err"; then
|
|
29
|
+
echo "expected repeated k-compile --lib to fail" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
grep -q -- '--lib may be specified at most once' "$TMP_DIR/two-lib-compile.err"
|
|
33
|
+
if ./kvm.mjs --lib "$TMP_DIR/export.klib" --lib "$TMP_DIR/export.klib" '()' > "$TMP_DIR/two-lib-kvm.out" 2> "$TMP_DIR/two-lib-kvm.err"; then
|
|
34
|
+
echo "expected repeated k-vm --lib to fail" >&2
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
grep -q -- '--lib may be specified at most once' "$TMP_DIR/two-lib-kvm.err"
|
|
38
|
+
if ./objects/compile.mjs "$TMP_DIR/missing.k" > "$TMP_DIR/missing.out" 2> "$TMP_DIR/missing.err"; then
|
|
39
|
+
echo "expected missing .k input to fail" >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
grep -q 'Input file not found:' "$TMP_DIR/missing.err"
|
|
43
|
+
|
|
11
44
|
echo '["zebara", "ela", "kupa", ala, owca]' | node ./codecs/k-parse.mjs | ./k.mjs '{.1 0,.3 1}' | node ./codecs/k-print.mjs
|
|
12
45
|
echo
|
|
46
|
+
echo "a" | ./codecs/k-parse.mjs --input-type '?< {} a, X b, ...>' | ./k.mjs '{() i, /a o}' | ./codecs/show.mjs > "$TMP_DIR/polymorphic-relation.wire" 2> "$TMP_DIR/polymorphic-relation.show"
|
|
47
|
+
grep -Fqx '{{}|a i, {} o} ?{<{}=X0 a, (...) b, ...> i, X0 o}' "$TMP_DIR/polymorphic-relation.show"
|
|
48
|
+
./codecs/k-print.mjs "$TMP_DIR/polymorphic-relation.wire" | grep -qx '{"i":"a","o":{}}'
|
|
49
|
+
echo
|
|
13
50
|
echo '-21' | node ./codecs/int.mjs --parse | node ./codecs/int.mjs --print | grep -qx -- '-21'
|
|
14
51
|
echo
|
|
15
52
|
node ./codecs/unit.mjs --parse | node ./codecs/unit.mjs --print | grep -qx '{}'
|
|
16
53
|
echo
|
|
17
|
-
|
|
54
|
+
if ./k.mjs -k Examples/byte.k > "$TMP_DIR/dash-k.out" 2> "$TMP_DIR/dash-k.err"; then
|
|
55
|
+
echo "expected -k to fail" >&2
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
grep -q -- '-k is no longer supported' "$TMP_DIR/dash-k.err"
|
|
59
|
+
printf 'A🙂\nBé~\t' | ./codecs/utf8.mjs --parse | ./k.mjs Examples/byte.k |./codecs/utf8.mjs --print
|
|
18
60
|
echo
|
|
19
|
-
./objects/compile
|
|
61
|
+
./objects/compile.mjs Examples/ieee.k "$TMP_DIR/ieee.klib"
|
|
20
62
|
MUL=`./objects/extract-aliases.mjs "$TMP_DIR/ieee.klib" | grep '^mul = @' | sed 's/^mul = \(@[^;]*\);.*$/\1/'`
|
|
21
63
|
DIV=`./objects/extract-aliases.mjs "$TMP_DIR/ieee.klib" | grep '^div = @' | sed 's/^div = \(@[^;]*\);.*$/\1/'`
|
|
22
64
|
ARG=0.12
|
package/objects/compile-lib.mjs
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from "node:fs";
|
|
4
|
-
import { argv, exit, stdin, stdout } from "node:process";
|
|
5
|
-
import { compileLibraryBuffer, decodeObject, loadLibrary } from "../object.mjs";
|
|
6
|
-
|
|
7
|
-
function helpText() {
|
|
8
|
-
return [
|
|
9
|
-
"Compile a k source file into a plain-JSON .klib library object.",
|
|
10
|
-
"The library records type-derivation convergence for each stored relation.",
|
|
11
|
-
"",
|
|
12
|
-
`Usage: ${argv[1]} [ --lib lib-file ]... [ k-file [ lib-file ] ]`,
|
|
13
|
-
"",
|
|
14
|
-
"Arguments:",
|
|
15
|
-
" k-file Source .k file to compile. Reads UTF-8 source from stdin when omitted.",
|
|
16
|
-
" lib-file Output .klib path. Writes plain JSON library data to stdout when omitted.",
|
|
17
|
-
"",
|
|
18
|
-
"Options:",
|
|
19
|
-
" --lib file Load a .klib dependency before compiling. May be repeated.",
|
|
20
|
-
" -h, --help Show this help.",
|
|
21
|
-
"",
|
|
22
|
-
"Example:",
|
|
23
|
-
` ${argv[1]} --lib core.klib library.k library.klib`
|
|
24
|
-
].join("\n");
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function usage(stream = console.error) {
|
|
28
|
-
stream(helpText());
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function readStdinText() {
|
|
32
|
-
const chunks = [];
|
|
33
|
-
for await (const chunk of stdin) {
|
|
34
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
35
|
-
}
|
|
36
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const args = argv.slice(2);
|
|
41
|
-
if (args.includes("-h") || args.includes("--help")) {
|
|
42
|
-
usage(console.log);
|
|
43
|
-
exit(0);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const libraries = [];
|
|
47
|
-
while (args.length > 0 && args[0] === "--lib") {
|
|
48
|
-
args.shift();
|
|
49
|
-
const libPath = args.shift();
|
|
50
|
-
if (!libPath) throw new Error("--lib requires a file argument");
|
|
51
|
-
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const sourcePath = args.shift();
|
|
55
|
-
const outputPath = args.shift();
|
|
56
|
-
const source = sourcePath == null ? await readStdinText() : fs.readFileSync(sourcePath, "utf8");
|
|
57
|
-
const objectBuffer = compileLibraryBuffer(source, { source: sourcePath || "<stdin>", libraries });
|
|
58
|
-
if (outputPath == null) {
|
|
59
|
-
stdout.write(objectBuffer);
|
|
60
|
-
} else {
|
|
61
|
-
fs.writeFileSync(outputPath, objectBuffer);
|
|
62
|
-
}
|
|
63
|
-
} catch (error) {
|
|
64
|
-
console.error(error);
|
|
65
|
-
usage();
|
|
66
|
-
exit(1);
|
|
67
|
-
}
|