@fraczak/k 7.0.1 → 7.1.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 +78 -0
- package/DOCS/BIG_PICTURE.md +5 -5
- package/DOCS/FILE_FORMATS.md +241 -18
- package/DOCS/OBJECT_FILE_AND_PATTERN.md +9 -9
- package/DOCS/PATTERNS.md +3 -3
- package/DOCS/POLYMORPHIC_IR.md +104 -0
- package/DOCS/PROFILING_AND_TRACING.md +60 -72
- package/DOCS/REPL.md +3 -3
- package/DOCS/book/03-types-as-finite-automata.md +4 -4
- package/DOCS/book/06-values-in-memory.md +9 -9
- package/DOCS/book/08-operational-semantics-and-execution.md +1 -1
- package/DOCS/book/09-compiler-architecture.md +2 -2
- package/DOCS/book/10-from-ast-to-intermediate-representation.md +2 -2
- package/DOCS/book/14-canonical-serialization.md +1 -1
- package/Examples/arithmetics.k +2 -2
- package/Examples/bnat.k +1 -1
- package/Examples/karatsuba-mult.k +2 -2
- package/JOURNAL.md +288 -0
- package/README.md +13 -10
- package/access-table.mjs +343 -0
- package/backend-api.mjs +18 -0
- package/backends/llvm/README.md +20 -21
- package/backends/llvm/bin/k-llvm-build.mjs +105 -32
- package/backends/llvm/bin/k-llvm-compile.mjs +63 -24
- package/backends/llvm/bin/k-llvm.mjs +292 -0
- package/backends/llvm/runtime/krt.c +41 -0
- package/backends/llvm/runtime/krt.h +2 -0
- package/backends/llvm/src/cli.mjs +95 -17
- package/backends/llvm/src/executable.mjs +132 -15
- package/backends/llvm/src/kvm2llvm.mjs +50 -23
- package/backends/llvm/src/llvm.mjs +133 -20
- package/backends/llvm/tests/integration.sh +11 -11
- package/backends/llvm/{bin → tests}/k-llvm-run.mjs +48 -22
- package/backends/llvm/tests/perf-support.mjs +25 -58
- package/backends/llvm/tests/test-llvm.mjs +25 -4
- package/backends/wasm/bin/k-wasm-compile.mjs +46 -20
- package/backends/wasm/bin/k-wasm-run.mjs +61 -20
- package/backends/wasm/bin/k-wasm.mjs +47 -13
- package/backends/wasm/src/cli.mjs +141 -19
- package/backends/wasm/src/wasm.mjs +132 -24
- package/backends/wasm/tests/test-wasm.mjs +5 -5
- package/browser/repl-browser.mjs +16 -5
- package/browser/vfs-data.mjs +5 -5
- package/codecs/k-parse.mjs +29 -8
- package/codecs/k-print.mjs +22 -6
- package/codecs/runtime/codec-sdk.mjs +8 -4
- package/codecs/show.mjs +20 -6
- package/core.k +8 -8
- package/harness/format.mjs +2 -2
- package/harness/registry.mjs +0 -2
- package/k.mjs +56 -16
- package/kir.mjs +200 -75
- package/kvm.mjs +49 -12
- package/objects/compile.mjs +37 -23
- package/objects/decompile.mjs +40 -10
- package/objects/extract-aliases.mjs +44 -14
- package/objects/inspect.mjs +19 -14
- package/objects/validate.mjs +18 -11
- package/package.json +10 -7
- package/pkir.mjs +772 -0
- package/repl.mjs +36 -18
- package/scripts/benchmark-idempotency.mjs +12 -8
- package/scripts/benchmark.mjs +5 -9
- package/scripts/build-repl-html.mjs +16 -4
- package/scripts/perf-ieee.mjs +253 -103
- package/scripts/perf-int.mjs +585 -297
- package/scripts/perf-multiplication.mjs +66 -25
- package/scripts/perf-poly.mjs +292 -164
- package/scripts/run-tests.mjs +3 -0
- package/tests/code-derivation/pattern-envelope.mjs +4 -4
- package/tests/test-access-table.mjs +213 -0
- package/tests/test-kvm-polymorphic.mjs +2 -2
- package/tests/test-pkir.mjs +530 -0
- package/tests/test-polymorphic-tdd.mjs +196 -0
- package/tests/test-repl-html.mjs +15 -2
- package/tests/test-repl.mjs +30 -1
- package/backends/arm64/README.md +0 -103
- package/backends/arm64/bin/k-arm64-compile.mjs +0 -93
- package/backends/arm64/bin/k-arm64-run.mjs +0 -58
- package/backends/arm64/bin/k-arm64.mjs +0 -88
- package/backends/arm64/scripts/run-tests.mjs +0 -57
- package/backends/arm64/src/arm64.mjs +0 -229
- package/backends/arm64/src/cli.mjs +0 -155
- package/backends/arm64/src/kvm2arm64.mjs +0 -906
- package/backends/arm64/src/runtime.c +0 -744
- package/backends/arm64/tests/integration.sh +0 -112
- package/backends/arm64/tests/test-arm64.mjs +0 -240
- package/backends/llvm/bin/k-llvm-jit.mjs +0 -160
- package/harness/adapters/arm64.mjs +0 -105
package/kir.mjs
CHANGED
|
@@ -337,6 +337,64 @@ function findRelation(rels, relAlias, name) {
|
|
|
337
337
|
return null;
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
function findDots(exp, res = []) {
|
|
341
|
+
if (!exp || typeof exp !== "object") return res;
|
|
342
|
+
if (exp.op === "dot") res.push(exp);
|
|
343
|
+
if (Array.isArray(exp.comp)) exp.comp.forEach((c) => findDots(c, res));
|
|
344
|
+
if (Array.isArray(exp.items)) exp.items.forEach((c) => findDots(c, res));
|
|
345
|
+
if (Array.isArray(exp.union)) exp.union.forEach((c) => findDots(c, res));
|
|
346
|
+
if (Array.isArray(exp.product)) {
|
|
347
|
+
exp.product.forEach((c) => findDots(c?.exp || c?.expr, res));
|
|
348
|
+
}
|
|
349
|
+
if (Array.isArray(exp.fields)) {
|
|
350
|
+
exp.fields.forEach((c) => findDots(c?.expr || c?.exp, res));
|
|
351
|
+
}
|
|
352
|
+
return res;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function getDotIndex(rel, dotExp) {
|
|
356
|
+
const label = dotExp.dot || dotExp.label;
|
|
357
|
+
if (!label) return -1;
|
|
358
|
+
const propList = expPatternPropertyList(rel, dotExp, 0);
|
|
359
|
+
if (Array.isArray(propList?.[0]?.[1])) {
|
|
360
|
+
return propList[0][1].findIndex(([l]) => l === label);
|
|
361
|
+
}
|
|
362
|
+
return -1;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function relationLayoutSignature(rel) {
|
|
366
|
+
const inPropList = relationPatternPropertyList(rel, 0);
|
|
367
|
+
if (!Array.isArray(inPropList)) return "";
|
|
368
|
+
|
|
369
|
+
const dots = findDots(rel.def);
|
|
370
|
+
const dotIndices = dots.map((d) => {
|
|
371
|
+
const lbl = d.dot || d.label || "";
|
|
372
|
+
return `${lbl}:${getDotIndex(rel, d)}`;
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const productShapes = [];
|
|
376
|
+
for (const [kind, edges] of inPropList) {
|
|
377
|
+
if (kind === "open-product" || kind === "closed-product") {
|
|
378
|
+
const fieldNames = edges.map(([l]) => l).sort().join(",");
|
|
379
|
+
if (fieldNames.length > 0) productShapes.push(fieldNames);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return dotIndices.join(",") + "|" + productShapes.sort().join(";");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function productShapeSignature(propList) {
|
|
387
|
+
if (!Array.isArray(propList)) return "";
|
|
388
|
+
const shapes = [];
|
|
389
|
+
for (const [kind, edges] of propList) {
|
|
390
|
+
if (kind === "open-product" || kind === "closed-product") {
|
|
391
|
+
const fieldNames = edges.map(([l]) => l).sort().join(",");
|
|
392
|
+
if (fieldNames.length > 0) shapes.push(fieldNames);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return shapes.sort().join(";");
|
|
396
|
+
}
|
|
397
|
+
|
|
340
398
|
export function specializeObjectRelations(object, entryRelationName = null, inputPattern = null) {
|
|
341
399
|
if (object?.format !== "k-object") {
|
|
342
400
|
throw new Error("specializeObjectRelations requires a k object");
|
|
@@ -362,77 +420,105 @@ export function specializeObjectRelations(object, entryRelationName = null, inpu
|
|
|
362
420
|
const specializedRels = new Map();
|
|
363
421
|
const specializedCounts = new Map();
|
|
364
422
|
const newRels = {};
|
|
423
|
+
const inProgress = new Map();
|
|
424
|
+
let entryCandidate = null;
|
|
425
|
+
|
|
426
|
+
function specializeRelation(relName, argPropList) {
|
|
427
|
+
const target = findRelation(baseRels, object.relAlias, relName);
|
|
428
|
+
if (!target) return relName;
|
|
365
429
|
|
|
366
|
-
|
|
367
|
-
|
|
430
|
+
const baseInSig = relationLayoutSignature(target.rel);
|
|
431
|
+
const shapeSig = productShapeSignature(argPropList);
|
|
432
|
+
const specKey = target.name + "::" + shapeSig;
|
|
368
433
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
// Best-effort entry unification
|
|
434
|
+
if (specializedRels.has(specKey)) {
|
|
435
|
+
return specializedRels.get(specKey);
|
|
436
|
+
}
|
|
437
|
+
if (inProgress.has(specKey)) {
|
|
438
|
+
return inProgress.get(specKey);
|
|
375
439
|
}
|
|
376
|
-
specializedRels.set(entry.name + "::" + JSON.stringify(inputPattern), entry.name);
|
|
377
|
-
}
|
|
378
440
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
const currentRel = newRels[currentName];
|
|
383
|
-
if (!currentRel) continue;
|
|
441
|
+
const count = (specializedCounts.get(target.name) || 0) + 1;
|
|
442
|
+
const specName = `${target.name}$${count}`;
|
|
443
|
+
inProgress.set(specKey, specName);
|
|
384
444
|
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
445
|
+
const candidateRel = cloneRelation(target.rel);
|
|
446
|
+
if (!candidateRel.typeDerivation) candidateRel.typeDerivation = {};
|
|
447
|
+
if (!candidateRel.typeDerivation.status) {
|
|
448
|
+
candidateRel.typeDerivation.status = target.rel?.typeDerivation?.status || "converged";
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (Array.isArray(argPropList) && argPropList.length > 0) {
|
|
452
|
+
const tInRoot = addPropertyListToPatternGraph(candidateRel.typePatternGraph, argPropList);
|
|
453
|
+
try {
|
|
454
|
+
candidateRel.typePatternGraph.unify("specialize", candidateRel.def.patterns[0], tInRoot);
|
|
455
|
+
} catch {
|
|
456
|
+
// Unification failure: keep target unspecialized
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (relName === entry.name && (!entryCandidate || Array.isArray(argPropList))) {
|
|
461
|
+
entryCandidate = candidateRel;
|
|
462
|
+
}
|
|
389
463
|
|
|
464
|
+
let anyCallChanged = false;
|
|
465
|
+
const calls = findCallSites(candidateRel.def);
|
|
466
|
+
for (const call of calls) {
|
|
390
467
|
const argPatternId = call.patterns?.[0];
|
|
391
468
|
if (argPatternId != null) {
|
|
392
|
-
const argRoot =
|
|
393
|
-
const
|
|
394
|
-
exportPatternGraph(
|
|
469
|
+
const argRoot = candidateRel.typePatternGraph.find(argPatternId);
|
|
470
|
+
const childArgPropList = patternToPropertyList(
|
|
471
|
+
exportPatternGraph(candidateRel.typePatternGraph, argRoot)
|
|
395
472
|
);
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
if (!specName) {
|
|
401
|
-
const count = (specializedCounts.get(target.name) || 0) + 1;
|
|
402
|
-
specializedCounts.set(target.name, count);
|
|
403
|
-
specName = count === 1 ? target.name : `${target.name}$${count}`;
|
|
404
|
-
specializedRels.set(specKey, specName);
|
|
405
|
-
|
|
406
|
-
const targetRel = cloneRelation(target.rel);
|
|
407
|
-
const tInRoot = addPropertyListToPatternGraph(targetRel.typePatternGraph, argPropList);
|
|
408
|
-
try {
|
|
409
|
-
targetRel.typePatternGraph.unify("specialize", targetRel.def.patterns[0], tInRoot);
|
|
410
|
-
} catch {
|
|
411
|
-
// Unification failure: keep target unspecialized
|
|
412
|
-
}
|
|
413
|
-
newRels[specName] = targetRel;
|
|
414
|
-
worklist.push(specName);
|
|
473
|
+
const childSpecName = specializeRelation(call.ref, childArgPropList);
|
|
474
|
+
if (childSpecName !== call.ref) {
|
|
475
|
+
call.ref = childSpecName;
|
|
476
|
+
anyCallChanged = true;
|
|
415
477
|
}
|
|
416
478
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
479
|
+
const childRel = newRels[childSpecName] || baseRels[childSpecName];
|
|
480
|
+
if (childRel) {
|
|
481
|
+
const outPatternId = childRel.def.patterns?.[1];
|
|
482
|
+
const retPatternId = call.patterns?.[1];
|
|
483
|
+
if (outPatternId != null && retPatternId != null) {
|
|
484
|
+
const outRoot = childRel.typePatternGraph.find(outPatternId);
|
|
485
|
+
const retPropList = patternToPropertyList(
|
|
486
|
+
exportPatternGraph(childRel.typePatternGraph, outRoot)
|
|
487
|
+
);
|
|
488
|
+
const rRetRoot = addPropertyListToPatternGraph(candidateRel.typePatternGraph, retPropList);
|
|
489
|
+
try {
|
|
490
|
+
candidateRel.typePatternGraph.unify("specialize", retPatternId, rRetRoot);
|
|
491
|
+
} catch {
|
|
492
|
+
// Keep existing call return pattern on unification error
|
|
493
|
+
}
|
|
432
494
|
}
|
|
433
495
|
}
|
|
434
496
|
}
|
|
435
497
|
}
|
|
498
|
+
|
|
499
|
+
inProgress.delete(specKey);
|
|
500
|
+
|
|
501
|
+
const newInSig = relationLayoutSignature(candidateRel);
|
|
502
|
+
const isDifferent = (newInSig !== baseInSig) || anyCallChanged;
|
|
503
|
+
|
|
504
|
+
if (!isDifferent) {
|
|
505
|
+
specializedRels.set(specKey, target.name);
|
|
506
|
+
return target.name;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
specializedCounts.set(target.name, count);
|
|
510
|
+
specializedRels.set(specKey, specName);
|
|
511
|
+
newRels[specName] = candidateRel;
|
|
512
|
+
return specName;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const entrySpecName = specializeRelation(entry.name, inputPattern);
|
|
516
|
+
if (entrySpecName !== entry.name && newRels[entrySpecName]) {
|
|
517
|
+
newRels[entry.name] = newRels[entrySpecName];
|
|
518
|
+
} else if (entryCandidate) {
|
|
519
|
+
newRels[entry.name] = entryCandidate;
|
|
520
|
+
} else if (!newRels[entry.name]) {
|
|
521
|
+
newRels[entry.name] = baseRels[entry.name];
|
|
436
522
|
}
|
|
437
523
|
|
|
438
524
|
const resultRels = {
|
|
@@ -515,20 +601,27 @@ export default {
|
|
|
515
601
|
};
|
|
516
602
|
|
|
517
603
|
function helpText() {
|
|
604
|
+
const prog = argv[1] || "kir.mjs";
|
|
518
605
|
return [
|
|
519
606
|
"Export the KIR-P JSON view from a k .ko or .klib object.",
|
|
520
607
|
"",
|
|
521
|
-
`Usage: ${
|
|
608
|
+
`Usage: ${prog} [options] [object-file]`,
|
|
522
609
|
"",
|
|
523
610
|
"Arguments:",
|
|
524
|
-
" object-file
|
|
611
|
+
" object-file Input .ko or .klib file (default: stdin).",
|
|
525
612
|
"",
|
|
526
613
|
"Options:",
|
|
614
|
+
" -o, --output path Specify output file path explicitly.",
|
|
527
615
|
" --retype rel Export retyped KIR-P for relation rel.",
|
|
528
|
-
" --input-pattern json Input pattern
|
|
529
|
-
" -h, --help
|
|
616
|
+
" --input-pattern json Input pattern JSON or file for spec.",
|
|
617
|
+
" -h, --help Show this help.",
|
|
618
|
+
"",
|
|
619
|
+
"KIR-P is an inspection/export view; retyping also emits KIR-P.",
|
|
530
620
|
"",
|
|
531
|
-
"
|
|
621
|
+
"Examples:",
|
|
622
|
+
` ${prog} program.ko`,
|
|
623
|
+
` ${prog} program.ko -o program.kir.json`,
|
|
624
|
+
` ${prog} --retype main --input-pattern pattern.json program.ko`
|
|
532
625
|
].join("\n");
|
|
533
626
|
}
|
|
534
627
|
|
|
@@ -557,32 +650,64 @@ async function main() {
|
|
|
557
650
|
|
|
558
651
|
let retypeRelation = null;
|
|
559
652
|
let inputPatternArg = null;
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
653
|
+
let explicitOutput = null;
|
|
654
|
+
const remainingArgs = [];
|
|
655
|
+
while (args.length > 0) {
|
|
656
|
+
const arg = args[0];
|
|
657
|
+
if (arg === "-o" || arg === "--output") {
|
|
658
|
+
args.shift();
|
|
659
|
+
explicitOutput = args.shift();
|
|
660
|
+
if (!explicitOutput) {
|
|
661
|
+
throw new Error("-o/--output requires a file argument");
|
|
662
|
+
}
|
|
663
|
+
} else if (arg === "--retype") {
|
|
664
|
+
args.shift();
|
|
563
665
|
retypeRelation = args.shift();
|
|
564
666
|
if (!retypeRelation) throw new Error("--retype requires a relation name");
|
|
565
|
-
} else if (
|
|
667
|
+
} else if (arg === "--input-pattern") {
|
|
668
|
+
args.shift();
|
|
566
669
|
inputPatternArg = args.shift();
|
|
567
|
-
if (!inputPatternArg)
|
|
670
|
+
if (!inputPatternArg) {
|
|
671
|
+
throw new Error("--input-pattern requires JSON or a file path");
|
|
672
|
+
}
|
|
673
|
+
} else if (arg.startsWith("-")) {
|
|
674
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
568
675
|
} else {
|
|
569
|
-
|
|
676
|
+
remainingArgs.push(args.shift());
|
|
570
677
|
}
|
|
571
678
|
}
|
|
572
679
|
|
|
573
|
-
const inputPath =
|
|
574
|
-
if (
|
|
575
|
-
throw new Error(`Unexpected argument: ${
|
|
680
|
+
const inputPath = remainingArgs.shift() || null;
|
|
681
|
+
if (remainingArgs.length > 0) {
|
|
682
|
+
throw new Error(`Unexpected argument: ${remainingArgs[0]}`);
|
|
576
683
|
}
|
|
577
684
|
|
|
578
|
-
const buffer = inputPath == null
|
|
685
|
+
const buffer = inputPath == null
|
|
686
|
+
? await readStdinBytes()
|
|
687
|
+
: fs.readFileSync(inputPath);
|
|
579
688
|
const object = decodeObject(buffer);
|
|
689
|
+
let outputData;
|
|
580
690
|
if (retypeRelation != null) {
|
|
581
|
-
if (inputPatternArg == null)
|
|
582
|
-
|
|
583
|
-
|
|
691
|
+
if (inputPatternArg == null) {
|
|
692
|
+
throw new Error("--retype requires --input-pattern");
|
|
693
|
+
}
|
|
694
|
+
const inputPatternText = fs.existsSync(inputPatternArg)
|
|
695
|
+
? fs.readFileSync(inputPatternArg, "utf8")
|
|
696
|
+
: inputPatternArg;
|
|
697
|
+
outputData = retypeObjectRelation(
|
|
698
|
+
object,
|
|
699
|
+
retypeRelation,
|
|
700
|
+
JSON.parse(inputPatternText)
|
|
701
|
+
);
|
|
702
|
+
} else {
|
|
703
|
+
outputData = objectToKIRP(object);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const json = JSON.stringify(outputData, null, 2) + "\n";
|
|
707
|
+
if (explicitOutput) {
|
|
708
|
+
fs.writeFileSync(explicitOutput, json, "utf8");
|
|
584
709
|
} else {
|
|
585
|
-
stdout.write(
|
|
710
|
+
stdout.write(json);
|
|
586
711
|
}
|
|
587
712
|
}
|
|
588
713
|
|
package/kvm.mjs
CHANGED
|
@@ -9,7 +9,12 @@ import {
|
|
|
9
9
|
} from "./run.mjs";
|
|
10
10
|
import { decodeWire, encodeToWire } from "./codecs/runtime/prefix-codec.mjs";
|
|
11
11
|
import { intersectPropertyListPatterns } from "./codecs/runtime/codec.mjs";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
compileLibrary,
|
|
14
|
+
compileObjectBuffer,
|
|
15
|
+
decodeObject,
|
|
16
|
+
loadLibrary
|
|
17
|
+
} from "./object.mjs";
|
|
13
18
|
import codes from "./codes.mjs";
|
|
14
19
|
import { isMainEntrypoint } from "./codecs/runtime/cli-entry.mjs";
|
|
15
20
|
import { isIntrinsic, unsupportedIntrinsic } from "./intrinsics.mjs";
|
|
@@ -964,20 +969,43 @@ export default {
|
|
|
964
969
|
specializeKVM
|
|
965
970
|
};
|
|
966
971
|
|
|
967
|
-
function
|
|
972
|
+
function helpText() {
|
|
968
973
|
const prog = argv[1] || "kvm.mjs";
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
974
|
+
return [
|
|
975
|
+
"Execute a k expression or file via the kVM bytecode interpreter",
|
|
976
|
+
"over a binary pattern+value wire stream.",
|
|
977
|
+
"",
|
|
978
|
+
`Usage: ${prog} [options]`,
|
|
979
|
+
" ( source-snippet | input-file ) [ input-wire-file ]",
|
|
980
|
+
"",
|
|
981
|
+
"Arguments:",
|
|
982
|
+
" source-snippet Inline k source snippet.",
|
|
983
|
+
" input-file Source .k, .ko, or .klib file.",
|
|
984
|
+
" input-wire-file Optional binary wire input (default: stdin).",
|
|
985
|
+
"",
|
|
986
|
+
"Options:",
|
|
987
|
+
" --lib file Load one .klib or .k library before compiling.",
|
|
988
|
+
" --export spec Export library alias into scope. Repeatable.",
|
|
989
|
+
" spec is 'name' or 'libname:localname'.",
|
|
990
|
+
" --envelope-free Run the interpreter in envelope-free mode.",
|
|
991
|
+
" -h, --help Show this help.",
|
|
992
|
+
"",
|
|
993
|
+
"Examples:",
|
|
994
|
+
` echo '"abc"' | k-parse | ${prog} '{.1 0}' | k-print`,
|
|
995
|
+
` echo '{"x":1,"y":2}' | k-json --parse | \\`,
|
|
996
|
+
` ${prog} --lib Examples/poly.k --export concat \\`,
|
|
997
|
+
` 's={()xs,()ys}concat; s' | k-json --print`
|
|
998
|
+
].join("\n");
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
function usage(stream = console.error) {
|
|
1002
|
+
stream(helpText());
|
|
975
1003
|
}
|
|
976
1004
|
|
|
977
1005
|
async function main() {
|
|
978
1006
|
const args = argv.slice(2);
|
|
979
1007
|
if (args.includes("-h") || args.includes("--help")) {
|
|
980
|
-
usage();
|
|
1008
|
+
usage(console.log);
|
|
981
1009
|
return exit(0);
|
|
982
1010
|
}
|
|
983
1011
|
|
|
@@ -994,9 +1022,18 @@ async function main() {
|
|
|
994
1022
|
args.shift();
|
|
995
1023
|
const libPath = args.shift();
|
|
996
1024
|
if (!libPath) throw new Error("--lib requires a file argument");
|
|
997
|
-
if (libraries.length > 0)
|
|
998
|
-
|
|
999
|
-
|
|
1025
|
+
if (libraries.length > 0) {
|
|
1026
|
+
throw new Error("--lib may be specified at most once");
|
|
1027
|
+
}
|
|
1028
|
+
if (libPath.endsWith(".k")) {
|
|
1029
|
+
const raw = fs.readFileSync(libPath, "utf8");
|
|
1030
|
+
libraries.push(
|
|
1031
|
+
loadLibrary(compileLibrary(raw, { source: libPath }))
|
|
1032
|
+
);
|
|
1033
|
+
} else {
|
|
1034
|
+
const libBuffer = fs.readFileSync(libPath);
|
|
1035
|
+
libraries.push(loadLibrary(decodeObject(libBuffer)));
|
|
1036
|
+
}
|
|
1000
1037
|
} else if (args[0] === "--export") {
|
|
1001
1038
|
args.shift();
|
|
1002
1039
|
const spec = args.shift();
|
package/objects/compile.mjs
CHANGED
|
@@ -13,38 +13,43 @@ import {
|
|
|
13
13
|
import { objectToKVMArtifact } from "../kvm.mjs";
|
|
14
14
|
|
|
15
15
|
function helpText() {
|
|
16
|
+
const prog = argv[1] || "compile.mjs";
|
|
16
17
|
return [
|
|
17
|
-
"Compile
|
|
18
|
-
"Output format is inferred from the output
|
|
18
|
+
"Compile k source, .ko, or .klib into .ko, .klib, or .kvm output.",
|
|
19
|
+
"Output format is inferred from the output extension or --format.",
|
|
19
20
|
"",
|
|
20
|
-
`Usage: ${
|
|
21
|
+
`Usage: ${prog} [options]`,
|
|
22
|
+
" [source-snippet | input-file [output-file]]",
|
|
21
23
|
"",
|
|
22
24
|
"Arguments:",
|
|
23
|
-
" source-snippet
|
|
24
|
-
" input-file
|
|
25
|
-
"
|
|
25
|
+
" source-snippet Inline k source, in the same style as k.mjs.",
|
|
26
|
+
" input-file Source .k, .ko, or .klib file.",
|
|
27
|
+
" Reads UTF-8 source from stdin when omitted.",
|
|
28
|
+
" output-file Output path (.ko, .klib, or .kvm).",
|
|
29
|
+
" Writes to stdout when omitted.",
|
|
26
30
|
"",
|
|
27
31
|
"Options:",
|
|
28
|
-
" --
|
|
29
|
-
" --
|
|
30
|
-
"
|
|
31
|
-
" --
|
|
32
|
-
"
|
|
32
|
+
" -o, --output path Specify output file path explicitly.",
|
|
33
|
+
" --format fmt Output format: ko, klib, or kvm.",
|
|
34
|
+
" Overrides extension detection.",
|
|
35
|
+
" --lib file Load one .klib or .k library before compiling.",
|
|
36
|
+
" --export spec Export library alias into scope. Repeatable.",
|
|
37
|
+
" spec is 'name' or 'libname:localname'.",
|
|
38
|
+
" -h, --help Show this help.",
|
|
33
39
|
"",
|
|
34
|
-
"Existing input paths are read as files. A non-existing input with
|
|
35
|
-
"or .klib extension is reported as
|
|
36
|
-
"as inline k source.",
|
|
40
|
+
"Existing input paths are read as files. A non-existing input with",
|
|
41
|
+
".k, .ko, or .klib extension is reported as missing; otherwise it is",
|
|
42
|
+
"compiled as inline k source.",
|
|
37
43
|
"",
|
|
38
44
|
".kvm is a polymorphic kVM template artifact (layer: KVM-P).",
|
|
39
45
|
"A .klib input can only be copied to .klib output.",
|
|
40
46
|
"",
|
|
41
47
|
"Examples:",
|
|
42
|
-
` ${
|
|
43
|
-
` ${
|
|
44
|
-
` ${
|
|
45
|
-
` ${
|
|
46
|
-
` ${
|
|
47
|
-
` ${argv[1]} --lib core.klib --export add --export mul:times program.k program.ko`
|
|
48
|
+
` ${prog} 'x = |x; x x'`,
|
|
49
|
+
` ${prog} program.k -o program.ko`,
|
|
50
|
+
` ${prog} program.k program.klib`,
|
|
51
|
+
` ${prog} program.k program.kvm`,
|
|
52
|
+
` ${prog} --lib core.klib --export add program.k program.ko`
|
|
48
53
|
].join("\n");
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -144,6 +149,7 @@ try {
|
|
|
144
149
|
|
|
145
150
|
const libraries = [];
|
|
146
151
|
let format = null;
|
|
152
|
+
let explicitOutput = null;
|
|
147
153
|
const exports = [];
|
|
148
154
|
|
|
149
155
|
while (args.length > 0) {
|
|
@@ -151,7 +157,13 @@ try {
|
|
|
151
157
|
args.shift();
|
|
152
158
|
break;
|
|
153
159
|
}
|
|
154
|
-
if (args[0] === "--
|
|
160
|
+
if (args[0] === "-o" || args[0] === "--output") {
|
|
161
|
+
args.shift();
|
|
162
|
+
explicitOutput = args.shift();
|
|
163
|
+
if (!explicitOutput) {
|
|
164
|
+
throw new Error("-o/--output requires a file argument");
|
|
165
|
+
}
|
|
166
|
+
} else if (args[0] === "--lib") {
|
|
155
167
|
args.shift();
|
|
156
168
|
const libPath = args.shift();
|
|
157
169
|
if (!libPath) throw new Error("--lib requires a file argument");
|
|
@@ -172,7 +184,7 @@ try {
|
|
|
172
184
|
format = args.shift();
|
|
173
185
|
if (!["ko", "klib", "kvm"].includes(format))
|
|
174
186
|
throw new Error(`Unknown format: ${format}. Use ko, klib, or kvm.`);
|
|
175
|
-
} else if (args[0].startsWith("
|
|
187
|
+
} else if (args[0].startsWith("-")) {
|
|
176
188
|
throw new Error(`Unknown option: ${args[0]}`);
|
|
177
189
|
} else {
|
|
178
190
|
break;
|
|
@@ -180,11 +192,13 @@ try {
|
|
|
180
192
|
}
|
|
181
193
|
|
|
182
194
|
const inputArg = args.shift() || null;
|
|
183
|
-
const
|
|
195
|
+
const positionalOutput = args.shift() || null;
|
|
184
196
|
if (args.length > 0) {
|
|
185
197
|
throw new Error(`Unexpected argument: ${args[0]}`);
|
|
186
198
|
}
|
|
187
199
|
|
|
200
|
+
const outputPath = explicitOutput || positionalOutput || null;
|
|
201
|
+
|
|
188
202
|
const outputFormat = format || inferFormat(outputPath);
|
|
189
203
|
const input = resolveInput(inputArg);
|
|
190
204
|
const inputType = input.type;
|
package/objects/decompile.mjs
CHANGED
|
@@ -5,20 +5,24 @@ import { argv, exit, stdin, stdout } from "node:process";
|
|
|
5
5
|
import { decompileObjectBuffer } from "../object.mjs";
|
|
6
6
|
|
|
7
7
|
function helpText() {
|
|
8
|
+
const prog = argv[1] || "decompile.mjs";
|
|
8
9
|
return [
|
|
9
10
|
"Decompile a .ko or .klib object into readable k source.",
|
|
10
11
|
"",
|
|
11
|
-
`Usage: ${
|
|
12
|
+
`Usage: ${prog} [options] [ object-file [ k-file ] ]`,
|
|
12
13
|
"",
|
|
13
14
|
"Arguments:",
|
|
14
|
-
" object-file
|
|
15
|
-
"
|
|
15
|
+
" object-file Input .ko or .klib path.",
|
|
16
|
+
" Reads object data from stdin when omitted.",
|
|
17
|
+
" k-file Output .k path (default: stdout).",
|
|
16
18
|
"",
|
|
17
19
|
"Options:",
|
|
18
|
-
" -
|
|
20
|
+
" -o, --output path Specify output file path explicitly.",
|
|
21
|
+
" -h, --help Show this help.",
|
|
19
22
|
"",
|
|
20
|
-
"
|
|
21
|
-
` ${
|
|
23
|
+
"Examples:",
|
|
24
|
+
` ${prog} program.ko -o program.decompiled.k`,
|
|
25
|
+
` ${prog} program.ko program.decompiled.k`
|
|
22
26
|
].join("\n");
|
|
23
27
|
}
|
|
24
28
|
|
|
@@ -35,13 +39,39 @@ async function readStdinBuffer() {
|
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
try {
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
42
|
+
const rawArgs = argv.slice(2);
|
|
43
|
+
if (rawArgs.includes("-h") || rawArgs.includes("--help")) {
|
|
40
44
|
usage(console.log);
|
|
41
45
|
exit(0);
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
let explicitOutput = null;
|
|
49
|
+
const remainingArgs = [];
|
|
50
|
+
while (rawArgs.length > 0) {
|
|
51
|
+
const arg = rawArgs[0];
|
|
52
|
+
if (arg === "-o" || arg === "--output") {
|
|
53
|
+
rawArgs.shift();
|
|
54
|
+
explicitOutput = rawArgs.shift();
|
|
55
|
+
if (!explicitOutput) {
|
|
56
|
+
throw new Error("-o/--output requires a file argument");
|
|
57
|
+
}
|
|
58
|
+
} else if (arg.startsWith("-")) {
|
|
59
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
60
|
+
} else {
|
|
61
|
+
remainingArgs.push(rawArgs.shift());
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const objectPath = remainingArgs.shift() || null;
|
|
66
|
+
const positionalOutput = remainingArgs.shift() || null;
|
|
67
|
+
if (remainingArgs.length > 0) {
|
|
68
|
+
throw new Error(`Unexpected argument: ${remainingArgs[0]}`);
|
|
69
|
+
}
|
|
70
|
+
const outputPath = explicitOutput || positionalOutput || null;
|
|
71
|
+
|
|
72
|
+
const objectBuffer = objectPath == null
|
|
73
|
+
? await readStdinBuffer()
|
|
74
|
+
: fs.readFileSync(objectPath);
|
|
45
75
|
const source = decompileObjectBuffer(objectBuffer);
|
|
46
76
|
if (outputPath == null) {
|
|
47
77
|
stdout.write(source);
|
|
@@ -49,7 +79,7 @@ try {
|
|
|
49
79
|
fs.writeFileSync(outputPath, source);
|
|
50
80
|
}
|
|
51
81
|
} catch (error) {
|
|
52
|
-
console.error(error);
|
|
82
|
+
console.error(error.message || String(error));
|
|
53
83
|
usage();
|
|
54
84
|
exit(1);
|
|
55
85
|
}
|