@fraczak/k 6.2.4 → 6.3.2

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.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env sh
2
+ set -eu
3
+
4
+ if [ "${SKIP_BENCHMARK:-}" = "1" ]; then
5
+ echo "Skipping benchmark because SKIP_BENCHMARK=1"
6
+ exit 0
7
+ fi
8
+
9
+ npm run benchmark
10
+ git add benchmark-results.json
@@ -0,0 +1,343 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+ const inputPath = path.resolve(root, process.argv[2] || "benchmark-results.json");
9
+ const outputPath = path.resolve(root, process.argv[3] || "benchmark-results.html");
10
+
11
+ const rowOrder = [
12
+ "Native JS (Envelope-Aware)",
13
+ "Native JS (Envelope-Free)",
14
+ "kVM Interpreter (Env-Free)"
15
+ ];
16
+
17
+ const columnGroups = [
18
+ {
19
+ title: "ieee",
20
+ source: "Examples/ieee.k",
21
+ columns: [
22
+ { key: "ieee.add", label: "add" },
23
+ { key: "ieee.div", label: "div" },
24
+ { key: "ieee.mul", label: "mul" },
25
+ { key: "ieee.sub", label: "sub" }
26
+ ]
27
+ },
28
+ {
29
+ title: "int",
30
+ source: "Examples/arithmetics.k",
31
+ columns: [
32
+ { key: "int.factorial", label: "factorial" },
33
+ { key: "int.gcd.90d", label: "gcd" },
34
+ { key: "int.times.220d", label: "times" }
35
+ ]
36
+ }
37
+ ];
38
+
39
+ function escapeHtml(value) {
40
+ return String(value)
41
+ .replaceAll("&", "&")
42
+ .replaceAll("<", "&lt;")
43
+ .replaceAll(">", "&gt;")
44
+ .replaceAll("\"", "&quot;");
45
+ }
46
+
47
+ function fmtMs(value) {
48
+ return value == null ? "n/a" : `${value.toFixed(2)} ms`;
49
+ }
50
+
51
+ function fmtMiB(value) {
52
+ return value == null ? "n/a" : `${value.toFixed(1)} MiB`;
53
+ }
54
+
55
+ function fmtRatio(value) {
56
+ return Number.isFinite(value) ? `${value.toFixed(2)}x` : "";
57
+ }
58
+
59
+ function resultMap(results) {
60
+ const map = new Map();
61
+ for (const result of results) {
62
+ if (!map.has(result.mode)) map.set(result.mode, new Map());
63
+ map.get(result.mode).set(result.case, result);
64
+ }
65
+ return map;
66
+ }
67
+
68
+ function factorialResults(byCase) {
69
+ return [...byCase.entries()]
70
+ .filter(([caseName]) => caseName.startsWith("int.factorial."))
71
+ .sort(([a], [b]) => Number(a.split(".").at(-1)) - Number(b.split(".").at(-1)))
72
+ .map(([caseName, result]) => ({
73
+ input: caseName.split(".").at(-1),
74
+ result
75
+ }));
76
+ }
77
+
78
+ function bestForColumn(results, column) {
79
+ const relevant = column.key === "int.factorial"
80
+ ? results.filter(result => result.case.startsWith("int.factorial."))
81
+ : results.filter(result => result.case === column.key);
82
+ return relevant
83
+ .filter(result => result.status === "ok" && typeof result.avgMs === "number")
84
+ .sort((a, b) => a.avgMs - b.avgMs)[0] || null;
85
+ }
86
+
87
+ function cellFor(result, best) {
88
+ if (!result) return `<span class="muted">not run</span>`;
89
+ if (result.status !== "ok") return `<span class="bad">${escapeHtml(result.status)}</span>`;
90
+ const ratio = best ? result.avgMs / best.avgMs : NaN;
91
+ return `
92
+ <div class="time">${fmtMs(result.avgMs)} <span>${fmtRatio(ratio)}</span></div>
93
+ <div class="sub">rss ${fmtMiB(result.maxRssMiB)}</div>`;
94
+ }
95
+
96
+ function factorialCell(byCase, allResults) {
97
+ const rows = factorialResults(byCase);
98
+ if (rows.length === 0) return `<span class="muted">not run</span>`;
99
+ return rows.map(({ input, result }) => {
100
+ const best = bestForColumn(allResults, { key: result.case });
101
+ return `
102
+ <div class="mini">
103
+ <span class="mini-label">${escapeHtml(input)}</span>
104
+ <div>${cellFor(result, best)}</div>
105
+ </div>`;
106
+ }).join("");
107
+ }
108
+
109
+ function buildMatrix(doc) {
110
+ const results = doc.results || [];
111
+ const byMode = resultMap(results);
112
+
113
+ const groupHeader = columnGroups.map(group =>
114
+ `<th colspan="${group.columns.length}" class="group">${escapeHtml(group.title)} <span>${escapeHtml(group.source)}</span></th>`
115
+ ).join("");
116
+
117
+ const columnHeader = columnGroups.flatMap(group => group.columns)
118
+ .map(column => `<th>${escapeHtml(column.label)}</th>`)
119
+ .join("");
120
+
121
+ const body = rowOrder.map(mode => {
122
+ const byCase = byMode.get(mode) || new Map();
123
+ const cells = columnGroups.flatMap(group => group.columns).map(column => {
124
+ if (column.key === "int.factorial") {
125
+ return `<td>${factorialCell(byCase, results)}</td>`;
126
+ }
127
+ return `<td>${cellFor(byCase.get(column.key), bestForColumn(results, column))}</td>`;
128
+ }).join("");
129
+ return `<tr><th class="row-head">${escapeHtml(mode)}</th>${cells}</tr>`;
130
+ }).join("");
131
+
132
+ return `
133
+ <table class="matrix">
134
+ <thead>
135
+ <tr><th></th>${groupHeader}</tr>
136
+ <tr><th>implementation</th>${columnHeader}</tr>
137
+ </thead>
138
+ <tbody>${body}</tbody>
139
+ </table>`;
140
+ }
141
+
142
+ function buildDetails(results) {
143
+ return results
144
+ .slice()
145
+ .sort((a, b) => a.case.localeCompare(b.case) || a.mode.localeCompare(b.mode))
146
+ .map(result => `
147
+ <tr>
148
+ <td>${escapeHtml(result.case)}</td>
149
+ <td>${escapeHtml(result.mode)}</td>
150
+ <td>${escapeHtml(result.status)}</td>
151
+ <td class="num">${fmtMs(result.avgMs)}</td>
152
+ <td class="num">${fmtMs(result.minMs)}</td>
153
+ <td class="num">${fmtMs(result.maxMs)}</td>
154
+ <td class="num">${fmtMiB(result.maxRssMiB)}</td>
155
+ <td class="hash">${escapeHtml(result.resultDigest || "")}</td>
156
+ </tr>`)
157
+ .join("");
158
+ }
159
+
160
+ function render(doc) {
161
+ const results = doc.results || [];
162
+ const generatedAt = doc.generatedAt ? new Date(doc.generatedAt).toLocaleString() : "unknown";
163
+ const completed = results.filter(result => result.status === "ok").length;
164
+
165
+ return `<!doctype html>
166
+ <html lang="en">
167
+ <head>
168
+ <meta charset="utf-8">
169
+ <meta name="viewport" content="width=device-width, initial-scale=1">
170
+ <title>k Benchmark Results</title>
171
+ <style>
172
+ :root {
173
+ color-scheme: light;
174
+ --ink: #17212f;
175
+ --muted: #627083;
176
+ --line: #d9e0ea;
177
+ --paper: #f5f7fa;
178
+ --panel: #fff;
179
+ --accent: #1d5fd1;
180
+ --soft: #eef3fb;
181
+ --bad: #b42318;
182
+ }
183
+ * { box-sizing: border-box; }
184
+ body {
185
+ margin: 0;
186
+ font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
187
+ color: var(--ink);
188
+ background: var(--paper);
189
+ }
190
+ header {
191
+ padding: 28px 32px 20px;
192
+ background: var(--panel);
193
+ border-bottom: 1px solid var(--line);
194
+ }
195
+ h1 { margin: 0 0 10px; font-size: 30px; letter-spacing: 0; }
196
+ .meta {
197
+ display: flex;
198
+ flex-wrap: wrap;
199
+ gap: 14px;
200
+ color: var(--muted);
201
+ font-size: 14px;
202
+ }
203
+ main {
204
+ max-width: 1320px;
205
+ margin: 0 auto;
206
+ padding: 24px 32px 40px;
207
+ }
208
+ .summary {
209
+ display: grid;
210
+ grid-template-columns: repeat(4, minmax(0, 1fr));
211
+ gap: 12px;
212
+ margin-bottom: 18px;
213
+ }
214
+ .metric, .panel {
215
+ background: var(--panel);
216
+ border: 1px solid var(--line);
217
+ border-radius: 8px;
218
+ }
219
+ .metric { padding: 14px 16px; }
220
+ .metric .label { color: var(--muted); font-size: 13px; }
221
+ .metric .value { margin-top: 5px; font-size: 22px; font-weight: 700; }
222
+ .panel { overflow: auto; }
223
+ table {
224
+ width: 100%;
225
+ border-collapse: collapse;
226
+ }
227
+ th, td {
228
+ border-bottom: 1px solid var(--line);
229
+ padding: 12px;
230
+ text-align: left;
231
+ vertical-align: top;
232
+ font-size: 13px;
233
+ }
234
+ thead th {
235
+ background: #fafbfd;
236
+ color: var(--muted);
237
+ font-weight: 700;
238
+ }
239
+ .matrix {
240
+ min-width: 980px;
241
+ }
242
+ .matrix .group {
243
+ text-align: center;
244
+ color: var(--ink);
245
+ font-size: 15px;
246
+ background: var(--soft);
247
+ }
248
+ .matrix .group span {
249
+ display: block;
250
+ color: var(--muted);
251
+ font-size: 12px;
252
+ font-weight: 500;
253
+ margin-top: 2px;
254
+ }
255
+ .row-head {
256
+ width: 230px;
257
+ color: var(--ink);
258
+ font-weight: 800;
259
+ background: #fbfcfe;
260
+ }
261
+ .time {
262
+ font-weight: 800;
263
+ font-variant-numeric: tabular-nums;
264
+ white-space: nowrap;
265
+ }
266
+ .time span {
267
+ color: var(--muted);
268
+ font-weight: 600;
269
+ margin-left: 4px;
270
+ }
271
+ .sub {
272
+ margin-top: 3px;
273
+ color: var(--muted);
274
+ font-size: 12px;
275
+ white-space: nowrap;
276
+ }
277
+ .mini {
278
+ display: grid;
279
+ grid-template-columns: 42px 1fr;
280
+ gap: 8px;
281
+ padding: 5px 0;
282
+ border-bottom: 1px solid #edf1f6;
283
+ }
284
+ .mini:last-child { border-bottom: 0; }
285
+ .mini-label {
286
+ color: var(--muted);
287
+ font-weight: 800;
288
+ font-variant-numeric: tabular-nums;
289
+ }
290
+ .muted { color: var(--muted); }
291
+ .bad { color: var(--bad); font-weight: 800; }
292
+ h2 { margin: 24px 0 10px; font-size: 18px; }
293
+ .details { margin-top: 16px; }
294
+ .details table { min-width: 920px; }
295
+ .num { text-align: right; font-variant-numeric: tabular-nums; }
296
+ .hash { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--muted); }
297
+ @media (max-width: 900px) {
298
+ header, main { padding-left: 16px; padding-right: 16px; }
299
+ .summary { grid-template-columns: 1fr; }
300
+ }
301
+ </style>
302
+ </head>
303
+ <body>
304
+ <header>
305
+ <h1>k Benchmark Results</h1>
306
+ <div class="meta">
307
+ <span>Generated: ${escapeHtml(generatedAt)}</span>
308
+ <span>Node: ${escapeHtml(doc.node || "unknown")}</span>
309
+ <span>Platform: ${escapeHtml(doc.platform || "unknown")}</span>
310
+ <span>CPUs: ${escapeHtml(doc.cpuCount ?? "unknown")}</span>
311
+ <span>Samples/job: ${escapeHtml(doc.samples ?? "unknown")}</span>
312
+ </div>
313
+ </header>
314
+ <main>
315
+ <section class="summary">
316
+ <div class="metric"><div class="label">Completed Jobs</div><div class="value">${completed}/${results.length}</div></div>
317
+ <div class="metric"><div class="label">Timeout</div><div class="value">${((doc.timeoutMs || 0) / 60000).toFixed(1)} min</div></div>
318
+ <div class="metric"><div class="label">Parallel Jobs</div><div class="value">${escapeHtml(doc.parallelJobs ?? "n/a")}</div></div>
319
+ <div class="metric"><div class="label">Result Rows</div><div class="value">${results.length}</div></div>
320
+ </section>
321
+ <section class="panel">
322
+ ${buildMatrix(doc)}
323
+ </section>
324
+ <section class="details">
325
+ <h2>Raw Result Rows</h2>
326
+ <div class="panel">
327
+ <table>
328
+ <thead>
329
+ <tr><th>Case</th><th>Mode</th><th>Status</th><th>Avg</th><th>Min</th><th>Max</th><th>Max RSS</th><th>Digest</th></tr>
330
+ </thead>
331
+ <tbody>${buildDetails(results)}</tbody>
332
+ </table>
333
+ </div>
334
+ </section>
335
+ </main>
336
+ </body>
337
+ </html>
338
+ `;
339
+ }
340
+
341
+ const data = JSON.parse(fs.readFileSync(inputPath, "utf8"));
342
+ fs.writeFileSync(outputPath, render(data));
343
+ console.log(`Rendered ${path.relative(root, outputPath)} from ${path.relative(root, inputPath)}`);
@@ -18,10 +18,12 @@ const tests = [
18
18
  ["tests/test-fingerprint.mjs", node, ["tests/test-fingerprint.mjs"]],
19
19
  ["tests/test-hash-fuzz.mjs", node, ["tests/test-hash-fuzz.mjs"]],
20
20
  ["tests/test-hash-normalization.mjs", node, ["tests/test-hash-normalization.mjs"]],
21
+ ["tests/test-structural-values.mjs", node, ["tests/test-structural-values.mjs"]],
21
22
  ["tests/test-deep-wire-value.mjs", node, ["tests/test-deep-wire-value.mjs"]],
22
23
  ["tests/test-k-object.mjs", node, ["tests/test-k-object.mjs"]],
23
24
  ["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
24
25
  ["tests/test-kvm.mjs", node, ["tests/test-kvm.mjs"]],
26
+ ["tests/test-arithmetics.mjs", node, ["tests/test-arithmetics.mjs"]],
25
27
  ["tests/test-ieee-arithmetic.mjs", node, ["tests/test-ieee-arithmetic.mjs"]],
26
28
  ["tests/integration.sh", path.join(root, "tests/integration.sh"), []]
27
29
  ];
@@ -1,7 +1,7 @@
1
1
  import assert from 'assert';
2
2
  import { parseValue } from '../../valueIO.mjs';
3
3
  import { encodeToWire, decodeWire } from '../../codecs/runtime/prefix-codec.mjs';
4
- import { Product, Variant } from '../../Value.mjs';
4
+ import { isProduct, isVariant } from '../../Value.mjs';
5
5
 
6
6
  const value = parseValue('{a:{b:x,c:{}}}');
7
7
  const repeatedClosedValue = parseValue('{a:{m:{},n:{}},b:{m:{},n:{}}}');
@@ -27,9 +27,9 @@ assert.deepEqual(explicitProductWire.pattern, [
27
27
  ]);
28
28
 
29
29
  const decoded = explicitProductWire.value;
30
- assert(decoded instanceof Product);
31
- assert(decoded.product.a instanceof Product);
32
- assert(decoded.product.a.product.b instanceof Variant);
30
+ assert(isProduct(decoded));
31
+ assert(isProduct(decoded.product.a));
32
+ assert(isVariant(decoded.product.a.product.b));
33
33
  assert.equal(decoded.product.a.product.b.tag, "x");
34
34
 
35
35
  const repeatedClosedWire = decodeWire(encodeToWire(repeatedClosedValue, null));
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import k from "../../index.mjs";
3
- import { Product, Variant, mergePatterns, withPattern } from "../../Value.mjs";
3
+ import { Value, mergePatterns, withPattern } from "../../Value.mjs";
4
4
  import { decodeWire, encodeToWire } from "../../codecs/runtime/prefix-codec.mjs";
5
5
  import run from "../../run.mjs";
6
6
  import codes from "../../codes.mjs";
@@ -17,16 +17,16 @@ const script = `
17
17
  {}|_|0|1|+ $int
18
18
  `;
19
19
 
20
- const result = k.compile(script)(new Product({}));
20
+ const result = k.compile(script)(Value.product({}));
21
21
  assert.deepEqual(result.pattern, INT_PATTERN);
22
22
 
23
23
  const decoded = decodeWire(encodeToWire(result, result.pattern));
24
24
  assert.deepEqual(decoded.pattern, INT_PATTERN);
25
25
 
26
- const two = new Variant("+",
27
- new Variant("1",
28
- new Variant("0",
29
- new Variant("_", new Product({}))
26
+ const two = Value.variant("+",
27
+ Value.variant("1",
28
+ Value.variant("0",
29
+ Value.variant("_", Value.product({}))
30
30
  )
31
31
  )
32
32
  );
@@ -53,13 +53,13 @@ const wrappedFloatPattern = [
53
53
  ["closed-product", []],
54
54
  ["closed-union", [["inexact", 5]]]
55
55
  ];
56
- const wrappedFloat = new Product({
57
- result: new Product({
58
- sign: new Variant("+", new Product({})),
59
- exponent: new Product({ 0: new Variant("1", new Product({})) }),
60
- fraction: new Product({ 0: new Variant("0", new Product({})) })
56
+ const wrappedFloat = Value.product({
57
+ result: Value.product({
58
+ sign: Value.variant("+", Value.product({})),
59
+ exponent: Value.product({ 0: Value.variant("1", Value.product({})) }),
60
+ fraction: Value.product({ 0: Value.variant("0", Value.product({})) })
61
61
  }),
62
- flags: new Variant("inexact", new Product({}))
62
+ flags: Value.variant("inexact", Value.product({}))
63
63
  });
64
64
  const resultProjection = k.compile(".result")(withPattern(wrappedFloat, wrappedFloatPattern));
65
65
  assert.deepEqual(resultProjection.pattern, [
@@ -19,7 +19,9 @@ grep -q '"__main__"' "$TMP_DIR/stdin.kvm"
19
19
  ./objects/compile.mjs --lib "$TMP_DIR/export.klib" --export id:alias 'alias' "$TMP_DIR/export.ko"
20
20
  ./objects/decompile.mjs "$TMP_DIR/export.ko" | grep -q '^----- main -----$'
21
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 | ./kvm.mjs --lib "$TMP_DIR/export.klib" --export id:alias 'alias' | node ./codecs/unit.mjs --print | grep -qx '{}'
22
23
  node ./codecs/unit.mjs --parse | ./k.mjs "$TMP_DIR/export.ko" | node ./codecs/unit.mjs --print | grep -qx '{}'
24
+ node ./codecs/unit.mjs --parse | ./kvm.mjs "$TMP_DIR/export.ko" | node ./codecs/unit.mjs --print | grep -qx '{}'
23
25
  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
26
  echo "expected repeated --lib to fail" >&2
25
27
  exit 1
@@ -56,6 +58,11 @@ if ./k.mjs -k Examples/byte.k > "$TMP_DIR/dash-k.out" 2> "$TMP_DIR/dash-k.err";
56
58
  exit 1
57
59
  fi
58
60
  grep -q -- '-k is no longer supported' "$TMP_DIR/dash-k.err"
61
+ if ./kvm.mjs -k Examples/byte.k > "$TMP_DIR/dash-k-kvm.out" 2> "$TMP_DIR/dash-k-kvm.err"; then
62
+ echo "expected k-vm -k to fail" >&2
63
+ exit 1
64
+ fi
65
+ grep -q -- '-k is no longer supported' "$TMP_DIR/dash-k-kvm.err"
59
66
  printf 'A🙂\nBé~\t' | ./codecs/utf8.mjs --parse | ./k.mjs Examples/byte.k |./codecs/utf8.mjs --print
60
67
  echo
61
68
  ./objects/compile.mjs Examples/ieee.k "$TMP_DIR/ieee.klib"
@@ -0,0 +1,95 @@
1
+ import assert from "node:assert/strict";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { spawnSync } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ const node = process.execPath;
10
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-arithmetics-"));
11
+ const libPath = path.join(tmpDir, "arithmetics.klib");
12
+ const program = "{inc x, () y} o";
13
+
14
+ const smallInput = "11111112222223333334444444555555666666777777788888899999900000011111122222233333344444455555";
15
+
16
+ function run(command, args, options = {}) {
17
+ const result = spawnSync(command, args, {
18
+ cwd: root,
19
+ input: options.input,
20
+ encoding: options.encoding ?? null,
21
+ maxBuffer: 64 * 1024 * 1024
22
+ });
23
+ if (result.error && result.status == null) throw result.error;
24
+ return result;
25
+ }
26
+
27
+ function assertOk(result, label) {
28
+ if (result.status !== 0) {
29
+ const stderr = Buffer.isBuffer(result.stderr) ? result.stderr.toString("utf8") : String(result.stderr ?? "");
30
+ const stdout = Buffer.isBuffer(result.stdout) ? result.stdout.toString("utf8") : String(result.stdout ?? "");
31
+ assert.fail(`${label} failed with status ${result.status}\nSTDERR:\n${stderr}\nSTDOUT:\n${stdout}`);
32
+ }
33
+ }
34
+
35
+ function parseIntWire(text) {
36
+ const result = run(node, ["codecs/int.mjs", "--parse"], {
37
+ input: `${text}\n`
38
+ });
39
+ assertOk(result, `parse int ${text}`);
40
+ return result.stdout;
41
+ }
42
+
43
+ function printIntWire(wire, label) {
44
+ const result = run(node, ["codecs/int.mjs", "--print"], {
45
+ input: wire,
46
+ encoding: "utf8"
47
+ });
48
+ assertOk(result, `${label} print`);
49
+ return result.stdout.trim();
50
+ }
51
+
52
+ function runnerArgs(operation) {
53
+ return [
54
+ "--lib", libPath,
55
+ "--export", `${operation}:o`,
56
+ "--export", "inc",
57
+ program
58
+ ];
59
+ }
60
+
61
+ function runNative(operation, inputText) {
62
+ const wire = parseIntWire(inputText);
63
+ const result = run(node, ["k.mjs", ...runnerArgs(operation)], {
64
+ input: wire
65
+ });
66
+ assertOk(result, `native ${operation}(${inputText})`);
67
+ return printIntWire(result.stdout, `native ${operation}`);
68
+ }
69
+
70
+ function expected(operation, inputText) {
71
+ const n = BigInt(inputText);
72
+ if (operation === "plus") return (n + n + 1n).toString();
73
+ if (operation === "minus") return "1";
74
+ if (operation === "times") return (n * (n + 1n)).toString();
75
+ throw new Error(`Unknown operation: ${operation}`);
76
+ }
77
+
78
+ try {
79
+ assertOk(
80
+ run(node, ["objects/compile.mjs", "Examples/arithmetics.k", libPath], { encoding: "utf8" }),
81
+ "compile Examples/arithmetics.k"
82
+ );
83
+
84
+ for (const operation of ["plus", "minus", "times"]) {
85
+ assert.equal(
86
+ runNative(operation, smallInput),
87
+ expected(operation, smallInput),
88
+ `native ${operation} should match BigInt oracle`
89
+ );
90
+ }
91
+
92
+ console.log("OK");
93
+ } finally {
94
+ fs.rmSync(tmpDir, { recursive: true, force: true });
95
+ }
@@ -1,5 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
- import { Product, Variant } from "../Value.mjs";
2
+ import { Value } from "../Value.mjs";
3
3
  import { decodeWire, encodeToWire } from "../codecs/runtime/prefix-codec.mjs";
4
4
 
5
5
  const depth = 12000;
@@ -9,12 +9,12 @@ const pattern = [
9
9
  ["closed-product", []]
10
10
  ];
11
11
 
12
- let bits = new Variant("_", new Product({}));
12
+ let bits = Value.variant("_", Value.product({}));
13
13
  for (let i = 0; i < depth; i++) {
14
- bits = new Variant("1", bits);
14
+ bits = Value.variant("1", bits);
15
15
  }
16
16
 
17
- const wire = encodeToWire(new Variant("+", bits), pattern);
17
+ const wire = encodeToWire(Value.variant("+", bits), pattern);
18
18
  const decoded = decodeWire(wire).value;
19
19
  assert.equal(decoded.tag, "+");
20
20
 
@@ -1,7 +1,7 @@
1
1
  import assert from "node:assert/strict";
2
2
 
3
3
  import { createState, evaluateInput } from "../repl.mjs";
4
- import { Product } from "../Value.mjs";
4
+ import { Value, isProduct } from "../Value.mjs";
5
5
  import codes from "../codes.mjs";
6
6
  import { parse as parseFloat64, print as printFloat64 } from "../codecs/ieee.mjs";
7
7
  import { singletonPatternToCodeHash, valueForCode } from "../repl-codecs.mjs";
@@ -21,7 +21,7 @@ function float64(text) {
21
21
  }
22
22
 
23
23
  function floatPair(x, y) {
24
- return valueForCode(new Product({
24
+ return valueForCode(Value.product({
25
25
  x: float64(x),
26
26
  y: float64(y)
27
27
  }), floatPairHash, codes.find);
@@ -46,7 +46,7 @@ function assertFloatResult(value, expected, label) {
46
46
 
47
47
  async function assertOperation({ op, x, y, result, flags = null, projection = false }) {
48
48
  const bundle = await runOperation(op, x, y);
49
- assert.ok(bundle instanceof Product, `${op} should return a result bundle`);
49
+ assert.ok(isProduct(bundle), `${op} should return a result bundle`);
50
50
  assert.deepEqual(Object.keys(bundle.product).sort(), ["flags", "result"]);
51
51
  assertFloatResult(bundle.product.result, result, `${op}(${x}, ${y}).result`);
52
52
  if (flags != null) {
@@ -3,7 +3,7 @@ import fs from "node:fs";
3
3
  import k from "../index.mjs";
4
4
  import { parseValue } from "../valueIO.mjs";
5
5
  import { decodeWire, encodeToWire, CORE_PATTERN_PROPERTY_LIST } from "../codecs/runtime/prefix-codec.mjs";
6
- import { Product, Variant } from "../Value.mjs";
6
+ import { Value } from "../Value.mjs";
7
7
  import { exportPatternGraph } from "../codecs/runtime/codec.mjs";
8
8
  import { patternToPropertyList } from "../codecs/runtime/pattern-json.mjs";
9
9
  import {
@@ -27,9 +27,9 @@ assert.deepEqual(decodedWire.pattern, [
27
27
  ]);
28
28
  assert.deepEqual(decodedWire.value.toJSON(), value.toJSON());
29
29
 
30
- const unicodeLabelValue = new Product({
31
- "é": new Variant("🙂", new Product({})),
32
- "𐐷": new Product({})
30
+ const unicodeLabelValue = Value.product({
31
+ "é": Value.variant("🙂", Value.product({})),
32
+ "𐐷": Value.product({})
33
33
  });
34
34
  const unicodeWire = encodeToWire(unicodeLabelValue, null);
35
35
  const decodedUnicode = decodeWire(unicodeWire);
@@ -73,15 +73,15 @@ assert(object.meta[object.relAlias.__main__]?.origins?.some((origin) =>
73
73
  ));
74
74
 
75
75
  const objectFn = objectToFunction(object);
76
- const objectResult = objectFn(new Product({}));
76
+ const objectResult = objectFn(Value.product({}));
77
77
  assert.deepEqual(objectResult.pattern, INT_PATTERN);
78
78
  assert.deepEqual(objectResult.toJSON(), { "+": { "1": { "0": "_" } } });
79
- const convergedObjectResult = runConvergedObject(object, new Product({}));
79
+ const convergedObjectResult = runConvergedObject(object, Value.product({}));
80
80
  assert.deepEqual(convergedObjectResult.pattern, INT_PATTERN);
81
81
  assert.deepEqual(convergedObjectResult.toJSON(), objectResult.toJSON());
82
82
 
83
83
  const decompiledSource = decompileObjectBuffer(objectBuffer);
84
- const decompiledResult = k.compile(decompiledSource)(new Product({}));
84
+ const decompiledResult = k.compile(decompiledSource)(Value.product({}));
85
85
  assert.deepEqual(decompiledResult.pattern, INT_PATTERN);
86
86
  assert.deepEqual(decompiledResult.toJSON(), objectResult.toJSON());
87
87
  assert.match(decompiledSource, /^----- codes -----$/m);
@@ -112,11 +112,11 @@ assert.equal(arithmeticsRoundTripSource, arithmeticsDecompiledSource);
112
112
 
113
113
  assert.throws(() => k.compile("@A = (); @A"), /Parse error/);
114
114
  assert.throws(() => k.compile("$ @A = {}; $@A"), /Parse error/);
115
- assert.deepEqual(k.compile("7jfi = ?X0; 7jfi")(new Product({})).toJSON(), {});
115
+ assert.deepEqual(k.compile("7jfi = ?X0; 7jfi")(Value.product({})).toJSON(), {});
116
116
 
117
117
  const sccSource = decompileObjectBuffer(compileObjectBuffer("c = {}; b = c |x; a = b |y; a"));
118
118
  assert.match(sccSource, /----- rels -----\nPQgV = .+\n\naQAD = .+\n\nN9UH = /s);
119
- assert.deepEqual(k.compile(sccSource)(new Product({})).toJSON(), { y: "x" });
119
+ assert.deepEqual(k.compile(sccSource)(Value.product({})).toJSON(), { y: "x" });
120
120
 
121
121
  const libraryBuffer = compileLibraryBuffer("$ nat = <{} zero, nat succ>;\nsucc = |succ;\n", { source: "defs-only.k" });
122
122
  assert.equal(libraryBuffer.subarray(0, 1).toString("utf8"), "{");
@@ -173,7 +173,7 @@ assert(derivedAliases.indexOf("other = @") < derivedAliases.indexOf("succ = @"))
173
173
  }));
174
174
  assert.equal(nonConverged.rels.__main__.typeDerivation.status, "not-converged");
175
175
  assert.throws(
176
- () => runConvergedObject(nonConverged, new Product({ x: new Product({}) })),
176
+ () => runConvergedObject(nonConverged, Value.product({ x: Value.product({}) })),
177
177
  /type derivation is not-converged/
178
178
  );
179
179
  } finally {