@fraczak/k 6.2.0 → 6.2.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/DOCS/KIR_SPECIALIZATION_DESIGN.md +11 -2
- package/DOCS/KVM_EXECUTION_MODEL.md +413 -0
- package/Examples/arithmetics.k +452 -0
- package/Examples/bnat.k +291 -0
- package/Examples/byte.k +67 -0
- package/Examples/code.k +44 -0
- package/Examples/esc.k +9 -0
- package/Examples/ieee.k +15766 -0
- package/Examples/list.k +7 -0
- package/Examples/nat.k +12 -0
- package/Examples/vhdl_to_k.k +50 -0
- package/README.md +8 -0
- package/backend-api.mjs +12 -0
- package/codecs/runtime/codec.mjs +136 -72
- package/codecs/runtime/prefix-codec.mjs +98 -64
- package/kvm.mjs +523 -0
- package/package.json +3 -2
- package/run.mjs +1 -1
- package/scripts/run-tests.mjs +2 -0
- package/tests/README.md +35 -0
- package/tests/code-derivation/codec-witness-pattern.mjs +1 -1
- package/tests/integration.sh +8 -5
- package/tests/test-deep-wire-value.mjs +29 -0
- package/tests/test-kvm.mjs +163 -0
package/Examples/list.k
ADDED
package/Examples/nat.k
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
$ nat = < {} 0, nat +1 >;
|
|
2
|
+
0 = {} | 0 ;
|
|
3
|
+
0? = $ nat { / 0 if, () then} .then;
|
|
4
|
+
succ = | +1 $ nat ;
|
|
5
|
+
pred = $nat / +1 ;
|
|
6
|
+
add = ?{ $nat x, ... } = X <{.x pred x, .y succ y} ?X add, .y> ;
|
|
7
|
+
mult = <
|
|
8
|
+
{{.x pred x, .y y} mult x, .y y} add,
|
|
9
|
+
0
|
|
10
|
+
> ;
|
|
11
|
+
|
|
12
|
+
succ {()x,()y} add
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- VHDL to K Translation Patterns
|
|
2
|
+
|
|
3
|
+
-- VHDL: signal a : std_logic;
|
|
4
|
+
-- K: $ bit = <{} 0, {} 1>;
|
|
5
|
+
|
|
6
|
+
-- VHDL: signal bus : std_logic_vector(7 downto 0);
|
|
7
|
+
-- K: $ byte = {bit 0, bit 1, bit 2, bit 3, bit 4, bit 5, bit 6, bit 7};
|
|
8
|
+
|
|
9
|
+
-- VHDL: entity adder is port(a,b: in std_logic; sum: out std_logic); end;
|
|
10
|
+
-- K: adder = ${bit a, bit b} {.a a_out, .b b_out, xor_gate sum_out};
|
|
11
|
+
|
|
12
|
+
-- VHDL: process(clk) begin if rising_edge(clk) then q <= d; end if; end;
|
|
13
|
+
-- K: dff = $bit .; -- Identity (register holds value)
|
|
14
|
+
|
|
15
|
+
-- VHDL: y <= a and b;
|
|
16
|
+
-- K: and_gate = ${bit a, bit b} <{.a.1 1, .b.1 1, 1}, 0>;
|
|
17
|
+
|
|
18
|
+
-- VHDL: y <= a or b;
|
|
19
|
+
-- K: or_gate = ${bit a, bit b} <{.a.0 0, .b.0 0, 0}, 1>;
|
|
20
|
+
|
|
21
|
+
-- VHDL: y <= not a;
|
|
22
|
+
-- K: not_gate = $bit <{.0 0, 1}, {.1 1, 0}>;
|
|
23
|
+
|
|
24
|
+
-- VHDL: with sel select y <= a when "00", b when "01", c when others;
|
|
25
|
+
-- K: mux = ${bit sel0, bit sel1, bit a, bit b, bit c} <
|
|
26
|
+
-- {.sel0.0 0, .sel1.0 0, .a},
|
|
27
|
+
-- {.sel0.1 1, .sel1.0 0, .b},
|
|
28
|
+
-- .c>;
|
|
29
|
+
|
|
30
|
+
-- VHDL: architecture of 4-bit adder
|
|
31
|
+
four_bit_adder = ${
|
|
32
|
+
{bit 0, bit 1, bit 2, bit 3} a,
|
|
33
|
+
{bit 0, bit 1, bit 2, bit 3} b
|
|
34
|
+
} {
|
|
35
|
+
{.a.0 a0, .b.0 b0} full_adder fa0,
|
|
36
|
+
{.a.1 a1, .b.1 b1, .fa0.carry c_in} full_adder fa1,
|
|
37
|
+
{.a.2 a2, .b.2 b2, .fa1.carry c_in} full_adder fa2,
|
|
38
|
+
{.a.3 a3, .b.3 b3, .fa2.carry c_in} full_adder fa3,
|
|
39
|
+
{.fa0.sum 0, .fa1.sum 1, .fa2.sum 2, .fa3.sum 3} sum,
|
|
40
|
+
.fa3.carry overflow
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
-- VHDL: Full adder component
|
|
44
|
+
full_adder = ${bit a, bit b, bit c_in} {
|
|
45
|
+
{.a a_val, .b b_val} xor_gate ab_xor,
|
|
46
|
+
{.ab_xor xor_val, .c_in c_val} xor_gate sum,
|
|
47
|
+
{.a a_val, .b b_val} and_gate ab_and,
|
|
48
|
+
{.ab_xor xor_val, .c_in c_val} and_gate xor_cin_and,
|
|
49
|
+
{.ab_and and1, .xor_cin_and and2} or_gate carry
|
|
50
|
+
};
|
package/README.md
CHANGED
|
@@ -195,6 +195,14 @@ Installed binary names are `k-` plus the source basename without `.mjs`, except
|
|
|
195
195
|
for `k.mjs` itself. Source names that already include `k-`, such as
|
|
196
196
|
`codecs/k-parse.mjs`, keep that name.
|
|
197
197
|
|
|
198
|
+
## WebAssembly Backend
|
|
199
|
+
|
|
200
|
+
The experimental WebAssembly backend lives in the separate
|
|
201
|
+
[`fraczak/k-wasm`](https://github.com/fraczak/k-wasm) repository. Keeping the
|
|
202
|
+
backend separate avoids adding its WABT dependency to the core runtime. External
|
|
203
|
+
backends integrate with the compiler and binary codecs through
|
|
204
|
+
[`backend-api.mjs`](backend-api.mjs).
|
|
205
|
+
|
|
198
206
|
## Node.js API
|
|
199
207
|
|
|
200
208
|
```js
|
package/backend-api.mjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { annotate } from "./index.mjs";
|
|
2
|
+
export { lowerToKVM, executeKVM } from "./kvm.mjs";
|
|
3
|
+
export { decodeWire, encodeToWire } from "./codecs/runtime/prefix-codec.mjs";
|
|
4
|
+
export { exportPatternGraph, NODE_KIND } from "./codecs/runtime/codec.mjs";
|
|
5
|
+
export { patternToPropertyList, propertyListToPattern } from "./codecs/runtime/pattern-json.mjs";
|
|
6
|
+
export { Product, Variant, fromObject } from "./Value.mjs";
|
|
7
|
+
export { decodeObject, loadLibrary } from "./object.mjs";
|
|
8
|
+
export { createState, evaluateInput } from "./repl.mjs";
|
|
9
|
+
export { parse as parseFloat64, print as printFloat64 } from "./codecs/ieee.mjs";
|
|
10
|
+
export { valueForCode } from "./repl-codecs.mjs";
|
|
11
|
+
export { default as codes } from "./codes.mjs";
|
|
12
|
+
export { default as run, run_converged } from "./run.mjs";
|
package/codecs/runtime/codec.mjs
CHANGED
|
@@ -480,67 +480,89 @@ function refinePatternForValue(pattern, value) {
|
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
function collect(patternNodeId, currentValue) {
|
|
483
|
-
const
|
|
484
|
-
if (!patternNode) {
|
|
485
|
-
throw new Error(`Unknown pattern node ${patternNodeId}`);
|
|
486
|
-
}
|
|
487
|
-
pushOccurrence(patternNodeId, currentValue);
|
|
483
|
+
const stack = [{ patternNodeId, currentValue }];
|
|
488
484
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
485
|
+
while (stack.length > 0) {
|
|
486
|
+
const frame = stack.pop();
|
|
487
|
+
if (frame.extra) {
|
|
488
|
+
pushExtra(frame.patternNodeId, frame.label, frame.currentValue);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
492
491
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
492
|
+
const patternNode = base.nodes[frame.patternNodeId];
|
|
493
|
+
if (!patternNode) {
|
|
494
|
+
throw new Error(`Unknown pattern node ${frame.patternNodeId}`);
|
|
495
|
+
}
|
|
496
|
+
pushOccurrence(frame.patternNodeId, frame.currentValue);
|
|
497
|
+
|
|
498
|
+
switch (patternNode.kind) {
|
|
499
|
+
case NODE_KIND.ANY:
|
|
500
|
+
break;
|
|
501
|
+
|
|
502
|
+
case NODE_KIND.CLOSED_PRODUCT:
|
|
503
|
+
case NODE_KIND.OPEN_PRODUCT: {
|
|
504
|
+
if (!(frame.currentValue instanceof Product)) {
|
|
505
|
+
throw new Error(`Expected Product for pattern node ${frame.patternNodeId}`);
|
|
503
506
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
507
|
+
const actualLabels = Object.keys(frame.currentValue.product).sort();
|
|
508
|
+
const explicit = patternNode.edges.map((edge) => edge.label);
|
|
509
|
+
if (patternNode.kind === NODE_KIND.CLOSED_PRODUCT) {
|
|
510
|
+
if (actualLabels.length !== explicit.length || actualLabels.some((label, idx) => label !== explicit[idx])) {
|
|
511
|
+
throw new Error(`Closed product pattern does not match value fields [${actualLabels.join(", ")}]`);
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
for (const label of explicit) {
|
|
515
|
+
if (!(label in frame.currentValue.product)) {
|
|
516
|
+
throw new Error(`Open product pattern is missing required field '${label}'`);
|
|
517
|
+
}
|
|
508
518
|
}
|
|
509
519
|
}
|
|
510
|
-
}
|
|
511
520
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
521
|
+
const explicitTargets = new Map(patternNode.edges.map((edge) => [edge.label, edge.target]));
|
|
522
|
+
for (let i = actualLabels.length - 1; i >= 0; i--) {
|
|
523
|
+
const label = actualLabels[i];
|
|
524
|
+
if (explicitTargets.has(label)) {
|
|
525
|
+
stack.push({
|
|
526
|
+
patternNodeId: explicitTargets.get(label),
|
|
527
|
+
currentValue: frame.currentValue.product[label]
|
|
528
|
+
});
|
|
529
|
+
} else if (patternNode.kind === NODE_KIND.OPEN_PRODUCT) {
|
|
530
|
+
stack.push({
|
|
531
|
+
extra: true,
|
|
532
|
+
patternNodeId: frame.patternNodeId,
|
|
533
|
+
label,
|
|
534
|
+
currentValue: frame.currentValue.product[label]
|
|
535
|
+
});
|
|
536
|
+
} else {
|
|
537
|
+
throw new Error(`Unexpected extra field '${label}' for closed product pattern`);
|
|
538
|
+
}
|
|
520
539
|
}
|
|
540
|
+
break;
|
|
521
541
|
}
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
542
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
543
|
+
case NODE_KIND.CLOSED_UNION:
|
|
544
|
+
case NODE_KIND.OPEN_UNION: {
|
|
545
|
+
if (!(frame.currentValue instanceof Variant)) {
|
|
546
|
+
throw new Error(`Expected Variant for pattern node ${frame.patternNodeId}`);
|
|
547
|
+
}
|
|
548
|
+
const explicitTargets = new Map(patternNode.edges.map((edge) => [edge.label, edge.target]));
|
|
549
|
+
if (explicitTargets.has(frame.currentValue.tag)) {
|
|
550
|
+
stack.push({
|
|
551
|
+
patternNodeId: explicitTargets.get(frame.currentValue.tag),
|
|
552
|
+
currentValue: frame.currentValue.value
|
|
553
|
+
});
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
if (patternNode.kind === NODE_KIND.OPEN_UNION) {
|
|
557
|
+
pushExtra(frame.patternNodeId, frame.currentValue.tag, frame.currentValue.value);
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
throw new Error(`Unexpected variant tag '${frame.currentValue.tag}' for closed union pattern`);
|
|
538
561
|
}
|
|
539
|
-
throw new Error(`Unexpected variant tag '${currentValue.tag}' for closed union pattern`);
|
|
540
|
-
}
|
|
541
562
|
|
|
542
|
-
|
|
543
|
-
|
|
563
|
+
default:
|
|
564
|
+
throw new Error(`Unknown pattern node kind: ${patternNode.kind}`);
|
|
565
|
+
}
|
|
544
566
|
}
|
|
545
567
|
}
|
|
546
568
|
|
|
@@ -614,6 +636,7 @@ function refinePatternForValue(pattern, value) {
|
|
|
614
636
|
}
|
|
615
637
|
|
|
616
638
|
if (baseNode.kind === NODE_KIND.CLOSED_PRODUCT || baseNode.kind === NODE_KIND.OPEN_PRODUCT) {
|
|
639
|
+
result.kind = NODE_KIND.CLOSED_PRODUCT;
|
|
617
640
|
const values = occurrences.get(patternNodeId) || [];
|
|
618
641
|
const explicit = new Map(baseNode.edges.map((edge) => [edge.label, edge.target]));
|
|
619
642
|
let labels = [...explicit.keys()].sort();
|
|
@@ -713,47 +736,88 @@ function refinePatternForValue(pattern, value) {
|
|
|
713
736
|
|
|
714
737
|
function coerceValueForPattern(pattern, value) {
|
|
715
738
|
const base = normalizePattern(pattern);
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
739
|
+
let result;
|
|
740
|
+
const stack = [{
|
|
741
|
+
patternNodeId: 0,
|
|
742
|
+
currentValue: value,
|
|
743
|
+
assign(coercedValue) {
|
|
744
|
+
result = coercedValue;
|
|
745
|
+
}
|
|
746
|
+
}];
|
|
747
|
+
|
|
748
|
+
while (stack.length > 0) {
|
|
749
|
+
const frame = stack.pop();
|
|
750
|
+
if (frame.passthrough) {
|
|
751
|
+
frame.assign();
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
const patternNode = base.nodes[frame.patternNodeId];
|
|
719
755
|
if (!patternNode) {
|
|
720
|
-
throw new Error(`Unknown pattern node ${patternNodeId}`);
|
|
756
|
+
throw new Error(`Unknown pattern node ${frame.patternNodeId}`);
|
|
721
757
|
}
|
|
722
758
|
|
|
723
759
|
switch (patternNode.kind) {
|
|
724
760
|
case NODE_KIND.ANY:
|
|
725
|
-
|
|
761
|
+
frame.assign(frame.currentValue);
|
|
762
|
+
break;
|
|
726
763
|
|
|
727
764
|
case NODE_KIND.CLOSED_PRODUCT:
|
|
728
765
|
case NODE_KIND.OPEN_PRODUCT: {
|
|
729
|
-
let productValue = currentValue;
|
|
730
|
-
if (currentValue instanceof Variant) {
|
|
731
|
-
productValue = new Product({ [currentValue.tag]: currentValue.value });
|
|
766
|
+
let productValue = frame.currentValue;
|
|
767
|
+
if (frame.currentValue instanceof Variant) {
|
|
768
|
+
productValue = new Product({ [frame.currentValue.tag]: frame.currentValue.value });
|
|
732
769
|
}
|
|
733
770
|
if (!(productValue instanceof Product)) {
|
|
734
|
-
throw new Error(`Expected Product for pattern node ${patternNodeId}`);
|
|
771
|
+
throw new Error(`Expected Product for pattern node ${frame.patternNodeId}`);
|
|
735
772
|
}
|
|
736
773
|
|
|
737
774
|
const explicitTargets = new Map(patternNode.edges.map((edge) => [edge.label, edge.target]));
|
|
738
775
|
const product = {};
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
776
|
+
frame.assign(new Product(product));
|
|
777
|
+
const entries = Object.entries(productValue.product);
|
|
778
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
779
|
+
const [label, childValue] = entries[i];
|
|
780
|
+
const target = explicitTargets.get(label);
|
|
781
|
+
if (target == null) {
|
|
782
|
+
stack.push({
|
|
783
|
+
assign() {
|
|
784
|
+
product[label] = childValue;
|
|
785
|
+
},
|
|
786
|
+
passthrough: true
|
|
787
|
+
});
|
|
788
|
+
} else {
|
|
789
|
+
stack.push({
|
|
790
|
+
patternNodeId: target,
|
|
791
|
+
currentValue: childValue,
|
|
792
|
+
assign(coercedValue) {
|
|
793
|
+
product[label] = coercedValue;
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
}
|
|
743
797
|
}
|
|
744
|
-
|
|
798
|
+
break;
|
|
745
799
|
}
|
|
746
800
|
|
|
747
801
|
case NODE_KIND.CLOSED_UNION:
|
|
748
802
|
case NODE_KIND.OPEN_UNION: {
|
|
749
|
-
if (!(currentValue instanceof Variant)) {
|
|
750
|
-
throw new Error(`Expected Variant for pattern node ${patternNodeId}`);
|
|
803
|
+
if (!(frame.currentValue instanceof Variant)) {
|
|
804
|
+
throw new Error(`Expected Variant for pattern node ${frame.patternNodeId}`);
|
|
805
|
+
}
|
|
806
|
+
const target = patternNode.edges.find((edge) => edge.label === frame.currentValue.tag)?.target;
|
|
807
|
+
const variant = new Variant(frame.currentValue.tag, undefined);
|
|
808
|
+
frame.assign(variant);
|
|
809
|
+
if (target == null) {
|
|
810
|
+
variant.value = frame.currentValue.value;
|
|
811
|
+
} else {
|
|
812
|
+
stack.push({
|
|
813
|
+
patternNodeId: target,
|
|
814
|
+
currentValue: frame.currentValue.value,
|
|
815
|
+
assign(coercedValue) {
|
|
816
|
+
variant.value = coercedValue;
|
|
817
|
+
}
|
|
818
|
+
});
|
|
751
819
|
}
|
|
752
|
-
|
|
753
|
-
return new Variant(
|
|
754
|
-
currentValue.tag,
|
|
755
|
-
target == null ? currentValue.value : coerce(target, currentValue.value)
|
|
756
|
-
);
|
|
820
|
+
break;
|
|
757
821
|
}
|
|
758
822
|
|
|
759
823
|
default:
|
|
@@ -761,7 +825,7 @@ function coerceValueForPattern(pattern, value) {
|
|
|
761
825
|
}
|
|
762
826
|
}
|
|
763
827
|
|
|
764
|
-
return
|
|
828
|
+
return result;
|
|
765
829
|
}
|
|
766
830
|
|
|
767
831
|
export { deriveClosedPattern, exportPatternGraph, refinePatternForValue, coerceValueForPattern, canonicalizePattern, intersectPropertyListPatterns, NODE_KIND };
|
|
@@ -120,87 +120,121 @@ function choiceWidth(cardinality) {
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
function encodeNode(writer, value, pattern, patternNodeId) {
|
|
123
|
-
const
|
|
124
|
-
if (!patternNode) {
|
|
125
|
-
throw new Error(`Unknown pattern node ${patternNodeId}`);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
switch (patternNode.kind) {
|
|
129
|
-
case NODE_KIND.ANY:
|
|
130
|
-
throw new Error("Pattern node 'any' is not directly encodable");
|
|
123
|
+
const stack = [{ value, patternNodeId }];
|
|
131
124
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
const actual = Object.keys(value.product).sort((a, b) => Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8")));
|
|
138
|
-
const expected = patternNode.edges.map((edge) => edge.label);
|
|
139
|
-
if (actual.length !== expected.length || actual.some((label, i) => label !== expected[i])) {
|
|
140
|
-
throw new Error(`Product fields do not match pattern node ${patternNodeId}`);
|
|
141
|
-
}
|
|
142
|
-
for (const edge of patternNode.edges) {
|
|
143
|
-
encodeNode(writer, value.product[edge.label], pattern, edge.target);
|
|
144
|
-
}
|
|
145
|
-
return;
|
|
125
|
+
while (stack.length > 0) {
|
|
126
|
+
const frame = stack.pop();
|
|
127
|
+
const patternNode = pattern.nodes[frame.patternNodeId];
|
|
128
|
+
if (!patternNode) {
|
|
129
|
+
throw new Error(`Unknown pattern node ${frame.patternNodeId}`);
|
|
146
130
|
}
|
|
147
131
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
132
|
+
switch (patternNode.kind) {
|
|
133
|
+
case NODE_KIND.ANY:
|
|
134
|
+
throw new Error("Pattern node 'any' is not directly encodable");
|
|
135
|
+
|
|
136
|
+
case NODE_KIND.OPEN_PRODUCT:
|
|
137
|
+
case NODE_KIND.CLOSED_PRODUCT: {
|
|
138
|
+
if (!(frame.value instanceof Product)) {
|
|
139
|
+
throw new Error(`Expected Product for pattern node ${frame.patternNodeId}`);
|
|
140
|
+
}
|
|
141
|
+
const actual = Object.keys(frame.value.product).sort((a, b) => Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8")));
|
|
142
|
+
const expected = patternNode.edges.map((edge) => edge.label);
|
|
143
|
+
if (actual.length !== expected.length || actual.some((label, i) => label !== expected[i])) {
|
|
144
|
+
throw new Error(`Product fields do not match pattern node ${frame.patternNodeId}`);
|
|
145
|
+
}
|
|
146
|
+
for (let i = patternNode.edges.length - 1; i >= 0; i--) {
|
|
147
|
+
const edge = patternNode.edges[i];
|
|
148
|
+
stack.push({ value: frame.value.product[edge.label], patternNodeId: edge.target });
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
152
151
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
|
|
153
|
+
case NODE_KIND.OPEN_UNION:
|
|
154
|
+
case NODE_KIND.CLOSED_UNION: {
|
|
155
|
+
if (!(frame.value instanceof Variant)) {
|
|
156
|
+
throw new Error(`Expected Variant for pattern node ${frame.patternNodeId}`);
|
|
157
|
+
}
|
|
158
|
+
const tagOrdinal = patternNode.edges.findIndex((edge) => edge.label === frame.value.tag);
|
|
159
|
+
if (tagOrdinal === -1) {
|
|
160
|
+
throw new Error(`Variant tag '${frame.value.tag}' is not present in pattern node ${frame.patternNodeId}`);
|
|
161
|
+
}
|
|
162
|
+
const width = choiceWidth(patternNode.edges.length);
|
|
163
|
+
writer.writeBits(tagOrdinal, width);
|
|
164
|
+
stack.push({ value: frame.value.value, patternNodeId: patternNode.edges[tagOrdinal].target });
|
|
165
|
+
break;
|
|
156
166
|
}
|
|
157
|
-
const width = choiceWidth(patternNode.edges.length);
|
|
158
|
-
writer.writeBits(tagOrdinal, width);
|
|
159
|
-
encodeNode(writer, value.value, pattern, patternNode.edges[tagOrdinal].target);
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
167
|
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
default:
|
|
169
|
+
throw new Error(`Unsupported pattern node kind ${patternNode.kind}`);
|
|
170
|
+
}
|
|
165
171
|
}
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
function decodeNode(reader, pattern, patternNodeId) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
throw new Error("Pattern node 'any' is not directly decodable");
|
|
175
|
+
let result;
|
|
176
|
+
const stack = [{
|
|
177
|
+
patternNodeId,
|
|
178
|
+
assign(value) {
|
|
179
|
+
result = value;
|
|
180
|
+
}
|
|
181
|
+
}];
|
|
177
182
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
return new Product(product);
|
|
183
|
+
while (stack.length > 0) {
|
|
184
|
+
const frame = stack.pop();
|
|
185
|
+
const patternNode = pattern.nodes[frame.patternNodeId];
|
|
186
|
+
if (!patternNode) {
|
|
187
|
+
throw new Error(`Unknown pattern node ${frame.patternNodeId}`);
|
|
185
188
|
}
|
|
186
189
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
190
|
+
switch (patternNode.kind) {
|
|
191
|
+
case NODE_KIND.ANY:
|
|
192
|
+
throw new Error("Pattern node 'any' is not directly decodable");
|
|
193
|
+
|
|
194
|
+
case NODE_KIND.OPEN_PRODUCT:
|
|
195
|
+
case NODE_KIND.CLOSED_PRODUCT: {
|
|
196
|
+
const product = {};
|
|
197
|
+
frame.assign(new Product(product));
|
|
198
|
+
for (let i = patternNode.edges.length - 1; i >= 0; i--) {
|
|
199
|
+
const edge = patternNode.edges[i];
|
|
200
|
+
stack.push({
|
|
201
|
+
patternNodeId: edge.target,
|
|
202
|
+
assign(value) {
|
|
203
|
+
product[edge.label] = value;
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
break;
|
|
191
208
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
|
|
210
|
+
case NODE_KIND.OPEN_UNION:
|
|
211
|
+
case NODE_KIND.CLOSED_UNION: {
|
|
212
|
+
if (patternNode.edges.length === 0) {
|
|
213
|
+
throw new Error(`Union pattern node ${frame.patternNodeId} has no tags`);
|
|
214
|
+
}
|
|
215
|
+
const width = choiceWidth(patternNode.edges.length);
|
|
216
|
+
const tagOrdinal = reader.readBits(width);
|
|
217
|
+
if (tagOrdinal >= patternNode.edges.length) {
|
|
218
|
+
throw new Error(`Choice ${tagOrdinal} is out of range for pattern node ${frame.patternNodeId}`);
|
|
219
|
+
}
|
|
220
|
+
const edge = patternNode.edges[tagOrdinal];
|
|
221
|
+
const variant = new Variant(edge.label, undefined);
|
|
222
|
+
frame.assign(variant);
|
|
223
|
+
stack.push({
|
|
224
|
+
patternNodeId: edge.target,
|
|
225
|
+
assign(value) {
|
|
226
|
+
variant.value = value;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
break;
|
|
196
230
|
}
|
|
197
|
-
const edge = patternNode.edges[tagOrdinal];
|
|
198
|
-
return new Variant(edge.label, decodeNode(reader, pattern, edge.target));
|
|
199
|
-
}
|
|
200
231
|
|
|
201
|
-
|
|
202
|
-
|
|
232
|
+
default:
|
|
233
|
+
throw new Error(`Unsupported pattern node kind ${patternNode.kind}`);
|
|
234
|
+
}
|
|
203
235
|
}
|
|
236
|
+
|
|
237
|
+
return result;
|
|
204
238
|
}
|
|
205
239
|
|
|
206
240
|
function preparePatternAndValue(value, propertyList) {
|