capdag 1.222.603 → 1.226.626
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/cap-fab-renderer.js +100 -1
- package/capdag.js +34 -22
- package/capdag.test.js +59 -29
- package/machine-parser.js +73 -120
- package/machine.pegjs +6 -7
- package/package.json +1 -1
package/cap-fab-renderer.js
CHANGED
|
@@ -590,6 +590,18 @@ function buildStylesheet() {
|
|
|
590
590
|
'target-arrow-shape': 'tee',
|
|
591
591
|
},
|
|
592
592
|
},
|
|
593
|
+
{
|
|
594
|
+
// A convergence (fan-in) edge: a second producer feeding a cap's
|
|
595
|
+
// non-main argument. Dotted + diamond-tail to read as a merging
|
|
596
|
+
// side-input distinct from the solid main backbone edge.
|
|
597
|
+
selector: 'edge.strand-convergence',
|
|
598
|
+
style: {
|
|
599
|
+
'line-style': 'dotted',
|
|
600
|
+
'width': 2,
|
|
601
|
+
'source-arrow-shape': 'diamond',
|
|
602
|
+
'source-arrow-color': machineEdgeColor || 'data(color)',
|
|
603
|
+
},
|
|
604
|
+
},
|
|
593
605
|
{
|
|
594
606
|
selector: 'edge.faded',
|
|
595
607
|
style: { 'opacity': fadedEdgeOpacity },
|
|
@@ -762,6 +774,28 @@ function validateStrandStep(step, path) {
|
|
|
762
774
|
if (typeof body.output_is_sequence !== 'boolean') {
|
|
763
775
|
throw new Error(`CapFabRenderer: ${path}.step_type.Cap.output_is_sequence must be a boolean`);
|
|
764
776
|
}
|
|
777
|
+
// `inputs` (capdag CapInput list) carries the data-flow graph: the main
|
|
778
|
+
// input plus any convergence inputs. Optional on legacy payloads, but
|
|
779
|
+
// when present it must be well-formed — the fan-in edges are drawn from
|
|
780
|
+
// it, so a malformed entry is a hard error, not a silent skip.
|
|
781
|
+
if (body.inputs !== undefined) {
|
|
782
|
+
assertArray(body.inputs, `${path}.step_type.Cap.inputs`);
|
|
783
|
+
body.inputs.forEach((input, inputIdx) => {
|
|
784
|
+
const inputPath = `${path}.step_type.Cap.inputs[${inputIdx}]`;
|
|
785
|
+
assertObject(input, inputPath);
|
|
786
|
+
assertString(input.arg_urn, `${inputPath}.arg_urn`);
|
|
787
|
+
// serde: unit variant → the string "StrandInput"; struct variant →
|
|
788
|
+
// { Step: { token_id } }. Anything else is malformed.
|
|
789
|
+
if (input.source === 'StrandInput') return;
|
|
790
|
+
if (input.source && typeof input.source === 'object' && input.source.Step) {
|
|
791
|
+
assertString(input.source.Step.token_id, `${inputPath}.source.Step.token_id`);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
throw new Error(
|
|
795
|
+
`CapFabRenderer: ${inputPath}.source must be "StrandInput" or { Step: { token_id } }`
|
|
796
|
+
);
|
|
797
|
+
});
|
|
798
|
+
}
|
|
765
799
|
} else {
|
|
766
800
|
assertString(body.media_def, `${path}.step_type.${variant}.media_def`);
|
|
767
801
|
}
|
|
@@ -1309,6 +1343,17 @@ function buildStrandGraphData(data) {
|
|
|
1309
1343
|
return outerExit;
|
|
1310
1344
|
}
|
|
1311
1345
|
|
|
1346
|
+
// Map every step's stable token_id to its output node id. A cap
|
|
1347
|
+
// step's node (`step_${i}`) IS its `to_spec` output, so a convergence
|
|
1348
|
+
// input naming a producer by token resolves to that producer's output
|
|
1349
|
+
// node — the value flowing into the fan-in arg.
|
|
1350
|
+
const tokenToNodeId = new Map();
|
|
1351
|
+
data.steps.forEach((step, i) => {
|
|
1352
|
+
if (step && typeof step.token_id === 'string' && step.token_id.length > 0) {
|
|
1353
|
+
tokenToNodeId.set(step.token_id, `step_${i}`);
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
|
|
1312
1357
|
data.steps.forEach((step, i) => {
|
|
1313
1358
|
const variant = Object.keys(step.step_type)[0];
|
|
1314
1359
|
const nodeId = `step_${i}`;
|
|
@@ -1330,8 +1375,43 @@ function buildStrandGraphData(data) {
|
|
|
1330
1375
|
if (cardinality !== '1\u21921') {
|
|
1331
1376
|
label = `${label} (${cardinality})`;
|
|
1332
1377
|
}
|
|
1378
|
+
// Resolve this cap's actual data-flow producers from `body.inputs`
|
|
1379
|
+
// (capdag CapInput serde shape): each `source` is the string
|
|
1380
|
+
// "StrandInput" (fed by the strand's input anchor → the input slot
|
|
1381
|
+
// node) or `{ Step: { token_id } }` (fed by a producing cap → that
|
|
1382
|
+
// step's output node). This is the authoritative topology; step
|
|
1383
|
+
// ORDER is not — the realizer emits edges greedily in dependency
|
|
1384
|
+
// order, so `prevNodeId` (the previously-emitted step) need not be
|
|
1385
|
+
// one of this cap's inputs at all.
|
|
1386
|
+
const capInputs = Array.isArray(body.inputs) ? body.inputs : [];
|
|
1387
|
+
const producerNodeIds = [];
|
|
1388
|
+
for (const input of capInputs) {
|
|
1389
|
+
const src = input && input.source;
|
|
1390
|
+
if (src === 'StrandInput') {
|
|
1391
|
+
producerNodeIds.push(inputSlotId);
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
const producerToken = src && typeof src === 'object' && src.Step && src.Step.token_id;
|
|
1395
|
+
if (typeof producerToken !== 'string' || producerToken.length === 0) continue;
|
|
1396
|
+
const producerNodeId = tokenToNodeId.get(producerToken);
|
|
1397
|
+
if (producerNodeId) producerNodeIds.push(producerNodeId);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// Pick the source of the single labeled "backbone" edge (the one
|
|
1401
|
+
// carrying the cap title + cardinality). Inside a ForEach body — or
|
|
1402
|
+
// when the cap declares no inputs — keep the linear `prevNodeId`
|
|
1403
|
+
// thread so the ForEach boundary handling and its (1→n) entry marker
|
|
1404
|
+
// are preserved exactly. Otherwise anchor the backbone on a REAL
|
|
1405
|
+
// input: `prevNodeId` if it is genuinely one, else the first actual
|
|
1406
|
+
// producer — so a non-input step ordered just before this cap can
|
|
1407
|
+
// never become a spurious backbone edge.
|
|
1408
|
+
const isForeachContext = isForeachEntry || insideForEachBody !== null;
|
|
1409
|
+
let backboneSource = prevNodeId;
|
|
1410
|
+
if (!isForeachContext && producerNodeIds.length > 0 && !producerNodeIds.includes(prevNodeId)) {
|
|
1411
|
+
backboneSource = producerNodeIds[0];
|
|
1412
|
+
}
|
|
1333
1413
|
addEdge(
|
|
1334
|
-
|
|
1414
|
+
backboneSource,
|
|
1335
1415
|
nodeId,
|
|
1336
1416
|
label,
|
|
1337
1417
|
body.title,
|
|
@@ -1340,6 +1420,25 @@ function buildStrandGraphData(data) {
|
|
|
1340
1420
|
{ foreachEntry: isForeachEntry }
|
|
1341
1421
|
);
|
|
1342
1422
|
|
|
1423
|
+
// Convergence (fan-in): draw an edge from every other producer that
|
|
1424
|
+
// isn't the backbone source, so a cap fed by several producers renders
|
|
1425
|
+
// as a DAG, not a chain. Deduplicated against the backbone edge and
|
|
1426
|
+
// against a self-loop.
|
|
1427
|
+
const drawnSources = new Set([backboneSource, nodeId]);
|
|
1428
|
+
for (const producerNodeId of producerNodeIds) {
|
|
1429
|
+
if (drawnSources.has(producerNodeId)) continue;
|
|
1430
|
+
drawnSources.add(producerNodeId);
|
|
1431
|
+
addEdge(
|
|
1432
|
+
producerNodeId,
|
|
1433
|
+
nodeId,
|
|
1434
|
+
'',
|
|
1435
|
+
body.title,
|
|
1436
|
+
body.cap_urn,
|
|
1437
|
+
'strand-convergence',
|
|
1438
|
+
{}
|
|
1439
|
+
);
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1343
1442
|
if (insideForEachBody !== null) {
|
|
1344
1443
|
if (bodyEntry === null) bodyEntry = nodeId;
|
|
1345
1444
|
bodyExit = nodeId;
|
package/capdag.js
CHANGED
|
@@ -6388,7 +6388,9 @@ async function scanChannelRoot(scanRoot, expectedSlug, identity, discovered) {
|
|
|
6388
6388
|
// [alias cap:in="...";op=...;out="..."] — header (defines a cap with alias)
|
|
6389
6389
|
// [src -> alias -> dst] — wiring (connects nodes via cap)
|
|
6390
6390
|
// [(a, b) -> alias -> dst] — fan-in wiring
|
|
6391
|
-
//
|
|
6391
|
+
//
|
|
6392
|
+
// Per-item map (`is_loop`) is a derived cardinality property, never authored
|
|
6393
|
+
// syntax — there is no LOOP marker in the grammar.
|
|
6392
6394
|
// ============================================================================
|
|
6393
6395
|
|
|
6394
6396
|
/**
|
|
@@ -6504,7 +6506,9 @@ class MachineEdge {
|
|
|
6504
6506
|
*/
|
|
6505
6507
|
toString() {
|
|
6506
6508
|
const sources = this.sources.map(s => s.toString()).join(', ');
|
|
6507
|
-
|
|
6509
|
+
// Debug-only marker for a per-item map edge. Not notation syntax (the
|
|
6510
|
+
// `LOOP` keyword is retired); `is_loop` is a derived cardinality property.
|
|
6511
|
+
const loopPrefix = this.isLoop ? 'map ' : '';
|
|
6508
6512
|
return `(${sources}) -${loopPrefix}${this.capUrn}-> ${this.target}`;
|
|
6509
6513
|
}
|
|
6510
6514
|
}
|
|
@@ -6703,13 +6707,13 @@ class Machine {
|
|
|
6703
6707
|
const targetKey = edge.target.toString();
|
|
6704
6708
|
const targetName = nodeNames.get(targetKey);
|
|
6705
6709
|
|
|
6706
|
-
|
|
6707
|
-
|
|
6710
|
+
// `is_loop` is NOT emitted into notation text: it is a derived cardinality
|
|
6711
|
+
// property, not authored syntax (the `LOOP` keyword is retired).
|
|
6708
6712
|
if (sources.length === 1) {
|
|
6709
|
-
output += `[${sources[0]} -> ${
|
|
6713
|
+
output += `[${sources[0]} -> ${alias} -> ${targetName}]`;
|
|
6710
6714
|
} else {
|
|
6711
6715
|
const group = sources.join(', ');
|
|
6712
|
-
output += `[(${group}) -> ${
|
|
6716
|
+
output += `[(${group}) -> ${alias} -> ${targetName}]`;
|
|
6713
6717
|
}
|
|
6714
6718
|
}
|
|
6715
6719
|
|
|
@@ -6756,13 +6760,13 @@ class Machine {
|
|
|
6756
6760
|
const targetKey = edge.target.toString();
|
|
6757
6761
|
const targetName = nodeNames.get(targetKey);
|
|
6758
6762
|
|
|
6759
|
-
|
|
6760
|
-
|
|
6763
|
+
// `is_loop` is NOT emitted into notation text: it is a derived cardinality
|
|
6764
|
+
// property, not authored syntax (the `LOOP` keyword is retired).
|
|
6761
6765
|
if (sources.length === 1) {
|
|
6762
|
-
lines.push(`[${sources[0]} -> ${
|
|
6766
|
+
lines.push(`[${sources[0]} -> ${alias} -> ${targetName}]`);
|
|
6763
6767
|
} else {
|
|
6764
6768
|
const group = sources.join(', ');
|
|
6765
|
-
lines.push(`[(${group}) -> ${
|
|
6769
|
+
lines.push(`[(${group}) -> ${alias} -> ${targetName}]`);
|
|
6766
6770
|
}
|
|
6767
6771
|
}
|
|
6768
6772
|
|
|
@@ -6814,13 +6818,13 @@ class Machine {
|
|
|
6814
6818
|
const targetKey = edge.target.toString();
|
|
6815
6819
|
const targetName = nodeNames.get(targetKey);
|
|
6816
6820
|
|
|
6817
|
-
|
|
6818
|
-
|
|
6821
|
+
// `is_loop` is NOT emitted into notation text: it is a derived cardinality
|
|
6822
|
+
// property, not authored syntax (the `LOOP` keyword is retired).
|
|
6819
6823
|
if (sources.length === 1) {
|
|
6820
|
-
lines.push(`${open}${sources[0]} -> ${
|
|
6824
|
+
lines.push(`${open}${sources[0]} -> ${alias} -> ${targetName}${close}`);
|
|
6821
6825
|
} else {
|
|
6822
6826
|
const group = sources.join(', ');
|
|
6823
|
-
lines.push(`${open}(${group}) -> ${
|
|
6827
|
+
lines.push(`${open}(${group}) -> ${alias} -> ${targetName}${close}`);
|
|
6824
6828
|
}
|
|
6825
6829
|
}
|
|
6826
6830
|
|
|
@@ -6896,13 +6900,14 @@ class Machine {
|
|
|
6896
6900
|
|
|
6897
6901
|
const sources = edge.sources.map(s => nodeNames.get(s.toString()));
|
|
6898
6902
|
const targetName = nodeNames.get(edge.target.toString());
|
|
6899
|
-
const loopPrefix = edge.isLoop ? 'LOOP ' : '';
|
|
6900
6903
|
|
|
6904
|
+
// `is_loop` is NOT emitted into notation text: it is a derived cardinality
|
|
6905
|
+
// property, not authored syntax (the `LOOP` keyword is retired).
|
|
6901
6906
|
if (sources.length === 1) {
|
|
6902
|
-
lines.push(`${open}${sources[0]} -> ${
|
|
6907
|
+
lines.push(`${open}${sources[0]} -> ${token} -> ${targetName}${close}`);
|
|
6903
6908
|
} else {
|
|
6904
6909
|
const group = sources.join(', ');
|
|
6905
|
-
lines.push(`${open}(${group}) -> ${
|
|
6910
|
+
lines.push(`${open}(${group}) -> ${token} -> ${targetName}${close}`);
|
|
6906
6911
|
}
|
|
6907
6912
|
}
|
|
6908
6913
|
|
|
@@ -6990,7 +6995,8 @@ class Machine {
|
|
|
6990
6995
|
* - Leaf targets: stadium-shaped nodes (rounded)
|
|
6991
6996
|
* - Intermediate nodes: rectangular
|
|
6992
6997
|
* - Edge labels: op= tag value (or full cap URN if no op)
|
|
6993
|
-
* -
|
|
6998
|
+
* - Per-item map edges (`is_loop`): dotted line style — `is_loop` is a
|
|
6999
|
+
* derived render property, never the retired `LOOP` keyword
|
|
6994
7000
|
* - Node labels: derived MediaUrn type
|
|
6995
7001
|
*
|
|
6996
7002
|
* @returns {string} Mermaid flowchart definition
|
|
@@ -7045,8 +7051,9 @@ class Machine {
|
|
|
7045
7051
|
const srcName = nodeNames.get(srcKey);
|
|
7046
7052
|
|
|
7047
7053
|
if (edge.isLoop) {
|
|
7048
|
-
// Dotted line
|
|
7049
|
-
|
|
7054
|
+
// Dotted line renders the derived per-item map (`is_loop`) property.
|
|
7055
|
+
// No "LOOP" text — that keyword is retired.
|
|
7056
|
+
lines.push(` ${srcName} -. "${label}" .-> ${targetName}`);
|
|
7050
7057
|
} else {
|
|
7051
7058
|
lines.push(` ${srcName} -- "${label}" --> ${targetName}`);
|
|
7052
7059
|
}
|
|
@@ -7164,7 +7171,6 @@ function _parseMachineInternal(input) {
|
|
|
7164
7171
|
sources: stmt.sources,
|
|
7165
7172
|
capAlias: stmt.capAlias,
|
|
7166
7173
|
target: stmt.target,
|
|
7167
|
-
isLoop: stmt.isLoop,
|
|
7168
7174
|
position: i,
|
|
7169
7175
|
location: stmt.location,
|
|
7170
7176
|
sourceLocations: stmt.sourceLocations,
|
|
@@ -7286,7 +7292,13 @@ function _parseMachineInternal(input) {
|
|
|
7286
7292
|
assignOrCheckNode(wiring.target, capOutMedia, nodeMedia, wiring.position,
|
|
7287
7293
|
wiring.targetLocation || wiring.location);
|
|
7288
7294
|
|
|
7289
|
-
|
|
7295
|
+
// `is_loop` is a derived cardinality property, not authored syntax (the
|
|
7296
|
+
// `LOOP` keyword is retired). The pure-JS parse path has no cap definitions
|
|
7297
|
+
// — it derives media only from the cap URN's in=/out= specs — so it cannot
|
|
7298
|
+
// evaluate the ForEach rule (a sequence source feeding a scalar-input cap).
|
|
7299
|
+
// Editors obtain the derived `is_loop` from the engine server's resolved
|
|
7300
|
+
// graph; here it defaults to false.
|
|
7301
|
+
edges.push(new MachineEdge(sourceUrns, capUrn, capOutMedia, false));
|
|
7290
7302
|
}
|
|
7291
7303
|
|
|
7292
7304
|
return {
|
package/capdag.test.js
CHANGED
|
@@ -3308,14 +3308,25 @@ function test6294_Machine_fanInSecondaryUnassignedGetsWildcard() {
|
|
|
3308
3308
|
assertEqual(g.edges()[0].sources[1].toString(), 'media:');
|
|
3309
3309
|
}
|
|
3310
3310
|
|
|
3311
|
-
// TEST6306:
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3311
|
+
// TEST6306: The retired LOOP keyword is no longer grammar. A wiring that still
|
|
3312
|
+
// writes `LOOP <cap>` before the cap alias no longer parses — `LOOP` is now an
|
|
3313
|
+
// ordinary alias, so `pages -> LOOP p2t -> texts` is two aliases in the cap
|
|
3314
|
+
// position with no arrow between them, which is a syntax error.
|
|
3315
|
+
function test6306_Machine_loopKeywordIsNotGrammar() {
|
|
3316
|
+
assertThrowsWithCode(
|
|
3317
|
+
() => Machine.fromString(
|
|
3318
|
+
'[p2t cap:in="media:disbound-page;enc=utf-8";page-to-text;out="media:enc=utf-8;ext=txt"]' +
|
|
3319
|
+
'[pages -> LOOP p2t -> texts]'
|
|
3320
|
+
),
|
|
3321
|
+
MachineSyntaxErrorCodes.PARSE_ERROR
|
|
3322
|
+
);
|
|
3323
|
+
|
|
3324
|
+
// `LOOP` on its own in the cap position parses fine — it is just an alias —
|
|
3325
|
+
// but resolves to nothing, proving it carries no special meaning.
|
|
3326
|
+
assertThrowsWithCode(
|
|
3327
|
+
() => Machine.fromString('[pages -> LOOP -> texts]'),
|
|
3328
|
+
MachineSyntaxErrorCodes.UNDEFINED_ALIAS
|
|
3316
3329
|
);
|
|
3317
|
-
assertEqual(g.edgeCount(), 1);
|
|
3318
|
-
assertEqual(g.edges()[0].isLoop, true);
|
|
3319
3330
|
}
|
|
3320
3331
|
|
|
3321
3332
|
// TEST6308: Machine undefined alias fails
|
|
@@ -3416,14 +3427,16 @@ function test6331_Machine_lineBasedTwoStepChain() {
|
|
|
3416
3427
|
assertEqual(g.edgeCount(), 2);
|
|
3417
3428
|
}
|
|
3418
3429
|
|
|
3419
|
-
// TEST6334:
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3430
|
+
// TEST6334: The retired LOOP keyword is not grammar in line-based mode either —
|
|
3431
|
+
// `pages -> LOOP p2t -> texts` is a syntax error, same as the bracketed form.
|
|
3432
|
+
function test6334_Machine_lineBasedLoopKeywordIsNotGrammar() {
|
|
3433
|
+
assertThrowsWithCode(
|
|
3434
|
+
() => Machine.fromString(
|
|
3435
|
+
'p2t cap:in="media:disbound-page;enc=utf-8";page-to-text;out="media:enc=utf-8;ext=txt"\n' +
|
|
3436
|
+
'pages -> LOOP p2t -> texts'
|
|
3437
|
+
),
|
|
3438
|
+
MachineSyntaxErrorCodes.PARSE_ERROR
|
|
3424
3439
|
);
|
|
3425
|
-
assertEqual(g.edgeCount(), 1);
|
|
3426
|
-
assertEqual(g.edges()[0].isLoop, true);
|
|
3427
3440
|
}
|
|
3428
3441
|
|
|
3429
3442
|
// TEST6337: Machine line based fan in
|
|
@@ -3835,8 +3848,12 @@ function test6415_Machine_roundtripFanOut() {
|
|
|
3835
3848
|
'Fan-out round-trip failed: ' + notation);
|
|
3836
3849
|
}
|
|
3837
3850
|
|
|
3838
|
-
// TEST6417:
|
|
3839
|
-
|
|
3851
|
+
// TEST6417: A per-item map (`is_loop`) edge serializes WITHOUT any LOOP marker —
|
|
3852
|
+
// `is_loop` is a derived cardinality property, not authored notation text. The
|
|
3853
|
+
// pure-JS parse path has no cap definitions to re-derive cardinality, so the
|
|
3854
|
+
// reparsed edge has `isLoop === false`; editors get the derived value from the
|
|
3855
|
+
// engine, not from re-parsing.
|
|
3856
|
+
function test6417_Machine_loopEdgeSerializesWithoutLoopText() {
|
|
3840
3857
|
const original = new Machine([new MachineEdge(
|
|
3841
3858
|
[MediaUrn.fromString('media:disbound-page;enc=utf-8')],
|
|
3842
3859
|
CapUrn.fromString('cap:in="media:disbound-page;enc=utf-8";page-to-text;out="media:enc=utf-8;ext=txt"'),
|
|
@@ -3844,9 +3861,17 @@ function test6417_Machine_roundtripLoopEdge() {
|
|
|
3844
3861
|
true
|
|
3845
3862
|
)]);
|
|
3846
3863
|
const notation = original.toMachineNotation();
|
|
3864
|
+
assert(!notation.includes('LOOP'), 'serialized notation must not contain the retired LOOP keyword');
|
|
3865
|
+
|
|
3847
3866
|
const reparsed = Machine.fromString(notation);
|
|
3848
|
-
|
|
3849
|
-
assertEqual(reparsed.edges()[0].isLoop,
|
|
3867
|
+
assertEqual(reparsed.edgeCount(), 1);
|
|
3868
|
+
assertEqual(reparsed.edges()[0].isLoop, false,
|
|
3869
|
+
'pure-JS parse path cannot derive cardinality — isLoop defaults to false');
|
|
3870
|
+
// The structural payload (sources, cap, target) round-trips intact.
|
|
3871
|
+
assert(reparsed.edges()[0].capUrn.isEquivalent(original.edges()[0].capUrn),
|
|
3872
|
+
'cap URN must round-trip');
|
|
3873
|
+
assert(reparsed.edges()[0].target.isEquivalent(original.edges()[0].target),
|
|
3874
|
+
'target media must round-trip');
|
|
3850
3875
|
}
|
|
3851
3876
|
|
|
3852
3877
|
// TEST6419: Machine serialization is deterministic
|
|
@@ -4203,16 +4228,21 @@ function test6462_Machine_toMermaid_linearChain() {
|
|
|
4203
4228
|
assert(mermaid.includes('(['), 'Should have stadium shape nodes');
|
|
4204
4229
|
}
|
|
4205
4230
|
|
|
4206
|
-
// TEST6463:
|
|
4231
|
+
// TEST6463: Mermaid renders a per-item map (`is_loop`) edge with a dotted line
|
|
4232
|
+
// — `is_loop` is a kept render property — but emits NO "LOOP" text, since that
|
|
4233
|
+
// keyword is retired. The loop edge is built programmatically because the
|
|
4234
|
+
// grammar no longer has any way to author one.
|
|
4207
4235
|
function test6463_Machine_toMermaid_loopEdge() {
|
|
4208
|
-
const machine = Machine
|
|
4209
|
-
'
|
|
4210
|
-
'
|
|
4211
|
-
|
|
4236
|
+
const machine = new Machine([new MachineEdge(
|
|
4237
|
+
[MediaUrn.fromString('media:disbound-page;enc=utf-8')],
|
|
4238
|
+
CapUrn.fromString('cap:in="media:disbound-page;enc=utf-8";page-to-text;out="media:enc=utf-8;ext=txt"'),
|
|
4239
|
+
MediaUrn.fromString('media:enc=utf-8;ext=txt'),
|
|
4240
|
+
true
|
|
4241
|
+
)]);
|
|
4212
4242
|
const mermaid = machine.toMermaid();
|
|
4213
|
-
assert(mermaid.includes('LOOP'), '
|
|
4214
|
-
assert(mermaid.includes('-.'), 'Should use dotted line for
|
|
4215
|
-
assert(mermaid.includes('.->'), 'Should use dotted arrow for
|
|
4243
|
+
assert(!mermaid.includes('LOOP'), 'Must not emit the retired LOOP label');
|
|
4244
|
+
assert(mermaid.includes('-.'), 'Should use dotted line for the per-item map edge');
|
|
4245
|
+
assert(mermaid.includes('.->'), 'Should use dotted arrow for the per-item map edge');
|
|
4216
4246
|
}
|
|
4217
4247
|
|
|
4218
4248
|
// TEST6464: Machine to mermaid empty graph
|
|
@@ -6749,7 +6779,7 @@ async function runTests() {
|
|
|
6749
6779
|
runTest('MACHINE:fan_out', test6290_Machine_fanOut);
|
|
6750
6780
|
runTest('MACHINE:fan_in_secondary_assigned_by_prior_wiring', test6292_Machine_fanInSecondaryAssignedByPriorWiring);
|
|
6751
6781
|
runTest('MACHINE:fan_in_secondary_unassigned_gets_wildcard', test6294_Machine_fanInSecondaryUnassignedGetsWildcard);
|
|
6752
|
-
runTest('MACHINE:
|
|
6782
|
+
runTest('MACHINE:loop_keyword_is_not_grammar', test6306_Machine_loopKeywordIsNotGrammar);
|
|
6753
6783
|
runTest('MACHINE:undefined_alias_fails', test6308_Machine_undefinedAliasFails);
|
|
6754
6784
|
runTest('MACHINE:node_alias_collision', test6310_Machine_nodeAliasCollision);
|
|
6755
6785
|
runTest('MACHINE:conflicting_media_types_fail', test6312_Machine_conflictingMediaTypesFail);
|
|
@@ -6762,7 +6792,7 @@ async function runTests() {
|
|
|
6762
6792
|
console.log('\n--- machine/parser.rs (line-based) ---');
|
|
6763
6793
|
runTest('MACHINE:line_based_simple_chain', test6327_Machine_lineBasedSimpleChain);
|
|
6764
6794
|
runTest('MACHINE:line_based_two_step_chain', test6331_Machine_lineBasedTwoStepChain);
|
|
6765
|
-
runTest('MACHINE:
|
|
6795
|
+
runTest('MACHINE:line_based_loop_keyword_is_not_grammar', test6334_Machine_lineBasedLoopKeywordIsNotGrammar);
|
|
6766
6796
|
runTest('MACHINE:line_based_fan_in', test6337_Machine_lineBasedFanIn);
|
|
6767
6797
|
runTest('MACHINE:mixed_bracketed_and_line_based', test6341_Machine_mixedBracketedAndLineBased);
|
|
6768
6798
|
runTest('MACHINE:line_based_equivalent_to_bracketed', test6345_Machine_lineBasedEquivalentToBracketed);
|
|
@@ -6797,7 +6827,7 @@ async function runTests() {
|
|
|
6797
6827
|
runTest('MACHINE:roundtrip_single_edge', test6410_Machine_roundtripSingleEdge);
|
|
6798
6828
|
runTest('MACHINE:roundtrip_two_edge_chain', test6413_Machine_roundtripTwoEdgeChain);
|
|
6799
6829
|
runTest('MACHINE:roundtrip_fan_out', test6415_Machine_roundtripFanOut);
|
|
6800
|
-
runTest('MACHINE:
|
|
6830
|
+
runTest('MACHINE:loop_edge_serializes_without_loop_text', test6417_Machine_loopEdgeSerializesWithoutLoopText);
|
|
6801
6831
|
runTest('MACHINE:serialization_is_deterministic', test6419_Machine_serializationIsDeterministic);
|
|
6802
6832
|
runTest('MACHINE:reordered_edges_produce_same_notation', test6421_Machine_reorderedEdgesProduceSameNotation);
|
|
6803
6833
|
runTest('MACHINE:multiline_serialize_format', test6429_Machine_multilineSerializeFormat);
|
package/machine-parser.js
CHANGED
|
@@ -170,14 +170,13 @@ function peg$parse(input, options) {
|
|
|
170
170
|
const peg$c2 = "(";
|
|
171
171
|
const peg$c3 = ",";
|
|
172
172
|
const peg$c4 = ")";
|
|
173
|
-
const peg$c5 = "
|
|
174
|
-
const peg$c6 = "
|
|
175
|
-
const peg$c7 = "
|
|
176
|
-
const peg$c8 = "
|
|
177
|
-
const peg$c9 = "\
|
|
178
|
-
const peg$c10 = "
|
|
179
|
-
const peg$c11 = "
|
|
180
|
-
const peg$c12 = "\\\\";
|
|
173
|
+
const peg$c5 = "-";
|
|
174
|
+
const peg$c6 = ">";
|
|
175
|
+
const peg$c7 = "cap:";
|
|
176
|
+
const peg$c8 = "\r\n";
|
|
177
|
+
const peg$c9 = "\"";
|
|
178
|
+
const peg$c10 = "\\\"";
|
|
179
|
+
const peg$c11 = "\\\\";
|
|
181
180
|
|
|
182
181
|
const peg$r0 = /^[a-zA-Z_]/;
|
|
183
182
|
const peg$r1 = /^[a-zA-Z0-9_\-]/;
|
|
@@ -189,21 +188,20 @@ function peg$parse(input, options) {
|
|
|
189
188
|
const peg$e2 = peg$literalExpectation("(", false);
|
|
190
189
|
const peg$e3 = peg$literalExpectation(",", false);
|
|
191
190
|
const peg$e4 = peg$literalExpectation(")", false);
|
|
192
|
-
const peg$e5 = peg$literalExpectation("
|
|
193
|
-
const peg$e6 = peg$literalExpectation("
|
|
194
|
-
const peg$e7 = peg$
|
|
195
|
-
const peg$e8 = peg$classExpectation([["a", "z"], ["A", "Z"], "_"], false, false, false);
|
|
196
|
-
const peg$e9 = peg$
|
|
197
|
-
const peg$e10 = peg$
|
|
198
|
-
const peg$e11 = peg$
|
|
199
|
-
const peg$e12 = peg$
|
|
200
|
-
const peg$e13 = peg$
|
|
201
|
-
const peg$e14 = peg$
|
|
202
|
-
const peg$e15 = peg$literalExpectation("
|
|
203
|
-
const peg$e16 = peg$literalExpectation("
|
|
204
|
-
const peg$e17 = peg$
|
|
205
|
-
const peg$e18 = peg$
|
|
206
|
-
const peg$e19 = peg$otherExpectation("required whitespace");
|
|
191
|
+
const peg$e5 = peg$literalExpectation("-", false);
|
|
192
|
+
const peg$e6 = peg$literalExpectation(">", false);
|
|
193
|
+
const peg$e7 = peg$classExpectation([["a", "z"], ["A", "Z"], "_"], false, false, false);
|
|
194
|
+
const peg$e8 = peg$classExpectation([["a", "z"], ["A", "Z"], ["0", "9"], "_", "-"], false, false, false);
|
|
195
|
+
const peg$e9 = peg$literalExpectation("cap:", false);
|
|
196
|
+
const peg$e10 = peg$anyExpectation();
|
|
197
|
+
const peg$e11 = peg$otherExpectation("newline");
|
|
198
|
+
const peg$e12 = peg$literalExpectation("\r\n", false);
|
|
199
|
+
const peg$e13 = peg$classExpectation(["\n", "\r"], false, false, false);
|
|
200
|
+
const peg$e14 = peg$literalExpectation("\"", false);
|
|
201
|
+
const peg$e15 = peg$literalExpectation("\\\"", false);
|
|
202
|
+
const peg$e16 = peg$literalExpectation("\\\\", false);
|
|
203
|
+
const peg$e17 = peg$classExpectation([" ", "\t", "\r", "\n"], false, false, false);
|
|
204
|
+
const peg$e18 = peg$otherExpectation("required whitespace");
|
|
207
205
|
|
|
208
206
|
function peg$f0(stmts) { return stmts; }
|
|
209
207
|
function peg$f1(inner) { return inner; }
|
|
@@ -211,18 +209,16 @@ function peg$parse(input, options) {
|
|
|
211
209
|
function peg$f3(a, c) {
|
|
212
210
|
return { type: 'header', alias: a.value, capUrn: c.value, location: location(), aliasLocation: a.location, capUrnLocation: c.location };
|
|
213
211
|
}
|
|
214
|
-
function peg$f4(s,
|
|
215
|
-
return { type: 'wiring', sources: s.values, capAlias:
|
|
212
|
+
function peg$f4(s, c, t) {
|
|
213
|
+
return { type: 'wiring', sources: s.values, capAlias: c.value, target: t.value, location: location(), sourceLocations: s.locations, capAliasLocation: c.location, targetLocation: t.location };
|
|
216
214
|
}
|
|
217
215
|
function peg$f5(a) { return { values: [a.value], locations: [a.location] }; }
|
|
218
216
|
function peg$f6(first, a) { return a; }
|
|
219
217
|
function peg$f7(first, rest) {
|
|
220
218
|
return { values: [first.value, ...rest.map(r => r.value)], locations: [first.location, ...rest.map(r => r.location)] };
|
|
221
219
|
}
|
|
222
|
-
function peg$f8(a) { return {
|
|
223
|
-
function peg$f9(
|
|
224
|
-
function peg$f10(a) { return { value: a, location: location() }; }
|
|
225
|
-
function peg$f11(c) { return { value: c, location: location() }; }
|
|
220
|
+
function peg$f8(a) { return { value: a, location: location() }; }
|
|
221
|
+
function peg$f9(c) { return { value: c, location: location() }; }
|
|
226
222
|
let peg$currPos = options.peg$currPos | 0;
|
|
227
223
|
let peg$savedPos = peg$currPos;
|
|
228
224
|
const peg$posDetailsCache = [{ line: 1, column: 1 }];
|
|
@@ -515,7 +511,7 @@ function peg$parse(input, options) {
|
|
|
515
511
|
s3 = peg$parsearrow();
|
|
516
512
|
if (s3 !== peg$FAILED) {
|
|
517
513
|
s4 = peg$parse_();
|
|
518
|
-
s5 = peg$
|
|
514
|
+
s5 = peg$parsealias_loc();
|
|
519
515
|
if (s5 !== peg$FAILED) {
|
|
520
516
|
s6 = peg$parse_();
|
|
521
517
|
s7 = peg$parsearrow();
|
|
@@ -673,70 +669,27 @@ function peg$parse(input, options) {
|
|
|
673
669
|
return s0;
|
|
674
670
|
}
|
|
675
671
|
|
|
676
|
-
function peg$parseloop_cap_loc() {
|
|
677
|
-
let s0, s1, s2, s3;
|
|
678
|
-
|
|
679
|
-
s0 = peg$currPos;
|
|
680
|
-
if (input.substr(peg$currPos, 4) === peg$c5) {
|
|
681
|
-
s1 = peg$c5;
|
|
682
|
-
peg$currPos += 4;
|
|
683
|
-
} else {
|
|
684
|
-
s1 = peg$FAILED;
|
|
685
|
-
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
|
686
|
-
}
|
|
687
|
-
if (s1 !== peg$FAILED) {
|
|
688
|
-
s2 = peg$parse__();
|
|
689
|
-
if (s2 !== peg$FAILED) {
|
|
690
|
-
s3 = peg$parsealias_loc();
|
|
691
|
-
if (s3 !== peg$FAILED) {
|
|
692
|
-
peg$savedPos = s0;
|
|
693
|
-
s0 = peg$f8(s3);
|
|
694
|
-
} else {
|
|
695
|
-
peg$currPos = s0;
|
|
696
|
-
s0 = peg$FAILED;
|
|
697
|
-
}
|
|
698
|
-
} else {
|
|
699
|
-
peg$currPos = s0;
|
|
700
|
-
s0 = peg$FAILED;
|
|
701
|
-
}
|
|
702
|
-
} else {
|
|
703
|
-
peg$currPos = s0;
|
|
704
|
-
s0 = peg$FAILED;
|
|
705
|
-
}
|
|
706
|
-
if (s0 === peg$FAILED) {
|
|
707
|
-
s0 = peg$currPos;
|
|
708
|
-
s1 = peg$parsealias_loc();
|
|
709
|
-
if (s1 !== peg$FAILED) {
|
|
710
|
-
peg$savedPos = s0;
|
|
711
|
-
s1 = peg$f9(s1);
|
|
712
|
-
}
|
|
713
|
-
s0 = s1;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
return s0;
|
|
717
|
-
}
|
|
718
|
-
|
|
719
672
|
function peg$parsearrow() {
|
|
720
673
|
let s0, s1, s2;
|
|
721
674
|
|
|
722
675
|
s0 = peg$currPos;
|
|
723
676
|
s1 = [];
|
|
724
677
|
if (input.charCodeAt(peg$currPos) === 45) {
|
|
725
|
-
s2 = peg$
|
|
678
|
+
s2 = peg$c5;
|
|
726
679
|
peg$currPos++;
|
|
727
680
|
} else {
|
|
728
681
|
s2 = peg$FAILED;
|
|
729
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
682
|
+
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
|
730
683
|
}
|
|
731
684
|
if (s2 !== peg$FAILED) {
|
|
732
685
|
while (s2 !== peg$FAILED) {
|
|
733
686
|
s1.push(s2);
|
|
734
687
|
if (input.charCodeAt(peg$currPos) === 45) {
|
|
735
|
-
s2 = peg$
|
|
688
|
+
s2 = peg$c5;
|
|
736
689
|
peg$currPos++;
|
|
737
690
|
} else {
|
|
738
691
|
s2 = peg$FAILED;
|
|
739
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
692
|
+
if (peg$silentFails === 0) { peg$fail(peg$e5); }
|
|
740
693
|
}
|
|
741
694
|
}
|
|
742
695
|
} else {
|
|
@@ -744,11 +697,11 @@ function peg$parse(input, options) {
|
|
|
744
697
|
}
|
|
745
698
|
if (s1 !== peg$FAILED) {
|
|
746
699
|
if (input.charCodeAt(peg$currPos) === 62) {
|
|
747
|
-
s2 = peg$
|
|
700
|
+
s2 = peg$c6;
|
|
748
701
|
peg$currPos++;
|
|
749
702
|
} else {
|
|
750
703
|
s2 = peg$FAILED;
|
|
751
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
704
|
+
if (peg$silentFails === 0) { peg$fail(peg$e6); }
|
|
752
705
|
}
|
|
753
706
|
if (s2 !== peg$FAILED) {
|
|
754
707
|
s1 = [s1, s2];
|
|
@@ -772,7 +725,7 @@ function peg$parse(input, options) {
|
|
|
772
725
|
s1 = peg$parsealias();
|
|
773
726
|
if (s1 !== peg$FAILED) {
|
|
774
727
|
peg$savedPos = s0;
|
|
775
|
-
s1 = peg$
|
|
728
|
+
s1 = peg$f8(s1);
|
|
776
729
|
}
|
|
777
730
|
s0 = s1;
|
|
778
731
|
|
|
@@ -789,7 +742,7 @@ function peg$parse(input, options) {
|
|
|
789
742
|
peg$currPos++;
|
|
790
743
|
} else {
|
|
791
744
|
s2 = peg$FAILED;
|
|
792
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
745
|
+
if (peg$silentFails === 0) { peg$fail(peg$e7); }
|
|
793
746
|
}
|
|
794
747
|
if (s2 !== peg$FAILED) {
|
|
795
748
|
s3 = [];
|
|
@@ -798,7 +751,7 @@ function peg$parse(input, options) {
|
|
|
798
751
|
peg$currPos++;
|
|
799
752
|
} else {
|
|
800
753
|
s4 = peg$FAILED;
|
|
801
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
754
|
+
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
802
755
|
}
|
|
803
756
|
while (s4 !== peg$FAILED) {
|
|
804
757
|
s3.push(s4);
|
|
@@ -807,7 +760,7 @@ function peg$parse(input, options) {
|
|
|
807
760
|
peg$currPos++;
|
|
808
761
|
} else {
|
|
809
762
|
s4 = peg$FAILED;
|
|
810
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
763
|
+
if (peg$silentFails === 0) { peg$fail(peg$e8); }
|
|
811
764
|
}
|
|
812
765
|
}
|
|
813
766
|
s2 = [s2, s3];
|
|
@@ -832,7 +785,7 @@ function peg$parse(input, options) {
|
|
|
832
785
|
s1 = peg$parsecap_urn();
|
|
833
786
|
if (s1 !== peg$FAILED) {
|
|
834
787
|
peg$savedPos = s0;
|
|
835
|
-
s1 = peg$
|
|
788
|
+
s1 = peg$f9(s1);
|
|
836
789
|
}
|
|
837
790
|
s0 = s1;
|
|
838
791
|
|
|
@@ -844,12 +797,12 @@ function peg$parse(input, options) {
|
|
|
844
797
|
|
|
845
798
|
s0 = peg$currPos;
|
|
846
799
|
s1 = peg$currPos;
|
|
847
|
-
if (input.substr(peg$currPos, 4) === peg$
|
|
848
|
-
s2 = peg$
|
|
800
|
+
if (input.substr(peg$currPos, 4) === peg$c7) {
|
|
801
|
+
s2 = peg$c7;
|
|
849
802
|
peg$currPos += 4;
|
|
850
803
|
} else {
|
|
851
804
|
s2 = peg$FAILED;
|
|
852
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
805
|
+
if (peg$silentFails === 0) { peg$fail(peg$e9); }
|
|
853
806
|
}
|
|
854
807
|
if (s2 !== peg$FAILED) {
|
|
855
808
|
s3 = [];
|
|
@@ -912,7 +865,7 @@ function peg$parse(input, options) {
|
|
|
912
865
|
peg$currPos++;
|
|
913
866
|
} else {
|
|
914
867
|
s3 = peg$FAILED;
|
|
915
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
868
|
+
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
|
916
869
|
}
|
|
917
870
|
if (s3 !== peg$FAILED) {
|
|
918
871
|
s1 = [s1, s2, s3];
|
|
@@ -938,12 +891,12 @@ function peg$parse(input, options) {
|
|
|
938
891
|
let s0, s1;
|
|
939
892
|
|
|
940
893
|
peg$silentFails++;
|
|
941
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
942
|
-
s0 = peg$
|
|
894
|
+
if (input.substr(peg$currPos, 2) === peg$c8) {
|
|
895
|
+
s0 = peg$c8;
|
|
943
896
|
peg$currPos += 2;
|
|
944
897
|
} else {
|
|
945
898
|
s0 = peg$FAILED;
|
|
946
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
899
|
+
if (peg$silentFails === 0) { peg$fail(peg$e12); }
|
|
947
900
|
}
|
|
948
901
|
if (s0 === peg$FAILED) {
|
|
949
902
|
s0 = input.charAt(peg$currPos);
|
|
@@ -951,13 +904,13 @@ function peg$parse(input, options) {
|
|
|
951
904
|
peg$currPos++;
|
|
952
905
|
} else {
|
|
953
906
|
s0 = peg$FAILED;
|
|
954
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
907
|
+
if (peg$silentFails === 0) { peg$fail(peg$e13); }
|
|
955
908
|
}
|
|
956
909
|
}
|
|
957
910
|
peg$silentFails--;
|
|
958
911
|
if (s0 === peg$FAILED) {
|
|
959
912
|
s1 = peg$FAILED;
|
|
960
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
913
|
+
if (peg$silentFails === 0) { peg$fail(peg$e11); }
|
|
961
914
|
}
|
|
962
915
|
|
|
963
916
|
return s0;
|
|
@@ -968,39 +921,39 @@ function peg$parse(input, options) {
|
|
|
968
921
|
|
|
969
922
|
s0 = peg$currPos;
|
|
970
923
|
if (input.charCodeAt(peg$currPos) === 34) {
|
|
971
|
-
s1 = peg$
|
|
924
|
+
s1 = peg$c9;
|
|
972
925
|
peg$currPos++;
|
|
973
926
|
} else {
|
|
974
927
|
s1 = peg$FAILED;
|
|
975
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
928
|
+
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
|
976
929
|
}
|
|
977
930
|
if (s1 !== peg$FAILED) {
|
|
978
931
|
s2 = [];
|
|
979
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
980
|
-
s3 = peg$
|
|
932
|
+
if (input.substr(peg$currPos, 2) === peg$c10) {
|
|
933
|
+
s3 = peg$c10;
|
|
981
934
|
peg$currPos += 2;
|
|
982
935
|
} else {
|
|
983
936
|
s3 = peg$FAILED;
|
|
984
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
937
|
+
if (peg$silentFails === 0) { peg$fail(peg$e15); }
|
|
985
938
|
}
|
|
986
939
|
if (s3 === peg$FAILED) {
|
|
987
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
988
|
-
s3 = peg$
|
|
940
|
+
if (input.substr(peg$currPos, 2) === peg$c11) {
|
|
941
|
+
s3 = peg$c11;
|
|
989
942
|
peg$currPos += 2;
|
|
990
943
|
} else {
|
|
991
944
|
s3 = peg$FAILED;
|
|
992
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
945
|
+
if (peg$silentFails === 0) { peg$fail(peg$e16); }
|
|
993
946
|
}
|
|
994
947
|
if (s3 === peg$FAILED) {
|
|
995
948
|
s3 = peg$currPos;
|
|
996
949
|
s4 = peg$currPos;
|
|
997
950
|
peg$silentFails++;
|
|
998
951
|
if (input.charCodeAt(peg$currPos) === 34) {
|
|
999
|
-
s5 = peg$
|
|
952
|
+
s5 = peg$c9;
|
|
1000
953
|
peg$currPos++;
|
|
1001
954
|
} else {
|
|
1002
955
|
s5 = peg$FAILED;
|
|
1003
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
956
|
+
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
|
1004
957
|
}
|
|
1005
958
|
peg$silentFails--;
|
|
1006
959
|
if (s5 === peg$FAILED) {
|
|
@@ -1015,7 +968,7 @@ function peg$parse(input, options) {
|
|
|
1015
968
|
peg$currPos++;
|
|
1016
969
|
} else {
|
|
1017
970
|
s5 = peg$FAILED;
|
|
1018
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
971
|
+
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
|
1019
972
|
}
|
|
1020
973
|
if (s5 !== peg$FAILED) {
|
|
1021
974
|
s4 = [s4, s5];
|
|
@@ -1032,31 +985,31 @@ function peg$parse(input, options) {
|
|
|
1032
985
|
}
|
|
1033
986
|
while (s3 !== peg$FAILED) {
|
|
1034
987
|
s2.push(s3);
|
|
1035
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
1036
|
-
s3 = peg$
|
|
988
|
+
if (input.substr(peg$currPos, 2) === peg$c10) {
|
|
989
|
+
s3 = peg$c10;
|
|
1037
990
|
peg$currPos += 2;
|
|
1038
991
|
} else {
|
|
1039
992
|
s3 = peg$FAILED;
|
|
1040
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
993
|
+
if (peg$silentFails === 0) { peg$fail(peg$e15); }
|
|
1041
994
|
}
|
|
1042
995
|
if (s3 === peg$FAILED) {
|
|
1043
|
-
if (input.substr(peg$currPos, 2) === peg$
|
|
1044
|
-
s3 = peg$
|
|
996
|
+
if (input.substr(peg$currPos, 2) === peg$c11) {
|
|
997
|
+
s3 = peg$c11;
|
|
1045
998
|
peg$currPos += 2;
|
|
1046
999
|
} else {
|
|
1047
1000
|
s3 = peg$FAILED;
|
|
1048
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1001
|
+
if (peg$silentFails === 0) { peg$fail(peg$e16); }
|
|
1049
1002
|
}
|
|
1050
1003
|
if (s3 === peg$FAILED) {
|
|
1051
1004
|
s3 = peg$currPos;
|
|
1052
1005
|
s4 = peg$currPos;
|
|
1053
1006
|
peg$silentFails++;
|
|
1054
1007
|
if (input.charCodeAt(peg$currPos) === 34) {
|
|
1055
|
-
s5 = peg$
|
|
1008
|
+
s5 = peg$c9;
|
|
1056
1009
|
peg$currPos++;
|
|
1057
1010
|
} else {
|
|
1058
1011
|
s5 = peg$FAILED;
|
|
1059
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1012
|
+
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
|
1060
1013
|
}
|
|
1061
1014
|
peg$silentFails--;
|
|
1062
1015
|
if (s5 === peg$FAILED) {
|
|
@@ -1071,7 +1024,7 @@ function peg$parse(input, options) {
|
|
|
1071
1024
|
peg$currPos++;
|
|
1072
1025
|
} else {
|
|
1073
1026
|
s5 = peg$FAILED;
|
|
1074
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1027
|
+
if (peg$silentFails === 0) { peg$fail(peg$e10); }
|
|
1075
1028
|
}
|
|
1076
1029
|
if (s5 !== peg$FAILED) {
|
|
1077
1030
|
s4 = [s4, s5];
|
|
@@ -1088,11 +1041,11 @@ function peg$parse(input, options) {
|
|
|
1088
1041
|
}
|
|
1089
1042
|
}
|
|
1090
1043
|
if (input.charCodeAt(peg$currPos) === 34) {
|
|
1091
|
-
s3 = peg$
|
|
1044
|
+
s3 = peg$c9;
|
|
1092
1045
|
peg$currPos++;
|
|
1093
1046
|
} else {
|
|
1094
1047
|
s3 = peg$FAILED;
|
|
1095
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1048
|
+
if (peg$silentFails === 0) { peg$fail(peg$e14); }
|
|
1096
1049
|
}
|
|
1097
1050
|
if (s3 !== peg$FAILED) {
|
|
1098
1051
|
s1 = [s1, s2, s3];
|
|
@@ -1119,7 +1072,7 @@ function peg$parse(input, options) {
|
|
|
1119
1072
|
peg$currPos++;
|
|
1120
1073
|
} else {
|
|
1121
1074
|
s1 = peg$FAILED;
|
|
1122
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1075
|
+
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
|
1123
1076
|
}
|
|
1124
1077
|
while (s1 !== peg$FAILED) {
|
|
1125
1078
|
s0.push(s1);
|
|
@@ -1128,7 +1081,7 @@ function peg$parse(input, options) {
|
|
|
1128
1081
|
peg$currPos++;
|
|
1129
1082
|
} else {
|
|
1130
1083
|
s1 = peg$FAILED;
|
|
1131
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1084
|
+
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
|
1132
1085
|
}
|
|
1133
1086
|
}
|
|
1134
1087
|
peg$silentFails--;
|
|
@@ -1146,7 +1099,7 @@ function peg$parse(input, options) {
|
|
|
1146
1099
|
peg$currPos++;
|
|
1147
1100
|
} else {
|
|
1148
1101
|
s1 = peg$FAILED;
|
|
1149
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1102
|
+
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
|
1150
1103
|
}
|
|
1151
1104
|
if (s1 !== peg$FAILED) {
|
|
1152
1105
|
while (s1 !== peg$FAILED) {
|
|
@@ -1156,7 +1109,7 @@ function peg$parse(input, options) {
|
|
|
1156
1109
|
peg$currPos++;
|
|
1157
1110
|
} else {
|
|
1158
1111
|
s1 = peg$FAILED;
|
|
1159
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1112
|
+
if (peg$silentFails === 0) { peg$fail(peg$e17); }
|
|
1160
1113
|
}
|
|
1161
1114
|
}
|
|
1162
1115
|
} else {
|
|
@@ -1165,7 +1118,7 @@ function peg$parse(input, options) {
|
|
|
1165
1118
|
peg$silentFails--;
|
|
1166
1119
|
if (s0 === peg$FAILED) {
|
|
1167
1120
|
s1 = peg$FAILED;
|
|
1168
|
-
if (peg$silentFails === 0) { peg$fail(peg$
|
|
1121
|
+
if (peg$silentFails === 0) { peg$fail(peg$e18); }
|
|
1169
1122
|
}
|
|
1170
1123
|
|
|
1171
1124
|
return s0;
|
package/machine.pegjs
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
// extract cap:in="media:ext=pdf";extract;out="media:txt;textable"
|
|
14
14
|
// doc -> extract -> text
|
|
15
15
|
// (thumbnail, model_spec) -> describe -> description
|
|
16
|
-
// pages -> LOOP p2t -> texts
|
|
17
16
|
//
|
|
18
17
|
// Both forms can be freely mixed in the same program.
|
|
19
18
|
|
|
@@ -29,9 +28,12 @@ header = a:alias_loc __ c:cap_urn_loc {
|
|
|
29
28
|
return { type: 'header', alias: a.value, capUrn: c.value, location: location(), aliasLocation: a.location, capUrnLocation: c.location };
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
// Wiring: source ->
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
// Wiring: source -> cap -> target
|
|
32
|
+
//
|
|
33
|
+
// The cap position is a plain alias. There is no LOOP marker: per-item map
|
|
34
|
+
// (`is_loop`) is a derived cardinality property, never authored syntax.
|
|
35
|
+
wiring = s:source_loc _ arrow _ c:alias_loc _ arrow _ t:alias_loc {
|
|
36
|
+
return { type: 'wiring', sources: s.values, capAlias: c.value, target: t.value, location: location(), sourceLocations: s.locations, capAliasLocation: c.location, targetLocation: t.location };
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
source_loc = group_loc / single_alias_loc
|
|
@@ -42,9 +44,6 @@ group_loc = "(" _ first:alias_loc rest:("," _ a:alias_loc { return a; })+ _ ")"
|
|
|
42
44
|
return { values: [first.value, ...rest.map(r => r.value)], locations: [first.location, ...rest.map(r => r.location)] };
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
loop_cap_loc = "LOOP" __ a:alias_loc { return { alias: a.value, isLoop: true, location: a.location }; }
|
|
46
|
-
/ a:alias_loc { return { alias: a.value, isLoop: false, location: a.location }; }
|
|
47
|
-
|
|
48
47
|
arrow = "-"+ ">"
|
|
49
48
|
|
|
50
49
|
// Alias with location tracking
|
package/package.json
CHANGED