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