237 lines
5.3 KiB
JavaScript
237 lines
5.3 KiB
JavaScript
define(() => {
|
|
'use strict';
|
|
|
|
function mergeSets(target, b) {
|
|
if(!b) {
|
|
return;
|
|
}
|
|
for(let i = 0; i < b.length; ++ i) {
|
|
if(target.indexOf(b[i]) === -1) {
|
|
target.push(b[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function lastElement(list) {
|
|
return list[list.length - 1];
|
|
}
|
|
|
|
return class Generator {
|
|
constructor() {
|
|
this.agentStates = new Map();
|
|
this.blockCount = 0;
|
|
this.nesting = [];
|
|
|
|
this.stageHandlers = {
|
|
'agent define': this.handleAgentDefine.bind(this),
|
|
'agent begin': this.handleAgentBegin.bind(this),
|
|
'agent end': this.handleAgentEnd.bind(this),
|
|
'block begin': this.handleBlockBegin.bind(this),
|
|
'block split': this.handleBlockSplit.bind(this),
|
|
'block end': this.handleBlockEnd.bind(this),
|
|
};
|
|
this.handleStage = this.handleStage.bind(this);
|
|
}
|
|
|
|
addStage(stage) {
|
|
this.currentSection.stages.push(stage);
|
|
mergeSets(this.currentNest.agents, stage.agents);
|
|
}
|
|
|
|
addColumnBounds(target, agentL, agentR, involvedAgents = []) {
|
|
const oldL = target.indexOf(agentL);
|
|
if(oldL !== -1) {
|
|
target.splice(oldL, 1);
|
|
}
|
|
const oldR = target.indexOf(agentR);
|
|
if(oldR !== -1) {
|
|
target.splice(oldR, 1);
|
|
}
|
|
|
|
let indexL = 0;
|
|
let indexR = target.length;
|
|
if(involvedAgents.length > 0) {
|
|
const found = (involvedAgents
|
|
.map((agent) => target.indexOf(agent))
|
|
.filter((p) => (p !== -1))
|
|
);
|
|
indexL = found.reduce((a, b) => Math.min(a, b), target.length);
|
|
indexR = found.reduce((a, b) => Math.max(a, b), indexL) + 1;
|
|
}
|
|
|
|
target.splice(indexL, 0, agentL);
|
|
target.splice(indexR + 1, 0, agentR);
|
|
}
|
|
|
|
addAgentMod(stageAgents, markVisible, mode) {
|
|
if(stageAgents.length === 0) {
|
|
return;
|
|
}
|
|
stageAgents.forEach((agent) => {
|
|
const state = this.agentStates.get(agent);
|
|
if(state) {
|
|
state.visible = markVisible;
|
|
} else {
|
|
this.agentStates.set(agent, {
|
|
visible: markVisible,
|
|
locked: false,
|
|
});
|
|
}
|
|
});
|
|
const type = (markVisible ? 'agent begin' : 'agent end');
|
|
const existing = lastElement(this.currentSection.stages) || {};
|
|
if(existing.type === type && existing.mode === mode) {
|
|
mergeSets(existing.agents, stageAgents);
|
|
mergeSets(this.currentNest.agents, stageAgents);
|
|
} else {
|
|
this.addStage({
|
|
type,
|
|
agents: stageAgents,
|
|
mode,
|
|
});
|
|
}
|
|
}
|
|
|
|
filterVis(stageAgents, visible, implicit = false) {
|
|
return stageAgents.filter((agent) => {
|
|
const state = this.agentStates.get(agent);
|
|
if(!state) {
|
|
return !visible;
|
|
} else if(!state.locked) {
|
|
return state.visible === visible;
|
|
} else if(!implicit) {
|
|
throw new Error('Cannot modify agent ' + agent);
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
beginNested(mode, label, name) {
|
|
const agents = [];
|
|
const stages = [];
|
|
this.currentSection = {
|
|
mode,
|
|
label,
|
|
stages,
|
|
};
|
|
this.currentNest = {
|
|
type: 'block',
|
|
agents,
|
|
sections: [this.currentSection],
|
|
leftColumn: name + '[',
|
|
rightColumn: name + ']',
|
|
};
|
|
this.agentStates.set(name + '[', {visible: false, locked: true});
|
|
this.agentStates.set(name + ']', {visible: false, locked: true});
|
|
this.nesting.push(this.currentNest);
|
|
|
|
return {agents, stages};
|
|
}
|
|
|
|
handleAgentDefine() {
|
|
}
|
|
|
|
handleAgentBegin({agents, mode}) {
|
|
this.addAgentMod(this.filterVis(agents, false), true, mode);
|
|
}
|
|
|
|
handleAgentEnd({agents, mode}) {
|
|
this.addAgentMod(this.filterVis(agents, true), false, mode);
|
|
}
|
|
|
|
handleBlockBegin({mode, label}) {
|
|
const name = '__BLOCK' + this.blockCount;
|
|
this.beginNested(mode, label, name);
|
|
++ this.blockCount;
|
|
}
|
|
|
|
handleBlockSplit({mode, label}) {
|
|
if(this.currentNest.sections[0].mode !== 'if') {
|
|
throw new Error('Invalid block nesting');
|
|
}
|
|
this.currentSection = {
|
|
mode,
|
|
label,
|
|
stages: [],
|
|
};
|
|
this.currentNest.sections.push(this.currentSection);
|
|
}
|
|
|
|
handleBlockEnd() {
|
|
if(this.nesting.length <= 1) {
|
|
throw new Error('Invalid block nesting');
|
|
}
|
|
const subNest = this.nesting.pop();
|
|
this.currentNest = lastElement(this.nesting);
|
|
this.currentSection = lastElement(this.currentNest.sections);
|
|
if(subNest.agents.length > 0) {
|
|
this.addStage(subNest);
|
|
this.addColumnBounds(
|
|
this.currentNest.agents,
|
|
subNest.leftColumn,
|
|
subNest.rightColumn,
|
|
subNest.agents
|
|
);
|
|
this.addColumnBounds(
|
|
subNest.agents,
|
|
subNest.leftColumn,
|
|
subNest.rightColumn
|
|
);
|
|
}
|
|
}
|
|
|
|
handleUnknownStage(stage) {
|
|
this.addAgentMod(
|
|
this.filterVis(stage.agents, false, true),
|
|
true,
|
|
'box'
|
|
);
|
|
this.addStage(stage);
|
|
}
|
|
|
|
handleStage(stage) {
|
|
const handler = this.stageHandlers[stage.type];
|
|
if(handler) {
|
|
handler(stage);
|
|
} else {
|
|
this.handleUnknownStage(stage);
|
|
}
|
|
}
|
|
|
|
generate({stages, meta = {}}) {
|
|
this.agentStates.clear();
|
|
this.blockCount = 0;
|
|
this.nesting.length = 0;
|
|
const globals = this.beginNested('global', '', '');
|
|
|
|
stages.forEach(this.handleStage);
|
|
|
|
if(this.nesting.length !== 1) {
|
|
throw new Error('Invalid block nesting');
|
|
}
|
|
|
|
this.addAgentMod(
|
|
this.filterVis(globals.agents, true, true),
|
|
false,
|
|
meta.terminators || 'none'
|
|
);
|
|
|
|
this.addColumnBounds(
|
|
globals.agents,
|
|
this.currentNest.leftColumn,
|
|
this.currentNest.rightColumn
|
|
);
|
|
|
|
return {
|
|
meta: {
|
|
title: meta.title,
|
|
},
|
|
agents: globals.agents,
|
|
stages: globals.stages,
|
|
};
|
|
}
|
|
};
|
|
});
|
|
|