@fraczak/k 6.1.0 → 6.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Code-derivation-tests/pattern-envelope.mjs +26 -0
- package/DOCS/REPL.md +33 -0
- package/DOCS/dependency-graph.html +140 -0
- package/DOCS/dependency-graph.md +144 -0
- package/codecs/ieee.mjs +19 -4
- package/codecs/int.mjs +11 -4
- package/codecs/json.mjs +7 -4
- package/codecs/k-parse.mjs +7 -4
- package/codecs/k-print.mjs +7 -4
- package/codecs/runtime/cli-entry.mjs +13 -0
- package/codecs/show.mjs +7 -4
- package/codecs/unit.mjs +23 -8
- package/codecs/utf16.mjs +27 -5
- package/codecs/utf8.mjs +27 -5
- package/ieee.klib +0 -0
- package/package.json +2 -2
- package/repl-codecs.mjs +173 -0
- package/repl.mjs +241 -12
- package/run.mjs +16 -2
- package/test-ieee-arithmetic.mjs +93 -0
- package/test-repl.mjs +72 -2
- package/tests.sh +10 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
|
|
3
|
+
import { createState, evaluateInput } from "./repl.mjs";
|
|
4
|
+
import { Product } from "./Value.mjs";
|
|
5
|
+
import codes from "./codes.mjs";
|
|
6
|
+
import { parse as parseFloat64, print as printFloat64 } from "./codecs/ieee.mjs";
|
|
7
|
+
import { singletonPatternToCodeHash, valueForCode } from "./repl-codecs.mjs";
|
|
8
|
+
|
|
9
|
+
const state = createState();
|
|
10
|
+
await evaluateInput(":load Examples/ieee.k", state);
|
|
11
|
+
|
|
12
|
+
for (const name of ["add", "sub", "mul", "div"]) {
|
|
13
|
+
assert.ok(state.relAliases[name], `Expected public IEEE relation '${name}'`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const float64Hash = state.typeAliases.float64;
|
|
17
|
+
const floatPairHash = state.typeAliases.float_pair;
|
|
18
|
+
|
|
19
|
+
function float64(text) {
|
|
20
|
+
return valueForCode(parseFloat64(text), float64Hash, codes.find);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function floatPair(x, y) {
|
|
24
|
+
return valueForCode(new Product({
|
|
25
|
+
x: float64(x),
|
|
26
|
+
y: float64(y)
|
|
27
|
+
}), floatPairHash, codes.find);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function runOperation(op, x, y) {
|
|
31
|
+
state.value = floatPair(x, y);
|
|
32
|
+
await evaluateInput(op, state);
|
|
33
|
+
return state.value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function runProjectedResult(op, x, y) {
|
|
37
|
+
state.value = floatPair(x, y);
|
|
38
|
+
await evaluateInput(`${op}.result`, state);
|
|
39
|
+
return state.value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function assertFloatResult(value, expected, label) {
|
|
43
|
+
assert.equal(singletonPatternToCodeHash(value.pattern), float64Hash, `${label}: result should keep float64 envelope`);
|
|
44
|
+
assert.equal(printFloat64(value), expected, label);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function assertOperation({ op, x, y, result, flags = null }) {
|
|
48
|
+
const bundle = await runOperation(op, x, y);
|
|
49
|
+
assert.ok(bundle instanceof Product, `${op} should return a result bundle`);
|
|
50
|
+
assert.deepEqual(Object.keys(bundle.product).sort(), ["flags", "result"]);
|
|
51
|
+
assertFloatResult(bundle.product.result, result, `${op}(${x}, ${y}).result`);
|
|
52
|
+
if (flags != null) {
|
|
53
|
+
assert.equal(bundle.product.flags.tag, flags, `${op}(${x}, ${y}).flags`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const projected = await runProjectedResult(op, x, y);
|
|
57
|
+
assertFloatResult(projected, result, `${op}(${x}, ${y}).result projection`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const cases = [
|
|
61
|
+
// Addition: same sign, cancellation, exact fractional add, and specials.
|
|
62
|
+
{ op: "add", x: "1", y: "1", result: "2", flags: "none" },
|
|
63
|
+
{ op: "add", x: "-1", y: "1", result: "0", flags: "none" },
|
|
64
|
+
{ op: "add", x: "0.5", y: "0.25", result: "0.75", flags: "none" },
|
|
65
|
+
{ op: "add", x: "Infinity", y: "-Infinity", result: "NaN", flags: "invalid" },
|
|
66
|
+
{ op: "add", x: "NaN", y: "1", result: "NaN", flags: "none" },
|
|
67
|
+
|
|
68
|
+
// Subtraction: positive result, negative result, signed-zero-equivalent, and invalid infinity subtraction.
|
|
69
|
+
{ op: "sub", x: "4", y: "2", result: "2", flags: "none" },
|
|
70
|
+
{ op: "sub", x: "2", y: "4", result: "-2", flags: "none" },
|
|
71
|
+
{ op: "sub", x: "0", y: "0", result: "0", flags: "none" },
|
|
72
|
+
{ op: "sub", x: "Infinity", y: "Infinity", result: "NaN", flags: "invalid" },
|
|
73
|
+
|
|
74
|
+
// Multiplication: positive, signed, zero, and invalid infinity-by-zero.
|
|
75
|
+
{ op: "mul", x: "4", y: "2", result: "8", flags: "none" },
|
|
76
|
+
{ op: "mul", x: "-2", y: "4", result: "-8", flags: "none" },
|
|
77
|
+
{ op: "mul", x: "0", y: "5", result: "0", flags: "none" },
|
|
78
|
+
{ op: "mul", x: "Infinity", y: "0", result: "NaN", flags: "invalid" },
|
|
79
|
+
|
|
80
|
+
// Division: normalized exact cases, fractional quotient, divide-by-zero, invalid zero-over-zero, and infinity quotient.
|
|
81
|
+
{ op: "div", x: "4", y: "2", result: "2" },
|
|
82
|
+
{ op: "div", x: "1", y: "1", result: "1" },
|
|
83
|
+
{ op: "div", x: "1", y: "2", result: "0.5" },
|
|
84
|
+
{ op: "div", x: "1", y: "0", result: "Infinity", flags: "div_by_zero" },
|
|
85
|
+
{ op: "div", x: "0", y: "0", result: "NaN", flags: "invalid" },
|
|
86
|
+
{ op: "div", x: "Infinity", y: "2", result: "Infinity", flags: "none" }
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
for (const testCase of cases) {
|
|
90
|
+
await assertOperation(testCase);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log("OK");
|
package/test-repl.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import assert from "node:assert";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
aliasNames,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
createState,
|
|
13
13
|
evaluateInput,
|
|
14
14
|
explicitSnippetTerminated,
|
|
15
|
+
helpText,
|
|
15
16
|
isMainEntrypoint,
|
|
16
17
|
lineHasExplicitContinuation,
|
|
17
18
|
lineTerminatesSnippet
|
|
@@ -36,6 +37,13 @@ assert.match(output[0], /^succ = @/m);
|
|
|
36
37
|
output = await evaluateInput(":run succ", state);
|
|
37
38
|
assert.equal(output[0], "{}|succ ?<{} succ, ...>");
|
|
38
39
|
|
|
40
|
+
output = await evaluateInput("/zero", state);
|
|
41
|
+
assert.equal(output[0], "... undefined");
|
|
42
|
+
assert.equal(state.value.toJSON(), "succ");
|
|
43
|
+
|
|
44
|
+
output = await evaluateInput("succ", state);
|
|
45
|
+
assert.match(output[0], /^\{\}\|succ\|succ \?</);
|
|
46
|
+
|
|
39
47
|
output = await evaluateInput(":t succ", state);
|
|
40
48
|
assert.match(output[0], /^succ : /);
|
|
41
49
|
|
|
@@ -54,6 +62,8 @@ assert(completions.includes(`(${state.typeAliases.nat}`));
|
|
|
54
62
|
|
|
55
63
|
completions = completeInput(":run su", state)[0];
|
|
56
64
|
assert(completions.includes(":run succ"));
|
|
65
|
+
completions = completeInput(":co", state)[0];
|
|
66
|
+
assert(completions.includes(":codec"));
|
|
57
67
|
|
|
58
68
|
completions = completeInput("$na", state)[0];
|
|
59
69
|
assert(completions.includes("$nat"));
|
|
@@ -82,6 +92,7 @@ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "k-repl-"));
|
|
|
82
92
|
const libPath = path.join(tmpDir, "session.klib");
|
|
83
93
|
const koPath = path.join(tmpDir, "succ.ko");
|
|
84
94
|
const sourcePath = path.join(tmpDir, "session.k");
|
|
95
|
+
const codecPath = path.join(tmpDir, "bool-codec.mjs");
|
|
85
96
|
const symlinkPath = path.resolve(".test-k-repl-link");
|
|
86
97
|
output = await evaluateInput(`:klib ${libPath}`, state);
|
|
87
98
|
assert.equal(output[0], `saved ${libPath}`);
|
|
@@ -97,7 +108,7 @@ const objectFn = objectToFunction(decodeObject(fs.readFileSync(koPath)));
|
|
|
97
108
|
const objectResult = objectFn(new Product({}, [["closed-product", []]]));
|
|
98
109
|
assert.equal(objectResult.toJSON(), "succ");
|
|
99
110
|
|
|
100
|
-
fs.writeFileSync(sourcePath, "$ nat = <{} zero, nat succ>;\nsucc = |succ;\n");
|
|
111
|
+
fs.writeFileSync(sourcePath, "$ nat = <{} zero, nat succ>;\nsucc = $nat |succ $nat;\ntwice = succ succ;\n");
|
|
101
112
|
const loadedSource = createState();
|
|
102
113
|
output = await evaluateInput(`:load ${sourcePath}`, loadedSource);
|
|
103
114
|
assert.equal(output[0], `loaded ${sourcePath}`);
|
|
@@ -105,6 +116,65 @@ output = await evaluateInput(":codes", loadedSource);
|
|
|
105
116
|
assert.match(output[0], /^nat = @/m);
|
|
106
117
|
output = await evaluateInput(":rels", loadedSource);
|
|
107
118
|
assert.match(output[0], /^succ = @/m);
|
|
119
|
+
output = await evaluateInput(":d twice", loadedSource);
|
|
120
|
+
assert.match(output[0], /^twice = \$nat succ succ \$nat; -- @/);
|
|
121
|
+
output = await evaluateInput(":C nat", loadedSource);
|
|
122
|
+
assert.match(output[0], /^\$ nat = < nat succ, @[^ ]+ zero >; -- @/);
|
|
123
|
+
|
|
124
|
+
const codecState = createState();
|
|
125
|
+
output = await evaluateInput(":type bool = <{} true, {} false>", codecState);
|
|
126
|
+
assert.match(output[0], /^\$ bool = @/);
|
|
127
|
+
const boolHash = codecState.typeAliases.bool;
|
|
128
|
+
const valueModuleUrl = pathToFileURL(path.resolve("Value.mjs")).href;
|
|
129
|
+
fs.writeFileSync(codecPath, `
|
|
130
|
+
import { Product, Variant } from ${JSON.stringify(valueModuleUrl)};
|
|
131
|
+
|
|
132
|
+
export const name = "yn";
|
|
133
|
+
export const codes = [${JSON.stringify(boolHash)}];
|
|
134
|
+
|
|
135
|
+
export function parse(text) {
|
|
136
|
+
const tag = text.trim();
|
|
137
|
+
if (tag !== "true" && tag !== "false") throw new Error("expected true or false");
|
|
138
|
+
return new Variant(tag, new Product({}));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function print(value) {
|
|
142
|
+
return value.tag;
|
|
143
|
+
}
|
|
144
|
+
`);
|
|
145
|
+
assert.match(helpText(), /:codec load file/);
|
|
146
|
+
output = await evaluateInput(":codec list", codecState);
|
|
147
|
+
assert.equal(output[0], "(none)");
|
|
148
|
+
completions = completeInput(":codec l", codecState)[0];
|
|
149
|
+
assert(completions.includes(":codec load"));
|
|
150
|
+
assert(completions.includes(":codec list"));
|
|
151
|
+
completions = completeInput(`:codec load ${tmpDir}/bool`, codecState)[0];
|
|
152
|
+
assert(completions.includes(`:codec load ${codecPath}`));
|
|
153
|
+
output = await evaluateInput(`:codec load ${codecPath}`, codecState);
|
|
154
|
+
assert.equal(output[0], `loaded codec yn for ${boolHash}`);
|
|
155
|
+
output = await evaluateInput(":codec list", codecState);
|
|
156
|
+
assert.match(output[0], new RegExp(`^yn ${boolHash.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`));
|
|
157
|
+
completions = completeInput(":input bo", codecState)[0];
|
|
158
|
+
assert(completions.includes(":input bool"));
|
|
159
|
+
completions = completeInput(":input bool y", codecState)[0];
|
|
160
|
+
assert(completions.includes(":input bool yn"));
|
|
161
|
+
output = await evaluateInput(":input bool yn", codecState);
|
|
162
|
+
assert.equal(output[0], `input ${boolHash} using yn: enter value text`);
|
|
163
|
+
output = await evaluateInput("true", codecState);
|
|
164
|
+
assert.match(output[0], /\{\}\|true \?</);
|
|
165
|
+
assert.match(output[0], /yn: true/);
|
|
166
|
+
output = await evaluateInput("{} | false", codecState);
|
|
167
|
+
assert.doesNotMatch(output[0], /yn: false/);
|
|
168
|
+
|
|
169
|
+
const utf8State = createState();
|
|
170
|
+
output = await evaluateInput(":load core.k", utf8State);
|
|
171
|
+
assert.equal(output[0], "loaded core.k");
|
|
172
|
+
output = await evaluateInput(":codec load ./codecs/utf8.mjs", utf8State);
|
|
173
|
+
assert.equal(output[0], `loaded codec utf8 for ${utf8State.typeAliases.string}`);
|
|
174
|
+
output = await evaluateInput(":input string utf8", utf8State);
|
|
175
|
+
assert.equal(output[0], `input ${utf8State.typeAliases.string} using utf8: enter value text`);
|
|
176
|
+
output = await evaluateInput("hello", utf8State);
|
|
177
|
+
assert.match(output[0], /utf8: hello/);
|
|
108
178
|
|
|
109
179
|
const replPath = fileURLToPath(new URL("./repl.mjs", import.meta.url));
|
|
110
180
|
try {
|
package/tests.sh
CHANGED
|
@@ -11,5 +11,14 @@ node ./codecs/unit.mjs --parse | node ./codecs/unit.mjs --print | grep -qx '{}'
|
|
|
11
11
|
echo
|
|
12
12
|
printf 'A🙂\nBé~\t' | ./codecs/utf8.mjs --parse | ./k.mjs -k Examples/byte.k |./codecs/utf8.mjs --print
|
|
13
13
|
echo
|
|
14
|
-
|
|
14
|
+
./objects/compile-lib.mjs Examples/ieee.k ieee.klib
|
|
15
|
+
MUL=`./objects/extract-aliases.mjs ieee.klib | grep '^mul = @' | sed 's/^mul = \(@[^;]*\);.*$/\1/'`
|
|
16
|
+
DIV=`./objects/extract-aliases.mjs ieee.klib | grep '^div = @' | sed 's/^div = \(@[^;]*\);.*$/\1/'`
|
|
17
|
+
ARG=0.12
|
|
18
|
+
printf "$ARG * $ARG = "
|
|
19
|
+
echo $ARG | ./codecs/ieee.mjs --parse | ./k.mjs --lib ieee.klib "{()x,()y} $MUL .result" | ./codecs/ieee.mjs --print
|
|
20
|
+
printf "$ARG / $ARG = "
|
|
21
|
+
DIV_RESULT=`echo $ARG | ./codecs/ieee.mjs --parse | ./k.mjs --lib ieee.klib "{()x,()y} $DIV .result" | ./codecs/ieee.mjs --print`
|
|
22
|
+
echo "$DIV_RESULT"
|
|
23
|
+
test "$DIV_RESULT" = "1"
|
|
15
24
|
echo
|