@fraczak/k 6.6.0 → 6.8.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/DOCS/REPL.md +23 -36
- package/TODO.md +112 -0
- package/backends/arm64/README.md +0 -1
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -8
- package/backends/llvm/README.md +13 -11
- package/backends/llvm/bin/k-llvm-build.mjs +40 -38
- package/backends/llvm/bin/k-llvm-compile.mjs +42 -32
- package/backends/llvm/src/cli.mjs +153 -0
- package/backends/llvm/src/executable.mjs +1 -1
- package/backends/llvm/src/kvm2llvm.mjs +11 -1
- package/backends/llvm/tests/integration.sh +18 -22
- package/backends/wasm/bin/k-wasm-compile.mjs +19 -4
- package/browser/repl-browser.mjs +146 -94
- package/browser/vfs-data.mjs +2 -0
- package/package.json +1 -1
- package/repl-codecs.mjs +26 -5
- package/repl.mjs +214 -148
- package/scripts/build-repl-html.mjs +213 -72
- package/tests/test-repl-html.mjs +84 -6
- package/tests/test-repl.mjs +90 -40
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { compileLibrary, compileObjectBuffer, decodeObject, loadLibrary } from "@fraczak/k/object.mjs";
|
|
4
|
+
|
|
5
|
+
export function readAll(stream) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const chunks = [];
|
|
8
|
+
stream.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
|
|
9
|
+
stream.on("end", () => resolve(Buffer.concat(chunks)));
|
|
10
|
+
stream.on("error", reject);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function parseCompileOptions(args) {
|
|
15
|
+
const libraries = [];
|
|
16
|
+
const exportSpecs = [];
|
|
17
|
+
let loadedLibrary = false;
|
|
18
|
+
|
|
19
|
+
while (args.length > 0) {
|
|
20
|
+
if (args[0] === "--") {
|
|
21
|
+
args.shift();
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
if (args[0] === "--lib") {
|
|
25
|
+
if (loadedLibrary) throw new Error("--lib may only be specified once");
|
|
26
|
+
args.shift();
|
|
27
|
+
const libPath = args.shift();
|
|
28
|
+
if (!libPath) throw new Error("--lib requires a file argument");
|
|
29
|
+
const ext = path.extname(libPath).toLowerCase();
|
|
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
|
+
}
|
|
35
|
+
loadedLibrary = true;
|
|
36
|
+
} else if (args[0] === "--export") {
|
|
37
|
+
args.shift();
|
|
38
|
+
const spec = args.shift();
|
|
39
|
+
if (!spec) throw new Error("--export requires a spec argument (name or libname:localname)");
|
|
40
|
+
exportSpecs.push(spec);
|
|
41
|
+
} else if (args[0].startsWith("--")) {
|
|
42
|
+
break;
|
|
43
|
+
} else {
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { libraries, exportSpecs };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function fileInputType(filePath) {
|
|
52
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
53
|
+
if (ext === ".ko") return "ko";
|
|
54
|
+
if (ext === ".kvm") return "kvm";
|
|
55
|
+
if (ext === ".klib") return "klib";
|
|
56
|
+
return "source-file";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function resolveProgramInput(args, { allowStdinSource = false } = {}) {
|
|
60
|
+
const inputArg = args.shift() ?? null;
|
|
61
|
+
if (inputArg == null) {
|
|
62
|
+
if (!allowStdinSource) throw new Error("Missing script argument");
|
|
63
|
+
return { kind: "stdin-source", sourceName: "<stdin>" };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const ext = path.extname(inputArg).toLowerCase();
|
|
67
|
+
if (fs.existsSync(inputArg)) {
|
|
68
|
+
const kind = fileInputType(inputArg);
|
|
69
|
+
return { kind, path: inputArg, sourceName: inputArg };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if ([".k", ".ko", ".kvm", ".klib"].includes(ext)) {
|
|
73
|
+
throw new Error(`Input file not found: ${inputArg}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return { kind: "inline-source", source: inputArg, sourceName: "<inline>" };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function buildExportPreamble(exports = [], libraries = []) {
|
|
80
|
+
if (exports.length === 0) return "";
|
|
81
|
+
const aliasMap = {};
|
|
82
|
+
for (const lib of libraries) {
|
|
83
|
+
for (const [name, hash] of Object.entries(lib.relAlias || {})) {
|
|
84
|
+
if (name !== "__main__") aliasMap[name] = hash;
|
|
85
|
+
}
|
|
86
|
+
for (const [hash, entry] of Object.entries(lib.meta || {})) {
|
|
87
|
+
if (entry?.type !== "rel") continue;
|
|
88
|
+
for (const origin of entry?.origins || []) {
|
|
89
|
+
if (origin?.name && origin.name !== "__main__") {
|
|
90
|
+
aliasMap[origin.name] = hash;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const lines = [];
|
|
96
|
+
for (const spec of exports) {
|
|
97
|
+
const [libName, localName] = spec.includes(":") ? spec.split(":", 2) : [spec, spec];
|
|
98
|
+
const hash = aliasMap[libName];
|
|
99
|
+
if (!hash) throw new Error(`--export: '${libName}' not found in loaded libraries`);
|
|
100
|
+
const body = hash.startsWith("@") ? hash.slice(1) : hash;
|
|
101
|
+
lines.push(`${localName} = @${body};`);
|
|
102
|
+
}
|
|
103
|
+
return lines.join("\n") + "\n";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function compileProgramInputToObject(
|
|
107
|
+
input,
|
|
108
|
+
{ libraries = [], exportSpecs = [], stdin = null } = {}
|
|
109
|
+
) {
|
|
110
|
+
let object;
|
|
111
|
+
if (input.kind === "inline-source") {
|
|
112
|
+
const preamble = buildExportPreamble(exportSpecs, libraries);
|
|
113
|
+
const objBuffer = compileObjectBuffer(preamble + input.source, {
|
|
114
|
+
libraries,
|
|
115
|
+
source: input.sourceName
|
|
116
|
+
});
|
|
117
|
+
object = decodeObject(objBuffer);
|
|
118
|
+
} else if (input.kind === "stdin-source") {
|
|
119
|
+
if (!stdin) throw new Error("stdin source requested without a stdin stream");
|
|
120
|
+
const source = (await readAll(stdin)).toString("utf8");
|
|
121
|
+
const preamble = buildExportPreamble(exportSpecs, libraries);
|
|
122
|
+
const objBuffer = compileObjectBuffer(preamble + source, {
|
|
123
|
+
libraries,
|
|
124
|
+
source: input.sourceName
|
|
125
|
+
});
|
|
126
|
+
object = decodeObject(objBuffer);
|
|
127
|
+
} else if (input.kind === "source-file") {
|
|
128
|
+
const source = fs.readFileSync(input.path, "utf8");
|
|
129
|
+
const preamble = buildExportPreamble(exportSpecs, libraries);
|
|
130
|
+
const objBuffer = compileObjectBuffer(preamble + source, {
|
|
131
|
+
libraries,
|
|
132
|
+
source: input.sourceName
|
|
133
|
+
});
|
|
134
|
+
object = decodeObject(objBuffer);
|
|
135
|
+
} else if (input.kind === "ko" || input.kind === "klib") {
|
|
136
|
+
object = decodeObject(fs.readFileSync(input.path));
|
|
137
|
+
} else if (input.kind === "kvm") {
|
|
138
|
+
throw new Error("Compiling directly from .kvm to LLVM is not yet supported; use .k or .ko input.");
|
|
139
|
+
} else {
|
|
140
|
+
throw new Error(`Unsupported input kind: ${input.kind}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return object;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default {
|
|
147
|
+
buildExportPreamble,
|
|
148
|
+
compileProgramInputToObject,
|
|
149
|
+
fileInputType,
|
|
150
|
+
parseCompileOptions,
|
|
151
|
+
readAll,
|
|
152
|
+
resolveProgramInput
|
|
153
|
+
};
|
|
@@ -757,6 +757,7 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
757
757
|
const fieldValues = [];
|
|
758
758
|
const pSrc = getReg(inst.src);
|
|
759
759
|
const pPattern = regPatterns.get(cleanReg(inst.src)) || (cleanReg(inst.src) === "in" ? regPatterns.get("in") : null);
|
|
760
|
+
let productFailed = false;
|
|
760
761
|
|
|
761
762
|
for (let bIdx = 0; bIdx < N; bIdx++) {
|
|
762
763
|
const branch = inst.branches[bIdx];
|
|
@@ -783,12 +784,21 @@ function lowerKVMFunction(kvmFunc, symbol, funcName, moduleCtx, linkage = "", op
|
|
|
783
784
|
nextBlock: branchDone
|
|
784
785
|
});
|
|
785
786
|
|
|
787
|
+
if (!branchRegValues.has(cleanReg(branchRes))) {
|
|
788
|
+
productFailed = true;
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
|
|
786
792
|
ctx.lines.push(`${branchDone}:`);
|
|
787
793
|
ctx.currentBlock = branchDone;
|
|
788
|
-
const fVal = branchRegValues.get(cleanReg(branchRes))
|
|
794
|
+
const fVal = branchRegValues.get(cleanReg(branchRes));
|
|
789
795
|
fieldValues.push({ label: branch.label, val: fVal });
|
|
790
796
|
}
|
|
791
797
|
|
|
798
|
+
if (productFailed) {
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
|
|
792
802
|
const requestedSize = K_PRODUCT_HEADER_SIZE + (K_FIELD_SIZE * N);
|
|
793
803
|
const product = lowerRawAlloc(ctx, requestedSize, alignArenaSize(requestedSize));
|
|
794
804
|
nullCheck(ctx, product, failTarget);
|
|
@@ -10,8 +10,7 @@ command -v clang >/dev/null || {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
node ../../objects/compile.mjs '()' "$TMP_DIR/id.ko"
|
|
13
|
-
|
|
14
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/input.pattern.json" "$TMP_DIR/id.ko" "$TMP_DIR/id.ll"
|
|
13
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/id.ko" "$TMP_DIR/id.ll"
|
|
15
14
|
grep -q '@k_llvm_metadata' "$TMP_DIR/id.ll"
|
|
16
15
|
grep -q 'define %k_result @k_main' "$TMP_DIR/id.ll"
|
|
17
16
|
|
|
@@ -19,32 +18,28 @@ clang -Wno-override-module -Iruntime runtime/krt.c tests/identity-driver.c "$TMP
|
|
|
19
18
|
"$TMP_DIR/id"
|
|
20
19
|
|
|
21
20
|
node ../../objects/compile.mjs '.x' "$TMP_DIR/projection.ko"
|
|
22
|
-
|
|
23
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/projection.pattern.json" "$TMP_DIR/projection.ko" "$TMP_DIR/projection.ll"
|
|
21
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/projection.ko" "$TMP_DIR/projection.ll"
|
|
24
22
|
grep -q 'product_fields_ptr' "$TMP_DIR/projection.ll"
|
|
25
23
|
! grep -q 'call ptr @k_product_get_at' "$TMP_DIR/projection.ll"
|
|
26
24
|
clang -Wno-override-module -Iruntime runtime/krt.c tests/projection-driver.c "$TMP_DIR/projection.ll" -o "$TMP_DIR/projection"
|
|
27
25
|
"$TMP_DIR/projection"
|
|
28
26
|
|
|
29
27
|
node ../../objects/compile.mjs '{ .x fieldA, .y fieldB }' "$TMP_DIR/product.ko"
|
|
30
|
-
|
|
31
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/product.pattern.json" "$TMP_DIR/product.ko" "$TMP_DIR/product.ll"
|
|
28
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/product.ko" "$TMP_DIR/product.ll"
|
|
32
29
|
grep -q 'store i32 1, ptr %i32_slot' "$TMP_DIR/product.ll"
|
|
33
30
|
grep -q 'store ptr %product_fields' "$TMP_DIR/product.ll"
|
|
34
31
|
clang -Wno-override-module -Iruntime runtime/krt.c tests/product-driver.c "$TMP_DIR/product.ll" -o "$TMP_DIR/product"
|
|
35
32
|
"$TMP_DIR/product"
|
|
36
33
|
|
|
37
34
|
node ../../objects/compile.mjs '|tag' "$TMP_DIR/variant.ko"
|
|
38
|
-
|
|
39
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/variant.pattern.json" "$TMP_DIR/variant.ko" "$TMP_DIR/variant.ll"
|
|
35
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/variant.ko" "$TMP_DIR/variant.ll"
|
|
40
36
|
grep -q 'call ptr @k_rt_alloc' "$TMP_DIR/variant.ll"
|
|
41
37
|
grep -q 'store i32 2, ptr %i32_slot' "$TMP_DIR/variant.ll"
|
|
42
38
|
clang -Wno-override-module -Iruntime runtime/krt.c tests/variant-driver.c "$TMP_DIR/variant.ll" -o "$TMP_DIR/variant"
|
|
43
39
|
"$TMP_DIR/variant"
|
|
44
40
|
|
|
45
41
|
node ../../objects/compile.mjs '/tag' "$TMP_DIR/variant-projection.ko"
|
|
46
|
-
|
|
47
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/variant-projection.pattern.json" "$TMP_DIR/variant-projection.ko" "$TMP_DIR/variant-projection.ll"
|
|
42
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/variant-projection.ko" "$TMP_DIR/variant-projection.ll"
|
|
48
43
|
grep -q 'tag_byte_ptr' "$TMP_DIR/variant-projection.ll"
|
|
49
44
|
grep -q 'payload_slot' "$TMP_DIR/variant-projection.ll"
|
|
50
45
|
! grep -q 'call i32 @k_variant_tag_matches' "$TMP_DIR/variant-projection.ll"
|
|
@@ -53,24 +48,21 @@ clang -Wno-override-module -Iruntime runtime/krt.c tests/variant-projection-driv
|
|
|
53
48
|
"$TMP_DIR/variant-projection"
|
|
54
49
|
|
|
55
50
|
node ../../objects/compile.mjs '(.x .y)' "$TMP_DIR/composition.ko"
|
|
56
|
-
|
|
57
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/composition.pattern.json" "$TMP_DIR/composition.ko" "$TMP_DIR/composition.ll"
|
|
51
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/composition.ko" "$TMP_DIR/composition.ll"
|
|
58
52
|
grep -q 'product_fields_ptr' "$TMP_DIR/composition.ll"
|
|
59
53
|
! grep -q 'call ptr @k_product_get_at' "$TMP_DIR/composition.ll"
|
|
60
54
|
clang -Wno-override-module -Iruntime runtime/krt.c tests/composition-driver.c "$TMP_DIR/composition.ll" -o "$TMP_DIR/composition"
|
|
61
55
|
"$TMP_DIR/composition"
|
|
62
56
|
|
|
63
57
|
node ../../objects/compile.mjs 'pick = .x; {.a pick left, .b pick right}' "$TMP_DIR/relation.ko"
|
|
64
|
-
|
|
65
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/relation.pattern.json" "$TMP_DIR/relation.ko" "$TMP_DIR/relation.ll"
|
|
58
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/relation.ko" "$TMP_DIR/relation.ll"
|
|
66
59
|
grep -q 'define internal %k_result @k_rel_pick' "$TMP_DIR/relation.ll"
|
|
67
60
|
grep -q 'call %k_result @k_rel_pick' "$TMP_DIR/relation.ll"
|
|
68
61
|
clang -Wno-override-module -Iruntime runtime/krt.c tests/relation-driver.c "$TMP_DIR/relation.ll" -o "$TMP_DIR/relation"
|
|
69
62
|
"$TMP_DIR/relation"
|
|
70
63
|
|
|
71
64
|
node ../../objects/compile.mjs '< /x |left, /y |right >' "$TMP_DIR/union.ko"
|
|
72
|
-
|
|
73
|
-
node ./bin/k-llvm-compile.mjs --input-pattern "$TMP_DIR/union.pattern.json" "$TMP_DIR/union.ko" "$TMP_DIR/union.ll"
|
|
65
|
+
node ./bin/k-llvm-compile.mjs "$TMP_DIR/union.ko" "$TMP_DIR/union.ll"
|
|
74
66
|
grep -q 'define internal %k_result @k_union_arm_0' "$TMP_DIR/union.ll"
|
|
75
67
|
grep -q 'call %k_result @k_union_arm_1' "$TMP_DIR/union.ll"
|
|
76
68
|
clang -Wno-override-module -Iruntime runtime/krt.c tests/union-driver.c "$TMP_DIR/union.ll" -o "$TMP_DIR/union"
|
|
@@ -94,8 +86,7 @@ node ../../objects/compile.mjs '{ .x fieldA, .y fieldB }' "$TMP_DIR/run-product.
|
|
|
94
86
|
printf '{"x":"left","y":"right"}' > "$TMP_DIR/run-product-input.kv"
|
|
95
87
|
node ./bin/k-llvm-run.mjs "$TMP_DIR/run-product.ko" "$TMP_DIR/run-product-input.kv" | grep -qx '{"fieldA":"left","fieldB":"right"}'
|
|
96
88
|
|
|
97
|
-
|
|
98
|
-
node ./bin/k-llvm-build.mjs --input-pattern "$TMP_DIR/run-envelope.pattern.json" "$TMP_DIR/run.ko" "$TMP_DIR/run-exe"
|
|
89
|
+
node ./bin/k-llvm-build.mjs '?{<{}=X0 left, ...> x, <X0 right, ...> y} .x' -o "$TMP_DIR/run-exe"
|
|
99
90
|
printf '{"x":"left","y":"right"}' \
|
|
100
91
|
| node ../../codecs/k-parse.mjs \
|
|
101
92
|
| "$TMP_DIR/run-exe" \
|
|
@@ -104,15 +95,13 @@ printf '{"x":"left","y":"right"}' \
|
|
|
104
95
|
|
|
105
96
|
printf '{"x":"left"}' | node ../../codecs/k-parse.mjs | "$TMP_DIR/run-exe" && exit 1 || test "$?" -eq 5
|
|
106
97
|
|
|
107
|
-
|
|
108
|
-
node ./bin/k-llvm-build.mjs --input-pattern "$TMP_DIR/identity-variant.pattern.json" "$TMP_DIR/id.ko" "$TMP_DIR/id-exe"
|
|
98
|
+
node ./bin/k-llvm-build.mjs '?<{} left, {} right, ...>' -o "$TMP_DIR/id-exe"
|
|
109
99
|
node --input-type=module -e "import { stdout } from 'node:process'; import { encodeToWire } from '../../codecs/runtime/prefix-codec.mjs'; import { Value } from '../../Value.mjs'; const pattern = [[\"open-union\",[[\"left\",1],[\"right\",1]]],[\"closed-product\",[]]]; stdout.write(encodeToWire(Value.variant(\"left\", Value.product({}), pattern), pattern));" \
|
|
110
100
|
| "$TMP_DIR/id-exe" \
|
|
111
101
|
| node --input-type=module -e "import { stdin } from 'node:process'; import { decodeWire } from '../../codecs/runtime/prefix-codec.mjs'; const chunks = []; stdin.on('data', (chunk) => chunks.push(chunk)); stdin.on('end', () => console.log(JSON.stringify(decodeWire(Buffer.concat(chunks)).pattern)));" \
|
|
112
102
|
| grep -Fqx '[["open-union",[["left",1],["right",1]]],["closed-product",[]]]'
|
|
113
103
|
|
|
114
|
-
|
|
115
|
-
node ./bin/k-llvm-build.mjs --input-pattern "$TMP_DIR/identity-int.pattern.json" "$TMP_DIR/id.ko" "$TMP_DIR/id-int-exe"
|
|
104
|
+
node ./bin/k-llvm-build.mjs '$bits = < {} _, bits 0, bits 1 >; $int = < bits "+", bits "-" >; $int' -o "$TMP_DIR/id-int-exe"
|
|
116
105
|
printf '2' \
|
|
117
106
|
| node ../../codecs/int.mjs --parse \
|
|
118
107
|
| "$TMP_DIR/id-int-exe" \
|
|
@@ -121,6 +110,13 @@ printf '2' \
|
|
|
121
110
|
|
|
122
111
|
node --input-type=module -e "import assert from 'node:assert/strict'; import { spawn } from 'node:child_process'; import { encodeToWire, decodeWire } from '../../codecs/runtime/prefix-codec.mjs'; import { parse, INT_PATTERN } from '../../codecs/int.mjs'; const input = encodeToWire(parse('2'), INT_PATTERN); const header = Buffer.alloc(4); header.writeUInt32BE(input.length); const child = spawn(process.argv[1], ['--server']); const chunks = []; const stderr = []; child.stdout.on('data', (chunk) => chunks.push(chunk)); child.stderr.on('data', (chunk) => stderr.push(chunk)); child.stdin.end(Buffer.concat([header, input])); const status = await new Promise((resolve, reject) => { child.on('error', reject); child.on('close', resolve); }); assert.equal(status, 0, Buffer.concat(stderr).toString('utf8')); const output = Buffer.concat(chunks); const length = output.readUInt32BE(0); assert.equal(output.length, 4 + length); assert.deepEqual(decodeWire(output.subarray(4)).value.toJSON(), parse('2').toJSON());" "$TMP_DIR/id-int-exe"
|
|
123
112
|
|
|
113
|
+
node ./bin/k-llvm-build.mjs --lib ../../Examples/arithmetics.k --export times 's = {()x, ()y} times; s' -o "$TMP_DIR/square-exe"
|
|
114
|
+
printf '987654321' \
|
|
115
|
+
| node ../../codecs/int.mjs --parse \
|
|
116
|
+
| "$TMP_DIR/square-exe" \
|
|
117
|
+
| node ../../codecs/int.mjs --print \
|
|
118
|
+
| grep -qx '975461057789971041'
|
|
119
|
+
|
|
124
120
|
printf '{"x":"left","y":"right"}' \
|
|
125
121
|
| node ../../codecs/k-parse.mjs \
|
|
126
122
|
| node ./bin/k-llvm-jit.mjs --cache-dir "$TMP_DIR/jit-cache" "$TMP_DIR/run.ko" \
|
|
@@ -20,6 +20,7 @@ function usage(stream = console.error) {
|
|
|
20
20
|
stream(" wasm-file Output .wasm path. Writes the binary artifact to stdout when omitted.");
|
|
21
21
|
stream("");
|
|
22
22
|
stream("Options:");
|
|
23
|
+
stream(" -o, --output Specify output file path explicitly.");
|
|
23
24
|
stream(" --lib file Load one .klib dependency before compiling.");
|
|
24
25
|
stream(" --export spec Export a library alias into source scope. May be repeated.");
|
|
25
26
|
stream(" spec is 'name' or 'libname:localname'.");
|
|
@@ -33,10 +34,24 @@ try {
|
|
|
33
34
|
exit(0);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
let explicitOutput = null;
|
|
38
|
+
const remainingArgs = [];
|
|
39
|
+
while (args.length > 0) {
|
|
40
|
+
const arg = args[0];
|
|
41
|
+
if (arg === "-o" || arg === "--output") {
|
|
42
|
+
args.shift();
|
|
43
|
+
explicitOutput = args.shift();
|
|
44
|
+
if (!explicitOutput) throw new Error("-o/--output requires a file argument");
|
|
45
|
+
} else {
|
|
46
|
+
remainingArgs.push(args.shift());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
|
|
51
|
+
const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
|
|
52
|
+
const positionalOutput = remainingArgs.shift();
|
|
53
|
+
if (remainingArgs.length > 0) throw new Error("Too many arguments");
|
|
54
|
+
const outputPath = explicitOutput || positionalOutput || null;
|
|
40
55
|
const artifact = await compileProgramInput(input, { libraries, exportSpecs, stdin });
|
|
41
56
|
if (outputPath == null) {
|
|
42
57
|
stdout.write(artifact);
|