@fraczak/k 7.0.1 → 7.1.0
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 +78 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +241 -18
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- package/DOCS/PROFILING_AND_TRACING.md +60 -72
- package/DOCS/REPL.md +3 -3
- package/DOCS/book/03-types-as-finite-automata.md +4 -4
- package/DOCS/book/06-values-in-memory.md +9 -9
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
- package/DOCS/book/14-canonical-serialization.md +1 -1
- package/Examples/arithmetics.k +2 -2
- package/Examples/bnat.k +1 -1
- package/Examples/karatsuba-mult.k +2 -2
- package/JOURNAL.md +288 -0
- package/README.md +13 -10
- package/access-table.mjs +343 -0
- package/backend-api.mjs +18 -0
- package/backends/llvm/README.md +20 -21
- package/backends/llvm/bin/k-llvm-build.mjs +105 -32
- package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
- package/backends/llvm/bin/k-llvm.mjs +292 -0
- package/backends/llvm/runtime/krt.c +41 -0
- package/backends/llvm/runtime/krt.h +2 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +132 -15
- package/backends/llvm/src/kvm2llvm.mjs +50 -23
- package/backends/llvm/src/llvm.mjs +133 -20
- package/backends/llvm/tests/integration.sh +11 -11
- package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
- package/backends/llvm/tests/perf-support.mjs +25 -58
- package/backends/llvm/tests/test-llvm.mjs +25 -4
- package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
- package/backends/wasm/bin/k-wasm-run.mjs +61 -20
- package/backends/wasm/bin/k-wasm.mjs +47 -13
- package/backends/wasm/src/cli.mjs +141 -19
- package/backends/wasm/src/wasm.mjs +132 -24
- package/backends/wasm/tests/test-wasm.mjs +5 -5
- package/browser/repl-browser.mjs +16 -5
- package/browser/vfs-data.mjs +5 -5
- package/codecs/k-parse.mjs +29 -8
- package/codecs/k-print.mjs +22 -6
- package/codecs/runtime/codec-sdk.mjs +8 -4
- package/codecs/show.mjs +20 -6
- package/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/k.mjs +56 -16
- package/kir.mjs +200 -75
- package/kvm.mjs +49 -12
- package/objects/compile.mjs +37 -23
- package/objects/decompile.mjs +40 -10
- package/objects/extract-aliases.mjs +44 -14
- package/objects/inspect.mjs +19 -14
- package/objects/validate.mjs +18 -11
- package/package.json +10 -7
- package/pkir.mjs +772 -0
- package/repl.mjs +36 -18
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +16 -4
- package/scripts/perf-ieee.mjs +253 -103
- package/scripts/perf-int.mjs +585 -297
- package/scripts/perf-multiplication.mjs +66 -25
- package/scripts/perf-poly.mjs +292 -164
- package/scripts/run-tests.mjs +3 -0
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-kvm-polymorphic.mjs +2 -2
- package/tests/test-pkir.mjs +530 -0
- package/tests/test-polymorphic-tdd.mjs +196 -0
- package/tests/test-repl-html.mjs +15 -2
- package/tests/test-repl.mjs +30 -1
- package/backends/arm64/README.md +0 -103
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
- package/backends/arm64/bin/k-arm64-run.mjs +0 -58
- package/backends/arm64/bin/k-arm64.mjs +0 -88
- package/backends/arm64/scripts/run-tests.mjs +0 -57
- package/backends/arm64/src/arm64.mjs +0 -229
- package/backends/arm64/src/cli.mjs +0 -155
- package/backends/arm64/src/kvm2arm64.mjs +0 -906
- package/backends/arm64/src/runtime.c +0 -744
- package/backends/arm64/tests/integration.sh +0 -112
- package/backends/arm64/tests/test-arm64.mjs +0 -240
- package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
- package/harness/adapters/arm64.mjs +0 -105
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { argv, exit, stdin, stdout } from "node:process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
import { decodeWire } from "@fraczak/k/backend-api.mjs";
|
|
12
|
+
import {
|
|
13
|
+
compileProgramInputToObject,
|
|
14
|
+
parseCompileOptions,
|
|
15
|
+
readAll,
|
|
16
|
+
resolveProgramInput
|
|
17
|
+
} from "../src/cli.mjs";
|
|
18
|
+
import {
|
|
19
|
+
compileKVMToExecutable,
|
|
20
|
+
compileObjectToExecutable,
|
|
21
|
+
compilePKIRToExecutable
|
|
22
|
+
} from "../src/executable.mjs";
|
|
23
|
+
|
|
24
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
25
|
+
|
|
26
|
+
function helpText() {
|
|
27
|
+
const prog = argv[1] || "k-llvm.mjs";
|
|
28
|
+
const defaultCache = path.join(os.tmpdir(), "k-llvm-cache");
|
|
29
|
+
return [
|
|
30
|
+
"Compile k source, .ko, .kvm, or .pkir to native machine code via",
|
|
31
|
+
"LLVM and execute it over a binary pattern+value wire stream.",
|
|
32
|
+
"",
|
|
33
|
+
`Usage: ${prog} [options]`,
|
|
34
|
+
" ( source-snippet | input-file ) [ input-wire-file ]",
|
|
35
|
+
"",
|
|
36
|
+
"Arguments:",
|
|
37
|
+
" source-snippet Inline k source snippet (in the style of k.mjs).",
|
|
38
|
+
" input-file Source .k, .ko, .klib, .kvm, or .pkir file.",
|
|
39
|
+
" input-wire-file Optional binary wire input (default: stdin).",
|
|
40
|
+
"",
|
|
41
|
+
"Options:",
|
|
42
|
+
" --lib file Load one .klib or .k library before compiling.",
|
|
43
|
+
" --export spec Export library alias into scope. Repeatable.",
|
|
44
|
+
" spec is 'name' or 'libname:localname'.",
|
|
45
|
+
" --pkir Lower via Polymorphic K IR (PKIR).",
|
|
46
|
+
" --retype rel Relation to specialize (default: object main).",
|
|
47
|
+
" --cache-dir path Directory to store compiled native executables.",
|
|
48
|
+
" --no-cache Compile in a temporary directory and discard.",
|
|
49
|
+
" --opt flag Clang optimization level (-O0, -O1, -O2, -O3).",
|
|
50
|
+
" -h, --help Show this help.",
|
|
51
|
+
"",
|
|
52
|
+
"Environment Variables:",
|
|
53
|
+
` K_LLVM_CACHE_DIR Cache directory (default: ${defaultCache}).`,
|
|
54
|
+
" K_LLVM_CLANG_OPT Default Clang optimization flag (default: -O1).",
|
|
55
|
+
" CLANG, CC Path to Clang compiler binary (default: clang).",
|
|
56
|
+
" K_TRACE, TRACE When '1', output phase tracing to stderr.",
|
|
57
|
+
"",
|
|
58
|
+
"Examples:",
|
|
59
|
+
` echo '"abc"' | k-parse | ${prog} '{.1 0}' | k-print`,
|
|
60
|
+
` echo '{"x":1,"y":2}' | k-json --parse | \\`,
|
|
61
|
+
` ${prog} --lib Examples/poly.k --export concat \\`,
|
|
62
|
+
` 's={()xs,()ys}concat; s' | k-json --print`
|
|
63
|
+
].join("\n");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function usage(stream = console.error) {
|
|
67
|
+
stream(helpText());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let cachedFingerprint = null;
|
|
71
|
+
function getBackendFingerprint() {
|
|
72
|
+
if (cachedFingerprint != null) return cachedFingerprint;
|
|
73
|
+
const hash = crypto.createHash("sha256");
|
|
74
|
+
for (const relPath of [
|
|
75
|
+
"src/llvm.mjs",
|
|
76
|
+
"src/kvm2llvm.mjs",
|
|
77
|
+
"src/executable.mjs",
|
|
78
|
+
"runtime/krt.c",
|
|
79
|
+
"runtime/krt.h"
|
|
80
|
+
]) {
|
|
81
|
+
const full = path.join(root, relPath);
|
|
82
|
+
if (fs.existsSync(full)) {
|
|
83
|
+
hash.update(relPath);
|
|
84
|
+
hash.update("\0");
|
|
85
|
+
hash.update(fs.readFileSync(full));
|
|
86
|
+
hash.update("\0");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
cachedFingerprint = hash.digest("hex").slice(0, 16);
|
|
90
|
+
return cachedFingerprint;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function computeCacheKey({
|
|
94
|
+
fingerprint,
|
|
95
|
+
inputKey,
|
|
96
|
+
relation,
|
|
97
|
+
inputPattern,
|
|
98
|
+
usePKIR,
|
|
99
|
+
clangOpt
|
|
100
|
+
}) {
|
|
101
|
+
const hash = crypto.createHash("sha256");
|
|
102
|
+
hash.update("k-llvm-runner-v1\0");
|
|
103
|
+
hash.update(fingerprint);
|
|
104
|
+
hash.update("\0");
|
|
105
|
+
hash.update(String(relation || ""));
|
|
106
|
+
hash.update("\0");
|
|
107
|
+
hash.update(JSON.stringify(inputPattern || null));
|
|
108
|
+
hash.update("\0");
|
|
109
|
+
hash.update(usePKIR ? "pkir\0" : "monomorphic\0");
|
|
110
|
+
hash.update(String(clangOpt || ""));
|
|
111
|
+
hash.update("\0");
|
|
112
|
+
hash.update(inputKey);
|
|
113
|
+
return hash.digest("hex");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function runNativeExecutable(exePath, inputBuffer) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
const child = spawn(exePath, [], {
|
|
119
|
+
stdio: ["pipe", "pipe", "inherit"]
|
|
120
|
+
});
|
|
121
|
+
const chunks = [];
|
|
122
|
+
child.stdout.on("data", (chunk) => chunks.push(chunk));
|
|
123
|
+
child.on("error", reject);
|
|
124
|
+
child.on("close", (status) => {
|
|
125
|
+
if (status !== 0) {
|
|
126
|
+
reject(new Error(`${exePath} exited with status ${status}`));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
resolve(Buffer.concat(chunks));
|
|
130
|
+
});
|
|
131
|
+
child.stdin.end(inputBuffer);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function main() {
|
|
136
|
+
const rawArgs = argv.slice(2);
|
|
137
|
+
if (rawArgs.includes("-h") || rawArgs.includes("--help")) {
|
|
138
|
+
usage(console.log);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let cacheDir = process.env.K_LLVM_CACHE_DIR ||
|
|
143
|
+
path.join(os.tmpdir(), "k-llvm-cache");
|
|
144
|
+
let useCache = true;
|
|
145
|
+
let relation = null;
|
|
146
|
+
let clangOpt = process.env.K_LLVM_CLANG_OPT || "-O1";
|
|
147
|
+
|
|
148
|
+
const remainingArgs = [];
|
|
149
|
+
while (rawArgs.length > 0) {
|
|
150
|
+
const arg = rawArgs[0];
|
|
151
|
+
if (arg === "--cache-dir") {
|
|
152
|
+
rawArgs.shift();
|
|
153
|
+
cacheDir = rawArgs.shift();
|
|
154
|
+
if (!cacheDir) {
|
|
155
|
+
throw new Error("--cache-dir requires a directory path");
|
|
156
|
+
}
|
|
157
|
+
} else if (arg === "--no-cache") {
|
|
158
|
+
rawArgs.shift();
|
|
159
|
+
useCache = false;
|
|
160
|
+
} else if (arg === "--retype") {
|
|
161
|
+
rawArgs.shift();
|
|
162
|
+
relation = rawArgs.shift();
|
|
163
|
+
if (!relation) {
|
|
164
|
+
throw new Error("--retype requires a relation name");
|
|
165
|
+
}
|
|
166
|
+
} else if (arg === "--opt") {
|
|
167
|
+
rawArgs.shift();
|
|
168
|
+
clangOpt = rawArgs.shift();
|
|
169
|
+
if (!clangOpt) {
|
|
170
|
+
throw new Error("--opt requires a flag (e.g. -O1)");
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
remainingArgs.push(rawArgs.shift());
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const { libraries, exportSpecs, usePKIR } =
|
|
178
|
+
parseCompileOptions(remainingArgs);
|
|
179
|
+
const programInput = resolveProgramInput(remainingArgs);
|
|
180
|
+
const inputWirePath = remainingArgs.shift();
|
|
181
|
+
if (remainingArgs.length > 0) {
|
|
182
|
+
throw new Error(`Unexpected argument: ${remainingArgs[0]}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const inputBuffer = await readAll(
|
|
186
|
+
inputWirePath == null ? stdin : fs.createReadStream(inputWirePath)
|
|
187
|
+
);
|
|
188
|
+
if (inputBuffer.length === 0) {
|
|
189
|
+
throw new Error("No input wire data received on stdin");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const { pattern: inputEnvelopePattern } = decodeWire(inputBuffer);
|
|
193
|
+
|
|
194
|
+
let exePath;
|
|
195
|
+
let tempExeDir = null;
|
|
196
|
+
|
|
197
|
+
if (useCache) {
|
|
198
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
199
|
+
const fingerprint = getBackendFingerprint();
|
|
200
|
+
const inputKey = JSON.stringify({
|
|
201
|
+
programInput,
|
|
202
|
+
libraries: libraries.map((lib) => Object.keys(lib.rels || {})),
|
|
203
|
+
exportSpecs
|
|
204
|
+
});
|
|
205
|
+
const key = computeCacheKey({
|
|
206
|
+
fingerprint,
|
|
207
|
+
inputKey,
|
|
208
|
+
relation,
|
|
209
|
+
inputPattern: inputEnvelopePattern,
|
|
210
|
+
usePKIR,
|
|
211
|
+
clangOpt
|
|
212
|
+
});
|
|
213
|
+
exePath = path.join(cacheDir, `${key}.exe`);
|
|
214
|
+
if (!fs.existsSync(exePath)) {
|
|
215
|
+
const tmpPath = path.join(cacheDir, `${key}.${process.pid}.tmp`);
|
|
216
|
+
if (programInput.kind === "pkir") {
|
|
217
|
+
const pkir = JSON.parse(
|
|
218
|
+
fs.readFileSync(programInput.path, "utf8")
|
|
219
|
+
);
|
|
220
|
+
compilePKIRToExecutable(pkir, tmpPath, {
|
|
221
|
+
inputPattern: inputEnvelopePattern,
|
|
222
|
+
clangOpt
|
|
223
|
+
});
|
|
224
|
+
} else if (programInput.kind === "kvm") {
|
|
225
|
+
const kvm = JSON.parse(
|
|
226
|
+
fs.readFileSync(programInput.path, "utf8")
|
|
227
|
+
);
|
|
228
|
+
compileKVMToExecutable(kvm, tmpPath, {
|
|
229
|
+
inputPattern: inputEnvelopePattern,
|
|
230
|
+
clangOpt,
|
|
231
|
+
usePKIR
|
|
232
|
+
});
|
|
233
|
+
} else {
|
|
234
|
+
const object = await compileProgramInputToObject(programInput, {
|
|
235
|
+
libraries,
|
|
236
|
+
exportSpecs,
|
|
237
|
+
stdin
|
|
238
|
+
});
|
|
239
|
+
compileObjectToExecutable(object, tmpPath, {
|
|
240
|
+
relation: relation || object.main,
|
|
241
|
+
inputPattern: inputEnvelopePattern,
|
|
242
|
+
clangOpt,
|
|
243
|
+
usePKIR
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
fs.renameSync(tmpPath, exePath);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
tempExeDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-llvm-run-"));
|
|
250
|
+
exePath = path.join(tempExeDir, "prog.exe");
|
|
251
|
+
if (programInput.kind === "pkir") {
|
|
252
|
+
const pkir = JSON.parse(fs.readFileSync(programInput.path, "utf8"));
|
|
253
|
+
compilePKIRToExecutable(pkir, exePath, {
|
|
254
|
+
inputPattern: inputEnvelopePattern,
|
|
255
|
+
clangOpt
|
|
256
|
+
});
|
|
257
|
+
} else if (programInput.kind === "kvm") {
|
|
258
|
+
const kvm = JSON.parse(fs.readFileSync(programInput.path, "utf8"));
|
|
259
|
+
compileKVMToExecutable(kvm, exePath, {
|
|
260
|
+
inputPattern: inputEnvelopePattern,
|
|
261
|
+
clangOpt,
|
|
262
|
+
usePKIR
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
const object = await compileProgramInputToObject(programInput, {
|
|
266
|
+
libraries,
|
|
267
|
+
exportSpecs,
|
|
268
|
+
stdin
|
|
269
|
+
});
|
|
270
|
+
compileObjectToExecutable(object, exePath, {
|
|
271
|
+
relation: relation || object.main,
|
|
272
|
+
inputPattern: inputEnvelopePattern,
|
|
273
|
+
clangOpt,
|
|
274
|
+
usePKIR
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const outputBuffer = await runNativeExecutable(exePath, inputBuffer);
|
|
281
|
+
stdout.write(outputBuffer);
|
|
282
|
+
} finally {
|
|
283
|
+
if (tempExeDir) {
|
|
284
|
+
fs.rmSync(tempExeDir, { recursive: true, force: true });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
main().catch((error) => {
|
|
290
|
+
console.error(error.stack || error.message || String(error));
|
|
291
|
+
exit(1);
|
|
292
|
+
});
|
|
@@ -1489,6 +1489,39 @@ k_value *k_read_wire(FILE *in, k_rt *rt) {
|
|
|
1489
1489
|
return value;
|
|
1490
1490
|
}
|
|
1491
1491
|
|
|
1492
|
+
static int is_union_kind(int kind) {
|
|
1493
|
+
return kind == KP_OPEN_UNION || kind == KP_CLOSED_UNION;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
static int is_product_kind(int kind) {
|
|
1497
|
+
return kind == KP_OPEN_PRODUCT || kind == KP_CLOSED_PRODUCT;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
int k_pattern_compatible(k_pattern *a, k_pattern *b) {
|
|
1501
|
+
if (a == b) return 1;
|
|
1502
|
+
if (a == NULL || b == NULL) return 0;
|
|
1503
|
+
if (a->node_count != b->node_count) return 0;
|
|
1504
|
+
for (size_t i = 0; i < a->node_count; i++) {
|
|
1505
|
+
k_pattern_node *left = &a->nodes[i];
|
|
1506
|
+
k_pattern_node *right = &b->nodes[i];
|
|
1507
|
+
if (left->kind != right->kind) {
|
|
1508
|
+
if (!(is_union_kind(left->kind) && is_union_kind(right->kind)) &&
|
|
1509
|
+
!(is_product_kind(left->kind) && is_product_kind(right->kind))) {
|
|
1510
|
+
return 0;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
if (left->edge_count != right->edge_count) return 0;
|
|
1514
|
+
for (size_t j = 0; j < left->edge_count; j++) {
|
|
1515
|
+
if (left->edges[j].target != right->edges[j].target) return 0;
|
|
1516
|
+
if (!bytes_equal(left->edges[j].label, left->edges[j].label_length,
|
|
1517
|
+
right->edges[j].label, right->edges[j].label_length)) {
|
|
1518
|
+
return 0;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return 1;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1492
1525
|
int k_pattern_equal(k_pattern *a, k_pattern *b) {
|
|
1493
1526
|
if (a == b) return 1;
|
|
1494
1527
|
if (a == NULL || b == NULL) return 0;
|
|
@@ -1881,6 +1914,14 @@ unsigned char *k_encode_wire_as(k_pattern *pattern, k_value *value, size_t *out_
|
|
|
1881
1914
|
return bytes;
|
|
1882
1915
|
}
|
|
1883
1916
|
|
|
1917
|
+
unsigned char *k_encode_wire(k_value *value, size_t *out_length) {
|
|
1918
|
+
k_pattern *pattern = derive_pattern(value);
|
|
1919
|
+
if (pattern == NULL) return NULL;
|
|
1920
|
+
unsigned char *bytes = k_encode_wire_as(pattern, value, out_length);
|
|
1921
|
+
free_pattern(pattern);
|
|
1922
|
+
return bytes;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1884
1925
|
int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value) {
|
|
1885
1926
|
size_t length = 0;
|
|
1886
1927
|
unsigned char *bytes = k_encode_wire_as(pattern, value, &length);
|
|
@@ -104,11 +104,13 @@ k_wire_input k_read_wire_envelope(FILE *in, k_rt *rt);
|
|
|
104
104
|
k_wire_input k_decode_wire_envelope(const unsigned char *bytes, size_t length, k_rt *rt);
|
|
105
105
|
void k_wire_input_free(k_wire_input input);
|
|
106
106
|
int k_pattern_equal(k_pattern *a, k_pattern *b);
|
|
107
|
+
int k_pattern_compatible(k_pattern *a, k_pattern *b);
|
|
107
108
|
k_wire_prefix k_wire_prefix_for_pattern(k_pattern *pattern);
|
|
108
109
|
void k_wire_prefix_free(k_wire_prefix prefix);
|
|
109
110
|
k_value *k_decode_wire_value_with_prefix(const unsigned char *bytes, size_t length, k_wire_prefix *prefix, k_pattern *pattern, k_rt *rt);
|
|
110
111
|
unsigned char *k_encode_wire_as_with_prefix(k_wire_prefix *prefix, k_pattern *pattern, k_value *value, size_t *out_length);
|
|
111
112
|
unsigned char *k_encode_wire_as(k_pattern *pattern, k_value *value, size_t *out_length);
|
|
113
|
+
unsigned char *k_encode_wire(k_value *value, size_t *out_length);
|
|
112
114
|
int k_write_wire(FILE *out, k_value *value);
|
|
113
115
|
int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value);
|
|
114
116
|
|
|
@@ -1,20 +1,73 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
compileLibrary,
|
|
5
|
+
compileObjectBuffer,
|
|
6
|
+
decodeObject,
|
|
7
|
+
loadLibrary
|
|
8
|
+
} from "@fraczak/k/object.mjs";
|
|
4
9
|
|
|
5
10
|
export function readAll(stream) {
|
|
6
11
|
return new Promise((resolve, reject) => {
|
|
7
12
|
const chunks = [];
|
|
8
|
-
stream.on("data", (chunk) =>
|
|
13
|
+
stream.on("data", (chunk) => {
|
|
14
|
+
chunks.push(
|
|
15
|
+
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
16
|
+
);
|
|
17
|
+
});
|
|
9
18
|
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
10
19
|
stream.on("error", reject);
|
|
11
20
|
});
|
|
12
21
|
}
|
|
13
22
|
|
|
23
|
+
export function loadLibraryFromPath(libPath) {
|
|
24
|
+
const ext = path.extname(libPath).toLowerCase();
|
|
25
|
+
if (ext === ".klib" || ext === ".ko") {
|
|
26
|
+
return loadLibrary(decodeObject(fs.readFileSync(libPath)));
|
|
27
|
+
}
|
|
28
|
+
if (ext === ".k") {
|
|
29
|
+
let depLibs = [];
|
|
30
|
+
const baseName = path.basename(libPath);
|
|
31
|
+
if (baseName === "poly.k") {
|
|
32
|
+
const dir = path.dirname(libPath);
|
|
33
|
+
const arithPath = path.join(dir, "arithmetics.k");
|
|
34
|
+
if (fs.existsSync(arithPath)) {
|
|
35
|
+
const arithLib = loadLibrary(
|
|
36
|
+
compileLibrary(fs.readFileSync(arithPath, "utf8"), {
|
|
37
|
+
source: arithPath
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
depLibs.push(arithLib);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const rawSource = fs.readFileSync(libPath, "utf8");
|
|
44
|
+
if (depLibs.length > 0) {
|
|
45
|
+
const neededExports = [
|
|
46
|
+
"0", "int", "inc", "dec", "nat", "zero_int?", "neg?", "GT"
|
|
47
|
+
];
|
|
48
|
+
const preamble = buildExportPreamble(neededExports, depLibs);
|
|
49
|
+
const compiled = compileLibrary(preamble + rawSource, {
|
|
50
|
+
source: libPath,
|
|
51
|
+
libraries: depLibs
|
|
52
|
+
});
|
|
53
|
+
const mergedLib = loadLibrary(compiled);
|
|
54
|
+
for (const dep of depLibs) {
|
|
55
|
+
for (const [k, v] of Object.entries(dep.relAlias || {})) {
|
|
56
|
+
if (!mergedLib.relAlias[k]) mergedLib.relAlias[k] = v;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return mergedLib;
|
|
60
|
+
}
|
|
61
|
+
return loadLibrary(compileLibrary(rawSource, { source: libPath }));
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`Unsupported library extension: ${ext}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
14
66
|
export function parseCompileOptions(args) {
|
|
15
67
|
const libraries = [];
|
|
16
68
|
const exportSpecs = [];
|
|
17
69
|
let loadedLibrary = false;
|
|
70
|
+
let usePKIR = false;
|
|
18
71
|
|
|
19
72
|
while (args.length > 0) {
|
|
20
73
|
if (args[0] === "--") {
|
|
@@ -22,22 +75,26 @@ export function parseCompileOptions(args) {
|
|
|
22
75
|
break;
|
|
23
76
|
}
|
|
24
77
|
if (args[0] === "--lib") {
|
|
25
|
-
if (loadedLibrary)
|
|
78
|
+
if (loadedLibrary) {
|
|
79
|
+
throw new Error("--lib may only be specified once");
|
|
80
|
+
}
|
|
26
81
|
args.shift();
|
|
27
82
|
const libPath = args.shift();
|
|
28
83
|
if (!libPath) throw new Error("--lib requires a file argument");
|
|
29
|
-
|
|
30
|
-
if (ext === ".k") {
|
|
31
|
-
libraries.push(loadLibrary(compileLibrary(fs.readFileSync(libPath, "utf8"), { source: libPath })));
|
|
32
|
-
} else {
|
|
33
|
-
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
34
|
-
}
|
|
84
|
+
libraries.push(loadLibraryFromPath(libPath));
|
|
35
85
|
loadedLibrary = true;
|
|
36
86
|
} else if (args[0] === "--export") {
|
|
37
87
|
args.shift();
|
|
38
88
|
const spec = args.shift();
|
|
39
|
-
if (!spec)
|
|
89
|
+
if (!spec) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
"--export requires a spec argument (name or libname:localname)"
|
|
92
|
+
);
|
|
93
|
+
}
|
|
40
94
|
exportSpecs.push(spec);
|
|
95
|
+
} else if (args[0] === "--pkir") {
|
|
96
|
+
args.shift();
|
|
97
|
+
usePKIR = true;
|
|
41
98
|
} else if (args[0].startsWith("--")) {
|
|
42
99
|
break;
|
|
43
100
|
} else {
|
|
@@ -45,7 +102,7 @@ export function parseCompileOptions(args) {
|
|
|
45
102
|
}
|
|
46
103
|
}
|
|
47
104
|
|
|
48
|
-
return { libraries, exportSpecs };
|
|
105
|
+
return { libraries, exportSpecs, usePKIR };
|
|
49
106
|
}
|
|
50
107
|
|
|
51
108
|
export function fileInputType(filePath) {
|
|
@@ -53,6 +110,7 @@ export function fileInputType(filePath) {
|
|
|
53
110
|
if (ext === ".ko") return "ko";
|
|
54
111
|
if (ext === ".kvm") return "kvm";
|
|
55
112
|
if (ext === ".klib") return "klib";
|
|
113
|
+
if (ext === ".pkir") return "pkir";
|
|
56
114
|
return "source-file";
|
|
57
115
|
}
|
|
58
116
|
|
|
@@ -69,11 +127,15 @@ export function resolveProgramInput(args, { allowStdinSource = false } = {}) {
|
|
|
69
127
|
return { kind, path: inputArg, sourceName: inputArg };
|
|
70
128
|
}
|
|
71
129
|
|
|
72
|
-
if ([".k", ".ko", ".kvm", ".klib"].includes(ext)) {
|
|
130
|
+
if ([".k", ".ko", ".kvm", ".klib", ".pkir"].includes(ext)) {
|
|
73
131
|
throw new Error(`Input file not found: ${inputArg}`);
|
|
74
132
|
}
|
|
75
133
|
|
|
76
|
-
return {
|
|
134
|
+
return {
|
|
135
|
+
kind: "inline-source",
|
|
136
|
+
source: inputArg,
|
|
137
|
+
sourceName: "<inline>"
|
|
138
|
+
};
|
|
77
139
|
}
|
|
78
140
|
|
|
79
141
|
export function buildExportPreamble(exports = [], libraries = []) {
|
|
@@ -94,9 +156,15 @@ export function buildExportPreamble(exports = [], libraries = []) {
|
|
|
94
156
|
}
|
|
95
157
|
const lines = [];
|
|
96
158
|
for (const spec of exports) {
|
|
97
|
-
const [libName, localName] = spec.includes(":")
|
|
159
|
+
const [libName, localName] = spec.includes(":")
|
|
160
|
+
? spec.split(":", 2)
|
|
161
|
+
: [spec, spec];
|
|
98
162
|
const hash = aliasMap[libName];
|
|
99
|
-
if (!hash)
|
|
163
|
+
if (!hash) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`--export: '${libName}' not found in loaded libraries`
|
|
166
|
+
);
|
|
167
|
+
}
|
|
100
168
|
const body = hash.startsWith("@") ? hash.slice(1) : hash;
|
|
101
169
|
lines.push(`${localName} = @${body};`);
|
|
102
170
|
}
|
|
@@ -116,7 +184,9 @@ export async function compileProgramInputToObject(
|
|
|
116
184
|
});
|
|
117
185
|
object = decodeObject(objBuffer);
|
|
118
186
|
} else if (input.kind === "stdin-source") {
|
|
119
|
-
if (!stdin)
|
|
187
|
+
if (!stdin) {
|
|
188
|
+
throw new Error("stdin source requested without a stdin stream");
|
|
189
|
+
}
|
|
120
190
|
const source = (await readAll(stdin)).toString("utf8");
|
|
121
191
|
const preamble = buildExportPreamble(exportSpecs, libraries);
|
|
122
192
|
const objBuffer = compileObjectBuffer(preamble + source, {
|
|
@@ -135,7 +205,15 @@ export async function compileProgramInputToObject(
|
|
|
135
205
|
} else if (input.kind === "ko" || input.kind === "klib") {
|
|
136
206
|
object = decodeObject(fs.readFileSync(input.path));
|
|
137
207
|
} else if (input.kind === "kvm") {
|
|
138
|
-
throw new Error(
|
|
208
|
+
throw new Error(
|
|
209
|
+
"Input is already lowered to .kvm bytecode; use " +
|
|
210
|
+
"compileLLVMArtifactFromKVM or compileKVMToExecutable."
|
|
211
|
+
);
|
|
212
|
+
} else if (input.kind === "pkir") {
|
|
213
|
+
throw new Error(
|
|
214
|
+
"Input is already lowered to .pkir IR; use " +
|
|
215
|
+
"compileLLVMArtifactFromPKIR or compilePKIRToExecutable."
|
|
216
|
+
);
|
|
139
217
|
} else {
|
|
140
218
|
throw new Error(`Unsupported input kind: ${input.kind}`);
|
|
141
219
|
}
|