@fraczak/k 7.0.2 → 7.2.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 +75 -0
- package/DOCS/CODECS.md +37 -2
- package/DOCS/CODEC_ARCHITECTURE.md +12 -5
- package/DOCS/FILE_FORMATS.md +238 -15
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- package/DOCS/REPL.md +11 -3
- package/DOCS/WEB_REPL_REQUIREMENTS.md +1 -1
- package/JOURNAL.md +288 -0
- package/README.md +12 -3
- package/Value.mjs +28 -3
- package/access-table.mjs +343 -0
- package/backend-api.mjs +23 -1
- 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 +8 -0
- package/backends/llvm/runtime/krt.h +1 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +128 -11
- package/backends/llvm/src/kvm2llvm.mjs +10 -5
- package/backends/llvm/src/llvm.mjs +121 -9
- 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 +24 -3
- package/backends/llvm/tests/test-llvm.mjs +22 -1
- 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 +130 -23
- package/browser/repl-browser.mjs +16 -5
- package/browser/tree-view.mjs +47 -8
- package/browser/vfs-data.mjs +6 -5
- package/codecs/README.md +1 -0
- package/codecs/djson.mjs +270 -0
- package/codecs/int.mjs +2 -1
- package/codecs/json.mjs +7 -2
- package/codecs/k-parse.mjs +36 -9
- package/codecs/k-print.mjs +26 -6
- package/codecs/runtime/codec-sdk.mjs +24 -6
- package/codecs/runtime/pattern-json.mjs +80 -1
- package/codecs/runtime/prefix-codec.mjs +629 -67
- package/codecs/runtime/show-value.mjs +93 -51
- package/codecs/runtime/unicode-string.mjs +29 -6
- package/codecs/show.mjs +24 -6
- package/codecs/utf16.mjs +2 -1
- package/k.mjs +56 -16
- package/kir.mjs +58 -19
- 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 +11 -3
- package/pkir.mjs +772 -0
- package/repl.mjs +92 -23
- package/scripts/build-repl-html.mjs +22 -3
- package/scripts/perf-ieee.mjs +264 -13
- package/scripts/perf-int.mjs +315 -22
- package/scripts/perf-multiplication.mjs +58 -7
- package/scripts/perf-poly.mjs +306 -24
- package/scripts/run-tests.mjs +3 -0
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-codecs.mjs +168 -0
- package/tests/test-pkir.mjs +530 -0
- package/tests/test-polymorphic-tdd.mjs +196 -0
- package/tests/test-repl-html.mjs +16 -2
- package/tests/test-repl.mjs +63 -1
- package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
|
@@ -9,26 +9,44 @@ import {
|
|
|
9
9
|
} from "../src/cli.mjs";
|
|
10
10
|
import {
|
|
11
11
|
compileObjectToLLVM,
|
|
12
|
-
compileLLVMArtifactFromKVM
|
|
12
|
+
compileLLVMArtifactFromKVM,
|
|
13
|
+
compileLLVMArtifactFromPKIR
|
|
13
14
|
} from "../src/llvm.mjs";
|
|
14
15
|
import { inputPatternForObjectRelation } from "../src/executable.mjs";
|
|
15
16
|
|
|
16
|
-
function
|
|
17
|
+
function helpText() {
|
|
17
18
|
const prog = argv[1] || "k-llvm-compile.mjs";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
return [
|
|
20
|
+
"Compile a k .ko/.klib object, source, .kvm, or .pkir into LLVM IR.",
|
|
21
|
+
"",
|
|
22
|
+
`Usage: ${prog} [options]`,
|
|
23
|
+
" [source-snippet | input-file [output.ll]]",
|
|
24
|
+
"",
|
|
25
|
+
"Arguments:",
|
|
26
|
+
" source-snippet Inline k source snippet (in the style of k.mjs).",
|
|
27
|
+
" input-file Source .k, .ko, .klib, .kvm, or .pkir file.",
|
|
28
|
+
" Reads UTF-8 source from stdin when omitted.",
|
|
29
|
+
" output.ll Output LLVM IR path (default: stdout).",
|
|
30
|
+
"",
|
|
31
|
+
"Options:",
|
|
32
|
+
" -o, --output path Specify output file path explicitly.",
|
|
33
|
+
" --pkir Lower via Polymorphic K IR (PKIR).",
|
|
34
|
+
" --retype rel Relation to compile (default: object main).",
|
|
35
|
+
" --lib file Load one .klib or .k library before compiling.",
|
|
36
|
+
" --export spec Export library alias into scope. Repeatable.",
|
|
37
|
+
" spec is 'name' or 'libname:localname'.",
|
|
38
|
+
" -h, --help Show this help.",
|
|
39
|
+
"",
|
|
40
|
+
"Examples:",
|
|
41
|
+
` ${prog} 'x = |x; x x' program.ll`,
|
|
42
|
+
` ${prog} program.k -o program.ll`,
|
|
43
|
+
` ${prog} --pkir program.k -o program.ll`,
|
|
44
|
+
` ${prog} --lib core.klib --export add program.k program.ll`
|
|
45
|
+
].join("\n");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function usage(stream = console.error) {
|
|
49
|
+
stream(helpText());
|
|
32
50
|
}
|
|
33
51
|
|
|
34
52
|
try {
|
|
@@ -39,29 +57,49 @@ try {
|
|
|
39
57
|
}
|
|
40
58
|
|
|
41
59
|
let explicitOutput = null;
|
|
60
|
+
let relation = null;
|
|
42
61
|
const remainingArgs = [];
|
|
43
62
|
while (args.length > 0) {
|
|
44
63
|
const arg = args[0];
|
|
45
64
|
if (arg === "-o" || arg === "--output") {
|
|
46
65
|
args.shift();
|
|
47
66
|
explicitOutput = args.shift();
|
|
48
|
-
if (!explicitOutput)
|
|
67
|
+
if (!explicitOutput) {
|
|
68
|
+
throw new Error("-o/--output requires a file argument");
|
|
69
|
+
}
|
|
70
|
+
} else if (arg === "--retype") {
|
|
71
|
+
args.shift();
|
|
72
|
+
relation = args.shift();
|
|
73
|
+
if (!relation) {
|
|
74
|
+
throw new Error("--retype requires a relation name");
|
|
75
|
+
}
|
|
49
76
|
} else {
|
|
50
77
|
remainingArgs.push(args.shift());
|
|
51
78
|
}
|
|
52
79
|
}
|
|
53
80
|
|
|
54
|
-
const { libraries, exportSpecs } =
|
|
55
|
-
|
|
81
|
+
const { libraries, exportSpecs, usePKIR } =
|
|
82
|
+
parseCompileOptions(remainingArgs);
|
|
83
|
+
const input = resolveProgramInput(remainingArgs, {
|
|
84
|
+
allowStdinSource: true
|
|
85
|
+
});
|
|
56
86
|
const positionalOutput = remainingArgs.shift();
|
|
57
87
|
if (remainingArgs.length > 0) throw new Error("Too many arguments");
|
|
58
88
|
|
|
59
89
|
const outputPath = explicitOutput || positionalOutput || null;
|
|
60
90
|
|
|
61
91
|
let llvm;
|
|
62
|
-
if (input.kind === "
|
|
92
|
+
if (input.kind === "pkir") {
|
|
93
|
+
const pkir = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
94
|
+
const entry = relation || pkir.entry || pkir.name || "__main__";
|
|
95
|
+
llvm = compileLLVMArtifactFromPKIR(pkir, {
|
|
96
|
+
relation: entry,
|
|
97
|
+
inputPattern: pkir.inputPattern,
|
|
98
|
+
outputPattern: pkir.outputPattern
|
|
99
|
+
});
|
|
100
|
+
} else if (input.kind === "kvm") {
|
|
63
101
|
const kvm = JSON.parse(fs.readFileSync(input.path, "utf8"));
|
|
64
|
-
const entry = kvm.entry || kvm.relation || "__main__";
|
|
102
|
+
const entry = relation || kvm.entry || kvm.relation || "__main__";
|
|
65
103
|
const mainFunc = kvm.functions?.[entry];
|
|
66
104
|
const artifact = compileLLVMArtifactFromKVM(kvm, {
|
|
67
105
|
relation: entry,
|
|
@@ -76,9 +114,11 @@ try {
|
|
|
76
114
|
stdin
|
|
77
115
|
});
|
|
78
116
|
|
|
117
|
+
const targetRel = relation || object.main;
|
|
79
118
|
const res = compileObjectToLLVM(object, {
|
|
80
|
-
relation:
|
|
81
|
-
inputPattern: inputPatternForObjectRelation(object,
|
|
119
|
+
relation: targetRel,
|
|
120
|
+
inputPattern: inputPatternForObjectRelation(object, targetRel),
|
|
121
|
+
usePKIR
|
|
82
122
|
});
|
|
83
123
|
llvm = res.llvm;
|
|
84
124
|
}
|
|
@@ -90,6 +130,5 @@ try {
|
|
|
90
130
|
}
|
|
91
131
|
} catch (error) {
|
|
92
132
|
console.error(error.stack || error.message || String(error));
|
|
93
|
-
usage();
|
|
94
133
|
exit(1);
|
|
95
134
|
}
|
|
@@ -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
|
+
});
|
|
@@ -1914,6 +1914,14 @@ unsigned char *k_encode_wire_as(k_pattern *pattern, k_value *value, size_t *out_
|
|
|
1914
1914
|
return bytes;
|
|
1915
1915
|
}
|
|
1916
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
|
+
|
|
1917
1925
|
int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value) {
|
|
1918
1926
|
size_t length = 0;
|
|
1919
1927
|
unsigned char *bytes = k_encode_wire_as(pattern, value, &length);
|
|
@@ -110,6 +110,7 @@ void k_wire_prefix_free(k_wire_prefix prefix);
|
|
|
110
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);
|
|
111
111
|
unsigned char *k_encode_wire_as_with_prefix(k_wire_prefix *prefix, k_pattern *pattern, k_value *value, size_t *out_length);
|
|
112
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);
|
|
113
114
|
int k_write_wire(FILE *out, k_value *value);
|
|
114
115
|
int k_write_wire_as(FILE *out, k_pattern *pattern, k_value *value);
|
|
115
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
|
}
|