@vessel-dsp/core 0.12.0 → 0.14.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.
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/formats/interchange/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,MAAM,EAIX,MAAM,0BAA0B,CAAC;AAIlC,OAAO,KAAK,EA0BX,eAAe,EAoEf,MAAM,mBAAmB,CAAC;AAqC3B,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACrD,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kBAAkB,EAAE,UAAU,GAAG,WAAW,CAAC;CAC7C,CAAC,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CA+EpE;AAED,gFAAgF;AAChF,wBAAgB,gCAAgC,CAC/C,MAAM,EAAE,MAAM,GACZ,8BAA8B,CAkBhC"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/formats/interchange/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,MAAM,EAIX,MAAM,0BAA0B,CAAC;AAIlC,OAAO,KAAK,EA0BX,eAAe,EAwEf,MAAM,mBAAmB,CAAC;AAqC3B,MAAM,MAAM,8BAA8B,GAAG,QAAQ,CAAC;IACrD,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,kBAAkB,EAAE,UAAU,GAAG,WAAW,CAAC;CAC7C,CAAC,CAAC;AAEH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CA+EpE;AAED,gFAAgF;AAChF,wBAAgB,gCAAgC,CAC/C,MAAM,EAAE,MAAM,GACZ,8BAA8B,CAkBhC"}
@@ -2145,6 +2145,7 @@ function parseComponents(value, warnings) {
2145
2145
  ...parseComponentDevices(component.devices, `${path}.devices`),
2146
2146
  ...parseComponentWindings(component.windings, `${path}.windings`),
2147
2147
  ...parseComponentProgram(component.program, `${path}.program`),
2148
+ ...parseComponentController(component.controller, component.terminals, `${path}.controller`),
2148
2149
  properties: parseProperties(component.properties, `${path}.properties`),
2149
2150
  sourceTypeName,
2150
2151
  };
@@ -2247,13 +2248,13 @@ function assertScannedRoutersNameRealComponents(components) {
2247
2248
  // A scanned parameter makes the same claim about a chip on the board, and is held to it.
2248
2249
  program.positions.forEach((position, index) => {
2249
2250
  for (const [name, parameter] of Object.entries(position.parameters ?? {})) {
2250
- if (parameter.read !== "scanned")
2251
+ if (parameter.read !== "scanned" && parameter.read !== "tapped")
2251
2252
  continue;
2252
2253
  check(parameter.scannedBy, `components.${component.id}.program.positions[${index}].parameters.${name}`);
2253
2254
  }
2254
2255
  for (const [line, parameters] of Object.entries(position.lines ?? {})) {
2255
2256
  for (const [name, parameter] of Object.entries(parameters)) {
2256
- if (parameter.read !== "scanned")
2257
+ if (parameter.read !== "scanned" && parameter.read !== "tapped")
2257
2258
  continue;
2258
2259
  check(parameter.scannedBy, `components.${component.id}.program.positions[${index}].lines.${line}.${name}`);
2259
2260
  }
@@ -2271,22 +2272,25 @@ function assertScannedRoutersNameRealComponents(components) {
2271
2272
  function parseScannedRead(value, path, what) {
2272
2273
  const readRaw = value.read;
2273
2274
  const read = readRaw === undefined ? "node" : expectString(readRaw, `${path}.read`).trim();
2274
- if (read !== "node" && read !== "scanned") {
2275
- throw new Error(`${path}.read: expected "node" or "scanned", got ${JSON.stringify(read)}`);
2275
+ const allowed = what === "parameter" ? ["node", "scanned", "tapped"] : ["node", "scanned"];
2276
+ if (!allowed.includes(read)) {
2277
+ throw new Error(`${path}.read: expected ${allowed.map((v) => JSON.stringify(v)).join(" or ")}, got ${JSON.stringify(read)}`);
2276
2278
  }
2277
2279
  const scannedByRaw = value.scannedBy;
2278
2280
  const scannedBy = scannedByRaw === undefined
2279
2281
  ? undefined
2280
2282
  : expectString(scannedByRaw, `${path}.scannedBy`).trim();
2281
- if (read === "scanned" && (scannedBy === undefined || scannedBy.length === 0)) {
2283
+ if (read !== "node" && (scannedBy === undefined || scannedBy.length === 0)) {
2282
2284
  // The claim has to name a chip that exists, or `scanned` becomes a way to make any
2283
2285
  // dangling control appear to work -- which is the opposite of what it is for.
2284
- throw new Error(`${path}.scannedBy: a scanned ${what} names the component that reads the control`);
2286
+ throw new Error(`${path}.scannedBy: a ${read} ${what} names the component that reads the control`);
2285
2287
  }
2286
2288
  if (read === "node" && scannedBy !== undefined) {
2287
2289
  throw new Error(`${path}.scannedBy: only a scanned ${what} names a reader; this one reads a node`);
2288
2290
  }
2289
- return scannedBy === undefined ? { read } : { read, scannedBy };
2291
+ return scannedBy === undefined
2292
+ ? { read: read }
2293
+ : { read: read, scannedBy };
2290
2294
  }
2291
2295
  function parseProgramRouter(value, path) {
2292
2296
  if (value === undefined)
@@ -2296,7 +2300,9 @@ function parseProgramRouter(value, path) {
2296
2300
  if (control.length === 0) {
2297
2301
  throw new Error(`${path}.control: a router names the control that selects the program`);
2298
2302
  }
2299
- const { read, scannedBy } = parseScannedRead(router, path, "router");
2303
+ const parsedRead = parseScannedRead(router, path, "router");
2304
+ const read = parsedRead.read;
2305
+ const scannedBy = parsedRead.scannedBy;
2300
2306
  const positions = expectNumber(router.positions, `${path}.positions`);
2301
2307
  if (!Number.isInteger(positions) || positions < 2) {
2302
2308
  throw new Error(`${path}.positions: a router's control has at least 2 discrete positions, got ${positions}`);
@@ -2345,13 +2351,24 @@ function parseProgramParameter(raw, path) {
2345
2351
  ? parameter.source.trim()
2346
2352
  : undefined;
2347
2353
  const reading = parseScannedRead(parameter, path, "parameter");
2348
- if (reading.read === "scanned" && control === undefined) {
2349
- throw new Error(`${path}.read: a scanned parameter names the control it reads`);
2354
+ if (reading.read !== "node" && control === undefined) {
2355
+ throw new Error(`${path}.read: a ${reading.read} parameter names the control it reads`);
2356
+ }
2357
+ let ratio;
2358
+ if (parameter.ratio !== undefined) {
2359
+ if (reading.read !== "tapped") {
2360
+ throw new Error(`${path}.ratio: only a tapped parameter scales its interval`);
2361
+ }
2362
+ ratio = expectNumber(parameter.ratio, `${path}.ratio`);
2363
+ if (!(Number.isFinite(ratio) && ratio > 0)) {
2364
+ throw new Error(`${path}.ratio: a tap ratio is a positive number, got ${ratio}`);
2365
+ }
2350
2366
  }
2351
2367
  return {
2352
2368
  ...(control === undefined ? {} : { control }),
2353
2369
  ...(parameter.read === undefined ? {} : { read: reading.read }),
2354
2370
  ...(reading.scannedBy === undefined ? {} : { scannedBy: reading.scannedBy }),
2371
+ ...(ratio === undefined ? {} : { ratio }),
2355
2372
  min: expectNumber(parameter.min, `${path}.min`),
2356
2373
  max: expectNumber(parameter.max, `${path}.max`),
2357
2374
  ...(source === undefined ? {} : { source }),
@@ -2434,6 +2451,78 @@ function parseComponentProgram(value, path) {
2434
2451
  },
2435
2452
  };
2436
2453
  }
2454
+ /**
2455
+ * A microcontroller's declared firmware rule, checked against the component it sits on.
2456
+ *
2457
+ * Refuses rather than repairs: a latch nothing toggles, a pin following a latch that does not
2458
+ * exist, or a pin on a terminal the chip does not have is a declaration no consumer can execute,
2459
+ * and a document that silently loses half of one is the defect this format exists to prevent.
2460
+ */
2461
+ function parseComponentController(value, rawTerminals, path) {
2462
+ if (value === undefined)
2463
+ return {};
2464
+ const controller = expectObject(value, path);
2465
+ const latches = optionalArray(controller.latches, `${path}.latches`).map((item, index) => {
2466
+ const latchPath = `${path}.latches[${index}]`;
2467
+ const latch = expectObject(item, latchPath);
2468
+ const initial = expectNumber(latch.initial, `${latchPath}.initial`);
2469
+ if (initial !== 0 && initial !== 1) {
2470
+ throw new Error(`${latchPath}.initial: a latch starts at 0 or 1, got ${initial}`);
2471
+ }
2472
+ return {
2473
+ id: expectString(latch.id, `${latchPath}.id`),
2474
+ toggledBy: expectString(latch.toggledBy, `${latchPath}.toggledBy`),
2475
+ initial,
2476
+ source: expectCitation(latch.source, `${latchPath}.source`),
2477
+ };
2478
+ });
2479
+ if (latches.length === 0) {
2480
+ throw new Error(`${path}.latches: a controller declares at least one latch`);
2481
+ }
2482
+ const latchIds = new Set();
2483
+ for (const latch of latches) {
2484
+ if (latchIds.has(latch.id)) {
2485
+ throw new Error(`${path}.latches: latch "${latch.id}" is declared twice`);
2486
+ }
2487
+ latchIds.add(latch.id);
2488
+ }
2489
+ const terminals = optionalArray(rawTerminals, `${path}.terminals`).map((item) => expectObject(item, `${path}.terminals`));
2490
+ const terminalNames = new Set(terminals.map((terminal) => terminal.name));
2491
+ const pins = optionalArray(controller.pins, `${path}.pins`).map((item, index) => {
2492
+ const pinPath = `${path}.pins[${index}]`;
2493
+ const pin = expectObject(item, pinPath);
2494
+ const terminal = expectString(pin.terminal, `${pinPath}.terminal`);
2495
+ if (!terminalNames.has(terminal)) {
2496
+ throw new Error(`${pinPath}.terminal: "${terminal}" is not a terminal of this component`);
2497
+ }
2498
+ const follows = expectString(pin.follows, `${pinPath}.follows`);
2499
+ if (!latchIds.has(follows)) {
2500
+ throw new Error(`${pinPath}.follows: "${follows}" is not a latch this controller declares`);
2501
+ }
2502
+ return {
2503
+ terminal,
2504
+ follows,
2505
+ ...(pin.invert === undefined ? {} : { invert: expectBoolean(pin.invert, `${pinPath}.invert`) }),
2506
+ source: expectCitation(pin.source, `${pinPath}.source`),
2507
+ };
2508
+ });
2509
+ if (pins.length > 0) {
2510
+ const roles = new Set(terminals.map((terminal) => terminal.role));
2511
+ for (const role of ["supplyPositive", "supplyNegative"]) {
2512
+ if (!roles.has(role)) {
2513
+ throw new Error(`${path}.pins: a controller that drives pins needs its component to declare a ${role} terminal, which sets the pin levels`);
2514
+ }
2515
+ }
2516
+ }
2517
+ return { controller: { latches, ...(pins.length === 0 ? {} : { pins }) } };
2518
+ }
2519
+ function expectCitation(value, path) {
2520
+ const text = expectString(value, path).trim();
2521
+ if (text.length === 0) {
2522
+ throw new Error(`${path}: a firmware rule is only as good as its source; cite one`);
2523
+ }
2524
+ return text;
2525
+ }
2437
2526
  /**
2438
2527
  * A transformer's declared winding list, or nothing when it declares none.
2439
2528
  *