@fraczak/k 6.8.0 → 6.10.1
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/CHANGELOG.md +40 -0
- package/CLAUDE.md +1 -1
- package/DOCS/BIG_PICTURE.md +3 -5
- package/DOCS/CODECS.md +80 -181
- package/DOCS/CODEC_ARCHITECTURE.md +194 -0
- package/DOCS/REPL.md +69 -51
- package/Examples/arithmetics.k +1 -0
- package/Examples/poly.k +46 -0
- package/backends/llvm/runtime/krt.c +4 -0
- package/backends/llvm/src/kvm2llvm.mjs +2 -2
- package/backends/llvm/tests/test-llvm.mjs +19 -0
- package/backends/wasm/src/kvm2wasm.mjs +4 -0
- package/backends/wasm/tests/test-wasm.mjs +4 -0
- package/browser/repl-browser.mjs +131 -779
- package/browser/shims/process.mjs +7 -0
- package/browser/tree-view.mjs +5 -2
- package/browser/vfs-data.mjs +14 -14
- package/codecs/README.md +7 -0
- package/codecs/ieee.mjs +50 -75
- package/codecs/int.mjs +83 -62
- package/codecs/json-codec.mjs +5 -77
- package/codecs/json.mjs +12 -56
- package/codecs/k-parse.mjs +3 -19
- package/codecs/runtime/codec-sdk.mjs +94 -0
- package/codecs/runtime/pattern-k.mjs +45 -0
- package/codecs/string-codec.mjs +48 -74
- package/codecs/unit.mjs +13 -61
- package/codecs/utf16.mjs +13 -56
- package/codecs/utf8.mjs +11 -55
- package/package.json +1 -1
- package/patterns/from-k.mjs +2 -22
- package/repl-codecs.mjs +69 -196
- package/repl.mjs +177 -281
- package/scripts/build-repl-html.mjs +114 -385
- package/scripts/run-tests.mjs +1 -0
- package/tests/test-codecs.mjs +186 -0
- package/tests/test-poly.mjs +44 -0
- package/tests/test-repl-html.mjs +163 -68
- package/tests/test-repl.mjs +210 -156
- package/tests/test-structural-values.mjs +17 -0
- package/tests/test-tree-view.mjs +3 -0
- package/codecs/example-pipeline.mjs +0 -163
- package/codecs/runtime/ieee-pattern.mjs +0 -30
- package/codecs/runtime/test-codec.mjs +0 -89
- package/codecs/runtime/typeFromValue.mjs +0 -34
package/tests/test-repl.mjs
CHANGED
|
@@ -29,16 +29,16 @@ output = await evaluateInput(":type nat", state);
|
|
|
29
29
|
assert.match(output[0], /^\$ nat = /);
|
|
30
30
|
|
|
31
31
|
output = await evaluateInput("succ = | succ;", state);
|
|
32
|
-
output = await evaluateInput(":
|
|
32
|
+
output = await evaluateInput(":rel succ", state);
|
|
33
33
|
assert.match(output[0], /^succ = /);
|
|
34
34
|
|
|
35
|
-
output = await evaluateInput(":
|
|
35
|
+
output = await evaluateInput(":types", state);
|
|
36
36
|
assert.match(output[0], /^nat = @/m);
|
|
37
37
|
|
|
38
38
|
output = await evaluateInput(":rels", state);
|
|
39
39
|
assert.match(output[0], /^succ = @/m);
|
|
40
40
|
|
|
41
|
-
output = await evaluateInput("
|
|
41
|
+
output = await evaluateInput("succ", state);
|
|
42
42
|
assert.equal(output[0], "{}|succ ?<{} succ, ...>");
|
|
43
43
|
|
|
44
44
|
output = await evaluateInput("/zero", state);
|
|
@@ -48,24 +48,38 @@ assert.equal(state.value.toJSON(), "succ");
|
|
|
48
48
|
output = await evaluateInput("succ", state);
|
|
49
49
|
assert.match(output[0], /^\{\}\|succ\|succ \?</);
|
|
50
50
|
|
|
51
|
-
output = await evaluateInput(":
|
|
52
|
-
assert.match(output[0], /^succ : /);
|
|
53
|
-
|
|
54
|
-
output = await evaluateInput(":d succ", state);
|
|
51
|
+
output = await evaluateInput(":rel succ", state);
|
|
55
52
|
assert.match(output[0], /^succ = /);
|
|
56
53
|
|
|
57
54
|
let completions = completeInput(":he", state)[0];
|
|
58
55
|
assert.deepEqual(completions, [":help"]);
|
|
59
56
|
|
|
60
57
|
const canonicalPartial = state.typeAliases.nat.slice(0, 8);
|
|
61
|
-
completions = completeInput(`:
|
|
58
|
+
completions = completeInput(`:rel ${canonicalPartial}`, state)[0];
|
|
62
59
|
assert(completions.some((line) => line.endsWith(state.typeAliases.nat)));
|
|
63
60
|
|
|
64
61
|
completions = completeInput(`(${canonicalPartial}`, state)[0];
|
|
65
62
|
assert(completions.includes(`(${state.typeAliases.nat}`));
|
|
66
63
|
|
|
67
|
-
completions = completeInput(":
|
|
68
|
-
assert(completions.includes(":
|
|
64
|
+
completions = completeInput(":rel su", state)[0];
|
|
65
|
+
assert(completions.includes(":rel succ"));
|
|
66
|
+
completions = completeInput(":ty", state)[0];
|
|
67
|
+
assert(completions.includes(":types"));
|
|
68
|
+
assert(completions.includes(":type"));
|
|
69
|
+
|
|
70
|
+
await assert.rejects(
|
|
71
|
+
() => evaluateInput(":run succ", state),
|
|
72
|
+
/Unknown command ':run'/
|
|
73
|
+
);
|
|
74
|
+
await assert.rejects(
|
|
75
|
+
() => evaluateInput(":time ()", state),
|
|
76
|
+
/Unknown command ':time'/
|
|
77
|
+
);
|
|
78
|
+
await assert.rejects(
|
|
79
|
+
() => evaluateInput(":t succ", state),
|
|
80
|
+
/Unknown command ':t'/
|
|
81
|
+
);
|
|
82
|
+
|
|
69
83
|
completions = completeInput(":co", state)[0];
|
|
70
84
|
assert(completions.includes(":codec"));
|
|
71
85
|
|
|
@@ -134,11 +148,11 @@ fs.writeFileSync(sourcePath, "$ nat = <{} zero, nat succ>;\nsucc = $nat |succ $n
|
|
|
134
148
|
const loadedSource = createState();
|
|
135
149
|
output = await evaluateInput(`:load ${sourcePath}`, loadedSource);
|
|
136
150
|
assert.equal(output[0], `loaded ${sourcePath}`);
|
|
137
|
-
output = await evaluateInput(":
|
|
151
|
+
output = await evaluateInput(":types", loadedSource);
|
|
138
152
|
assert.match(output[0], /^nat = @/m);
|
|
139
153
|
output = await evaluateInput(":rels", loadedSource);
|
|
140
154
|
assert.match(output[0], /^succ = @/m);
|
|
141
|
-
output = await evaluateInput(":
|
|
155
|
+
output = await evaluateInput(":rel twice", loadedSource);
|
|
142
156
|
assert.match(output[0], /^twice = \$nat succ succ \$nat; -- @/);
|
|
143
157
|
output = await evaluateInput(":C nat", loadedSource);
|
|
144
158
|
assert.match(output[0], /^\$ nat = < nat succ, @[^ ]+ zero >; -- @/);
|
|
@@ -160,6 +174,9 @@ export function parse(text) {
|
|
|
160
174
|
}
|
|
161
175
|
|
|
162
176
|
export function print(value) {
|
|
177
|
+
if (!value || (value.tag !== "true" && value.tag !== "false")) {
|
|
178
|
+
throw new Error("expected true or false");
|
|
179
|
+
}
|
|
163
180
|
return value.tag;
|
|
164
181
|
}
|
|
165
182
|
`);
|
|
@@ -172,58 +189,52 @@ assert(completions.includes(":codec list"));
|
|
|
172
189
|
completions = completeInput(`:codec load ${tmpDir}/bool`, codecState)[0];
|
|
173
190
|
assert(completions.includes(`:codec load ${codecPath}`));
|
|
174
191
|
output = await evaluateInput(`:codec load ${codecPath}`, codecState);
|
|
175
|
-
assert.equal(output[0], `loaded codec
|
|
192
|
+
assert.equal(output[0], `loaded codec bool-codec.mjs`);
|
|
176
193
|
output = await evaluateInput(":codec list", codecState);
|
|
177
|
-
assert.match(output[0],
|
|
178
|
-
completions = completeInput(":input
|
|
179
|
-
assert(completions.includes(":input
|
|
180
|
-
completions = completeInput(":input
|
|
181
|
-
assert(completions.includes(":input bool
|
|
182
|
-
|
|
183
|
-
assert.
|
|
194
|
+
assert.match(output[0], /^bool-codec\.mjs/);
|
|
195
|
+
completions = completeInput(":input", codecState)[0];
|
|
196
|
+
assert(completions.includes(":input"));
|
|
197
|
+
completions = completeInput(":input ", codecState)[0];
|
|
198
|
+
assert(completions.includes(":input bool-codec.mjs"));
|
|
199
|
+
completions = completeInput(":input b", codecState)[0];
|
|
200
|
+
assert(completions.includes(":input bool-codec.mjs"));
|
|
201
|
+
output = await evaluateInput(":input bool-codec.mjs", codecState);
|
|
202
|
+
assert.equal(promptForState(codecState), "bool-codec.mjs> ");
|
|
184
203
|
output = await evaluateInput("true", codecState);
|
|
185
204
|
assert.match(output[0], /\{\}\|true \?</);
|
|
186
|
-
assert.match(output[0], /
|
|
205
|
+
assert.match(output[0], /bool-codec\.mjs: true/);
|
|
187
206
|
output = await evaluateInput("{} | false", codecState);
|
|
188
|
-
assert.
|
|
207
|
+
assert.match(output[0], /bool-codec\.mjs: false/);
|
|
208
|
+
output = await evaluateInput("{}", codecState);
|
|
209
|
+
assert.doesNotMatch(output[0], /bool-codec\.mjs:/);
|
|
189
210
|
|
|
190
211
|
const utf8State = createState();
|
|
191
212
|
output = await evaluateInput(":load core.k", utf8State);
|
|
192
213
|
assert.equal(output[0], "loaded core.k");
|
|
193
214
|
output = await evaluateInput(":codec load ./codecs/utf8.mjs", utf8State);
|
|
194
|
-
assert.equal(output[0], `loaded codec utf8
|
|
195
|
-
output = await evaluateInput(":input
|
|
196
|
-
assert.
|
|
197
|
-
output = await evaluateInput("hello", utf8State);
|
|
198
|
-
assert.match(output[0], /utf8: hello/);
|
|
215
|
+
assert.equal(output[0], `loaded codec utf8.mjs`);
|
|
216
|
+
output = await evaluateInput(":input utf8.mjs hello", utf8State);
|
|
217
|
+
assert.match(output[0], /utf8\.mjs: hello/);
|
|
199
218
|
|
|
200
219
|
const jsonState = createState();
|
|
201
|
-
output = await evaluateInput("$ bool = < {} true, {} false >;", jsonState);
|
|
202
220
|
output = await evaluateInput(":codec load ./codecs/json.mjs", jsonState);
|
|
203
|
-
assert.match(output[0], /^loaded codec json
|
|
221
|
+
assert.match(output[0], /^loaded codec json\.mjs/);
|
|
204
222
|
output = await evaluateInput(":codec list", jsonState);
|
|
205
|
-
assert.match(output[0], /^json
|
|
206
|
-
completions = completeInput(":input
|
|
207
|
-
assert(completions.includes(":input
|
|
208
|
-
output = await evaluateInput(":input
|
|
209
|
-
assert.equal(
|
|
210
|
-
assert.equal(promptForState(jsonState), "json> ");
|
|
223
|
+
assert.match(output[0], /^json\.mjs/);
|
|
224
|
+
completions = completeInput(":input j", jsonState)[0];
|
|
225
|
+
assert(completions.includes(":input json.mjs"));
|
|
226
|
+
output = await evaluateInput(":input json.mjs", jsonState);
|
|
227
|
+
assert.equal(promptForState(jsonState), "json.mjs> ");
|
|
211
228
|
output = await evaluateInput("true", jsonState);
|
|
212
229
|
assert.match(output[0], /\{\}\|true \?</);
|
|
213
|
-
assert.match(output[0], /json: true/);
|
|
230
|
+
assert.match(output[0], /json\.mjs: true/);
|
|
214
231
|
assert.equal(promptForState(jsonState), "> ");
|
|
215
|
-
output = await evaluateInput(":input
|
|
216
|
-
assert.equal(output[0], `input ${jsonState.typeAliases.bool}: enter value text`);
|
|
217
|
-
assert.equal(promptForState(jsonState), "json> ");
|
|
218
|
-
output = await evaluateInput("false", jsonState);
|
|
232
|
+
output = await evaluateInput(":input json.mjs false", jsonState);
|
|
219
233
|
assert.match(output[0], /\{\}\|false \?</);
|
|
220
|
-
assert.match(output[0], /json: false/);
|
|
234
|
+
assert.match(output[0], /json\.mjs: false/);
|
|
221
235
|
assert.equal(promptForState(jsonState), "> ");
|
|
222
|
-
output = await evaluateInput(
|
|
223
|
-
assert.match(output[0],
|
|
224
|
-
assert.equal(promptForState(jsonState), "json> ");
|
|
225
|
-
output = await evaluateInput('{"left":true,"right":false}', jsonState);
|
|
226
|
-
assert.match(output[0], /json: \{"left":true,"right":false\}/);
|
|
236
|
+
output = await evaluateInput(':input json.mjs {"left":true,"right":false}', jsonState);
|
|
237
|
+
assert.match(output[0], /json\.mjs: \{"left":true,"right":false\}/);
|
|
227
238
|
assert.equal(promptForState(jsonState), "> ");
|
|
228
239
|
|
|
229
240
|
const replPath = fileURLToPath(new URL("../repl.mjs", import.meta.url));
|
|
@@ -238,7 +249,7 @@ const reloaded = createState();
|
|
|
238
249
|
output = await evaluateInput(`:load ${libPath}`, reloaded);
|
|
239
250
|
assert.equal(output[0], `loaded ${libPath}`);
|
|
240
251
|
|
|
241
|
-
output = await evaluateInput(":
|
|
252
|
+
output = await evaluateInput(":types", reloaded);
|
|
242
253
|
assert.match(output[0], /^nat = @/m);
|
|
243
254
|
|
|
244
255
|
output = await evaluateInput(":rels", reloaded);
|
|
@@ -250,7 +261,7 @@ assert.equal(output[0], "{}|succ ?<{} succ, ...>");
|
|
|
250
261
|
const noAliasReloaded = createState();
|
|
251
262
|
output = await evaluateInput(`:load --no-alias ${libPath}`, noAliasReloaded);
|
|
252
263
|
assert.equal(output[0], `loaded ${libPath}`);
|
|
253
|
-
output = await evaluateInput(":
|
|
264
|
+
output = await evaluateInput(":types", noAliasReloaded);
|
|
254
265
|
assert.equal(output[0], "(none)");
|
|
255
266
|
output = await evaluateInput(":rels", noAliasReloaded);
|
|
256
267
|
assert.equal(output[0], "(none)");
|
|
@@ -258,7 +269,7 @@ assert.equal(output[0], "(none)");
|
|
|
258
269
|
const shorthand = createState();
|
|
259
270
|
output = await evaluateInput("$ nat = <{} zero, nat succ>\n; succ = |succ\n;", shorthand);
|
|
260
271
|
assert.match(output[0], /\/\* comp: .* \*\//);
|
|
261
|
-
output = await evaluateInput(":
|
|
272
|
+
output = await evaluateInput(":types", shorthand);
|
|
262
273
|
assert.match(output[0], /^nat = @/m);
|
|
263
274
|
output = await evaluateInput(":rels", shorthand);
|
|
264
275
|
assert.match(output[0], /^succ = @/m);
|
|
@@ -268,7 +279,7 @@ assert.match(output[0], / false/);
|
|
|
268
279
|
assert.match(output[0], / true/);
|
|
269
280
|
output = await evaluateInput("$ maybe = <{} none, {} some>;", shorthand);
|
|
270
281
|
assert.match(output[0], /\/\* comp: .* \*\//);
|
|
271
|
-
output = await evaluateInput(":
|
|
282
|
+
output = await evaluateInput(":types", shorthand);
|
|
272
283
|
assert.match(output[0], /^maybe = @/m);
|
|
273
284
|
output = await evaluateInput("{} | true not", shorthand);
|
|
274
285
|
assert.match(output[0], /^\{\}\|false \?</);
|
|
@@ -302,41 +313,45 @@ assert.match(output[0], /^\{\}\|_\|0\|1\|0\|1/);
|
|
|
302
313
|
output = await evaluateInput("{10 int x, 5 int y} plus", arithState);
|
|
303
314
|
assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
|
|
304
315
|
|
|
305
|
-
// Verify
|
|
316
|
+
// Verify codecs loading, listing, and unloading
|
|
306
317
|
const codecTestState = createState();
|
|
307
318
|
output = await evaluateInput(":codecs", codecTestState);
|
|
308
319
|
assert.equal(output[0], "(none)");
|
|
309
320
|
|
|
310
|
-
output = await evaluateInput(":codec load int", codecTestState);
|
|
311
|
-
assert.match(output[0], /^loaded codec int
|
|
321
|
+
output = await evaluateInput(":codec load ./codecs/int.mjs", codecTestState);
|
|
322
|
+
assert.match(output[0], /^loaded codec int\.mjs/);
|
|
312
323
|
|
|
313
324
|
output = await evaluateInput(":codecs", codecTestState);
|
|
314
|
-
assert.match(output[0], /^int
|
|
325
|
+
assert.match(output[0], /^int\.mjs/);
|
|
315
326
|
|
|
316
327
|
completions = completeInput(":codec un", codecTestState)[0];
|
|
317
328
|
assert(completions.includes(":codec unload"));
|
|
318
329
|
completions = completeInput(":codec unload ", codecTestState)[0];
|
|
319
|
-
assert(completions.includes(":codec unload int"));
|
|
320
|
-
completions = completeInput(":codec load i", codecTestState)[0];
|
|
321
|
-
assert(completions.includes(":codec load int"));
|
|
330
|
+
assert(completions.includes(":codec unload int.mjs"));
|
|
331
|
+
completions = completeInput(":codec load ./codecs/i", codecTestState)[0];
|
|
332
|
+
assert(completions.includes(":codec load ./codecs/int.mjs"));
|
|
322
333
|
|
|
323
|
-
output = await evaluateInput(":codec unload int", codecTestState);
|
|
324
|
-
assert.equal(output[0], "unloaded codec int");
|
|
334
|
+
output = await evaluateInput(":codec unload int.mjs", codecTestState);
|
|
335
|
+
assert.equal(output[0], "unloaded codec int.mjs");
|
|
325
336
|
output = await evaluateInput(":codecs", codecTestState);
|
|
326
337
|
assert.equal(output[0], "(none)");
|
|
327
338
|
|
|
328
339
|
output = await evaluateInput("$ bool = < {} true, {} false >;", codecTestState);
|
|
329
340
|
registerCodec(codecTestState, {
|
|
330
|
-
name: "yn",
|
|
331
|
-
codes: [codecTestState.typeAliases.bool],
|
|
332
|
-
universal: false,
|
|
341
|
+
name: "yn.mjs",
|
|
333
342
|
parse: (text) => Value.variant(text.trim() === "yes" ? "true" : "false", Value.product({})),
|
|
334
|
-
print: (v) =>
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
343
|
+
print: (v) => {
|
|
344
|
+
if (v.tag !== "true" && v.tag !== "false") throw new Error("not a bool");
|
|
345
|
+
return v.tag === "true" ? "YES" : "NO";
|
|
346
|
+
}
|
|
347
|
+
}, "yn.mjs");
|
|
348
|
+
|
|
349
|
+
output = await evaluateInput(":input yn.mjs", codecTestState);
|
|
350
|
+
assert.equal(promptForState(codecTestState), "yn.mjs> ");
|
|
338
351
|
output = await evaluateInput("yes", codecTestState);
|
|
339
|
-
assert.match(output[0], /yn: YES/);
|
|
352
|
+
assert.match(output[0], /yn\.mjs: YES/);
|
|
353
|
+
output = await evaluateInput(":input yn.mjs no", codecTestState);
|
|
354
|
+
assert.match(output[0], /yn\.mjs: NO/);
|
|
340
355
|
|
|
341
356
|
// Projection on product with variant field (avoids filter variable collisions)
|
|
342
357
|
const projState = createState();
|
|
@@ -351,103 +366,88 @@ assert.match(output[0], /^\{\{\}\|a a, \{\} y\}/);
|
|
|
351
366
|
output = await evaluateInput(".a", projState2);
|
|
352
367
|
assert.match(output[0], /^\{\}\|a/);
|
|
353
368
|
|
|
354
|
-
//
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
assert.
|
|
359
|
-
output = await evaluateInput(":input int",
|
|
360
|
-
output = await evaluateInput("123",
|
|
361
|
-
assert.match(output[0], /int: 123/);
|
|
369
|
+
// Explicit loading of dependencies and codec testing (no auto-loading)
|
|
370
|
+
const intState = createState();
|
|
371
|
+
await evaluateInput(":load Examples/arithmetics.k", intState);
|
|
372
|
+
output = await evaluateInput(":codec load ./codecs/int.mjs", intState);
|
|
373
|
+
assert.match(output[0], /^loaded codec int\.mjs/);
|
|
374
|
+
output = await evaluateInput(":input int.mjs", intState);
|
|
375
|
+
output = await evaluateInput("123", intState);
|
|
376
|
+
assert.match(output[0], /int\.mjs: 123/);
|
|
377
|
+
output = await evaluateInput(":input int.mjs", intState);
|
|
378
|
+
output = await evaluateInput("[0,1,2]", intState);
|
|
379
|
+
assert.match(output[0], /int\.mjs: \[0,1,2\]/);
|
|
362
380
|
|
|
363
381
|
const utf8AutoState = createState();
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
assert.
|
|
367
|
-
|
|
368
|
-
assert.equal(utf8AutoState.typeAliases.utf8, utf8AutoState.typeAliases.string);
|
|
369
|
-
output = await evaluateInput(":input utf8", utf8AutoState);
|
|
382
|
+
await evaluateInput(":load core.k", utf8AutoState);
|
|
383
|
+
output = await evaluateInput(":codec load ./codecs/utf8.mjs", utf8AutoState);
|
|
384
|
+
assert.match(output[0], /^loaded codec utf8\.mjs/);
|
|
385
|
+
output = await evaluateInput(":input utf8.mjs", utf8AutoState);
|
|
370
386
|
output = await evaluateInput("test utf8", utf8AutoState);
|
|
371
|
-
assert.match(output[0], /utf8: test utf8/);
|
|
387
|
+
assert.match(output[0], /utf8\.mjs: test utf8/);
|
|
372
388
|
|
|
373
389
|
const ieeeAutoState = createState();
|
|
374
|
-
|
|
375
|
-
|
|
390
|
+
await evaluateInput(":load Examples/ieee.k", ieeeAutoState);
|
|
391
|
+
output = await evaluateInput(":codec load ./codecs/ieee.mjs", ieeeAutoState);
|
|
392
|
+
assert.match(output[0], /^loaded codec ieee\.mjs/);
|
|
376
393
|
assert.ok(ieeeAutoState.typeAliases.ieee, "ieee type alias should be registered");
|
|
377
|
-
assert.ok(ieeeAutoState.typeAliases.float64, "float64 type alias should be registered");
|
|
378
|
-
assert.equal(ieeeAutoState.typeAliases.ieee, ieeeAutoState.typeAliases.float64);
|
|
379
394
|
|
|
380
395
|
const jsonInputState = createState();
|
|
381
396
|
await evaluateInput(":load core.k", jsonInputState);
|
|
382
|
-
await evaluateInput(":load ieee.k", jsonInputState);
|
|
383
|
-
await evaluateInput(":codec load json", jsonInputState);
|
|
384
|
-
output = await evaluateInput(
|
|
385
|
-
assert.match(output[0], /
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
await evaluateInput(":codec load
|
|
392
|
-
|
|
393
|
-
// 1.
|
|
394
|
-
output = await evaluateInput(
|
|
395
|
-
assert.match(output[0], /
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
output = await evaluateInput(":
|
|
401
|
-
assert.match(output[0], /
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
output
|
|
407
|
-
|
|
408
|
-
output = await evaluateInput(
|
|
409
|
-
assert.match(output[0], /
|
|
410
|
-
|
|
411
|
-
// 4.
|
|
412
|
-
output = await evaluateInput(":input ? {string a, float64 n} json", patternInputState);
|
|
413
|
-
await assert.rejects(
|
|
414
|
-
() => evaluateInput('{"wrong_key": 999}', patternInputState),
|
|
415
|
-
/Closed product pattern does not match value fields/
|
|
416
|
-
);
|
|
417
|
-
|
|
418
|
-
// 5. Bare :input defaults to :input (...) with auto-loaded json codec
|
|
397
|
+
await evaluateInput(":load Examples/ieee.k", jsonInputState);
|
|
398
|
+
await evaluateInput(":codec load ./codecs/json.mjs", jsonInputState);
|
|
399
|
+
output = await evaluateInput(':input json.mjs {"a":"Woj","n":123}', jsonInputState);
|
|
400
|
+
assert.match(output[0], /json\.mjs: \{"a":"Woj","n":123\}/);
|
|
401
|
+
|
|
402
|
+
// Verify standalone codec input, one-line and interactive modes, and multi-codec printing
|
|
403
|
+
const multiCodecState = createState();
|
|
404
|
+
await evaluateInput(":codec load ./codecs/json.mjs", multiCodecState);
|
|
405
|
+
await evaluateInput(":codec load ./codecs/utf8.mjs", multiCodecState);
|
|
406
|
+
await evaluateInput(":codec load ./codecs/int.mjs", multiCodecState);
|
|
407
|
+
|
|
408
|
+
// 1. One-line JSON input: :input json.mjs {"hello":"world"}
|
|
409
|
+
output = await evaluateInput(':input json.mjs {"hello":"world"}', multiCodecState);
|
|
410
|
+
assert.match(output[0], /json\.mjs: \{"hello":"world"\}/);
|
|
411
|
+
|
|
412
|
+
// 2. Interactive JSON input: :input json.mjs then {"anything": [1, 2, 3]}
|
|
413
|
+
output = await evaluateInput(":input json.mjs", multiCodecState);
|
|
414
|
+
assert.equal(promptForState(multiCodecState), "json.mjs> ");
|
|
415
|
+
output = await evaluateInput('{"anything": [1, 2, 3]}', multiCodecState);
|
|
416
|
+
assert.match(output[0], /json\.mjs: \{"anything":\[1,2,3\]\}/);
|
|
417
|
+
assert.equal(promptForState(multiCodecState), "> ");
|
|
418
|
+
|
|
419
|
+
// 3. Integer scalar and list input
|
|
420
|
+
output = await evaluateInput(":input int.mjs 42", multiCodecState);
|
|
421
|
+
assert.match(output[0], /int\.mjs: 42/);
|
|
422
|
+
|
|
423
|
+
output = await evaluateInput(":input int.mjs [0,1,2]", multiCodecState);
|
|
424
|
+
assert.match(output[0], /int\.mjs: \[0,1,2\]/);
|
|
425
|
+
|
|
426
|
+
// 4. Bare :input displays usage and loaded codecs
|
|
419
427
|
const bareInputState = createState();
|
|
420
|
-
assert.equal(promptForState(bareInputState), "> ");
|
|
421
428
|
output = await evaluateInput(":input", bareInputState);
|
|
422
|
-
assert.
|
|
423
|
-
assert.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
assert.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
await evaluateInput("$ bool = <{} true, {} false>;", dollarPatternState);
|
|
438
|
-
output = await evaluateInput(":input $ bool json", dollarPatternState);
|
|
439
|
-
assert.deepEqual(output, ["input $ bool using json: enter value text"]);
|
|
440
|
-
assert.equal(promptForState(dollarPatternState), "json> ");
|
|
441
|
-
output = await evaluateInput("true", dollarPatternState);
|
|
442
|
-
assert.match(output[0], /json: true/);
|
|
429
|
+
assert.match(output[0], /^Usage: :input <codec\.mjs> \[text\]/);
|
|
430
|
+
assert.match(output[1], /Loaded codecs:/);
|
|
431
|
+
|
|
432
|
+
// 5. Codec with leading dollar sign is accepted
|
|
433
|
+
output = await evaluateInput(":input $ int.mjs 42", multiCodecState);
|
|
434
|
+
assert.match(output[0], /int\.mjs: 42/);
|
|
435
|
+
output = await evaluateInput(":input $int.mjs 100", multiCodecState);
|
|
436
|
+
assert.match(output[0], /int\.mjs: 100/);
|
|
437
|
+
|
|
438
|
+
// 6. Unknown codec throws descriptive error
|
|
439
|
+
await assert.rejects(
|
|
440
|
+
() => evaluateInput(":input nonexistent 123", multiCodecState),
|
|
441
|
+
/Codec 'nonexistent' is not loaded/
|
|
442
|
+
);
|
|
443
443
|
|
|
444
444
|
const unitState = createState();
|
|
445
|
-
output = await evaluateInput(":codec load unit", unitState);
|
|
446
|
-
assert.match(output[0],
|
|
447
|
-
output = await evaluateInput(":input unit", unitState);
|
|
448
|
-
assert.
|
|
445
|
+
output = await evaluateInput(":codec load ./codecs/unit.mjs", unitState);
|
|
446
|
+
assert.match(output[0], /^loaded codec unit\.mjs/);
|
|
447
|
+
output = await evaluateInput(":input unit.mjs", unitState);
|
|
448
|
+
assert.equal(promptForState(unitState), "unit.mjs> ");
|
|
449
449
|
output = await evaluateInput("{}", unitState);
|
|
450
|
-
assert.match(output[0], /unit: \{\}/);
|
|
450
|
+
assert.match(output[0], /unit\.mjs: \{\}/);
|
|
451
451
|
|
|
452
452
|
const polyAutoState = createState();
|
|
453
453
|
output = await evaluateInput(":load Examples/poly.k", polyAutoState);
|
|
@@ -455,21 +455,75 @@ assert.equal(output[0], "loaded Examples/poly.k");
|
|
|
455
455
|
assert.ok(polyAutoState.relAliases.reverse, "poly relations should be loaded");
|
|
456
456
|
assert.ok(polyAutoState.typeAliases.int, "arithmetics int should be auto-loaded");
|
|
457
457
|
|
|
458
|
-
// Verify timing reporting is always on and
|
|
458
|
+
// Verify timing reporting is always on and reflects active engine
|
|
459
459
|
const timingState = createState();
|
|
460
460
|
assert.equal(timingState.showTiming, true);
|
|
461
|
+
assert.equal(timingState.engine, "wasm");
|
|
461
462
|
|
|
462
463
|
output = await evaluateInput("()", timingState);
|
|
463
464
|
assert.equal(output[0], "{} ?{}");
|
|
464
|
-
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
465
|
-
|
|
466
|
-
output = await evaluateInput(":time ()", timingState);
|
|
467
|
-
assert.equal(output[0], "{} ?{}");
|
|
468
|
-
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
465
|
+
assert.match(output[1], /\/\* comp: .*, exec: .* \(wasm\) \*\//);
|
|
469
466
|
assert.ok(timingState.lastTiming);
|
|
467
|
+
assert.equal(timingState.lastTiming.engine, "wasm");
|
|
470
468
|
assert.ok(typeof timingState.lastTiming.compileMs === "number");
|
|
471
469
|
assert.ok(typeof timingState.lastTiming.executeMs === "number");
|
|
472
470
|
|
|
471
|
+
// Verify :engine command and switching between wasm and js engines
|
|
472
|
+
output = await evaluateInput(":engine", timingState);
|
|
473
|
+
assert.equal(output[0], "Current engine: wasm");
|
|
474
|
+
|
|
475
|
+
output = await evaluateInput(":engine js", timingState);
|
|
476
|
+
assert.equal(output[0], "engine set to js");
|
|
477
|
+
assert.equal(timingState.engine, "js");
|
|
478
|
+
|
|
479
|
+
output = await evaluateInput("()", timingState);
|
|
480
|
+
assert.equal(output[0], "{} ?{}");
|
|
481
|
+
assert.match(output[1], /\/\* comp: .*, exec: .* \(js\) \*\//);
|
|
482
|
+
assert.equal(timingState.lastTiming.engine, "js");
|
|
483
|
+
|
|
484
|
+
// Verify shortcuts :wasm and :js
|
|
485
|
+
output = await evaluateInput(":wasm", timingState);
|
|
486
|
+
assert.equal(output[0], "engine set to wasm");
|
|
487
|
+
assert.equal(timingState.engine, "wasm");
|
|
488
|
+
|
|
489
|
+
output = await evaluateInput(":js", timingState);
|
|
490
|
+
assert.equal(output[0], "engine set to js");
|
|
491
|
+
assert.equal(timingState.engine, "js");
|
|
492
|
+
|
|
493
|
+
// Verify :reset preserves the active engine
|
|
494
|
+
output = await evaluateInput(":reset", timingState);
|
|
495
|
+
assert.equal(output[0], "reset");
|
|
496
|
+
assert.equal(timingState.engine, "js");
|
|
497
|
+
|
|
498
|
+
// Verify invalid engine error
|
|
499
|
+
await assert.rejects(
|
|
500
|
+
() => evaluateInput(":engine invalid", timingState),
|
|
501
|
+
/Invalid engine 'invalid'\. Valid engines: wasm, js/
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
// Verify autocompletion for :engine
|
|
505
|
+
completions = completeInput(":engine ", timingState)[0];
|
|
506
|
+
assert(completions.includes(":engine wasm"));
|
|
507
|
+
assert(completions.includes(":engine js"));
|
|
508
|
+
|
|
509
|
+
// Verify evaluating expressions in JS engine
|
|
510
|
+
const jsState = createState({ engine: "js" });
|
|
511
|
+
assert.equal(jsState.engine, "js");
|
|
512
|
+
|
|
513
|
+
output = await evaluateInput("$ nat = < {} zero, nat succ >;\nsucc = | succ;\n{} | zero succ succ", jsState);
|
|
514
|
+
assert.match(output[0], /^\{\}\|zero\|succ\|succ/);
|
|
515
|
+
assert.match(output[1], /\(js\)/);
|
|
516
|
+
|
|
517
|
+
output = await evaluateInput("$ bool = < {} true, {} false >;\nnot = $bool </true | false, {} | true >;\n{} | true not", jsState);
|
|
518
|
+
assert.match(output[0], /^\{\}\|false/);
|
|
519
|
+
assert.match(output[1], /\(js\)/);
|
|
520
|
+
|
|
521
|
+
output = await evaluateInput(":load Examples/arithmetics.k", jsState);
|
|
522
|
+
assert.equal(output[0], "loaded Examples/arithmetics.k");
|
|
523
|
+
output = await evaluateInput("{ 10 int x, 5 int y } plus", jsState);
|
|
524
|
+
assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
|
|
525
|
+
assert.match(output[1], /\(js\)/);
|
|
526
|
+
|
|
473
527
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
474
528
|
fs.rmSync(symlinkPath, { force: true });
|
|
475
529
|
console.log("OK");
|
|
@@ -25,3 +25,20 @@ assert.equal(valueToK(foreignTwo), "{}|_|0|1|+");
|
|
|
25
25
|
|
|
26
26
|
const decoded = decodeWire(encodeToWire(foreignTwo, INT_PATTERN)).value;
|
|
27
27
|
assert.equal(printInt(decoded), "2");
|
|
28
|
+
|
|
29
|
+
const { INT_LIST_PATTERN, parse: parseIntCodec } = await import("../codecs/int.mjs");
|
|
30
|
+
const { valueWithEnvelopeToK } = await import("../codecs/runtime/show-value.mjs");
|
|
31
|
+
|
|
32
|
+
const parsedList = parseIntCodec("[0,1,2]");
|
|
33
|
+
const listWire = encodeToWire(parsedList, INT_LIST_PATTERN);
|
|
34
|
+
const decodedList = decodeWire(listWire).value;
|
|
35
|
+
assert.equal(
|
|
36
|
+
valueWithEnvelopeToK(decodedList),
|
|
37
|
+
"{{}|_|0|+ car, {{}|_|1|+ car, {{}|_|0|1|+ car, {}|nil cdr}|cons cdr}|cons cdr}|cons ?<{<<X1 0, X1 1, {}=X2 _>=X1 +, X1 -> car, X0 cdr} cons, X2 nil>=X0"
|
|
38
|
+
);
|
|
39
|
+
assert.equal(printInt(decodedList), "[0,1,2]");
|
|
40
|
+
|
|
41
|
+
const emptyList = parseIntCodec("[]");
|
|
42
|
+
const emptyWire = encodeToWire(emptyList, INT_LIST_PATTERN);
|
|
43
|
+
const decodedEmpty = decodeWire(emptyWire).value;
|
|
44
|
+
assert.equal(printInt(decodedEmpty), "[]");
|
package/tests/test-tree-view.mjs
CHANGED
|
@@ -172,6 +172,7 @@ assert.strictEqual(patTree.open, true, "Root pattern node must be open");
|
|
|
172
172
|
assert(patTree.textContent.includes("recursive"), "Recursive cycle must be detected and marked 'recursive'");
|
|
173
173
|
|
|
174
174
|
// 5. Test createOutputEntryElement
|
|
175
|
+
variantVal.pattern = recursivePattern;
|
|
175
176
|
const entryEl = createOutputEntryElement({
|
|
176
177
|
value: variantVal,
|
|
177
178
|
rawText: "{}|_|1|0|1|+ ?<<X0 0, X0 1, {} _>=X0 +, X0 ->\nint: 11",
|
|
@@ -180,6 +181,8 @@ const entryEl = createOutputEntryElement({
|
|
|
180
181
|
|
|
181
182
|
assert(entryEl.querySelector(".k-tree-view"), "Tree view container must exist");
|
|
182
183
|
assert(entryEl.querySelector(".k-raw-view"), "Raw view container must exist");
|
|
184
|
+
assert.strictEqual(entryEl.querySelector(".k-tree-value-section")?.open, false, "Value section must be collapsed by default");
|
|
185
|
+
assert.strictEqual(entryEl.querySelector(".k-tree-pat-section")?.open, false, "Pattern section must be collapsed by default");
|
|
183
186
|
assert(entryEl.textContent.includes("int: 11"), "Codec output must be visible in container");
|
|
184
187
|
|
|
185
188
|
console.log("OK - All tree view unit tests passed.");
|