@fraczak/k 6.2.1 → 6.2.3
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 +11 -0
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +6 -3
- package/README.md +5 -4
- package/a.klib +1 -0
- package/codecs/runtime/codec.mjs +135 -72
- package/codecs/runtime/prefix-codec.mjs +98 -64
- package/k.mjs +51 -15
- package/objects/README.md +7 -17
- package/objects/compile.mjs +213 -24
- package/package.json +1 -2
- package/scripts/benchmark-idempotency.mjs +3 -3
- package/scripts/run-tests.mjs +1 -0
- package/tests/integration.sh +18 -1
- package/tests/test-deep-wire-value.mjs +29 -0
- package/objects/compile-lib.mjs +0 -67
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
|
|
|
@@ -714,47 +736,88 @@ function refinePatternForValue(pattern, value) {
|
|
|
714
736
|
|
|
715
737
|
function coerceValueForPattern(pattern, value) {
|
|
716
738
|
const base = normalizePattern(pattern);
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
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];
|
|
720
755
|
if (!patternNode) {
|
|
721
|
-
throw new Error(`Unknown pattern node ${patternNodeId}`);
|
|
756
|
+
throw new Error(`Unknown pattern node ${frame.patternNodeId}`);
|
|
722
757
|
}
|
|
723
758
|
|
|
724
759
|
switch (patternNode.kind) {
|
|
725
760
|
case NODE_KIND.ANY:
|
|
726
|
-
|
|
761
|
+
frame.assign(frame.currentValue);
|
|
762
|
+
break;
|
|
727
763
|
|
|
728
764
|
case NODE_KIND.CLOSED_PRODUCT:
|
|
729
765
|
case NODE_KIND.OPEN_PRODUCT: {
|
|
730
|
-
let productValue = currentValue;
|
|
731
|
-
if (currentValue instanceof Variant) {
|
|
732
|
-
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 });
|
|
733
769
|
}
|
|
734
770
|
if (!(productValue instanceof Product)) {
|
|
735
|
-
throw new Error(`Expected Product for pattern node ${patternNodeId}`);
|
|
771
|
+
throw new Error(`Expected Product for pattern node ${frame.patternNodeId}`);
|
|
736
772
|
}
|
|
737
773
|
|
|
738
774
|
const explicitTargets = new Map(patternNode.edges.map((edge) => [edge.label, edge.target]));
|
|
739
775
|
const product = {};
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
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
|
+
}
|
|
744
797
|
}
|
|
745
|
-
|
|
798
|
+
break;
|
|
746
799
|
}
|
|
747
800
|
|
|
748
801
|
case NODE_KIND.CLOSED_UNION:
|
|
749
802
|
case NODE_KIND.OPEN_UNION: {
|
|
750
|
-
if (!(currentValue instanceof Variant)) {
|
|
751
|
-
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
|
+
});
|
|
752
819
|
}
|
|
753
|
-
|
|
754
|
-
return new Variant(
|
|
755
|
-
currentValue.tag,
|
|
756
|
-
target == null ? currentValue.value : coerce(target, currentValue.value)
|
|
757
|
-
);
|
|
820
|
+
break;
|
|
758
821
|
}
|
|
759
822
|
|
|
760
823
|
default:
|
|
@@ -762,7 +825,7 @@ function coerceValueForPattern(pattern, value) {
|
|
|
762
825
|
}
|
|
763
826
|
}
|
|
764
827
|
|
|
765
|
-
return
|
|
828
|
+
return result;
|
|
766
829
|
}
|
|
767
830
|
|
|
768
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) {
|
package/k.mjs
CHANGED
|
@@ -12,19 +12,20 @@ const prog = argv[1];
|
|
|
12
12
|
let kScript, inputStream;
|
|
13
13
|
|
|
14
14
|
function usage() {
|
|
15
|
-
console.error(`Usage: ${prog} [ --lib lib-file ]... ( k-expr | -k file ) [ input-file ]`);
|
|
15
|
+
console.error(`Usage: ${prog} [ --lib lib-file ]... [ --export spec ]... ( k-expr | -k 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
|
|
19
|
-
console.error("
|
|
18
|
+
console.error(" --lib file Load a .klib dependency before compiling. May be repeated.");
|
|
19
|
+
console.error(" --export spec Export a library alias into scope. 'name' or 'libname:localname'. May be repeated.");
|
|
20
|
+
console.error(" -h, --help Show this help.");
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
function compileFile(
|
|
23
|
-
const buffer = fs.readFileSync(
|
|
23
|
+
function compileFile(filePath, libraries, preamble) {
|
|
24
|
+
const buffer = fs.readFileSync(filePath);
|
|
24
25
|
try {
|
|
25
26
|
return objectToFunction(decodeObject(buffer));
|
|
26
27
|
} catch {
|
|
27
|
-
return k.compile(buffer.toString("utf8"), { libraries });
|
|
28
|
+
return k.compile(preamble + buffer.toString("utf8"), { libraries });
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -36,13 +37,48 @@ function compileFile(path, libraries) {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
const libraries = [];
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
const exports = [];
|
|
41
|
+
// Parse --lib and --export flags
|
|
42
|
+
while (args.length > 0 && args[0].startsWith("--")) {
|
|
43
|
+
if (args[0] === "--lib") {
|
|
44
|
+
args.shift();
|
|
45
|
+
const libPath = args.shift();
|
|
46
|
+
if (!libPath) throw new Error("--lib requires a file argument");
|
|
47
|
+
const libBuffer = fs.readFileSync(libPath);
|
|
48
|
+
libraries.push(loadLibrary(decodeObject(libBuffer)));
|
|
49
|
+
} else if (args[0] === "--export") {
|
|
50
|
+
args.shift();
|
|
51
|
+
const spec = args.shift();
|
|
52
|
+
if (!spec) throw new Error("--export requires a spec argument");
|
|
53
|
+
exports.push(spec);
|
|
54
|
+
} else {
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildExportPreamble() {
|
|
60
|
+
if (exports.length === 0) return "";
|
|
61
|
+
const aliasMap = {};
|
|
62
|
+
for (const lib of libraries) {
|
|
63
|
+
for (const [name, hash] of Object.entries(lib.relAlias || {})) {
|
|
64
|
+
if (name !== "__main__") aliasMap[name] = hash;
|
|
65
|
+
}
|
|
66
|
+
for (const [hash, entry] of Object.entries(lib.meta || {})) {
|
|
67
|
+
if (entry?.type !== "rel") continue;
|
|
68
|
+
for (const origin of entry?.origins || []) {
|
|
69
|
+
if (origin?.name && origin.name !== "__main__") aliasMap[origin.name] = hash;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const lines = [];
|
|
74
|
+
for (const spec of exports) {
|
|
75
|
+
const [libName, localName] = spec.includes(":") ? spec.split(":", 2) : [spec, spec];
|
|
76
|
+
const hash = aliasMap[libName];
|
|
77
|
+
if (!hash) throw new Error(`--export: '${libName}' not found in loaded libraries`);
|
|
78
|
+
const body = hash.startsWith("@") ? hash.slice(1) : hash;
|
|
79
|
+
lines.push(`${localName} = @${body};`);
|
|
80
|
+
}
|
|
81
|
+
return lines.join("\n") + "\n";
|
|
46
82
|
}
|
|
47
83
|
|
|
48
84
|
let kScriptStr = (function (arg) {
|
|
@@ -50,9 +86,9 @@ function compileFile(path, libraries) {
|
|
|
50
86
|
throw new Error("Missing script argument");
|
|
51
87
|
}
|
|
52
88
|
if (arg === "-k") {
|
|
53
|
-
return compileFile(args.shift(), libraries);
|
|
89
|
+
return compileFile(args.shift(), libraries, buildExportPreamble());
|
|
54
90
|
} else {
|
|
55
|
-
return arg;
|
|
91
|
+
return buildExportPreamble() + arg;
|
|
56
92
|
}
|
|
57
93
|
})(args.shift());
|
|
58
94
|
let kScript = typeof kScriptStr === "function" ? kScriptStr : k.compile(kScriptStr, { libraries });
|
package/objects/README.md
CHANGED
|
@@ -6,27 +6,17 @@ CLI tools for compiling and inspecting k object files (`.ko`) and library files
|
|
|
6
6
|
|
|
7
7
|
### `k-compile`
|
|
8
8
|
|
|
9
|
-
Compiles
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
Compiles `.k` source into an executable object file (`.ko`), library file
|
|
10
|
+
(`.klib`), or kVM JSON (`.kvm`). Output format is inferred from the output
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
The output is plain JSON with no binary header. It contains the library closure
|
|
21
|
-
formed from loaded `--lib` dependencies plus the compiled source definitions,
|
|
22
|
-
with `main: null`.
|
|
23
|
-
Use `--lib` to build on top of existing libraries.
|
|
24
|
-
Each stored relation includes `typeDerivation.status`; libraries may contain a
|
|
25
|
-
mix of converged and non-converged relations.
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
k-compile-lib [--lib lib-file]... [k-file [lib-file]]
|
|
29
|
-
```
|
|
17
|
+
Existing input paths are read as files. A non-existing input with `.k`, `.ko`,
|
|
18
|
+
or `.klib` extension is reported as a missing file; otherwise it is compiled as
|
|
19
|
+
inline k source, in the same style as `k.mjs`.
|
|
30
20
|
|
|
31
21
|
### `k-decompile`
|
|
32
22
|
|