@styx-api/core 0.3.0 → 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@styx-api/core",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Styx compiler core: parses CLI tool descriptors (e.g. Boutiques), optimizes an IR, solves typed parameter bindings, and generates type-safe wrappers (TypeScript, Python, JSON Schema). Part of the Styx/NiWrap ecosystem.",
5
5
  "keywords": [
6
6
  "styx",
@@ -22,6 +22,10 @@ function isString(x: unknown): x is string {
22
22
  return typeof x === "string";
23
23
  }
24
24
 
25
+ function isNumber(x: unknown): x is number {
26
+ return typeof x === "number" && Number.isFinite(x);
27
+ }
28
+
25
29
  function isArray(x: unknown): x is unknown[] {
26
30
  return Array.isArray(x);
27
31
  }
@@ -60,9 +64,10 @@ function emptyExpr(): Sequence {
60
64
  * - option, 1 arg -> opt(seq(lit(-switch), value)) (flat optional)
61
65
  * - option, >1 arg / multi -> opt|rep(seq(lit(-switch), ...)) (sub-struct)
62
66
  *
63
- * Type mapping mirrors the v1 `mrt2bt.js` converter's `set_type`. The dump does
64
- * not carry choice values, so a `choice` argument degrades to a plain string
65
- * (as it did in v1). Per-command quirks v1 hand-coded that the flat dump cannot
67
+ * Type mapping mirrors the v1 `mrt2bt.js` converter's `set_type`, plus `choice`
68
+ * values: when the dump carries `choices` it lowers to an enum (an alternative
69
+ * of literals), else a plain string (older dumps that omit the values - as v1
70
+ * always did). Per-command quirks v1 hand-coded that the flat dump cannot
66
71
  * express (e.g. dwi2fod/mtnormalise paired in/out args) are intentionally NOT
67
72
  * special-cased here - they are patched on the niwrap side post-dump, keeping
68
73
  * this frontend format-general.
@@ -160,6 +165,20 @@ export class MrtrixParser implements Frontend {
160
165
 
161
166
  // -- Terminals --
162
167
 
168
+ /**
169
+ * Integer/float bounds, when the dump carries them. The C++ hook serializes
170
+ * `Argument::limits.{i,f}.{min,max}` as `min`/`max`, omitting the unbounded
171
+ * sentinels - so a present value is a real, tool-enforced bound.
172
+ */
173
+ private numericBounds(arg: Record<string, unknown>): { minValue?: number; maxValue?: number } {
174
+ const lo = arg.min;
175
+ const hi = arg.max;
176
+ return {
177
+ ...(isNumber(lo) && { minValue: lo }),
178
+ ...(isNumber(hi) && { maxValue: hi }),
179
+ };
180
+ }
181
+
163
182
  /**
164
183
  * Lower one MRtrix argument to its terminal node (carrying name + doc) and,
165
184
  * for output types, an accompanying `Output`.
@@ -177,14 +196,38 @@ export class MrtrixParser implements Frontend {
177
196
  const name = meta.name!;
178
197
 
179
198
  switch (argType) {
180
- case "integer":
181
- return { node: int(meta) };
182
- case "float":
183
- return { node: float(meta) };
199
+ case "integer": {
200
+ const node = int(meta);
201
+ Object.assign(node.attrs, this.numericBounds(arg));
202
+ return { node };
203
+ }
204
+ case "float": {
205
+ const node = float(meta);
206
+ Object.assign(node.attrs, this.numericBounds(arg));
207
+ return { node };
208
+ }
184
209
  case "text":
185
- case "choice": // choice values are not emitted in the dump; treat as string
186
210
  case "undefined":
211
+ case "boolean":
212
+ // `boolean` rarely (if ever) appears in a dump; treat its literal token
213
+ // as a string rather than erroring on an unknown type.
187
214
  return { node: str(meta) };
215
+ case "choice": {
216
+ // A `choice` carries its allowed values in `choices` (the C++ hook
217
+ // serializes `Argument::limits.choices`). Lower to an enum: an
218
+ // alternative of string literals. Older dumps omit the values - fall
219
+ // back to a plain string so they still compile.
220
+ const choices = arg.choices;
221
+ if (isArray(choices)) {
222
+ const alts = choices.filter(isString).map((c) => lit(c));
223
+ if (alts.length > 0) {
224
+ const node = alt(...alts);
225
+ node.meta = meta;
226
+ return { node };
227
+ }
228
+ }
229
+ return { node: str(meta) };
230
+ }
188
231
  // MRtrix sequences are always a single comma-separated token (parse_ints /
189
232
  // parse_floats split on `,`), so the list is comma-joined, never spaced.
190
233
  case "int seq": {