@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 +23 -36
- package/TODO.md +112 -0
- package/backends/arm64/README.md +0 -1
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -8
- package/backends/llvm/README.md +13 -11
- package/backends/llvm/bin/k-llvm-build.mjs +40 -38
- package/backends/llvm/bin/k-llvm-compile.mjs +42 -32
- package/backends/llvm/src/cli.mjs +153 -0
- package/backends/llvm/src/executable.mjs +1 -1
- package/backends/llvm/src/kvm2llvm.mjs +11 -1
- package/backends/llvm/tests/integration.sh +18 -22
- package/backends/wasm/bin/k-wasm-compile.mjs +19 -4
- package/browser/repl-browser.mjs +146 -94
- package/browser/vfs-data.mjs +2 -0
- package/package.json +1 -1
- package/repl-codecs.mjs +26 -5
- package/repl.mjs +214 -148
- package/scripts/build-repl-html.mjs +213 -72
- package/tests/test-repl-html.mjs +84 -6
- package/tests/test-repl.mjs +90 -40
package/tests/test-repl.mjs
CHANGED
|
@@ -16,18 +16,21 @@ import {
|
|
|
16
16
|
isMainEntrypoint,
|
|
17
17
|
lineHasExplicitContinuation,
|
|
18
18
|
lineTerminatesSnippet,
|
|
19
|
-
promptForState
|
|
19
|
+
promptForState,
|
|
20
|
+
registerCodec
|
|
20
21
|
} from "../repl.mjs";
|
|
21
22
|
import { decodeObject, objectToFunction } from "../object.mjs";
|
|
22
23
|
import { Value } from "../Value.mjs";
|
|
23
24
|
|
|
24
25
|
const state = createState();
|
|
25
26
|
|
|
26
|
-
let output = await evaluateInput("
|
|
27
|
-
|
|
27
|
+
let output = await evaluateInput("$ nat = < {} zero, nat succ >;", state);
|
|
28
|
+
output = await evaluateInput(":type nat", state);
|
|
29
|
+
assert.match(output[0], /^\$ nat = /);
|
|
28
30
|
|
|
29
|
-
output = await evaluateInput("
|
|
30
|
-
|
|
31
|
+
output = await evaluateInput("succ = | succ;", state);
|
|
32
|
+
output = await evaluateInput(":d succ", state);
|
|
33
|
+
assert.match(output[0], /^succ = /);
|
|
31
34
|
|
|
32
35
|
output = await evaluateInput(":codes", state);
|
|
33
36
|
assert.match(output[0], /^nat = @/m);
|
|
@@ -82,8 +85,8 @@ assert.equal(analyzeRawSnippet("a =").kind, "incomplete");
|
|
|
82
85
|
assert.equal(analyzeRawSnippet("{} | succ").kind, "withMain");
|
|
83
86
|
|
|
84
87
|
const offsetState = createState();
|
|
85
|
-
await evaluateInput("
|
|
86
|
-
await evaluateInput("
|
|
88
|
+
await evaluateInput("$ ab = < {} 1, {} 2 >;", offsetState);
|
|
89
|
+
await evaluateInput("$ bool = < {} true, {} false >;", offsetState);
|
|
87
90
|
await assert.rejects(
|
|
88
91
|
() => evaluateInput("$ab", offsetState),
|
|
89
92
|
/Type Error in 'filter' \(lines 1:1\.\.\.1:4\)/
|
|
@@ -100,10 +103,10 @@ output = await evaluateInput(`:klib ${libPath}`, state);
|
|
|
100
103
|
assert.equal(output[0], `saved ${libPath}`);
|
|
101
104
|
|
|
102
105
|
const recursiveState = createState();
|
|
103
|
-
await evaluateInput("
|
|
104
|
-
await evaluateInput("
|
|
106
|
+
await evaluateInput("$ nat = < {} _, nat 0, nat 1 >;", recursiveState);
|
|
107
|
+
await evaluateInput("0? = $ nat < / _ {} | 0, / 0 0? >;", recursiveState);
|
|
105
108
|
const staleZeroHash = recursiveState.relAliases["0?"];
|
|
106
|
-
await evaluateInput("
|
|
109
|
+
await evaluateInput("0? = $ nat < / _ {} | _, / 0 0? >;", recursiveState);
|
|
107
110
|
const zeroHash = recursiveState.relAliases["0?"];
|
|
108
111
|
assert.notEqual(zeroHash, staleZeroHash);
|
|
109
112
|
await evaluateInput("{}|_|0|0 $nat", recursiveState);
|
|
@@ -141,8 +144,7 @@ output = await evaluateInput(":C nat", loadedSource);
|
|
|
141
144
|
assert.match(output[0], /^\$ nat = < nat succ, @[^ ]+ zero >; -- @/);
|
|
142
145
|
|
|
143
146
|
const codecState = createState();
|
|
144
|
-
output = await evaluateInput("
|
|
145
|
-
assert.match(output[0], /^\$ bool = @/);
|
|
147
|
+
output = await evaluateInput("$ bool = < {} true, {} false >;", codecState);
|
|
146
148
|
const boolHash = codecState.typeAliases.bool;
|
|
147
149
|
const valueModuleUrl = pathToFileURL(path.resolve("Value.mjs")).href;
|
|
148
150
|
fs.writeFileSync(codecPath, `
|
|
@@ -196,8 +198,7 @@ output = await evaluateInput("hello", utf8State);
|
|
|
196
198
|
assert.match(output[0], /utf8: hello/);
|
|
197
199
|
|
|
198
200
|
const jsonState = createState();
|
|
199
|
-
output = await evaluateInput("
|
|
200
|
-
assert.match(output[0], /^\$ bool = @/);
|
|
201
|
+
output = await evaluateInput("$ bool = < {} true, {} false >;", jsonState);
|
|
201
202
|
output = await evaluateInput(":codec load ./codecs/json.mjs", jsonState);
|
|
202
203
|
assert.match(output[0], /^loaded codec json for all types/);
|
|
203
204
|
output = await evaluateInput(":codec list", jsonState);
|
|
@@ -256,7 +257,7 @@ assert.equal(output[0], "(none)");
|
|
|
256
257
|
|
|
257
258
|
const shorthand = createState();
|
|
258
259
|
output = await evaluateInput("$ nat = <{} zero, nat succ>\n; succ = |succ\n;", shorthand);
|
|
259
|
-
assert.
|
|
260
|
+
assert.match(output[0], /\/\* comp: .* \*\//);
|
|
260
261
|
output = await evaluateInput(":codes", shorthand);
|
|
261
262
|
assert.match(output[0], /^nat = @/m);
|
|
262
263
|
output = await evaluateInput(":rels", shorthand);
|
|
@@ -266,7 +267,7 @@ assert.match(output[0], /^\{\}\|false \?</);
|
|
|
266
267
|
assert.match(output[0], / false/);
|
|
267
268
|
assert.match(output[0], / true/);
|
|
268
269
|
output = await evaluateInput("$ maybe = <{} none, {} some>;", shorthand);
|
|
269
|
-
assert.
|
|
270
|
+
assert.match(output[0], /\/\* comp: .* \*\//);
|
|
270
271
|
output = await evaluateInput(":codes", shorthand);
|
|
271
272
|
assert.match(output[0], /^maybe = @/m);
|
|
272
273
|
output = await evaluateInput("{} | true not", shorthand);
|
|
@@ -279,8 +280,8 @@ await assert.rejects(
|
|
|
279
280
|
|
|
280
281
|
// Verify WebAssembly in-process backend supports TCO / stack safety on deep recursion
|
|
281
282
|
const tcoState = createState();
|
|
282
|
-
await evaluateInput("
|
|
283
|
-
await evaluateInput("
|
|
283
|
+
await evaluateInput("$ nat = < {} zero, nat succ >;", tcoState);
|
|
284
|
+
await evaluateInput("countdown = $ nat < / zero {} | zero, / succ countdown >;", tcoState);
|
|
284
285
|
|
|
285
286
|
let deepNat = Value.variant("zero", Value.product({}, [["closed-product", []]]), [["open-union", [["zero", 1]]], ["closed-product", []]]);
|
|
286
287
|
for (let i = 0; i < 5000; i++) {
|
|
@@ -301,7 +302,7 @@ assert.match(output[0], /^\{\}\|_\|0\|1\|0\|1/);
|
|
|
301
302
|
output = await evaluateInput("{10 int x, 5 int y} plus", arithState);
|
|
302
303
|
assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
|
|
303
304
|
|
|
304
|
-
// Verify built-in codecs, :codecs,
|
|
305
|
+
// Verify built-in codecs, :codecs, and :codec unload
|
|
305
306
|
const codecTestState = createState();
|
|
306
307
|
output = await evaluateInput(":codecs", codecTestState);
|
|
307
308
|
assert.equal(output[0], "(none)");
|
|
@@ -324,12 +325,14 @@ assert.equal(output[0], "unloaded codec int");
|
|
|
324
325
|
output = await evaluateInput(":codecs", codecTestState);
|
|
325
326
|
assert.equal(output[0], "(none)");
|
|
326
327
|
|
|
327
|
-
output = await evaluateInput("
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
codecTestState
|
|
331
|
-
|
|
332
|
-
|
|
328
|
+
output = await evaluateInput("$ bool = < {} true, {} false >;", codecTestState);
|
|
329
|
+
registerCodec(codecTestState, {
|
|
330
|
+
name: "yn",
|
|
331
|
+
codes: [codecTestState.typeAliases.bool],
|
|
332
|
+
universal: false,
|
|
333
|
+
parse: (text) => Value.variant(text.trim() === "yes" ? "true" : "false", Value.product({})),
|
|
334
|
+
print: (v) => v.tag === "true" ? "YES" : "NO"
|
|
335
|
+
}, "<test>");
|
|
333
336
|
|
|
334
337
|
output = await evaluateInput(":input bool yn", codecTestState);
|
|
335
338
|
output = await evaluateInput("yes", codecTestState);
|
|
@@ -383,6 +386,61 @@ assert.match(output[0], /enter value text/);
|
|
|
383
386
|
output = await evaluateInput('{"a":"Woj","n":123}', jsonInputState);
|
|
384
387
|
assert.match(output[0], /json: \{"a":"Woj","n":123\}/);
|
|
385
388
|
|
|
389
|
+
// Verify pattern expressions and standalone codec input
|
|
390
|
+
const patternInputState = createState();
|
|
391
|
+
await evaluateInput(":codec load json", patternInputState);
|
|
392
|
+
|
|
393
|
+
// 1. Explicit pattern expression: ? {string a, float64 n} json
|
|
394
|
+
output = await evaluateInput(":input ? {string a, float64 n} json", patternInputState);
|
|
395
|
+
assert.match(output[0], /input \? \{string a, float64 n\} using json: enter value text/);
|
|
396
|
+
output = await evaluateInput('{"a":"Woj","n":123}', patternInputState);
|
|
397
|
+
assert.match(output[0], /json: \{"a":"Woj","n":123\}/);
|
|
398
|
+
|
|
399
|
+
// 2. Wildcard pattern expression: ? (...) json
|
|
400
|
+
output = await evaluateInput(":input ? (...) json", patternInputState);
|
|
401
|
+
assert.match(output[0], /enter value text/);
|
|
402
|
+
output = await evaluateInput('{"anything": [1, 2, 3]}', patternInputState);
|
|
403
|
+
assert.match(output[0], /json: \{"anything":\[1,2,3\]\}/);
|
|
404
|
+
|
|
405
|
+
// 3. Standalone codec input: :input json (no predefined type or pattern required)
|
|
406
|
+
output = await evaluateInput(":input json", patternInputState);
|
|
407
|
+
assert.match(output[0], /input json: enter value text/);
|
|
408
|
+
output = await evaluateInput('{"hello":"world"}', patternInputState);
|
|
409
|
+
assert.match(output[0], /json: \{"hello":"world"\}/);
|
|
410
|
+
|
|
411
|
+
// 4. Pattern mismatch detection
|
|
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
|
|
419
|
+
const bareInputState = createState();
|
|
420
|
+
assert.equal(promptForState(bareInputState), "> ");
|
|
421
|
+
output = await evaluateInput(":input", bareInputState);
|
|
422
|
+
assert.deepEqual(output, ["input (...): enter value text"]);
|
|
423
|
+
assert.equal(promptForState(bareInputState), "json> ");
|
|
424
|
+
output = await evaluateInput('{"test": 123}', bareInputState);
|
|
425
|
+
assert.match(output[0], /json: \{"test":123\}/);
|
|
426
|
+
assert.equal(promptForState(bareInputState), "> ");
|
|
427
|
+
|
|
428
|
+
// 6. Pattern as type prefixed by dollar
|
|
429
|
+
const dollarPatternState = createState();
|
|
430
|
+
await evaluateInput(":load arithmetics.k", dollarPatternState);
|
|
431
|
+
output = await evaluateInput(":input $ int", dollarPatternState);
|
|
432
|
+
assert.deepEqual(output, ["input $ int: enter value text"]);
|
|
433
|
+
assert.equal(promptForState(dollarPatternState), "int> ");
|
|
434
|
+
output = await evaluateInput("42", dollarPatternState);
|
|
435
|
+
assert.match(output[0], /int: 42/);
|
|
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/);
|
|
443
|
+
|
|
386
444
|
const unitState = createState();
|
|
387
445
|
output = await evaluateInput(":codec load unit", unitState);
|
|
388
446
|
assert.match(output[0], /loaded codec unit/);
|
|
@@ -397,28 +455,20 @@ assert.equal(output[0], "loaded Examples/poly.k");
|
|
|
397
455
|
assert.ok(polyAutoState.relAliases.reverse, "poly relations should be loaded");
|
|
398
456
|
assert.ok(polyAutoState.typeAliases.int, "arithmetics int should be auto-loaded");
|
|
399
457
|
|
|
400
|
-
// Verify
|
|
458
|
+
// Verify timing reporting is always on and :time command
|
|
401
459
|
const timingState = createState();
|
|
402
|
-
output = await evaluateInput(":time ()", timingState);
|
|
403
|
-
assert.equal(output[0], "{} ?{}");
|
|
404
|
-
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
405
|
-
assert.ok(timingState.lastTiming);
|
|
406
|
-
assert.ok(typeof timingState.lastTiming.compileMs === "number");
|
|
407
|
-
assert.ok(typeof timingState.lastTiming.executeMs === "number");
|
|
408
|
-
|
|
409
|
-
output = await evaluateInput(":timing on", timingState);
|
|
410
|
-
assert.equal(output[0], "timing reporting enabled");
|
|
411
460
|
assert.equal(timingState.showTiming, true);
|
|
461
|
+
|
|
412
462
|
output = await evaluateInput("()", timingState);
|
|
413
463
|
assert.equal(output[0], "{} ?{}");
|
|
414
464
|
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
415
465
|
|
|
416
|
-
output = await evaluateInput(":
|
|
417
|
-
assert.equal(output[0], "timing reporting disabled");
|
|
418
|
-
assert.equal(timingState.showTiming, false);
|
|
419
|
-
output = await evaluateInput("()", timingState);
|
|
420
|
-
assert.equal(output.length, 1);
|
|
466
|
+
output = await evaluateInput(":time ()", timingState);
|
|
421
467
|
assert.equal(output[0], "{} ?{}");
|
|
468
|
+
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
469
|
+
assert.ok(timingState.lastTiming);
|
|
470
|
+
assert.ok(typeof timingState.lastTiming.compileMs === "number");
|
|
471
|
+
assert.ok(typeof timingState.lastTiming.executeMs === "number");
|
|
422
472
|
|
|
423
473
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
424
474
|
fs.rmSync(symlinkPath, { force: true });
|