Begin introducing concept of agents and tokens having metadata
This commit is contained in:
parent
55b5232fa6
commit
12f81b4f9d
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
requirejs.config(window.getRequirejsCDN());
|
requirejs.config(window.getRequirejsCDN());
|
||||||
|
|
||||||
/* jshint -W072 */
|
/* jshint -W072 */ // Allow several required modules
|
||||||
requirejs([
|
requirejs([
|
||||||
'interface/Interface',
|
'interface/Interface',
|
||||||
'sequence/Parser',
|
'sequence/Parser',
|
||||||
|
|
|
@ -20,10 +20,11 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
const CM_AGENT_LIST_TO_TEXT = makeCMCommaBlock('variable', 'Agent', {
|
const CM_AGENT_LIST_TO_TEXT = makeCMCommaBlock('variable', 'Agent', {
|
||||||
':': {type: 'operator', suggest: true, then: {'': CM_TEXT_TO_END}},
|
':': {type: 'operator', suggest: true, then: {'': CM_TEXT_TO_END}},
|
||||||
});
|
});
|
||||||
const CM_AGENT_LIST_TO_OPTTEXT = makeCMCommaBlock('variable', 'Agent', {
|
const CM_AGENT_TO_OPTTEXT = {type: 'variable', suggest: 'Agent', then: {
|
||||||
|
'': 0,
|
||||||
':': {type: 'operator', suggest: true, then: {'': CM_TEXT_TO_END}},
|
':': {type: 'operator', suggest: true, then: {'': CM_TEXT_TO_END}},
|
||||||
'\n': CM_END,
|
'\n': CM_END,
|
||||||
});
|
}};
|
||||||
|
|
||||||
const CM_NOTE_SIDE_THEN = {
|
const CM_NOTE_SIDE_THEN = {
|
||||||
'of': {type: 'keyword', suggest: true, then: {
|
'of': {type: 'keyword', suggest: true, then: {
|
||||||
|
@ -35,20 +36,29 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
'': CM_AGENT_LIST_TO_TEXT,
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CM_NOTE_LSIDE = {
|
function makeCMSideNote(side) {
|
||||||
|
return {
|
||||||
type: 'keyword',
|
type: 'keyword',
|
||||||
suggest: ['left of ', 'left: '],
|
suggest: [side + ' of ', side + ': '],
|
||||||
then: CM_NOTE_SIDE_THEN,
|
|
||||||
};
|
|
||||||
|
|
||||||
const CM_NOTE_RSIDE = {
|
|
||||||
type: 'keyword',
|
|
||||||
suggest: ['right of ', 'right: '],
|
|
||||||
then: CM_NOTE_SIDE_THEN,
|
then: CM_NOTE_SIDE_THEN,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const CM_CONNECT = {type: 'keyword', suggest: true, then: {
|
const CM_CONNECT = {type: 'keyword', suggest: true, then: {
|
||||||
'': CM_AGENT_LIST_TO_OPTTEXT,
|
'+': {type: 'operator', suggest: true, then: {'': CM_AGENT_TO_OPTTEXT}},
|
||||||
|
'-': {type: 'operator', suggest: true, then: {'': CM_AGENT_TO_OPTTEXT}},
|
||||||
|
'': CM_AGENT_TO_OPTTEXT,
|
||||||
|
}};
|
||||||
|
|
||||||
|
const CM_CONNECT_FULL = {type: 'variable', suggest: 'Agent', then: {
|
||||||
|
'->': CM_CONNECT,
|
||||||
|
'-->': CM_CONNECT,
|
||||||
|
'<-': CM_CONNECT,
|
||||||
|
'<--': CM_CONNECT,
|
||||||
|
'<->': CM_CONNECT,
|
||||||
|
'<-->': CM_CONNECT,
|
||||||
|
':': {type: 'operator', suggest: true, override: 'Label', then: {}},
|
||||||
|
'': 0,
|
||||||
}};
|
}};
|
||||||
|
|
||||||
const CM_COMMANDS = {type: 'error', then: {
|
const CM_COMMANDS = {type: 'error', then: {
|
||||||
|
@ -98,8 +108,8 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
'over': {type: 'keyword', suggest: true, then: {
|
'over': {type: 'keyword', suggest: true, then: {
|
||||||
'': CM_AGENT_LIST_TO_TEXT,
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
}},
|
}},
|
||||||
'left': CM_NOTE_LSIDE,
|
'left': makeCMSideNote('left'),
|
||||||
'right': CM_NOTE_RSIDE,
|
'right': makeCMSideNote('right'),
|
||||||
'between': {type: 'keyword', suggest: true, then: {
|
'between': {type: 'keyword', suggest: true, then: {
|
||||||
'': CM_AGENT_LIST_TO_TEXT,
|
'': CM_AGENT_LIST_TO_TEXT,
|
||||||
}},
|
}},
|
||||||
|
@ -110,8 +120,8 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
'text': {type: 'keyword', suggest: true, then: {
|
'text': {type: 'keyword', suggest: true, then: {
|
||||||
'left': CM_NOTE_LSIDE,
|
'left': makeCMSideNote('left'),
|
||||||
'right': CM_NOTE_RSIDE,
|
'right': makeCMSideNote('right'),
|
||||||
}},
|
}},
|
||||||
'simultaneously': {type: 'keyword', suggest: true, then: {
|
'simultaneously': {type: 'keyword', suggest: true, then: {
|
||||||
':': {type: 'operator', suggest: true, then: {}},
|
':': {type: 'operator', suggest: true, then: {}},
|
||||||
|
@ -122,31 +132,38 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
'': {type: 'variable', suggest: 'Agent', then: {
|
'+': {type: 'operator', suggest: true, then: {'': CM_CONNECT_FULL}},
|
||||||
'->': CM_CONNECT,
|
'-': {type: 'operator', suggest: true, then: {'': CM_CONNECT_FULL}},
|
||||||
'-->': CM_CONNECT,
|
'': CM_CONNECT_FULL,
|
||||||
'<-': CM_CONNECT,
|
|
||||||
'<--': CM_CONNECT,
|
|
||||||
'<->': CM_CONNECT,
|
|
||||||
'<-->': CM_CONNECT,
|
|
||||||
':': {type: 'operator', suggest: true, override: 'Label', then: {}},
|
|
||||||
'': 0,
|
|
||||||
}},
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
function cmGetSuggestions(state, token, {suggest, then}) {
|
function cmCappedToken(token, current) {
|
||||||
if(token === '') {
|
if(Object.keys(current.then).length > 0) {
|
||||||
return state['known' + suggest];
|
return token + ' ';
|
||||||
} else if(suggest === true) {
|
|
||||||
if(Object.keys(then).length > 0) {
|
|
||||||
return [token + ' '];
|
|
||||||
} else {
|
} else {
|
||||||
return [token + '\n'];
|
return token + '\n';
|
||||||
}
|
}
|
||||||
} else if(Array.isArray(suggest)) {
|
}
|
||||||
return suggest;
|
|
||||||
} else if(suggest) {
|
function cmGetVarSuggestions(state, previous, current) {
|
||||||
return [suggest];
|
if(
|
||||||
|
typeof current.suggest !== 'string' ||
|
||||||
|
previous.suggest === current.suggest
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return state['known' + current.suggest];
|
||||||
|
}
|
||||||
|
|
||||||
|
function cmGetSuggestions(state, token, previous, current) {
|
||||||
|
if(token === '') {
|
||||||
|
return cmGetVarSuggestions(state, previous, current);
|
||||||
|
} else if(current.suggest === true) {
|
||||||
|
return [cmCappedToken(token, current)];
|
||||||
|
} else if(Array.isArray(current.suggest)) {
|
||||||
|
return current.suggest;
|
||||||
|
} else if(current.suggest) {
|
||||||
|
return [current.suggest];
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -154,13 +171,16 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
|
|
||||||
function cmMakeCompletions(state, path) {
|
function cmMakeCompletions(state, path) {
|
||||||
const comp = [];
|
const comp = [];
|
||||||
const {then} = array.last(path);
|
const current = array.last(path);
|
||||||
Object.keys(then).forEach((token) => {
|
Object.keys(current.then).forEach((token) => {
|
||||||
let next = then[token];
|
let next = current.then[token];
|
||||||
if(typeof next === 'number') {
|
if(typeof next === 'number') {
|
||||||
next = path[path.length - next - 1];
|
next = path[path.length - next - 1];
|
||||||
}
|
}
|
||||||
array.mergeSets(comp, cmGetSuggestions(state, token, next));
|
array.mergeSets(
|
||||||
|
comp,
|
||||||
|
cmGetSuggestions(state, token, current, next)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
return comp;
|
return comp;
|
||||||
}
|
}
|
||||||
|
@ -213,6 +233,10 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
return current.type;
|
return current.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInitialValue(block) {
|
||||||
|
return (block.baseToken || {}).v || '';
|
||||||
|
}
|
||||||
|
|
||||||
return class Mode {
|
return class Mode {
|
||||||
constructor(tokenDefinitions) {
|
constructor(tokenDefinitions) {
|
||||||
this.tokenDefinitions = tokenDefinitions;
|
this.tokenDefinitions = tokenDefinitions;
|
||||||
|
@ -250,7 +274,7 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
const block = this.tokenDefinitions[i];
|
const block = this.tokenDefinitions[i];
|
||||||
if(this._matchPattern(stream, block.start, true)) {
|
if(this._matchPattern(stream, block.start, true)) {
|
||||||
state.currentType = i;
|
state.currentType = i;
|
||||||
state.current = block.prefix || '';
|
state.current = getInitialValue(block);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,12 +295,6 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
return 'comment';
|
return 'comment';
|
||||||
}
|
}
|
||||||
state.line.push(state.current);
|
state.line.push(state.current);
|
||||||
if(state.current === '\n') {
|
|
||||||
// quoted newline is interpreted as a command separator;
|
|
||||||
// probably not what the writer expected, so highlight it
|
|
||||||
state.line.length = 0;
|
|
||||||
return 'warning';
|
|
||||||
}
|
|
||||||
return cmCheckToken(state, stream.eol());
|
return cmCheckToken(state, stream.eol());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,9 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
const DEFAULT_AGENT = new AgentState(false);
|
const DEFAULT_AGENT = new AgentState(false);
|
||||||
|
|
||||||
const NOTE_DEFAULT_AGENTS = {
|
const NOTE_DEFAULT_AGENTS = {
|
||||||
'note over': ['[', ']'],
|
'note over': [{name: '['}, {name: ']'}],
|
||||||
'note left': ['['],
|
'note left': [{name: '['}],
|
||||||
'note right': [']'],
|
'note right': [{name: ']'}],
|
||||||
};
|
};
|
||||||
|
|
||||||
return class Generator {
|
return class Generator {
|
||||||
|
@ -151,16 +151,19 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentDefine({agents}) {
|
handleAgentDefine({agents}) {
|
||||||
array.mergeSets(this.currentNest.agents, agents);
|
const agentNames = agents.map((agent) => agent.name);
|
||||||
array.mergeSets(this.agents, agents);
|
array.mergeSets(this.currentNest.agents, agentNames);
|
||||||
|
array.mergeSets(this.agents, agentNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentBegin({agents, mode}) {
|
handleAgentBegin({agents, mode}) {
|
||||||
this.setAgentVis(agents, true, mode, true);
|
const agentNames = agents.map((agent) => agent.name);
|
||||||
|
this.setAgentVis(agentNames, true, mode, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAgentEnd({agents, mode}) {
|
handleAgentEnd({agents, mode}) {
|
||||||
this.setAgentVis(agents, false, mode, true);
|
const agentNames = agents.map((agent) => agent.name);
|
||||||
|
this.setAgentVis(agentNames, false, mode, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlockBegin({mode, label}) {
|
handleBlockBegin({mode, label}) {
|
||||||
|
@ -204,11 +207,16 @@ define(['core/ArrayUtilities'], (array) => {
|
||||||
|
|
||||||
handleUnknownStage(stage) {
|
handleUnknownStage(stage) {
|
||||||
if(stage.agents) {
|
if(stage.agents) {
|
||||||
this.setAgentVis(stage.agents, true, 'box');
|
const agentNames = stage.agents.map((agent) => agent.name);
|
||||||
array.mergeSets(this.currentNest.agents, stage.agents);
|
this.setAgentVis(agentNames, true, 'box');
|
||||||
array.mergeSets(this.agents, stage.agents);
|
array.mergeSets(this.currentNest.agents, agentNames);
|
||||||
}
|
array.mergeSets(this.agents, agentNames);
|
||||||
|
this.currentSection.stages.push(Object.assign({}, stage, {
|
||||||
|
agents: agentNames,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
this.currentSection.stages.push(stage);
|
this.currentSection.stages.push(stage);
|
||||||
|
}
|
||||||
this.currentNest.hasContent = true;
|
this.currentNest.hasContent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,9 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('returns aggregated agents', () => {
|
it('returns aggregated agents', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: '<-', agents: ['C', 'D']},
|
{type: '<-', agents: [{name: 'C'}, {name: 'D'}]},
|
||||||
{type: AGENT_BEGIN, agents: ['E'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [{name: 'E'}], mode: 'box'},
|
||||||
]});
|
]});
|
||||||
expect(sequence.agents).toEqual(
|
expect(sequence.agents).toEqual(
|
||||||
['[', 'A', 'B', 'C', 'D', 'E', ']']
|
['[', 'A', 'B', 'C', 'D', 'E', ']']
|
||||||
|
@ -64,23 +64,23 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('always puts the implicit right agent on the right', () => {
|
it('always puts the implicit right agent on the right', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: [']', 'B']},
|
{type: '->', agents: [{name: ']'}, {name: 'B'}]},
|
||||||
]});
|
]});
|
||||||
expect(sequence.agents).toEqual(['[', 'B', ']']);
|
expect(sequence.agents).toEqual(['[', 'B', ']']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('accounts for define calls when ordering agents', () => {
|
it('accounts for define calls when ordering agents', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: AGENT_DEFINE, agents: ['B']},
|
{type: AGENT_DEFINE, agents: [{name: 'B'}]},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
]});
|
]});
|
||||||
expect(sequence.agents).toEqual(['[', 'B', 'A', ']']);
|
expect(sequence.agents).toEqual(['[', 'B', 'A', ']']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates implicit begin stages for agents when used', () => {
|
it('creates implicit begin stages for agents when used', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: '->', agents: ['B', 'C']},
|
{type: '->', agents: [{name: 'B'}, {name: 'C'}]},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
||||||
|
@ -97,7 +97,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
terminators: 'foo',
|
terminators: 'foo',
|
||||||
},
|
},
|
||||||
stages: [
|
stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
|
@ -109,9 +109,13 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('does not create duplicate begin stages', () => {
|
it('does not create duplicate begin stages', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'B', 'C'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{name: 'A'},
|
||||||
{type: '->', agents: ['B', 'C']},
|
{name: 'B'},
|
||||||
|
{name: 'C'},
|
||||||
|
], mode: 'box'},
|
||||||
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
|
{type: '->', agents: [{name: 'B'}, {name: 'C'}]},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'B', 'C'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: ['A', 'B', 'C'], mode: 'box'},
|
||||||
|
@ -123,10 +127,13 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('redisplays agents if they have been hidden', () => {
|
it('redisplays agents if they have been hidden', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{name: 'A'},
|
||||||
{type: AGENT_END, agents: ['B'], mode: 'cross'},
|
{name: 'B'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
], mode: 'box'},
|
||||||
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
|
{type: AGENT_END, agents: [{name: 'B'}], mode: 'cross'},
|
||||||
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
||||||
|
@ -140,10 +147,10 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('collapses adjacent begin statements', () => {
|
it('collapses adjacent begin statements', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: AGENT_BEGIN, agents: ['D'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [{name: 'D'}], mode: 'box'},
|
||||||
{type: '->', agents: ['B', 'C']},
|
{type: '->', agents: [{name: 'B'}, {name: 'C'}]},
|
||||||
{type: '->', agents: ['C', 'D']},
|
{type: '->', agents: [{name: 'C'}, {name: 'D'}]},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
||||||
|
@ -157,9 +164,16 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('removes superfluous begin statements', () => {
|
it('removes superfluous begin statements', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'C', 'D'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [
|
||||||
{type: AGENT_BEGIN, agents: ['C', 'E'], mode: 'box'},
|
{name: 'A'},
|
||||||
|
{name: 'C'},
|
||||||
|
{name: 'D'},
|
||||||
|
], mode: 'box'},
|
||||||
|
{type: AGENT_BEGIN, agents: [
|
||||||
|
{name: 'C'},
|
||||||
|
{name: 'E'},
|
||||||
|
], mode: 'box'},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: ['A', 'B'], mode: 'box'},
|
||||||
|
@ -173,11 +187,22 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('removes superfluous end statements', () => {
|
it('removes superfluous end statements', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: AGENT_DEFINE, agents: ['E']},
|
{type: AGENT_DEFINE, agents: [{name: 'E'}]},
|
||||||
{type: AGENT_BEGIN, agents: ['C', 'D'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{name: 'C'},
|
||||||
{type: AGENT_END, agents: ['A', 'B', 'C'], mode: 'cross'},
|
{name: 'D'},
|
||||||
{type: AGENT_END, agents: ['A', 'D', 'E'], mode: 'cross'},
|
], mode: 'box'},
|
||||||
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
|
{type: AGENT_END, agents: [
|
||||||
|
{name: 'A'},
|
||||||
|
{name: 'B'},
|
||||||
|
{name: 'C'},
|
||||||
|
], mode: 'cross'},
|
||||||
|
{type: AGENT_END, agents: [
|
||||||
|
{name: 'A'},
|
||||||
|
{name: 'D'},
|
||||||
|
{name: 'E'},
|
||||||
|
], mode: 'cross'},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: AGENT_BEGIN, agents: ['C', 'D', 'A', 'B'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: ['C', 'D', 'A', 'B'], mode: 'box'},
|
||||||
|
@ -188,9 +213,19 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('does not merge different modes of end', () => {
|
it('does not merge different modes of end', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: AGENT_BEGIN, agents: ['C', 'D'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{name: 'C'},
|
||||||
{type: AGENT_END, agents: ['A', 'B', 'C'], mode: 'cross'},
|
{name: 'D'},
|
||||||
|
], mode: 'box'},
|
||||||
|
{type: '->', agents: [
|
||||||
|
{name: 'A'},
|
||||||
|
{name: 'B'},
|
||||||
|
]},
|
||||||
|
{type: AGENT_END, agents: [
|
||||||
|
{name: 'A'},
|
||||||
|
{name: 'B'},
|
||||||
|
{name: 'C'},
|
||||||
|
], mode: 'cross'},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: AGENT_BEGIN, agents: ['C', 'D', 'A', 'B'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: ['C', 'D', 'A', 'B'], mode: 'box'},
|
||||||
|
@ -203,7 +238,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('creates virtual agents for block statements', () => {
|
it('creates virtual agents for block statements', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
@ -214,14 +249,14 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('positions virtual block agents near involved agents', () => {
|
it('positions virtual block agents near involved agents', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['C', 'D']},
|
{type: '->', agents: [{name: 'C'}, {name: 'D'}]},
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['E', 'F']},
|
{type: '->', agents: [{name: 'E'}, {name: 'F'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
{type: '->', agents: ['G', 'H']},
|
{type: '->', agents: [{name: 'G'}, {name: 'H'}]},
|
||||||
]});
|
]});
|
||||||
|
|
||||||
expect(sequence.agents).toEqual([
|
expect(sequence.agents).toEqual([
|
||||||
|
@ -245,7 +280,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('records virtual block agent names in blocks', () => {
|
it('records virtual block agent names in blocks', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
@ -258,9 +293,9 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('records all sections within blocks', () => {
|
it('records all sections within blocks', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||||
{type: '->', agents: ['A', 'C']},
|
{type: '->', agents: [{name: 'A'}, {name: 'C'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
@ -280,10 +315,10 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('records all involved agents in nested blocks', () => {
|
it('records all involved agents in nested blocks', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'def'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'def'},
|
||||||
{type: '->', agents: ['A', 'C']},
|
{type: '->', agents: [{name: 'A'}, {name: 'C'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
@ -312,10 +347,10 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('preserves block boundaries when agents exist outside', () => {
|
it('preserves block boundaries when agents exist outside', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'def'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'def'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
@ -344,7 +379,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('allows empty block parts after split', () => {
|
it('allows empty block parts after split', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
@ -363,7 +398,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
@ -392,7 +427,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('removes blocks containing only define statements / markers', () => {
|
it('removes blocks containing only define statements / markers', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: AGENT_DEFINE, agents: ['A']},
|
{type: AGENT_DEFINE, agents: [{name: 'A'}]},
|
||||||
{type: 'mark', name: 'foo'},
|
{type: 'mark', name: 'foo'},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]});
|
]});
|
||||||
|
@ -415,7 +450,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('removes entirely empty nested blocks', () => {
|
it('removes entirely empty nested blocks', () => {
|
||||||
const sequence = generator.generate({stages: [
|
const sequence = generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
|
@ -435,13 +470,13 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
it('rejects unterminated blocks', () => {
|
it('rejects unterminated blocks', () => {
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
|
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'def'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'def'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
});
|
});
|
||||||
|
@ -453,7 +488,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
|
@ -466,7 +501,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'if', label: 'abc'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
|
@ -476,7 +511,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: BLOCK_BEGIN, mode: 'repeat', label: 'abc'},
|
{type: BLOCK_BEGIN, mode: 'repeat', label: 'abc'},
|
||||||
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
{type: BLOCK_SPLIT, mode: 'else', label: 'xyz'},
|
||||||
{type: '->', agents: ['A', 'B']},
|
{type: '->', agents: [{name: 'A'}, {name: 'B'}]},
|
||||||
{type: BLOCK_END},
|
{type: BLOCK_END},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
});
|
});
|
||||||
|
@ -486,7 +521,7 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
{type: 'note right', agents: [], foo: 'bar'},
|
{type: 'note right', agents: [], foo: 'bar'},
|
||||||
{type: 'note left', agents: [], foo: 'bar'},
|
{type: 'note left', agents: [], foo: 'bar'},
|
||||||
{type: 'note over', agents: [], foo: 'bar'},
|
{type: 'note over', agents: [], foo: 'bar'},
|
||||||
{type: 'note right', agents: ['[']},
|
{type: 'note right', agents: [{name: '['}]},
|
||||||
]});
|
]});
|
||||||
expect(sequence.stages).toEqual([
|
expect(sequence.stages).toEqual([
|
||||||
{type: 'note right', agents: [']'], foo: 'bar'},
|
{type: 'note right', agents: [']'], foo: 'bar'},
|
||||||
|
@ -498,19 +533,19 @@ defineDescribe('Sequence Generator', ['./Generator'], (Generator) => {
|
||||||
|
|
||||||
it('rejects attempts to change implicit agents', () => {
|
it('rejects attempts to change implicit agents', () => {
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: AGENT_BEGIN, agents: ['['], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [{name: '['}], mode: 'box'},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
|
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: AGENT_BEGIN, agents: [']'], mode: 'box'},
|
{type: AGENT_BEGIN, agents: [{name: ']'}], mode: 'box'},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
|
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: AGENT_END, agents: ['['], mode: 'cross'},
|
{type: AGENT_END, agents: [{name: '['}], mode: 'cross'},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
|
|
||||||
expect(() => generator.generate({stages: [
|
expect(() => generator.generate({stages: [
|
||||||
{type: AGENT_END, agents: [']'], mode: 'cross'},
|
{type: AGENT_END, agents: [{name: ']'}], mode: 'cross'},
|
||||||
]})).toThrow();
|
]})).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,13 +24,26 @@ define([
|
||||||
|
|
||||||
const TOKENS = [
|
const TOKENS = [
|
||||||
{start: /#/y, end: /(?=\n)|$/y, omit: true},
|
{start: /#/y, end: /(?=\n)|$/y, omit: true},
|
||||||
{start: /"/y, end: /"/y, escape: /\\(.)/y, escapeWith: unescape},
|
{
|
||||||
{start: /'/y, end: /'/y, escape: /\\(.)/y, escapeWith: unescape},
|
start: /"/y,
|
||||||
|
end: /"/y,
|
||||||
|
escape: /\\(.)/y,
|
||||||
|
escapeWith: unescape,
|
||||||
|
baseToken: {q: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: /'/y,
|
||||||
|
end: /'/y,
|
||||||
|
escape: /\\(.)/y,
|
||||||
|
escapeWith:
|
||||||
|
unescape,
|
||||||
|
baseToken: {q: true},
|
||||||
|
},
|
||||||
{start: /(?=[^ \t\r\n:+\-<>,])/y, end: /(?=[ \t\r\n:+\-<>,])|$/y},
|
{start: /(?=[^ \t\r\n:+\-<>,])/y, end: /(?=[ \t\r\n:+\-<>,])|$/y},
|
||||||
{start: /(?=[+\-<>])/y, end: /(?=[^+\-<>])|$/y},
|
{start: /(?=[+\-<>])/y, end: /(?=[^+\-<>])|$/y},
|
||||||
{start: /,/y, prefix: ','},
|
{start: /,/y, baseToken: {v: ','}},
|
||||||
{start: /:/y, prefix: ':'},
|
{start: /:/y, baseToken: {v: ':'}},
|
||||||
{start: /\n/y, prefix: '\n'},
|
{start: /\n/y, baseToken: {v: '\n'}},
|
||||||
];
|
];
|
||||||
|
|
||||||
const BLOCK_TYPES = {
|
const BLOCK_TYPES = {
|
||||||
|
@ -95,7 +108,8 @@ define([
|
||||||
return {
|
return {
|
||||||
newBlock: block,
|
newBlock: block,
|
||||||
end: !block.end,
|
end: !block.end,
|
||||||
append: (block.prefix || ''),
|
appendSpace: '',
|
||||||
|
appendValue: '',
|
||||||
skip: match[0].length,
|
skip: match[0].length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -103,7 +117,8 @@ define([
|
||||||
return {
|
return {
|
||||||
newBlock: null,
|
newBlock: null,
|
||||||
end: false,
|
end: false,
|
||||||
append: '',
|
appendSpace: src[i],
|
||||||
|
appendValue: '',
|
||||||
skip: 1,
|
skip: 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -115,7 +130,8 @@ define([
|
||||||
return {
|
return {
|
||||||
newBlock: null,
|
newBlock: null,
|
||||||
end: false,
|
end: false,
|
||||||
append: block.escapeWith(match),
|
appendSpace: '',
|
||||||
|
appendValue: block.escapeWith(match),
|
||||||
skip: match[0].length,
|
skip: match[0].length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -125,14 +141,16 @@ define([
|
||||||
return {
|
return {
|
||||||
newBlock: null,
|
newBlock: null,
|
||||||
end: true,
|
end: true,
|
||||||
append: '',
|
appendSpace: '',
|
||||||
|
appendValue: '',
|
||||||
skip: match[0].length,
|
skip: match[0].length,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
newBlock: null,
|
newBlock: null,
|
||||||
end: false,
|
end: false,
|
||||||
append: src[i],
|
appendSpace: '',
|
||||||
|
appendValue: src[i],
|
||||||
skip: 1,
|
skip: 1,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -145,10 +163,28 @@ define([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function joinLabel(line, begin, end) {
|
||||||
|
if(end <= begin) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
let result = line[begin].v;
|
||||||
|
for(let i = begin + 1; i < end; ++ i) {
|
||||||
|
result += line[i].s + line[i].v;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugLine(line) {
|
||||||
|
return joinLabel(line, 0, line.length);
|
||||||
|
}
|
||||||
|
|
||||||
function skipOver(line, start, skip, error = null) {
|
function skipOver(line, start, skip, error = null) {
|
||||||
if(skip.some((token, i) => (line[start + i] !== token))) {
|
if(skip.some((token, i) => (
|
||||||
|
!line[start + i] ||
|
||||||
|
line[start + i].v !== token
|
||||||
|
))) {
|
||||||
if(error) {
|
if(error) {
|
||||||
throw new Error(error + ': ' + line.join(' '));
|
throw new Error(error + ': ' + debugLine(line));
|
||||||
} else {
|
} else {
|
||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
@ -156,53 +192,83 @@ define([
|
||||||
return start + skip.length;
|
return start + skip.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCommaList(tokens) {
|
function findToken(line, token, start = 0) {
|
||||||
|
for(let i = start; i < line.length; ++ i) {
|
||||||
|
if(line[i].v === token) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseAgentList(line, start, end) {
|
||||||
const list = [];
|
const list = [];
|
||||||
let current = '';
|
let current = '';
|
||||||
tokens.forEach((token) => {
|
let first = true;
|
||||||
if(token === ',') {
|
for(let i = start; i < end; ++ i) {
|
||||||
|
if(line[i].v === ',') {
|
||||||
if(current) {
|
if(current) {
|
||||||
list.push(current);
|
list.push({name: current});
|
||||||
current = '';
|
current = '';
|
||||||
|
first = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
current += (current ? ' ' : '') + token;
|
if(!first) {
|
||||||
|
current += line[i].s;
|
||||||
|
} else {
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
current += line[i].v;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if(current) {
|
if(current) {
|
||||||
list.push(current);
|
list.push({name: current});
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readAgent(line, begin, end) {
|
||||||
|
if(line[begin].v === '+' || line[begin].v === '-') {
|
||||||
|
return {
|
||||||
|
opt: line[begin].v,
|
||||||
|
name: joinLabel(line, begin + 1, end),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
opt: '',
|
||||||
|
name: joinLabel(line, begin, end),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const PARSERS = [
|
const PARSERS = [
|
||||||
(line, meta) => { // title
|
(line, meta) => { // title
|
||||||
if(line[0] !== 'title') {
|
if(line[0].v !== 'title') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.title = line.slice(1).join(' ');
|
meta.title = joinLabel(line, 1, line.length);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
(line, meta) => { // terminators
|
(line, meta) => { // terminators
|
||||||
if(line[0] !== 'terminators') {
|
if(line[0].v !== 'terminators') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(TERMINATOR_TYPES.indexOf(line[1]) === -1) {
|
if(TERMINATOR_TYPES.indexOf(line[1].v) === -1) {
|
||||||
throw new Error('Unknown termination: ' + line.join(' '));
|
throw new Error('Unknown termination: ' + debugLine(line));
|
||||||
}
|
}
|
||||||
meta.terminators = line[1];
|
meta.terminators = line[1].v;
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
(line) => { // block
|
(line) => { // block
|
||||||
if(line[0] === 'end' && line.length === 1) {
|
if(line[0].v === 'end' && line.length === 1) {
|
||||||
return {type: 'block end'};
|
return {type: 'block end'};
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = BLOCK_TYPES[line[0]];
|
const type = BLOCK_TYPES[line[0].v];
|
||||||
if(!type) {
|
if(!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -214,36 +280,33 @@ define([
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
type: type.type,
|
||||||
mode: type.mode,
|
mode: type.mode,
|
||||||
label: line.slice(skip).join(' '),
|
label: joinLabel(line, skip, line.length),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
(line) => { // agent
|
(line) => { // agent
|
||||||
const type = AGENT_MANIPULATION_TYPES[line[0]];
|
const type = AGENT_MANIPULATION_TYPES[line[0].v];
|
||||||
if(!type) {
|
if(!type || line.length <= 1) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if(line.length <= 1) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
agents: parseCommaList(line.slice(1)),
|
agents: parseAgentList(line, 1, line.length),
|
||||||
}, type);
|
}, type);
|
||||||
},
|
},
|
||||||
|
|
||||||
(line) => { // async
|
(line) => { // async
|
||||||
if(line[0] !== 'simultaneously') {
|
if(line[0].v !== 'simultaneously') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if(array.last(line) !== ':') {
|
if(array.last(line).v !== ':') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let target = '';
|
let target = '';
|
||||||
if(line.length > 2) {
|
if(line.length > 2) {
|
||||||
if(line[1] !== 'with') {
|
if(line[1].v !== 'with') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
target = line.slice(2, line.length - 1).join(' ');
|
target = joinLabel(line, 2, line.length - 1);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'async',
|
type: 'async',
|
||||||
|
@ -252,41 +315,44 @@ define([
|
||||||
},
|
},
|
||||||
|
|
||||||
(line) => { // note
|
(line) => { // note
|
||||||
const mode = NOTE_TYPES[line[0]];
|
const mode = NOTE_TYPES[line[0].v];
|
||||||
const labelSplit = line.indexOf(':');
|
const labelSplit = findToken(line, ':');
|
||||||
if(!mode || labelSplit === -1) {
|
if(!mode || labelSplit === -1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const type = mode.types[line[1]];
|
const type = mode.types[line[1].v];
|
||||||
if(!type) {
|
if(!type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let skip = 2;
|
let skip = 2;
|
||||||
skip = skipOver(line, skip, type.skip);
|
skip = skipOver(line, skip, type.skip);
|
||||||
const agents = parseCommaList(line.slice(skip, labelSplit));
|
const agents = parseAgentList(line, skip, labelSplit);
|
||||||
if(
|
if(
|
||||||
agents.length < type.min ||
|
agents.length < type.min ||
|
||||||
(type.max !== null && agents.length > type.max)
|
(type.max !== null && agents.length > type.max)
|
||||||
) {
|
) {
|
||||||
throw new Error('Invalid ' + line[0] + ': ' + line.join(' '));
|
throw new Error(
|
||||||
|
'Invalid ' + mode.mode +
|
||||||
|
': ' + debugLine(line)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: type.type,
|
type: type.type,
|
||||||
agents,
|
agents,
|
||||||
mode: mode.mode,
|
mode: mode.mode,
|
||||||
label: line.slice(labelSplit + 1).join(' '),
|
label: joinLabel(line, labelSplit + 1, line.length),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
(line) => { // connection
|
(line) => { // connection
|
||||||
let labelSplit = line.indexOf(':');
|
let labelSplit = findToken(line, ':');
|
||||||
if(labelSplit === -1) {
|
if(labelSplit === -1) {
|
||||||
labelSplit = line.length;
|
labelSplit = line.length;
|
||||||
}
|
}
|
||||||
let typeSplit = -1;
|
let typeSplit = -1;
|
||||||
let options = null;
|
let options = null;
|
||||||
for(let j = 0; j < line.length; ++ j) {
|
for(let j = 0; j < line.length; ++ j) {
|
||||||
const opts = CONNECTION_TYPES[line[j]];
|
const opts = CONNECTION_TYPES[line[j].v];
|
||||||
if(opts) {
|
if(opts) {
|
||||||
typeSplit = j;
|
typeSplit = j;
|
||||||
options = opts;
|
options = opts;
|
||||||
|
@ -299,20 +365,20 @@ define([
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
type: 'connection',
|
type: 'connection',
|
||||||
agents: [
|
agents: [
|
||||||
line.slice(0, typeSplit).join(' '),
|
readAgent(line, 0, typeSplit),
|
||||||
line.slice(typeSplit + 1, labelSplit).join(' '),
|
readAgent(line, typeSplit + 1, labelSplit),
|
||||||
],
|
],
|
||||||
label: line.slice(labelSplit + 1).join(' '),
|
label: joinLabel(line, labelSplit + 1, line.length),
|
||||||
}, options);
|
}, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
(line) => { // marker
|
(line) => { // marker
|
||||||
if(line.length < 2 || array.last(line) !== ':') {
|
if(line.length < 2 || array.last(line).v !== ':') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: 'mark',
|
type: 'mark',
|
||||||
name: line.slice(0, line.length - 1).join(' '),
|
name: joinLabel(line, 0, line.length - 1),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -326,7 +392,7 @@ define([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!stage) {
|
if(!stage) {
|
||||||
throw new Error('Unrecognised command: ' + line.join(' '));
|
throw new Error('Unrecognised command: ' + debugLine(line));
|
||||||
}
|
}
|
||||||
if(typeof stage === 'object') {
|
if(typeof stage === 'object') {
|
||||||
stages.push(stage);
|
stages.push(stage);
|
||||||
|
@ -337,19 +403,21 @@ define([
|
||||||
tokenise(src) {
|
tokenise(src) {
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
let block = null;
|
let block = null;
|
||||||
let current = '';
|
let current = {s: '', v: '', q: false};
|
||||||
for(let i = 0; i <= src.length;) {
|
for(let i = 0; i <= src.length;) {
|
||||||
const {newBlock, end, append, skip} = tokAdvance(src, i, block);
|
const advance = tokAdvance(src, i, block);
|
||||||
if(newBlock) {
|
if(advance.newBlock) {
|
||||||
block = newBlock;
|
block = advance.newBlock;
|
||||||
current = '';
|
Object.assign(current, block.baseToken);
|
||||||
}
|
}
|
||||||
current += append;
|
current.s += advance.appendSpace;
|
||||||
i += skip;
|
current.v += advance.appendValue;
|
||||||
if(end) {
|
i += advance.skip;
|
||||||
|
if(advance.end) {
|
||||||
if(!block.omit) {
|
if(!block.omit) {
|
||||||
tokens.push(current);
|
tokens.push(current);
|
||||||
}
|
}
|
||||||
|
current = {s: '', v: '', q: false};
|
||||||
block = null;
|
block = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,7 +439,7 @@ define([
|
||||||
const lines = [];
|
const lines = [];
|
||||||
let line = [];
|
let line = [];
|
||||||
tokens.forEach((token) => {
|
tokens.forEach((token) => {
|
||||||
if(token === '\n') {
|
if(token.v === '\n' && !token.q) {
|
||||||
if(line.length > 0) {
|
if(line.length > 0) {
|
||||||
lines.push(line);
|
lines.push(line);
|
||||||
line = [];
|
line = [];
|
||||||
|
|
|
@ -7,55 +7,103 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
it('converts the source into atomic tokens', () => {
|
it('converts the source into atomic tokens', () => {
|
||||||
const input = 'foo bar -> baz';
|
const input = 'foo bar -> baz';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', 'bar', '->', 'baz']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: ' ', v: 'bar', q: false},
|
||||||
|
{s: ' ', v: '->', q: false},
|
||||||
|
{s: ' ', v: 'baz', q: false},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('splits tokens at flexible boundaries', () => {
|
it('splits tokens at flexible boundaries', () => {
|
||||||
const input = 'foo bar->baz';
|
const input = 'foo bar->baz';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', 'bar', '->', 'baz']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: ' ', v: 'bar', q: false},
|
||||||
|
{s: '', v: '->', q: false},
|
||||||
|
{s: '', v: 'baz', q: false},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses newlines as tokens', () => {
|
it('parses newlines as tokens', () => {
|
||||||
const input = 'foo bar\nbaz';
|
const input = 'foo bar\nbaz';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', 'bar', '\n', 'baz']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: ' ', v: 'bar', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
{s: '', v: 'baz', q: false},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses quoted newlines as quoted tokens', () => {
|
||||||
|
const input = 'foo "\n" baz';
|
||||||
|
const tokens = parser.tokenise(input);
|
||||||
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: ' ', v: '\n', q: true},
|
||||||
|
{s: ' ', v: 'baz', q: false},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes leading and trailing whitespace', () => {
|
it('removes leading and trailing whitespace', () => {
|
||||||
const input = ' foo \t bar\t\n baz';
|
const input = ' foo \t bar\t\n baz';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', 'bar', '\n', 'baz']);
|
expect(tokens).toEqual([
|
||||||
|
{s: ' ', v: 'foo', q: false},
|
||||||
|
{s: ' \t ', v: 'bar', q: false},
|
||||||
|
{s: '\t', v: '\n', q: false},
|
||||||
|
{s: ' ', v: 'baz', q: false},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses quoted strings as single tokens', () => {
|
it('parses quoted strings as single tokens', () => {
|
||||||
const input = 'foo "zig zag" \'abc def\'';
|
const input = 'foo "zig zag" \'abc def\'';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', 'zig zag', 'abc def']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: ' ', v: 'zig zag', q: true},
|
||||||
|
{s: ' ', v: 'abc def', q: true},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores comments', () => {
|
it('ignores comments', () => {
|
||||||
const input = 'foo # bar baz\nzig';
|
const input = 'foo # bar baz\nzig';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', '\n', 'zig']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
{s: '', v: 'zig', q: false},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores quotes within comments', () => {
|
it('ignores quotes within comments', () => {
|
||||||
const input = 'foo # bar "\'baz\nzig';
|
const input = 'foo # bar "\'baz\nzig';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', '\n', 'zig']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
{s: '', v: 'zig', q: false},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('interprets special characters within quoted strings', () => {
|
it('interprets special characters within quoted strings', () => {
|
||||||
const input = 'foo "zig\\" zag\\n"';
|
const input = 'foo "zig\\" zag\\n"';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', 'zig" zag\n']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: ' ', v: 'zig" zag\n', q: true},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('maintains whitespace and newlines within quoted strings', () => {
|
it('maintains whitespace and newlines within quoted strings', () => {
|
||||||
const input = 'foo " zig\n zag "';
|
const input = 'foo " zig\n zag "';
|
||||||
const tokens = parser.tokenise(input);
|
const tokens = parser.tokenise(input);
|
||||||
expect(tokens).toEqual(['foo', ' zig\n zag ']);
|
expect(tokens).toEqual([
|
||||||
|
{s: '', v: 'foo', q: false},
|
||||||
|
{s: ' ', v: ' zig\n zag ', q: true},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects unterminated quoted values', () => {
|
it('rejects unterminated quoted values', () => {
|
||||||
|
@ -65,33 +113,70 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
|
|
||||||
describe('.splitLines', () => {
|
describe('.splitLines', () => {
|
||||||
it('combines tokens', () => {
|
it('combines tokens', () => {
|
||||||
const lines = parser.splitLines(['abc', 'd']);
|
const lines = parser.splitLines([
|
||||||
|
{s: '', v: 'abc', q: false},
|
||||||
|
{s: '', v: 'd', q: false},
|
||||||
|
]);
|
||||||
expect(lines).toEqual([
|
expect(lines).toEqual([
|
||||||
['abc', 'd'],
|
[{s: '', v: 'abc', q: false}, {s: '', v: 'd', q: false}],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('splits at newlines', () => {
|
it('splits at newlines', () => {
|
||||||
const lines = parser.splitLines(['abc', 'd', '\n', 'e']);
|
const lines = parser.splitLines([
|
||||||
|
{s: '', v: 'abc', q: false},
|
||||||
|
{s: '', v: 'd', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
{s: '', v: 'e', q: false},
|
||||||
|
]);
|
||||||
expect(lines).toEqual([
|
expect(lines).toEqual([
|
||||||
['abc', 'd'],
|
[{s: '', v: 'abc', q: false}, {s: '', v: 'd', q: false}],
|
||||||
['e'],
|
[{s: '', v: 'e', q: false}],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores multiple newlines', () => {
|
it('ignores multiple newlines', () => {
|
||||||
const lines = parser.splitLines(['abc', 'd', '\n', '\n', 'e']);
|
const lines = parser.splitLines([
|
||||||
|
{s: '', v: 'abc', q: false},
|
||||||
|
{s: '', v: 'd', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
{s: '', v: 'e', q: false},
|
||||||
|
]);
|
||||||
expect(lines).toEqual([
|
expect(lines).toEqual([
|
||||||
['abc', 'd'],
|
[{s: '', v: 'abc', q: false}, {s: '', v: 'd', q: false}],
|
||||||
['e'],
|
[{s: '', v: 'e', q: false}],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ignores trailing newlines', () => {
|
it('ignores trailing newlines', () => {
|
||||||
const lines = parser.splitLines(['abc', 'd', '\n', 'e', '\n']);
|
const lines = parser.splitLines([
|
||||||
|
{s: '', v: 'abc', q: false},
|
||||||
|
{s: '', v: 'd', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
{s: '', v: 'e', q: false},
|
||||||
|
{s: '', v: '\n', q: false},
|
||||||
|
]);
|
||||||
expect(lines).toEqual([
|
expect(lines).toEqual([
|
||||||
['abc', 'd'],
|
[{s: '', v: 'abc', q: false}, {s: '', v: 'd', q: false}],
|
||||||
['e'],
|
[{s: '', v: 'e', q: false}],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles quoted newlines as regular tokens', () => {
|
||||||
|
const lines = parser.splitLines([
|
||||||
|
{s: '', v: 'abc', q: false},
|
||||||
|
{s: '', v: 'd', q: false},
|
||||||
|
{s: '', v: '\n', q: true},
|
||||||
|
{s: '', v: 'e', q: false},
|
||||||
|
]);
|
||||||
|
expect(lines).toEqual([
|
||||||
|
[
|
||||||
|
{s: '', v: 'abc', q: false},
|
||||||
|
{s: '', v: 'd', q: false},
|
||||||
|
{s: '', v: '\n', q: true},
|
||||||
|
{s: '', v: 'e', q: false},
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -101,13 +186,13 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function connectionStage(agents, label = '') {
|
function connectionStage(agentNames, label = '') {
|
||||||
return {
|
return {
|
||||||
type: 'connection',
|
type: 'connection',
|
||||||
line: 'solid',
|
line: 'solid',
|
||||||
left: false,
|
left: false,
|
||||||
right: true,
|
right: true,
|
||||||
agents,
|
agents: agentNames.map((agent) => ({opt: '', name: agent})),
|
||||||
label,
|
label,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -153,6 +238,13 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('respects spacing within agent names', () => {
|
||||||
|
const parsed = parser.parse('A+B -> C D');
|
||||||
|
expect(parsed.stages).toEqual([
|
||||||
|
connectionStage(['A+B', 'C D']),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('parses optional labels', () => {
|
it('parses optional labels', () => {
|
||||||
const parsed = parser.parse('A B -> C D: foo bar');
|
const parsed = parser.parse('A B -> C D: foo bar');
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
|
@ -191,7 +283,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'solid',
|
line: 'solid',
|
||||||
left: false,
|
left: false,
|
||||||
right: true,
|
right: true,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: '',
|
label: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -199,7 +291,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'solid',
|
line: 'solid',
|
||||||
left: true,
|
left: true,
|
||||||
right: false,
|
right: false,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: '',
|
label: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -207,7 +299,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'solid',
|
line: 'solid',
|
||||||
left: true,
|
left: true,
|
||||||
right: true,
|
right: true,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: '',
|
label: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -215,7 +307,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'dash',
|
line: 'dash',
|
||||||
left: false,
|
left: false,
|
||||||
right: true,
|
right: true,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: '',
|
label: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -223,7 +315,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'dash',
|
line: 'dash',
|
||||||
left: true,
|
left: true,
|
||||||
right: false,
|
right: false,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: '',
|
label: '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -231,7 +323,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'dash',
|
line: 'dash',
|
||||||
left: true,
|
left: true,
|
||||||
right: true,
|
right: true,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: '',
|
label: '',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -248,7 +340,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'solid',
|
line: 'solid',
|
||||||
left: true,
|
left: true,
|
||||||
right: false,
|
right: false,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: 'B -> A',
|
label: 'B -> A',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -256,7 +348,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
line: 'solid',
|
line: 'solid',
|
||||||
left: false,
|
left: false,
|
||||||
right: true,
|
right: true,
|
||||||
agents: ['A', 'B'],
|
agents: [{opt: '', name: 'A'}, {opt: '', name: 'B'}],
|
||||||
label: 'B <- A',
|
label: 'B <- A',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -266,7 +358,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
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',
|
type: 'note over',
|
||||||
agents: ['A'],
|
agents: [{name: 'A'}],
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
label: 'hello there',
|
label: 'hello there',
|
||||||
}]);
|
}]);
|
||||||
|
@ -283,31 +375,31 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{
|
{
|
||||||
type: 'note left',
|
type: 'note left',
|
||||||
agents: ['A'],
|
agents: [{name: 'A'}],
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
label: 'hello there',
|
label: 'hello there',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'note left',
|
type: 'note left',
|
||||||
agents: ['A'],
|
agents: [{name: 'A'}],
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
label: 'hello there',
|
label: 'hello there',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'note right',
|
type: 'note right',
|
||||||
agents: ['A'],
|
agents: [{name: 'A'}],
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
label: 'hello there',
|
label: 'hello there',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'note right',
|
type: 'note right',
|
||||||
agents: ['A'],
|
agents: [{name: 'A'}],
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
label: 'hello there',
|
label: 'hello there',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'note between',
|
type: 'note between',
|
||||||
agents: ['A', 'B'],
|
agents: [{name: 'A'}, {name: 'B'}],
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
label: 'hi',
|
label: 'hi',
|
||||||
},
|
},
|
||||||
|
@ -318,7 +410,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
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',
|
type: 'note over',
|
||||||
agents: ['A B', 'C D'],
|
agents: [{name: 'A B'}, {name: 'C D'}],
|
||||||
mode: 'note',
|
mode: 'note',
|
||||||
label: 'hi',
|
label: 'hi',
|
||||||
}]);
|
}]);
|
||||||
|
@ -332,7 +424,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
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',
|
type: 'note over',
|
||||||
agents: ['A'],
|
agents: [{name: 'A'}],
|
||||||
mode: 'state',
|
mode: 'state',
|
||||||
label: 'doing stuff',
|
label: 'doing stuff',
|
||||||
}]);
|
}]);
|
||||||
|
@ -346,7 +438,7 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
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',
|
type: 'note right',
|
||||||
agents: ['A'],
|
agents: [{name: 'A'}],
|
||||||
mode: 'text',
|
mode: 'text',
|
||||||
label: 'doing stuff',
|
label: 'doing stuff',
|
||||||
}]);
|
}]);
|
||||||
|
@ -359,9 +451,20 @@ defineDescribe('Sequence Parser', ['./Parser'], (Parser) => {
|
||||||
'end A, B\n'
|
'end A, B\n'
|
||||||
);
|
);
|
||||||
expect(parsed.stages).toEqual([
|
expect(parsed.stages).toEqual([
|
||||||
{type: 'agent define', agents: ['A', 'B']},
|
{
|
||||||
{type: 'agent begin', agents: ['A', 'B'], mode: 'box'},
|
type: 'agent define',
|
||||||
{type: 'agent end', agents: ['A', 'B'], mode: 'cross'},
|
agents: [{name: 'A'}, {name: 'B'}],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'agent begin',
|
||||||
|
agents: [{name: 'A'}, {name: 'B'}],
|
||||||
|
mode: 'box',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'agent end',
|
||||||
|
agents: [{name: 'A'}, {name: 'B'}],
|
||||||
|
mode: 'cross',
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,7 @@ define([
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 3,
|
left: 3,
|
||||||
right: 3,
|
right: 3,
|
||||||
bottom: 0,
|
bottom: 1,
|
||||||
},
|
},
|
||||||
maskAttrs: {
|
maskAttrs: {
|
||||||
'fill': '#FFFFFF',
|
'fill': '#FFFFFF',
|
||||||
|
|
|
@ -24,9 +24,6 @@ html, body {
|
||||||
.cm-s-default .cm-string {color: #221111;}
|
.cm-s-default .cm-string {color: #221111;}
|
||||||
.cm-s-default .cm-error {color: #FF0000;}
|
.cm-s-default .cm-error {color: #FF0000;}
|
||||||
|
|
||||||
.cm-s-default .cm-warning {
|
|
||||||
background: #FFFF00;
|
|
||||||
}
|
|
||||||
.cm-s-default .cm-trailingspace {
|
.cm-s-default .cm-trailingspace {
|
||||||
background: rgba(255, 0, 0, 0.5);
|
background: rgba(255, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue