@fraczak/k 6.2.3 → 6.3.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/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +4 -4
- package/README.md +1 -1
- package/Value.mjs +50 -40
- package/backend-api.mjs +1 -1
- package/codecs/example-pipeline.mjs +7 -7
- package/codecs/ieee.mjs +8 -8
- package/codecs/int.mjs +6 -6
- package/codecs/json-codec.mjs +15 -15
- package/codecs/runtime/codec.mjs +13 -13
- package/codecs/runtime/prefix-codec.mjs +18 -18
- package/codecs/runtime/show-value.mjs +3 -3
- package/codecs/runtime/test-codec.mjs +7 -7
- package/codecs/runtime/typeFromValue.mjs +4 -4
- package/codecs/runtime/unicode-string.mjs +30 -30
- package/codecs/unit.mjs +4 -4
- package/index.mjs +2 -2
- package/k.mjs +15 -5
- package/kvm.mjs +10 -9
- package/objects/README.md +1 -1
- package/objects/compile.mjs +2 -1
- package/package.json +1 -1
- package/repl.mjs +2 -2
- package/run.mjs +33 -34
- package/scripts/run-tests.mjs +1 -0
- package/tests/code-derivation/codec-witness-pattern.mjs +4 -4
- package/tests/code-derivation/pattern-envelope.mjs +12 -12
- package/tests/integration.sh +26 -1
- package/tests/test-deep-wire-value.mjs +4 -4
- package/tests/test-ieee-arithmetic.mjs +3 -3
- package/tests/test-k-object.mjs +10 -10
- package/tests/test-kvm.mjs +1 -1
- package/tests/test-repl.mjs +4 -4
- package/tests/test-structural-values.mjs +27 -0
- package/valueIO.mjs +1 -1
- package/valueParser.jison +9 -9
- package/valueParser.mjs +7 -7
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isProduct, isVariant } from "../../Value.mjs";
|
|
2
2
|
|
|
3
3
|
const nameRE = /^[a-zA-Z0-9_+-][a-zA-Z0-9_?!+-]*$/;
|
|
4
4
|
|
|
@@ -50,8 +50,8 @@ function propertyListToFilter(propertyList) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function valueToK(v) {
|
|
53
|
-
if (v
|
|
54
|
-
if (v
|
|
53
|
+
if (isVariant(v)) return `${valueToK(v.value)}|${pLabel(v.tag).trimStart()}`;
|
|
54
|
+
if (isProduct(v)) {
|
|
55
55
|
const keys = Object.keys(v.product);
|
|
56
56
|
if (keys.length === 0) return "{}";
|
|
57
57
|
const fields = keys.map((k) => `${valueToK(v.product[k])}${pLabel(k)}`);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { deriveClosedPattern } from './codec.mjs';
|
|
7
7
|
import { encodeToWire, decodeWire } from './prefix-codec.mjs';
|
|
8
8
|
import { patternToPropertyList } from './pattern-json.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import { Value, isProduct, isVariant } from '../../Value.mjs';
|
|
10
10
|
import hash from '../../hash.mjs';
|
|
11
11
|
|
|
12
12
|
// Type repository
|
|
@@ -44,7 +44,7 @@ function resolveType(typeName) {
|
|
|
44
44
|
|
|
45
45
|
// Test 1: Unit value
|
|
46
46
|
console.log('\n=== Test 1: Unit ===');
|
|
47
|
-
const unitValue =
|
|
47
|
+
const unitValue = Value.product({});
|
|
48
48
|
console.log('Encoding unit:', unitValue.toString());
|
|
49
49
|
|
|
50
50
|
const unitPattern = patternToPropertyList(deriveClosedPattern(unitTypeName, unitTypeDef, resolveType));
|
|
@@ -54,12 +54,12 @@ console.log('Hex:', unitEncoded.toString('hex'));
|
|
|
54
54
|
|
|
55
55
|
const unitDecoded = decodeWire(unitEncoded);
|
|
56
56
|
console.log('Decoded value:', unitDecoded.value.toString());
|
|
57
|
-
console.log('Match:', unitDecoded.value
|
|
57
|
+
console.log('Match:', isProduct(unitDecoded.value) &&
|
|
58
58
|
Object.keys(unitDecoded.value.product).length === 0);
|
|
59
59
|
|
|
60
60
|
// Test 2: Bool false
|
|
61
61
|
console.log('\n=== Test 2: Bool (false) ===');
|
|
62
|
-
const falseValue =
|
|
62
|
+
const falseValue = Value.variant('false', Value.product({}));
|
|
63
63
|
console.log('Encoding false:', falseValue.toString());
|
|
64
64
|
|
|
65
65
|
const boolPattern = patternToPropertyList(deriveClosedPattern(boolTypeName, boolTypeDef, resolveType));
|
|
@@ -69,12 +69,12 @@ console.log('Hex:', falseEncoded.toString('hex'));
|
|
|
69
69
|
|
|
70
70
|
const falseDecoded = decodeWire(falseEncoded);
|
|
71
71
|
console.log('Decoded value:', falseDecoded.value.toString());
|
|
72
|
-
console.log('Match:', falseDecoded.value
|
|
72
|
+
console.log('Match:', isVariant(falseDecoded.value) &&
|
|
73
73
|
falseDecoded.value.tag === 'false');
|
|
74
74
|
|
|
75
75
|
// Test 3: Bool true
|
|
76
76
|
console.log('\n=== Test 3: Bool (true) ===');
|
|
77
|
-
const trueValue =
|
|
77
|
+
const trueValue = Value.variant('true', Value.product({}));
|
|
78
78
|
console.log('Encoding true:', trueValue.toString());
|
|
79
79
|
|
|
80
80
|
const trueEncoded = encodeToWire(trueValue, boolPattern);
|
|
@@ -83,7 +83,7 @@ console.log('Hex:', trueEncoded.toString('hex'));
|
|
|
83
83
|
|
|
84
84
|
const trueDecoded = decodeWire(trueEncoded);
|
|
85
85
|
console.log('Decoded value:', trueDecoded.value.toString());
|
|
86
|
-
console.log('Match:', trueDecoded.value
|
|
86
|
+
console.log('Match:', isVariant(trueDecoded.value) &&
|
|
87
87
|
trueDecoded.value.tag === 'true');
|
|
88
88
|
|
|
89
89
|
console.log('\n=== All tests complete ===');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isProduct, isVariant } from "../../Value.mjs";
|
|
2
2
|
|
|
3
3
|
function typeDefsFromValue(value) {
|
|
4
4
|
let nextId = 0;
|
|
@@ -6,7 +6,7 @@ function typeDefsFromValue(value) {
|
|
|
6
6
|
|
|
7
7
|
function visit(node) {
|
|
8
8
|
const name = `:auto${nextId++}`;
|
|
9
|
-
if (node
|
|
9
|
+
if (isProduct(node)) {
|
|
10
10
|
const labels = Object.keys(node.product).sort();
|
|
11
11
|
const product = {};
|
|
12
12
|
for (const label of labels) {
|
|
@@ -16,7 +16,7 @@ function typeDefsFromValue(value) {
|
|
|
16
16
|
return name;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
if (node
|
|
19
|
+
if (isVariant(node)) {
|
|
20
20
|
const childName = visit(node.value);
|
|
21
21
|
defs[name] = { code: "union", union: { [node.tag]: childName } };
|
|
22
22
|
return name;
|
|
@@ -31,4 +31,4 @@ function typeDefsFromValue(value) {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export { typeDefsFromValue };
|
|
34
|
-
export default { typeDefsFromValue };
|
|
34
|
+
export default { typeDefsFromValue };
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Value, isProduct, isVariant } from "../../Value.mjs";
|
|
2
2
|
|
|
3
|
-
const UNIT =
|
|
4
|
-
const BIT0 =
|
|
5
|
-
const BIT1 =
|
|
3
|
+
const UNIT = Value.product({});
|
|
4
|
+
const BIT0 = Value.variant("0", UNIT);
|
|
5
|
+
const BIT1 = Value.variant("1", UNIT);
|
|
6
6
|
|
|
7
7
|
function bitValue(bit) {
|
|
8
8
|
return bit === 0 ? BIT0 : BIT1;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
function requireProduct(value, where) {
|
|
12
|
-
if (!(value
|
|
12
|
+
if (!isProduct(value)) {
|
|
13
13
|
throw new Error(`${where}: expected Product`);
|
|
14
14
|
}
|
|
15
15
|
return value.product;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function requireVariant(value, where) {
|
|
19
|
-
if (!(value
|
|
19
|
+
if (!isVariant(value)) {
|
|
20
20
|
throw new Error(`${where}: expected Variant`);
|
|
21
21
|
}
|
|
22
22
|
return value;
|
|
@@ -27,7 +27,7 @@ function bitsProduct(width, n) {
|
|
|
27
27
|
for (let i = width - 1; i >= 0; i--) {
|
|
28
28
|
product[String(i)] = bitValue((n >> i) & 1);
|
|
29
29
|
}
|
|
30
|
-
return
|
|
30
|
+
return Value.product(product);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function parseBitsProduct(value, maxBit, where) {
|
|
@@ -52,14 +52,14 @@ function parseByteProduct(value, where) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function encodeBmpCommonHi(hi) {
|
|
55
|
-
if (hi >= 0x08 && hi <= 0x0f) return
|
|
56
|
-
if (hi >= 0x10 && hi <= 0x7f) return
|
|
57
|
-
if (hi >= 0x80 && hi <= 0xcf) return
|
|
58
|
-
if (hi >= 0xd0 && hi <= 0xd7) return
|
|
59
|
-
if (hi === 0xf9) return
|
|
60
|
-
if (hi >= 0xfa && hi <= 0xfb) return
|
|
61
|
-
if (hi >= 0xfc && hi <= 0xfd) return
|
|
62
|
-
if (hi >= 0xfe && hi <= 0xff) return
|
|
55
|
+
if (hi >= 0x08 && hi <= 0x0f) return Value.variant("h08_0F", bitsProduct(3, hi - 0x08));
|
|
56
|
+
if (hi >= 0x10 && hi <= 0x7f) return Value.variant("h10_7F", bitsProduct(7, hi - 0x10));
|
|
57
|
+
if (hi >= 0x80 && hi <= 0xcf) return Value.variant("h80_CF", bitsProduct(7, hi - 0x80));
|
|
58
|
+
if (hi >= 0xd0 && hi <= 0xd7) return Value.variant("hD0_D7", bitsProduct(3, hi - 0xd0));
|
|
59
|
+
if (hi === 0xf9) return Value.variant("hF9", UNIT);
|
|
60
|
+
if (hi >= 0xfa && hi <= 0xfb) return Value.variant("hFA_FB", bitsProduct(1, hi - 0xfa));
|
|
61
|
+
if (hi >= 0xfc && hi <= 0xfd) return Value.variant("hFC_FD", bitsProduct(1, hi - 0xfc));
|
|
62
|
+
if (hi >= 0xfe && hi <= 0xff) return Value.variant("hFE_FF", bitsProduct(1, hi - 0xfe));
|
|
63
63
|
throw new Error(`Unsupported BMP common hi byte: 0x${hi.toString(16)}`);
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -77,9 +77,9 @@ function decodeBmpCommonHi(value) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function encodeBmpPrivateHi(hi) {
|
|
80
|
-
if (hi >= 0xe0 && hi <= 0xef) return
|
|
81
|
-
if (hi >= 0xf0 && hi <= 0xf7) return
|
|
82
|
-
if (hi === 0xf8) return
|
|
80
|
+
if (hi >= 0xe0 && hi <= 0xef) return Value.variant("hE0_EF", bitsProduct(4, hi - 0xe0));
|
|
81
|
+
if (hi >= 0xf0 && hi <= 0xf7) return Value.variant("hF0_F7", bitsProduct(3, hi - 0xf0));
|
|
82
|
+
if (hi === 0xf8) return Value.variant("hF8", UNIT);
|
|
83
83
|
throw new Error(`Unsupported BMP private-use hi byte: 0x${hi.toString(16)}`);
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -92,10 +92,10 @@ function decodeBmpPrivateHi(value) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function encodePlane2To16(plane) {
|
|
95
|
-
if (plane >= 2 && plane <= 3) return
|
|
96
|
-
if (plane >= 4 && plane <= 7) return
|
|
97
|
-
if (plane >= 8 && plane <= 15) return
|
|
98
|
-
if (plane === 16) return
|
|
95
|
+
if (plane >= 2 && plane <= 3) return Value.variant("p02_03", bitsProduct(1, plane - 2));
|
|
96
|
+
if (plane >= 4 && plane <= 7) return Value.variant("p04_07", bitsProduct(2, plane - 4));
|
|
97
|
+
if (plane >= 8 && plane <= 15) return Value.variant("p08_0F", bitsProduct(3, plane - 8));
|
|
98
|
+
if (plane === 16) return Value.variant("p10", UNIT);
|
|
99
99
|
throw new Error(`Plane out of range for supplementary_planes2_16: ${plane}`);
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -117,34 +117,34 @@ function codePointToUnicodeValue(cp) {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (cp <= 0x7f) {
|
|
120
|
-
return
|
|
120
|
+
return Value.variant("ascii", bitsProduct(7, cp));
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
if (cp <= 0x7ff) {
|
|
124
|
-
return
|
|
124
|
+
return Value.variant("plane0", bitsProduct(11, cp));
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
if (cp <= 0xffff) {
|
|
128
128
|
const hi = (cp >> 8) & 0xff;
|
|
129
129
|
const lo = cp & 0xff;
|
|
130
130
|
if (hi >= 0xe0 && hi <= 0xf8) {
|
|
131
|
-
return
|
|
131
|
+
return Value.variant("bmp_private_use", Value.product({ hi: encodeBmpPrivateHi(hi), lo: byteProduct(lo) }));
|
|
132
132
|
}
|
|
133
|
-
return
|
|
133
|
+
return Value.variant("bmp_common", Value.product({ hi: encodeBmpCommonHi(hi), lo: byteProduct(lo) }));
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
const mid = (cp >> 8) & 0xff;
|
|
137
137
|
const lo = cp & 0xff;
|
|
138
138
|
|
|
139
139
|
if (cp <= 0x1ffff) {
|
|
140
|
-
return
|
|
140
|
+
return Value.variant("supplementary_plane1", Value.product({
|
|
141
141
|
mid: byteProduct(mid),
|
|
142
142
|
lo: byteProduct(lo)
|
|
143
143
|
}));
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
const plane = cp >> 16;
|
|
147
|
-
return
|
|
147
|
+
return Value.variant("supplementary_planes2_16", Value.product({
|
|
148
148
|
plane: encodePlane2To16(plane),
|
|
149
149
|
mid: byteProduct(mid),
|
|
150
150
|
lo: byteProduct(lo)
|
|
@@ -181,11 +181,11 @@ function unicodeValueToCodePoint(value) {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
function textToStringValue(text) {
|
|
184
|
-
let out =
|
|
184
|
+
let out = Value.variant("nil", UNIT);
|
|
185
185
|
const chars = Array.from(text);
|
|
186
186
|
for (let i = chars.length - 1; i >= 0; i--) {
|
|
187
187
|
const cp = chars[i].codePointAt(0);
|
|
188
|
-
out =
|
|
188
|
+
out = Value.variant("cons", Value.product({
|
|
189
189
|
car: codePointToUnicodeValue(cp),
|
|
190
190
|
cdr: out
|
|
191
191
|
}));
|
package/codecs/unit.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { stdin, stdout, argv, exit } from "node:process";
|
|
4
|
-
import {
|
|
4
|
+
import { Value, isProduct } from "../Value.mjs";
|
|
5
5
|
import { isMainEntrypoint } from "./runtime/cli-entry.mjs";
|
|
6
6
|
import { decodeWire, encodeToWire } from "./runtime/prefix-codec.mjs";
|
|
7
7
|
|
|
@@ -28,15 +28,15 @@ function readAll(stream) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function unitEncoding() {
|
|
31
|
-
return encodeToWire(
|
|
31
|
+
return encodeToWire(Value.product({}), UNIT_PATTERN);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function parse() {
|
|
35
|
-
return
|
|
35
|
+
return Value.product({});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function print(value) {
|
|
39
|
-
if (!(value
|
|
39
|
+
if (!isProduct(value) || Object.keys(value.product).length !== 0) {
|
|
40
40
|
throw new Error("Input is not a unit value");
|
|
41
41
|
}
|
|
42
42
|
return "{}";
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parse } from "./parser.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { fromObject, isValue } from "./Value.mjs";
|
|
3
3
|
import { parseValue } from "./valueIO.mjs";
|
|
4
4
|
import { compileTypes } from "./compiler.mjs";
|
|
5
5
|
import run from "./run.mjs";
|
|
@@ -19,7 +19,7 @@ compile.doc = "Transforms k-script (string) into a function";
|
|
|
19
19
|
|
|
20
20
|
function runScriptOnData(script, data, options = {}) {
|
|
21
21
|
let parsedData;
|
|
22
|
-
if (data
|
|
22
|
+
if (isValue(data)) {
|
|
23
23
|
parsedData = data;
|
|
24
24
|
} else if (typeof data === "string") {
|
|
25
25
|
parsedData = parseValue(data, null, null);
|
package/k.mjs
CHANGED
|
@@ -12,14 +12,23 @@ const prog = argv[1];
|
|
|
12
12
|
let kScript, inputStream;
|
|
13
13
|
|
|
14
14
|
function usage() {
|
|
15
|
-
console.error(`Usage: ${prog} [ --lib lib-file ]
|
|
15
|
+
console.error(`Usage: ${prog} [ --lib lib-file ] [ --export spec ]... ( k-expr | file ) [ input-file ]`);
|
|
16
16
|
console.error(` E.g., echo '["zebara","ela"]' | k-parse --input-type '$x=<{} zebara, {} ela>; $v={x 0, x 1}; $v' | ${prog} '{.1 0}'`);
|
|
17
17
|
console.error("Options:");
|
|
18
|
-
console.error(" --lib file Load
|
|
18
|
+
console.error(" --lib file Load one .klib dependency before compiling.");
|
|
19
19
|
console.error(" --export spec Export a library alias into scope. 'name' or 'libname:localname'. May be repeated.");
|
|
20
20
|
console.error(" -h, --help Show this help.");
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function isFile(filePath) {
|
|
24
|
+
try {
|
|
25
|
+
return fs.statSync(filePath).isFile();
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error?.code === "ENOENT" || error?.code === "ENOTDIR") return false;
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
23
32
|
function compileFile(filePath, libraries, preamble) {
|
|
24
33
|
const buffer = fs.readFileSync(filePath);
|
|
25
34
|
try {
|
|
@@ -44,6 +53,7 @@ function compileFile(filePath, libraries, preamble) {
|
|
|
44
53
|
args.shift();
|
|
45
54
|
const libPath = args.shift();
|
|
46
55
|
if (!libPath) throw new Error("--lib requires a file argument");
|
|
56
|
+
if (libraries.length > 0) throw new Error("--lib may be specified at most once");
|
|
47
57
|
const libBuffer = fs.readFileSync(libPath);
|
|
48
58
|
libraries.push(loadLibrary(decodeObject(libBuffer)));
|
|
49
59
|
} else if (args[0] === "--export") {
|
|
@@ -86,10 +96,10 @@ function compileFile(filePath, libraries, preamble) {
|
|
|
86
96
|
throw new Error("Missing script argument");
|
|
87
97
|
}
|
|
88
98
|
if (arg === "-k") {
|
|
89
|
-
|
|
90
|
-
} else {
|
|
91
|
-
return buildExportPreamble() + arg;
|
|
99
|
+
throw new Error("-k is no longer supported; pass the source/object file path directly");
|
|
92
100
|
}
|
|
101
|
+
if (isFile(arg)) return compileFile(arg, libraries, buildExportPreamble());
|
|
102
|
+
return buildExportPreamble() + arg;
|
|
93
103
|
})(args.shift());
|
|
94
104
|
let kScript = typeof kScriptStr === "function" ? kScriptStr : k.compile(kScriptStr, { libraries });
|
|
95
105
|
inputStream = (function (arg) {
|
package/kvm.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { argv, stdin, exit, stdout } from "node:process";
|
|
4
|
-
import {
|
|
4
|
+
import { Value, composePattern, withPattern, isProduct, isVariant } from "./Value.mjs";
|
|
5
5
|
import { exportPatternGraph } from "./codecs/runtime/codec.mjs";
|
|
6
6
|
import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
|
|
7
7
|
import {
|
|
@@ -281,7 +281,7 @@ function executeInstruction(inst, registers, context) {
|
|
|
281
281
|
}
|
|
282
282
|
case "project_field": {
|
|
283
283
|
const val = registers.get(inst.src);
|
|
284
|
-
if (!(val
|
|
284
|
+
if (!isProduct(val)) return undefined;
|
|
285
285
|
const fieldVal = val.product[inst.label];
|
|
286
286
|
if (fieldVal === undefined) return undefined;
|
|
287
287
|
|
|
@@ -295,7 +295,7 @@ function executeInstruction(inst, registers, context) {
|
|
|
295
295
|
}
|
|
296
296
|
case "project_variant": {
|
|
297
297
|
const val = registers.get(inst.src);
|
|
298
|
-
if (!(val
|
|
298
|
+
if (!isVariant(val) || val.tag !== inst.tag) return undefined;
|
|
299
299
|
|
|
300
300
|
if (options.envelopeFree) {
|
|
301
301
|
registers.set(inst.dest, val.value);
|
|
@@ -308,11 +308,11 @@ function executeInstruction(inst, registers, context) {
|
|
|
308
308
|
case "make_variant": {
|
|
309
309
|
const val = registers.get(inst.src);
|
|
310
310
|
if (options.envelopeFree) {
|
|
311
|
-
registers.set(inst.dest,
|
|
311
|
+
registers.set(inst.dest, Value.variant(inst.tag, val));
|
|
312
312
|
} else {
|
|
313
313
|
registers.set(
|
|
314
314
|
inst.dest,
|
|
315
|
-
|
|
315
|
+
Value.variant(inst.tag, val, composePattern("open-union", [[inst.tag, val.pattern]]))
|
|
316
316
|
);
|
|
317
317
|
}
|
|
318
318
|
return { type: "continue" };
|
|
@@ -375,13 +375,13 @@ function executeInstruction(inst, registers, context) {
|
|
|
375
375
|
}
|
|
376
376
|
let productValue;
|
|
377
377
|
if (options.envelopeFree) {
|
|
378
|
-
productValue =
|
|
378
|
+
productValue = Value.product(result);
|
|
379
379
|
} else {
|
|
380
380
|
const staticOutputPattern = inst.pattern;
|
|
381
381
|
if (staticOutputPattern) {
|
|
382
|
-
productValue =
|
|
382
|
+
productValue = Value.product(result, staticOutputPattern);
|
|
383
383
|
} else {
|
|
384
|
-
productValue =
|
|
384
|
+
productValue = Value.product(result, composePattern("closed-product", patternEntries));
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
registers.set(inst.dest, productValue);
|
|
@@ -430,7 +430,7 @@ function usage() {
|
|
|
430
430
|
const prog = argv[1] || "kvm.mjs";
|
|
431
431
|
console.error(`Usage: node ${prog} [ options ] ( k-expr | -k file ) [ input-file ]`);
|
|
432
432
|
console.error("Options:");
|
|
433
|
-
console.error(" --lib file Load
|
|
433
|
+
console.error(" --lib file Load one .klib dependency before compiling.");
|
|
434
434
|
console.error(" --envelope-free Run the interpreter in envelope-free mode.");
|
|
435
435
|
console.error(" -h, --help Show this help.");
|
|
436
436
|
}
|
|
@@ -454,6 +454,7 @@ async function main() {
|
|
|
454
454
|
args.shift();
|
|
455
455
|
const libPath = args.shift();
|
|
456
456
|
if (!libPath) throw new Error("--lib requires a file argument");
|
|
457
|
+
if (libraries.length > 0) throw new Error("--lib may be specified at most once");
|
|
457
458
|
const libBuffer = fs.readFileSync(libPath);
|
|
458
459
|
libraries.push(loadLibrary(decodeObject(libBuffer)));
|
|
459
460
|
} else {
|
package/objects/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Compiles `.k` source into an executable object file (`.ko`), library file
|
|
|
11
11
|
extension, or from `--format`.
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
k-compile [--lib lib-file]
|
|
14
|
+
k-compile [--lib lib-file] [--format ko|klib|kvm] [source-snippet | input-file [output-file]]
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
Existing input paths are read as files. A non-existing input with `.k`, `.ko`,
|
package/objects/compile.mjs
CHANGED
|
@@ -25,7 +25,7 @@ function helpText() {
|
|
|
25
25
|
" output-file Output path (.ko, .klib, or .kvm). Writes to stdout when omitted.",
|
|
26
26
|
"",
|
|
27
27
|
"Options:",
|
|
28
|
-
" --lib file Load
|
|
28
|
+
" --lib file Load one .klib dependency before compiling.",
|
|
29
29
|
" --export spec Export a library alias into the source scope. May be repeated.",
|
|
30
30
|
" spec is 'name' or 'libname:localname'.",
|
|
31
31
|
" --format fmt Output format: ko, klib, or kvm. Overrides extension detection.",
|
|
@@ -168,6 +168,7 @@ try {
|
|
|
168
168
|
args.shift();
|
|
169
169
|
const libPath = args.shift();
|
|
170
170
|
if (!libPath) throw new Error("--lib requires a file argument");
|
|
171
|
+
if (libraries.length > 0) throw new Error("--lib may be specified at most once");
|
|
171
172
|
libraries.push(loadLibrary(decodeObject(fs.readFileSync(libPath))));
|
|
172
173
|
} else if (args[0] === "--export") {
|
|
173
174
|
args.shift();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraczak/k",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "k — a language for composing first-order partial functions over algebraic data types (products and tagged unions); includes runtime, type derivation, REPL, and codec toolchain",
|
|
5
5
|
"main": "index.mjs",
|
|
6
6
|
"scripts": {
|
package/repl.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { argv, exit, stdin, stdout } from "node:process";
|
|
|
10
10
|
import { annotate, parse } from "./index.mjs";
|
|
11
11
|
import run from "./run.mjs";
|
|
12
12
|
import codes from "./codes.mjs";
|
|
13
|
-
import {
|
|
13
|
+
import { Value } from "./Value.mjs";
|
|
14
14
|
import { patterns2filters, prettyCode, prettyRel } from "./pretty.mjs";
|
|
15
15
|
import {
|
|
16
16
|
compileObject,
|
|
@@ -50,7 +50,7 @@ function cloneJSON(value) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function emptyValue() {
|
|
53
|
-
return
|
|
53
|
+
return Value.product({}, [["closed-product", []]]);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function createState() {
|
package/run.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "assert";
|
|
2
|
-
import {
|
|
2
|
+
import { Value, edgeSubpattern, composePattern, withPattern, isProduct, isVariant } from "./Value.mjs"
|
|
3
3
|
import { exportPatternGraph, intersectPropertyListPatterns } from "./codecs/runtime/codec.mjs";
|
|
4
4
|
import { patternToPropertyList } from "./codecs/runtime/pattern-json.mjs";
|
|
5
5
|
|
|
@@ -238,38 +238,37 @@ function compiledExp(findCode, exp, typePatternGraph) {
|
|
|
238
238
|
case "product": {
|
|
239
239
|
const labels = exp.product.map(({ label }) => label);
|
|
240
240
|
const fieldFns = exp.product.map(({ exp: e }) => compiledExp(findCode, e, typePatternGraph));
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
241
|
+
fn = (value) => {
|
|
242
|
+
if (value === undefined) return undefined;
|
|
243
|
+
const result = {};
|
|
244
|
+
const patternEntries = [];
|
|
245
|
+
for (let i = 0, len = labels.length; i < len; i++) {
|
|
246
|
+
const r = fieldFns[i](value);
|
|
247
|
+
if (r === undefined) return undefined;
|
|
248
|
+
const label = labels[i];
|
|
249
|
+
result[label] = r;
|
|
250
|
+
patternEntries.push([label, r.pattern]);
|
|
251
|
+
}
|
|
252
|
+
const dynamicOutputPattern = composePattern("closed-product", patternEntries);
|
|
253
|
+
const pattern = staticOutputPattern && dynamicOutputPattern
|
|
254
|
+
? intersectPatterns(staticOutputPattern, dynamicOutputPattern)
|
|
255
|
+
: staticOutputPattern || dynamicOutputPattern;
|
|
256
|
+
if (!pattern && staticOutputPattern && dynamicOutputPattern) {
|
|
257
|
+
throw new TypeError(
|
|
258
|
+
`Type Error in '${exp.op}'${expLocation(exp)}\n` +
|
|
259
|
+
` - Product value envelope does not intersect expression output pattern.\n` +
|
|
260
|
+
` - output pattern: ${patternPreview(staticOutputPattern)}\n` +
|
|
261
|
+
` - product envelope: ${patternPreview(dynamicOutputPattern)}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
return Value.product(result, pattern);
|
|
265
|
+
};
|
|
267
266
|
break;
|
|
268
267
|
}
|
|
269
268
|
case "vid":
|
|
270
269
|
fn = (value) => {
|
|
271
270
|
if (value === undefined) return undefined;
|
|
272
|
-
return
|
|
271
|
+
return Value.variant(exp.vid, value, composePattern("open-union", [[exp.vid, value.pattern]]));
|
|
273
272
|
};
|
|
274
273
|
break;
|
|
275
274
|
default:
|
|
@@ -376,13 +375,13 @@ function compiledConvergedExp(findCode, exp, typePatternGraph, options = {}) {
|
|
|
376
375
|
break;
|
|
377
376
|
case "dot":
|
|
378
377
|
fn = (value) => {
|
|
379
|
-
if (value === undefined || !(value
|
|
378
|
+
if (value === undefined || !isProduct(value)) return undefined;
|
|
380
379
|
return Object.hasOwn(value.product, exp.dot) ? value.product[exp.dot] : undefined;
|
|
381
380
|
};
|
|
382
381
|
break;
|
|
383
382
|
case "div":
|
|
384
383
|
fn = (value) => {
|
|
385
|
-
if (value === undefined || !(value
|
|
384
|
+
if (value === undefined || !isVariant(value) || value.tag !== exp.div) return undefined;
|
|
386
385
|
return value.value;
|
|
387
386
|
};
|
|
388
387
|
break;
|
|
@@ -420,12 +419,12 @@ function compiledConvergedExp(findCode, exp, typePatternGraph, options = {}) {
|
|
|
420
419
|
if (r === undefined) return undefined;
|
|
421
420
|
result[labels[i]] = r;
|
|
422
421
|
}
|
|
423
|
-
return
|
|
422
|
+
return Value.product(result);
|
|
424
423
|
};
|
|
425
424
|
break;
|
|
426
425
|
}
|
|
427
426
|
case "vid":
|
|
428
|
-
fn = (value) => value === undefined ? undefined :
|
|
427
|
+
fn = (value) => value === undefined ? undefined : Value.variant(exp.vid, value);
|
|
429
428
|
break;
|
|
430
429
|
default:
|
|
431
430
|
assert(false,`Unknown operation: '${exp.op}'`);
|
|
@@ -441,7 +440,7 @@ function verify(findCode, code, value) {
|
|
|
441
440
|
code = findCode(code);
|
|
442
441
|
switch (code.code) {
|
|
443
442
|
case "product":
|
|
444
|
-
if (!
|
|
443
|
+
if (!isProduct(value)) return false;
|
|
445
444
|
else {
|
|
446
445
|
const fields = Object.keys(value.product);
|
|
447
446
|
if (fields.length !== Object.keys(code.product).length) return false;
|
|
@@ -450,7 +449,7 @@ function verify(findCode, code, value) {
|
|
|
450
449
|
);
|
|
451
450
|
}
|
|
452
451
|
case "union":
|
|
453
|
-
if (!
|
|
452
|
+
if (!isProduct(value)) return false;
|
|
454
453
|
else {
|
|
455
454
|
const fields = Object.keys(value.product);
|
|
456
455
|
if (fields.length !== 1) return false;
|
package/scripts/run-tests.mjs
CHANGED
|
@@ -18,6 +18,7 @@ const tests = [
|
|
|
18
18
|
["tests/test-fingerprint.mjs", node, ["tests/test-fingerprint.mjs"]],
|
|
19
19
|
["tests/test-hash-fuzz.mjs", node, ["tests/test-hash-fuzz.mjs"]],
|
|
20
20
|
["tests/test-hash-normalization.mjs", node, ["tests/test-hash-normalization.mjs"]],
|
|
21
|
+
["tests/test-structural-values.mjs", node, ["tests/test-structural-values.mjs"]],
|
|
21
22
|
["tests/test-deep-wire-value.mjs", node, ["tests/test-deep-wire-value.mjs"]],
|
|
22
23
|
["tests/test-k-object.mjs", node, ["tests/test-k-object.mjs"]],
|
|
23
24
|
["tests/test-repl.mjs", node, ["tests/test-repl.mjs"]],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'assert';
|
|
2
2
|
import { parseValue } from '../../valueIO.mjs';
|
|
3
3
|
import { encodeToWire, decodeWire } from '../../codecs/runtime/prefix-codec.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { isProduct, isVariant } from '../../Value.mjs';
|
|
5
5
|
|
|
6
6
|
const value = parseValue('{a:{b:x,c:{}}}');
|
|
7
7
|
const repeatedClosedValue = parseValue('{a:{m:{},n:{}},b:{m:{},n:{}}}');
|
|
@@ -27,9 +27,9 @@ assert.deepEqual(explicitProductWire.pattern, [
|
|
|
27
27
|
]);
|
|
28
28
|
|
|
29
29
|
const decoded = explicitProductWire.value;
|
|
30
|
-
assert(decoded
|
|
31
|
-
assert(decoded.product.a
|
|
32
|
-
assert(decoded.product.a.product.b
|
|
30
|
+
assert(isProduct(decoded));
|
|
31
|
+
assert(isProduct(decoded.product.a));
|
|
32
|
+
assert(isVariant(decoded.product.a.product.b));
|
|
33
33
|
assert.equal(decoded.product.a.product.b.tag, "x");
|
|
34
34
|
|
|
35
35
|
const repeatedClosedWire = decodeWire(encodeToWire(repeatedClosedValue, null));
|