Add support for parallel actions using & syntax [#11]
This commit is contained in:
parent
a1caf2b16a
commit
918c62f049
|
@ -1643,6 +1643,87 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractParallel(target, stages) {
|
||||||
|
for(const stage of stages) {
|
||||||
|
if(!stage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(stage.type === 'parallel') {
|
||||||
|
extractParallel(target, stage.stages);
|
||||||
|
} else {
|
||||||
|
target.push(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAgentConflicts(allStages) {
|
||||||
|
const createIDs = flatMap(
|
||||||
|
allStages
|
||||||
|
.filter((stage) => (stage.type === 'agent begin')),
|
||||||
|
(stage) => stage.agentIDs
|
||||||
|
);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'agent end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for(const id of stage.agentIDs) {
|
||||||
|
if(createIDs.indexOf(id) !== -1) {
|
||||||
|
return 'Cannot create and destroy ' + id + ' simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkReferenceConflicts(allStages) {
|
||||||
|
const leftIDs = allStages
|
||||||
|
.filter((stage) => (stage.type === 'block begin'))
|
||||||
|
.map((stage) => stage.left);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'block end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(leftIDs.indexOf(stage.left) !== -1) {
|
||||||
|
return 'Cannot create and destroy reference simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDelayedConflicts(allStages) {
|
||||||
|
const tags = allStages
|
||||||
|
.filter((stage) => (stage.type === 'connect-delay-begin'))
|
||||||
|
.map((stage) => stage.tag);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'connect-delay-end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(tags.indexOf(stage.tag) !== -1) {
|
||||||
|
return 'Cannot start and finish delayed connection simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorForParallel(existing, latest) {
|
||||||
|
if(!existing) {
|
||||||
|
return 'Nothing to run statement in parallel with';
|
||||||
|
}
|
||||||
|
|
||||||
|
const allStages = [];
|
||||||
|
extractParallel(allStages, [existing]);
|
||||||
|
extractParallel(allStages, [latest]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
checkAgentConflicts(allStages) ||
|
||||||
|
checkReferenceConflicts(allStages) ||
|
||||||
|
checkDelayedConflicts(allStages)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function swapBegin(stage, mode) {
|
function swapBegin(stage, mode) {
|
||||||
if(stage.type === 'agent begin') {
|
if(stage.type === 'agent begin') {
|
||||||
stage.mode = mode;
|
stage.mode = mode;
|
||||||
|
@ -1758,37 +1839,75 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addStage(stage, isVisible = true) {
|
addStage(stage, {isVisible = true, parallel = false} = {}) {
|
||||||
|
if(!stage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(isVisible) {
|
||||||
|
this.currentNest.hasContent = true;
|
||||||
|
}
|
||||||
|
if(typeof stage.ln === 'undefined') {
|
||||||
|
stage.ln = this.latestLine;
|
||||||
|
}
|
||||||
|
const {stages} = this.currentSection;
|
||||||
|
if(parallel) {
|
||||||
|
const target = last(stages);
|
||||||
|
const err = errorForParallel(target, stage);
|
||||||
|
if(err) {
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
const pstage = this.makeParallel([target, stage]);
|
||||||
|
pstage.ln = stage.ln;
|
||||||
|
-- stages.length;
|
||||||
|
stages.push(pstage);
|
||||||
|
} else {
|
||||||
|
stages.push(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addImpStage(stage, {parallel = false} = {}) {
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(typeof stage.ln === 'undefined') {
|
if(typeof stage.ln === 'undefined') {
|
||||||
stage.ln = this.latestLine;
|
stage.ln = this.latestLine;
|
||||||
}
|
}
|
||||||
this.currentSection.stages.push(stage);
|
const {stages} = this.currentSection;
|
||||||
if(isVisible) {
|
if(parallel) {
|
||||||
this.currentNest.hasContent = true;
|
const target = stages[stages.length - 2];
|
||||||
|
if(!target) {
|
||||||
|
throw new Error('Nothing to run statement in parallel with');
|
||||||
|
}
|
||||||
|
if(errorForParallel(target, stage)) {
|
||||||
|
stages.splice(stages.length - 1, 0, stage);
|
||||||
|
} else {
|
||||||
|
const pstage = this.makeParallel([target, stage]);
|
||||||
|
pstage.ln = stage.ln;
|
||||||
|
stages.splice(stages.length - 2, 1, pstage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stages.push(stage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addParallelStages(stages) {
|
makeParallel(stages) {
|
||||||
const viableStages = stages.filter((stage) => Boolean(stage));
|
const viableStages = [];
|
||||||
|
extractParallel(viableStages, stages);
|
||||||
if(viableStages.length === 0) {
|
if(viableStages.length === 0) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
if(viableStages.length === 1) {
|
if(viableStages.length === 1) {
|
||||||
this.addStage(viableStages[0]);
|
return viableStages[0];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
viableStages.forEach((stage) => {
|
viableStages.forEach((stage) => {
|
||||||
if(typeof stage.ln === 'undefined') {
|
if(typeof stage.ln === 'undefined') {
|
||||||
stage.ln = this.latestLine;
|
stage.ln = this.latestLine;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.addStage({
|
return {
|
||||||
stages: viableStages,
|
stages: viableStages,
|
||||||
type: 'parallel',
|
type: 'parallel',
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
defineGAgents(gAgents) {
|
defineGAgents(gAgents) {
|
||||||
|
@ -2004,7 +2123,10 @@
|
||||||
this.currentNest = last(this.nesting);
|
this.currentNest = last(this.nesting);
|
||||||
this.currentSection = last(this.currentNest.sections);
|
this.currentSection = last(this.currentNest.sections);
|
||||||
|
|
||||||
if(nested.hasContent) {
|
if(!nested.hasContent) {
|
||||||
|
throw new Error('Empty block');
|
||||||
|
}
|
||||||
|
|
||||||
this.defineGAgents(nested.gAgents);
|
this.defineGAgents(nested.gAgents);
|
||||||
addBounds(
|
addBounds(
|
||||||
this.gAgents,
|
this.gAgents,
|
||||||
|
@ -2021,9 +2143,6 @@
|
||||||
right: nested.rightGAgent.id,
|
right: nested.rightGAgent.id,
|
||||||
type: 'block end',
|
type: 'block end',
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
throw new Error('Empty block');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeGroupDetails(pAgents, alias) {
|
makeGroupDetails(pAgents, alias) {
|
||||||
|
@ -2065,7 +2184,7 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleGroupBegin({agents, blockType, tag, label, alias}) {
|
handleGroupBegin({agents, blockType, tag, label, alias, parallel}) {
|
||||||
const details = this.makeGroupDetails(agents, alias);
|
const details = this.makeGroupDetails(agents, alias);
|
||||||
|
|
||||||
details.gAgentsContained.forEach((gAgent) => {
|
details.gAgentsContained.forEach((gAgent) => {
|
||||||
|
@ -2075,7 +2194,10 @@
|
||||||
this.updateGAgentState(gAgent, {covered: true});
|
this.updateGAgentState(gAgent, {covered: true});
|
||||||
});
|
});
|
||||||
this.activeGroups.set(alias, details);
|
this.activeGroups.set(alias, details);
|
||||||
this.addStage(this.setGAgentVis(details.gAgents, true, 'box'));
|
this.addImpStage(
|
||||||
|
this.setGAgentVis(details.gAgents, true, 'box'),
|
||||||
|
{parallel}
|
||||||
|
);
|
||||||
this.addStage({
|
this.addStage({
|
||||||
blockType,
|
blockType,
|
||||||
canHide: false,
|
canHide: false,
|
||||||
|
@ -2084,7 +2206,7 @@
|
||||||
right: details.rightGAgent.id,
|
right: details.rightGAgent.id,
|
||||||
tag: this.textFormatter(tag),
|
tag: this.textFormatter(tag),
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
});
|
}, {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
endGroup({name}) {
|
endGroup({name}) {
|
||||||
|
@ -2111,7 +2233,7 @@
|
||||||
|
|
||||||
handleMark({name}) {
|
handleMark({name}) {
|
||||||
this.markers.add(name);
|
this.markers.add(name);
|
||||||
this.addStage({name, type: 'mark'}, false);
|
this.addStage({name, type: 'mark'}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDivider({mode, height, label}) {
|
handleDivider({mode, height, label}) {
|
||||||
|
@ -2120,14 +2242,14 @@
|
||||||
height,
|
height,
|
||||||
mode,
|
mode,
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
}, false);
|
}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAsync({target}) {
|
handleAsync({target}) {
|
||||||
if(target !== '' && !this.markers.has(target)) {
|
if(target !== '' && !this.markers.has(target)) {
|
||||||
throw new Error('Unknown marker: ' + target);
|
throw new Error('Unknown marker: ' + target);
|
||||||
}
|
}
|
||||||
this.addStage({target, type: 'async'}, false);
|
this.addStage({target, type: 'async'}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLabelPattern({pattern}) {
|
handleLabelPattern({pattern}) {
|
||||||
|
@ -2281,7 +2403,7 @@
|
||||||
return gAgents;
|
return gAgents;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePartialConnect(agents) {
|
_handlePartialConnect(agents, parallel) {
|
||||||
const flags = this.filterConnectFlags(agents);
|
const flags = this.filterConnectFlags(agents);
|
||||||
|
|
||||||
const gAgents = agents.map(this.toGAgent);
|
const gAgents = agents.map(this.toGAgent);
|
||||||
|
@ -2298,19 +2420,22 @@
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
.filter((gAgent) => !gAgent.isVirtualSource)
|
.filter((gAgent) => !gAgent.isVirtualSource)
|
||||||
);
|
);
|
||||||
this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box'));
|
this.addImpStage(
|
||||||
|
this.setGAgentVis(implicitBeginGAgents, true, 'box'),
|
||||||
|
{parallel}
|
||||||
|
);
|
||||||
|
|
||||||
return {flags, gAgents};
|
return {flags, gAgents};
|
||||||
}
|
}
|
||||||
|
|
||||||
_makeConnectParallelStages(flags, connectStage) {
|
_makeConnectParallelStages(flags, connectStage) {
|
||||||
return [
|
return this.makeParallel([
|
||||||
this.setGAgentVis(flags.beginGAgents, true, 'box', true),
|
this.setGAgentVis(flags.beginGAgents, true, 'box', true),
|
||||||
this.setGAgentHighlight(flags.startGAgents, true, true),
|
this.setGAgentHighlight(flags.startGAgents, true, true),
|
||||||
connectStage,
|
connectStage,
|
||||||
this.setGAgentHighlight(flags.stopGAgents, false, true),
|
this.setGAgentHighlight(flags.stopGAgents, false, true),
|
||||||
this.setGAgentVis(flags.endGAgents, false, 'cross', true),
|
this.setGAgentVis(flags.endGAgents, false, 'cross', true),
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isSelfConnect(agents) {
|
_isSelfConnect(agents) {
|
||||||
|
@ -2325,7 +2450,7 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnect({agents, label, options}) {
|
handleConnect({agents, label, options, parallel}) {
|
||||||
if(this._isSelfConnect(agents)) {
|
if(this._isSelfConnect(agents)) {
|
||||||
const tag = {};
|
const tag = {};
|
||||||
this.handleConnectDelayBegin({
|
this.handleConnectDelayBegin({
|
||||||
|
@ -2333,6 +2458,7 @@
|
||||||
ln: 0,
|
ln: 0,
|
||||||
options,
|
options,
|
||||||
tag,
|
tag,
|
||||||
|
parallel,
|
||||||
});
|
});
|
||||||
this.handleConnectDelayEnd({
|
this.handleConnectDelayEnd({
|
||||||
agent: agents[1],
|
agent: agents[1],
|
||||||
|
@ -2343,7 +2469,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {flags, gAgents} = this._handlePartialConnect(agents);
|
let {flags, gAgents} = this._handlePartialConnect(agents, parallel);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
||||||
gAgents = this.expandVirtualSourceAgents(gAgents);
|
gAgents = this.expandVirtualSourceAgents(gAgents);
|
||||||
|
@ -2355,19 +2481,19 @@
|
||||||
type: 'connect',
|
type: 'connect',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectStage),
|
||||||
connectStage
|
{parallel}
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnectDelayBegin({agent, tag, options, ln}) {
|
handleConnectDelayBegin({agent, tag, options, ln, parallel}) {
|
||||||
const dcs = this.currentSection.delayedConnections;
|
const dcs = this.currentSection.delayedConnections;
|
||||||
if(dcs.has(tag)) {
|
if(dcs.has(tag)) {
|
||||||
throw new Error('Duplicate delayed connection "' + tag + '"');
|
throw new Error('Duplicate delayed connection "' + tag + '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {flags, gAgents} = this._handlePartialConnect([agent]);
|
const {flags, gAgents} = this._handlePartialConnect([agent], parallel);
|
||||||
const uniqueTag = this.nextVirtualAgentName();
|
const uniqueTag = this.nextVirtualAgentName();
|
||||||
|
|
||||||
const connectStage = {
|
const connectStage = {
|
||||||
|
@ -2380,20 +2506,20 @@
|
||||||
|
|
||||||
dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag});
|
dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag});
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectStage),
|
||||||
connectStage
|
{parallel}
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnectDelayEnd({agent, tag, label, options}) {
|
handleConnectDelayEnd({agent, tag, label, options, parallel}) {
|
||||||
const dcs = this.currentSection.delayedConnections;
|
const dcs = this.currentSection.delayedConnections;
|
||||||
const dcInfo = dcs.get(tag);
|
const dcInfo = dcs.get(tag);
|
||||||
if(!dcInfo) {
|
if(!dcInfo) {
|
||||||
throw new Error('Unknown delayed connection "' + tag + '"');
|
throw new Error('Unknown delayed connection "' + tag + '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
let {flags, gAgents} = this._handlePartialConnect([agent]);
|
let {flags, gAgents} = this._handlePartialConnect([agent], parallel);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection([
|
gAgents = this.expandGroupedGAgentConnection([
|
||||||
...dcInfo.gAgents,
|
...dcInfo.gAgents,
|
||||||
|
@ -2422,15 +2548,15 @@
|
||||||
type: 'connect-delay-end',
|
type: 'connect-delay-end',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectEndStage),
|
||||||
connectEndStage
|
{parallel}
|
||||||
));
|
);
|
||||||
|
|
||||||
dcs.delete(tag);
|
dcs.delete(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNote({type, agents, mode, label}) {
|
handleNote({type, agents, mode, label, parallel}) {
|
||||||
let gAgents = null;
|
let gAgents = null;
|
||||||
if(agents.length === 0) {
|
if(agents.length === 0) {
|
||||||
gAgents = NOTE_DEFAULT_G_AGENTS[type] || [];
|
gAgents = NOTE_DEFAULT_G_AGENTS[type] || [];
|
||||||
|
@ -2446,15 +2572,14 @@
|
||||||
throw new Error('note between requires at least 2 agents');
|
throw new Error('note between requires at least 2 agents');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addStage(this.setGAgentVis(gAgents, true, 'box'));
|
|
||||||
this.defineGAgents(gAgents);
|
this.defineGAgents(gAgents);
|
||||||
|
this.addImpStage(this.setGAgentVis(gAgents, true, 'box'), {parallel});
|
||||||
this.addStage({
|
this.addStage({
|
||||||
agentIDs,
|
agentIDs,
|
||||||
label: this.textFormatter(label),
|
label: this.textFormatter(label),
|
||||||
mode,
|
mode,
|
||||||
type,
|
type,
|
||||||
});
|
}, {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentDefine({agents}) {
|
handleAgentDefine({agents}) {
|
||||||
|
@ -2482,13 +2607,13 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentBegin({agents, mode}) {
|
handleAgentBegin({agents, mode, parallel}) {
|
||||||
const gAgents = agents.map(this.toGAgent);
|
const gAgents = agents.map(this.toGAgent);
|
||||||
this.validateGAgents(gAgents);
|
this.validateGAgents(gAgents);
|
||||||
this.addStage(this.setGAgentVis(gAgents, true, mode, true));
|
this.addStage(this.setGAgentVis(gAgents, true, mode, true), {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentEnd({agents, mode}) {
|
handleAgentEnd({agents, mode, parallel}) {
|
||||||
const groupPAgents = (agents
|
const groupPAgents = (agents
|
||||||
.filter((pAgent) => this.activeGroups.has(pAgent.name))
|
.filter((pAgent) => this.activeGroups.has(pAgent.name))
|
||||||
);
|
);
|
||||||
|
@ -2497,11 +2622,11 @@
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
);
|
);
|
||||||
this.validateGAgents(gAgents);
|
this.validateGAgents(gAgents);
|
||||||
this.addParallelStages([
|
this.addStage(this.makeParallel([
|
||||||
this.setGAgentHighlight(gAgents, false),
|
this.setGAgentHighlight(gAgents, false),
|
||||||
this.setGAgentVis(gAgents, false, mode, true),
|
this.setGAgentVis(gAgents, false, mode, true),
|
||||||
...groupPAgents.map(this.endGroup),
|
...groupPAgents.map(this.endGroup),
|
||||||
]);
|
]), {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStage(stage) {
|
handleStage(stage) {
|
||||||
|
@ -2570,10 +2695,10 @@
|
||||||
this._checkSectionEnd();
|
this._checkSectionEnd();
|
||||||
|
|
||||||
const terminators = meta.terminators || 'none';
|
const terminators = meta.terminators || 'none';
|
||||||
this.addParallelStages([
|
this.addStage(this.makeParallel([
|
||||||
this.setGAgentHighlight(this.gAgents, false),
|
this.setGAgentHighlight(this.gAgents, false),
|
||||||
this.setGAgentVis(this.gAgents, false, terminators),
|
this.setGAgentVis(this.gAgents, false, terminators),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
this._finalise(globals);
|
this._finalise(globals);
|
||||||
|
|
||||||
|
@ -3026,6 +3151,14 @@
|
||||||
'red',
|
'red',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const PARALLEL_TASKS = [
|
||||||
|
'begin',
|
||||||
|
'end',
|
||||||
|
'note',
|
||||||
|
'state',
|
||||||
|
'text',
|
||||||
|
];
|
||||||
|
|
||||||
const makeCommands = ((() => {
|
const makeCommands = ((() => {
|
||||||
function agentListTo(exit, next = 1) {
|
function agentListTo(exit, next = 1) {
|
||||||
return {
|
return {
|
||||||
|
@ -3386,10 +3519,25 @@
|
||||||
}},
|
}},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (arrows) => ({
|
return (arrows) => {
|
||||||
|
const arrowConnect = makeCMConnect(arrows);
|
||||||
|
|
||||||
|
const parallel = {};
|
||||||
|
for(const task of PARALLEL_TASKS) {
|
||||||
|
parallel[task] = BASE_THEN[task];
|
||||||
|
}
|
||||||
|
Object.assign(parallel, arrowConnect);
|
||||||
|
|
||||||
|
return {
|
||||||
type: 'error line-error',
|
type: 'error line-error',
|
||||||
then: Object.assign({}, BASE_THEN, makeCMConnect(arrows)),
|
then: Object.assign(
|
||||||
});
|
{},
|
||||||
|
BASE_THEN,
|
||||||
|
{'&': {type: 'keyword', then: parallel}},
|
||||||
|
arrowConnect
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
})());
|
})());
|
||||||
|
|
||||||
/* eslint-enable sort-keys */
|
/* eslint-enable sort-keys */
|
||||||
|
@ -3637,6 +3785,7 @@
|
||||||
if(state.currentType === NO_TOKEN) {
|
if(state.currentType === NO_TOKEN) {
|
||||||
if(stream.sol()) {
|
if(stream.sol()) {
|
||||||
state.line.length = 0;
|
state.line.length = 0;
|
||||||
|
state.valid = true;
|
||||||
}
|
}
|
||||||
if(!this._tokenBegin(stream, state)) {
|
if(!this._tokenBegin(stream, state)) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -4070,36 +4219,40 @@
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable sort-keys */ // Maybe later
|
const BLOCK_TYPES = new Map();
|
||||||
|
BLOCK_TYPES.set('if', {
|
||||||
const BLOCK_TYPES = {
|
|
||||||
'if': {
|
|
||||||
type: 'block begin',
|
|
||||||
blockType: 'if',
|
blockType: 'if',
|
||||||
|
skip: [],
|
||||||
tag: 'if',
|
tag: 'if',
|
||||||
skip: [],
|
type: 'block begin',
|
||||||
},
|
});
|
||||||
'else': {
|
BLOCK_TYPES.set('else', {
|
||||||
type: 'block split',
|
|
||||||
blockType: 'else',
|
blockType: 'else',
|
||||||
tag: 'else',
|
|
||||||
skip: ['if'],
|
skip: ['if'],
|
||||||
},
|
tag: 'else',
|
||||||
'repeat': {
|
type: 'block split',
|
||||||
type: 'block begin',
|
});
|
||||||
|
BLOCK_TYPES.set('repeat', {
|
||||||
blockType: 'repeat',
|
blockType: 'repeat',
|
||||||
|
skip: [],
|
||||||
tag: 'repeat',
|
tag: 'repeat',
|
||||||
skip: [],
|
|
||||||
},
|
|
||||||
'group': {
|
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
|
});
|
||||||
|
BLOCK_TYPES.set('group', {
|
||||||
blockType: 'group',
|
blockType: 'group',
|
||||||
tag: '',
|
|
||||||
skip: [],
|
skip: [],
|
||||||
},
|
tag: '',
|
||||||
};
|
type: 'block begin',
|
||||||
|
});
|
||||||
|
|
||||||
const CONNECT = {
|
const CONNECT = {
|
||||||
|
agentFlags: {
|
||||||
|
'!': {flag: 'end'},
|
||||||
|
'*': {allowBlankName: true, blankNameFlag: 'source', flag: 'begin'},
|
||||||
|
'+': {flag: 'start'},
|
||||||
|
'-': {flag: 'stop'},
|
||||||
|
},
|
||||||
|
|
||||||
types: ((() => {
|
types: ((() => {
|
||||||
const lTypes = [
|
const lTypes = [
|
||||||
{tok: '', type: 0},
|
{tok: '', type: 0},
|
||||||
|
@ -4125,21 +4278,14 @@
|
||||||
|
|
||||||
arrows.forEach((arrow) => {
|
arrows.forEach((arrow) => {
|
||||||
types.set(arrow.map((part) => part.tok).join(''), {
|
types.set(arrow.map((part) => part.tok).join(''), {
|
||||||
line: arrow[1].type,
|
|
||||||
left: arrow[0].type,
|
left: arrow[0].type,
|
||||||
|
line: arrow[1].type,
|
||||||
right: arrow[2].type,
|
right: arrow[2].type,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
})()),
|
})()),
|
||||||
|
|
||||||
agentFlags: {
|
|
||||||
'*': {flag: 'begin', allowBlankName: true, blankNameFlag: 'source'},
|
|
||||||
'+': {flag: 'start'},
|
|
||||||
'-': {flag: 'stop'},
|
|
||||||
'!': {flag: 'end'},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TERMINATOR_TYPES = [
|
const TERMINATOR_TYPES = [
|
||||||
|
@ -4150,73 +4296,70 @@
|
||||||
'bar',
|
'bar',
|
||||||
];
|
];
|
||||||
|
|
||||||
const NOTE_TYPES = {
|
const NOTE_TYPES = new Map();
|
||||||
'text': {
|
NOTE_TYPES.set('text', {
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
types: {
|
types: {
|
||||||
'left': {
|
'left': {
|
||||||
type: 'note left',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note left',
|
||||||
},
|
},
|
||||||
'right': {
|
'right': {
|
||||||
type: 'note right',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note right',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
'note': {
|
NOTE_TYPES.set('note', {
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
types: {
|
types: {
|
||||||
'over': {
|
'between': {
|
||||||
type: 'note over',
|
|
||||||
skip: [],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 2,
|
||||||
|
skip: [],
|
||||||
|
type: 'note between',
|
||||||
},
|
},
|
||||||
'left': {
|
'left': {
|
||||||
type: 'note left',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note left',
|
||||||
|
},
|
||||||
|
'over': {
|
||||||
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: [],
|
||||||
|
type: 'note over',
|
||||||
},
|
},
|
||||||
'right': {
|
'right': {
|
||||||
type: 'note right',
|
max: Number.POSITIVE_INFINITY,
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
min: 0,
|
||||||
max: Number.POSITIVE_INFINITY,
|
skip: ['of'],
|
||||||
},
|
type: 'note right',
|
||||||
'between': {
|
|
||||||
type: 'note between',
|
|
||||||
skip: [],
|
|
||||||
min: 2,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
'state': {
|
NOTE_TYPES.set('state', {
|
||||||
mode: 'state',
|
mode: 'state',
|
||||||
types: {
|
types: {
|
||||||
'over': {type: 'note over', skip: [], min: 1, max: 1},
|
'over': {max: 1, min: 1, skip: [], type: 'note over'},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const DIVIDER_TYPES = {
|
const DIVIDER_TYPES = new Map();
|
||||||
'line': {defaultHeight: 6},
|
DIVIDER_TYPES.set('line', {defaultHeight: 6});
|
||||||
'space': {defaultHeight: 6},
|
DIVIDER_TYPES.set('space', {defaultHeight: 6});
|
||||||
'delay': {defaultHeight: 30},
|
DIVIDER_TYPES.set('delay', {defaultHeight: 30});
|
||||||
'tear': {defaultHeight: 6},
|
DIVIDER_TYPES.set('tear', {defaultHeight: 6});
|
||||||
};
|
|
||||||
|
|
||||||
const AGENT_MANIPULATION_TYPES = {
|
const AGENT_MANIPULATION_TYPES = new Map();
|
||||||
'define': {type: 'agent define'},
|
AGENT_MANIPULATION_TYPES.set('define', {type: 'agent define'});
|
||||||
'begin': {type: 'agent begin', mode: 'box'},
|
AGENT_MANIPULATION_TYPES.set('begin', {mode: 'box', type: 'agent begin'});
|
||||||
'end': {type: 'agent end', mode: 'cross'},
|
AGENT_MANIPULATION_TYPES.set('end', {mode: 'cross', type: 'agent end'});
|
||||||
};
|
|
||||||
|
|
||||||
function makeError(message, token = null) {
|
function makeError(message, token = null) {
|
||||||
let suffix = '';
|
let suffix = '';
|
||||||
|
@ -4316,8 +4459,8 @@
|
||||||
throw makeError('Missing agent name', errPosToken);
|
throw makeError('Missing agent name', errPosToken);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: joinLabel(line, start, aliasSep),
|
|
||||||
alias: joinLabel(line, aliasSep + 1, end),
|
alias: joinLabel(line, aliasSep + 1, end),
|
||||||
|
name: joinLabel(line, start, aliasSep),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4344,13 +4487,13 @@
|
||||||
blankNameFlags.push(flag.blankNameFlag);
|
blankNameFlags.push(flag.blankNameFlag);
|
||||||
}
|
}
|
||||||
const {name, alias} = readAgentAlias(line, p, end, {
|
const {name, alias} = readAgentAlias(line, p, end, {
|
||||||
enableAlias: aliases,
|
|
||||||
allowBlankName,
|
allowBlankName,
|
||||||
|
enableAlias: aliases,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
name,
|
|
||||||
alias,
|
alias,
|
||||||
flags: name ? flags : blankNameFlags,
|
flags: name ? flags : blankNameFlags,
|
||||||
|
name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4417,7 +4560,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const mode = joinLabel(line, 1, heightSep) || 'line';
|
const mode = joinLabel(line, 1, heightSep) || 'line';
|
||||||
if(!DIVIDER_TYPES[mode]) {
|
if(!DIVIDER_TYPES.has(mode)) {
|
||||||
throw makeError('Unknown divider type', line[1]);
|
throw makeError('Unknown divider type', line[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4425,17 +4568,17 @@
|
||||||
line,
|
line,
|
||||||
heightSep + 2,
|
heightSep + 2,
|
||||||
labelSep,
|
labelSep,
|
||||||
DIVIDER_TYPES[mode].defaultHeight
|
DIVIDER_TYPES.get(mode).defaultHeight
|
||||||
);
|
);
|
||||||
if(Number.isNaN(height) || height < 0) {
|
if(Number.isNaN(height) || height < 0) {
|
||||||
throw makeError('Invalid divider height', line[heightSep + 2]);
|
throw makeError('Invalid divider height', line[heightSep + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'divider',
|
|
||||||
mode,
|
|
||||||
height,
|
height,
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
|
mode,
|
||||||
|
type: 'divider',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4447,8 +4590,8 @@
|
||||||
raw = joinLabel(line, 1);
|
raw = joinLabel(line, 1);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'label pattern',
|
|
||||||
pattern: parsePattern(raw),
|
pattern: parsePattern(raw),
|
||||||
|
type: 'label pattern',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4460,7 +4603,7 @@
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Block
|
{begin: [], fn: (line) => { // Block
|
||||||
const type = BLOCK_TYPES[tokenKeyword(line[0])];
|
const type = BLOCK_TYPES.get(tokenKeyword(line[0]));
|
||||||
if(!type) {
|
if(!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -4470,10 +4613,10 @@
|
||||||
}
|
}
|
||||||
skip = skipOver(line, skip, [':']);
|
skip = skipOver(line, skip, [':']);
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
|
||||||
blockType: type.blockType,
|
blockType: type.blockType,
|
||||||
tag: type.tag,
|
|
||||||
label: joinLabel(line, skip),
|
label: joinLabel(line, skip),
|
||||||
|
tag: type.tag,
|
||||||
|
type: type.type,
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4495,17 +4638,17 @@
|
||||||
throw makeError('Reference must have an alias', line[labelSep]);
|
throw makeError('Reference must have an alias', line[labelSep]);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'group begin',
|
|
||||||
agents,
|
agents,
|
||||||
blockType: 'ref',
|
|
||||||
tag: 'ref',
|
|
||||||
label: def.name,
|
|
||||||
alias: def.alias,
|
alias: def.alias,
|
||||||
|
blockType: 'ref',
|
||||||
|
label: def.name,
|
||||||
|
tag: 'ref',
|
||||||
|
type: 'group begin',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Agent
|
{begin: [], fn: (line) => { // Agent
|
||||||
const type = AGENT_MANIPULATION_TYPES[tokenKeyword(line[0])];
|
const type = AGENT_MANIPULATION_TYPES.get(tokenKeyword(line[0]));
|
||||||
if(!type || line.length <= 1) {
|
if(!type || line.length <= 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -4526,13 +4669,13 @@
|
||||||
target = joinLabel(line, 2, line.length - 1);
|
target = joinLabel(line, 2, line.length - 1);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'async',
|
|
||||||
target,
|
target,
|
||||||
|
type: 'async',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Note
|
{begin: [], fn: (line) => { // Note
|
||||||
const mode = NOTE_TYPES[tokenKeyword(line[0])];
|
const mode = NOTE_TYPES.get(tokenKeyword(line[0]));
|
||||||
const labelSep = findTokens(line, [':']);
|
const labelSep = findTokens(line, [':']);
|
||||||
if(!mode || labelSep === -1) {
|
if(!mode || labelSep === -1) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -4551,10 +4694,10 @@
|
||||||
throw makeError('Too many agents for ' + mode.mode, line[0]);
|
throw makeError('Too many agents for ' + mode.mode, line[0]);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
|
||||||
agents,
|
agents,
|
||||||
mode: mode.mode,
|
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
|
mode: mode.mode,
|
||||||
|
type: type.type,
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4563,7 +4706,7 @@
|
||||||
const connectionToken = findFirstToken(
|
const connectionToken = findFirstToken(
|
||||||
line,
|
line,
|
||||||
CONNECT.types,
|
CONNECT.types,
|
||||||
{start: 0, limit: labelSep - 1}
|
{limit: labelSep - 1, start: 0}
|
||||||
);
|
);
|
||||||
if(!connectionToken) {
|
if(!connectionToken) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -4577,11 +4720,11 @@
|
||||||
|
|
||||||
if(tokenKeyword(line[0]) === '...') {
|
if(tokenKeyword(line[0]) === '...') {
|
||||||
return {
|
return {
|
||||||
type: 'connect-delay-end',
|
|
||||||
tag: joinLabel(line, 1, connectPos),
|
|
||||||
agent: readAgent(line, connectPos + 1, labelSep, readOpts),
|
agent: readAgent(line, connectPos + 1, labelSep, readOpts),
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
tag: joinLabel(line, 1, connectPos),
|
||||||
|
type: 'connect-delay-end',
|
||||||
};
|
};
|
||||||
} else if(tokenKeyword(line[connectPos + 1]) === '...') {
|
} else if(tokenKeyword(line[connectPos + 1]) === '...') {
|
||||||
if(labelSep !== line.length) {
|
if(labelSep !== line.length) {
|
||||||
|
@ -4591,20 +4734,20 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'connect-delay-begin',
|
|
||||||
tag: joinLabel(line, connectPos + 2, labelSep),
|
|
||||||
agent: readAgent(line, 0, connectPos, readOpts),
|
agent: readAgent(line, 0, connectPos, readOpts),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
tag: joinLabel(line, connectPos + 2, labelSep),
|
||||||
|
type: 'connect-delay-begin',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
type: 'connect',
|
|
||||||
agents: [
|
agents: [
|
||||||
readAgent(line, 0, connectPos, readOpts),
|
readAgent(line, 0, connectPos, readOpts),
|
||||||
readAgent(line, connectPos + 1, labelSep, readOpts),
|
readAgent(line, connectPos + 1, labelSep, readOpts),
|
||||||
],
|
],
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
type: 'connect',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
|
@ -4614,8 +4757,8 @@
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'mark',
|
|
||||||
name: joinLabel(line, 0, line.length - 1),
|
name: joinLabel(line, 0, line.length - 1),
|
||||||
|
type: 'mark',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4638,33 +4781,44 @@
|
||||||
options.push(line[i].v);
|
options.push(line[i].v);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'agent options',
|
|
||||||
agent,
|
agent,
|
||||||
options,
|
options,
|
||||||
|
type: 'agent options',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
];
|
];
|
||||||
|
|
||||||
function parseLine(line, {meta, stages}) {
|
function stageFromLine(line, meta) {
|
||||||
let stage = null;
|
|
||||||
for(const {begin, fn} of PARSERS) {
|
for(const {begin, fn} of PARSERS) {
|
||||||
if(skipOver(line, 0, begin) !== begin.length) {
|
if(skipOver(line, 0, begin) !== begin.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
stage = fn(line, meta);
|
const stage = fn(line, meta);
|
||||||
if(stage) {
|
if(stage) {
|
||||||
break;
|
return stage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLine(line, {meta, stages}) {
|
||||||
|
let parallel = false;
|
||||||
|
const [start] = line;
|
||||||
|
if(tokenKeyword(start) === '&') {
|
||||||
|
parallel = true;
|
||||||
|
line.splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stage = stageFromLine(line, meta);
|
||||||
|
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
throw makeError(
|
throw makeError('Unrecognised command: ' + joinLabel(line), line[0]);
|
||||||
'Unrecognised command: ' + joinLabel(line),
|
} else if(typeof stage === 'object') {
|
||||||
line[0]
|
stage.ln = start.b.ln;
|
||||||
);
|
stage.parallel = parallel;
|
||||||
}
|
|
||||||
if(typeof stage === 'object') {
|
|
||||||
stage.ln = line[0].b.ln;
|
|
||||||
stages.push(stage);
|
stages.push(stage);
|
||||||
|
} else if(parallel) {
|
||||||
|
throw makeError('Metadata cannot be parallel', start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4680,12 +4834,12 @@
|
||||||
parseLines(lines, src) {
|
parseLines(lines, src) {
|
||||||
const result = {
|
const result = {
|
||||||
meta: {
|
meta: {
|
||||||
title: '',
|
|
||||||
theme: '',
|
|
||||||
code: src,
|
code: src,
|
||||||
terminators: 'none',
|
|
||||||
headers: 'box',
|
headers: 'box',
|
||||||
|
terminators: 'none',
|
||||||
textFormatter: parseMarkdown,
|
textFormatter: parseMarkdown,
|
||||||
|
theme: '',
|
||||||
|
title: '',
|
||||||
},
|
},
|
||||||
stages: [],
|
stages: [],
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1643,6 +1643,87 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractParallel(target, stages) {
|
||||||
|
for(const stage of stages) {
|
||||||
|
if(!stage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(stage.type === 'parallel') {
|
||||||
|
extractParallel(target, stage.stages);
|
||||||
|
} else {
|
||||||
|
target.push(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAgentConflicts(allStages) {
|
||||||
|
const createIDs = flatMap(
|
||||||
|
allStages
|
||||||
|
.filter((stage) => (stage.type === 'agent begin')),
|
||||||
|
(stage) => stage.agentIDs
|
||||||
|
);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'agent end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for(const id of stage.agentIDs) {
|
||||||
|
if(createIDs.indexOf(id) !== -1) {
|
||||||
|
return 'Cannot create and destroy ' + id + ' simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkReferenceConflicts(allStages) {
|
||||||
|
const leftIDs = allStages
|
||||||
|
.filter((stage) => (stage.type === 'block begin'))
|
||||||
|
.map((stage) => stage.left);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'block end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(leftIDs.indexOf(stage.left) !== -1) {
|
||||||
|
return 'Cannot create and destroy reference simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDelayedConflicts(allStages) {
|
||||||
|
const tags = allStages
|
||||||
|
.filter((stage) => (stage.type === 'connect-delay-begin'))
|
||||||
|
.map((stage) => stage.tag);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'connect-delay-end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(tags.indexOf(stage.tag) !== -1) {
|
||||||
|
return 'Cannot start and finish delayed connection simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorForParallel(existing, latest) {
|
||||||
|
if(!existing) {
|
||||||
|
return 'Nothing to run statement in parallel with';
|
||||||
|
}
|
||||||
|
|
||||||
|
const allStages = [];
|
||||||
|
extractParallel(allStages, [existing]);
|
||||||
|
extractParallel(allStages, [latest]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
checkAgentConflicts(allStages) ||
|
||||||
|
checkReferenceConflicts(allStages) ||
|
||||||
|
checkDelayedConflicts(allStages)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function swapBegin(stage, mode) {
|
function swapBegin(stage, mode) {
|
||||||
if(stage.type === 'agent begin') {
|
if(stage.type === 'agent begin') {
|
||||||
stage.mode = mode;
|
stage.mode = mode;
|
||||||
|
@ -1758,37 +1839,75 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addStage(stage, isVisible = true) {
|
addStage(stage, {isVisible = true, parallel = false} = {}) {
|
||||||
|
if(!stage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(isVisible) {
|
||||||
|
this.currentNest.hasContent = true;
|
||||||
|
}
|
||||||
|
if(typeof stage.ln === 'undefined') {
|
||||||
|
stage.ln = this.latestLine;
|
||||||
|
}
|
||||||
|
const {stages} = this.currentSection;
|
||||||
|
if(parallel) {
|
||||||
|
const target = last(stages);
|
||||||
|
const err = errorForParallel(target, stage);
|
||||||
|
if(err) {
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
const pstage = this.makeParallel([target, stage]);
|
||||||
|
pstage.ln = stage.ln;
|
||||||
|
-- stages.length;
|
||||||
|
stages.push(pstage);
|
||||||
|
} else {
|
||||||
|
stages.push(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addImpStage(stage, {parallel = false} = {}) {
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(typeof stage.ln === 'undefined') {
|
if(typeof stage.ln === 'undefined') {
|
||||||
stage.ln = this.latestLine;
|
stage.ln = this.latestLine;
|
||||||
}
|
}
|
||||||
this.currentSection.stages.push(stage);
|
const {stages} = this.currentSection;
|
||||||
if(isVisible) {
|
if(parallel) {
|
||||||
this.currentNest.hasContent = true;
|
const target = stages[stages.length - 2];
|
||||||
|
if(!target) {
|
||||||
|
throw new Error('Nothing to run statement in parallel with');
|
||||||
|
}
|
||||||
|
if(errorForParallel(target, stage)) {
|
||||||
|
stages.splice(stages.length - 1, 0, stage);
|
||||||
|
} else {
|
||||||
|
const pstage = this.makeParallel([target, stage]);
|
||||||
|
pstage.ln = stage.ln;
|
||||||
|
stages.splice(stages.length - 2, 1, pstage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stages.push(stage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addParallelStages(stages) {
|
makeParallel(stages) {
|
||||||
const viableStages = stages.filter((stage) => Boolean(stage));
|
const viableStages = [];
|
||||||
|
extractParallel(viableStages, stages);
|
||||||
if(viableStages.length === 0) {
|
if(viableStages.length === 0) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
if(viableStages.length === 1) {
|
if(viableStages.length === 1) {
|
||||||
this.addStage(viableStages[0]);
|
return viableStages[0];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
viableStages.forEach((stage) => {
|
viableStages.forEach((stage) => {
|
||||||
if(typeof stage.ln === 'undefined') {
|
if(typeof stage.ln === 'undefined') {
|
||||||
stage.ln = this.latestLine;
|
stage.ln = this.latestLine;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.addStage({
|
return {
|
||||||
stages: viableStages,
|
stages: viableStages,
|
||||||
type: 'parallel',
|
type: 'parallel',
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
defineGAgents(gAgents) {
|
defineGAgents(gAgents) {
|
||||||
|
@ -2004,7 +2123,10 @@
|
||||||
this.currentNest = last(this.nesting);
|
this.currentNest = last(this.nesting);
|
||||||
this.currentSection = last(this.currentNest.sections);
|
this.currentSection = last(this.currentNest.sections);
|
||||||
|
|
||||||
if(nested.hasContent) {
|
if(!nested.hasContent) {
|
||||||
|
throw new Error('Empty block');
|
||||||
|
}
|
||||||
|
|
||||||
this.defineGAgents(nested.gAgents);
|
this.defineGAgents(nested.gAgents);
|
||||||
addBounds(
|
addBounds(
|
||||||
this.gAgents,
|
this.gAgents,
|
||||||
|
@ -2021,9 +2143,6 @@
|
||||||
right: nested.rightGAgent.id,
|
right: nested.rightGAgent.id,
|
||||||
type: 'block end',
|
type: 'block end',
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
throw new Error('Empty block');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeGroupDetails(pAgents, alias) {
|
makeGroupDetails(pAgents, alias) {
|
||||||
|
@ -2065,7 +2184,7 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleGroupBegin({agents, blockType, tag, label, alias}) {
|
handleGroupBegin({agents, blockType, tag, label, alias, parallel}) {
|
||||||
const details = this.makeGroupDetails(agents, alias);
|
const details = this.makeGroupDetails(agents, alias);
|
||||||
|
|
||||||
details.gAgentsContained.forEach((gAgent) => {
|
details.gAgentsContained.forEach((gAgent) => {
|
||||||
|
@ -2075,7 +2194,10 @@
|
||||||
this.updateGAgentState(gAgent, {covered: true});
|
this.updateGAgentState(gAgent, {covered: true});
|
||||||
});
|
});
|
||||||
this.activeGroups.set(alias, details);
|
this.activeGroups.set(alias, details);
|
||||||
this.addStage(this.setGAgentVis(details.gAgents, true, 'box'));
|
this.addImpStage(
|
||||||
|
this.setGAgentVis(details.gAgents, true, 'box'),
|
||||||
|
{parallel}
|
||||||
|
);
|
||||||
this.addStage({
|
this.addStage({
|
||||||
blockType,
|
blockType,
|
||||||
canHide: false,
|
canHide: false,
|
||||||
|
@ -2084,7 +2206,7 @@
|
||||||
right: details.rightGAgent.id,
|
right: details.rightGAgent.id,
|
||||||
tag: this.textFormatter(tag),
|
tag: this.textFormatter(tag),
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
});
|
}, {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
endGroup({name}) {
|
endGroup({name}) {
|
||||||
|
@ -2111,7 +2233,7 @@
|
||||||
|
|
||||||
handleMark({name}) {
|
handleMark({name}) {
|
||||||
this.markers.add(name);
|
this.markers.add(name);
|
||||||
this.addStage({name, type: 'mark'}, false);
|
this.addStage({name, type: 'mark'}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDivider({mode, height, label}) {
|
handleDivider({mode, height, label}) {
|
||||||
|
@ -2120,14 +2242,14 @@
|
||||||
height,
|
height,
|
||||||
mode,
|
mode,
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
}, false);
|
}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAsync({target}) {
|
handleAsync({target}) {
|
||||||
if(target !== '' && !this.markers.has(target)) {
|
if(target !== '' && !this.markers.has(target)) {
|
||||||
throw new Error('Unknown marker: ' + target);
|
throw new Error('Unknown marker: ' + target);
|
||||||
}
|
}
|
||||||
this.addStage({target, type: 'async'}, false);
|
this.addStage({target, type: 'async'}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLabelPattern({pattern}) {
|
handleLabelPattern({pattern}) {
|
||||||
|
@ -2281,7 +2403,7 @@
|
||||||
return gAgents;
|
return gAgents;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePartialConnect(agents) {
|
_handlePartialConnect(agents, parallel) {
|
||||||
const flags = this.filterConnectFlags(agents);
|
const flags = this.filterConnectFlags(agents);
|
||||||
|
|
||||||
const gAgents = agents.map(this.toGAgent);
|
const gAgents = agents.map(this.toGAgent);
|
||||||
|
@ -2298,19 +2420,22 @@
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
.filter((gAgent) => !gAgent.isVirtualSource)
|
.filter((gAgent) => !gAgent.isVirtualSource)
|
||||||
);
|
);
|
||||||
this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box'));
|
this.addImpStage(
|
||||||
|
this.setGAgentVis(implicitBeginGAgents, true, 'box'),
|
||||||
|
{parallel}
|
||||||
|
);
|
||||||
|
|
||||||
return {flags, gAgents};
|
return {flags, gAgents};
|
||||||
}
|
}
|
||||||
|
|
||||||
_makeConnectParallelStages(flags, connectStage) {
|
_makeConnectParallelStages(flags, connectStage) {
|
||||||
return [
|
return this.makeParallel([
|
||||||
this.setGAgentVis(flags.beginGAgents, true, 'box', true),
|
this.setGAgentVis(flags.beginGAgents, true, 'box', true),
|
||||||
this.setGAgentHighlight(flags.startGAgents, true, true),
|
this.setGAgentHighlight(flags.startGAgents, true, true),
|
||||||
connectStage,
|
connectStage,
|
||||||
this.setGAgentHighlight(flags.stopGAgents, false, true),
|
this.setGAgentHighlight(flags.stopGAgents, false, true),
|
||||||
this.setGAgentVis(flags.endGAgents, false, 'cross', true),
|
this.setGAgentVis(flags.endGAgents, false, 'cross', true),
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isSelfConnect(agents) {
|
_isSelfConnect(agents) {
|
||||||
|
@ -2325,7 +2450,7 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnect({agents, label, options}) {
|
handleConnect({agents, label, options, parallel}) {
|
||||||
if(this._isSelfConnect(agents)) {
|
if(this._isSelfConnect(agents)) {
|
||||||
const tag = {};
|
const tag = {};
|
||||||
this.handleConnectDelayBegin({
|
this.handleConnectDelayBegin({
|
||||||
|
@ -2333,6 +2458,7 @@
|
||||||
ln: 0,
|
ln: 0,
|
||||||
options,
|
options,
|
||||||
tag,
|
tag,
|
||||||
|
parallel,
|
||||||
});
|
});
|
||||||
this.handleConnectDelayEnd({
|
this.handleConnectDelayEnd({
|
||||||
agent: agents[1],
|
agent: agents[1],
|
||||||
|
@ -2343,7 +2469,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {flags, gAgents} = this._handlePartialConnect(agents);
|
let {flags, gAgents} = this._handlePartialConnect(agents, parallel);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
||||||
gAgents = this.expandVirtualSourceAgents(gAgents);
|
gAgents = this.expandVirtualSourceAgents(gAgents);
|
||||||
|
@ -2355,19 +2481,19 @@
|
||||||
type: 'connect',
|
type: 'connect',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectStage),
|
||||||
connectStage
|
{parallel}
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnectDelayBegin({agent, tag, options, ln}) {
|
handleConnectDelayBegin({agent, tag, options, ln, parallel}) {
|
||||||
const dcs = this.currentSection.delayedConnections;
|
const dcs = this.currentSection.delayedConnections;
|
||||||
if(dcs.has(tag)) {
|
if(dcs.has(tag)) {
|
||||||
throw new Error('Duplicate delayed connection "' + tag + '"');
|
throw new Error('Duplicate delayed connection "' + tag + '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {flags, gAgents} = this._handlePartialConnect([agent]);
|
const {flags, gAgents} = this._handlePartialConnect([agent], parallel);
|
||||||
const uniqueTag = this.nextVirtualAgentName();
|
const uniqueTag = this.nextVirtualAgentName();
|
||||||
|
|
||||||
const connectStage = {
|
const connectStage = {
|
||||||
|
@ -2380,20 +2506,20 @@
|
||||||
|
|
||||||
dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag});
|
dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag});
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectStage),
|
||||||
connectStage
|
{parallel}
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnectDelayEnd({agent, tag, label, options}) {
|
handleConnectDelayEnd({agent, tag, label, options, parallel}) {
|
||||||
const dcs = this.currentSection.delayedConnections;
|
const dcs = this.currentSection.delayedConnections;
|
||||||
const dcInfo = dcs.get(tag);
|
const dcInfo = dcs.get(tag);
|
||||||
if(!dcInfo) {
|
if(!dcInfo) {
|
||||||
throw new Error('Unknown delayed connection "' + tag + '"');
|
throw new Error('Unknown delayed connection "' + tag + '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
let {flags, gAgents} = this._handlePartialConnect([agent]);
|
let {flags, gAgents} = this._handlePartialConnect([agent], parallel);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection([
|
gAgents = this.expandGroupedGAgentConnection([
|
||||||
...dcInfo.gAgents,
|
...dcInfo.gAgents,
|
||||||
|
@ -2422,15 +2548,15 @@
|
||||||
type: 'connect-delay-end',
|
type: 'connect-delay-end',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectEndStage),
|
||||||
connectEndStage
|
{parallel}
|
||||||
));
|
);
|
||||||
|
|
||||||
dcs.delete(tag);
|
dcs.delete(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNote({type, agents, mode, label}) {
|
handleNote({type, agents, mode, label, parallel}) {
|
||||||
let gAgents = null;
|
let gAgents = null;
|
||||||
if(agents.length === 0) {
|
if(agents.length === 0) {
|
||||||
gAgents = NOTE_DEFAULT_G_AGENTS[type] || [];
|
gAgents = NOTE_DEFAULT_G_AGENTS[type] || [];
|
||||||
|
@ -2446,15 +2572,14 @@
|
||||||
throw new Error('note between requires at least 2 agents');
|
throw new Error('note between requires at least 2 agents');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addStage(this.setGAgentVis(gAgents, true, 'box'));
|
|
||||||
this.defineGAgents(gAgents);
|
this.defineGAgents(gAgents);
|
||||||
|
this.addImpStage(this.setGAgentVis(gAgents, true, 'box'), {parallel});
|
||||||
this.addStage({
|
this.addStage({
|
||||||
agentIDs,
|
agentIDs,
|
||||||
label: this.textFormatter(label),
|
label: this.textFormatter(label),
|
||||||
mode,
|
mode,
|
||||||
type,
|
type,
|
||||||
});
|
}, {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentDefine({agents}) {
|
handleAgentDefine({agents}) {
|
||||||
|
@ -2482,13 +2607,13 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentBegin({agents, mode}) {
|
handleAgentBegin({agents, mode, parallel}) {
|
||||||
const gAgents = agents.map(this.toGAgent);
|
const gAgents = agents.map(this.toGAgent);
|
||||||
this.validateGAgents(gAgents);
|
this.validateGAgents(gAgents);
|
||||||
this.addStage(this.setGAgentVis(gAgents, true, mode, true));
|
this.addStage(this.setGAgentVis(gAgents, true, mode, true), {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentEnd({agents, mode}) {
|
handleAgentEnd({agents, mode, parallel}) {
|
||||||
const groupPAgents = (agents
|
const groupPAgents = (agents
|
||||||
.filter((pAgent) => this.activeGroups.has(pAgent.name))
|
.filter((pAgent) => this.activeGroups.has(pAgent.name))
|
||||||
);
|
);
|
||||||
|
@ -2497,11 +2622,11 @@
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
);
|
);
|
||||||
this.validateGAgents(gAgents);
|
this.validateGAgents(gAgents);
|
||||||
this.addParallelStages([
|
this.addStage(this.makeParallel([
|
||||||
this.setGAgentHighlight(gAgents, false),
|
this.setGAgentHighlight(gAgents, false),
|
||||||
this.setGAgentVis(gAgents, false, mode, true),
|
this.setGAgentVis(gAgents, false, mode, true),
|
||||||
...groupPAgents.map(this.endGroup),
|
...groupPAgents.map(this.endGroup),
|
||||||
]);
|
]), {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStage(stage) {
|
handleStage(stage) {
|
||||||
|
@ -2570,10 +2695,10 @@
|
||||||
this._checkSectionEnd();
|
this._checkSectionEnd();
|
||||||
|
|
||||||
const terminators = meta.terminators || 'none';
|
const terminators = meta.terminators || 'none';
|
||||||
this.addParallelStages([
|
this.addStage(this.makeParallel([
|
||||||
this.setGAgentHighlight(this.gAgents, false),
|
this.setGAgentHighlight(this.gAgents, false),
|
||||||
this.setGAgentVis(this.gAgents, false, terminators),
|
this.setGAgentVis(this.gAgents, false, terminators),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
this._finalise(globals);
|
this._finalise(globals);
|
||||||
|
|
||||||
|
@ -3026,6 +3151,14 @@
|
||||||
'red',
|
'red',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const PARALLEL_TASKS = [
|
||||||
|
'begin',
|
||||||
|
'end',
|
||||||
|
'note',
|
||||||
|
'state',
|
||||||
|
'text',
|
||||||
|
];
|
||||||
|
|
||||||
const makeCommands = ((() => {
|
const makeCommands = ((() => {
|
||||||
function agentListTo(exit, next = 1) {
|
function agentListTo(exit, next = 1) {
|
||||||
return {
|
return {
|
||||||
|
@ -3386,10 +3519,25 @@
|
||||||
}},
|
}},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (arrows) => ({
|
return (arrows) => {
|
||||||
|
const arrowConnect = makeCMConnect(arrows);
|
||||||
|
|
||||||
|
const parallel = {};
|
||||||
|
for(const task of PARALLEL_TASKS) {
|
||||||
|
parallel[task] = BASE_THEN[task];
|
||||||
|
}
|
||||||
|
Object.assign(parallel, arrowConnect);
|
||||||
|
|
||||||
|
return {
|
||||||
type: 'error line-error',
|
type: 'error line-error',
|
||||||
then: Object.assign({}, BASE_THEN, makeCMConnect(arrows)),
|
then: Object.assign(
|
||||||
});
|
{},
|
||||||
|
BASE_THEN,
|
||||||
|
{'&': {type: 'keyword', then: parallel}},
|
||||||
|
arrowConnect
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
})());
|
})());
|
||||||
|
|
||||||
/* eslint-enable sort-keys */
|
/* eslint-enable sort-keys */
|
||||||
|
@ -3637,6 +3785,7 @@
|
||||||
if(state.currentType === NO_TOKEN) {
|
if(state.currentType === NO_TOKEN) {
|
||||||
if(stream.sol()) {
|
if(stream.sol()) {
|
||||||
state.line.length = 0;
|
state.line.length = 0;
|
||||||
|
state.valid = true;
|
||||||
}
|
}
|
||||||
if(!this._tokenBegin(stream, state)) {
|
if(!this._tokenBegin(stream, state)) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -4070,36 +4219,40 @@
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable sort-keys */ // Maybe later
|
const BLOCK_TYPES = new Map();
|
||||||
|
BLOCK_TYPES.set('if', {
|
||||||
const BLOCK_TYPES = {
|
|
||||||
'if': {
|
|
||||||
type: 'block begin',
|
|
||||||
blockType: 'if',
|
blockType: 'if',
|
||||||
|
skip: [],
|
||||||
tag: 'if',
|
tag: 'if',
|
||||||
skip: [],
|
type: 'block begin',
|
||||||
},
|
});
|
||||||
'else': {
|
BLOCK_TYPES.set('else', {
|
||||||
type: 'block split',
|
|
||||||
blockType: 'else',
|
blockType: 'else',
|
||||||
tag: 'else',
|
|
||||||
skip: ['if'],
|
skip: ['if'],
|
||||||
},
|
tag: 'else',
|
||||||
'repeat': {
|
type: 'block split',
|
||||||
type: 'block begin',
|
});
|
||||||
|
BLOCK_TYPES.set('repeat', {
|
||||||
blockType: 'repeat',
|
blockType: 'repeat',
|
||||||
|
skip: [],
|
||||||
tag: 'repeat',
|
tag: 'repeat',
|
||||||
skip: [],
|
|
||||||
},
|
|
||||||
'group': {
|
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
|
});
|
||||||
|
BLOCK_TYPES.set('group', {
|
||||||
blockType: 'group',
|
blockType: 'group',
|
||||||
tag: '',
|
|
||||||
skip: [],
|
skip: [],
|
||||||
},
|
tag: '',
|
||||||
};
|
type: 'block begin',
|
||||||
|
});
|
||||||
|
|
||||||
const CONNECT = {
|
const CONNECT = {
|
||||||
|
agentFlags: {
|
||||||
|
'!': {flag: 'end'},
|
||||||
|
'*': {allowBlankName: true, blankNameFlag: 'source', flag: 'begin'},
|
||||||
|
'+': {flag: 'start'},
|
||||||
|
'-': {flag: 'stop'},
|
||||||
|
},
|
||||||
|
|
||||||
types: ((() => {
|
types: ((() => {
|
||||||
const lTypes = [
|
const lTypes = [
|
||||||
{tok: '', type: 0},
|
{tok: '', type: 0},
|
||||||
|
@ -4125,21 +4278,14 @@
|
||||||
|
|
||||||
arrows.forEach((arrow) => {
|
arrows.forEach((arrow) => {
|
||||||
types.set(arrow.map((part) => part.tok).join(''), {
|
types.set(arrow.map((part) => part.tok).join(''), {
|
||||||
line: arrow[1].type,
|
|
||||||
left: arrow[0].type,
|
left: arrow[0].type,
|
||||||
|
line: arrow[1].type,
|
||||||
right: arrow[2].type,
|
right: arrow[2].type,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
})()),
|
})()),
|
||||||
|
|
||||||
agentFlags: {
|
|
||||||
'*': {flag: 'begin', allowBlankName: true, blankNameFlag: 'source'},
|
|
||||||
'+': {flag: 'start'},
|
|
||||||
'-': {flag: 'stop'},
|
|
||||||
'!': {flag: 'end'},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TERMINATOR_TYPES = [
|
const TERMINATOR_TYPES = [
|
||||||
|
@ -4150,73 +4296,70 @@
|
||||||
'bar',
|
'bar',
|
||||||
];
|
];
|
||||||
|
|
||||||
const NOTE_TYPES = {
|
const NOTE_TYPES = new Map();
|
||||||
'text': {
|
NOTE_TYPES.set('text', {
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
types: {
|
types: {
|
||||||
'left': {
|
'left': {
|
||||||
type: 'note left',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note left',
|
||||||
},
|
},
|
||||||
'right': {
|
'right': {
|
||||||
type: 'note right',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note right',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
'note': {
|
NOTE_TYPES.set('note', {
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
types: {
|
types: {
|
||||||
'over': {
|
'between': {
|
||||||
type: 'note over',
|
|
||||||
skip: [],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 2,
|
||||||
|
skip: [],
|
||||||
|
type: 'note between',
|
||||||
},
|
},
|
||||||
'left': {
|
'left': {
|
||||||
type: 'note left',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note left',
|
||||||
|
},
|
||||||
|
'over': {
|
||||||
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: [],
|
||||||
|
type: 'note over',
|
||||||
},
|
},
|
||||||
'right': {
|
'right': {
|
||||||
type: 'note right',
|
max: Number.POSITIVE_INFINITY,
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
min: 0,
|
||||||
max: Number.POSITIVE_INFINITY,
|
skip: ['of'],
|
||||||
},
|
type: 'note right',
|
||||||
'between': {
|
|
||||||
type: 'note between',
|
|
||||||
skip: [],
|
|
||||||
min: 2,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
'state': {
|
NOTE_TYPES.set('state', {
|
||||||
mode: 'state',
|
mode: 'state',
|
||||||
types: {
|
types: {
|
||||||
'over': {type: 'note over', skip: [], min: 1, max: 1},
|
'over': {max: 1, min: 1, skip: [], type: 'note over'},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const DIVIDER_TYPES = {
|
const DIVIDER_TYPES = new Map();
|
||||||
'line': {defaultHeight: 6},
|
DIVIDER_TYPES.set('line', {defaultHeight: 6});
|
||||||
'space': {defaultHeight: 6},
|
DIVIDER_TYPES.set('space', {defaultHeight: 6});
|
||||||
'delay': {defaultHeight: 30},
|
DIVIDER_TYPES.set('delay', {defaultHeight: 30});
|
||||||
'tear': {defaultHeight: 6},
|
DIVIDER_TYPES.set('tear', {defaultHeight: 6});
|
||||||
};
|
|
||||||
|
|
||||||
const AGENT_MANIPULATION_TYPES = {
|
const AGENT_MANIPULATION_TYPES = new Map();
|
||||||
'define': {type: 'agent define'},
|
AGENT_MANIPULATION_TYPES.set('define', {type: 'agent define'});
|
||||||
'begin': {type: 'agent begin', mode: 'box'},
|
AGENT_MANIPULATION_TYPES.set('begin', {mode: 'box', type: 'agent begin'});
|
||||||
'end': {type: 'agent end', mode: 'cross'},
|
AGENT_MANIPULATION_TYPES.set('end', {mode: 'cross', type: 'agent end'});
|
||||||
};
|
|
||||||
|
|
||||||
function makeError(message, token = null) {
|
function makeError(message, token = null) {
|
||||||
let suffix = '';
|
let suffix = '';
|
||||||
|
@ -4316,8 +4459,8 @@
|
||||||
throw makeError('Missing agent name', errPosToken);
|
throw makeError('Missing agent name', errPosToken);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: joinLabel(line, start, aliasSep),
|
|
||||||
alias: joinLabel(line, aliasSep + 1, end),
|
alias: joinLabel(line, aliasSep + 1, end),
|
||||||
|
name: joinLabel(line, start, aliasSep),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4344,13 +4487,13 @@
|
||||||
blankNameFlags.push(flag.blankNameFlag);
|
blankNameFlags.push(flag.blankNameFlag);
|
||||||
}
|
}
|
||||||
const {name, alias} = readAgentAlias(line, p, end, {
|
const {name, alias} = readAgentAlias(line, p, end, {
|
||||||
enableAlias: aliases,
|
|
||||||
allowBlankName,
|
allowBlankName,
|
||||||
|
enableAlias: aliases,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
name,
|
|
||||||
alias,
|
alias,
|
||||||
flags: name ? flags : blankNameFlags,
|
flags: name ? flags : blankNameFlags,
|
||||||
|
name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4417,7 +4560,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
const mode = joinLabel(line, 1, heightSep) || 'line';
|
const mode = joinLabel(line, 1, heightSep) || 'line';
|
||||||
if(!DIVIDER_TYPES[mode]) {
|
if(!DIVIDER_TYPES.has(mode)) {
|
||||||
throw makeError('Unknown divider type', line[1]);
|
throw makeError('Unknown divider type', line[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4425,17 +4568,17 @@
|
||||||
line,
|
line,
|
||||||
heightSep + 2,
|
heightSep + 2,
|
||||||
labelSep,
|
labelSep,
|
||||||
DIVIDER_TYPES[mode].defaultHeight
|
DIVIDER_TYPES.get(mode).defaultHeight
|
||||||
);
|
);
|
||||||
if(Number.isNaN(height) || height < 0) {
|
if(Number.isNaN(height) || height < 0) {
|
||||||
throw makeError('Invalid divider height', line[heightSep + 2]);
|
throw makeError('Invalid divider height', line[heightSep + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'divider',
|
|
||||||
mode,
|
|
||||||
height,
|
height,
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
|
mode,
|
||||||
|
type: 'divider',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4447,8 +4590,8 @@
|
||||||
raw = joinLabel(line, 1);
|
raw = joinLabel(line, 1);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'label pattern',
|
|
||||||
pattern: parsePattern(raw),
|
pattern: parsePattern(raw),
|
||||||
|
type: 'label pattern',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4460,7 +4603,7 @@
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Block
|
{begin: [], fn: (line) => { // Block
|
||||||
const type = BLOCK_TYPES[tokenKeyword(line[0])];
|
const type = BLOCK_TYPES.get(tokenKeyword(line[0]));
|
||||||
if(!type) {
|
if(!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -4470,10 +4613,10 @@
|
||||||
}
|
}
|
||||||
skip = skipOver(line, skip, [':']);
|
skip = skipOver(line, skip, [':']);
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
|
||||||
blockType: type.blockType,
|
blockType: type.blockType,
|
||||||
tag: type.tag,
|
|
||||||
label: joinLabel(line, skip),
|
label: joinLabel(line, skip),
|
||||||
|
tag: type.tag,
|
||||||
|
type: type.type,
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4495,17 +4638,17 @@
|
||||||
throw makeError('Reference must have an alias', line[labelSep]);
|
throw makeError('Reference must have an alias', line[labelSep]);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'group begin',
|
|
||||||
agents,
|
agents,
|
||||||
blockType: 'ref',
|
|
||||||
tag: 'ref',
|
|
||||||
label: def.name,
|
|
||||||
alias: def.alias,
|
alias: def.alias,
|
||||||
|
blockType: 'ref',
|
||||||
|
label: def.name,
|
||||||
|
tag: 'ref',
|
||||||
|
type: 'group begin',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Agent
|
{begin: [], fn: (line) => { // Agent
|
||||||
const type = AGENT_MANIPULATION_TYPES[tokenKeyword(line[0])];
|
const type = AGENT_MANIPULATION_TYPES.get(tokenKeyword(line[0]));
|
||||||
if(!type || line.length <= 1) {
|
if(!type || line.length <= 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -4526,13 +4669,13 @@
|
||||||
target = joinLabel(line, 2, line.length - 1);
|
target = joinLabel(line, 2, line.length - 1);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'async',
|
|
||||||
target,
|
target,
|
||||||
|
type: 'async',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Note
|
{begin: [], fn: (line) => { // Note
|
||||||
const mode = NOTE_TYPES[tokenKeyword(line[0])];
|
const mode = NOTE_TYPES.get(tokenKeyword(line[0]));
|
||||||
const labelSep = findTokens(line, [':']);
|
const labelSep = findTokens(line, [':']);
|
||||||
if(!mode || labelSep === -1) {
|
if(!mode || labelSep === -1) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -4551,10 +4694,10 @@
|
||||||
throw makeError('Too many agents for ' + mode.mode, line[0]);
|
throw makeError('Too many agents for ' + mode.mode, line[0]);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
|
||||||
agents,
|
agents,
|
||||||
mode: mode.mode,
|
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
|
mode: mode.mode,
|
||||||
|
type: type.type,
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4563,7 +4706,7 @@
|
||||||
const connectionToken = findFirstToken(
|
const connectionToken = findFirstToken(
|
||||||
line,
|
line,
|
||||||
CONNECT.types,
|
CONNECT.types,
|
||||||
{start: 0, limit: labelSep - 1}
|
{limit: labelSep - 1, start: 0}
|
||||||
);
|
);
|
||||||
if(!connectionToken) {
|
if(!connectionToken) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -4577,11 +4720,11 @@
|
||||||
|
|
||||||
if(tokenKeyword(line[0]) === '...') {
|
if(tokenKeyword(line[0]) === '...') {
|
||||||
return {
|
return {
|
||||||
type: 'connect-delay-end',
|
|
||||||
tag: joinLabel(line, 1, connectPos),
|
|
||||||
agent: readAgent(line, connectPos + 1, labelSep, readOpts),
|
agent: readAgent(line, connectPos + 1, labelSep, readOpts),
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
tag: joinLabel(line, 1, connectPos),
|
||||||
|
type: 'connect-delay-end',
|
||||||
};
|
};
|
||||||
} else if(tokenKeyword(line[connectPos + 1]) === '...') {
|
} else if(tokenKeyword(line[connectPos + 1]) === '...') {
|
||||||
if(labelSep !== line.length) {
|
if(labelSep !== line.length) {
|
||||||
|
@ -4591,20 +4734,20 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'connect-delay-begin',
|
|
||||||
tag: joinLabel(line, connectPos + 2, labelSep),
|
|
||||||
agent: readAgent(line, 0, connectPos, readOpts),
|
agent: readAgent(line, 0, connectPos, readOpts),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
tag: joinLabel(line, connectPos + 2, labelSep),
|
||||||
|
type: 'connect-delay-begin',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
type: 'connect',
|
|
||||||
agents: [
|
agents: [
|
||||||
readAgent(line, 0, connectPos, readOpts),
|
readAgent(line, 0, connectPos, readOpts),
|
||||||
readAgent(line, connectPos + 1, labelSep, readOpts),
|
readAgent(line, connectPos + 1, labelSep, readOpts),
|
||||||
],
|
],
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
type: 'connect',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
|
@ -4614,8 +4757,8 @@
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'mark',
|
|
||||||
name: joinLabel(line, 0, line.length - 1),
|
name: joinLabel(line, 0, line.length - 1),
|
||||||
|
type: 'mark',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -4638,33 +4781,44 @@
|
||||||
options.push(line[i].v);
|
options.push(line[i].v);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'agent options',
|
|
||||||
agent,
|
agent,
|
||||||
options,
|
options,
|
||||||
|
type: 'agent options',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
];
|
];
|
||||||
|
|
||||||
function parseLine(line, {meta, stages}) {
|
function stageFromLine(line, meta) {
|
||||||
let stage = null;
|
|
||||||
for(const {begin, fn} of PARSERS) {
|
for(const {begin, fn} of PARSERS) {
|
||||||
if(skipOver(line, 0, begin) !== begin.length) {
|
if(skipOver(line, 0, begin) !== begin.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
stage = fn(line, meta);
|
const stage = fn(line, meta);
|
||||||
if(stage) {
|
if(stage) {
|
||||||
break;
|
return stage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLine(line, {meta, stages}) {
|
||||||
|
let parallel = false;
|
||||||
|
const [start] = line;
|
||||||
|
if(tokenKeyword(start) === '&') {
|
||||||
|
parallel = true;
|
||||||
|
line.splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stage = stageFromLine(line, meta);
|
||||||
|
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
throw makeError(
|
throw makeError('Unrecognised command: ' + joinLabel(line), line[0]);
|
||||||
'Unrecognised command: ' + joinLabel(line),
|
} else if(typeof stage === 'object') {
|
||||||
line[0]
|
stage.ln = start.b.ln;
|
||||||
);
|
stage.parallel = parallel;
|
||||||
}
|
|
||||||
if(typeof stage === 'object') {
|
|
||||||
stage.ln = line[0].b.ln;
|
|
||||||
stages.push(stage);
|
stages.push(stage);
|
||||||
|
} else if(parallel) {
|
||||||
|
throw makeError('Metadata cannot be parallel', start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4680,12 +4834,12 @@
|
||||||
parseLines(lines, src) {
|
parseLines(lines, src) {
|
||||||
const result = {
|
const result = {
|
||||||
meta: {
|
meta: {
|
||||||
title: '',
|
|
||||||
theme: '',
|
|
||||||
code: src,
|
code: src,
|
||||||
terminators: 'none',
|
|
||||||
headers: 'box',
|
headers: 'box',
|
||||||
|
terminators: 'none',
|
||||||
textFormatter: parseMarkdown,
|
textFormatter: parseMarkdown,
|
||||||
|
theme: '',
|
||||||
|
title: '',
|
||||||
},
|
},
|
||||||
stages: [],
|
stages: [],
|
||||||
};
|
};
|
||||||
|
|
|
@ -34,6 +34,14 @@ const AGENT_INFO_TYPES = [
|
||||||
'red',
|
'red',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const PARALLEL_TASKS = [
|
||||||
|
'begin',
|
||||||
|
'end',
|
||||||
|
'note',
|
||||||
|
'state',
|
||||||
|
'text',
|
||||||
|
];
|
||||||
|
|
||||||
const makeCommands = ((() => {
|
const makeCommands = ((() => {
|
||||||
function agentListTo(exit, next = 1) {
|
function agentListTo(exit, next = 1) {
|
||||||
return {
|
return {
|
||||||
|
@ -394,10 +402,25 @@ const makeCommands = ((() => {
|
||||||
}},
|
}},
|
||||||
};
|
};
|
||||||
|
|
||||||
return (arrows) => ({
|
return (arrows) => {
|
||||||
|
const arrowConnect = makeCMConnect(arrows);
|
||||||
|
|
||||||
|
const parallel = {};
|
||||||
|
for(const task of PARALLEL_TASKS) {
|
||||||
|
parallel[task] = BASE_THEN[task];
|
||||||
|
}
|
||||||
|
Object.assign(parallel, arrowConnect);
|
||||||
|
|
||||||
|
return {
|
||||||
type: 'error line-error',
|
type: 'error line-error',
|
||||||
then: Object.assign({}, BASE_THEN, makeCMConnect(arrows)),
|
then: Object.assign(
|
||||||
});
|
{},
|
||||||
|
BASE_THEN,
|
||||||
|
{'&': {type: 'keyword', then: parallel}},
|
||||||
|
arrowConnect
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
})());
|
})());
|
||||||
|
|
||||||
/* eslint-enable sort-keys */
|
/* eslint-enable sort-keys */
|
||||||
|
@ -645,6 +668,7 @@ export default class Mode {
|
||||||
if(state.currentType === NO_TOKEN) {
|
if(state.currentType === NO_TOKEN) {
|
||||||
if(stream.sol()) {
|
if(stream.sol()) {
|
||||||
state.line.length = 0;
|
state.line.length = 0;
|
||||||
|
state.valid = true;
|
||||||
}
|
}
|
||||||
if(!this._tokenBegin(stream, state)) {
|
if(!this._tokenBegin(stream, state)) {
|
||||||
return '';
|
return '';
|
||||||
|
|
|
@ -92,6 +92,23 @@ describe('Code Mirror Mode', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('highlights parallel statements', () => {
|
||||||
|
cm.getDoc().setValue('& A -> B');
|
||||||
|
|
||||||
|
expect(getTokens(0)).toEqual([
|
||||||
|
{type: 'keyword', v: '&'},
|
||||||
|
{type: 'variable', v: ' A'},
|
||||||
|
{type: 'keyword', v: ' ->'},
|
||||||
|
{type: 'variable', v: ' B'},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('highlights invalid parallel statements', () => {
|
||||||
|
cm.getDoc().setValue('& terminators cross');
|
||||||
|
|
||||||
|
expect(getTokens(0)[2].type).toContain('line-error');
|
||||||
|
});
|
||||||
|
|
||||||
it('does not consider quoted tokens as keywords', () => {
|
it('does not consider quoted tokens as keywords', () => {
|
||||||
cm.getDoc().setValue('A "->" -> B');
|
cm.getDoc().setValue('A "->" -> B');
|
||||||
|
|
||||||
|
@ -449,6 +466,13 @@ describe('Code Mirror Mode', () => {
|
||||||
|
|
||||||
expect(getTokens(0)[3].type).toContain('line-error');
|
expect(getTokens(0)[3].type).toContain('line-error');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resets error handling on new lines with comments', () => {
|
||||||
|
cm.getDoc().setValue('nope\n#foo');
|
||||||
|
|
||||||
|
expect(getTokens(0)[0].type).toContain('line-error');
|
||||||
|
expect(getTokens(1)[0].type).not.toContain('line-error');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('autocomplete', () => {
|
describe('autocomplete', () => {
|
||||||
|
|
|
@ -193,6 +193,87 @@ function optimiseStages(stages) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractParallel(target, stages) {
|
||||||
|
for(const stage of stages) {
|
||||||
|
if(!stage) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(stage.type === 'parallel') {
|
||||||
|
extractParallel(target, stage.stages);
|
||||||
|
} else {
|
||||||
|
target.push(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAgentConflicts(allStages) {
|
||||||
|
const createIDs = flatMap(
|
||||||
|
allStages
|
||||||
|
.filter((stage) => (stage.type === 'agent begin')),
|
||||||
|
(stage) => stage.agentIDs
|
||||||
|
);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'agent end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for(const id of stage.agentIDs) {
|
||||||
|
if(createIDs.indexOf(id) !== -1) {
|
||||||
|
return 'Cannot create and destroy ' + id + ' simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkReferenceConflicts(allStages) {
|
||||||
|
const leftIDs = allStages
|
||||||
|
.filter((stage) => (stage.type === 'block begin'))
|
||||||
|
.map((stage) => stage.left);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'block end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(leftIDs.indexOf(stage.left) !== -1) {
|
||||||
|
return 'Cannot create and destroy reference simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDelayedConflicts(allStages) {
|
||||||
|
const tags = allStages
|
||||||
|
.filter((stage) => (stage.type === 'connect-delay-begin'))
|
||||||
|
.map((stage) => stage.tag);
|
||||||
|
|
||||||
|
for(const stage of allStages) {
|
||||||
|
if(stage.type !== 'connect-delay-end') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(tags.indexOf(stage.tag) !== -1) {
|
||||||
|
return 'Cannot start and finish delayed connection simultaneously';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorForParallel(existing, latest) {
|
||||||
|
if(!existing) {
|
||||||
|
return 'Nothing to run statement in parallel with';
|
||||||
|
}
|
||||||
|
|
||||||
|
const allStages = [];
|
||||||
|
extractParallel(allStages, [existing]);
|
||||||
|
extractParallel(allStages, [latest]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
checkAgentConflicts(allStages) ||
|
||||||
|
checkReferenceConflicts(allStages) ||
|
||||||
|
checkDelayedConflicts(allStages)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function swapBegin(stage, mode) {
|
function swapBegin(stage, mode) {
|
||||||
if(stage.type === 'agent begin') {
|
if(stage.type === 'agent begin') {
|
||||||
stage.mode = mode;
|
stage.mode = mode;
|
||||||
|
@ -308,37 +389,75 @@ export default class Generator {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addStage(stage, isVisible = true) {
|
addStage(stage, {isVisible = true, parallel = false} = {}) {
|
||||||
|
if(!stage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(isVisible) {
|
||||||
|
this.currentNest.hasContent = true;
|
||||||
|
}
|
||||||
|
if(typeof stage.ln === 'undefined') {
|
||||||
|
stage.ln = this.latestLine;
|
||||||
|
}
|
||||||
|
const {stages} = this.currentSection;
|
||||||
|
if(parallel) {
|
||||||
|
const target = last(stages);
|
||||||
|
const err = errorForParallel(target, stage);
|
||||||
|
if(err) {
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
const pstage = this.makeParallel([target, stage]);
|
||||||
|
pstage.ln = stage.ln;
|
||||||
|
-- stages.length;
|
||||||
|
stages.push(pstage);
|
||||||
|
} else {
|
||||||
|
stages.push(stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addImpStage(stage, {parallel = false} = {}) {
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(typeof stage.ln === 'undefined') {
|
if(typeof stage.ln === 'undefined') {
|
||||||
stage.ln = this.latestLine;
|
stage.ln = this.latestLine;
|
||||||
}
|
}
|
||||||
this.currentSection.stages.push(stage);
|
const {stages} = this.currentSection;
|
||||||
if(isVisible) {
|
if(parallel) {
|
||||||
this.currentNest.hasContent = true;
|
const target = stages[stages.length - 2];
|
||||||
|
if(!target) {
|
||||||
|
throw new Error('Nothing to run statement in parallel with');
|
||||||
|
}
|
||||||
|
if(errorForParallel(target, stage)) {
|
||||||
|
stages.splice(stages.length - 1, 0, stage);
|
||||||
|
} else {
|
||||||
|
const pstage = this.makeParallel([target, stage]);
|
||||||
|
pstage.ln = stage.ln;
|
||||||
|
stages.splice(stages.length - 2, 1, pstage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stages.push(stage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addParallelStages(stages) {
|
makeParallel(stages) {
|
||||||
const viableStages = stages.filter((stage) => Boolean(stage));
|
const viableStages = [];
|
||||||
|
extractParallel(viableStages, stages);
|
||||||
if(viableStages.length === 0) {
|
if(viableStages.length === 0) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
if(viableStages.length === 1) {
|
if(viableStages.length === 1) {
|
||||||
this.addStage(viableStages[0]);
|
return viableStages[0];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
viableStages.forEach((stage) => {
|
viableStages.forEach((stage) => {
|
||||||
if(typeof stage.ln === 'undefined') {
|
if(typeof stage.ln === 'undefined') {
|
||||||
stage.ln = this.latestLine;
|
stage.ln = this.latestLine;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.addStage({
|
return {
|
||||||
stages: viableStages,
|
stages: viableStages,
|
||||||
type: 'parallel',
|
type: 'parallel',
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
defineGAgents(gAgents) {
|
defineGAgents(gAgents) {
|
||||||
|
@ -554,7 +673,10 @@ export default class Generator {
|
||||||
this.currentNest = last(this.nesting);
|
this.currentNest = last(this.nesting);
|
||||||
this.currentSection = last(this.currentNest.sections);
|
this.currentSection = last(this.currentNest.sections);
|
||||||
|
|
||||||
if(nested.hasContent) {
|
if(!nested.hasContent) {
|
||||||
|
throw new Error('Empty block');
|
||||||
|
}
|
||||||
|
|
||||||
this.defineGAgents(nested.gAgents);
|
this.defineGAgents(nested.gAgents);
|
||||||
addBounds(
|
addBounds(
|
||||||
this.gAgents,
|
this.gAgents,
|
||||||
|
@ -571,9 +693,6 @@ export default class Generator {
|
||||||
right: nested.rightGAgent.id,
|
right: nested.rightGAgent.id,
|
||||||
type: 'block end',
|
type: 'block end',
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
throw new Error('Empty block');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeGroupDetails(pAgents, alias) {
|
makeGroupDetails(pAgents, alias) {
|
||||||
|
@ -615,7 +734,7 @@ export default class Generator {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleGroupBegin({agents, blockType, tag, label, alias}) {
|
handleGroupBegin({agents, blockType, tag, label, alias, parallel}) {
|
||||||
const details = this.makeGroupDetails(agents, alias);
|
const details = this.makeGroupDetails(agents, alias);
|
||||||
|
|
||||||
details.gAgentsContained.forEach((gAgent) => {
|
details.gAgentsContained.forEach((gAgent) => {
|
||||||
|
@ -625,7 +744,10 @@ export default class Generator {
|
||||||
this.updateGAgentState(gAgent, {covered: true});
|
this.updateGAgentState(gAgent, {covered: true});
|
||||||
});
|
});
|
||||||
this.activeGroups.set(alias, details);
|
this.activeGroups.set(alias, details);
|
||||||
this.addStage(this.setGAgentVis(details.gAgents, true, 'box'));
|
this.addImpStage(
|
||||||
|
this.setGAgentVis(details.gAgents, true, 'box'),
|
||||||
|
{parallel}
|
||||||
|
);
|
||||||
this.addStage({
|
this.addStage({
|
||||||
blockType,
|
blockType,
|
||||||
canHide: false,
|
canHide: false,
|
||||||
|
@ -634,7 +756,7 @@ export default class Generator {
|
||||||
right: details.rightGAgent.id,
|
right: details.rightGAgent.id,
|
||||||
tag: this.textFormatter(tag),
|
tag: this.textFormatter(tag),
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
});
|
}, {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
endGroup({name}) {
|
endGroup({name}) {
|
||||||
|
@ -661,7 +783,7 @@ export default class Generator {
|
||||||
|
|
||||||
handleMark({name}) {
|
handleMark({name}) {
|
||||||
this.markers.add(name);
|
this.markers.add(name);
|
||||||
this.addStage({name, type: 'mark'}, false);
|
this.addStage({name, type: 'mark'}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDivider({mode, height, label}) {
|
handleDivider({mode, height, label}) {
|
||||||
|
@ -670,14 +792,14 @@ export default class Generator {
|
||||||
height,
|
height,
|
||||||
mode,
|
mode,
|
||||||
type: 'divider',
|
type: 'divider',
|
||||||
}, false);
|
}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAsync({target}) {
|
handleAsync({target}) {
|
||||||
if(target !== '' && !this.markers.has(target)) {
|
if(target !== '' && !this.markers.has(target)) {
|
||||||
throw new Error('Unknown marker: ' + target);
|
throw new Error('Unknown marker: ' + target);
|
||||||
}
|
}
|
||||||
this.addStage({target, type: 'async'}, false);
|
this.addStage({target, type: 'async'}, {isVisible: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLabelPattern({pattern}) {
|
handleLabelPattern({pattern}) {
|
||||||
|
@ -831,7 +953,7 @@ export default class Generator {
|
||||||
return gAgents;
|
return gAgents;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePartialConnect(agents) {
|
_handlePartialConnect(agents, parallel) {
|
||||||
const flags = this.filterConnectFlags(agents);
|
const flags = this.filterConnectFlags(agents);
|
||||||
|
|
||||||
const gAgents = agents.map(this.toGAgent);
|
const gAgents = agents.map(this.toGAgent);
|
||||||
|
@ -848,19 +970,22 @@ export default class Generator {
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
.filter((gAgent) => !gAgent.isVirtualSource)
|
.filter((gAgent) => !gAgent.isVirtualSource)
|
||||||
);
|
);
|
||||||
this.addStage(this.setGAgentVis(implicitBeginGAgents, true, 'box'));
|
this.addImpStage(
|
||||||
|
this.setGAgentVis(implicitBeginGAgents, true, 'box'),
|
||||||
|
{parallel}
|
||||||
|
);
|
||||||
|
|
||||||
return {flags, gAgents};
|
return {flags, gAgents};
|
||||||
}
|
}
|
||||||
|
|
||||||
_makeConnectParallelStages(flags, connectStage) {
|
_makeConnectParallelStages(flags, connectStage) {
|
||||||
return [
|
return this.makeParallel([
|
||||||
this.setGAgentVis(flags.beginGAgents, true, 'box', true),
|
this.setGAgentVis(flags.beginGAgents, true, 'box', true),
|
||||||
this.setGAgentHighlight(flags.startGAgents, true, true),
|
this.setGAgentHighlight(flags.startGAgents, true, true),
|
||||||
connectStage,
|
connectStage,
|
||||||
this.setGAgentHighlight(flags.stopGAgents, false, true),
|
this.setGAgentHighlight(flags.stopGAgents, false, true),
|
||||||
this.setGAgentVis(flags.endGAgents, false, 'cross', true),
|
this.setGAgentVis(flags.endGAgents, false, 'cross', true),
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_isSelfConnect(agents) {
|
_isSelfConnect(agents) {
|
||||||
|
@ -875,7 +1000,7 @@ export default class Generator {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnect({agents, label, options}) {
|
handleConnect({agents, label, options, parallel}) {
|
||||||
if(this._isSelfConnect(agents)) {
|
if(this._isSelfConnect(agents)) {
|
||||||
const tag = {};
|
const tag = {};
|
||||||
this.handleConnectDelayBegin({
|
this.handleConnectDelayBegin({
|
||||||
|
@ -883,6 +1008,7 @@ export default class Generator {
|
||||||
ln: 0,
|
ln: 0,
|
||||||
options,
|
options,
|
||||||
tag,
|
tag,
|
||||||
|
parallel,
|
||||||
});
|
});
|
||||||
this.handleConnectDelayEnd({
|
this.handleConnectDelayEnd({
|
||||||
agent: agents[1],
|
agent: agents[1],
|
||||||
|
@ -893,7 +1019,7 @@ export default class Generator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {flags, gAgents} = this._handlePartialConnect(agents);
|
let {flags, gAgents} = this._handlePartialConnect(agents, parallel);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
gAgents = this.expandGroupedGAgentConnection(gAgents);
|
||||||
gAgents = this.expandVirtualSourceAgents(gAgents);
|
gAgents = this.expandVirtualSourceAgents(gAgents);
|
||||||
|
@ -905,19 +1031,19 @@ export default class Generator {
|
||||||
type: 'connect',
|
type: 'connect',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectStage),
|
||||||
connectStage
|
{parallel}
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnectDelayBegin({agent, tag, options, ln}) {
|
handleConnectDelayBegin({agent, tag, options, ln, parallel}) {
|
||||||
const dcs = this.currentSection.delayedConnections;
|
const dcs = this.currentSection.delayedConnections;
|
||||||
if(dcs.has(tag)) {
|
if(dcs.has(tag)) {
|
||||||
throw new Error('Duplicate delayed connection "' + tag + '"');
|
throw new Error('Duplicate delayed connection "' + tag + '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
const {flags, gAgents} = this._handlePartialConnect([agent]);
|
const {flags, gAgents} = this._handlePartialConnect([agent], parallel);
|
||||||
const uniqueTag = this.nextVirtualAgentName();
|
const uniqueTag = this.nextVirtualAgentName();
|
||||||
|
|
||||||
const connectStage = {
|
const connectStage = {
|
||||||
|
@ -930,20 +1056,20 @@ export default class Generator {
|
||||||
|
|
||||||
dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag});
|
dcs.set(tag, {connectStage, gAgents, ln, tag, uniqueTag});
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectStage),
|
||||||
connectStage
|
{parallel}
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleConnectDelayEnd({agent, tag, label, options}) {
|
handleConnectDelayEnd({agent, tag, label, options, parallel}) {
|
||||||
const dcs = this.currentSection.delayedConnections;
|
const dcs = this.currentSection.delayedConnections;
|
||||||
const dcInfo = dcs.get(tag);
|
const dcInfo = dcs.get(tag);
|
||||||
if(!dcInfo) {
|
if(!dcInfo) {
|
||||||
throw new Error('Unknown delayed connection "' + tag + '"');
|
throw new Error('Unknown delayed connection "' + tag + '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
let {flags, gAgents} = this._handlePartialConnect([agent]);
|
let {flags, gAgents} = this._handlePartialConnect([agent], parallel);
|
||||||
|
|
||||||
gAgents = this.expandGroupedGAgentConnection([
|
gAgents = this.expandGroupedGAgentConnection([
|
||||||
...dcInfo.gAgents,
|
...dcInfo.gAgents,
|
||||||
|
@ -972,15 +1098,15 @@ export default class Generator {
|
||||||
type: 'connect-delay-end',
|
type: 'connect-delay-end',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.addParallelStages(this._makeConnectParallelStages(
|
this.addStage(
|
||||||
flags,
|
this._makeConnectParallelStages(flags, connectEndStage),
|
||||||
connectEndStage
|
{parallel}
|
||||||
));
|
);
|
||||||
|
|
||||||
dcs.delete(tag);
|
dcs.delete(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleNote({type, agents, mode, label}) {
|
handleNote({type, agents, mode, label, parallel}) {
|
||||||
let gAgents = null;
|
let gAgents = null;
|
||||||
if(agents.length === 0) {
|
if(agents.length === 0) {
|
||||||
gAgents = NOTE_DEFAULT_G_AGENTS[type] || [];
|
gAgents = NOTE_DEFAULT_G_AGENTS[type] || [];
|
||||||
|
@ -996,15 +1122,14 @@ export default class Generator {
|
||||||
throw new Error('note between requires at least 2 agents');
|
throw new Error('note between requires at least 2 agents');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addStage(this.setGAgentVis(gAgents, true, 'box'));
|
|
||||||
this.defineGAgents(gAgents);
|
this.defineGAgents(gAgents);
|
||||||
|
this.addImpStage(this.setGAgentVis(gAgents, true, 'box'), {parallel});
|
||||||
this.addStage({
|
this.addStage({
|
||||||
agentIDs,
|
agentIDs,
|
||||||
label: this.textFormatter(label),
|
label: this.textFormatter(label),
|
||||||
mode,
|
mode,
|
||||||
type,
|
type,
|
||||||
});
|
}, {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentDefine({agents}) {
|
handleAgentDefine({agents}) {
|
||||||
|
@ -1032,13 +1157,13 @@ export default class Generator {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentBegin({agents, mode}) {
|
handleAgentBegin({agents, mode, parallel}) {
|
||||||
const gAgents = agents.map(this.toGAgent);
|
const gAgents = agents.map(this.toGAgent);
|
||||||
this.validateGAgents(gAgents);
|
this.validateGAgents(gAgents);
|
||||||
this.addStage(this.setGAgentVis(gAgents, true, mode, true));
|
this.addStage(this.setGAgentVis(gAgents, true, mode, true), {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentEnd({agents, mode}) {
|
handleAgentEnd({agents, mode, parallel}) {
|
||||||
const groupPAgents = (agents
|
const groupPAgents = (agents
|
||||||
.filter((pAgent) => this.activeGroups.has(pAgent.name))
|
.filter((pAgent) => this.activeGroups.has(pAgent.name))
|
||||||
);
|
);
|
||||||
|
@ -1047,11 +1172,11 @@ export default class Generator {
|
||||||
.map(this.toGAgent)
|
.map(this.toGAgent)
|
||||||
);
|
);
|
||||||
this.validateGAgents(gAgents);
|
this.validateGAgents(gAgents);
|
||||||
this.addParallelStages([
|
this.addStage(this.makeParallel([
|
||||||
this.setGAgentHighlight(gAgents, false),
|
this.setGAgentHighlight(gAgents, false),
|
||||||
this.setGAgentVis(gAgents, false, mode, true),
|
this.setGAgentVis(gAgents, false, mode, true),
|
||||||
...groupPAgents.map(this.endGroup),
|
...groupPAgents.map(this.endGroup),
|
||||||
]);
|
]), {parallel});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleStage(stage) {
|
handleStage(stage) {
|
||||||
|
@ -1120,10 +1245,10 @@ export default class Generator {
|
||||||
this._checkSectionEnd();
|
this._checkSectionEnd();
|
||||||
|
|
||||||
const terminators = meta.terminators || 'none';
|
const terminators = meta.terminators || 'none';
|
||||||
this.addParallelStages([
|
this.addStage(this.makeParallel([
|
||||||
this.setGAgentHighlight(this.gAgents, false),
|
this.setGAgentHighlight(this.gAgents, false),
|
||||||
this.setGAgentVis(this.gAgents, false, terminators),
|
this.setGAgentVis(this.gAgents, false, terminators),
|
||||||
]);
|
]));
|
||||||
|
|
||||||
this._finalise(globals);
|
this._finalise(globals);
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,11 @@ describe('Sequence Generator', () => {
|
||||||
|
|
||||||
function makeParsedAgents(source) {
|
function makeParsedAgents(source) {
|
||||||
return source.map((item) => {
|
return source.map((item) => {
|
||||||
|
const base = {alias: '', flags: [], name: ''};
|
||||||
if(typeof item === 'object') {
|
if(typeof item === 'object') {
|
||||||
return item;
|
return Object.assign(base, item);
|
||||||
} else {
|
} else {
|
||||||
return {name: item, alias: '', flags: []};
|
return Object.assign(base, {name: item});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -26,31 +27,42 @@ describe('Sequence Generator', () => {
|
||||||
const PARSED = {
|
const PARSED = {
|
||||||
sourceAgent: {name: '', alias: '', flags: ['source']},
|
sourceAgent: {name: '', alias: '', flags: ['source']},
|
||||||
|
|
||||||
blockBegin: (tag, label, {ln = 0} = {}) => ({
|
blockBegin: (tag, label, {ln = 0, parallel = false} = {}) => ({
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
blockType: tag,
|
blockType: tag,
|
||||||
tag,
|
tag,
|
||||||
label,
|
label,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
blockSplit: (tag, label, {ln = 0} = {}) => ({
|
blockSplit: (tag, label, {ln = 0, parallel = false} = {}) => ({
|
||||||
type: 'block split',
|
type: 'block split',
|
||||||
blockType: tag,
|
blockType: tag,
|
||||||
tag,
|
tag,
|
||||||
label,
|
label,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
blockEnd: ({ln = 0} = {}) => ({type: 'block end', ln}),
|
blockEnd: ({ln = 0, parallel = false} = {}) => ({
|
||||||
|
type: 'block end',
|
||||||
|
ln,
|
||||||
|
parallel,
|
||||||
|
}),
|
||||||
|
|
||||||
labelPattern: (pattern, {ln = 0} = {}) => ({
|
labelPattern: (pattern, {ln = 0, parallel = false} = {}) => ({
|
||||||
type: 'label pattern',
|
type: 'label pattern',
|
||||||
pattern,
|
pattern,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
groupBegin: (alias, agentIDs, {label = '', ln = 0} = {}) => ({
|
groupBegin: (alias, agentIDs, {
|
||||||
|
label = '',
|
||||||
|
ln = 0,
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
type: 'group begin',
|
type: 'group begin',
|
||||||
agents: makeParsedAgents(agentIDs),
|
agents: makeParsedAgents(agentIDs),
|
||||||
blockType: 'ref',
|
blockType: 'ref',
|
||||||
|
@ -58,33 +70,46 @@ describe('Sequence Generator', () => {
|
||||||
label,
|
label,
|
||||||
alias,
|
alias,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
defineAgents: (agentIDs, {ln = 0} = {}) => ({
|
defineAgents: (agentIDs, {ln = 0, parallel = false} = {}) => ({
|
||||||
type: 'agent define',
|
type: 'agent define',
|
||||||
agents: makeParsedAgents(agentIDs),
|
agents: makeParsedAgents(agentIDs),
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
agentOptions: (agentID, options, {ln = 0} = {}) => ({
|
agentOptions: (agentID, options, {ln = 0, parallel = false} = {}) => ({
|
||||||
type: 'agent options',
|
type: 'agent options',
|
||||||
agent: makeParsedAgents([agentID])[0],
|
agent: makeParsedAgents([agentID])[0],
|
||||||
options,
|
options,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
beginAgents: (agentIDs, {mode = 'box', ln = 0} = {}) => ({
|
beginAgents: (agentIDs, {
|
||||||
|
mode = 'box',
|
||||||
|
ln = 0,
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
type: 'agent begin',
|
type: 'agent begin',
|
||||||
agents: makeParsedAgents(agentIDs),
|
agents: makeParsedAgents(agentIDs),
|
||||||
mode,
|
mode,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
endAgents: (agentIDs, {mode = 'cross', ln = 0} = {}) => ({
|
endAgents: (agentIDs, {
|
||||||
|
mode = 'cross',
|
||||||
|
ln = 0,
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
type: 'agent end',
|
type: 'agent end',
|
||||||
agents: makeParsedAgents(agentIDs),
|
agents: makeParsedAgents(agentIDs),
|
||||||
mode,
|
mode,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
connect: (agentIDs, {
|
connect: (agentIDs, {
|
||||||
|
@ -93,6 +118,7 @@ describe('Sequence Generator', () => {
|
||||||
left = 0,
|
left = 0,
|
||||||
right = 0,
|
right = 0,
|
||||||
ln = 0,
|
ln = 0,
|
||||||
|
parallel = false,
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type: 'connect',
|
type: 'connect',
|
||||||
agents: makeParsedAgents(agentIDs),
|
agents: makeParsedAgents(agentIDs),
|
||||||
|
@ -103,6 +129,7 @@ describe('Sequence Generator', () => {
|
||||||
right,
|
right,
|
||||||
},
|
},
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
connectDelayBegin: (agentID, {
|
connectDelayBegin: (agentID, {
|
||||||
|
@ -111,6 +138,7 @@ describe('Sequence Generator', () => {
|
||||||
left = 0,
|
left = 0,
|
||||||
right = 0,
|
right = 0,
|
||||||
ln = 0,
|
ln = 0,
|
||||||
|
parallel = false,
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type: 'connect-delay-begin',
|
type: 'connect-delay-begin',
|
||||||
ln,
|
ln,
|
||||||
|
@ -121,6 +149,7 @@ describe('Sequence Generator', () => {
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
},
|
},
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
connectDelayEnd: (agentID, {
|
connectDelayEnd: (agentID, {
|
||||||
|
@ -130,6 +159,7 @@ describe('Sequence Generator', () => {
|
||||||
left = 0,
|
left = 0,
|
||||||
right = 0,
|
right = 0,
|
||||||
ln = 0,
|
ln = 0,
|
||||||
|
parallel = false,
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type: 'connect-delay-end',
|
type: 'connect-delay-end',
|
||||||
ln,
|
ln,
|
||||||
|
@ -141,18 +171,21 @@ describe('Sequence Generator', () => {
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
},
|
},
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
note: (type, agentIDs, {
|
note: (type, agentIDs, {
|
||||||
mode = '',
|
mode = '',
|
||||||
label = '',
|
label = '',
|
||||||
ln = 0,
|
ln = 0,
|
||||||
|
parallel = false,
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type,
|
type,
|
||||||
agents: makeParsedAgents(agentIDs),
|
agents: makeParsedAgents(agentIDs),
|
||||||
mode,
|
mode,
|
||||||
label,
|
label,
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2053,6 +2086,151 @@ describe('Sequence Generator', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('combines parallel statements', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.connect(['A', 'B']),
|
||||||
|
PARSED.note('note right', ['B'], {parallel: true}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
any(),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.connect(['A', 'B']),
|
||||||
|
GENERATED.note('note right', ['B']),
|
||||||
|
]),
|
||||||
|
any(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combines parallel creation and destruction', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.beginAgents(['A']),
|
||||||
|
PARSED.beginAgents(['B']),
|
||||||
|
PARSED.endAgents(['A'], {parallel: true}),
|
||||||
|
PARSED.endAgents(['B']),
|
||||||
|
PARSED.beginAgents(['A'], {parallel: true}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
GENERATED.beginAgents(['A']),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.beginAgents(['B']),
|
||||||
|
GENERATED.endAgents(['A']),
|
||||||
|
]),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.endAgents(['B']),
|
||||||
|
GENERATED.beginAgents(['A']),
|
||||||
|
]),
|
||||||
|
GENERATED.endAgents(['A']),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combines parallel connects and implicit begins', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.connect(['A', 'B']),
|
||||||
|
PARSED.connect(['B', 'C'], {parallel: true}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
GENERATED.beginAgents(['A', 'B', 'C']),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.connect(['A', 'B']),
|
||||||
|
GENERATED.connect(['B', 'C']),
|
||||||
|
]),
|
||||||
|
any(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combines parallel delayed connections', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B', 'C']),
|
||||||
|
PARSED.connectDelayBegin('B', {tag: 'foo'}),
|
||||||
|
PARSED.connectDelayBegin('B', {tag: 'bar', parallel: true}),
|
||||||
|
PARSED.connectDelayEnd('A', {tag: 'foo'}),
|
||||||
|
PARSED.connectDelayEnd('C', {tag: 'bar', parallel: true}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
any(),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.connectDelayBegin(['B', 'A'], {tag: '__0'}),
|
||||||
|
GENERATED.connectDelayBegin(['B', 'C'], {tag: '__1'}),
|
||||||
|
]),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.connectDelayEnd({tag: '__0'}),
|
||||||
|
GENERATED.connectDelayEnd({tag: '__1'}),
|
||||||
|
]),
|
||||||
|
any(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combines parallel references', () => {
|
||||||
|
const sequence = invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B', 'C', 'D']),
|
||||||
|
PARSED.groupBegin('AB', ['A', 'B']),
|
||||||
|
PARSED.groupBegin('CD', ['C', 'D'], {parallel: true}),
|
||||||
|
PARSED.endAgents(['AB']),
|
||||||
|
PARSED.endAgents(['CD'], {parallel: true}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(sequence.stages).toEqual([
|
||||||
|
any(),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.blockBegin('ref'),
|
||||||
|
GENERATED.blockBegin('ref'),
|
||||||
|
]),
|
||||||
|
GENERATED.parallel([
|
||||||
|
GENERATED.blockEnd(),
|
||||||
|
GENERATED.blockEnd(),
|
||||||
|
]),
|
||||||
|
any(),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects parallel marks on initial statements', () => {
|
||||||
|
expect(() => invoke([
|
||||||
|
PARSED.connect(['A', 'B'], {parallel: true}),
|
||||||
|
])).toThrow(new Error(
|
||||||
|
'Nothing to run statement in parallel with at line 1'
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(() => invoke([
|
||||||
|
PARSED.note('note over', ['A'], {parallel: true}),
|
||||||
|
])).toThrow(new Error(
|
||||||
|
'Nothing to run statement in parallel with at line 1'
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects parallel creation and destruction of an agent', () => {
|
||||||
|
expect(() => invoke([
|
||||||
|
PARSED.beginAgents(['A']),
|
||||||
|
PARSED.endAgents(['A'], {parallel: true}),
|
||||||
|
])).toThrow(new Error(
|
||||||
|
'Cannot create and destroy A simultaneously at line 1'
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects parallel begin and end of a delayed communication', () => {
|
||||||
|
expect(() => invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B']),
|
||||||
|
PARSED.connectDelayBegin('A', {tag: 'foo'}),
|
||||||
|
PARSED.connectDelayEnd('B', {tag: 'foo', parallel: true}),
|
||||||
|
])).toThrow(new Error(
|
||||||
|
'Cannot start and finish delayed connection simultaneously' +
|
||||||
|
' at line 1'
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects parallel creation and destruction of a reference', () => {
|
||||||
|
expect(() => invoke([
|
||||||
|
PARSED.beginAgents(['A', 'B', 'C', 'D']),
|
||||||
|
PARSED.groupBegin('AB', ['A', 'B'], {label: 'Foo'}),
|
||||||
|
PARSED.endAgents(['AB'], {parallel: true}),
|
||||||
|
])).toThrow(new Error(
|
||||||
|
'Cannot create and destroy reference simultaneously at line 1'
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
it('rejects note between with a repeated agent', () => {
|
it('rejects note between with a repeated agent', () => {
|
||||||
expect(() => invoke([
|
expect(() => invoke([
|
||||||
PARSED.note('note between', ['A', 'A'], {
|
PARSED.note('note between', ['A', 'A'], {
|
||||||
|
|
|
@ -1,38 +1,42 @@
|
||||||
/* eslint-disable sort-keys */ // Maybe later
|
|
||||||
|
|
||||||
import {combine, last} from '../../core/ArrayUtilities.mjs';
|
import {combine, last} from '../../core/ArrayUtilities.mjs';
|
||||||
import Tokeniser from './Tokeniser.mjs';
|
import Tokeniser from './Tokeniser.mjs';
|
||||||
import labelPatternParser from './LabelPatternParser.mjs';
|
import labelPatternParser from './LabelPatternParser.mjs';
|
||||||
import markdownParser from './MarkdownParser.mjs';
|
import markdownParser from './MarkdownParser.mjs';
|
||||||
|
|
||||||
const BLOCK_TYPES = {
|
const BLOCK_TYPES = new Map();
|
||||||
'if': {
|
BLOCK_TYPES.set('if', {
|
||||||
type: 'block begin',
|
|
||||||
blockType: 'if',
|
blockType: 'if',
|
||||||
|
skip: [],
|
||||||
tag: 'if',
|
tag: 'if',
|
||||||
skip: [],
|
type: 'block begin',
|
||||||
},
|
});
|
||||||
'else': {
|
BLOCK_TYPES.set('else', {
|
||||||
type: 'block split',
|
|
||||||
blockType: 'else',
|
blockType: 'else',
|
||||||
tag: 'else',
|
|
||||||
skip: ['if'],
|
skip: ['if'],
|
||||||
},
|
tag: 'else',
|
||||||
'repeat': {
|
type: 'block split',
|
||||||
type: 'block begin',
|
});
|
||||||
|
BLOCK_TYPES.set('repeat', {
|
||||||
blockType: 'repeat',
|
blockType: 'repeat',
|
||||||
|
skip: [],
|
||||||
tag: 'repeat',
|
tag: 'repeat',
|
||||||
skip: [],
|
|
||||||
},
|
|
||||||
'group': {
|
|
||||||
type: 'block begin',
|
type: 'block begin',
|
||||||
|
});
|
||||||
|
BLOCK_TYPES.set('group', {
|
||||||
blockType: 'group',
|
blockType: 'group',
|
||||||
tag: '',
|
|
||||||
skip: [],
|
skip: [],
|
||||||
},
|
tag: '',
|
||||||
};
|
type: 'block begin',
|
||||||
|
});
|
||||||
|
|
||||||
const CONNECT = {
|
const CONNECT = {
|
||||||
|
agentFlags: {
|
||||||
|
'!': {flag: 'end'},
|
||||||
|
'*': {allowBlankName: true, blankNameFlag: 'source', flag: 'begin'},
|
||||||
|
'+': {flag: 'start'},
|
||||||
|
'-': {flag: 'stop'},
|
||||||
|
},
|
||||||
|
|
||||||
types: ((() => {
|
types: ((() => {
|
||||||
const lTypes = [
|
const lTypes = [
|
||||||
{tok: '', type: 0},
|
{tok: '', type: 0},
|
||||||
|
@ -58,21 +62,14 @@ const CONNECT = {
|
||||||
|
|
||||||
arrows.forEach((arrow) => {
|
arrows.forEach((arrow) => {
|
||||||
types.set(arrow.map((part) => part.tok).join(''), {
|
types.set(arrow.map((part) => part.tok).join(''), {
|
||||||
line: arrow[1].type,
|
|
||||||
left: arrow[0].type,
|
left: arrow[0].type,
|
||||||
|
line: arrow[1].type,
|
||||||
right: arrow[2].type,
|
right: arrow[2].type,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
})()),
|
})()),
|
||||||
|
|
||||||
agentFlags: {
|
|
||||||
'*': {flag: 'begin', allowBlankName: true, blankNameFlag: 'source'},
|
|
||||||
'+': {flag: 'start'},
|
|
||||||
'-': {flag: 'stop'},
|
|
||||||
'!': {flag: 'end'},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const TERMINATOR_TYPES = [
|
const TERMINATOR_TYPES = [
|
||||||
|
@ -83,73 +80,70 @@ const TERMINATOR_TYPES = [
|
||||||
'bar',
|
'bar',
|
||||||
];
|
];
|
||||||
|
|
||||||
const NOTE_TYPES = {
|
const NOTE_TYPES = new Map();
|
||||||
'text': {
|
NOTE_TYPES.set('text', {
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
types: {
|
types: {
|
||||||
'left': {
|
'left': {
|
||||||
type: 'note left',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note left',
|
||||||
},
|
},
|
||||||
'right': {
|
'right': {
|
||||||
type: 'note right',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note right',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
'note': {
|
NOTE_TYPES.set('note', {
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
types: {
|
types: {
|
||||||
'over': {
|
'between': {
|
||||||
type: 'note over',
|
|
||||||
skip: [],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 2,
|
||||||
|
skip: [],
|
||||||
|
type: 'note between',
|
||||||
},
|
},
|
||||||
'left': {
|
'left': {
|
||||||
type: 'note left',
|
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: ['of'],
|
||||||
|
type: 'note left',
|
||||||
|
},
|
||||||
|
'over': {
|
||||||
|
max: Number.POSITIVE_INFINITY,
|
||||||
|
min: 0,
|
||||||
|
skip: [],
|
||||||
|
type: 'note over',
|
||||||
},
|
},
|
||||||
'right': {
|
'right': {
|
||||||
type: 'note right',
|
max: Number.POSITIVE_INFINITY,
|
||||||
skip: ['of'],
|
|
||||||
min: 0,
|
min: 0,
|
||||||
max: Number.POSITIVE_INFINITY,
|
skip: ['of'],
|
||||||
},
|
type: 'note right',
|
||||||
'between': {
|
|
||||||
type: 'note between',
|
|
||||||
skip: [],
|
|
||||||
min: 2,
|
|
||||||
max: Number.POSITIVE_INFINITY,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
'state': {
|
NOTE_TYPES.set('state', {
|
||||||
mode: 'state',
|
mode: 'state',
|
||||||
types: {
|
types: {
|
||||||
'over': {type: 'note over', skip: [], min: 1, max: 1},
|
'over': {max: 1, min: 1, skip: [], type: 'note over'},
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const DIVIDER_TYPES = {
|
const DIVIDER_TYPES = new Map();
|
||||||
'line': {defaultHeight: 6},
|
DIVIDER_TYPES.set('line', {defaultHeight: 6});
|
||||||
'space': {defaultHeight: 6},
|
DIVIDER_TYPES.set('space', {defaultHeight: 6});
|
||||||
'delay': {defaultHeight: 30},
|
DIVIDER_TYPES.set('delay', {defaultHeight: 30});
|
||||||
'tear': {defaultHeight: 6},
|
DIVIDER_TYPES.set('tear', {defaultHeight: 6});
|
||||||
};
|
|
||||||
|
|
||||||
const AGENT_MANIPULATION_TYPES = {
|
const AGENT_MANIPULATION_TYPES = new Map();
|
||||||
'define': {type: 'agent define'},
|
AGENT_MANIPULATION_TYPES.set('define', {type: 'agent define'});
|
||||||
'begin': {type: 'agent begin', mode: 'box'},
|
AGENT_MANIPULATION_TYPES.set('begin', {mode: 'box', type: 'agent begin'});
|
||||||
'end': {type: 'agent end', mode: 'cross'},
|
AGENT_MANIPULATION_TYPES.set('end', {mode: 'cross', type: 'agent end'});
|
||||||
};
|
|
||||||
|
|
||||||
function makeError(message, token = null) {
|
function makeError(message, token = null) {
|
||||||
let suffix = '';
|
let suffix = '';
|
||||||
|
@ -249,8 +243,8 @@ function readAgentAlias(line, start, end, {enableAlias, allowBlankName}) {
|
||||||
throw makeError('Missing agent name', errPosToken);
|
throw makeError('Missing agent name', errPosToken);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
name: joinLabel(line, start, aliasSep),
|
|
||||||
alias: joinLabel(line, aliasSep + 1, end),
|
alias: joinLabel(line, aliasSep + 1, end),
|
||||||
|
name: joinLabel(line, start, aliasSep),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,13 +271,13 @@ function readAgent(line, start, end, {
|
||||||
blankNameFlags.push(flag.blankNameFlag);
|
blankNameFlags.push(flag.blankNameFlag);
|
||||||
}
|
}
|
||||||
const {name, alias} = readAgentAlias(line, p, end, {
|
const {name, alias} = readAgentAlias(line, p, end, {
|
||||||
enableAlias: aliases,
|
|
||||||
allowBlankName,
|
allowBlankName,
|
||||||
|
enableAlias: aliases,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
name,
|
|
||||||
alias,
|
alias,
|
||||||
flags: name ? flags : blankNameFlags,
|
flags: name ? flags : blankNameFlags,
|
||||||
|
name,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,7 +344,7 @@ const PARSERS = [
|
||||||
});
|
});
|
||||||
|
|
||||||
const mode = joinLabel(line, 1, heightSep) || 'line';
|
const mode = joinLabel(line, 1, heightSep) || 'line';
|
||||||
if(!DIVIDER_TYPES[mode]) {
|
if(!DIVIDER_TYPES.has(mode)) {
|
||||||
throw makeError('Unknown divider type', line[1]);
|
throw makeError('Unknown divider type', line[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,17 +352,17 @@ const PARSERS = [
|
||||||
line,
|
line,
|
||||||
heightSep + 2,
|
heightSep + 2,
|
||||||
labelSep,
|
labelSep,
|
||||||
DIVIDER_TYPES[mode].defaultHeight
|
DIVIDER_TYPES.get(mode).defaultHeight
|
||||||
);
|
);
|
||||||
if(Number.isNaN(height) || height < 0) {
|
if(Number.isNaN(height) || height < 0) {
|
||||||
throw makeError('Invalid divider height', line[heightSep + 2]);
|
throw makeError('Invalid divider height', line[heightSep + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'divider',
|
|
||||||
mode,
|
|
||||||
height,
|
height,
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
|
mode,
|
||||||
|
type: 'divider',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -380,8 +374,8 @@ const PARSERS = [
|
||||||
raw = joinLabel(line, 1);
|
raw = joinLabel(line, 1);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'label pattern',
|
|
||||||
pattern: labelPatternParser(raw),
|
pattern: labelPatternParser(raw),
|
||||||
|
type: 'label pattern',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -393,7 +387,7 @@ const PARSERS = [
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Block
|
{begin: [], fn: (line) => { // Block
|
||||||
const type = BLOCK_TYPES[tokenKeyword(line[0])];
|
const type = BLOCK_TYPES.get(tokenKeyword(line[0]));
|
||||||
if(!type) {
|
if(!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -403,10 +397,10 @@ const PARSERS = [
|
||||||
}
|
}
|
||||||
skip = skipOver(line, skip, [':']);
|
skip = skipOver(line, skip, [':']);
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
|
||||||
blockType: type.blockType,
|
blockType: type.blockType,
|
||||||
tag: type.tag,
|
|
||||||
label: joinLabel(line, skip),
|
label: joinLabel(line, skip),
|
||||||
|
tag: type.tag,
|
||||||
|
type: type.type,
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -428,17 +422,17 @@ const PARSERS = [
|
||||||
throw makeError('Reference must have an alias', line[labelSep]);
|
throw makeError('Reference must have an alias', line[labelSep]);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'group begin',
|
|
||||||
agents,
|
agents,
|
||||||
blockType: 'ref',
|
|
||||||
tag: 'ref',
|
|
||||||
label: def.name,
|
|
||||||
alias: def.alias,
|
alias: def.alias,
|
||||||
|
blockType: 'ref',
|
||||||
|
label: def.name,
|
||||||
|
tag: 'ref',
|
||||||
|
type: 'group begin',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Agent
|
{begin: [], fn: (line) => { // Agent
|
||||||
const type = AGENT_MANIPULATION_TYPES[tokenKeyword(line[0])];
|
const type = AGENT_MANIPULATION_TYPES.get(tokenKeyword(line[0]));
|
||||||
if(!type || line.length <= 1) {
|
if(!type || line.length <= 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -459,13 +453,13 @@ const PARSERS = [
|
||||||
target = joinLabel(line, 2, line.length - 1);
|
target = joinLabel(line, 2, line.length - 1);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'async',
|
|
||||||
target,
|
target,
|
||||||
|
type: 'async',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{begin: [], fn: (line) => { // Note
|
{begin: [], fn: (line) => { // Note
|
||||||
const mode = NOTE_TYPES[tokenKeyword(line[0])];
|
const mode = NOTE_TYPES.get(tokenKeyword(line[0]));
|
||||||
const labelSep = findTokens(line, [':']);
|
const labelSep = findTokens(line, [':']);
|
||||||
if(!mode || labelSep === -1) {
|
if(!mode || labelSep === -1) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -484,10 +478,10 @@ const PARSERS = [
|
||||||
throw makeError('Too many agents for ' + mode.mode, line[0]);
|
throw makeError('Too many agents for ' + mode.mode, line[0]);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
|
||||||
agents,
|
agents,
|
||||||
mode: mode.mode,
|
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
|
mode: mode.mode,
|
||||||
|
type: type.type,
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -496,7 +490,7 @@ const PARSERS = [
|
||||||
const connectionToken = findFirstToken(
|
const connectionToken = findFirstToken(
|
||||||
line,
|
line,
|
||||||
CONNECT.types,
|
CONNECT.types,
|
||||||
{start: 0, limit: labelSep - 1}
|
{limit: labelSep - 1, start: 0}
|
||||||
);
|
);
|
||||||
if(!connectionToken) {
|
if(!connectionToken) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -510,11 +504,11 @@ const PARSERS = [
|
||||||
|
|
||||||
if(tokenKeyword(line[0]) === '...') {
|
if(tokenKeyword(line[0]) === '...') {
|
||||||
return {
|
return {
|
||||||
type: 'connect-delay-end',
|
|
||||||
tag: joinLabel(line, 1, connectPos),
|
|
||||||
agent: readAgent(line, connectPos + 1, labelSep, readOpts),
|
agent: readAgent(line, connectPos + 1, labelSep, readOpts),
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
tag: joinLabel(line, 1, connectPos),
|
||||||
|
type: 'connect-delay-end',
|
||||||
};
|
};
|
||||||
} else if(tokenKeyword(line[connectPos + 1]) === '...') {
|
} else if(tokenKeyword(line[connectPos + 1]) === '...') {
|
||||||
if(labelSep !== line.length) {
|
if(labelSep !== line.length) {
|
||||||
|
@ -524,20 +518,20 @@ const PARSERS = [
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'connect-delay-begin',
|
|
||||||
tag: joinLabel(line, connectPos + 2, labelSep),
|
|
||||||
agent: readAgent(line, 0, connectPos, readOpts),
|
agent: readAgent(line, 0, connectPos, readOpts),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
tag: joinLabel(line, connectPos + 2, labelSep),
|
||||||
|
type: 'connect-delay-begin',
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
type: 'connect',
|
|
||||||
agents: [
|
agents: [
|
||||||
readAgent(line, 0, connectPos, readOpts),
|
readAgent(line, 0, connectPos, readOpts),
|
||||||
readAgent(line, connectPos + 1, labelSep, readOpts),
|
readAgent(line, connectPos + 1, labelSep, readOpts),
|
||||||
],
|
],
|
||||||
label: joinLabel(line, labelSep + 1),
|
label: joinLabel(line, labelSep + 1),
|
||||||
options: connectionToken.value,
|
options: connectionToken.value,
|
||||||
|
type: 'connect',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
|
@ -547,8 +541,8 @@ const PARSERS = [
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'mark',
|
|
||||||
name: joinLabel(line, 0, line.length - 1),
|
name: joinLabel(line, 0, line.length - 1),
|
||||||
|
type: 'mark',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
@ -571,33 +565,44 @@ const PARSERS = [
|
||||||
options.push(line[i].v);
|
options.push(line[i].v);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'agent options',
|
|
||||||
agent,
|
agent,
|
||||||
options,
|
options,
|
||||||
|
type: 'agent options',
|
||||||
};
|
};
|
||||||
}},
|
}},
|
||||||
];
|
];
|
||||||
|
|
||||||
function parseLine(line, {meta, stages}) {
|
function stageFromLine(line, meta) {
|
||||||
let stage = null;
|
|
||||||
for(const {begin, fn} of PARSERS) {
|
for(const {begin, fn} of PARSERS) {
|
||||||
if(skipOver(line, 0, begin) !== begin.length) {
|
if(skipOver(line, 0, begin) !== begin.length) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
stage = fn(line, meta);
|
const stage = fn(line, meta);
|
||||||
if(stage) {
|
if(stage) {
|
||||||
break;
|
return stage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLine(line, {meta, stages}) {
|
||||||
|
let parallel = false;
|
||||||
|
const [start] = line;
|
||||||
|
if(tokenKeyword(start) === '&') {
|
||||||
|
parallel = true;
|
||||||
|
line.splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stage = stageFromLine(line, meta);
|
||||||
|
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
throw makeError(
|
throw makeError('Unrecognised command: ' + joinLabel(line), line[0]);
|
||||||
'Unrecognised command: ' + joinLabel(line),
|
} else if(typeof stage === 'object') {
|
||||||
line[0]
|
stage.ln = start.b.ln;
|
||||||
);
|
stage.parallel = parallel;
|
||||||
}
|
|
||||||
if(typeof stage === 'object') {
|
|
||||||
stage.ln = line[0].b.ln;
|
|
||||||
stages.push(stage);
|
stages.push(stage);
|
||||||
|
} else if(parallel) {
|
||||||
|
throw makeError('Metadata cannot be parallel', start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,12 +618,12 @@ export default class Parser {
|
||||||
parseLines(lines, src) {
|
parseLines(lines, src) {
|
||||||
const result = {
|
const result = {
|
||||||
meta: {
|
meta: {
|
||||||
title: '',
|
|
||||||
theme: '',
|
|
||||||
code: src,
|
code: src,
|
||||||
terminators: 'none',
|
|
||||||
headers: 'box',
|
headers: 'box',
|
||||||
|
terminators: 'none',
|
||||||
textFormatter: markdownParser,
|
textFormatter: markdownParser,
|
||||||
|
theme: '',
|
||||||
|
title: '',
|
||||||
},
|
},
|
||||||
stages: [],
|
stages: [],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,66 +1,230 @@
|
||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
/* eslint-disable max-statements */
|
/* eslint-disable max-statements */
|
||||||
/* eslint-disable sort-keys */ // Maybe later
|
|
||||||
|
|
||||||
import Parser from './Parser.mjs';
|
import Parser from './Parser.mjs';
|
||||||
|
|
||||||
describe('Sequence Parser', () => {
|
describe('Sequence Parser', () => {
|
||||||
const parser = new Parser();
|
const parser = new Parser();
|
||||||
|
|
||||||
|
function makeParsedAgents(source) {
|
||||||
|
return source.map((item) => {
|
||||||
|
const base = {alias: '', flags: [], name: ''};
|
||||||
|
if(typeof item === 'object') {
|
||||||
|
return Object.assign(base, item);
|
||||||
|
} else {
|
||||||
|
return Object.assign(base, {name: item});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const PARSED = {
|
const PARSED = {
|
||||||
blockBegin: ({
|
agentBegin: (agents, {
|
||||||
ln = jasmine.anything(),
|
ln = jasmine.anything(),
|
||||||
blockType = jasmine.anything(),
|
mode = jasmine.anything(),
|
||||||
tag = jasmine.anything(),
|
parallel = false,
|
||||||
label = jasmine.anything(),
|
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type: 'block begin',
|
agents: makeParsedAgents(agents),
|
||||||
ln,
|
ln,
|
||||||
blockType,
|
mode,
|
||||||
tag,
|
parallel,
|
||||||
label,
|
type: 'agent begin',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
blockSplit: ({
|
agentDefine: (agents, {
|
||||||
ln = jasmine.anything(),
|
ln = jasmine.anything(),
|
||||||
blockType = jasmine.anything(),
|
parallel = false,
|
||||||
tag = jasmine.anything(),
|
|
||||||
label = jasmine.anything(),
|
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type: 'block split',
|
agents: makeParsedAgents(agents),
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
|
type: 'agent define',
|
||||||
|
}),
|
||||||
|
|
||||||
|
agentEnd: (agents, {
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
mode = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
|
agents: makeParsedAgents(agents),
|
||||||
|
ln,
|
||||||
|
mode,
|
||||||
|
parallel,
|
||||||
|
type: 'agent end',
|
||||||
|
}),
|
||||||
|
|
||||||
|
agentOptions: (agent, options, {
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
|
agent: makeParsedAgents([agent])[0],
|
||||||
|
ln,
|
||||||
|
options,
|
||||||
|
parallel,
|
||||||
|
type: 'agent options',
|
||||||
|
}),
|
||||||
|
|
||||||
|
async: (target, {
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
|
ln,
|
||||||
|
parallel,
|
||||||
|
target,
|
||||||
|
type: 'async',
|
||||||
|
}),
|
||||||
|
|
||||||
|
blockBegin: ({
|
||||||
|
blockType = jasmine.anything(),
|
||||||
|
label = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
tag = jasmine.anything(),
|
||||||
|
} = {}) => ({
|
||||||
blockType,
|
blockType,
|
||||||
tag,
|
|
||||||
label,
|
label,
|
||||||
|
ln,
|
||||||
|
parallel,
|
||||||
|
tag,
|
||||||
|
type: 'block begin',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
blockEnd: ({
|
blockEnd: ({
|
||||||
ln = jasmine.anything(),
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type: 'block end',
|
|
||||||
ln,
|
ln,
|
||||||
|
parallel,
|
||||||
|
type: 'block end',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
connect: (agentNames, {
|
blockSplit: ({
|
||||||
ln = jasmine.anything(),
|
blockType = jasmine.anything(),
|
||||||
line = jasmine.anything(),
|
|
||||||
left = jasmine.anything(),
|
|
||||||
right = jasmine.anything(),
|
|
||||||
label = jasmine.anything(),
|
label = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
tag = jasmine.anything(),
|
||||||
} = {}) => ({
|
} = {}) => ({
|
||||||
type: 'connect',
|
blockType,
|
||||||
ln,
|
|
||||||
agents: agentNames.map((name) => ({
|
|
||||||
name,
|
|
||||||
alias: '',
|
|
||||||
flags: [],
|
|
||||||
})),
|
|
||||||
label,
|
label,
|
||||||
options: {
|
ln,
|
||||||
line,
|
parallel,
|
||||||
left,
|
tag,
|
||||||
right,
|
type: 'block split',
|
||||||
},
|
}),
|
||||||
|
|
||||||
|
connect: (agents, {
|
||||||
|
label = jasmine.anything(),
|
||||||
|
left = jasmine.anything(),
|
||||||
|
line = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
right = jasmine.anything(),
|
||||||
|
} = {}) => ({
|
||||||
|
agents: makeParsedAgents(agents),
|
||||||
|
label,
|
||||||
|
ln,
|
||||||
|
options: {left, line, right},
|
||||||
|
parallel,
|
||||||
|
type: 'connect',
|
||||||
|
}),
|
||||||
|
|
||||||
|
connectBegin: (agent, tag, {
|
||||||
|
left = jasmine.anything(),
|
||||||
|
line = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
right = jasmine.anything(),
|
||||||
|
} = {}) => ({
|
||||||
|
agent: makeParsedAgents([agent])[0],
|
||||||
|
ln,
|
||||||
|
options: {left, line, right},
|
||||||
|
parallel,
|
||||||
|
tag,
|
||||||
|
type: 'connect-delay-begin',
|
||||||
|
}),
|
||||||
|
|
||||||
|
connectEnd: (agent, tag, {
|
||||||
|
label = jasmine.anything(),
|
||||||
|
left = jasmine.anything(),
|
||||||
|
line = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
right = jasmine.anything(),
|
||||||
|
} = {}) => ({
|
||||||
|
agent: makeParsedAgents([agent])[0],
|
||||||
|
label,
|
||||||
|
ln,
|
||||||
|
options: {left, line, right},
|
||||||
|
parallel,
|
||||||
|
tag,
|
||||||
|
type: 'connect-delay-end',
|
||||||
|
}),
|
||||||
|
|
||||||
|
divider: ({
|
||||||
|
height = jasmine.anything(),
|
||||||
|
label = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
mode = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
|
height,
|
||||||
|
label,
|
||||||
|
ln,
|
||||||
|
mode,
|
||||||
|
parallel,
|
||||||
|
type: 'divider',
|
||||||
|
}),
|
||||||
|
|
||||||
|
groupBegin: (agents, {
|
||||||
|
alias = jasmine.anything(),
|
||||||
|
blockType = jasmine.anything(),
|
||||||
|
label = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
tag = jasmine.anything(),
|
||||||
|
} = {}) => ({
|
||||||
|
agents: makeParsedAgents(agents),
|
||||||
|
alias,
|
||||||
|
blockType,
|
||||||
|
label,
|
||||||
|
ln,
|
||||||
|
parallel,
|
||||||
|
tag,
|
||||||
|
type: 'group begin',
|
||||||
|
}),
|
||||||
|
|
||||||
|
labelPattern: (pattern, {
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
|
ln,
|
||||||
|
parallel,
|
||||||
|
pattern,
|
||||||
|
type: 'label pattern',
|
||||||
|
}),
|
||||||
|
|
||||||
|
mark: (name, {
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
|
ln,
|
||||||
|
name,
|
||||||
|
parallel,
|
||||||
|
type: 'mark',
|
||||||
|
}),
|
||||||
|
|
||||||
|
note: (position, agents, {
|
||||||
|
label = jasmine.anything(),
|
||||||
|
ln = jasmine.anything(),
|
||||||
|
mode = 'note',
|
||||||
|
parallel = false,
|
||||||
|
} = {}) => ({
|
||||||
|
agents: makeParsedAgents(agents),
|
||||||
|
label,
|
||||||
|
ln,
|
||||||
|
mode,
|
||||||
|
parallel,
|
||||||
|
type: 'note ' + position,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -143,9 +307,9 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('define Foo Bar as A');
|
const parsed = parser.parse('define Foo Bar as A');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{type: 'agent define', ln: jasmine.anything(), agents: [
|
PARSED.agentDefine([
|
||||||
{name: 'Foo Bar', alias: 'A', flags: []},
|
{alias: 'A', name: 'Foo Bar'},
|
||||||
]},
|
]),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -153,9 +317,9 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('define Foo Bar as A B');
|
const parsed = parser.parse('define Foo Bar as A B');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{type: 'agent define', ln: jasmine.anything(), agents: [
|
PARSED.agentDefine([
|
||||||
{name: 'Foo Bar', alias: 'A B', flags: []},
|
{alias: 'A B', name: 'Foo Bar'},
|
||||||
]},
|
]),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -163,9 +327,7 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('define Foo Bar as');
|
const parsed = parser.parse('define Foo Bar as');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{type: 'agent define', ln: jasmine.anything(), agents: [
|
PARSED.agentDefine(['Foo Bar']),
|
||||||
{name: 'Foo Bar', alias: '', flags: []},
|
|
||||||
]},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -173,16 +335,7 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('Foo bar is zig zag');
|
const parsed = parser.parse('Foo bar is zig zag');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.agentOptions('Foo bar', ['zig', 'zag']),
|
||||||
type: 'agent options',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agent: {
|
|
||||||
name: 'Foo bar',
|
|
||||||
alias: '',
|
|
||||||
flags: [],
|
|
||||||
},
|
|
||||||
options: ['zig', 'zag'],
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -190,26 +343,8 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('Foo is a zig\nBar is an oom');
|
const parsed = parser.parse('Foo is a zig\nBar is an oom');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.agentOptions('Foo', ['zig']),
|
||||||
type: 'agent options',
|
PARSED.agentOptions('Bar', ['oom']),
|
||||||
ln: jasmine.anything(),
|
|
||||||
agent: {
|
|
||||||
name: 'Foo',
|
|
||||||
alias: '',
|
|
||||||
flags: [],
|
|
||||||
},
|
|
||||||
options: ['zig'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'agent options',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agent: {
|
|
||||||
name: 'Bar',
|
|
||||||
alias: '',
|
|
||||||
flags: [],
|
|
||||||
},
|
|
||||||
options: ['oom'],
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -243,20 +378,10 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('+A -> -*!B');
|
const parsed = parser.parse('+A -> -*!B');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.connect([
|
||||||
type: 'connect',
|
{flags: ['start'], name: 'A'},
|
||||||
ln: jasmine.anything(),
|
{flags: ['stop', 'begin', 'end'], name: 'B'},
|
||||||
agents: [
|
]),
|
||||||
{name: 'A', alias: '', flags: ['start']},
|
|
||||||
{name: 'B', alias: '', flags: [
|
|
||||||
'stop',
|
|
||||||
'begin',
|
|
||||||
'end',
|
|
||||||
]},
|
|
||||||
],
|
|
||||||
label: jasmine.anything(),
|
|
||||||
options: jasmine.anything(),
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -286,16 +411,7 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('A -> *');
|
const parsed = parser.parse('A -> *');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.connect(['A', {flags: ['source'], name: ''}]),
|
||||||
type: 'connect',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [
|
|
||||||
{name: 'A', alias: '', flags: []},
|
|
||||||
{name: '', alias: '', flags: ['source']},
|
|
||||||
],
|
|
||||||
label: jasmine.anything(),
|
|
||||||
options: jasmine.anything(),
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -303,16 +419,9 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('A -> *: foo');
|
const parsed = parser.parse('A -> *: foo');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.connect(['A', {flags: ['source'], name: ''}], {
|
||||||
type: 'connect',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [
|
|
||||||
{name: 'A', alias: '', flags: []},
|
|
||||||
{name: '', alias: '', flags: ['source']},
|
|
||||||
],
|
|
||||||
label: 'foo',
|
label: 'foo',
|
||||||
options: jasmine.anything(),
|
}),
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -376,37 +485,37 @@ describe('Sequence Parser', () => {
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
PARSED.connect(['A', 'B'], {
|
PARSED.connect(['A', 'B'], {
|
||||||
line: 'solid',
|
|
||||||
left: 0,
|
|
||||||
right: 1,
|
|
||||||
label: '',
|
label: '',
|
||||||
|
left: 0,
|
||||||
|
line: 'solid',
|
||||||
|
right: 1,
|
||||||
}),
|
}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 0, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'solid', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 1, right: 0}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'solid', right: 0}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 1, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'solid', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 1, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'solid', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 2, right: 0}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'solid', right: 0}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 2, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'solid', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 2, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'solid', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'solid', left: 0, right: 3}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'solid', right: 3}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 0, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'dash', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 0, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'dash', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 1, right: 0}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'dash', right: 0}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 1, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'dash', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 1, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'dash', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 2, right: 0}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'dash', right: 0}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 2, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'dash', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 2, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'dash', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'dash', left: 0, right: 3}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'dash', right: 3}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 0, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'wave', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 0, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'wave', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 1, right: 0}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'wave', right: 0}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 1, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'wave', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 1, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 1, line: 'wave', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 2, right: 0}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'wave', right: 0}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 2, right: 1}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'wave', right: 1}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 2, right: 2}),
|
PARSED.connect(['A', 'B'], {left: 2, line: 'wave', right: 2}),
|
||||||
PARSED.connect(['A', 'B'], {line: 'wave', left: 0, right: 3}),
|
PARSED.connect(['A', 'B'], {left: 0, line: 'wave', right: 3}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -418,16 +527,16 @@ describe('Sequence Parser', () => {
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
PARSED.connect(['A', 'B'], {
|
PARSED.connect(['A', 'B'], {
|
||||||
line: 'solid',
|
|
||||||
left: 1,
|
|
||||||
right: 0,
|
|
||||||
label: 'B -> A',
|
label: 'B -> A',
|
||||||
|
left: 1,
|
||||||
|
line: 'solid',
|
||||||
|
right: 0,
|
||||||
}),
|
}),
|
||||||
PARSED.connect(['A', 'B'], {
|
PARSED.connect(['A', 'B'], {
|
||||||
line: 'solid',
|
|
||||||
left: 0,
|
|
||||||
right: 1,
|
|
||||||
label: 'B <- A',
|
label: 'B <- A',
|
||||||
|
left: 0,
|
||||||
|
line: 'solid',
|
||||||
|
right: 1,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -436,50 +545,25 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('+A <- ...foo\n...foo -> -B: woo');
|
const parsed = parser.parse('+A <- ...foo\n...foo -> -B: woo');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.connectBegin(
|
||||||
type: 'connect-delay-begin',
|
{flags: ['start'], name: 'A'},
|
||||||
ln: jasmine.anything(),
|
'foo',
|
||||||
tag: 'foo',
|
{left: 1, line: 'solid', right: 0}
|
||||||
agent: {
|
),
|
||||||
name: 'A',
|
PARSED.connectEnd(
|
||||||
alias: '',
|
{flags: ['stop'], name: 'B'},
|
||||||
flags: ['start'],
|
'foo',
|
||||||
},
|
{label: 'woo', left: 0, line: 'solid', right: 1}
|
||||||
options: {
|
),
|
||||||
line: 'solid',
|
|
||||||
left: 1,
|
|
||||||
right: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'connect-delay-end',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
tag: 'foo',
|
|
||||||
agent: {
|
|
||||||
name: 'B',
|
|
||||||
alias: '',
|
|
||||||
flags: ['stop'],
|
|
||||||
},
|
|
||||||
label: 'woo',
|
|
||||||
options: {
|
|
||||||
line: 'solid',
|
|
||||||
left: 0,
|
|
||||||
right: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts notes', () => {
|
it('converts notes', () => {
|
||||||
const parsed = parser.parse('note over A: hello there');
|
const parsed = parser.parse('note over A: hello there');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'note over',
|
PARSED.note('over', ['A'], {label: 'hello there'}),
|
||||||
ln: jasmine.anything(),
|
]);
|
||||||
agents: [{name: 'A', alias: '', flags: []}],
|
|
||||||
mode: 'note',
|
|
||||||
label: 'hello there',
|
|
||||||
}]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts different note types', () => {
|
it('converts different note types', () => {
|
||||||
|
@ -492,60 +576,20 @@ describe('Sequence Parser', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.note('left', ['A'], {label: 'hello there'}),
|
||||||
type: 'note left',
|
PARSED.note('left', ['A'], {label: 'hello there'}),
|
||||||
ln: jasmine.anything(),
|
PARSED.note('right', ['A'], {label: 'hello there'}),
|
||||||
agents: [{name: 'A', alias: '', flags: []}],
|
PARSED.note('right', ['A'], {label: 'hello there'}),
|
||||||
mode: 'note',
|
PARSED.note('between', ['A', 'B'], {label: 'hi'}),
|
||||||
label: 'hello there',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'note left',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [{name: 'A', alias: '', flags: []}],
|
|
||||||
mode: 'note',
|
|
||||||
label: 'hello there',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'note right',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [{name: 'A', alias: '', flags: []}],
|
|
||||||
mode: 'note',
|
|
||||||
label: 'hello there',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'note right',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [{name: 'A', alias: '', flags: []}],
|
|
||||||
mode: 'note',
|
|
||||||
label: 'hello there',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'note between',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [
|
|
||||||
{name: 'A', alias: '', flags: []},
|
|
||||||
{name: 'B', alias: '', flags: []},
|
|
||||||
],
|
|
||||||
mode: 'note',
|
|
||||||
label: 'hi',
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows multiple agents for notes', () => {
|
it('allows multiple agents for notes', () => {
|
||||||
const parsed = parser.parse('note over A B, C D: hi');
|
const parsed = parser.parse('note over A B, C D: hi');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'note over',
|
PARSED.note('over', ['A B', 'C D'], {label: 'hi'}),
|
||||||
ln: jasmine.anything(),
|
]);
|
||||||
agents: [
|
|
||||||
{name: 'A B', alias: '', flags: []},
|
|
||||||
{name: 'C D', alias: '', flags: []},
|
|
||||||
],
|
|
||||||
mode: 'note',
|
|
||||||
label: 'hi',
|
|
||||||
}]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects note between for a single agent', () => {
|
it('rejects note between for a single agent', () => {
|
||||||
|
@ -557,13 +601,12 @@ describe('Sequence Parser', () => {
|
||||||
it('converts state', () => {
|
it('converts state', () => {
|
||||||
const parsed = parser.parse('state over A: doing stuff');
|
const parsed = parser.parse('state over A: doing stuff');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'note over',
|
PARSED.note('over', ['A'], {
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [{name: 'A', alias: '', flags: []}],
|
|
||||||
mode: 'state',
|
|
||||||
label: 'doing stuff',
|
label: 'doing stuff',
|
||||||
}]);
|
mode: 'state',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects multiple agents for state', () => {
|
it('rejects multiple agents for state', () => {
|
||||||
|
@ -575,13 +618,12 @@ describe('Sequence Parser', () => {
|
||||||
it('converts text blocks', () => {
|
it('converts text blocks', () => {
|
||||||
const parsed = parser.parse('text right of A: doing stuff');
|
const parsed = parser.parse('text right of A: doing stuff');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'note right',
|
PARSED.note('right', ['A'], {
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [{name: 'A', alias: '', flags: []}],
|
|
||||||
mode: 'text',
|
|
||||||
label: 'doing stuff',
|
label: 'doing stuff',
|
||||||
}]);
|
mode: 'text',
|
||||||
|
}),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts agent commands', () => {
|
it('converts agent commands', () => {
|
||||||
|
@ -592,45 +634,18 @@ describe('Sequence Parser', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.agentDefine(['A', 'B']),
|
||||||
type: 'agent define',
|
PARSED.agentBegin(['A', 'B'], {mode: 'box'}),
|
||||||
ln: jasmine.anything(),
|
PARSED.agentEnd(['A', 'B'], {mode: 'cross'}),
|
||||||
agents: [
|
|
||||||
{name: 'A', alias: '', flags: []},
|
|
||||||
{name: 'B', alias: '', flags: []},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'agent begin',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [
|
|
||||||
{name: 'A', alias: '', flags: []},
|
|
||||||
{name: 'B', alias: '', flags: []},
|
|
||||||
],
|
|
||||||
mode: 'box',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'agent end',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [
|
|
||||||
{name: 'A', alias: '', flags: []},
|
|
||||||
{name: 'B', alias: '', flags: []},
|
|
||||||
],
|
|
||||||
mode: 'cross',
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts dividers', () => {
|
it('converts dividers', () => {
|
||||||
const parsed = parser.parse('divider');
|
const parsed = parser.parse('divider');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'divider',
|
PARSED.divider({height: 6, label: '', mode: 'line'}),
|
||||||
ln: jasmine.anything(),
|
]);
|
||||||
mode: 'line',
|
|
||||||
height: 6,
|
|
||||||
label: '',
|
|
||||||
}]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts different divider types', () => {
|
it('converts different divider types', () => {
|
||||||
|
@ -642,34 +657,10 @@ describe('Sequence Parser', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.divider({height: 6, mode: 'line'}),
|
||||||
type: 'divider',
|
PARSED.divider({height: 6, mode: 'space'}),
|
||||||
ln: jasmine.anything(),
|
PARSED.divider({height: 30, mode: 'delay'}),
|
||||||
mode: 'line',
|
PARSED.divider({height: 6, mode: 'tear'}),
|
||||||
height: 6,
|
|
||||||
label: jasmine.anything(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
mode: 'space',
|
|
||||||
height: 6,
|
|
||||||
label: jasmine.anything(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
mode: 'delay',
|
|
||||||
height: 30,
|
|
||||||
label: jasmine.anything(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
mode: 'tear',
|
|
||||||
height: 6,
|
|
||||||
label: jasmine.anything(),
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -680,20 +671,8 @@ describe('Sequence Parser', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.divider({height: 40, label: '', mode: 'line'}),
|
||||||
type: 'divider',
|
PARSED.divider({height: 0, label: '', mode: 'delay'}),
|
||||||
ln: jasmine.anything(),
|
|
||||||
mode: 'line',
|
|
||||||
height: 40,
|
|
||||||
label: '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
mode: 'delay',
|
|
||||||
height: 0,
|
|
||||||
label: '',
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -705,27 +684,9 @@ describe('Sequence Parser', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.divider({label: 'message 1'}),
|
||||||
type: 'divider',
|
PARSED.divider({label: 'message 2'}),
|
||||||
ln: jasmine.anything(),
|
PARSED.divider({label: 'message 3'}),
|
||||||
mode: jasmine.anything(),
|
|
||||||
height: jasmine.anything(),
|
|
||||||
label: 'message 1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
mode: jasmine.anything(),
|
|
||||||
height: jasmine.anything(),
|
|
||||||
label: 'message 2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
mode: jasmine.anything(),
|
|
||||||
height: jasmine.anything(),
|
|
||||||
label: 'message 3',
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -736,53 +697,34 @@ describe('Sequence Parser', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.groupBegin([], {
|
||||||
type: 'group begin',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [],
|
|
||||||
blockType: 'ref',
|
|
||||||
tag: 'ref',
|
|
||||||
label: 'Foo bar',
|
|
||||||
alias: 'baz',
|
alias: 'baz',
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'group begin',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
agents: [
|
|
||||||
{name: 'A', alias: '', flags: []},
|
|
||||||
{name: 'B', alias: '', flags: []},
|
|
||||||
],
|
|
||||||
blockType: 'ref',
|
blockType: 'ref',
|
||||||
tag: 'ref',
|
|
||||||
label: 'Foo bar',
|
label: 'Foo bar',
|
||||||
|
tag: 'ref',
|
||||||
|
}),
|
||||||
|
PARSED.groupBegin(['A', 'B'], {
|
||||||
alias: 'baz',
|
alias: 'baz',
|
||||||
},
|
blockType: 'ref',
|
||||||
|
label: 'Foo bar',
|
||||||
|
tag: 'ref',
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts markers', () => {
|
it('converts markers', () => {
|
||||||
const parsed = parser.parse('abc:');
|
const parsed = parser.parse('abc:');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'mark',
|
PARSED.mark('abc'),
|
||||||
ln: jasmine.anything(),
|
]);
|
||||||
name: 'abc',
|
|
||||||
}]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts autolabel commands', () => {
|
it('converts autolabel commands', () => {
|
||||||
const parsed = parser.parse('autolabel "foo <label> bar"');
|
const parsed = parser.parse('autolabel "foo <label> bar"');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.labelPattern(['foo ', {token: 'label'}, ' bar']),
|
||||||
type: 'label pattern',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
pattern: [
|
|
||||||
'foo ',
|
|
||||||
{token: 'label'},
|
|
||||||
' bar',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -790,32 +732,24 @@ describe('Sequence Parser', () => {
|
||||||
const parsed = parser.parse('autolabel off');
|
const parsed = parser.parse('autolabel off');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
PARSED.labelPattern([{token: 'label'}]),
|
||||||
type: 'label pattern',
|
|
||||||
ln: jasmine.anything(),
|
|
||||||
pattern: [{token: 'label'}],
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts "simultaneously" flow commands', () => {
|
it('converts "simultaneously" flow commands', () => {
|
||||||
const parsed = parser.parse('simultaneously:');
|
const parsed = parser.parse('simultaneously:');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'async',
|
PARSED.async(''),
|
||||||
ln: jasmine.anything(),
|
]);
|
||||||
target: '',
|
|
||||||
}]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts named "simultaneously" flow commands', () => {
|
it('converts named "simultaneously" flow commands', () => {
|
||||||
const parsed = parser.parse('simultaneously with abc:');
|
const parsed = parser.parse('simultaneously with abc:');
|
||||||
|
|
||||||
expect(parsed.stages).toEqual([{
|
expect(parsed.stages).toEqual([
|
||||||
type: 'async',
|
PARSED.async('abc'),
|
||||||
ln: jasmine.anything(),
|
]);
|
||||||
target: 'abc',
|
|
||||||
}]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('converts conditional blocks', () => {
|
it('converts conditional blocks', () => {
|
||||||
|
@ -833,21 +767,21 @@ describe('Sequence Parser', () => {
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
PARSED.blockBegin({
|
PARSED.blockBegin({
|
||||||
blockType: 'if',
|
blockType: 'if',
|
||||||
tag: 'if',
|
|
||||||
label: 'something happens',
|
label: 'something happens',
|
||||||
|
tag: 'if',
|
||||||
}),
|
}),
|
||||||
PARSED.connect(['A', 'B']),
|
PARSED.connect(['A', 'B']),
|
||||||
PARSED.blockSplit({
|
PARSED.blockSplit({
|
||||||
blockType: 'else',
|
blockType: 'else',
|
||||||
tag: 'else',
|
|
||||||
label: 'something else',
|
label: 'something else',
|
||||||
|
tag: 'else',
|
||||||
}),
|
}),
|
||||||
PARSED.connect(['A', 'C']),
|
PARSED.connect(['A', 'C']),
|
||||||
PARSED.connect(['C', 'B']),
|
PARSED.connect(['C', 'B']),
|
||||||
PARSED.blockSplit({
|
PARSED.blockSplit({
|
||||||
blockType: 'else',
|
blockType: 'else',
|
||||||
tag: 'else',
|
|
||||||
label: '',
|
label: '',
|
||||||
|
tag: 'else',
|
||||||
}),
|
}),
|
||||||
PARSED.connect(['A', 'D']),
|
PARSED.connect(['A', 'D']),
|
||||||
PARSED.blockEnd(),
|
PARSED.blockEnd(),
|
||||||
|
@ -860,8 +794,8 @@ describe('Sequence Parser', () => {
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
PARSED.blockBegin({
|
PARSED.blockBegin({
|
||||||
blockType: 'repeat',
|
blockType: 'repeat',
|
||||||
tag: 'repeat',
|
|
||||||
label: 'until something',
|
label: 'until something',
|
||||||
|
tag: 'repeat',
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -872,12 +806,26 @@ describe('Sequence Parser', () => {
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
PARSED.blockBegin({
|
PARSED.blockBegin({
|
||||||
blockType: 'group',
|
blockType: 'group',
|
||||||
tag: '',
|
|
||||||
label: 'something',
|
label: 'something',
|
||||||
|
tag: '',
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('propagates parallel markers', () => {
|
||||||
|
const parsed = parser.parse('& A -> B');
|
||||||
|
|
||||||
|
expect(parsed.stages).toEqual([
|
||||||
|
PARSED.connect(['A', 'B'], {parallel: true}),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects parallel markers on metadata', () => {
|
||||||
|
expect(() => parser.parse('& title foo')).toThrow(new Error(
|
||||||
|
'Metadata cannot be parallel at line 1, character 0'
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
it('rejects quoted keywords', () => {
|
it('rejects quoted keywords', () => {
|
||||||
expect(() => parser.parse('"repeat" until something')).toThrow();
|
expect(() => parser.parse('"repeat" until something')).toThrow();
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 13 KiB |
|
@ -10,4 +10,5 @@ export default [
|
||||||
'ReferenceLayering.svg',
|
'ReferenceLayering.svg',
|
||||||
'Markdown.svg',
|
'Markdown.svg',
|
||||||
'AgentOptions.svg',
|
'AgentOptions.svg',
|
||||||
|
'Parallel.svg',
|
||||||
];
|
];
|
||||||
|
|
|
@ -39,6 +39,13 @@
|
||||||
code: '* -> {Agent1}: {Message}',
|
code: '* -> {Agent1}: {Message}',
|
||||||
title: 'Found message',
|
title: 'Found message',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
code: (
|
||||||
|
'{Agent1} -> {Agent2}\n' +
|
||||||
|
'& {Agent1} -> {Agent3}: {Broadcast}'
|
||||||
|
),
|
||||||
|
title: 'Broadcast message',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
code: (
|
code: (
|
||||||
'{Agent1} -> +{Agent2}: {Request}\n' +
|
'{Agent1} -> +{Agent2}: {Request}\n' +
|
||||||
|
@ -178,6 +185,13 @@
|
||||||
code: 'note between {Agent1}, {Agent2}: {Message}',
|
code: 'note between {Agent1}, {Agent2}: {Message}',
|
||||||
title: 'Note between agents',
|
title: 'Note between agents',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
code: (
|
||||||
|
'{Agent1} -> {Agent2}\n' +
|
||||||
|
'& note right of {Agent2}: {Message}'
|
||||||
|
),
|
||||||
|
title: 'Inline note',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
code: 'state over {Agent1}: {State}',
|
code: 'state over {Agent1}: {State}',
|
||||||
title: 'State over agent',
|
title: 'State over agent',
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -36,6 +36,13 @@ export default [
|
||||||
code: '* -> {Agent1}: {Message}',
|
code: '* -> {Agent1}: {Message}',
|
||||||
title: 'Found message',
|
title: 'Found message',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
code: (
|
||||||
|
'{Agent1} -> {Agent2}\n' +
|
||||||
|
'& {Agent1} -> {Agent3}: {Broadcast}'
|
||||||
|
),
|
||||||
|
title: 'Broadcast message',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
code: (
|
code: (
|
||||||
'{Agent1} -> +{Agent2}: {Request}\n' +
|
'{Agent1} -> +{Agent2}: {Request}\n' +
|
||||||
|
@ -175,6 +182,13 @@ export default [
|
||||||
code: 'note between {Agent1}, {Agent2}: {Message}',
|
code: 'note between {Agent1}, {Agent2}: {Message}',
|
||||||
title: 'Note between agents',
|
title: 'Note between agents',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
code: (
|
||||||
|
'{Agent1} -> {Agent2}\n' +
|
||||||
|
'& note right of {Agent2}: {Message}'
|
||||||
|
),
|
||||||
|
title: 'Inline note',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
code: 'state over {Agent1}: {State}',
|
code: 'state over {Agent1}: {State}',
|
||||||
title: 'State over agent',
|
title: 'State over agent',
|
||||||
|
|
Loading…
Reference in New Issue