@styx-api/core 0.7.0 → 0.8.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/dist/index.cjs +430 -356
- package/dist/index.d.cts +1 -34
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -34
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +431 -352
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
- package/src/backend/argtype/__fixtures__/microsyntax.argtype +0 -2
- package/src/backend/argtype/emit.ts +2 -51
- package/src/backend/boutiques/boutiques.ts +55 -47
- package/src/backend/field-defaults.ts +40 -0
- package/src/backend/find-struct-node.ts +7 -0
- package/src/backend/index.ts +1 -9
- package/src/backend/nipype/emit.ts +11 -3
- package/src/backend/python/arg-builder.ts +2 -27
- package/src/backend/python/emit.ts +18 -6
- package/src/backend/python/outputs-emit.ts +27 -42
- package/src/backend/python/python.ts +46 -22
- package/src/backend/python/validate-emit.ts +4 -1
- package/src/backend/resolve-output-tokens.ts +0 -76
- package/src/backend/typescript/arg-builder.ts +2 -24
- package/src/backend/typescript/emit.ts +7 -4
- package/src/backend/typescript/outputs-emit.ts +15 -39
- package/src/backend/typescript/typemap.ts +9 -1
- package/src/backend/typescript/typescript.ts +41 -18
- package/src/backend/typescript/validate-emit.ts +4 -1
- package/src/backend/union-variants.ts +41 -1
- package/src/frontend/argdump/parser.ts +20 -19
- package/src/frontend/argtype/__fixtures__/afni-3dtstat.argtype +41 -0
- package/src/frontend/argtype/__fixtures__/bet.argtype +0 -2
- package/src/frontend/argtype/__fixtures__/fsl-flirt.argtype +50 -0
- package/src/frontend/argtype/__fixtures__/wb-command-sub.argtype +39 -0
- package/src/frontend/argtype/ast.ts +14 -1
- package/src/frontend/argtype/frontmatter.ts +49 -5
- package/src/frontend/argtype/lexer.ts +20 -0
- package/src/frontend/argtype/lower.ts +115 -75
- package/src/frontend/argtype/parser-frontend.ts +2 -2
- package/src/frontend/argtype/parser.ts +70 -24
- package/src/frontend/boutiques/destruct-template.ts +24 -17
- package/src/frontend/boutiques/parser.ts +27 -8
- package/src/ir/passes/canonicalize.ts +18 -5
- package/src/ir/passes/simplify.ts +17 -2
- package/src/solver/solver.ts +2 -2
package/dist/index.cjs
CHANGED
|
@@ -206,10 +206,7 @@ var ArgdumpParser = class {
|
|
|
206
206
|
if (nargs.__argparse__ === "REMAINDER") return {
|
|
207
207
|
kind: "repeat",
|
|
208
208
|
attrs: {
|
|
209
|
-
node
|
|
210
|
-
kind: "str",
|
|
211
|
-
attrs: {}
|
|
212
|
-
},
|
|
209
|
+
node,
|
|
213
210
|
countMin: 0
|
|
214
211
|
}
|
|
215
212
|
};
|
|
@@ -580,25 +577,24 @@ var ArgdumpParser = class {
|
|
|
580
577
|
this.error(`No valid subparsers for '${action.dest}'`);
|
|
581
578
|
return null;
|
|
582
579
|
}
|
|
583
|
-
|
|
584
|
-
const alt = {
|
|
580
|
+
const node = alts.length === 1 ? alts[0] : {
|
|
585
581
|
kind: "alternative",
|
|
586
582
|
attrs: { alts }
|
|
587
583
|
};
|
|
588
|
-
|
|
584
|
+
const isRequired = action.subparsers_required === true || action.required === true;
|
|
585
|
+
const meta = this.buildNodeMeta(action);
|
|
586
|
+
if (!isRequired) {
|
|
589
587
|
const opt = {
|
|
590
588
|
kind: "optional",
|
|
591
|
-
attrs: { node
|
|
589
|
+
attrs: { node }
|
|
592
590
|
};
|
|
593
|
-
const meta = this.buildNodeMeta(action);
|
|
594
591
|
if (meta) opt.meta = meta;
|
|
595
592
|
return opt;
|
|
596
593
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
return alt;
|
|
594
|
+
if (node.kind === "alternative" && meta) node.meta = meta;
|
|
595
|
+
return node;
|
|
600
596
|
}
|
|
601
|
-
applyMutualExclusion(
|
|
597
|
+
applyMutualExclusion(groups, nodes, nodesByDest) {
|
|
602
598
|
const excluded = /* @__PURE__ */ new Set();
|
|
603
599
|
for (const group of groups) {
|
|
604
600
|
if (!isObject$3(group)) continue;
|
|
@@ -658,23 +654,19 @@ var ArgdumpParser = class {
|
|
|
658
654
|
};
|
|
659
655
|
const positionals = [];
|
|
660
656
|
const optionals = [];
|
|
661
|
-
const actionsByDest = /* @__PURE__ */ new Map();
|
|
662
657
|
const nodesByDest = /* @__PURE__ */ new Map();
|
|
663
658
|
for (const rawAction of actions) {
|
|
664
659
|
if (!isObject$3(rawAction)) continue;
|
|
665
660
|
const node = this.buildAction(rawAction);
|
|
666
661
|
if (!node) continue;
|
|
667
662
|
const dest = rawAction.dest;
|
|
668
|
-
if (isString$3(dest))
|
|
669
|
-
actionsByDest.set(dest, rawAction);
|
|
670
|
-
nodesByDest.set(dest, node);
|
|
671
|
-
}
|
|
663
|
+
if (isString$3(dest)) nodesByDest.set(dest, node);
|
|
672
664
|
if (this.isPositional(rawAction) && rawAction.action_type !== "parsers") positionals.push(node);
|
|
673
665
|
else optionals.push(node);
|
|
674
666
|
}
|
|
675
667
|
let allNodes = [...positionals, ...optionals];
|
|
676
668
|
const mutexGroups = descriptor.mutually_exclusive_groups;
|
|
677
|
-
if (isArray$3(mutexGroups) && mutexGroups.length > 0) allNodes = this.applyMutualExclusion(
|
|
669
|
+
if (isArray$3(mutexGroups) && mutexGroups.length > 0) allNodes = this.applyMutualExclusion(mutexGroups, allNodes, nodesByDest);
|
|
678
670
|
return {
|
|
679
671
|
kind: "sequence",
|
|
680
672
|
attrs: { nodes: allNodes }
|
|
@@ -864,6 +856,18 @@ function lex(source) {
|
|
|
864
856
|
});
|
|
865
857
|
}
|
|
866
858
|
}
|
|
859
|
+
if (/^\d+$/.test(num) && isIdentStart(peek())) {
|
|
860
|
+
let rest = "";
|
|
861
|
+
while (i < source.length && isIdentPart(peek())) rest += advance();
|
|
862
|
+
const full = num + rest;
|
|
863
|
+
errors.push({
|
|
864
|
+
message: `Identifier cannot start with a digit; quote it as "${full}"`,
|
|
865
|
+
line: startLine,
|
|
866
|
+
column: startCol
|
|
867
|
+
});
|
|
868
|
+
push("ident", full, startLine, startCol);
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
867
871
|
push("number", num, startLine, startCol);
|
|
868
872
|
continue;
|
|
869
873
|
}
|
|
@@ -1023,7 +1027,44 @@ function findColon(s) {
|
|
|
1023
1027
|
function unescapeScalar(s) {
|
|
1024
1028
|
return s.replace(/\\(.)/g, (_, c) => c === "n" ? "\n" : c === "t" ? " " : c === "r" ? "\r" : c);
|
|
1025
1029
|
}
|
|
1030
|
+
/** Split a flow-sequence body (`a, "b, c", [d, e]`) on top-level commas, keeping
|
|
1031
|
+
* commas inside quotes or a nested `[...]` intact. Empty segments (from a leading,
|
|
1032
|
+
* trailing, or doubled comma, or an empty `[]` body) are dropped, matching YAML;
|
|
1033
|
+
* a quoted empty string keeps its quotes and so survives. */
|
|
1034
|
+
function splitFlow(body) {
|
|
1035
|
+
const parts = [];
|
|
1036
|
+
let depth = 0;
|
|
1037
|
+
let inQuote = null;
|
|
1038
|
+
let cur = "";
|
|
1039
|
+
for (let i = 0; i < body.length; i++) {
|
|
1040
|
+
const ch = body[i];
|
|
1041
|
+
if (inQuote === "\"" && ch === "\\") {
|
|
1042
|
+
cur += ch + (body[i + 1] ?? "");
|
|
1043
|
+
i++;
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
if (inQuote) {
|
|
1047
|
+
if (ch === inQuote) inQuote = null;
|
|
1048
|
+
cur += ch;
|
|
1049
|
+
} else if (ch === "\"" || ch === "'") {
|
|
1050
|
+
inQuote = ch;
|
|
1051
|
+
cur += ch;
|
|
1052
|
+
} else if (ch === "[") {
|
|
1053
|
+
depth++;
|
|
1054
|
+
cur += ch;
|
|
1055
|
+
} else if (ch === "]") {
|
|
1056
|
+
depth--;
|
|
1057
|
+
cur += ch;
|
|
1058
|
+
} else if (ch === "," && depth === 0) {
|
|
1059
|
+
parts.push(cur);
|
|
1060
|
+
cur = "";
|
|
1061
|
+
} else cur += ch;
|
|
1062
|
+
}
|
|
1063
|
+
parts.push(cur);
|
|
1064
|
+
return parts.map((p) => p.trim()).filter((p) => p !== "");
|
|
1065
|
+
}
|
|
1026
1066
|
function parseScalar(text) {
|
|
1067
|
+
if (text.startsWith("[") && text.endsWith("]") && text.length >= 2) return splitFlow(text.slice(1, -1)).map(parseScalar);
|
|
1027
1068
|
if (text.startsWith("\"") && text.endsWith("\"") && text.length >= 2) return unescapeScalar(text.slice(1, -1));
|
|
1028
1069
|
if (text.startsWith("'") && text.endsWith("'") && text.length >= 2) return text.slice(1, -1);
|
|
1029
1070
|
if (text === "true") return true;
|
|
@@ -1236,6 +1277,37 @@ const KNOWN_METHODS = new Set([
|
|
|
1236
1277
|
"mutable",
|
|
1237
1278
|
"resolveParent"
|
|
1238
1279
|
]);
|
|
1280
|
+
/** Levenshtein edit distance between two strings. */
|
|
1281
|
+
function editDistance(a, b) {
|
|
1282
|
+
const m = a.length;
|
|
1283
|
+
const n = b.length;
|
|
1284
|
+
let prev = Array.from({ length: n + 1 }, (_, j) => j);
|
|
1285
|
+
for (let i = 1; i <= m; i++) {
|
|
1286
|
+
const cur = [i];
|
|
1287
|
+
for (let j = 1; j <= n; j++) cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
1288
|
+
prev = cur;
|
|
1289
|
+
}
|
|
1290
|
+
return prev[n];
|
|
1291
|
+
}
|
|
1292
|
+
/** The closest candidate to `name` within a small edit distance, for a "did you
|
|
1293
|
+
* mean?" hint on an unknown method. Returns undefined when nothing is close
|
|
1294
|
+
* enough, so a deliberate unknown-extension method gets no spurious suggestion.
|
|
1295
|
+
* A typo (`reqires`, `mediaTpe`) lands within distance 2 of a real method; an
|
|
1296
|
+
* unrelated extension method (`conflicts`) does not. The `bestDist < name.length`
|
|
1297
|
+
* clause additionally stops a very short unknown name from matching a longer
|
|
1298
|
+
* method by pure insertion (a 1-char `.n()` is not "did you mean `.min()`"). */
|
|
1299
|
+
function suggestMethod(name, candidates) {
|
|
1300
|
+
let best;
|
|
1301
|
+
let bestDist = Infinity;
|
|
1302
|
+
for (const c of candidates) {
|
|
1303
|
+
const d = editDistance(name, c);
|
|
1304
|
+
if (d < bestDist) {
|
|
1305
|
+
bestDist = d;
|
|
1306
|
+
best = c;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
return best !== void 0 && bestDist <= 2 && bestDist < name.length ? best : void 0;
|
|
1310
|
+
}
|
|
1239
1311
|
var Parser = class {
|
|
1240
1312
|
tokens;
|
|
1241
1313
|
pos = 0;
|
|
@@ -1362,38 +1434,41 @@ var Parser = class {
|
|
|
1362
1434
|
return {
|
|
1363
1435
|
kind: "comb",
|
|
1364
1436
|
op: "alt",
|
|
1365
|
-
children: alts
|
|
1437
|
+
children: alts,
|
|
1438
|
+
...first.span && { span: first.span }
|
|
1366
1439
|
};
|
|
1367
1440
|
}
|
|
1368
1441
|
parseChain() {
|
|
1369
1442
|
const node = this.parsePrimary();
|
|
1370
|
-
let chained = false;
|
|
1371
1443
|
while (this.at("dot")) {
|
|
1372
1444
|
this.next();
|
|
1373
1445
|
this.applyMethod(node);
|
|
1374
|
-
chained = true;
|
|
1375
1446
|
}
|
|
1376
1447
|
if (this.at("eq")) {
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
if (node.kind === "terminal" && !chained) node.default = value;
|
|
1380
|
-
else this.error("`= value` default is only allowed on a bare terminal; use `.default(...)` after a method chain", eqTok);
|
|
1448
|
+
this.next();
|
|
1449
|
+
node.default = this.parseValue();
|
|
1381
1450
|
}
|
|
1382
1451
|
return node;
|
|
1383
1452
|
}
|
|
1384
1453
|
parsePrimary() {
|
|
1385
1454
|
const tok = this.peek();
|
|
1455
|
+
const span = {
|
|
1456
|
+
line: tok.line,
|
|
1457
|
+
column: tok.column
|
|
1458
|
+
};
|
|
1386
1459
|
if (tok.kind === "string") {
|
|
1387
1460
|
this.next();
|
|
1388
1461
|
return {
|
|
1389
1462
|
kind: "literal",
|
|
1390
|
-
value: tok.value
|
|
1463
|
+
value: tok.value,
|
|
1464
|
+
span
|
|
1391
1465
|
};
|
|
1392
1466
|
}
|
|
1393
1467
|
if (tok.kind === "lparen") return {
|
|
1394
1468
|
kind: "comb",
|
|
1395
1469
|
op: "seq",
|
|
1396
|
-
children: this.parseParenList()
|
|
1470
|
+
children: this.parseParenList(),
|
|
1471
|
+
span
|
|
1397
1472
|
};
|
|
1398
1473
|
if (tok.kind === "ident") {
|
|
1399
1474
|
if (COMBINATORS.has(tok.value) && this.peek(1).kind === "lparen") {
|
|
@@ -1402,27 +1477,31 @@ var Parser = class {
|
|
|
1402
1477
|
return {
|
|
1403
1478
|
kind: "comb",
|
|
1404
1479
|
op: tok.value,
|
|
1405
|
-
children
|
|
1480
|
+
children,
|
|
1481
|
+
span
|
|
1406
1482
|
};
|
|
1407
1483
|
}
|
|
1408
1484
|
if (TERMINALS.has(tok.value)) {
|
|
1409
1485
|
this.next();
|
|
1410
1486
|
return {
|
|
1411
1487
|
kind: "terminal",
|
|
1412
|
-
terminal: tok.value
|
|
1488
|
+
terminal: tok.value,
|
|
1489
|
+
span
|
|
1413
1490
|
};
|
|
1414
1491
|
}
|
|
1415
1492
|
this.next();
|
|
1416
1493
|
return {
|
|
1417
1494
|
kind: "ref",
|
|
1418
|
-
refName: tok.value
|
|
1495
|
+
refName: tok.value,
|
|
1496
|
+
span
|
|
1419
1497
|
};
|
|
1420
1498
|
}
|
|
1421
1499
|
this.error(`Unexpected token '${tok.value || tok.kind}' in expression`, tok);
|
|
1422
1500
|
this.next();
|
|
1423
1501
|
return {
|
|
1424
1502
|
kind: "literal",
|
|
1425
|
-
value: ""
|
|
1503
|
+
value: "",
|
|
1504
|
+
span
|
|
1426
1505
|
};
|
|
1427
1506
|
}
|
|
1428
1507
|
/** Parse `( elem, elem, ... )` with optional leading docs and trailing comma. */
|
|
@@ -1450,7 +1529,8 @@ var Parser = class {
|
|
|
1450
1529
|
}
|
|
1451
1530
|
if (!KNOWN_METHODS.has(method)) {
|
|
1452
1531
|
this.skipBalancedArgs();
|
|
1453
|
-
|
|
1532
|
+
const hint = suggestMethod(method, [...KNOWN_METHODS, "output"]);
|
|
1533
|
+
this.warn(`Ignoring unsupported method '.${method}()'` + (hint ? ` (did you mean '.${hint}()'?)` : ""), nameTok);
|
|
1454
1534
|
return;
|
|
1455
1535
|
}
|
|
1456
1536
|
const args = this.parseScalarArgs();
|
|
@@ -1570,7 +1650,8 @@ var Parser = class {
|
|
|
1570
1650
|
else out.description = arg;
|
|
1571
1651
|
} else {
|
|
1572
1652
|
this.skipBalancedArgs();
|
|
1573
|
-
|
|
1653
|
+
const hint = suggestMethod(m.value, CHAIN);
|
|
1654
|
+
this.warn(`Ignoring unsupported output-template method '.${m.value}()'` + (hint ? ` (did you mean '.${hint}()'?)` : ""), m);
|
|
1574
1655
|
}
|
|
1575
1656
|
}
|
|
1576
1657
|
if (name !== void 0) out.name = name;
|
|
@@ -1732,6 +1813,13 @@ function effectiveOutputName(output, index) {
|
|
|
1732
1813
|
* so an output nested in a repeat / subcommand keeps its list / struct shape
|
|
1733
1814
|
* (per-output gating is recovered downstream from each ref binding's gate).
|
|
1734
1815
|
*/
|
|
1816
|
+
/** Spread a node's span into a diagnostic (no-op when the node has no span). */
|
|
1817
|
+
function at(span) {
|
|
1818
|
+
return span ? {
|
|
1819
|
+
line: span.line,
|
|
1820
|
+
column: span.column
|
|
1821
|
+
} : {};
|
|
1822
|
+
}
|
|
1735
1823
|
/** Build IR `Documentation` from an AST node's already-split title/description. */
|
|
1736
1824
|
function docFrom(node) {
|
|
1737
1825
|
const doc = {
|
|
@@ -1743,16 +1831,27 @@ function docFrom(node) {
|
|
|
1743
1831
|
var Lowerer = class {
|
|
1744
1832
|
errors = [];
|
|
1745
1833
|
warnings = [];
|
|
1746
|
-
/** Extensions whose annotations the document actually uses, collected during
|
|
1747
|
-
* lowering so `lowerDocument` can flag any used-but-undeclared in frontmatter. */
|
|
1748
|
-
usedExtensions = /* @__PURE__ */ new Set();
|
|
1749
1834
|
aliases = /* @__PURE__ */ new Map();
|
|
1750
1835
|
constructor(aliases) {
|
|
1751
1836
|
for (const a of aliases) {
|
|
1752
|
-
if (this.aliases.has(a.name)) this.
|
|
1837
|
+
if (this.aliases.has(a.name)) this.warn(`Duplicate alias '${a.name}'; last definition wins`, a.expr);
|
|
1753
1838
|
this.aliases.set(a.name, a.expr);
|
|
1754
1839
|
}
|
|
1755
1840
|
}
|
|
1841
|
+
/** Record an error, located at `node`'s span when one is available. */
|
|
1842
|
+
err(message, node) {
|
|
1843
|
+
this.errors.push({
|
|
1844
|
+
message,
|
|
1845
|
+
...at(node?.span)
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
/** Record a warning, located at `node`'s span when one is available. */
|
|
1849
|
+
warn(message, node) {
|
|
1850
|
+
this.warnings.push({
|
|
1851
|
+
message,
|
|
1852
|
+
...at(node?.span)
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1756
1855
|
lower(doc) {
|
|
1757
1856
|
const rootSink = [];
|
|
1758
1857
|
const expr = this.lowerNode(doc.root, /* @__PURE__ */ new Set(), void 0, rootSink);
|
|
@@ -1778,17 +1877,19 @@ var Lowerer = class {
|
|
|
1778
1877
|
if (node.kind === "ref") return this.lowerRef(node, expanding, selfName, sink);
|
|
1779
1878
|
const name = node.name ?? selfName;
|
|
1780
1879
|
const joinable = node.kind === "comb" && (node.op === "seq" || node.op === "set" || node.op === "rep" || node.op === "opt");
|
|
1781
|
-
if (node.join !== void 0 && !joinable) this.
|
|
1880
|
+
if (node.join !== void 0 && !joinable) this.err("`.join()` is only supported on seq/set/rep/opt", node);
|
|
1782
1881
|
const isNumericTerminal = node.kind === "terminal" && (node.terminal === "int" || node.terminal === "float");
|
|
1783
1882
|
const isRep = node.kind === "comb" && node.op === "rep";
|
|
1784
1883
|
const isPath = node.kind === "terminal" && node.terminal === "path";
|
|
1785
|
-
if ((node.min !== void 0 || node.max !== void 0) && !isNumericTerminal) this.
|
|
1786
|
-
if ((node.countMin !== void 0 || node.countMax !== void 0) && !isRep) this.
|
|
1787
|
-
if ((node.mutable || node.resolveParent) && !isPath) this.
|
|
1788
|
-
if (node.mediaTypes?.length && !isPath) this.
|
|
1789
|
-
|
|
1790
|
-
if (node.
|
|
1791
|
-
|
|
1884
|
+
if ((node.min !== void 0 || node.max !== void 0) && !isNumericTerminal) this.err("`.min()`/`.max()` is only supported on int/float terminals", node);
|
|
1885
|
+
if ((node.countMin !== void 0 || node.countMax !== void 0) && !isRep) this.err("`.count()`/`.countMin()`/`.countMax()` is only supported on rep", node);
|
|
1886
|
+
if ((node.mutable || node.resolveParent) && !isPath) this.err("`.mutable()`/`.resolveParent()` is only supported on path", node);
|
|
1887
|
+
if (node.mediaTypes?.length && !isPath) this.err("`.mediaType()` is only supported on path", node);
|
|
1888
|
+
const isStruct = node.kind === "comb" && (node.op === "seq" || node.op === "set");
|
|
1889
|
+
if (node.default !== void 0 && isStruct) {
|
|
1890
|
+
this.warn("`= value` / `.default()` is not supported on a seq/set struct; ignored", node);
|
|
1891
|
+
node.default = void 0;
|
|
1892
|
+
}
|
|
1792
1893
|
const isSeqSet = node.kind === "comb" && (node.op === "seq" || node.op === "set");
|
|
1793
1894
|
if (node.outputs?.length && !isSeqSet) for (const o of node.outputs) sink.push(this.toOutput(o, name));
|
|
1794
1895
|
switch (node.kind) {
|
|
@@ -1800,7 +1901,7 @@ var Lowerer = class {
|
|
|
1800
1901
|
case "terminal": return this.lowerTerminal(node);
|
|
1801
1902
|
case "comb": return this.lowerComb(node, expanding, name, sink);
|
|
1802
1903
|
default:
|
|
1803
|
-
this.
|
|
1904
|
+
this.err(`Unknown AST node kind '${node.kind}'`, node);
|
|
1804
1905
|
return seq();
|
|
1805
1906
|
}
|
|
1806
1907
|
}
|
|
@@ -1808,14 +1909,15 @@ var Lowerer = class {
|
|
|
1808
1909
|
const target = node.refName;
|
|
1809
1910
|
const aliasExpr = this.aliases.get(target);
|
|
1810
1911
|
if (!aliasExpr) {
|
|
1811
|
-
this.
|
|
1912
|
+
this.err(`Unknown alias '${target}'`, node);
|
|
1812
1913
|
return seq();
|
|
1813
1914
|
}
|
|
1814
1915
|
if (expanding.has(target)) {
|
|
1815
|
-
this.
|
|
1916
|
+
this.err(`Recursive alias '${target}' is not allowed`, node);
|
|
1816
1917
|
return seq();
|
|
1817
1918
|
}
|
|
1818
1919
|
const clone = structuredClone(aliasExpr);
|
|
1920
|
+
clone.span = node.span ?? clone.span;
|
|
1819
1921
|
clone.name = node.name ?? clone.name;
|
|
1820
1922
|
clone.title = node.title ?? clone.title;
|
|
1821
1923
|
clone.description = node.description ?? clone.description;
|
|
@@ -1837,7 +1939,7 @@ var Lowerer = class {
|
|
|
1837
1939
|
switch (node.terminal) {
|
|
1838
1940
|
case "int": {
|
|
1839
1941
|
const e = int();
|
|
1840
|
-
this.checkBounds(node.min, node.max, "value", "min", "max");
|
|
1942
|
+
this.checkBounds(node.min, node.max, "value", "min", "max", node);
|
|
1841
1943
|
if (node.min !== void 0) e.attrs.minValue = node.min;
|
|
1842
1944
|
if (node.max !== void 0) e.attrs.maxValue = node.max;
|
|
1843
1945
|
this.applyMeta(e, node);
|
|
@@ -1845,7 +1947,7 @@ var Lowerer = class {
|
|
|
1845
1947
|
}
|
|
1846
1948
|
case "float": {
|
|
1847
1949
|
const e = float();
|
|
1848
|
-
this.checkBounds(node.min, node.max, "value", "min", "max");
|
|
1950
|
+
this.checkBounds(node.min, node.max, "value", "min", "max", node);
|
|
1849
1951
|
if (node.min !== void 0) e.attrs.minValue = node.min;
|
|
1850
1952
|
if (node.max !== void 0) e.attrs.maxValue = node.max;
|
|
1851
1953
|
this.applyMeta(e, node);
|
|
@@ -1865,7 +1967,7 @@ var Lowerer = class {
|
|
|
1865
1967
|
return e;
|
|
1866
1968
|
}
|
|
1867
1969
|
default:
|
|
1868
|
-
this.
|
|
1970
|
+
this.err(`Unknown terminal '${String(node.terminal)}'`, node);
|
|
1869
1971
|
return str();
|
|
1870
1972
|
}
|
|
1871
1973
|
}
|
|
@@ -1878,7 +1980,7 @@ var Lowerer = class {
|
|
|
1878
1980
|
const selfOutputs = [];
|
|
1879
1981
|
for (const o of node.outputs ?? []) selfOutputs.push(this.toOutput(o, name));
|
|
1880
1982
|
const lowered = lowerChildren(selfOutputs);
|
|
1881
|
-
this.dedupeSiblingNames(lowered);
|
|
1983
|
+
this.dedupeSiblingNames(lowered, node);
|
|
1882
1984
|
const e = seq(...lowered);
|
|
1883
1985
|
if (node.join !== void 0) e.attrs.join = node.join;
|
|
1884
1986
|
this.applyMeta(e, node);
|
|
@@ -1890,14 +1992,14 @@ var Lowerer = class {
|
|
|
1890
1992
|
}
|
|
1891
1993
|
case "alt": {
|
|
1892
1994
|
const arms = lowerChildren(sink);
|
|
1893
|
-
this.dedupeSiblingNames(arms);
|
|
1995
|
+
this.dedupeSiblingNames(arms, node);
|
|
1894
1996
|
const e = alt(...arms);
|
|
1895
1997
|
this.applyMeta(e, node);
|
|
1896
1998
|
return e;
|
|
1897
1999
|
}
|
|
1898
2000
|
case "any": {
|
|
1899
2001
|
if (children.length === 0) {
|
|
1900
|
-
this.
|
|
2002
|
+
this.err("`any(...)` requires at least one branch", node);
|
|
1901
2003
|
return seq();
|
|
1902
2004
|
}
|
|
1903
2005
|
const e = this.lowerNode(children[0], expanding, name, sink);
|
|
@@ -1928,21 +2030,21 @@ var Lowerer = class {
|
|
|
1928
2030
|
case "rep": {
|
|
1929
2031
|
const e = rep(this.wrapChildren(children, expanding, name, sink));
|
|
1930
2032
|
if (node.join !== void 0) e.attrs.join = node.join;
|
|
1931
|
-
this.checkBounds(node.countMin, node.countMax, "repetition count", "countMin", "countMax");
|
|
2033
|
+
this.checkBounds(node.countMin, node.countMax, "repetition count", "countMin", "countMax", node);
|
|
1932
2034
|
if (node.countMin !== void 0) e.attrs.countMin = node.countMin;
|
|
1933
2035
|
if (node.countMax !== void 0) e.attrs.countMax = node.countMax;
|
|
1934
2036
|
this.applyMeta(e, node);
|
|
1935
2037
|
return e;
|
|
1936
2038
|
}
|
|
1937
2039
|
default:
|
|
1938
|
-
this.
|
|
2040
|
+
this.err(`Unknown combinator '${String(node.op)}'`, node);
|
|
1939
2041
|
return seq();
|
|
1940
2042
|
}
|
|
1941
2043
|
}
|
|
1942
2044
|
/** Warn on an inverted `[min, max]` pair (a lower bound above its upper
|
|
1943
2045
|
* bound), which yields an unsatisfiable constraint downstream. */
|
|
1944
|
-
checkBounds(min, max, what, minLabel, maxLabel) {
|
|
1945
|
-
if (min !== void 0 && max !== void 0 && min > max) this.
|
|
2046
|
+
checkBounds(min, max, what, minLabel, maxLabel, node) {
|
|
2047
|
+
if (min !== void 0 && max !== void 0 && min > max) this.warn(`Inverted ${what} bounds: ${minLabel} (${min}) > ${maxLabel} (${max})`, node);
|
|
1946
2048
|
}
|
|
1947
2049
|
/**
|
|
1948
2050
|
* Disambiguate duplicate explicit names among the direct children of a
|
|
@@ -1956,7 +2058,7 @@ var Lowerer = class {
|
|
|
1956
2058
|
* explicit labels; it does not detect collisions between solver-derived names
|
|
1957
2059
|
* of otherwise-unnamed children, which the solver also does not guard.)
|
|
1958
2060
|
*/
|
|
1959
|
-
dedupeSiblingNames(children) {
|
|
2061
|
+
dedupeSiblingNames(children, parent) {
|
|
1960
2062
|
const used = /* @__PURE__ */ new Set();
|
|
1961
2063
|
for (const child of children) {
|
|
1962
2064
|
const nm = child.meta?.name;
|
|
@@ -1973,7 +2075,7 @@ var Lowerer = class {
|
|
|
1973
2075
|
...child.meta,
|
|
1974
2076
|
name: renamed
|
|
1975
2077
|
};
|
|
1976
|
-
this.
|
|
2078
|
+
this.warn(`Duplicate sibling name '${nm}'; renamed one occurrence to '${renamed}'`, parent);
|
|
1977
2079
|
}
|
|
1978
2080
|
}
|
|
1979
2081
|
/** `opt`/`rep` with multiple children implicitly wrap them in a sequence,
|
|
@@ -2012,15 +2114,15 @@ var Lowerer = class {
|
|
|
2012
2114
|
}
|
|
2013
2115
|
const targetName = t.name ?? selfName;
|
|
2014
2116
|
if (!targetName) {
|
|
2015
|
-
this.
|
|
2117
|
+
this.warn("Output self-reference '{}' has no named node to resolve to; emitting empty");
|
|
2016
2118
|
tokens.push({
|
|
2017
2119
|
kind: "literal",
|
|
2018
2120
|
value: ""
|
|
2019
2121
|
});
|
|
2020
2122
|
continue;
|
|
2021
2123
|
}
|
|
2022
|
-
if (t.stripPrefix?.length) this.
|
|
2023
|
-
if (t.basename) this.
|
|
2124
|
+
if (t.stripPrefix?.length) this.warn("Output ref op 'strip_prefix' is not supported by the IR; ignored");
|
|
2125
|
+
if (t.basename) this.warn("Output ref op 'basename' is not supported by the IR; ignored");
|
|
2024
2126
|
tokens.push({
|
|
2025
2127
|
kind: "ref",
|
|
2026
2128
|
target: nodeRef(targetName),
|
|
@@ -2028,7 +2130,7 @@ var Lowerer = class {
|
|
|
2028
2130
|
...t.or !== void 0 && { fallback: t.or }
|
|
2029
2131
|
});
|
|
2030
2132
|
}
|
|
2031
|
-
if (o.fallback !== void 0) this.
|
|
2133
|
+
if (o.fallback !== void 0) this.warn("Output-level '.or(...)' fallback is not supported by the IR; ignored");
|
|
2032
2134
|
const doc = docFrom(o);
|
|
2033
2135
|
return {
|
|
2034
2136
|
...o.name && { name: o.name },
|
|
@@ -2057,6 +2159,17 @@ function buildAppMeta(fm, rootDoc, rootName) {
|
|
|
2057
2159
|
const authors = strList(fm.authors);
|
|
2058
2160
|
const urls = strList(fm.urls);
|
|
2059
2161
|
const references = strList(fm.references);
|
|
2162
|
+
const checkList = (key, v) => {
|
|
2163
|
+
if (v !== void 0 && v !== null && !Array.isArray(v)) warnings.push(`Frontmatter '${key}' should be a list; got a scalar - ignored`);
|
|
2164
|
+
};
|
|
2165
|
+
checkList("authors", fm.authors);
|
|
2166
|
+
checkList("urls", fm.urls);
|
|
2167
|
+
checkList("references", fm.references);
|
|
2168
|
+
if (fm.version !== void 0 && fm.version !== null && version === void 0) warnings.push(`Frontmatter 'version' should be a string or number; ignored`);
|
|
2169
|
+
if (fm.container !== void 0 && fm.container !== null) {
|
|
2170
|
+
if (!isRecord$1(fm.container)) warnings.push(`Frontmatter 'container' should be a mapping with an 'image'; ignored`);
|
|
2171
|
+
else if (!asStr(fm.container.image)) warnings.push(`Frontmatter 'container' is missing an 'image'; ignored`);
|
|
2172
|
+
}
|
|
2060
2173
|
const doc = {
|
|
2061
2174
|
...rootTitleDesc,
|
|
2062
2175
|
...authors.length > 0 && { authors },
|
|
@@ -2106,28 +2219,18 @@ function isRecord$1(x) {
|
|
|
2106
2219
|
return typeof x === "object" && x !== null && !Array.isArray(x);
|
|
2107
2220
|
}
|
|
2108
2221
|
function lowerDocument(doc) {
|
|
2109
|
-
const
|
|
2110
|
-
const result = lowerer.lower(doc);
|
|
2222
|
+
const result = new Lowerer(doc.aliases).lower(doc);
|
|
2111
2223
|
const exe = typeof doc.frontmatter?.exe === "string" ? doc.frontmatter.exe : void 0;
|
|
2112
2224
|
if (exe) result.expr.attrs.nodes.unshift(lit(exe));
|
|
2113
2225
|
const { meta, warnings } = buildAppMeta(doc.frontmatter, {
|
|
2114
2226
|
title: doc.root.title,
|
|
2115
2227
|
description: doc.root.description
|
|
2116
2228
|
}, doc.rootName);
|
|
2117
|
-
const extWarnings = [];
|
|
2118
|
-
if (doc.frontmatter) {
|
|
2119
|
-
const declared = new Set(Array.isArray(doc.frontmatter.extensions) ? doc.frontmatter.extensions.filter((e) => typeof e === "string") : []);
|
|
2120
|
-
for (const used of lowerer.usedExtensions) if (!declared.has(used)) extWarnings.push(`Uses the '${used}' extension but does not declare it in frontmatter (add it to 'extensions:')`);
|
|
2121
|
-
}
|
|
2122
2229
|
return {
|
|
2123
2230
|
...meta && { meta },
|
|
2124
2231
|
expr: result.expr,
|
|
2125
2232
|
errors: result.errors,
|
|
2126
|
-
warnings: [
|
|
2127
|
-
...result.warnings,
|
|
2128
|
-
...warnings,
|
|
2129
|
-
...extWarnings
|
|
2130
|
-
]
|
|
2233
|
+
warnings: [...result.warnings, ...warnings.map((message) => ({ message }))]
|
|
2131
2234
|
};
|
|
2132
2235
|
}
|
|
2133
2236
|
|
|
@@ -2174,8 +2277,14 @@ var ArgtypeParser = class {
|
|
|
2174
2277
|
warnings
|
|
2175
2278
|
};
|
|
2176
2279
|
const lowered = lowerDocument(doc);
|
|
2177
|
-
warnings.push(...lowered.warnings.map((
|
|
2178
|
-
|
|
2280
|
+
warnings.push(...lowered.warnings.map((d) => ({
|
|
2281
|
+
message: d.message,
|
|
2282
|
+
...toLocation(d)
|
|
2283
|
+
})));
|
|
2284
|
+
errors.push(...lowered.errors.map((d) => ({
|
|
2285
|
+
message: d.message,
|
|
2286
|
+
...toLocation(d)
|
|
2287
|
+
})));
|
|
2179
2288
|
return {
|
|
2180
2289
|
...lowered.meta && { meta: lowered.meta },
|
|
2181
2290
|
expr: lowered.expr,
|
|
@@ -2205,20 +2314,26 @@ function destructTemplate(template, lookup) {
|
|
|
2205
2314
|
destructed.push(x);
|
|
2206
2315
|
continue;
|
|
2207
2316
|
}
|
|
2208
|
-
let
|
|
2317
|
+
let best = null;
|
|
2209
2318
|
for (const [alias, replacement] of Object.entries(lookup)) {
|
|
2319
|
+
if (alias.length === 0) continue;
|
|
2210
2320
|
const idx = x.indexOf(alias);
|
|
2211
|
-
if (idx
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
didSplit = true;
|
|
2218
|
-
break;
|
|
2219
|
-
}
|
|
2321
|
+
if (idx === -1) continue;
|
|
2322
|
+
if (best === null || idx < best.idx || idx === best.idx && alias.length > best.alias.length) best = {
|
|
2323
|
+
idx,
|
|
2324
|
+
alias,
|
|
2325
|
+
replacement
|
|
2326
|
+
};
|
|
2220
2327
|
}
|
|
2221
|
-
if (
|
|
2328
|
+
if (best === null) {
|
|
2329
|
+
destructed.push(x);
|
|
2330
|
+
continue;
|
|
2331
|
+
}
|
|
2332
|
+
const left = x.slice(0, best.idx);
|
|
2333
|
+
const right = x.slice(best.idx + best.alias.length);
|
|
2334
|
+
if (right.length > 0) stack.unshift(right);
|
|
2335
|
+
stack.unshift(best.replacement);
|
|
2336
|
+
if (left.length > 0) stack.unshift(left);
|
|
2222
2337
|
}
|
|
2223
2338
|
return destructed;
|
|
2224
2339
|
}
|
|
@@ -2546,14 +2661,8 @@ var BoutiquesParser = class {
|
|
|
2546
2661
|
kind: "int",
|
|
2547
2662
|
attrs: {}
|
|
2548
2663
|
};
|
|
2549
|
-
if (isNumber$1(btInput.minimum))
|
|
2550
|
-
|
|
2551
|
-
if (btInput["exclusive-minimum"] === true) node.attrs.minValue += 1;
|
|
2552
|
-
}
|
|
2553
|
-
if (isNumber$1(btInput.maximum)) {
|
|
2554
|
-
node.attrs.maxValue = Math.floor(btInput.maximum);
|
|
2555
|
-
if (btInput["exclusive-maximum"] === true) node.attrs.maxValue -= 1;
|
|
2556
|
-
}
|
|
2664
|
+
if (isNumber$1(btInput.minimum)) node.attrs.minValue = btInput["exclusive-minimum"] === true ? Math.floor(btInput.minimum) + 1 : Math.ceil(btInput.minimum);
|
|
2665
|
+
if (isNumber$1(btInput.maximum)) node.attrs.maxValue = btInput["exclusive-maximum"] === true ? Math.ceil(btInput.maximum) - 1 : Math.floor(btInput.maximum);
|
|
2557
2666
|
if (meta) node.meta = meta;
|
|
2558
2667
|
return node;
|
|
2559
2668
|
}
|
|
@@ -2603,7 +2712,10 @@ var BoutiquesParser = class {
|
|
|
2603
2712
|
return null;
|
|
2604
2713
|
}
|
|
2605
2714
|
const node = this.parseDescriptor(nested);
|
|
2606
|
-
if (node && meta) node.meta =
|
|
2715
|
+
if (node && meta) node.meta = {
|
|
2716
|
+
...node.meta,
|
|
2717
|
+
...meta
|
|
2718
|
+
};
|
|
2607
2719
|
return node;
|
|
2608
2720
|
}
|
|
2609
2721
|
case InputTypePrimitive.SubCommandUnion: {
|
|
@@ -2702,13 +2814,16 @@ var BoutiquesParser = class {
|
|
|
2702
2814
|
node = this.wrapWithFlag(node, btInput);
|
|
2703
2815
|
if (inputType.isOptional) node = this.wrapWithOptional(node);
|
|
2704
2816
|
if (node !== inner && inner.meta) {
|
|
2705
|
-
const { name, ...rest } = inner.meta;
|
|
2817
|
+
const { name, outputs, ...rest } = inner.meta;
|
|
2706
2818
|
if (Object.keys(rest).length > 0) {
|
|
2707
2819
|
node.meta = {
|
|
2708
2820
|
...node.meta,
|
|
2709
2821
|
...rest
|
|
2710
2822
|
};
|
|
2711
|
-
|
|
2823
|
+
const kept = {};
|
|
2824
|
+
if (name) kept.name = name;
|
|
2825
|
+
if (outputs) kept.outputs = outputs;
|
|
2826
|
+
inner.meta = Object.keys(kept).length > 0 ? kept : void 0;
|
|
2712
2827
|
}
|
|
2713
2828
|
}
|
|
2714
2829
|
return node;
|
|
@@ -3786,25 +3901,6 @@ function syntheticWrap(root) {
|
|
|
3786
3901
|
...meta?.outputs && { outputs: meta.outputs }
|
|
3787
3902
|
};
|
|
3788
3903
|
}
|
|
3789
|
-
function hasOutputs(expr) {
|
|
3790
|
-
return walkSome(expr, (n) => (n.meta?.outputs?.length ?? 0) > 0);
|
|
3791
|
-
}
|
|
3792
|
-
function hasMediaTypes(expr) {
|
|
3793
|
-
return walkSome(expr, (n) => n.kind === "path" && (n.attrs.mediaTypes?.length ?? 0) > 0);
|
|
3794
|
-
}
|
|
3795
|
-
function hasPaths(expr) {
|
|
3796
|
-
return walkSome(expr, (n) => n.kind === "path" && (!!n.attrs.mutable || !!n.attrs.resolveParent));
|
|
3797
|
-
}
|
|
3798
|
-
function walkSome(node, pred) {
|
|
3799
|
-
if (pred(node)) return true;
|
|
3800
|
-
switch (node.kind) {
|
|
3801
|
-
case "sequence": return node.attrs.nodes.some((n) => walkSome(n, pred));
|
|
3802
|
-
case "alternative": return node.attrs.alts.some((n) => walkSome(n, pred));
|
|
3803
|
-
case "optional": return walkSome(node.attrs.node, pred);
|
|
3804
|
-
case "repeat": return walkSome(node.attrs.node, pred);
|
|
3805
|
-
default: return false;
|
|
3806
|
-
}
|
|
3807
|
-
}
|
|
3808
3904
|
const IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3809
3905
|
/** A name rendered where an identifier is expected: bare when it is a valid
|
|
3810
3906
|
* identifier, otherwise a double-quoted form preserving it exactly. Used for
|
|
@@ -3901,7 +3997,7 @@ var ArgtypeEmitter = class {
|
|
|
3901
3997
|
};
|
|
3902
3998
|
if (raw.meta) root.meta = raw.meta;
|
|
3903
3999
|
}
|
|
3904
|
-
const frontmatter = this.emitFrontmatter(app, exe
|
|
4000
|
+
const frontmatter = this.emitFrontmatter(app, exe);
|
|
3905
4001
|
if (frontmatter) lines.push(frontmatter, "");
|
|
3906
4002
|
const synthetic = syntheticWrap(root);
|
|
3907
4003
|
const defNode = synthetic ? synthetic.child : root;
|
|
@@ -4101,7 +4197,7 @@ var ArgtypeEmitter = class {
|
|
|
4101
4197
|
fmScalar(value) {
|
|
4102
4198
|
return quote(value);
|
|
4103
4199
|
}
|
|
4104
|
-
emitFrontmatter(app, exe
|
|
4200
|
+
emitFrontmatter(app, exe) {
|
|
4105
4201
|
const lines = [];
|
|
4106
4202
|
if (exe !== void 0) lines.push(`exe: ${this.fmScalar(exe)}`);
|
|
4107
4203
|
if (app) {
|
|
@@ -4130,14 +4226,6 @@ var ArgtypeEmitter = class {
|
|
|
4130
4226
|
if (app.stderr) this.emitStream(lines, "stderr", app.stderr);
|
|
4131
4227
|
if (app.doc?.comment) this.warn("AppMeta.doc.comment has no argtype surface; ignored");
|
|
4132
4228
|
}
|
|
4133
|
-
const extensions = [];
|
|
4134
|
-
if (outputs) extensions.push("outputs");
|
|
4135
|
-
if (mediaTypes) extensions.push("mediatypes");
|
|
4136
|
-
if (paths) extensions.push("paths");
|
|
4137
|
-
if (extensions.length > 0) {
|
|
4138
|
-
lines.push("extensions:");
|
|
4139
|
-
for (const e of extensions) lines.push(` - ${e}`);
|
|
4140
|
-
}
|
|
4141
4229
|
if (lines.length === 0) return void 0;
|
|
4142
4230
|
return [
|
|
4143
4231
|
"---",
|
|
@@ -4279,6 +4367,7 @@ function resolveFieldBinding(node, ctx, structType, outermost) {
|
|
|
4279
4367
|
* sequence is the actual struct owner (e.g. `seq(lit("--flag"), seq(field1, field2))`).
|
|
4280
4368
|
*/
|
|
4281
4369
|
function findStructNode(node, ctx, structType) {
|
|
4370
|
+
if (node.kind === "sequence" && Object.keys(structType.fields).length === 0) return node;
|
|
4282
4371
|
switch (node.kind) {
|
|
4283
4372
|
case "sequence":
|
|
4284
4373
|
for (const child of node.attrs.nodes) {
|
|
@@ -4484,53 +4573,6 @@ function formatType(type, indent = 0) {
|
|
|
4484
4573
|
}
|
|
4485
4574
|
}
|
|
4486
4575
|
|
|
4487
|
-
//#endregion
|
|
4488
|
-
//#region src/backend/resolve-output-tokens.ts
|
|
4489
|
-
/**
|
|
4490
|
-
* Compact consecutive literal tokens. Backends that emit string concatenation
|
|
4491
|
-
* benefit from a shorter token stream; backends that template each token
|
|
4492
|
-
* individually can ignore this and use `output.tokens` directly.
|
|
4493
|
-
*/
|
|
4494
|
-
function compactTokens(tokens) {
|
|
4495
|
-
const out = [];
|
|
4496
|
-
for (const tok of tokens) {
|
|
4497
|
-
const last = out[out.length - 1];
|
|
4498
|
-
if (tok.kind === "literal" && last && last.kind === "literal") out[out.length - 1] = {
|
|
4499
|
-
kind: "literal",
|
|
4500
|
-
value: last.value + tok.value
|
|
4501
|
-
};
|
|
4502
|
-
else out.push(tok);
|
|
4503
|
-
}
|
|
4504
|
-
return out;
|
|
4505
|
-
}
|
|
4506
|
-
function planOutput(scopeGate, output, bindings) {
|
|
4507
|
-
return {
|
|
4508
|
-
name: output.name,
|
|
4509
|
-
gate: outputGate(scopeGate, output, bindings),
|
|
4510
|
-
tokens: compactTokens(output.tokens),
|
|
4511
|
-
resolved: output
|
|
4512
|
-
};
|
|
4513
|
-
}
|
|
4514
|
-
/**
|
|
4515
|
-
* Does the output have any conditional wrapper? Equivalent to "is at least one
|
|
4516
|
-
* atom a `present` or `variant`?". `iter` alone means the output emits a list
|
|
4517
|
-
* and is not conditionally absent.
|
|
4518
|
-
*/
|
|
4519
|
-
function isGated(plan) {
|
|
4520
|
-
return plan.gate.some((a) => a.kind === "present" || a.kind === "variant");
|
|
4521
|
-
}
|
|
4522
|
-
/** Does the output iterate (emit zero-or-more values)? */
|
|
4523
|
-
function isIterated(plan) {
|
|
4524
|
-
return plan.gate.some((a) => a.kind === "iter");
|
|
4525
|
-
}
|
|
4526
|
-
/**
|
|
4527
|
-
* Convenience for backends emitting all outputs of a scope at once. The caller
|
|
4528
|
-
* provides the scope's gate (typically `bindings.get(scope.scope)?.gate ?? []`).
|
|
4529
|
-
*/
|
|
4530
|
-
function planScope(scope, scopeGate, bindings) {
|
|
4531
|
-
return scope.outputs.map((output) => planOutput(scopeGate, output, bindings));
|
|
4532
|
-
}
|
|
4533
|
-
|
|
4534
4576
|
//#endregion
|
|
4535
4577
|
//#region src/backend/boutiques/boutiques.ts
|
|
4536
4578
|
var BoutiquesEmitter = class {
|
|
@@ -4746,14 +4788,12 @@ var BoutiquesEmitter = class {
|
|
|
4746
4788
|
if (flagStr) peeled.flag = flagStr;
|
|
4747
4789
|
}
|
|
4748
4790
|
const input = this.buildInput(binding, id, fieldType, valueKeyStr, peeled, fieldInfo, wrapperNode);
|
|
4749
|
-
|
|
4750
|
-
else commandParts.push(valueKeyStr);
|
|
4751
|
-
else commandParts.push(valueKeyStr);
|
|
4791
|
+
commandParts.push(valueKeyStr);
|
|
4752
4792
|
inputs.push(input);
|
|
4753
4793
|
}
|
|
4754
4794
|
bt["command-line"] = commandParts.join(" ");
|
|
4755
4795
|
bt.inputs = inputs;
|
|
4756
|
-
this.emitOutputFiles(bt,
|
|
4796
|
+
this.emitOutputFiles(bt, structNode, valueKeyByBinding, idScope);
|
|
4757
4797
|
}
|
|
4758
4798
|
emitOutputFiles(bt, scopeNode, valueKeys, idScope) {
|
|
4759
4799
|
const scopeBinding = this.ctx.resolve(scopeNode);
|
|
@@ -4846,6 +4886,7 @@ var BoutiquesEmitter = class {
|
|
|
4846
4886
|
}
|
|
4847
4887
|
finalizeInput(input) {
|
|
4848
4888
|
if (input.name === void 0) input.name = input.id;
|
|
4889
|
+
this.finalizeSubDescriptors(input);
|
|
4849
4890
|
if (input.type === "String" && typeof input["default-value"] === "boolean" && input["value-choices"] === void 0) input.type = "Flag";
|
|
4850
4891
|
if (input.type === "Flag") {
|
|
4851
4892
|
delete input["default-value"];
|
|
@@ -4872,6 +4913,15 @@ var BoutiquesEmitter = class {
|
|
|
4872
4913
|
if (Array.isArray(choices) && dv !== void 0 && !choices.some((c) => c === dv)) delete input["default-value"];
|
|
4873
4914
|
this.mergeDefaultIntoDescription(input);
|
|
4874
4915
|
}
|
|
4916
|
+
finalizeSubDescriptors(input) {
|
|
4917
|
+
const type = input.type;
|
|
4918
|
+
if (Array.isArray(type)) type.forEach((sub, i) => this.ensureSubDescriptorId(sub, `${input.id}_${i + 1}`));
|
|
4919
|
+
else if (typeof type === "object" && type !== null) this.ensureSubDescriptorId(type, input.id);
|
|
4920
|
+
}
|
|
4921
|
+
ensureSubDescriptorId(sub, fallback) {
|
|
4922
|
+
if (sub.id === void 0) sub.id = this.sanitizeId(fallback);
|
|
4923
|
+
if (sub.name === void 0) sub.name = sub.id;
|
|
4924
|
+
}
|
|
4875
4925
|
peelNode(node, type) {
|
|
4876
4926
|
const result = {
|
|
4877
4927
|
isOptional: false,
|
|
@@ -4895,6 +4945,8 @@ var BoutiquesEmitter = class {
|
|
|
4895
4945
|
this.peelNodeInner(node.attrs.node, type.kind === "list" ? type.item : type, result);
|
|
4896
4946
|
break;
|
|
4897
4947
|
case "sequence": {
|
|
4948
|
+
const structType = this.unwrapType(type);
|
|
4949
|
+
if (structType.kind === "struct" && findStructNode(node, this.ctx, structType) === node) break;
|
|
4898
4950
|
const nodes = node.attrs.nodes;
|
|
4899
4951
|
if (nodes.length === 2 && nodes[0].kind === "literal") {
|
|
4900
4952
|
const flagLit = nodes[0].attrs.str;
|
|
@@ -4983,8 +5035,7 @@ var BoutiquesEmitter = class {
|
|
|
4983
5035
|
type: "String",
|
|
4984
5036
|
valueChoices: type.variants.map((v) => v.type.kind === "literal" ? v.type.value : "")
|
|
4985
5037
|
};
|
|
4986
|
-
|
|
4987
|
-
return { type: this.buildMixedUnionAsSubCommands(type, node) };
|
|
5038
|
+
return { type: this.buildUnionSubCommands(type, node) };
|
|
4988
5039
|
}
|
|
4989
5040
|
buildSubCommand(type, node) {
|
|
4990
5041
|
const bt = {};
|
|
@@ -4995,22 +5046,7 @@ var BoutiquesEmitter = class {
|
|
|
4995
5046
|
}
|
|
4996
5047
|
return bt;
|
|
4997
5048
|
}
|
|
4998
|
-
|
|
4999
|
-
const alts = node.kind === "alternative" ? node.attrs.alts : [node];
|
|
5000
|
-
return type.variants.map((variant, i) => {
|
|
5001
|
-
const altNode = alts[i] ?? node;
|
|
5002
|
-
if (variant.type.kind === "struct") {
|
|
5003
|
-
const bt = this.buildSubCommand(variant.type, altNode);
|
|
5004
|
-
if (variant.name) {
|
|
5005
|
-
bt.name = variant.name;
|
|
5006
|
-
bt.id = this.sanitizeId(variant.name);
|
|
5007
|
-
}
|
|
5008
|
-
return bt;
|
|
5009
|
-
}
|
|
5010
|
-
return this.wrapAsDescriptor(variant, altNode);
|
|
5011
|
-
});
|
|
5012
|
-
}
|
|
5013
|
-
buildMixedUnionAsSubCommands(type, node) {
|
|
5049
|
+
buildUnionSubCommands(type, node) {
|
|
5014
5050
|
const alts = node.kind === "alternative" ? node.attrs.alts : [node];
|
|
5015
5051
|
return type.variants.map((variant, i) => {
|
|
5016
5052
|
const altNode = alts[i] ?? node;
|
|
@@ -5109,7 +5145,8 @@ var BoutiquesEmitter = class {
|
|
|
5109
5145
|
inputs: []
|
|
5110
5146
|
},
|
|
5111
5147
|
list: true,
|
|
5112
|
-
minListEntries: countMin
|
|
5148
|
+
minListEntries: countMin,
|
|
5149
|
+
...countMin === 0 && { optional: true }
|
|
5113
5150
|
};
|
|
5114
5151
|
}
|
|
5115
5152
|
};
|
|
@@ -5315,9 +5352,65 @@ function resolveTypeName(namedTypes) {
|
|
|
5315
5352
|
};
|
|
5316
5353
|
}
|
|
5317
5354
|
|
|
5355
|
+
//#endregion
|
|
5356
|
+
//#region src/backend/field-defaults.ts
|
|
5357
|
+
/**
|
|
5358
|
+
* Build the field-name -> rendered-default map for a struct root (else empty).
|
|
5359
|
+
* Includes only non-optional defaulted fields (optional fields are
|
|
5360
|
+
* presence-guarded; their default comes from the factory's kwarg signature).
|
|
5361
|
+
*
|
|
5362
|
+
* `rootType` defaults to the resolved root type; callers that already have it
|
|
5363
|
+
* (the arg-builders) pass it to avoid re-resolving.
|
|
5364
|
+
*/
|
|
5365
|
+
function collectDefaults(ctx, renderLiteral, rootType = ctx.resolve(ctx.expr)?.type) {
|
|
5366
|
+
const out = /* @__PURE__ */ new Map();
|
|
5367
|
+
if (rootType?.kind !== "struct") return out;
|
|
5368
|
+
for (const [name, fi] of collectFieldInfo(ctx, rootType)) {
|
|
5369
|
+
if (fi.defaultValue === void 0) continue;
|
|
5370
|
+
if (rootType.fields[name]?.kind === "optional") continue;
|
|
5371
|
+
out.set(name, renderLiteral(fi.defaultValue));
|
|
5372
|
+
}
|
|
5373
|
+
return out;
|
|
5374
|
+
}
|
|
5375
|
+
/** The rendered default for a binding iff it is a root-level defaulted field. */
|
|
5376
|
+
function rootFieldDefault(binding, defaults) {
|
|
5377
|
+
if (!binding) return void 0;
|
|
5378
|
+
const a = binding.access;
|
|
5379
|
+
if (a.length === 1 && a[0]?.kind === "field") return defaults.get(binding.name);
|
|
5380
|
+
}
|
|
5381
|
+
|
|
5318
5382
|
//#endregion
|
|
5319
5383
|
//#region src/backend/union-variants.ts
|
|
5320
5384
|
/**
|
|
5385
|
+
* Whether a union is "mixed": it has at least one non-struct (bare-literal) arm
|
|
5386
|
+
* alongside its struct variants (e.g. ants `n4_correction = Literal[0] | N4On`).
|
|
5387
|
+
*
|
|
5388
|
+
* The `@type` discriminator only exists on the struct arms, so indexing
|
|
5389
|
+
* `value["@type"]` on the union value is unsound (a type error, and a runtime
|
|
5390
|
+
* KeyError if the literal arm is ever hit) until the value is narrowed to a
|
|
5391
|
+
* struct at runtime. Backends that dispatch on `@type` must first emit a shape
|
|
5392
|
+
* guard (Python `isinstance(value, dict)`, TS `typeof value === "object"`) when
|
|
5393
|
+
* this is true; a pure-struct union (every arm a struct) needs no guard.
|
|
5394
|
+
*/
|
|
5395
|
+
function unionIsMixed(unionType) {
|
|
5396
|
+
return unionType.variants.some((v) => v.type.kind !== "struct");
|
|
5397
|
+
}
|
|
5398
|
+
/**
|
|
5399
|
+
* The union type bound by a `variant` gate atom, if the atom's binding resolves
|
|
5400
|
+
* to a union - used by the outputs emitters to decide whether the variant gate
|
|
5401
|
+
* needs a mixed-union shape guard before its `@type` check. Returns `undefined`
|
|
5402
|
+
* for a well-formed non-union (defensive; a variant atom should always name a
|
|
5403
|
+
* union binding).
|
|
5404
|
+
*/
|
|
5405
|
+
function variantAtomUnion(atom, bindings) {
|
|
5406
|
+
return resolveUnion(bindings.get(atom.binding)?.type);
|
|
5407
|
+
}
|
|
5408
|
+
function resolveUnion(type) {
|
|
5409
|
+
if (!type) return void 0;
|
|
5410
|
+
if (type.kind === "optional") return resolveUnion(type.inner);
|
|
5411
|
+
if (type.kind === "union") return type;
|
|
5412
|
+
}
|
|
5413
|
+
/**
|
|
5321
5414
|
* The struct variants of a union, each with its index into `variants`.
|
|
5322
5415
|
*
|
|
5323
5416
|
* A discriminated union dispatches on a unique `@type`, so two struct variants
|
|
@@ -5455,10 +5548,6 @@ function pathArg$1(node, expr) {
|
|
|
5455
5548
|
* carrying a Boutiques default. Restricted to single-segment (root) field access
|
|
5456
5549
|
* so a nested field can never accidentally pick up a same-named root default.
|
|
5457
5550
|
*/
|
|
5458
|
-
function rootFieldDefault$3(binding, defaults) {
|
|
5459
|
-
const a = binding.access;
|
|
5460
|
-
if (a.length === 1 && a[0]?.kind === "field") return defaults.get(binding.name);
|
|
5461
|
-
}
|
|
5462
5551
|
/**
|
|
5463
5552
|
* Render a binding's access for an UNCONDITIONAL value read (terminal, repeat
|
|
5464
5553
|
* loop, alternative dispatch): substitutes the field's default via
|
|
@@ -5469,7 +5558,7 @@ function rootFieldDefault$3(binding, defaults) {
|
|
|
5469
5558
|
* via `.get()`).
|
|
5470
5559
|
*/
|
|
5471
5560
|
function readAccess$1(binding, arg) {
|
|
5472
|
-
const def = rootFieldDefault
|
|
5561
|
+
const def = rootFieldDefault(binding, arg.defaults);
|
|
5473
5562
|
return accessOf$1(binding, arg, def !== void 0 ? { finalDefault: def } : {});
|
|
5474
5563
|
}
|
|
5475
5564
|
/**
|
|
@@ -5489,21 +5578,6 @@ function accessOf$1(binding, arg, opts = {}) {
|
|
|
5489
5578
|
subst: arg.valueSubst
|
|
5490
5579
|
});
|
|
5491
5580
|
}
|
|
5492
|
-
/**
|
|
5493
|
-
* Build the field-name -> rendered-default map for a struct root (else empty).
|
|
5494
|
-
* Includes only non-optional defaulted fields (optional fields are
|
|
5495
|
-
* presence-guarded; their default comes from the factory's kwarg signature).
|
|
5496
|
-
*/
|
|
5497
|
-
function collectDefaults$3(ctx, rootType) {
|
|
5498
|
-
const out = /* @__PURE__ */ new Map();
|
|
5499
|
-
if (rootType?.kind !== "struct") return out;
|
|
5500
|
-
for (const [name, fi] of collectFieldInfo(ctx, rootType)) {
|
|
5501
|
-
if (fi.defaultValue === void 0) continue;
|
|
5502
|
-
if (rootType.fields[name]?.kind === "optional") continue;
|
|
5503
|
-
out.set(name, renderPyLiteral(fi.defaultValue));
|
|
5504
|
-
}
|
|
5505
|
-
return out;
|
|
5506
|
-
}
|
|
5507
5581
|
let loopVarCounter$1 = 0;
|
|
5508
5582
|
let optVarCounter = 0;
|
|
5509
5583
|
/**
|
|
@@ -5519,7 +5593,7 @@ function buildArgs$1(rootExpr, ctx, rootType) {
|
|
|
5519
5593
|
joinDepth: 0,
|
|
5520
5594
|
loopVars: /* @__PURE__ */ new Map(),
|
|
5521
5595
|
valueSubst: /* @__PURE__ */ new Map(),
|
|
5522
|
-
defaults: collectDefaults
|
|
5596
|
+
defaults: collectDefaults(ctx, renderPyLiteral, rootType)
|
|
5523
5597
|
});
|
|
5524
5598
|
}
|
|
5525
5599
|
function walk$2(node, ctx, arg) {
|
|
@@ -5679,8 +5753,8 @@ function walkAlternative$1(node, ctx, arg) {
|
|
|
5679
5753
|
*/
|
|
5680
5754
|
function emitDocstring(cb, text) {
|
|
5681
5755
|
if (!text) return;
|
|
5682
|
-
const lines = text.split("\n");
|
|
5683
|
-
if (lines.length === 1 && !lines[0].includes("\"")) {
|
|
5756
|
+
const lines = text.replace(/"""/g, "\\\"\\\"\\\"").split("\n");
|
|
5757
|
+
if (lines.length === 1 && !lines[0].includes("\"") && !lines[0].endsWith("\\")) {
|
|
5684
5758
|
cb.line(`"""${lines[0]}"""`);
|
|
5685
5759
|
return;
|
|
5686
5760
|
}
|
|
@@ -5688,20 +5762,18 @@ function emitDocstring(cb, text) {
|
|
|
5688
5762
|
for (const line of lines) cb.line(line);
|
|
5689
5763
|
cb.line(`"""`);
|
|
5690
5764
|
}
|
|
5691
|
-
function emitImports$1(cb
|
|
5692
|
-
cb.line("import dataclasses");
|
|
5765
|
+
function emitImports$1(cb) {
|
|
5693
5766
|
cb.line("import pathlib");
|
|
5694
5767
|
cb.line("import typing");
|
|
5695
5768
|
cb.blank();
|
|
5696
|
-
|
|
5769
|
+
cb.line(`from styxdefs import ${[
|
|
5697
5770
|
"Execution",
|
|
5698
5771
|
"InputPathType",
|
|
5699
5772
|
"Metadata",
|
|
5700
5773
|
"Runner",
|
|
5701
|
-
"StyxValidationError"
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
cb.line(`from styxdefs import ${fromStyxdefs.join(", ")}, get_global_runner`);
|
|
5774
|
+
"StyxValidationError",
|
|
5775
|
+
"OutputPathType"
|
|
5776
|
+
].join(", ")}, get_global_runner`);
|
|
5705
5777
|
}
|
|
5706
5778
|
function emitMetadata$1(ctx, metaConst, cb) {
|
|
5707
5779
|
const id = ctx.app?.id ?? "unknown";
|
|
@@ -6203,9 +6275,10 @@ function renderInputTrait(p) {
|
|
|
6203
6275
|
case "str": return call("traits.Str", [...hasDef ? [renderPyLiteral(def), "usedefault=True"] : [], ...tail]);
|
|
6204
6276
|
case "enum": {
|
|
6205
6277
|
const choices = p.choices ?? [];
|
|
6278
|
+
const defChoice = hasDef && def !== void 0 && typeof def !== "boolean" && choices.includes(def) ? def : void 0;
|
|
6206
6279
|
return call("traits.Enum", [
|
|
6207
|
-
...(
|
|
6208
|
-
...
|
|
6280
|
+
...(defChoice !== void 0 ? [defChoice, ...choices.filter((c) => c !== defChoice)] : choices).map((c) => renderPyLiteral(c)),
|
|
6281
|
+
...defChoice !== void 0 ? ["usedefault=True"] : [],
|
|
6209
6282
|
...tail
|
|
6210
6283
|
]);
|
|
6211
6284
|
}
|
|
@@ -6685,7 +6758,7 @@ function emitValue$1(e, type, node, wireKey, valueExpr, expected) {
|
|
|
6685
6758
|
const itemNode = findRepeatNode(node)?.attrs.node;
|
|
6686
6759
|
const elem = e.scope.add("e");
|
|
6687
6760
|
e.cb.line(`for ${elem} in ${valueExpr}:`);
|
|
6688
|
-
e.cb.indent(() => emitValue$1(e, type.item, itemNode, wireKey, elem,
|
|
6761
|
+
e.cb.indent(() => emitValue$1(e, type.item, itemNode, wireKey, elem, expectedType$1(e, type.item)));
|
|
6689
6762
|
return;
|
|
6690
6763
|
}
|
|
6691
6764
|
case "struct":
|
|
@@ -6972,10 +7045,9 @@ function streamFieldIds$1(ctx) {
|
|
|
6972
7045
|
if (ctx.app?.stderr) res.stderr = fields[idx++].id;
|
|
6973
7046
|
return res;
|
|
6974
7047
|
}
|
|
6975
|
-
/** Emit
|
|
7048
|
+
/** Emit `class <outputsType>(typing.NamedTuple):` declaration. */
|
|
6976
7049
|
function emitOutputsClass(ctx, outputsType, cb) {
|
|
6977
|
-
cb.line(
|
|
6978
|
-
cb.line(`class ${outputsType}:`);
|
|
7050
|
+
cb.line(`class ${outputsType}(typing.NamedTuple):`);
|
|
6979
7051
|
cb.indent(() => {
|
|
6980
7052
|
emitDocstring(cb, "Output paths produced by the tool.");
|
|
6981
7053
|
const fields = collectOutputFields(ctx, pyId);
|
|
@@ -6994,26 +7066,6 @@ function emitOutputsClass(ctx, outputsType, cb) {
|
|
|
6994
7066
|
}
|
|
6995
7067
|
});
|
|
6996
7068
|
}
|
|
6997
|
-
/** The rendered default for a binding iff it is a root-level defaulted field. */
|
|
6998
|
-
function rootFieldDefault$2(binding, defaults) {
|
|
6999
|
-
if (!binding) return void 0;
|
|
7000
|
-
const a = binding.access;
|
|
7001
|
-
if (a.length === 1 && a[0]?.kind === "field") return defaults.get(binding.name);
|
|
7002
|
-
}
|
|
7003
|
-
/** Build the field-name -> rendered-default map for the struct root (else empty).
|
|
7004
|
-
* Includes only non-optional defaulted fields (optional fields are
|
|
7005
|
-
* presence-guarded; their default comes from the factory's kwarg signature). */
|
|
7006
|
-
function collectDefaults$2(ctx) {
|
|
7007
|
-
const out = /* @__PURE__ */ new Map();
|
|
7008
|
-
const rootType = ctx.resolve(ctx.expr)?.type;
|
|
7009
|
-
if (rootType?.kind !== "struct") return out;
|
|
7010
|
-
for (const [name, fi] of collectFieldInfo(ctx, rootType)) {
|
|
7011
|
-
if (fi.defaultValue === void 0) continue;
|
|
7012
|
-
if (rootType.fields[name]?.kind === "optional") continue;
|
|
7013
|
-
out.set(name, renderPyLiteral(fi.defaultValue));
|
|
7014
|
-
}
|
|
7015
|
-
return out;
|
|
7016
|
-
}
|
|
7017
7069
|
let loopCounter$1 = 0;
|
|
7018
7070
|
function renderWrapperOpen$1(atom, ec) {
|
|
7019
7071
|
if (atom.kind === "iter") {
|
|
@@ -7024,7 +7076,13 @@ function renderWrapperOpen$1(atom, ec) {
|
|
|
7024
7076
|
loopVar: v
|
|
7025
7077
|
};
|
|
7026
7078
|
}
|
|
7027
|
-
if (atom.kind === "variant")
|
|
7079
|
+
if (atom.kind === "variant") {
|
|
7080
|
+
const access = bindingAccess$1(atom.binding, ec);
|
|
7081
|
+
const check = `${access}["@type"] == ${pyStr(atom.variant)}`;
|
|
7082
|
+
const union = variantAtomUnion(atom, ec.ctx.bindings);
|
|
7083
|
+
if (union && unionIsMixed(union)) return { open: `if isinstance(${access}, dict) and ${check}:` };
|
|
7084
|
+
return { open: `if ${check}:` };
|
|
7085
|
+
}
|
|
7028
7086
|
const binding = ec.ctx.bindings.get(atom.binding);
|
|
7029
7087
|
if (binding?.type.kind === "optional") {
|
|
7030
7088
|
const subscriptAccess = bindingAccess$1(atom.binding, ec);
|
|
@@ -7080,7 +7138,7 @@ function renderToken$1(tok, ec) {
|
|
|
7080
7138
|
return renderRefValue$1(tok, ec);
|
|
7081
7139
|
}
|
|
7082
7140
|
function renderRefValue$1(tok, ec) {
|
|
7083
|
-
const def = rootFieldDefault
|
|
7141
|
+
const def = rootFieldDefault(ec.ctx.bindings.get(tok.binding), ec.defaults);
|
|
7084
7142
|
let expr = def !== void 0 && !ec.iter.has(tok.binding) ? bindingAccess$1(tok.binding, ec, false, def) : bindingAccess$1(tok.binding, ec);
|
|
7085
7143
|
if (tok.fallback !== void 0) expr = `(${expr} if ${expr} is not None else ${pyStr(tok.fallback)})`;
|
|
7086
7144
|
if (tok.stripExtensions && tok.stripExtensions.length > 0) {
|
|
@@ -7143,7 +7201,7 @@ function emitBuildOutputs$1(ctx, paramsType, outputsType, funcName, cb) {
|
|
|
7143
7201
|
ctx,
|
|
7144
7202
|
iter: /* @__PURE__ */ new Map(),
|
|
7145
7203
|
subst: /* @__PURE__ */ new Map(),
|
|
7146
|
-
defaults: collectDefaults
|
|
7204
|
+
defaults: collectDefaults(ctx, renderPyLiteral)
|
|
7147
7205
|
};
|
|
7148
7206
|
const fields = collectOutputFields(ctx, pyId);
|
|
7149
7207
|
const localVarOf = /* @__PURE__ */ new Map();
|
|
@@ -7180,11 +7238,16 @@ function emitBuildOutputs$1(ctx, paramsType, outputsType, funcName, cb) {
|
|
|
7180
7238
|
}
|
|
7181
7239
|
});
|
|
7182
7240
|
}
|
|
7183
|
-
/**
|
|
7241
|
+
/**
|
|
7242
|
+
* Sanitize an output name to a valid Python identifier. Uses a *letter*-leading
|
|
7243
|
+
* prefix (`v_`) for digit-leading / empty names, never a leading underscore:
|
|
7244
|
+
* the Outputs type is a `typing.NamedTuple`, which raises `ValueError` at import
|
|
7245
|
+
* time for a field whose name starts with `_`. Matches styx1 and `pyScrubIdent`.
|
|
7246
|
+
* (A trailing underscore for keywords is fine - only leading underscores fail.)
|
|
7247
|
+
*/
|
|
7184
7248
|
function pyId(name) {
|
|
7185
7249
|
let s = name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
7186
|
-
if (/^\d/.test(s)) s = "
|
|
7187
|
-
if (s === "") s = "_";
|
|
7250
|
+
if (/^\d/.test(s) || s === "") s = "v_" + s;
|
|
7188
7251
|
if (PY_KEYWORDS.has(s)) s = s + "_";
|
|
7189
7252
|
return s;
|
|
7190
7253
|
}
|
|
@@ -7370,13 +7433,22 @@ function buildEmitModel(ctx, scope = new Scope(PY_RESERVED)) {
|
|
|
7370
7433
|
};
|
|
7371
7434
|
}
|
|
7372
7435
|
function generatePython(ctx, packageScope) {
|
|
7436
|
+
return generatePythonModule(ctx, packageScope).code;
|
|
7437
|
+
}
|
|
7438
|
+
/**
|
|
7439
|
+
* Emit the module and, alongside it, the dispatch entrypoint carrying the
|
|
7440
|
+
* *scope-registered* execute-function name. Computing the entrypoint here (not
|
|
7441
|
+
* via the scope-blind `appEntrypoint`) keeps the suite dispatcher in sync with
|
|
7442
|
+
* the actual emitted symbol when a shared package scope suffix-bumps a collision.
|
|
7443
|
+
*/
|
|
7444
|
+
function generatePythonModule(ctx, packageScope) {
|
|
7373
7445
|
const cb = new CodeBuilder(" ");
|
|
7374
7446
|
const scope = packageScope ?? new Scope(PY_RESERVED);
|
|
7375
7447
|
const { names, rootType, rootIsStruct, namedTypes, typeDecls, rootTypeTag, paramsType, sigEntries, nestedFactories } = buildEmitModel(ctx, scope);
|
|
7376
7448
|
cb.comment("This file was auto generated by Styx.", "# ");
|
|
7377
7449
|
cb.comment("Do not edit this file directly.", "# ");
|
|
7378
7450
|
cb.blank();
|
|
7379
|
-
emitImports$1(cb
|
|
7451
|
+
emitImports$1(cb);
|
|
7380
7452
|
cb.blank();
|
|
7381
7453
|
emitMetadata$1(ctx, names.metadata, cb);
|
|
7382
7454
|
cb.blank();
|
|
@@ -7401,7 +7473,8 @@ function generatePython(ctx, packageScope) {
|
|
|
7401
7473
|
cb.blank();
|
|
7402
7474
|
emitBuildOutputs$1(ctx, paramsType, names.outputs, names.outputsFn, cb);
|
|
7403
7475
|
cb.blank();
|
|
7404
|
-
|
|
7476
|
+
const executeName = rootIsStruct ? names.execute : names.wrapper;
|
|
7477
|
+
emitWrapperFunction$1(ctx, paramsType, executeName, names.metadata, names.cargs, names.outputsFn, names.outputs, names.validate, streamFieldIds$1(ctx), cb);
|
|
7405
7478
|
cb.blank();
|
|
7406
7479
|
if (rootIsStruct) {
|
|
7407
7480
|
emitKwargWrapper$1(ctx, sigEntries, names.wrapper, names.paramsFn, names.execute, names.outputs, cb);
|
|
@@ -7409,10 +7482,10 @@ function generatePython(ctx, packageScope) {
|
|
|
7409
7482
|
}
|
|
7410
7483
|
const publicSymbols = [
|
|
7411
7484
|
names.params,
|
|
7412
|
-
|
|
7485
|
+
names.outputs,
|
|
7413
7486
|
names.metadata,
|
|
7414
7487
|
names.cargs,
|
|
7415
|
-
|
|
7488
|
+
names.outputsFn,
|
|
7416
7489
|
...rootIsStruct ? [names.paramsFn, names.execute] : [],
|
|
7417
7490
|
...nestedFactories.map((nf) => nf.funcName),
|
|
7418
7491
|
names.validate,
|
|
@@ -7423,7 +7496,16 @@ function generatePython(ctx, packageScope) {
|
|
|
7423
7496
|
for (const sym of publicSymbols) cb.line(`"${sym}",`);
|
|
7424
7497
|
});
|
|
7425
7498
|
cb.line("]");
|
|
7426
|
-
|
|
7499
|
+
const appId = ctx.app?.id;
|
|
7500
|
+
const pkg = ctx.package?.name;
|
|
7501
|
+
const entrypoint = appId && pkg ? {
|
|
7502
|
+
type: `${pkg}/${appId}`,
|
|
7503
|
+
executeFn: executeName
|
|
7504
|
+
} : void 0;
|
|
7505
|
+
return {
|
|
7506
|
+
code: cb.toString(),
|
|
7507
|
+
entrypoint
|
|
7508
|
+
};
|
|
7427
7509
|
}
|
|
7428
7510
|
/**
|
|
7429
7511
|
* Module name (file stem) for an app: snake_case of app.id, fallback `output`.
|
|
@@ -7435,22 +7517,6 @@ function appModuleName$1(meta) {
|
|
|
7435
7517
|
return pyScrubIdent(snakeCase(meta.id), PY_RESERVED);
|
|
7436
7518
|
}
|
|
7437
7519
|
/**
|
|
7438
|
-
* The dispatch entrypoint for one app: its root `@type` (`<package>/<app>`) and
|
|
7439
|
-
* the dict-style execute function name. Returns undefined when the id or package
|
|
7440
|
-
* is unknown (no stable `@type`), so the app is left out of the suite dispatcher.
|
|
7441
|
-
*/
|
|
7442
|
-
function appEntrypoint$1(ctx) {
|
|
7443
|
-
const appId = ctx.app?.id;
|
|
7444
|
-
const pkg = ctx.package?.name;
|
|
7445
|
-
if (!appId || !pkg) return void 0;
|
|
7446
|
-
const publicNames = computePublicNames$1(appId);
|
|
7447
|
-
const executeFn = pyScrubIdent(ctx.resolve(ctx.expr)?.type.kind === "struct" ? publicNames.execute : publicNames.wrapper, PY_RESERVED);
|
|
7448
|
-
return {
|
|
7449
|
-
type: `${pkg}/${appId}`,
|
|
7450
|
-
executeFn
|
|
7451
|
-
};
|
|
7452
|
-
}
|
|
7453
|
-
/**
|
|
7454
7520
|
* Generate the suite-level `__init__.py` re-export for a package containing
|
|
7455
7521
|
* multiple tool modules. Each tool module's public symbols are surfaced via
|
|
7456
7522
|
* `from .bet import *` (each tool file defines `__all__`). When apps carry a
|
|
@@ -7487,10 +7553,11 @@ function emitPackageDispatch$1(cb, dispatch) {
|
|
|
7487
7553
|
for (const e of dispatch) cb.line(`${JSON.stringify(e.type)}: ${e.executeFn},`);
|
|
7488
7554
|
});
|
|
7489
7555
|
cb.line("}");
|
|
7490
|
-
cb.line("
|
|
7556
|
+
cb.line("_type = params.get(\"@type\")");
|
|
7557
|
+
cb.line("_fn = _dispatch.get(_type) if _type is not None else None");
|
|
7491
7558
|
cb.line("if _fn is None:");
|
|
7492
7559
|
cb.indent(() => {
|
|
7493
|
-
cb.line(`raise ValueError(f"No tool registered for @type {
|
|
7560
|
+
cb.line(`raise ValueError(f"No tool registered for @type {_type!r}")`);
|
|
7494
7561
|
});
|
|
7495
7562
|
cb.line("return _fn(params, runner)");
|
|
7496
7563
|
});
|
|
@@ -7499,11 +7566,11 @@ var PythonBackend = class {
|
|
|
7499
7566
|
name = "python";
|
|
7500
7567
|
target = "python";
|
|
7501
7568
|
emitApp(ctx, scope) {
|
|
7502
|
-
const code =
|
|
7569
|
+
const { code, entrypoint } = generatePythonModule(ctx, scope);
|
|
7503
7570
|
const fileName = `${appModuleName$1(ctx.app)}.py`;
|
|
7504
7571
|
return {
|
|
7505
7572
|
meta: ctx.app,
|
|
7506
|
-
entrypoint
|
|
7573
|
+
entrypoint,
|
|
7507
7574
|
files: new Map([[fileName, code]]),
|
|
7508
7575
|
errors: [],
|
|
7509
7576
|
warnings: []
|
|
@@ -8497,6 +8564,10 @@ var JsonSchemaBackend = class {
|
|
|
8497
8564
|
|
|
8498
8565
|
//#endregion
|
|
8499
8566
|
//#region src/backend/typescript/typemap.ts
|
|
8567
|
+
const TS_IDENT_RE$1 = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
8568
|
+
function objKey(key) {
|
|
8569
|
+
return TS_IDENT_RE$1.test(key) ? key : JSON.stringify(key);
|
|
8570
|
+
}
|
|
8500
8571
|
function mapType(type, resolve) {
|
|
8501
8572
|
switch (type.kind) {
|
|
8502
8573
|
case "scalar": return {
|
|
@@ -8516,7 +8587,7 @@ function mapType(type, resolve) {
|
|
|
8516
8587
|
case "struct": {
|
|
8517
8588
|
const name = resolve(type);
|
|
8518
8589
|
if (name) return name;
|
|
8519
|
-
return `{ ${Object.entries(type.fields).filter(([, v]) => v.kind !== "literal").map(([k, v]) => `${k}: ${mapType(v, resolve)}`).join("; ")} }`;
|
|
8590
|
+
return `{ ${Object.entries(type.fields).filter(([, v]) => v.kind !== "literal").map(([k, v]) => `${objKey(k)}: ${mapType(v, resolve)}`).join("; ")} }`;
|
|
8520
8591
|
}
|
|
8521
8592
|
case "union": {
|
|
8522
8593
|
const name = resolve(type);
|
|
@@ -8678,10 +8749,6 @@ function accessOf(binding, arg) {
|
|
|
8678
8749
|
* carrying a Boutiques default. Restricted to single-segment (root) field access
|
|
8679
8750
|
* so a nested field can never accidentally pick up a same-named root default.
|
|
8680
8751
|
*/
|
|
8681
|
-
function rootFieldDefault$1(binding, defaults) {
|
|
8682
|
-
const a = binding.access;
|
|
8683
|
-
if (a.length === 1 && a[0]?.kind === "field") return defaults.get(binding.name);
|
|
8684
|
-
}
|
|
8685
8752
|
/**
|
|
8686
8753
|
* Render a binding's access for an UNCONDITIONAL value read (terminal, repeat
|
|
8687
8754
|
* loop, alternative dispatch): substitutes the field's default via
|
|
@@ -8690,22 +8757,9 @@ function rootFieldDefault$1(binding, defaults) {
|
|
|
8690
8757
|
* emitted code is byte-identical to before for the common case.
|
|
8691
8758
|
*/
|
|
8692
8759
|
function readAccess(binding, arg) {
|
|
8693
|
-
const def = rootFieldDefault
|
|
8760
|
+
const def = rootFieldDefault(binding, arg.defaults);
|
|
8694
8761
|
return def !== void 0 ? `(${accessOf(binding, arg)} ?? ${def})` : accessOf(binding, arg);
|
|
8695
8762
|
}
|
|
8696
|
-
/** Build the field-name -> rendered-default map for a struct root (else empty).
|
|
8697
|
-
* Includes only non-optional defaulted fields (optional fields are
|
|
8698
|
-
* presence-guarded; their default comes from the factory's kwarg signature). */
|
|
8699
|
-
function collectDefaults$1(ctx, rootType) {
|
|
8700
|
-
const out = /* @__PURE__ */ new Map();
|
|
8701
|
-
if (rootType?.kind !== "struct") return out;
|
|
8702
|
-
for (const [name, fi] of collectFieldInfo(ctx, rootType)) {
|
|
8703
|
-
if (fi.defaultValue === void 0) continue;
|
|
8704
|
-
if (rootType.fields[name]?.kind === "optional") continue;
|
|
8705
|
-
out.set(name, renderTsLiteral(fi.defaultValue));
|
|
8706
|
-
}
|
|
8707
|
-
return out;
|
|
8708
|
-
}
|
|
8709
8763
|
let loopVarCounter = 0;
|
|
8710
8764
|
/**
|
|
8711
8765
|
* Build arg-building code for an IR tree via recursive descent.
|
|
@@ -8718,7 +8772,7 @@ function buildArgs(rootExpr, ctx, rootType) {
|
|
|
8718
8772
|
return walk$1(rootExpr, ctx, {
|
|
8719
8773
|
joinDepth: 0,
|
|
8720
8774
|
loopVars: /* @__PURE__ */ new Map(),
|
|
8721
|
-
defaults: collectDefaults
|
|
8775
|
+
defaults: collectDefaults(ctx, renderTsLiteral, rootType)
|
|
8722
8776
|
});
|
|
8723
8777
|
}
|
|
8724
8778
|
function walk$1(node, ctx, arg) {
|
|
@@ -8873,7 +8927,7 @@ function walkAlternative(node, ctx, arg) {
|
|
|
8873
8927
|
//#region src/backend/typescript/emit.ts
|
|
8874
8928
|
function emitJsDoc(cb, description) {
|
|
8875
8929
|
if (!description) return;
|
|
8876
|
-
const lines = description.split("\n");
|
|
8930
|
+
const lines = description.replace(/\*\//g, "*\\/").split("\n");
|
|
8877
8931
|
if (lines.length === 1) cb.line(`/** ${lines[0]} */`);
|
|
8878
8932
|
else {
|
|
8879
8933
|
cb.line("/**");
|
|
@@ -8881,15 +8935,14 @@ function emitJsDoc(cb, description) {
|
|
|
8881
8935
|
cb.line(" */");
|
|
8882
8936
|
}
|
|
8883
8937
|
}
|
|
8884
|
-
function emitImports(cb
|
|
8885
|
-
|
|
8938
|
+
function emitImports(cb) {
|
|
8939
|
+
cb.line(`import type { ${[
|
|
8886
8940
|
"Runner",
|
|
8887
8941
|
"Execution",
|
|
8888
8942
|
"Metadata",
|
|
8889
|
-
"InputPathType"
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
cb.line(`import type { ${inputs.join(", ")} } from "styxdefs";`);
|
|
8943
|
+
"InputPathType",
|
|
8944
|
+
"OutputPathType"
|
|
8945
|
+
].join(", ")} } from "styxdefs";`);
|
|
8893
8946
|
cb.line("import { getGlobalRunner, StyxValidationError } from \"styxdefs\";");
|
|
8894
8947
|
}
|
|
8895
8948
|
function emitMetadata(ctx, metaConst, cb) {
|
|
@@ -9233,7 +9286,7 @@ function emitValue(e, type, node, wireKey, access, expected) {
|
|
|
9233
9286
|
const itemNode = findRepeatNode(node)?.attrs.node;
|
|
9234
9287
|
const elem = e.scope.add("el");
|
|
9235
9288
|
e.cb.line(`for (const ${elem} of ${access}) {`);
|
|
9236
|
-
e.cb.indent(() => emitValue(e, type.item, itemNode, wireKey, elem,
|
|
9289
|
+
e.cb.indent(() => emitValue(e, type.item, itemNode, wireKey, elem, expectedType(e, type.item)));
|
|
9237
9290
|
e.cb.line("}");
|
|
9238
9291
|
return;
|
|
9239
9292
|
}
|
|
@@ -9368,26 +9421,6 @@ function emitOutputsInterface(ctx, outputsType, cb) {
|
|
|
9368
9421
|
});
|
|
9369
9422
|
cb.line(`}`);
|
|
9370
9423
|
}
|
|
9371
|
-
/** The rendered default for a binding iff it is a root-level defaulted field. */
|
|
9372
|
-
function rootFieldDefault(binding, defaults) {
|
|
9373
|
-
if (!binding) return void 0;
|
|
9374
|
-
const a = binding.access;
|
|
9375
|
-
if (a.length === 1 && a[0]?.kind === "field") return defaults.get(binding.name);
|
|
9376
|
-
}
|
|
9377
|
-
/** Build the field-name -> rendered-default map for the struct root (else empty).
|
|
9378
|
-
* Includes only non-optional defaulted fields (optional fields are
|
|
9379
|
-
* presence-guarded; their default comes from the factory's kwarg signature). */
|
|
9380
|
-
function collectDefaults(ctx) {
|
|
9381
|
-
const out = /* @__PURE__ */ new Map();
|
|
9382
|
-
const rootType = ctx.resolve(ctx.expr)?.type;
|
|
9383
|
-
if (rootType?.kind !== "struct") return out;
|
|
9384
|
-
for (const [name, fi] of collectFieldInfo(ctx, rootType)) {
|
|
9385
|
-
if (fi.defaultValue === void 0) continue;
|
|
9386
|
-
if (rootType.fields[name]?.kind === "optional") continue;
|
|
9387
|
-
out.set(name, renderTsLiteral(fi.defaultValue));
|
|
9388
|
-
}
|
|
9389
|
-
return out;
|
|
9390
|
-
}
|
|
9391
9424
|
/**
|
|
9392
9425
|
* Render one output's wrapper stack and emit the assignment inside the
|
|
9393
9426
|
* innermost wrapper. Nesting is done via recursive callbacks so the
|
|
@@ -9431,10 +9464,15 @@ function renderWrapperOpen(atom, ec) {
|
|
|
9431
9464
|
loopVar: v
|
|
9432
9465
|
};
|
|
9433
9466
|
}
|
|
9434
|
-
if (atom.kind === "variant")
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9467
|
+
if (atom.kind === "variant") {
|
|
9468
|
+
const access = bindingAccess(atom.binding, ec);
|
|
9469
|
+
const check = `${access}["@type"] === ${JSON.stringify(atom.variant)}`;
|
|
9470
|
+
const union = variantAtomUnion(atom, ec.ctx.bindings);
|
|
9471
|
+
return {
|
|
9472
|
+
open: `if (${union && unionIsMixed(union) ? `typeof ${access} === "object" && ${access} !== null && ${check}` : check}) {`,
|
|
9473
|
+
close: `}`
|
|
9474
|
+
};
|
|
9475
|
+
}
|
|
9438
9476
|
const binding = ec.ctx.bindings.get(atom.binding);
|
|
9439
9477
|
const access = bindingAccess(atom.binding, ec);
|
|
9440
9478
|
return {
|
|
@@ -9506,7 +9544,7 @@ function emitBuildOutputs(ctx, paramsType, outputsType, funcName, cb) {
|
|
|
9506
9544
|
ctx,
|
|
9507
9545
|
iter: /* @__PURE__ */ new Map(),
|
|
9508
9546
|
fieldShapes: new Map(fields.map((f) => [f.id, f.shape])),
|
|
9509
|
-
defaults: collectDefaults(ctx)
|
|
9547
|
+
defaults: collectDefaults(ctx, renderTsLiteral)
|
|
9510
9548
|
};
|
|
9511
9549
|
cb.line(`const outputs: ${outputsType} = {`);
|
|
9512
9550
|
cb.indent(() => {
|
|
@@ -9676,13 +9714,22 @@ function buildEmitModel$1(ctx, scope = new Scope(TS_RESERVED)) {
|
|
|
9676
9714
|
};
|
|
9677
9715
|
}
|
|
9678
9716
|
function generateTypeScript(ctx, packageScope) {
|
|
9717
|
+
return generateTypeScriptModule(ctx, packageScope).code;
|
|
9718
|
+
}
|
|
9719
|
+
/**
|
|
9720
|
+
* Emit the module and, alongside it, the dispatch entrypoint carrying the
|
|
9721
|
+
* *scope-registered* execute-function name. Computing the entrypoint here (not
|
|
9722
|
+
* via the scope-blind `appEntrypoint`) keeps the suite dispatcher in sync with
|
|
9723
|
+
* the actual emitted symbol when a shared package scope suffix-bumps a collision.
|
|
9724
|
+
*/
|
|
9725
|
+
function generateTypeScriptModule(ctx, packageScope) {
|
|
9679
9726
|
const cb = new CodeBuilder(" ");
|
|
9680
9727
|
const scope = packageScope ?? new Scope(TS_RESERVED);
|
|
9681
9728
|
const { appId, pkg, names, rootType, rootIsStruct, namedTypes, typeDecls, rootTypeTag, paramsType, sigEntries } = buildEmitModel$1(ctx, scope);
|
|
9682
9729
|
cb.comment("This file was auto generated by Styx.");
|
|
9683
9730
|
cb.comment("Do not edit this file directly.");
|
|
9684
9731
|
cb.blank();
|
|
9685
|
-
emitImports(cb
|
|
9732
|
+
emitImports(cb);
|
|
9686
9733
|
cb.blank();
|
|
9687
9734
|
emitMetadata(ctx, names.metadata, cb);
|
|
9688
9735
|
cb.blank();
|
|
@@ -9703,13 +9750,23 @@ function generateTypeScript(ctx, packageScope) {
|
|
|
9703
9750
|
cb.blank();
|
|
9704
9751
|
emitBuildOutputs(ctx, paramsType, names.outputs, names.outputsFn, cb);
|
|
9705
9752
|
cb.blank();
|
|
9706
|
-
|
|
9753
|
+
const executeName = rootIsStruct ? names.execute : names.wrapper;
|
|
9754
|
+
emitWrapperFunction(ctx, paramsType, executeName, names.metadata, names.cargs, names.outputsFn, names.outputs, names.validate, streamFieldIds(ctx), cb);
|
|
9707
9755
|
cb.blank();
|
|
9708
9756
|
if (rootIsStruct) {
|
|
9709
9757
|
emitKwargWrapper(ctx, sigEntries, names.wrapper, names.paramsFn, names.execute, names.outputs, cb);
|
|
9710
9758
|
cb.blank();
|
|
9711
9759
|
}
|
|
9712
|
-
|
|
9760
|
+
const entryAppId = ctx.app?.id;
|
|
9761
|
+
const entryPkg = ctx.package?.name;
|
|
9762
|
+
const entrypoint = entryAppId && entryPkg ? {
|
|
9763
|
+
type: `${entryPkg}/${entryAppId}`,
|
|
9764
|
+
executeFn: executeName
|
|
9765
|
+
} : void 0;
|
|
9766
|
+
return {
|
|
9767
|
+
code: cb.toString(),
|
|
9768
|
+
entrypoint
|
|
9769
|
+
};
|
|
9713
9770
|
}
|
|
9714
9771
|
/**
|
|
9715
9772
|
* Module name (file stem) for an app: snake_case of app.id, fallback `output`.
|
|
@@ -9791,11 +9848,11 @@ var TypeScriptBackend = class {
|
|
|
9791
9848
|
name = "typescript";
|
|
9792
9849
|
target = "typescript";
|
|
9793
9850
|
emitApp(ctx, scope) {
|
|
9794
|
-
const code =
|
|
9851
|
+
const { code, entrypoint } = generateTypeScriptModule(ctx, scope);
|
|
9795
9852
|
const fileName = `${appModuleName(ctx.app)}.ts`;
|
|
9796
9853
|
return {
|
|
9797
9854
|
meta: ctx.app,
|
|
9798
|
-
entrypoint
|
|
9855
|
+
entrypoint,
|
|
9799
9856
|
files: new Map([[fileName, code]]),
|
|
9800
9857
|
errors: [],
|
|
9801
9858
|
warnings: []
|
|
@@ -10061,6 +10118,15 @@ const canonicalize = {
|
|
|
10061
10118
|
default: return "";
|
|
10062
10119
|
}
|
|
10063
10120
|
}
|
|
10121
|
+
function identityKey(node) {
|
|
10122
|
+
const m = node.meta;
|
|
10123
|
+
const metaKey = m ? [
|
|
10124
|
+
m.name ?? "",
|
|
10125
|
+
m.variantTag ?? "",
|
|
10126
|
+
m.outputs ? JSON.stringify(m.outputs) : ""
|
|
10127
|
+
].join("|") : "";
|
|
10128
|
+
return `${structuralHash(node)}#${metaKey}`;
|
|
10129
|
+
}
|
|
10064
10130
|
function sortKey(node) {
|
|
10065
10131
|
const name = node.meta?.name ?? "";
|
|
10066
10132
|
return `${node.kind}:${name}:${structuralHash(node)}`;
|
|
@@ -10073,13 +10139,13 @@ const canonicalize = {
|
|
|
10073
10139
|
const seen = /* @__PURE__ */ new Set();
|
|
10074
10140
|
const alts = [];
|
|
10075
10141
|
for (const child of sorted) {
|
|
10076
|
-
const
|
|
10077
|
-
if (!seen.has(
|
|
10078
|
-
seen.add(
|
|
10142
|
+
const key = identityKey(child);
|
|
10143
|
+
if (!seen.has(key)) {
|
|
10144
|
+
seen.add(key);
|
|
10079
10145
|
alts.push(child);
|
|
10080
10146
|
} else changed = true;
|
|
10081
10147
|
}
|
|
10082
|
-
if (alts.length !== children.length || alts.some((alt, i) =>
|
|
10148
|
+
if (alts.length !== children.length || alts.some((alt, i) => identityKey(alt) !== identityKey(children[i]))) changed = true;
|
|
10083
10149
|
return {
|
|
10084
10150
|
...node,
|
|
10085
10151
|
attrs: {
|
|
@@ -10362,11 +10428,24 @@ const simplify = {
|
|
|
10362
10428
|
const prev = nodes[nodes.length - 1];
|
|
10363
10429
|
if (prev?.kind === "literal" && child.kind === "literal" && !prev.meta && !child.meta && node.attrs.join === "") {
|
|
10364
10430
|
changed = true;
|
|
10365
|
-
|
|
10431
|
+
nodes[nodes.length - 1] = {
|
|
10432
|
+
...prev,
|
|
10433
|
+
attrs: {
|
|
10434
|
+
...prev.attrs,
|
|
10435
|
+
str: prev.attrs.str + child.attrs.str
|
|
10436
|
+
}
|
|
10437
|
+
};
|
|
10366
10438
|
} else nodes.push(child);
|
|
10367
10439
|
}
|
|
10368
10440
|
if (nodes.length === 1) {
|
|
10369
10441
|
const child = nodes[0];
|
|
10442
|
+
if (node.meta?.outputs?.length && child.kind === "literal") return {
|
|
10443
|
+
...node,
|
|
10444
|
+
attrs: {
|
|
10445
|
+
...node.attrs,
|
|
10446
|
+
nodes
|
|
10447
|
+
}
|
|
10448
|
+
};
|
|
10370
10449
|
changed = true;
|
|
10371
10450
|
const mergedMeta = mergeMeta(node.meta, child.meta);
|
|
10372
10451
|
return mergedMeta ? {
|
|
@@ -10784,7 +10863,7 @@ function defaultNamingStrategy() {
|
|
|
10784
10863
|
function isBooleanLiteralPair(variants) {
|
|
10785
10864
|
if (variants.length !== 2 || !variants.every((v) => v.type.kind === "literal")) return false;
|
|
10786
10865
|
const [a, b] = variants.map((v) => v.type.kind === "literal" ? v.type.value : null);
|
|
10787
|
-
return a === 0 && b === 1 || a === 1 && b === 0 || a === "
|
|
10866
|
+
return a === 0 && b === 1 || a === 1 && b === 0 || a === "false" && b === "true" || a === "true" && b === "false";
|
|
10788
10867
|
}
|
|
10789
10868
|
function literalFromNode(node) {
|
|
10790
10869
|
const str = node.attrs.str;
|
|
@@ -11055,7 +11134,6 @@ exports.camelCase = camelCase;
|
|
|
11055
11134
|
exports.canonicalize = canonicalize;
|
|
11056
11135
|
exports.collectFieldInfo = collectFieldInfo;
|
|
11057
11136
|
exports.collectNamedTypes = collectNamedTypes;
|
|
11058
|
-
exports.compactTokens = compactTokens;
|
|
11059
11137
|
exports.compile = compile;
|
|
11060
11138
|
exports.compose = compose;
|
|
11061
11139
|
exports.createContext = createContext;
|
|
@@ -11081,8 +11159,6 @@ exports.generatePython = generatePython;
|
|
|
11081
11159
|
exports.generateSchema = generateSchema;
|
|
11082
11160
|
exports.generateTypeScript = generateTypeScript;
|
|
11083
11161
|
exports.int = int;
|
|
11084
|
-
exports.isGated = isGated;
|
|
11085
|
-
exports.isIterated = isIterated;
|
|
11086
11162
|
exports.isStructural = isStructural;
|
|
11087
11163
|
exports.isTerminal = isTerminal;
|
|
11088
11164
|
exports.lit = lit;
|
|
@@ -11092,8 +11168,6 @@ exports.opt = opt;
|
|
|
11092
11168
|
exports.outputGate = outputGate;
|
|
11093
11169
|
exports.pascalCase = pascalCase;
|
|
11094
11170
|
exports.path = path;
|
|
11095
|
-
exports.planOutput = planOutput;
|
|
11096
|
-
exports.planScope = planScope;
|
|
11097
11171
|
exports.pydraNames = pydraNames;
|
|
11098
11172
|
exports.removeEmpty = removeEmpty;
|
|
11099
11173
|
exports.renderPythonCall = renderPythonCall;
|