@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 CHANGED
@@ -33,29 +33,23 @@ complete.
33
33
  | Command | Meaning |
34
34
  | --- | --- |
35
35
  | `:help` | Show command summary |
36
- | `:type name = <...>` | Define a type alias |
37
36
  | `:type name` | Show the canonical definition of a type |
38
37
  | `:code name` | Alias for `:C name` |
39
- | `:rel name = expr` | Define a relation alias |
40
- | `:def name = expr` | Alias for `:rel` |
41
38
  | `:run expr` | Evaluate `expr` on the current value |
42
39
  | `:eval expr` | Alias for `:run` |
43
40
  | `:time expr` | Evaluate `expr` and report compilation and execution time |
44
- | `:timing [on\|off]` | Toggle compilation and execution timing reporting for all evaluations |
45
41
  | `:t name` | Show relation input/output filters |
46
42
  | `:d name` | Show relation definition |
47
43
  | `:C name` | Show canonical code definition |
48
44
  | `:codes` | List type aliases |
49
45
  | `:rels` | List relation aliases |
50
46
  | `:codec load name\|file` | Load a built-in codec (`int`, `utf8`, `json`, `ieee`, `unit`) or an ES module file |
51
- | `:codec define n t b` | Define an inline custom codec: `name type { parse: ..., print: ... }` |
52
47
  | `:codec unload name` | Unload a registered codec |
53
48
  | `:codec list` | List loaded REPL codecs (alias: `:codecs`) |
54
- | `:input type [codec]` | Read the next line as codec input for a type |
49
+ | `:input [<filter=(...)> [codec]]` | Read next input line using specified codec (opens dialog in browser) |
55
50
  | `:load [--no-alias] file` | Load `.k` source or `.klib` into the current state |
56
51
  | `:klib file` | Export the active relation closure as a `.klib` library |
57
52
  | `:ko file expr` | Export a `.ko` executable with `expr` as main |
58
- | `:val` | Print current value and JSON form |
59
53
  | `:reset` | Reset interpreter state |
60
54
  | `:quit` / `:exit` | Exit |
61
55
 
@@ -143,8 +137,8 @@ Tab completion covers:
143
137
  Type aliases also complete in `$name` position inside raw k input.
144
138
 
145
139
  For codec commands, completion covers `:codec load`, `:codec list`, file paths
146
- after `:codec load`, type names after `:input`, and loaded codec names
147
- after the input type.
140
+ after `:codec load`, type names or codec names after `:input`, and loaded codec names
141
+ after the input pattern or type.
148
142
 
149
143
  ## Loading
150
144
 
@@ -193,44 +187,39 @@ property-list pattern that can be canonicalized to a code hash, and that hash is
193
187
  recalculated by the REPL. A universal codec exports `universal = true` instead
194
188
  of `codes` or `patterns`.
195
189
 
196
- ### `:codec define name type { parse: ..., print: ... }`
197
-
198
- Registers a dynamic custom codec directly from the REPL:
199
-
200
- ```k
201
- > :type bool = <{} true, {} false>
202
- > :codec define yn bool ({ parse: (t) => Value.variant(t.trim() === 'yes' ? 'true' : 'false', Value.product({})), print: (v) => v.tag === 'true' ? 'YES' : 'NO' })
203
- ```
204
190
 
205
191
  ### `:codec unload name`
206
192
 
207
193
  Unloads a previously registered codec by name.
208
194
 
209
- ### `:input type [codec]`
195
+ ### `:input [<filter=(...)> [codec]]`
210
196
 
211
- Resolves `type` to a canonical code hash, selects the registered codec, and
212
- consumes the next line verbatim as codec input. `type` may be a type alias, a
213
- canonical code hash, or an inline type expression such as
214
- `<{} true, {} false>`. The parsed value is validated against that type before
215
- becoming the current value.
197
+ Resolves a pattern expression (e.g. `? {string a, float64 n}`, `? (...)`, `? <{} true, {} false>`), a type alias, or a canonical code hash, selects the registered codec, and consumes the next line verbatim as codec input.
216
198
 
217
- ## Timing and Profiling
199
+ If only a universal codec is specified (e.g. `:input json`), no pre-declared type or pattern is required; input is parsed directly and derives its pattern from the input payload.
218
200
 
219
- ### `:time expr`
201
+ When a pattern expression or type is provided, the parsed value is validated and constrained against that pattern before becoming the current value:
220
202
 
221
- Evaluates `expr` and reports the elapsed time broken down into compilation
222
- (type derivation + WebAssembly lowering/compilation) and runtime execution:
203
+ ```text
204
+ > :input ? {string a, float64 n} json
205
+ input ? {string a, float64 n} using json: enter value text
206
+ json> {"a": "Woj", "n": 123}
207
+ ```
208
+
209
+ ## Timing and Profiling
210
+
211
+ Timing reporting is enabled by default for all evaluations, reporting elapsed time
212
+ broken down into compilation (type derivation + WebAssembly lowering/compilation) and runtime execution:
223
213
 
224
214
  ```text
225
- > :time {10 int x, 5 int y} plus
215
+ > {10 int x, 5 int y} plus
226
216
  {}|_|1|1|1|1|+ ?<{} _, ...>
227
217
  /* comp: 12.4ms, exec: 0.8ms */
228
218
  ```
229
219
 
230
- ### `:timing [on|off]`
220
+ ### `:time expr`
231
221
 
232
- Toggles automatic timing reporting for every snippet or expression evaluated in
233
- the session.
222
+ Evaluates `expr` with explicit timing and records performance metrics in session state.
234
223
 
235
224
  ## Web REPL (Studio)
236
225
 
@@ -279,11 +268,9 @@ Evaluated values print in k syntax together with the inferred envelope:
279
268
  ## Example Session
280
269
 
281
270
  ```text
282
- > :type nat = <{} 0, nat +1>
283
- $ nat = @...
284
- > :rel inc = |+1
285
- inc = @...
286
- > {} |0
271
+ > $ nat = < {} 0, nat +1 >;
272
+ > inc = | +1;
273
+ > {} | 0
287
274
  {}|0 ?<{} 0, ...>
288
275
  > :t inc
289
276
  inc : ?X0 --> ?<X0 +1, ...> (@...)
package/TODO.md ADDED
@@ -0,0 +1,112 @@
1
+ # Web REPL Improvements & Conceptual Cleanup Plan
2
+
3
+ This document details the issues, conceptual improvements, and completed enhancements for the `k` Web REPL (`browser/repl-browser.mjs`, `scripts/build-repl-html.mjs`, and `repl.html`).
4
+
5
+ ---
6
+
7
+ ## 1. General Branding & Visual Cleanup
8
+
9
+ ### Removal of `λ` (Lambda) Symbol
10
+ - [x] Remove `<span class="brand-lambda">λ</span>` from the navbar in `scripts/build-repl-html.mjs`.
11
+ - [x] Remove the `.brand-lambda` CSS class.
12
+ - [x] Update welcome banner in `browser/repl-browser.mjs` to `k interactive repl`.
13
+ - [x] Replace `λ` in string codec sample inputs with ASCII/Unicode text samples (e.g. `"abc"`).
14
+
15
+ ---
16
+
17
+ ## 2. Help Modal (`? Help` / `:help`) Overhaul
18
+
19
+ ### Language Overview & Grammar Categorization
20
+ - [x] **Language Overview**: Short, positive, accessible description: `k` is a concise language of types and relations (functions) over tree-like (JSON-like) documents.
21
+ - [x] **Content-Addressed Identity**: Functions and types are identified by their canonical content hash (`@...`) derived directly from their structure; local names are just temporary aliases used for recursion and readability.
22
+ - [x] **Three Clean Grammar Categories**:
23
+ 1. **Definitions (Local Aliases)**:
24
+ - `name = relExpr;` &mdash; Relation alias (e.g. `swap = { . y x, . x y };`).
25
+ - `$ name = typeExpr;` &mdash; Type alias (e.g. `$ bool = < {} true, {} false >;`).
26
+ 2. **Type Expressions (`typeExpr`)**: Schemas describing tree-like documents:
27
+ - `{ Type_1 label_1, ..., Type_k label_k }` &mdash; Product type of arity $k$ (labeled records, e.g. `{float64 x, string n}`; special case: `{}` is the 0-ary product).
28
+ - `< Type_1 tag_1, ..., Type_k tag_k >` &mdash; Union type of arity $k$ (tagged sum of variants, e.g. `<int ok, string err>`).
29
+ - `name` / `@hash` &mdash; Type reference.
30
+ 3. **Relation Expressions (`relExpr`)**: Functions transforming an input document to an output document:
31
+ - `{ rel_1 label_1, ..., rel_k label_k }` &mdash; Product of arity $k$ (evaluates relations into record fields; special case: `{}` evaluates 0 relations, producing empty record `{}`).
32
+ - `< rel_1, ..., rel_k >` &mdash; Union / ordered choice of arity $k$ (evaluates relations in order until one succeeds).
33
+ - `( rel_1 ... rel_k )` &mdash; Composition of arity $k$ (sequentially composes relations; special case: `()` is the 0-ary composition denoting identity; parentheses can be omitted when non-empty: `f g`).
34
+ - `. label` &mdash; Field projection (extracts field `label`).
35
+ - `/ tag` &mdash; Variant branch projection (extracts payload of variant `tag`).
36
+ - `| tag` &mdash; Variant constructor (wraps document into variant `tag`).
37
+ - `$ typeExpr` &mdash; Type constraint (asserts input matches schema; compile-time Type Error on mismatch).
38
+ - `name` / `@hash` &mdash; Relation reference.
39
+ - [x] **Simplified Intro**:
40
+ - Made arbitrary arity explicit across products `{...}`, unions `<...>`, and compositions `(...)`; explained `{}` and `()` as the natural 0-ary special cases; omitted the pattern/filter expressions table from the introductory reference.
41
+ - [x] **Replaced Misleading Expressions & Snippets**:
42
+ - Removed phantom concepts (`10` as literal, "bit-path", `{10 int x, 5 int y} plus` in default state).
43
+ - Added self-contained snippets (`{} | ok`, `not = < / true | false, / false | true >;`, `$ bool = < {} true, {} false >;`, `not = $ bool < / true | false, {} | true > $ bool;`, `swap = { . y x, . x y };`).
44
+ - [x] **Command Reference Table**:
45
+ - Added `:time <expr>`.
46
+ - Removed obsolete commands: `:rel <name> = expr`, `:val`, `:timing [on|off]`, `:codec define n t b`.
47
+ - Updated `:type <name>` to purely inspect and display type definitions (type definition is native via `$ name = typeExpr;`).
48
+ - Timing is now always enabled by default across all evaluations.
49
+ - Updated `:input` documentation to note the interactive browser dialog.
50
+ - Clarified `:codes` (type aliases/hashes) and `:rels` (relation aliases).
51
+
52
+ ---
53
+
54
+ ## 3. Codecs Modal Overhaul
55
+
56
+ ### Concept Correction: External Format Adapters
57
+ - [x] Reframed codecs as format adapters translating external text to/from $k$ values defined as pairs `(filter, tree)`.
58
+ - [x] **Type-Specific Codecs**:
59
+ - `int`: Converts decimal integers to/from `$ int = < bits '+', bits '-' >`.
60
+ - `ieee`: Converts decimal floats to/from `$ float64` records (`sign`, `exponent`, `fraction`).
61
+ - `utf8`: Converts text to/from `$ string` (aliased to `$ utf8`), a list of Unicode scalar values (`$ unicode`) partitioned by Unicode planes.
62
+ - `unit`: Converts to/from `{}`.
63
+ - [x] **Parameterized Codecs**:
64
+ - `json`: JSON text is untyped; deserializing JSON requires a target $k$ type schema parameter (e.g. `:input {float64 x, string n} json`).
65
+ - [x] **Custom Codec Studio**:
66
+ - Updated explanation to emphasize pairs `(filter, tree)`.
67
+ - Fixed `yn` preset to provide self-contained type definition `$ bool = < {} true, {} false >;`.
68
+ - Fixed `blank` preset with proper Value payload formatting.
69
+
70
+ ---
71
+
72
+ ## 4. 'Input' Modal Overhaul
73
+
74
+ - [x] **Parameterized Codec Support**:
75
+ - Converted "Target Type" control into an editable combobox / text input with datalist autocomplete for known aliases, while allowing arbitrary composite type expressions (`{float64 x, string n}`, `<int ok, string err>`).
76
+ - Integrated `resolveInputTypeHash(state, rawType)` into live validation and modal submit.
77
+ - Marked `json` as a Parameterized Codec requiring a target type schema.
78
+ - Added composite quick samples demonstrating type + JSON payload pairs (e.g. `{float64 x, string n}` with `{"x":12, "n":"Woj"}`).
79
+ - Preserved CLI-specified type expressions when opening via `:input <type> [codec]`.
80
+
81
+ ---
82
+
83
+ ## 5. Welcome Banner
84
+
85
+ - [x] Shortened initial welcome message to just the title line: `k interactive repl`.
86
+ - [x] Removed secondary description and quick link lists to keep the terminal prompt clean and uncluttered.
87
+
88
+ ---
89
+
90
+ ## 6. Curated VFS & Navbar Examples Dropdown
91
+
92
+ - [x] Added `poly.k` to `verifiedExamples` and `#example-select`.
93
+ - [x] Structured Navbar Examples dropdown into self-contained basic expressions vs. library-dependent workflows.
94
+ - [x] Removed `:val` from example dropdown list.
95
+ - [x] Fixed `() ()` option display text.
96
+
97
+ ---
98
+
99
+ ## 7. Responsive Navbar & Obsolete Command Removal
100
+
101
+ - [x] **Responsive Navbar on Narrow Screens**:
102
+ - Changed `.navbar` from fixed `height: 48px;` to `min-height: 48px; height: auto; flex-wrap: wrap; padding: 6px 16px; gap: 8px 12px;`.
103
+ - Styled `.nav-right` with `flex-wrap: wrap; justify-content: flex-end; min-width: 0;` so controls wrap smoothly without overflowing or clipping buttons (such as `? Help`).
104
+ - Added comprehensive responsive media query for narrow screens ($\le 768\text{px}$) with full width wrapping and adaptable control inputs.
105
+ - Verified via headless Chromium CDP automated tests that `#btn-help` is completely visible and interactable at narrow viewport widths (e.g. 600px).
106
+ - [x] **Removed Obsolete Commands**:
107
+ - Removed `:rel <name> = expr` and `:def` (relations are defined using native syntax `name = expr;`).
108
+ - Removed `:type <name> = <...>` definition syntax from `:type` (types are defined using native syntax `$ name = typeExpr;`; `:type <name>` remains for inspecting type definitions).
109
+ - Removed `:val` completely.
110
+ - Removed `:timing [on|off]` (timing reporting is now permanently enabled by default for all evaluations; `:time <expr>` remains for explicit timing).
111
+ - Removed `:codec define n t b` (custom codecs are created using the browser Custom Codec Studio UI or loaded from modules via `:codec load`).
112
+
@@ -78,7 +78,6 @@ All tools accept `--help`.
78
78
 
79
79
  - `-S, --assembly`: Emit GNU ARM64 assembly text (`.s`) instead of an ELF binary.
80
80
  - `-o, --output <file>`: Specify the output file path.
81
- - `--entry <name>`: Specify the entry relation (defaults to `__main__`).
82
81
  - `--lib <file>`: Load one `.klib` or `.k` library dependency.
83
82
  - `--export <spec>`: Export library symbols into source scope (`name` or `libname:localname`).
84
83
  - `--json`: Format output as JSON instead of binary wire format.
@@ -22,7 +22,6 @@ function usage(stream = console.error) {
22
22
  stream("Options:");
23
23
  stream(" -S, --assembly Emit GNU ARM64 assembly (.s) instead of an executable binary.");
24
24
  stream(" -o, --output Specify output file path explicitly.");
25
- stream(" --entry name Specify entry relation name.");
26
25
  stream(" --lib file Load one .klib dependency before compiling.");
27
26
  stream(" --export spec Export a library alias into source scope. May be repeated.");
28
27
  stream(" spec is 'name' or 'libname:localname'.");
@@ -38,8 +37,6 @@ try {
38
37
 
39
38
  let emitAssembly = false;
40
39
  let explicitOutput = null;
41
- let entry = null;
42
-
43
40
  // Extract ARM64-specific flags
44
41
  const remainingArgs = [];
45
42
  while (args.length > 0) {
@@ -51,10 +48,6 @@ try {
51
48
  args.shift();
52
49
  explicitOutput = args.shift();
53
50
  if (!explicitOutput) throw new Error("-o/--output requires a file argument");
54
- } else if (arg === "--entry") {
55
- args.shift();
56
- entry = args.shift();
57
- if (!entry) throw new Error("--entry requires a name argument");
58
51
  } else {
59
52
  remainingArgs.push(args.shift());
60
53
  }
@@ -71,7 +64,6 @@ try {
71
64
  libraries,
72
65
  exportSpecs,
73
66
  stdin,
74
- entry,
75
67
  emit: emitAssembly ? "assembly" : null,
76
68
  outputPath: (!emitAssembly && outputPath) ? outputPath : null
77
69
  });
@@ -23,7 +23,7 @@ backend from `backends/llvm`:
23
23
  npm install
24
24
  cd backends/llvm
25
25
  node ../../objects/compile.mjs '()' /tmp/id.ko
26
- node ./bin/k-llvm-compile.mjs --input-pattern '[["open-product",[]]]' /tmp/id.ko /tmp/id.ll
26
+ node ./bin/k-llvm-compile.mjs /tmp/id.ko /tmp/id.ll
27
27
  ```
28
28
 
29
29
  The generated `.ll` embeds the envelope-specialized KIR-P contract as module
@@ -44,24 +44,26 @@ status in `k_result`. These operations can compose through KIR `comp`.
44
44
  ## CLI
45
45
 
46
46
  ```sh
47
- k-llvm-build [options] object.ko output-exe
48
- k-llvm-compile [options] object.ko [output.ll]
47
+ k-llvm-build [options] [source-snippet | input-file [output-exe]]
48
+ k-llvm-compile [options] [source-snippet | input-file [output.ll]]
49
49
  k-llvm-jit [options] object.ko
50
50
  k-llvm-run [options] object.ko [input.kv]
51
51
  ```
52
52
 
53
53
  Options:
54
54
 
55
- - `--retype rel`: current option spelling for the relation to
56
- envelope-specialize; defaults to the object's `main`.
57
- - `--input-pattern json-or-file`: KIR property-list input pattern; required
58
- for `k-llvm-build`.
59
- - `--expect value-or-file`: for `k-llvm-run`, compare the output value against
60
- expected value text.
55
+ - `-o, --output path`: specify output file path explicitly.
56
+ - `--lib file`: load one `.klib` or `.k` dependency before compiling.
57
+ - `--export spec`: export a library alias into source scope (`name` or `libname:localname`). May be repeated.
58
+ - `--input-pattern json-or-file`: for `k-llvm-run`, KIR property-list input pattern.
59
+ - `--retype rel`: for `k-llvm-run`, relation to specialize (defaults to object main).
60
+ - `--expect value-or-file`: for `k-llvm-run`, compare the output value against expected value text.
61
61
  - `-h`, `--help`: show usage.
62
62
 
63
- Only `.ko` / `.klib` object input is supported in this first prototype.
64
- Source compilation remains owned by core `k`.
63
+ The compile commands follow the unified k CLI input convention: pass inline
64
+ source, an existing `.k` source file, or a `.ko`/`.klib` object. Reads UTF-8
65
+ source from stdin when input is omitted. Output paths can be specified as a
66
+ positional argument or explicitly with `-o, --output`.
65
67
  Without `--expect`, `k-llvm-run` prints the result value as compact JSON.
66
68
  `k-llvm-build` emits a native executable that reads and writes the binary
67
69
  k pattern+value envelope used by `codecs/k-parse.mjs` and `codecs/k-print.mjs`.
@@ -1,14 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from "node:fs";
4
- import { argv, exit } from "node:process";
5
- import { decodeObject } from "@fraczak/k/object.mjs";
3
+ import { argv, exit, stdin } from "node:process";
4
+ import {
5
+ compileProgramInputToObject,
6
+ parseCompileOptions,
7
+ resolveProgramInput
8
+ } from "../src/cli.mjs";
6
9
  import { compileObjectToExecutable } from "../src/executable.mjs";
7
10
 
8
11
  function usage(stream = console.error) {
9
12
  const prog = argv[1] || "k-llvm-build.mjs";
10
- stream(`Usage: node ${prog} [options] object-file output-exe`);
11
- stream("Compile a k .ko/.klib object into a native executable.");
13
+ stream(`Usage: node ${prog} [options] [source-snippet | input-file [output-exe]]`);
14
+ stream("Compile k source, .ko, or .klib input into a native executable.");
15
+ stream("");
16
+ stream("Arguments:");
17
+ stream(" source-snippet Inline k source, in the same style as k.mjs.");
18
+ stream(" input-file Source .k, .ko, or .klib file. Reads UTF-8 source from stdin when omitted.");
19
+ stream(" output-exe Output executable path.");
12
20
  stream("");
13
21
  stream("The executable reads a binary k pattern+value envelope from stdin and");
14
22
  stream("writes a binary k pattern+value envelope to stdout.");
@@ -16,18 +24,11 @@ function usage(stream = console.error) {
16
24
  stream("stdout is encoded with the compiled output pattern.");
17
25
  stream("");
18
26
  stream("Options:");
19
- stream(" --retype rel Relation to specialize. Defaults to object main.");
20
- stream(" --input-pattern value Required input pattern property-list JSON, or a file containing it.");
21
- stream(" -h, --help Show this help.");
22
- }
23
-
24
- function readMaybeFile(textOrPath) {
25
- return fs.existsSync(textOrPath) ? fs.readFileSync(textOrPath, "utf8") : textOrPath;
26
- }
27
-
28
- function readPattern(inputPattern) {
29
- if (inputPattern == null) return null;
30
- return JSON.parse(readMaybeFile(inputPattern));
27
+ stream(" -o, --output path Specify output file path explicitly.");
28
+ stream(" --lib file Load one .klib dependency before compiling.");
29
+ stream(" --export spec Export a library alias into source scope. May be repeated.");
30
+ stream(" spec is 'name' or 'libname:localname'.");
31
+ stream(" -h, --help Show this help.");
31
32
  }
32
33
 
33
34
  try {
@@ -37,33 +38,34 @@ try {
37
38
  exit(0);
38
39
  }
39
40
 
40
- let relation = null;
41
- let inputPattern = null;
42
- while (args.length > 0 && args[0].startsWith("--")) {
43
- const option = args.shift();
44
- if (option === "--retype") {
45
- relation = args.shift();
46
- if (!relation) throw new Error("--retype requires a relation name");
47
- } else if (option === "--input-pattern") {
48
- inputPattern = args.shift();
49
- if (!inputPattern) throw new Error("--input-pattern requires JSON or a file path");
41
+ let explicitOutput = null;
42
+ const remainingArgs = [];
43
+ while (args.length > 0) {
44
+ const arg = args[0];
45
+ if (arg === "-o" || arg === "--output") {
46
+ args.shift();
47
+ explicitOutput = args.shift();
48
+ if (!explicitOutput) throw new Error("-o/--output requires a file argument");
50
49
  } else {
51
- throw new Error(`Unknown option: ${option}`);
50
+ remainingArgs.push(args.shift());
52
51
  }
53
52
  }
54
53
 
55
- const objectPath = args.shift() || null;
56
- const outputPath = args.shift() || null;
57
- if (objectPath == null) throw new Error("object-file is required");
58
- if (outputPath == null) throw new Error("output-exe is required");
59
- if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
60
- if (inputPattern == null) throw new Error("--input-pattern is required for k-llvm-build");
54
+ const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
55
+ const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
56
+ const positionalOutput = remainingArgs.shift();
57
+ if (remainingArgs.length > 0) throw new Error("Too many arguments");
58
+
59
+ const outputPath = explicitOutput || positionalOutput || null;
60
+ if (outputPath == null) throw new Error("Output executable path is required (pass output-exe or -o/--output)");
61
61
 
62
- const object = decodeObject(fs.readFileSync(objectPath));
63
- compileObjectToExecutable(object, outputPath, {
64
- relation: relation || object.main,
65
- inputPattern: readPattern(inputPattern)
62
+ const object = await compileProgramInputToObject(input, {
63
+ libraries,
64
+ exportSpecs,
65
+ stdin
66
66
  });
67
+
68
+ compileObjectToExecutable(object, outputPath);
67
69
  } catch (error) {
68
70
  console.error(error.stack || error.message || String(error));
69
71
  usage();
@@ -2,29 +2,30 @@
2
2
 
3
3
  import fs from "node:fs";
4
4
  import { argv, exit, stdin, stdout } from "node:process";
5
- import { compileBufferToLLVM } from "../src/llvm.mjs";
5
+ import {
6
+ compileProgramInputToObject,
7
+ parseCompileOptions,
8
+ resolveProgramInput
9
+ } from "../src/cli.mjs";
10
+ import { compileObjectToLLVM } from "../src/llvm.mjs";
11
+ import { inputPatternForObjectRelation } from "../src/executable.mjs";
6
12
 
7
13
  function usage(stream = console.error) {
8
14
  const prog = argv[1] || "k-llvm-compile.mjs";
9
- stream(`Usage: node ${prog} [options] object-file [output.ll]`);
15
+ stream(`Usage: node ${prog} [options] [source-snippet | input-file [output.ll]]`);
10
16
  stream("Compile a k .ko/.klib object into prototype LLVM IR.");
11
17
  stream("");
12
18
  stream("Arguments:");
13
- stream(" object-file Input .ko or .klib file. Reads bytes from stdin when omitted.");
19
+ stream(" source-snippet Inline k source, in the same style as k.mjs.");
20
+ stream(" input-file Source .k, .ko, or .klib file. Reads UTF-8 source from stdin when omitted.");
14
21
  stream(" output.ll Output LLVM IR path. Writes to stdout when omitted.");
15
22
  stream("");
16
23
  stream("Options:");
17
- stream(" --retype rel Relation to specialize. Defaults to object main.");
18
- stream(" --input-pattern value Input pattern property-list JSON, or a file containing it.");
19
- stream(" -h, --help Show this help.");
20
- }
21
-
22
- async function readStdinBytes() {
23
- const chunks = [];
24
- for await (const chunk of stdin) {
25
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
26
- }
27
- return Buffer.concat(chunks);
24
+ stream(" -o, --output path Specify output file path explicitly.");
25
+ stream(" --lib file Load one .klib dependency before compiling.");
26
+ stream(" --export spec Export a library alias into source scope. May be repeated.");
27
+ stream(" spec is 'name' or 'libname:localname'.");
28
+ stream(" -h, --help Show this help.");
28
29
  }
29
30
 
30
31
  try {
@@ -34,32 +35,41 @@ try {
34
35
  exit(0);
35
36
  }
36
37
 
37
- let relation = null;
38
- let inputPattern = null;
39
- while (args.length > 0 && args[0].startsWith("--")) {
40
- const option = args.shift();
41
- if (option === "--retype") {
42
- relation = args.shift();
43
- if (!relation) throw new Error("--retype requires a relation name");
44
- } else if (option === "--input-pattern") {
45
- inputPattern = args.shift();
46
- if (!inputPattern) throw new Error("--input-pattern requires JSON or a file path");
38
+ let explicitOutput = null;
39
+ const remainingArgs = [];
40
+ while (args.length > 0) {
41
+ const arg = args[0];
42
+ if (arg === "-o" || arg === "--output") {
43
+ args.shift();
44
+ explicitOutput = args.shift();
45
+ if (!explicitOutput) throw new Error("-o/--output requires a file argument");
47
46
  } else {
48
- throw new Error(`Unknown option: ${option}`);
47
+ remainingArgs.push(args.shift());
49
48
  }
50
49
  }
51
- if (inputPattern == null) throw new Error("--input-pattern is required");
52
50
 
53
- const inputPath = args.shift() || null;
54
- const outputPath = args.shift() || null;
55
- if (args.length > 0) throw new Error(`Unexpected argument: ${args[0]}`);
51
+ const { libraries, exportSpecs } = parseCompileOptions(remainingArgs);
52
+ const input = resolveProgramInput(remainingArgs, { allowStdinSource: true });
53
+ const positionalOutput = remainingArgs.shift();
54
+ if (remainingArgs.length > 0) throw new Error("Too many arguments");
55
+
56
+ const outputPath = explicitOutput || positionalOutput || null;
57
+
58
+ const object = await compileProgramInputToObject(input, {
59
+ libraries,
60
+ exportSpecs,
61
+ stdin
62
+ });
63
+
64
+ const { llvm } = compileObjectToLLVM(object, {
65
+ relation: object.main,
66
+ inputPattern: inputPatternForObjectRelation(object, object.main)
67
+ });
56
68
 
57
- const input = inputPath == null ? await readStdinBytes() : fs.readFileSync(inputPath);
58
- const { llvm } = compileBufferToLLVM(input, { relation, inputPattern });
59
69
  if (outputPath == null) {
60
70
  stdout.write(llvm);
61
71
  } else {
62
- fs.writeFileSync(outputPath, llvm);
72
+ fs.writeFileSync(outputPath, llvm, "utf8");
63
73
  }
64
74
  } catch (error) {
65
75
  console.error(error.stack || error.message || String(error));