@fraczak/k 6.2.1 → 6.2.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/CHANGELOG.md +11 -0
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +6 -3
- package/README.md +5 -4
- package/a.klib +1 -0
- package/codecs/runtime/codec.mjs +135 -72
- package/codecs/runtime/prefix-codec.mjs +98 -64
- package/k.mjs +51 -15
- package/objects/README.md +7 -17
- package/objects/compile.mjs +213 -24
- package/package.json +1 -2
- package/scripts/benchmark-idempotency.mjs +3 -3
- package/scripts/run-tests.mjs +1 -0
- package/tests/integration.sh +18 -1
- package/tests/test-deep-wire-value.mjs +29 -0
- package/objects/compile-lib.mjs +0 -67
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 a .klib dependency before compiling. May be repeated.",
|
|
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,101 @@ 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
|
+
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
172
|
+
} else if (args[0] === "--export") {
|
|
173
|
+
args.shift();
|
|
174
|
+
const spec = args.shift();
|
|
175
|
+
if (!spec) throw new Error("--export requires a spec argument (name or libname:localname)");
|
|
176
|
+
exports.push(spec);
|
|
177
|
+
} else if (args[0] === "--format") {
|
|
178
|
+
args.shift();
|
|
179
|
+
format = args.shift();
|
|
180
|
+
if (!["ko", "klib", "kvm"].includes(format))
|
|
181
|
+
throw new Error(`Unknown format: ${format}. Use ko, klib, or kvm.`);
|
|
182
|
+
} else if (args[0].startsWith("--")) {
|
|
183
|
+
throw new Error(`Unknown option: ${args[0]}`);
|
|
184
|
+
} else {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const inputArg = args.shift() || null;
|
|
190
|
+
const outputPath = args.shift() || null;
|
|
191
|
+
if (args.length > 0) {
|
|
192
|
+
throw new Error(`Unexpected argument: ${args[0]}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const outputFormat = format || inferFormat(outputPath);
|
|
196
|
+
const input = resolveInput(inputArg);
|
|
197
|
+
const inputType = input.type;
|
|
198
|
+
|
|
199
|
+
let output;
|
|
200
|
+
|
|
201
|
+
if (inputType === "ko" || inputType === "klib") {
|
|
202
|
+
const buffer = input.kind === "stdin" ? await readStdinBytes() : fs.readFileSync(input.path);
|
|
203
|
+
const object = decodeObject(buffer);
|
|
204
|
+
if (inputType === "klib") {
|
|
205
|
+
libraries.push(loadLibrary(object));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (outputFormat === "kvm") {
|
|
209
|
+
if (inputType === "klib") {
|
|
210
|
+
throw new Error("Cannot produce .kvm from .klib alone; use a .ko or .k input.");
|
|
211
|
+
}
|
|
212
|
+
output = objectToKVM(object);
|
|
213
|
+
} else if (outputFormat === "ko") {
|
|
214
|
+
if (inputType === "ko") {
|
|
215
|
+
// already a .ko, just copy
|
|
216
|
+
output = buffer;
|
|
217
|
+
} else {
|
|
218
|
+
throw new Error("Cannot produce .ko from .klib without a main expression; provide .k source instead.");
|
|
219
|
+
}
|
|
220
|
+
} else if (outputFormat === "klib") {
|
|
221
|
+
if (inputType === "klib") {
|
|
222
|
+
output = buffer;
|
|
223
|
+
} else {
|
|
224
|
+
throw new Error("Cannot produce .klib from .ko; provide .k source instead.");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
// Input is .k source
|
|
229
|
+
const rawSource = input.kind === "stdin"
|
|
230
|
+
? (await readStdinBytes()).toString("utf8")
|
|
231
|
+
: input.kind === "inline"
|
|
232
|
+
? input.source
|
|
233
|
+
: fs.readFileSync(input.path, "utf8");
|
|
234
|
+
const preamble = buildExportPreamble(exports, libraries);
|
|
235
|
+
const source = preamble + rawSource;
|
|
236
|
+
const opts = { source: input.sourceName, libraries };
|
|
237
|
+
|
|
238
|
+
if (outputFormat === "ko") {
|
|
239
|
+
output = compileObjectBuffer(source, opts);
|
|
240
|
+
} else if (outputFormat === "klib") {
|
|
241
|
+
output = compileLibraryBuffer(source, opts);
|
|
242
|
+
} else if (outputFormat === "kvm") {
|
|
243
|
+
output = compileToKVM(source, opts);
|
|
244
|
+
}
|
|
52
245
|
}
|
|
53
246
|
|
|
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
247
|
if (outputPath == null) {
|
|
59
|
-
stdout.write(
|
|
248
|
+
stdout.write(output);
|
|
60
249
|
} else {
|
|
61
|
-
fs.writeFileSync(outputPath,
|
|
250
|
+
fs.writeFileSync(outputPath, output);
|
|
62
251
|
}
|
|
63
252
|
} catch (error) {
|
|
64
|
-
console.error(error);
|
|
253
|
+
console.error(error.message || String(error));
|
|
65
254
|
usage();
|
|
66
255
|
exit(1);
|
|
67
256
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.3",
|
|
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",
|
|
@@ -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/scripts/run-tests.mjs
CHANGED
|
@@ -18,6 +18,7 @@ const tests = [
|
|
|
18
18
|
["tests/test-fingerprint.mjs", node, ["tests/test-fingerprint.mjs"]],
|
|
19
19
|
["tests/test-hash-fuzz.mjs", node, ["tests/test-hash-fuzz.mjs"]],
|
|
20
20
|
["tests/test-hash-normalization.mjs", node, ["tests/test-hash-normalization.mjs"]],
|
|
21
|
+
["tests/test-deep-wire-value.mjs", node, ["tests/test-deep-wire-value.mjs"]],
|
|
21
22
|
["tests/test-k-object.mjs", node, ["tests/test-k-object.mjs"]],
|
|
22
23
|
["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
|
|
23
24
|
["tests/test-kvm.mjs", node, ["tests/test-kvm.mjs"]],
|
package/tests/integration.sh
CHANGED
|
@@ -8,6 +8,23 @@ 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
|
+
if ./objects/compile.mjs "$TMP_DIR/missing.k" > "$TMP_DIR/missing.out" 2> "$TMP_DIR/missing.err"; then
|
|
23
|
+
echo "expected missing .k input to fail" >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
grep -q 'Input file not found:' "$TMP_DIR/missing.err"
|
|
27
|
+
|
|
11
28
|
echo '["zebara", "ela", "kupa", ala, owca]' | node ./codecs/k-parse.mjs | ./k.mjs '{.1 0,.3 1}' | node ./codecs/k-print.mjs
|
|
12
29
|
echo
|
|
13
30
|
echo '-21' | node ./codecs/int.mjs --parse | node ./codecs/int.mjs --print | grep -qx -- '-21'
|
|
@@ -16,7 +33,7 @@ node ./codecs/unit.mjs --parse | node ./codecs/unit.mjs --print | grep -qx '{}'
|
|
|
16
33
|
echo
|
|
17
34
|
printf 'A🙂\nBé~\t' | ./codecs/utf8.mjs --parse | ./k.mjs -k Examples/byte.k |./codecs/utf8.mjs --print
|
|
18
35
|
echo
|
|
19
|
-
./objects/compile
|
|
36
|
+
./objects/compile.mjs Examples/ieee.k "$TMP_DIR/ieee.klib"
|
|
20
37
|
MUL=`./objects/extract-aliases.mjs "$TMP_DIR/ieee.klib" | grep '^mul = @' | sed 's/^mul = \(@[^;]*\);.*$/\1/'`
|
|
21
38
|
DIV=`./objects/extract-aliases.mjs "$TMP_DIR/ieee.klib" | grep '^div = @' | sed 's/^div = \(@[^;]*\);.*$/\1/'`
|
|
22
39
|
ARG=0.12
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { Product, Variant } from "../Value.mjs";
|
|
3
|
+
import { decodeWire, encodeToWire } from "../codecs/runtime/prefix-codec.mjs";
|
|
4
|
+
|
|
5
|
+
const depth = 12000;
|
|
6
|
+
const pattern = [
|
|
7
|
+
["closed-union", [["+", 1]]],
|
|
8
|
+
["closed-union", [["_", 2], ["1", 1]]],
|
|
9
|
+
["closed-product", []]
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
let bits = new Variant("_", new Product({}));
|
|
13
|
+
for (let i = 0; i < depth; i++) {
|
|
14
|
+
bits = new Variant("1", bits);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const wire = encodeToWire(new Variant("+", bits), pattern);
|
|
18
|
+
const decoded = decodeWire(wire).value;
|
|
19
|
+
assert.equal(decoded.tag, "+");
|
|
20
|
+
|
|
21
|
+
bits = decoded.value;
|
|
22
|
+
for (let i = 0; i < depth; i++) {
|
|
23
|
+
assert.equal(bits.tag, "1");
|
|
24
|
+
bits = bits.value;
|
|
25
|
+
}
|
|
26
|
+
assert.equal(bits.tag, "_");
|
|
27
|
+
assert.deepEqual(bits.value.product, {});
|
|
28
|
+
|
|
29
|
+
console.log("deep wire value: ok");
|
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
|
-
}
|