@fraczak/k 6.10.4 → 6.10.6

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/core.k CHANGED
@@ -4,15 +4,12 @@
4
4
  -- 1. $bits — the canonical binary number / bit-path type
5
5
  -- 2. $string — unicode strings (used for labels)
6
6
  -- 3. $pattern — the type of pattern graphs
7
- -- 4. Basic $bits arithmetic: succ, plus, times
8
7
 
9
8
  ---------------------------------------------------------------
10
9
  -- §1. Bits
11
10
  ---------------------------------------------------------------
12
11
 
13
12
  $ bit = < {} 0, {} 1 >;
14
- b0 = {}|0 $bit;
15
- b1 = {}|1 $bit;
16
13
 
17
14
  $ bits = < {} _, bits 0, bits 1 >;
18
15
 
@@ -21,78 +18,8 @@ _ = {}|_;
21
18
  0 = _ |0;
22
19
  1 = _ |1;
23
20
 
24
- inv = {() x, _ r} _inv;
25
- _inv = ${bits x, bits r} < {.x /0 x, .r |0 r} _inv, {.x /1 x, .r |1 r} _inv, .r >;
26
-
27
- concat = ${bits x, bits y} {.x inv x, .y y} concat_;
28
- concat_ = ${bits x, bits y}
29
- <
30
- {.x /0 x, .y |0 y} concat_,
31
- {.x /1 x, .y |1 y} concat_,
32
- .y
33
- >;
34
-
35
- ---------------------------------------------------------------
36
- -- §2. Bits arithmetic
37
- ---------------------------------------------------------------
38
-
39
- -- successor (increment by 1)
40
- succ = {inv x, _ y} _inc;
41
-
42
- _inc = ?{X x, X y}
43
- <
44
- {.x </0,/_|_> x, .y |1 y} concat_,
45
- {.x /1 x, .y |0 y} _inc
46
- > ?X;
47
-
48
- -- constants
49
- 2 = 1 succ;
50
- 3 = 2 succ;
51
- 4 = 3 succ;
52
- 5 = 4 succ;
53
- 6 = 5 succ;
54
- 7 = 6 succ;
55
- 8 = 7 succ;
56
- 9 = 8 succ;
57
- 10 = 9 succ;
58
-
59
- -- addition on $bits (unsigned, LSB-first with carry)
60
- plus = ${bits x, bits y} {.x inv x, .y inv y, b0 c} _plus;
61
- _plus = ${bits x, bits y, bit c}
62
- <
63
- {.x /_ if, .y /_ and, <.c/0 0, 1> then} .then,
64
- {.x /_ if, .c/0 and, .y then} .then,
65
- {.x y, .y /_|_ x, .c c} _plus,
66
- {<.c/0 0, 1> x, .y y, .x /_ b0 c} _plus,
67
- {.x /0 x, .y /0 y, .c/0 b0 c} _plus |0,
68
- {.x /0 x, .y /0 y, .c/1 b0 c} _plus |1,
69
- {.x /0 x, .y /1 y, .c/0 b0 c} _plus |1,
70
- {.x /0 x, .y /1 y, .c/1 b1 c} _plus |0,
71
- {.x /1 x, .y /0 y, .c/0 b0 c} _plus |1,
72
- {.x /1 x, .y /0 y, .c/1 b1 c} _plus |0,
73
- {.x /1 x, .y /1 y, .c/0 b1 c} _plus |0,
74
- {.x /1 x, .y /1 y, .c/1 b1 c} _plus |1
75
- >;
76
-
77
- -- multiplication on $bits (unsigned, shift-and-add)
78
- times = ${bits x, bits y} {0 result, .x inv _x, .y y} _times;
79
- _times = ${bits result, bits _x, bits y}
80
- <
81
- { .y /_ if, .result then } .then,
82
- {
83
- .result |0 result,
84
- ._x _x,
85
- .y /0 y
86
- } _times,
87
- {
88
- {._x x, .result |0 y, b0 c} _plus result,
89
- ._x _x,
90
- .y /1 y
91
- } _times
92
- >;
93
-
94
21
  ---------------------------------------------------------------
95
- -- §3. Unicode and String
22
+ -- §2. Unicode and String
96
23
  ---------------------------------------------------------------
97
24
 
98
25
  -- 8-bit octet used as a building block for UTF-8 payload pieces.
@@ -108,8 +35,9 @@ $ ascii = { bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 };
108
35
  -- Plane 0 is the Basic Multilingual Plane (BMP): U+0000..U+FFFF.
109
36
  $ u0080_07FF = { bit 10, bit 9, bit 8, bit 7, bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 };
110
37
 
111
- -- High-byte classes for common BMP ranges, excluding surrogate/private-use partitions.
112
- -- BMP (Basic Multilingual Plane) is Plane 0: the first 65,536 code points.
38
+ -- High-byte classes for common BMP ranges, excluding surrogate and
39
+ -- private-use partitions.
40
+ -- BMP (Basic Multilingual Plane) is Plane 0: first 65,536 code points.
113
41
  $ bmp_hi_common = <
114
42
  { bit 2, bit 1, bit 0 } h08_0F,
115
43
  { bit 6, bit 5, bit 4, bit 3, bit 2, bit 1, bit 0 } h10_7F,
@@ -158,12 +86,12 @@ $ unicode = <
158
86
  $ string = < {} nil, { unicode car, string cdr } cons >;
159
87
 
160
88
  ---------------------------------------------------------------
161
- -- §4. Pattern
89
+ -- §3. Pattern
162
90
  ---------------------------------------------------------------
163
91
 
164
92
  -- Edge: a labeled edge in a pattern graph.
165
93
  -- 'label' is the field name or tag name.
166
- -- 'target' is the index (as $bits) of the target node in the pattern list.
94
+ -- 'target' is index (as $bits) of the target node in the pattern list.
167
95
 
168
96
  $ edge = { string label, bits target };
169
97
 
@@ -48,7 +48,7 @@ export async function createPolySuite({ listLength = 40, cacheDir = null } = {})
48
48
  return lines.join("\n") + "\n";
49
49
  }
50
50
 
51
- const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?", "nil", "cons", "car", "cdr"];
51
+ const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?"];
52
52
  const exportPreamble = buildExportPreamble(neededExports, [arithmeticsLib]);
53
53
  const polyLib = loadLibrary(compileLibrary(exportPreamble + polySource, {
54
54
  source: polyPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraczak/k",
3
- "version": "6.10.4",
3
+ "version": "6.10.6",
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
  "exports": {
package/repl-codecs.mjs CHANGED
@@ -107,8 +107,10 @@ function registerCodec(state, rawCodec, filePath) {
107
107
 
108
108
  function unregisterCodec(state, codecName) {
109
109
  const store = codecStore(state);
110
- if (!store[codecName]) return 0;
111
- delete store[codecName];
110
+ const baseName = path.basename(codecName);
111
+ const key = store[codecName] ? codecName : (store[baseName] ? baseName : null);
112
+ if (!key) return 0;
113
+ delete store[key];
112
114
  return 1;
113
115
  }
114
116
 
@@ -129,7 +131,8 @@ function resolveCodec(state, codecName, capability = null) {
129
131
  throw new Error("Codec name is required");
130
132
  }
131
133
  const store = codecStore(state);
132
- const codec = store[codecName];
134
+ const baseName = path.basename(codecName);
135
+ const codec = store[codecName] || store[baseName];
133
136
  if (!codec) {
134
137
  throw new Error(`Codec '${codecName}' is not loaded`);
135
138
  }
@@ -78,8 +78,8 @@ if (fs.existsSync(coreKPath)) {
78
78
  vfsFiles["core.k"] = fs.readFileSync(coreKPath, "utf8");
79
79
  }
80
80
 
81
- // Add verified k-code files: arithmetics.k, ieee.k, poly.k
82
- const verifiedExamples = ["arithmetics.k", "ieee.k", "poly.k"];
81
+ // Add verified k-code files: arithmetics.k, ieee.k, poly.k, karatsuba-mult.k
82
+ const verifiedExamples = ["arithmetics.k", "ieee.k", "poly.k", "karatsuba-mult.k"];
83
83
  const examplesDir = path.join(projectRoot, "Examples");
84
84
  for (const filename of verifiedExamples) {
85
85
  const fullPath = path.join(examplesDir, filename);
@@ -131,7 +131,6 @@ for (const filename of verifiedCodecs) {
131
131
  });
132
132
  const bundledContent = buildRes.outputFiles[0].text;
133
133
  vfsFiles[filename] = bundledContent;
134
- vfsFiles[`codecs/${filename}`] = bundledContent;
135
134
  }
136
135
  }
137
136
 
@@ -1663,14 +1662,15 @@ const htmlContent = `<!DOCTYPE html>
1663
1662
  <option value="arithmetics.k">arithmetics.k</option>
1664
1663
  <option value="ieee.k">ieee.k</option>
1665
1664
  <option value="poly.k">poly.k</option>
1665
+ <option value="karatsuba-mult.k">karatsuba-mult.k</option>
1666
1666
  </optgroup>
1667
1667
  <optgroup label="Quick Expressions">
1668
1668
  <option value="expr:{} | ok">{} | ok</option>
1669
1669
  <option value="expr:()">()</option>
1670
1670
  <option value="expr:$ bool = &lt; {} true, {} false &gt;; not = $ bool &lt; / true | false, {} | true &gt; $ bool; {} | true not">not (boolean relation with fallback)</option>
1671
- <option value="expr:{10 x, 5 y} plus">{10 x, 5 y} plus (bits)</option>
1672
1671
  <option value="expr:1 succ">1 succ</option>
1673
1672
  <option value="expr:{10 int x, 5 int y} plus">{10 int x, 5 int y} plus (int)</option>
1673
+ <option value="expr:{10 int x, 5 int y} karatsuba">{10 int x, 5 int y} karatsuba</option>
1674
1674
  <option value="expr::types">:types (list types)</option>
1675
1675
  <option value="expr::rels">:rels (list relations)</option>
1676
1676
  <option value="expr::engine">:engine (show engine)</option>
@@ -1693,8 +1693,8 @@ const htmlContent = `<!DOCTYPE html>
1693
1693
  </div>
1694
1694
 
1695
1695
  <!-- File Upload -->
1696
- <label class="btn btn-secondary btn-file" title="Upload local .k or .klib file into VFS">
1697
- <input type="file" id="file-upload" accept=".k,.klib,.wat,.ko" style="display:none;">
1696
+ <label class="btn btn-secondary btn-file" title="Upload local file into VFS">
1697
+ <input type="file" id="file-upload" accept=".k,.klib,.wat,.ko,.mjs,.js,.json" style="display:none;">
1698
1698
  <span>📁 Upload</span>
1699
1699
  </label>
1700
1700
 
@@ -193,7 +193,7 @@ function buildExportPreamble(exports, libraries) {
193
193
  return lines.join("\n") + "\n";
194
194
  }
195
195
 
196
- const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?", "nil", "cons", "car", "cdr"];
196
+ const neededExports = ["0", "int", "inc", "dec", "nat", "zero_int?"];
197
197
  const exportPreamble = buildExportPreamble(neededExports, [arithmeticsLib]);
198
198
  const polyLib = loadLibrary(compileLibrary(exportPreamble + polySource, {
199
199
  source: polyPath,
@@ -100,10 +100,13 @@ try {
100
100
  assertOk(resDec, "2 int dec dec zero_int? should succeed");
101
101
 
102
102
  const divTestCases = [
103
+ { x: "0", yAlias: "7", yVal: 7n },
104
+ { x: "3", yAlias: "7", yVal: 7n },
103
105
  { x: "4", yAlias: "1", yVal: 1n },
104
106
  { x: "10", yAlias: "2", yVal: 2n },
105
107
  { x: "12", yAlias: "3", yVal: 3n },
106
108
  { x: "11", yAlias: "3", yVal: 3n },
109
+ { x: "12", yAlias: "12", yVal: 12n },
107
110
  { x: "100", yAlias: "7", yVal: 7n },
108
111
  { x: "-100", yAlias: "7", yVal: 7n },
109
112
  { x: "1000", yAlias: "10", yVal: 10n }
@@ -143,6 +146,99 @@ try {
143
146
  assert.equal(remOut, expectedRem, `div(${x}, ${yAlias}) remainder should match BigInt oracle`);
144
147
  }
145
148
 
149
+ // Regression tests for EQ
150
+ const dummyWire = parseIntWire("0");
151
+ const resEqTrue = run(node, [
152
+ "k.mjs",
153
+ "--lib", libPath,
154
+ "--export", "EQ:EQ",
155
+ "--export", "int:int",
156
+ "--export", "2:two",
157
+ "{two x, two y} EQ .x int"
158
+ ], { input: dummyWire });
159
+ assertOk(resEqTrue, "{2, 2} EQ should succeed");
160
+
161
+ const resEqFalse = run(node, [
162
+ "k.mjs",
163
+ "--lib", libPath,
164
+ "--export", "EQ:EQ",
165
+ "--export", "int:int",
166
+ "--export", "2:two",
167
+ "--export", "3:three",
168
+ "{two x, three y} EQ .x int"
169
+ ], { input: dummyWire });
170
+ assert.notEqual(resEqFalse.status, 0, "{2, 3} EQ should fail");
171
+
172
+ // Regression tests for pos?
173
+ const resPosTrue = run(node, [
174
+ "k.mjs",
175
+ "--lib", libPath,
176
+ "--export", "pos?:pos?",
177
+ "--export", "int:int",
178
+ "--export", "2:two",
179
+ "two int pos?"
180
+ ], { input: dummyWire });
181
+ assertOk(resPosTrue, "2 int pos? should succeed");
182
+
183
+ const resPosZero = run(node, [
184
+ "k.mjs",
185
+ "--lib", libPath,
186
+ "--export", "pos?:pos?",
187
+ "--export", "int:int",
188
+ "--export", "0:zero",
189
+ "zero int pos?"
190
+ ], { input: dummyWire });
191
+ assert.notEqual(resPosZero.status, 0, "0 int pos? should fail");
192
+
193
+ // Regression tests for mod, gcd, lcm
194
+ const resMod = run(node, [
195
+ "k.mjs",
196
+ "--lib", libPath,
197
+ "--export", "mod:mod",
198
+ "--export", "int:int",
199
+ "--export", "10:ten",
200
+ "--export", "3:three",
201
+ "{ten x, three y} mod int"
202
+ ], { input: dummyWire });
203
+ assertOk(resMod, "10 % 3 mod");
204
+ assert.equal(printIntWire(resMod.stdout, "mod"), "1");
205
+
206
+ const resGcd = run(node, [
207
+ "k.mjs",
208
+ "--lib", libPath,
209
+ "--export", "gcd:gcd",
210
+ "--export", "int:int",
211
+ "--export", "12:twelve",
212
+ "--export", "8:eight",
213
+ "{twelve x, eight y} gcd int"
214
+ ], { input: dummyWire });
215
+ assertOk(resGcd, "gcd(12, 8)");
216
+ assert.equal(printIntWire(resGcd.stdout, "gcd"), "4");
217
+
218
+ const resGcdZero = run(node, [
219
+ "k.mjs",
220
+ "--lib", libPath,
221
+ "--export", "gcd:gcd",
222
+ "--export", "int:int",
223
+ "--export", "4:four",
224
+ "--export", "0b:zero_b",
225
+ "{four x, zero_b y} gcd int"
226
+ ], { input: dummyWire });
227
+ assertOk(resGcdZero, "gcd(4, 0b)");
228
+ assert.equal(printIntWire(resGcdZero.stdout, "gcd 0b"), "4");
229
+
230
+ const resLcm = run(node, [
231
+ "k.mjs",
232
+ "--lib", libPath,
233
+ "--export", "lcm:lcm",
234
+ "--export", "int:int",
235
+ "--export", "4:four",
236
+ "--export", "6:six",
237
+ "{four x, six y} lcm int"
238
+ ], { input: dummyWire });
239
+ assertOk(resLcm, "lcm(4, 6)");
240
+ assert.equal(printIntWire(resLcm.stdout, "lcm"), "12");
241
+
146
242
  console.log("OK");
147
243
  } finally {
148
244
  fs.rmSync(tmpDir, { recursive: true, force: true });
@@ -144,6 +144,7 @@ if (chromiumBin) {
144
144
  "ieee.mjs",
145
145
  "int.mjs",
146
146
  "json.mjs",
147
+ "karatsuba-mult.k",
147
148
  "poly.k",
148
149
  "unit.mjs",
149
150
  "utf8.mjs"
@@ -158,6 +159,22 @@ if (chromiumBin) {
158
159
  })()`);
159
160
  assert(loadOut.includes("loaded arithmetics.k"), `Expected loaded arithmetics.k, got: ${loadOut}`);
160
161
 
162
+ // Test :load karatsuba-mult.k
163
+ const loadKaratsubaOut = await evaluateAsync(`(async () => {
164
+ await window.kRepl.executeCommand(":load karatsuba-mult.k");
165
+ const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
166
+ return lines[lines.length - 1];
167
+ })()`);
168
+ assert(loadKaratsubaOut.includes("loaded karatsuba-mult.k"), `Expected loaded karatsuba-mult.k, got: ${loadKaratsubaOut}`);
169
+
170
+ // Test evaluating {1 int x, 1 int y} karatsuba
171
+ const karatsubaVal = await evaluateAsync(`(async () => {
172
+ await window.kRepl.executeCommand("{1 int x, 1 int y} karatsuba");
173
+ const lines = Array.from(document.querySelectorAll(".entry-line")).map(el => el.textContent);
174
+ return lines[lines.length - 1];
175
+ })()`);
176
+ assert(karatsubaVal.includes("{}|_|1|+"), `Expected {}|_|1|+ in output, got: ${karatsubaVal}`);
177
+
161
178
  // Test evaluating 10
162
179
  const valOut = await evaluateAsync(`(async () => {
163
180
  await window.kRepl.executeCommand("10");
@@ -618,7 +635,81 @@ if (chromiumBin) {
618
635
  assert.strictEqual(engineCheck.jsEngine, "js");
619
636
  assert.strictEqual(engineCheck.badgeJsText, "js-in-process");
620
637
  assert.strictEqual(engineCheck.wasmEngine, "wasm");
621
- assert.strictEqual(engineCheck.badgeWasmText, "wasm-in-process");
638
+ // 7. Test uploading a file (adds to VFS without auto-loading)
639
+ const uploadCheck = await evaluateAsync(`(async () => {
640
+ const codecCode = \`export default {
641
+ name: "custom.mjs",
642
+ target: "(...)",
643
+ format: "custom",
644
+ parse: (raw) => raw,
645
+ print: (v) => "CUSTOM_VAL"
646
+ };\`;
647
+ const file = new File(
648
+ [codecCode],
649
+ "custom.mjs",
650
+ { type: "text/javascript" }
651
+ );
652
+ await window.kRepl.handleFileUpload(file);
653
+
654
+ const vfsContent = window.kRepl.getVfsFile("custom.mjs");
655
+ const inVfsList = window.kRepl.getAllVfsFiles().includes("custom.mjs");
656
+
657
+ // Verify system message showed upload confirmation
658
+ const systemMessages = Array.from(
659
+ document.querySelectorAll(".system-msg")
660
+ ).map(el => el.textContent);
661
+ const hasUploadedMsg = systemMessages.some(
662
+ m => m.includes("Uploaded custom.mjs into VFS")
663
+ );
664
+
665
+ // Verify it was NOT auto-loaded (:codecs does not list it)
666
+ await window.kRepl.executeCommand(":codecs");
667
+ const linesBefore = Array.from(
668
+ document.querySelectorAll(".entry-line")
669
+ ).map(el => el.textContent);
670
+ const codecsBefore = linesBefore[linesBefore.length - 1];
671
+
672
+ // Explicitly load it to prove it can be loaded as a codec
673
+ await window.kRepl.executeCommand(":codec load custom.mjs");
674
+ const linesAfter = Array.from(
675
+ document.querySelectorAll(".entry-line")
676
+ ).map(el => el.textContent);
677
+ const codecsAfter = linesAfter[linesAfter.length - 1];
678
+
679
+ return {
680
+ hasVfsContent: typeof vfsContent === "string" &&
681
+ vfsContent.includes("custom.mjs"),
682
+ inVfsList,
683
+ hasUploadedMsg,
684
+ codecsBefore,
685
+ codecsAfter
686
+ };
687
+ })()`);
688
+ assert.strictEqual(
689
+ uploadCheck.hasVfsContent,
690
+ true,
691
+ "Uploaded file must be present in VFS"
692
+ );
693
+ assert.strictEqual(
694
+ uploadCheck.inVfsList,
695
+ true,
696
+ "Uploaded file must be in getAllVfsFiles()"
697
+ );
698
+ assert.strictEqual(
699
+ uploadCheck.hasUploadedMsg,
700
+ true,
701
+ "Upload confirmation message must be displayed"
702
+ );
703
+ assert.strictEqual(
704
+ uploadCheck.codecsBefore.includes("custom.mjs"),
705
+ false,
706
+ "Upload must NOT auto-load the file"
707
+ );
708
+ assert.strictEqual(
709
+ uploadCheck.codecsAfter.includes("custom.mjs"),
710
+ true,
711
+ "Explicit :codec load of uploaded file must succeed"
712
+ );
622
713
 
623
714
  ws.close();
624
715
  console.log(" Browser execution verified successfully!");