@fraczak/k 6.7.0 → 6.9.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/CHANGELOG.md +10 -0
- package/DOCS/CODECS.md +80 -181
- package/DOCS/CODEC_ARCHITECTURE.md +194 -0
- package/DOCS/REPL.md +49 -60
- package/Examples/arithmetics.k +1 -0
- package/Examples/poly.k +46 -0
- package/TODO.md +112 -0
- package/browser/repl-browser.mjs +112 -733
- package/browser/shims/process.mjs +7 -0
- package/browser/tree-view.mjs +5 -2
- package/browser/vfs-data.mjs +14 -12
- 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 +78 -184
- package/repl.mjs +168 -226
- package/scripts/build-repl-html.mjs +273 -424
- 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 +190 -53
- package/tests/test-repl.mjs +150 -114
- 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
|
@@ -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, `
|
|
@@ -158,6 +160,9 @@ export function parse(text) {
|
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
export function print(value) {
|
|
163
|
+
if (!value || (value.tag !== "true" && value.tag !== "false")) {
|
|
164
|
+
throw new Error("expected true or false");
|
|
165
|
+
}
|
|
161
166
|
return value.tag;
|
|
162
167
|
}
|
|
163
168
|
`);
|
|
@@ -170,59 +175,52 @@ assert(completions.includes(":codec list"));
|
|
|
170
175
|
completions = completeInput(`:codec load ${tmpDir}/bool`, codecState)[0];
|
|
171
176
|
assert(completions.includes(`:codec load ${codecPath}`));
|
|
172
177
|
output = await evaluateInput(`:codec load ${codecPath}`, codecState);
|
|
173
|
-
assert.equal(output[0], `loaded codec
|
|
178
|
+
assert.equal(output[0], `loaded codec bool-codec.mjs`);
|
|
174
179
|
output = await evaluateInput(":codec list", codecState);
|
|
175
|
-
assert.match(output[0],
|
|
176
|
-
completions = completeInput(":input
|
|
177
|
-
assert(completions.includes(":input
|
|
178
|
-
completions = completeInput(":input
|
|
179
|
-
assert(completions.includes(":input bool
|
|
180
|
-
|
|
181
|
-
assert.
|
|
180
|
+
assert.match(output[0], /^bool-codec\.mjs/);
|
|
181
|
+
completions = completeInput(":input", codecState)[0];
|
|
182
|
+
assert(completions.includes(":input"));
|
|
183
|
+
completions = completeInput(":input ", codecState)[0];
|
|
184
|
+
assert(completions.includes(":input bool-codec.mjs"));
|
|
185
|
+
completions = completeInput(":input b", codecState)[0];
|
|
186
|
+
assert(completions.includes(":input bool-codec.mjs"));
|
|
187
|
+
output = await evaluateInput(":input bool-codec.mjs", codecState);
|
|
188
|
+
assert.equal(promptForState(codecState), "bool-codec.mjs> ");
|
|
182
189
|
output = await evaluateInput("true", codecState);
|
|
183
190
|
assert.match(output[0], /\{\}\|true \?</);
|
|
184
|
-
assert.match(output[0], /
|
|
191
|
+
assert.match(output[0], /bool-codec\.mjs: true/);
|
|
185
192
|
output = await evaluateInput("{} | false", codecState);
|
|
186
|
-
assert.
|
|
193
|
+
assert.match(output[0], /bool-codec\.mjs: false/);
|
|
194
|
+
output = await evaluateInput("{}", codecState);
|
|
195
|
+
assert.doesNotMatch(output[0], /bool-codec\.mjs:/);
|
|
187
196
|
|
|
188
197
|
const utf8State = createState();
|
|
189
198
|
output = await evaluateInput(":load core.k", utf8State);
|
|
190
199
|
assert.equal(output[0], "loaded core.k");
|
|
191
200
|
output = await evaluateInput(":codec load ./codecs/utf8.mjs", utf8State);
|
|
192
|
-
assert.equal(output[0], `loaded codec utf8
|
|
193
|
-
output = await evaluateInput(":input
|
|
194
|
-
assert.
|
|
195
|
-
output = await evaluateInput("hello", utf8State);
|
|
196
|
-
assert.match(output[0], /utf8: hello/);
|
|
201
|
+
assert.equal(output[0], `loaded codec utf8.mjs`);
|
|
202
|
+
output = await evaluateInput(":input utf8.mjs hello", utf8State);
|
|
203
|
+
assert.match(output[0], /utf8\.mjs: hello/);
|
|
197
204
|
|
|
198
205
|
const jsonState = createState();
|
|
199
|
-
output = await evaluateInput(":type bool = <{} true, {} false>", jsonState);
|
|
200
|
-
assert.match(output[0], /^\$ bool = @/);
|
|
201
206
|
output = await evaluateInput(":codec load ./codecs/json.mjs", jsonState);
|
|
202
|
-
assert.match(output[0], /^loaded codec json
|
|
207
|
+
assert.match(output[0], /^loaded codec json\.mjs/);
|
|
203
208
|
output = await evaluateInput(":codec list", jsonState);
|
|
204
|
-
assert.match(output[0], /^json
|
|
205
|
-
completions = completeInput(":input
|
|
206
|
-
assert(completions.includes(":input
|
|
207
|
-
output = await evaluateInput(":input
|
|
208
|
-
assert.equal(
|
|
209
|
-
assert.equal(promptForState(jsonState), "json> ");
|
|
209
|
+
assert.match(output[0], /^json\.mjs/);
|
|
210
|
+
completions = completeInput(":input j", jsonState)[0];
|
|
211
|
+
assert(completions.includes(":input json.mjs"));
|
|
212
|
+
output = await evaluateInput(":input json.mjs", jsonState);
|
|
213
|
+
assert.equal(promptForState(jsonState), "json.mjs> ");
|
|
210
214
|
output = await evaluateInput("true", jsonState);
|
|
211
215
|
assert.match(output[0], /\{\}\|true \?</);
|
|
212
|
-
assert.match(output[0], /json: true/);
|
|
216
|
+
assert.match(output[0], /json\.mjs: true/);
|
|
213
217
|
assert.equal(promptForState(jsonState), "> ");
|
|
214
|
-
output = await evaluateInput(":input
|
|
215
|
-
assert.equal(output[0], `input ${jsonState.typeAliases.bool}: enter value text`);
|
|
216
|
-
assert.equal(promptForState(jsonState), "json> ");
|
|
217
|
-
output = await evaluateInput("false", jsonState);
|
|
218
|
+
output = await evaluateInput(":input json.mjs false", jsonState);
|
|
218
219
|
assert.match(output[0], /\{\}\|false \?</);
|
|
219
|
-
assert.match(output[0], /json: false/);
|
|
220
|
+
assert.match(output[0], /json\.mjs: false/);
|
|
220
221
|
assert.equal(promptForState(jsonState), "> ");
|
|
221
|
-
output = await evaluateInput(
|
|
222
|
-
assert.match(output[0],
|
|
223
|
-
assert.equal(promptForState(jsonState), "json> ");
|
|
224
|
-
output = await evaluateInput('{"left":true,"right":false}', jsonState);
|
|
225
|
-
assert.match(output[0], /json: \{"left":true,"right":false\}/);
|
|
222
|
+
output = await evaluateInput(':input json.mjs {"left":true,"right":false}', jsonState);
|
|
223
|
+
assert.match(output[0], /json\.mjs: \{"left":true,"right":false\}/);
|
|
226
224
|
assert.equal(promptForState(jsonState), "> ");
|
|
227
225
|
|
|
228
226
|
const replPath = fileURLToPath(new URL("../repl.mjs", import.meta.url));
|
|
@@ -256,7 +254,7 @@ assert.equal(output[0], "(none)");
|
|
|
256
254
|
|
|
257
255
|
const shorthand = createState();
|
|
258
256
|
output = await evaluateInput("$ nat = <{} zero, nat succ>\n; succ = |succ\n;", shorthand);
|
|
259
|
-
assert.
|
|
257
|
+
assert.match(output[0], /\/\* comp: .* \*\//);
|
|
260
258
|
output = await evaluateInput(":codes", shorthand);
|
|
261
259
|
assert.match(output[0], /^nat = @/m);
|
|
262
260
|
output = await evaluateInput(":rels", shorthand);
|
|
@@ -266,7 +264,7 @@ assert.match(output[0], /^\{\}\|false \?</);
|
|
|
266
264
|
assert.match(output[0], / false/);
|
|
267
265
|
assert.match(output[0], / true/);
|
|
268
266
|
output = await evaluateInput("$ maybe = <{} none, {} some>;", shorthand);
|
|
269
|
-
assert.
|
|
267
|
+
assert.match(output[0], /\/\* comp: .* \*\//);
|
|
270
268
|
output = await evaluateInput(":codes", shorthand);
|
|
271
269
|
assert.match(output[0], /^maybe = @/m);
|
|
272
270
|
output = await evaluateInput("{} | true not", shorthand);
|
|
@@ -279,8 +277,8 @@ await assert.rejects(
|
|
|
279
277
|
|
|
280
278
|
// Verify WebAssembly in-process backend supports TCO / stack safety on deep recursion
|
|
281
279
|
const tcoState = createState();
|
|
282
|
-
await evaluateInput("
|
|
283
|
-
await evaluateInput("
|
|
280
|
+
await evaluateInput("$ nat = < {} zero, nat succ >;", tcoState);
|
|
281
|
+
await evaluateInput("countdown = $ nat < / zero {} | zero, / succ countdown >;", tcoState);
|
|
284
282
|
|
|
285
283
|
let deepNat = Value.variant("zero", Value.product({}, [["closed-product", []]]), [["open-union", [["zero", 1]]], ["closed-product", []]]);
|
|
286
284
|
for (let i = 0; i < 5000; i++) {
|
|
@@ -301,39 +299,45 @@ assert.match(output[0], /^\{\}\|_\|0\|1\|0\|1/);
|
|
|
301
299
|
output = await evaluateInput("{10 int x, 5 int y} plus", arithState);
|
|
302
300
|
assert.match(output[0], /^\{\}\|_\|1\|1\|1\|1\|\+/);
|
|
303
301
|
|
|
304
|
-
// Verify
|
|
302
|
+
// Verify codecs loading, listing, and unloading
|
|
305
303
|
const codecTestState = createState();
|
|
306
304
|
output = await evaluateInput(":codecs", codecTestState);
|
|
307
305
|
assert.equal(output[0], "(none)");
|
|
308
306
|
|
|
309
|
-
output = await evaluateInput(":codec load int", codecTestState);
|
|
310
|
-
assert.match(output[0], /^loaded codec int
|
|
307
|
+
output = await evaluateInput(":codec load ./codecs/int.mjs", codecTestState);
|
|
308
|
+
assert.match(output[0], /^loaded codec int\.mjs/);
|
|
311
309
|
|
|
312
310
|
output = await evaluateInput(":codecs", codecTestState);
|
|
313
|
-
assert.match(output[0], /^int
|
|
311
|
+
assert.match(output[0], /^int\.mjs/);
|
|
314
312
|
|
|
315
313
|
completions = completeInput(":codec un", codecTestState)[0];
|
|
316
314
|
assert(completions.includes(":codec unload"));
|
|
317
315
|
completions = completeInput(":codec unload ", codecTestState)[0];
|
|
318
|
-
assert(completions.includes(":codec unload int"));
|
|
319
|
-
completions = completeInput(":codec load i", codecTestState)[0];
|
|
320
|
-
assert(completions.includes(":codec load int"));
|
|
316
|
+
assert(completions.includes(":codec unload int.mjs"));
|
|
317
|
+
completions = completeInput(":codec load ./codecs/i", codecTestState)[0];
|
|
318
|
+
assert(completions.includes(":codec load ./codecs/int.mjs"));
|
|
321
319
|
|
|
322
|
-
output = await evaluateInput(":codec unload int", codecTestState);
|
|
323
|
-
assert.equal(output[0], "unloaded codec int");
|
|
320
|
+
output = await evaluateInput(":codec unload int.mjs", codecTestState);
|
|
321
|
+
assert.equal(output[0], "unloaded codec int.mjs");
|
|
324
322
|
output = await evaluateInput(":codecs", codecTestState);
|
|
325
323
|
assert.equal(output[0], "(none)");
|
|
326
324
|
|
|
327
|
-
output = await evaluateInput("
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
325
|
+
output = await evaluateInput("$ bool = < {} true, {} false >;", codecTestState);
|
|
326
|
+
registerCodec(codecTestState, {
|
|
327
|
+
name: "yn.mjs",
|
|
328
|
+
parse: (text) => Value.variant(text.trim() === "yes" ? "true" : "false", Value.product({})),
|
|
329
|
+
print: (v) => {
|
|
330
|
+
if (v.tag !== "true" && v.tag !== "false") throw new Error("not a bool");
|
|
331
|
+
return v.tag === "true" ? "YES" : "NO";
|
|
332
|
+
}
|
|
333
|
+
}, "yn.mjs");
|
|
334
|
+
|
|
335
|
+
output = await evaluateInput(":input yn.mjs", codecTestState);
|
|
336
|
+
assert.equal(promptForState(codecTestState), "yn.mjs> ");
|
|
335
337
|
output = await evaluateInput("yes", codecTestState);
|
|
336
|
-
assert.match(output[0], /yn: YES/);
|
|
338
|
+
assert.match(output[0], /yn\.mjs: YES/);
|
|
339
|
+
output = await evaluateInput(":input yn.mjs no", codecTestState);
|
|
340
|
+
assert.match(output[0], /yn\.mjs: NO/);
|
|
337
341
|
|
|
338
342
|
// Projection on product with variant field (avoids filter variable collisions)
|
|
339
343
|
const projState = createState();
|
|
@@ -348,48 +352,88 @@ assert.match(output[0], /^\{\{\}\|a a, \{\} y\}/);
|
|
|
348
352
|
output = await evaluateInput(".a", projState2);
|
|
349
353
|
assert.match(output[0], /^\{\}\|a/);
|
|
350
354
|
|
|
351
|
-
//
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
assert.
|
|
356
|
-
output = await evaluateInput(":input int",
|
|
357
|
-
output = await evaluateInput("123",
|
|
358
|
-
assert.match(output[0], /int: 123/);
|
|
355
|
+
// Explicit loading of dependencies and codec testing (no auto-loading)
|
|
356
|
+
const intState = createState();
|
|
357
|
+
await evaluateInput(":load Examples/arithmetics.k", intState);
|
|
358
|
+
output = await evaluateInput(":codec load ./codecs/int.mjs", intState);
|
|
359
|
+
assert.match(output[0], /^loaded codec int\.mjs/);
|
|
360
|
+
output = await evaluateInput(":input int.mjs", intState);
|
|
361
|
+
output = await evaluateInput("123", intState);
|
|
362
|
+
assert.match(output[0], /int\.mjs: 123/);
|
|
363
|
+
output = await evaluateInput(":input int.mjs", intState);
|
|
364
|
+
output = await evaluateInput("[0,1,2]", intState);
|
|
365
|
+
assert.match(output[0], /int\.mjs: \[0,1,2\]/);
|
|
359
366
|
|
|
360
367
|
const utf8AutoState = createState();
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
assert.
|
|
364
|
-
|
|
365
|
-
assert.equal(utf8AutoState.typeAliases.utf8, utf8AutoState.typeAliases.string);
|
|
366
|
-
output = await evaluateInput(":input utf8", utf8AutoState);
|
|
368
|
+
await evaluateInput(":load core.k", utf8AutoState);
|
|
369
|
+
output = await evaluateInput(":codec load ./codecs/utf8.mjs", utf8AutoState);
|
|
370
|
+
assert.match(output[0], /^loaded codec utf8\.mjs/);
|
|
371
|
+
output = await evaluateInput(":input utf8.mjs", utf8AutoState);
|
|
367
372
|
output = await evaluateInput("test utf8", utf8AutoState);
|
|
368
|
-
assert.match(output[0], /utf8: test utf8/);
|
|
373
|
+
assert.match(output[0], /utf8\.mjs: test utf8/);
|
|
369
374
|
|
|
370
375
|
const ieeeAutoState = createState();
|
|
371
|
-
|
|
372
|
-
|
|
376
|
+
await evaluateInput(":load Examples/ieee.k", ieeeAutoState);
|
|
377
|
+
output = await evaluateInput(":codec load ./codecs/ieee.mjs", ieeeAutoState);
|
|
378
|
+
assert.match(output[0], /^loaded codec ieee\.mjs/);
|
|
373
379
|
assert.ok(ieeeAutoState.typeAliases.ieee, "ieee type alias should be registered");
|
|
374
|
-
assert.ok(ieeeAutoState.typeAliases.float64, "float64 type alias should be registered");
|
|
375
|
-
assert.equal(ieeeAutoState.typeAliases.ieee, ieeeAutoState.typeAliases.float64);
|
|
376
380
|
|
|
377
381
|
const jsonInputState = createState();
|
|
378
382
|
await evaluateInput(":load core.k", jsonInputState);
|
|
379
|
-
await evaluateInput(":load ieee.k", jsonInputState);
|
|
380
|
-
await evaluateInput(":codec load json", jsonInputState);
|
|
381
|
-
output = await evaluateInput(
|
|
382
|
-
assert.match(output[0], /
|
|
383
|
-
|
|
384
|
-
|
|
383
|
+
await evaluateInput(":load Examples/ieee.k", jsonInputState);
|
|
384
|
+
await evaluateInput(":codec load ./codecs/json.mjs", jsonInputState);
|
|
385
|
+
output = await evaluateInput(':input json.mjs {"a":"Woj","n":123}', jsonInputState);
|
|
386
|
+
assert.match(output[0], /json\.mjs: \{"a":"Woj","n":123\}/);
|
|
387
|
+
|
|
388
|
+
// Verify standalone codec input, one-line and interactive modes, and multi-codec printing
|
|
389
|
+
const multiCodecState = createState();
|
|
390
|
+
await evaluateInput(":codec load ./codecs/json.mjs", multiCodecState);
|
|
391
|
+
await evaluateInput(":codec load ./codecs/utf8.mjs", multiCodecState);
|
|
392
|
+
await evaluateInput(":codec load ./codecs/int.mjs", multiCodecState);
|
|
393
|
+
|
|
394
|
+
// 1. One-line JSON input: :input json.mjs {"hello":"world"}
|
|
395
|
+
output = await evaluateInput(':input json.mjs {"hello":"world"}', multiCodecState);
|
|
396
|
+
assert.match(output[0], /json\.mjs: \{"hello":"world"\}/);
|
|
397
|
+
|
|
398
|
+
// 2. Interactive JSON input: :input json.mjs then {"anything": [1, 2, 3]}
|
|
399
|
+
output = await evaluateInput(":input json.mjs", multiCodecState);
|
|
400
|
+
assert.equal(promptForState(multiCodecState), "json.mjs> ");
|
|
401
|
+
output = await evaluateInput('{"anything": [1, 2, 3]}', multiCodecState);
|
|
402
|
+
assert.match(output[0], /json\.mjs: \{"anything":\[1,2,3\]\}/);
|
|
403
|
+
assert.equal(promptForState(multiCodecState), "> ");
|
|
404
|
+
|
|
405
|
+
// 3. Integer scalar and list input
|
|
406
|
+
output = await evaluateInput(":input int.mjs 42", multiCodecState);
|
|
407
|
+
assert.match(output[0], /int\.mjs: 42/);
|
|
408
|
+
|
|
409
|
+
output = await evaluateInput(":input int.mjs [0,1,2]", multiCodecState);
|
|
410
|
+
assert.match(output[0], /int\.mjs: \[0,1,2\]/);
|
|
411
|
+
|
|
412
|
+
// 4. Bare :input displays usage and loaded codecs
|
|
413
|
+
const bareInputState = createState();
|
|
414
|
+
output = await evaluateInput(":input", bareInputState);
|
|
415
|
+
assert.match(output[0], /^Usage: :input <codec\.mjs> \[text\]/);
|
|
416
|
+
assert.match(output[1], /Loaded codecs:/);
|
|
417
|
+
|
|
418
|
+
// 5. Codec with leading dollar sign is accepted
|
|
419
|
+
output = await evaluateInput(":input $ int.mjs 42", multiCodecState);
|
|
420
|
+
assert.match(output[0], /int\.mjs: 42/);
|
|
421
|
+
output = await evaluateInput(":input $int.mjs 100", multiCodecState);
|
|
422
|
+
assert.match(output[0], /int\.mjs: 100/);
|
|
423
|
+
|
|
424
|
+
// 6. Unknown codec throws descriptive error
|
|
425
|
+
await assert.rejects(
|
|
426
|
+
() => evaluateInput(":input nonexistent 123", multiCodecState),
|
|
427
|
+
/Codec 'nonexistent' is not loaded/
|
|
428
|
+
);
|
|
385
429
|
|
|
386
430
|
const unitState = createState();
|
|
387
|
-
output = await evaluateInput(":codec load unit", unitState);
|
|
388
|
-
assert.match(output[0],
|
|
389
|
-
output = await evaluateInput(":input unit", unitState);
|
|
390
|
-
assert.
|
|
431
|
+
output = await evaluateInput(":codec load ./codecs/unit.mjs", unitState);
|
|
432
|
+
assert.match(output[0], /^loaded codec unit\.mjs/);
|
|
433
|
+
output = await evaluateInput(":input unit.mjs", unitState);
|
|
434
|
+
assert.equal(promptForState(unitState), "unit.mjs> ");
|
|
391
435
|
output = await evaluateInput("{}", unitState);
|
|
392
|
-
assert.match(output[0], /unit: \{\}/);
|
|
436
|
+
assert.match(output[0], /unit\.mjs: \{\}/);
|
|
393
437
|
|
|
394
438
|
const polyAutoState = createState();
|
|
395
439
|
output = await evaluateInput(":load Examples/poly.k", polyAutoState);
|
|
@@ -397,28 +441,20 @@ assert.equal(output[0], "loaded Examples/poly.k");
|
|
|
397
441
|
assert.ok(polyAutoState.relAliases.reverse, "poly relations should be loaded");
|
|
398
442
|
assert.ok(polyAutoState.typeAliases.int, "arithmetics int should be auto-loaded");
|
|
399
443
|
|
|
400
|
-
// Verify
|
|
444
|
+
// Verify timing reporting is always on and :time command
|
|
401
445
|
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
446
|
assert.equal(timingState.showTiming, true);
|
|
447
|
+
|
|
412
448
|
output = await evaluateInput("()", timingState);
|
|
413
449
|
assert.equal(output[0], "{} ?{}");
|
|
414
450
|
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
415
451
|
|
|
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);
|
|
452
|
+
output = await evaluateInput(":time ()", timingState);
|
|
421
453
|
assert.equal(output[0], "{} ?{}");
|
|
454
|
+
assert.match(output[1], /\/\* comp: .*, exec: .* \*\//);
|
|
455
|
+
assert.ok(timingState.lastTiming);
|
|
456
|
+
assert.ok(typeof timingState.lastTiming.compileMs === "number");
|
|
457
|
+
assert.ok(typeof timingState.lastTiming.executeMs === "number");
|
|
422
458
|
|
|
423
459
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
424
460
|
fs.rmSync(symlinkPath, { force: true });
|
|
@@ -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.");
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Example: Demonstrating the codec pipeline
|
|
4
|
-
*
|
|
5
|
-
* Shows how to:
|
|
6
|
-
* 1. Define types with canonical names
|
|
7
|
-
* 2. Create values
|
|
8
|
-
* 3. Encode to binary
|
|
9
|
-
* 4. Pass through "compiled program" (identity)
|
|
10
|
-
* 5. Decode from binary
|
|
11
|
-
* 6. Print result
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { deriveClosedPattern } from './runtime/codec.mjs';
|
|
15
|
-
import { encodeToWire, decodeWire } from './runtime/prefix-codec.mjs';
|
|
16
|
-
import { patternToPropertyList } from './runtime/pattern-json.mjs';
|
|
17
|
-
import { Value, isVariant } from '../Value.mjs';
|
|
18
|
-
import hash from '../hash.mjs';
|
|
19
|
-
import { argv, exit } from 'node:process';
|
|
20
|
-
|
|
21
|
-
function usage() {
|
|
22
|
-
console.log(`Usage: ${argv[1]}`);
|
|
23
|
-
console.log(" Run a self-contained demonstration of the codec pipeline.");
|
|
24
|
-
console.log("");
|
|
25
|
-
console.log("Options:");
|
|
26
|
-
console.log(" -h, --help Show this help.");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (argv.slice(2).includes("-h") || argv.slice(2).includes("--help")) {
|
|
30
|
-
usage();
|
|
31
|
-
exit(0);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
console.log('=== K Codec Pipeline Example ===\n');
|
|
35
|
-
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// Step 1: Define types (normally from compiler)
|
|
38
|
-
// ============================================================================
|
|
39
|
-
|
|
40
|
-
const types = {};
|
|
41
|
-
|
|
42
|
-
// Unit: {}
|
|
43
|
-
const unitDef = { code: 'product', product: {} };
|
|
44
|
-
const unitName = hash('$C0={};');
|
|
45
|
-
types[unitName] = unitDef;
|
|
46
|
-
|
|
47
|
-
// Bool: <{} false, {} true>
|
|
48
|
-
const boolDef = {
|
|
49
|
-
code: 'union',
|
|
50
|
-
union: { false: unitName, true: unitName }
|
|
51
|
-
};
|
|
52
|
-
const boolName = hash(`$C0=<${unitName}"false",${unitName}"true">;`);
|
|
53
|
-
types[boolName] = boolDef;
|
|
54
|
-
|
|
55
|
-
// Maybe: <{} nothing, Bool something>
|
|
56
|
-
const maybeDef = {
|
|
57
|
-
code: 'union',
|
|
58
|
-
union: { nothing: unitName, something: boolName }
|
|
59
|
-
};
|
|
60
|
-
const maybeName = hash(
|
|
61
|
-
`$C0=<${unitName}"nothing",${boolName}"something">;`
|
|
62
|
-
);
|
|
63
|
-
types[maybeName] = maybeDef;
|
|
64
|
-
|
|
65
|
-
console.log('Types defined:');
|
|
66
|
-
console.log(' unit:', unitName);
|
|
67
|
-
console.log(' bool:', boolName);
|
|
68
|
-
console.log(' maybe:', maybeName);
|
|
69
|
-
console.log();
|
|
70
|
-
|
|
71
|
-
// ============================================================================
|
|
72
|
-
// Step 2: Type resolver
|
|
73
|
-
// ============================================================================
|
|
74
|
-
|
|
75
|
-
function resolveType(typeName) {
|
|
76
|
-
const def = types[typeName];
|
|
77
|
-
if (!def) {
|
|
78
|
-
throw new Error(`Unknown type: ${typeName}`);
|
|
79
|
-
}
|
|
80
|
-
return def;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ============================================================================
|
|
84
|
-
// Step 3: Create input value: Maybe Bool = something(true)
|
|
85
|
-
// ============================================================================
|
|
86
|
-
|
|
87
|
-
const inputValue = Value.variant(
|
|
88
|
-
'something',
|
|
89
|
-
Value.variant('true', Value.product({}))
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
console.log('Input value:', inputValue.toString());
|
|
93
|
-
console.log();
|
|
94
|
-
|
|
95
|
-
const maybePattern = patternToPropertyList(
|
|
96
|
-
deriveClosedPattern(maybeName, maybeDef, resolveType)
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
// ============================================================================
|
|
100
|
-
// Step 4: Parse (TEXT → BINARY)
|
|
101
|
-
// ============================================================================
|
|
102
|
-
|
|
103
|
-
console.log('--- PARSE (text → binary) ---');
|
|
104
|
-
const binaryInput = encodeToWire(inputValue, maybePattern);
|
|
105
|
-
console.log('Binary size:', binaryInput.length, 'bytes');
|
|
106
|
-
console.log(' Bytes:', binaryInput.toString('hex'));
|
|
107
|
-
console.log();
|
|
108
|
-
|
|
109
|
-
// ============================================================================
|
|
110
|
-
// Step 5: Compiled k-program (BINARY → BINARY)
|
|
111
|
-
// ============================================================================
|
|
112
|
-
|
|
113
|
-
console.log('--- COMPILED PROGRAM (binary → binary) ---');
|
|
114
|
-
|
|
115
|
-
// Simulate a compiled k-program: decode → transform → encode
|
|
116
|
-
const { value: programInput } = decodeWire(binaryInput);
|
|
117
|
-
|
|
118
|
-
console.log('Program sees value:', programInput.toString());
|
|
119
|
-
|
|
120
|
-
// Example transformation: NOT operation (flip bool inside Maybe)
|
|
121
|
-
let programOutput;
|
|
122
|
-
if (isVariant(programInput) && programInput.tag === 'something') {
|
|
123
|
-
const innerBool = programInput.value;
|
|
124
|
-
if (isVariant(innerBool)) {
|
|
125
|
-
const flipped = Value.variant(
|
|
126
|
-
innerBool.tag === 'true' ? 'false' : 'true',
|
|
127
|
-
innerBool.value
|
|
128
|
-
);
|
|
129
|
-
programOutput = Value.variant('something', flipped);
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
programOutput = programInput; // nothing → nothing
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.log('Program output value:', programOutput.toString());
|
|
136
|
-
|
|
137
|
-
// Encode output
|
|
138
|
-
const binaryOutput = encodeToWire(programOutput, maybePattern);
|
|
139
|
-
|
|
140
|
-
console.log('Binary output size:', binaryOutput.length, 'bytes');
|
|
141
|
-
console.log(' Bytes:', binaryOutput.toString('hex'));
|
|
142
|
-
console.log();
|
|
143
|
-
|
|
144
|
-
// ============================================================================
|
|
145
|
-
// Step 6: Print (BINARY → TEXT)
|
|
146
|
-
// ============================================================================
|
|
147
|
-
|
|
148
|
-
console.log('--- PRINT (binary → text) ---');
|
|
149
|
-
const { value: finalValue } = decodeWire(binaryOutput);
|
|
150
|
-
|
|
151
|
-
console.log('Output value:', finalValue.toString());
|
|
152
|
-
console.log();
|
|
153
|
-
|
|
154
|
-
// ============================================================================
|
|
155
|
-
// Summary
|
|
156
|
-
// ============================================================================
|
|
157
|
-
|
|
158
|
-
console.log('=== Pipeline Summary ===');
|
|
159
|
-
console.log('Input: ', inputValue.toString(), '(true inside Maybe)');
|
|
160
|
-
console.log('Transform: NOT operation on inner bool');
|
|
161
|
-
console.log('Output: ', finalValue.toString(), '(false inside Maybe)');
|
|
162
|
-
console.log();
|
|
163
|
-
console.log('Binary representations are canonical and type-safe!');
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import { parse as parseScript } from "../../parser.mjs";
|
|
3
|
-
import codes from "../../codes.mjs";
|
|
4
|
-
import { deriveClosedPattern } from "./codec.mjs";
|
|
5
|
-
import { patternToPropertyList } from "./pattern-json.mjs";
|
|
6
|
-
|
|
7
|
-
const IEEE_SCRIPT = fs.readFileSync(new URL("../../Examples/ieee.k", import.meta.url), "utf8");
|
|
8
|
-
|
|
9
|
-
function loadFloat64Pattern() {
|
|
10
|
-
const { defs } = parseScript(IEEE_SCRIPT);
|
|
11
|
-
const { codes: finalizedCodes, representatives } = codes.finalize(defs.codes);
|
|
12
|
-
const typeName = representatives.float64 || "float64";
|
|
13
|
-
const typeInfo = finalizedCodes[typeName];
|
|
14
|
-
if (!typeInfo) {
|
|
15
|
-
throw new Error("Could not resolve float64 type from Examples/ieee.k");
|
|
16
|
-
}
|
|
17
|
-
const resolveType = (name) => {
|
|
18
|
-
const type = finalizedCodes[name];
|
|
19
|
-
if (!type) {
|
|
20
|
-
throw new Error(`Unknown IEEE helper type '${name}'`);
|
|
21
|
-
}
|
|
22
|
-
return type;
|
|
23
|
-
};
|
|
24
|
-
return patternToPropertyList(deriveClosedPattern(typeName, typeInfo, resolveType));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const FLOAT64_PATTERN = loadFloat64Pattern();
|
|
28
|
-
|
|
29
|
-
export { FLOAT64_PATTERN, loadFloat64Pattern };
|
|
30
|
-
export default { FLOAT64_PATTERN, loadFloat64Pattern };
|