@fraczak/k 6.2.2 → 6.2.4

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