#945 Recursive object from parsing and stateDb

This commit is contained in:
Knut Sveidqvist 2019-10-02 19:32:13 +02:00
parent 94afcfb6f9
commit f9f8785aef
4 changed files with 107 additions and 226 deletions

View File

@ -49,6 +49,23 @@ describe('State diagram', () => {
state "Long state description" as XState1 state "Long state description" as XState1
state "Another Long state description" as XState2 state "Another Long state description" as XState2
XState2 : New line XState2 : New line
XState1 --> XState2
`,
{ logLevel: 0 }
);
cy.get('svg');
});
it('should render composit states', () => {
imgSnapshotTest(
`
stateDiagram
[*] --> NotShooting
state NotShooting {
[*] --> Idle
Idle --> Configuring : EvConfig
Configuring --> Idle : EvConfig
}
`, `,
{ logLevel: 0 } { logLevel: 0 }
); );

View File

@ -89,30 +89,52 @@
start start
: SPACE start : SPACE start
| NL start | NL start
| SD document { return $2; } | SD document { console.warn('Root document', $2); return $2; }
; ;
document document
: /* empty */ { $$ = [] } : /* empty */ { $$ = [] }
| document line {$1.push($2);$$ = $1} | document line {
if($2!='nl'){
$1.push($2);$$ = $1
}
console.warn('Got document',$1, $2);
}
; ;
line line
: SPACE statement { console.log('here');$$ = $2 } : SPACE statement { console.warn('here');$$ = $2 }
| statement {console.log('line', $1); $$ = $1 } | statement {console.warn('line', $1); $$ = $1 }
| NL { $$=[];} | NL { console.warn('NL'); $$='nl';}
; ;
statement statement
: idStatement DESCR {yy.addState($1, 'default');yy.addDescription($1, $2.trim());} : idStatement DESCR { $$={ stmt: 'state', id: $1, type: 'default', description: $2.trim()};}
| idStatement '-->' idStatement {yy.addRelation($1, $3);} | idStatement '-->' idStatement
| idStatement '-->' idStatement DESCR {yy.addRelation($1, $3, $4.substr(1).trim());} {
/*console.warn('got id', $1);yy.addRelation($1, $3);*/
$$={ stmt: 'relation', state1: { stmt: 'state', id: $1, type: 'default', description: '' }, state2:{ stmt: 'state', id: $3 ,type: 'default', description: ''}};
}
| idStatement '-->' idStatement DESCR
{
/*yy.addRelation($1, $3, $4.substr(1).trim());*/
$$={ stmt: 'relation', state1: { stmt: 'state', id: $1, type: 'default', description: '' }, state2:{ stmt: 'state', id: $3 ,type: 'default', description: ''}, description: $4.substr(1).trim()};
}
| HIDE_EMPTY | HIDE_EMPTY
| scale WIDTH | scale WIDTH
| COMPOSIT_STATE | COMPOSIT_STATE
| COMPOSIT_STATE STRUCT_START document STRUCT_STOP | COMPOSIT_STATE STRUCT_START document STRUCT_STOP
| STATE_DESCR AS ID {yy.addState($3, 'default');yy.addDescription($3, $1);} {
console.warn('Adding document for state without id ', $3);
// yy.addDocument('noId');
$$={ stmt: 'state', id: 'noId', type: 'default', description: '', doc: $3 }
}
| STATE_DESCR AS ID { $$={id: $3, type: 'default', description: $1.trim()};}
| STATE_DESCR AS ID STRUCT_START document STRUCT_STOP | STATE_DESCR AS ID STRUCT_START document STRUCT_STOP
{
//console.warn('Adding document for state with id ', $3, $4); yy.addDocument($3);
$$={ stmt: 'state', id: $3, type: 'default', description: $1, doc: $5 }
}
| FORK | FORK
| JOIN | JOIN
| CONCURRENT | CONCURRENT
@ -129,112 +151,5 @@ notePosition
: left_of : left_of
| right_of | right_of
; ;
// statement
// : 'participant' actor 'AS' restOfLine 'NL' {$2.description=$4; $$=$2;}
// | 'participant' actor 'NL' {$$=$2;}
// | signal 'NL'
// | 'activate' actor 'NL' {$$={type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $2};}
// | 'deactivate' actor 'NL' {$$={type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $2};}
// | note_statement 'NL'
// | title text2 'NL' {$$=[{type:'setTitle', text:$2}]}
// | 'loop' restOfLine document end
// {
// $3.unshift({type: 'loopStart', loopText:$2, signalType: yy.LINETYPE.LOOP_START});
// $3.push({type: 'loopEnd', loopText:$2, signalType: yy.LINETYPE.LOOP_END});
// $$=$3;}
// | 'rect' restOfLine document end
// {
// $3.unshift({type: 'rectStart', color:$2, signalType: yy.LINETYPE.RECT_START });
// $3.push({type: 'rectEnd', color:$2, signalType: yy.LINETYPE.RECT_END });
// $$=$3;}
// | opt restOfLine document end
// {
// $3.unshift({type: 'optStart', optText:$2, signalType: yy.LINETYPE.OPT_START});
// $3.push({type: 'optEnd', optText:$2, signalType: yy.LINETYPE.OPT_END});
// $$=$3;}
// | alt restOfLine else_sections end
// {
// // Alt start
// $3.unshift({type: 'altStart', altText:$2, signalType: yy.LINETYPE.ALT_START});
// // Content in alt is already in $3
// // End
// $3.push({type: 'altEnd', signalType: yy.LINETYPE.ALT_END});
// $$=$3;}
// | par restOfLine par_sections end
// {
// // Parallel start
// $3.unshift({type: 'parStart', parText:$2, signalType: yy.LINETYPE.PAR_START});
// // Content in par is already in $3
// // End
// $3.push({type: 'parEnd', signalType: yy.LINETYPE.PAR_END});
// $$=$3;}
// ;
// par_sections
// : document
// | document and restOfLine par_sections
// { $$ = $1.concat([{type: 'and', parText:$3, signalType: yy.LINETYPE.PAR_AND}, $4]); }
// ;
// else_sections
// : document
// | document else restOfLine else_sections
// { $$ = $1.concat([{type: 'else', altText:$3, signalType: yy.LINETYPE.ALT_ELSE}, $4]); }
// ;
// note_statement
// : 'note' placement actor text2
// {
// $$ = [$3, {type:'addNote', placement:$2, actor:$3.actor, text:$4}];}
// | 'note' 'over' actor_pair text2
// {
// // Coerce actor_pair into a [to, from, ...] array
// $2 = [].concat($3, $3).slice(0, 2);
// $2[0] = $2[0].actor;
// $2[1] = $2[1].actor;
// $$ = [$3, {type:'addNote', placement:yy.PLACEMENT.OVER, actor:$2.slice(0, 2), text:$4}];}
// ;
// spaceList
// : SPACE spaceList
// | SPACE
// ;
// actor_pair
// : actor ',' actor { $$ = [$1, $3]; }
// | actor { $$ = $1; }
// ;
// placement
// : 'left_of' { $$ = yy.PLACEMENT.LEFTOF; }
// | 'right_of' { $$ = yy.PLACEMENT.RIGHTOF; }
// ;
// signal
// : actor signaltype '+' actor text2
// { $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
// {type: 'activeStart', signalType: yy.LINETYPE.ACTIVE_START, actor: $4}
// ]}
// | actor signaltype '-' actor text2
// { $$ = [$1,$4,{type: 'addMessage', from:$1.actor, to:$4.actor, signalType:$2, msg:$5},
// {type: 'activeEnd', signalType: yy.LINETYPE.ACTIVE_END, actor: $1}
// ]}
// | actor signaltype actor text2
// { $$ = [$1,$3,{type: 'addMessage', from:$1.actor, to:$3.actor, signalType:$2, msg:$4}]}
// ;
// actor
// : ACTOR {$$={type: 'addActor', actor:$1}}
// ;
// signaltype
// : SOLID_OPEN_ARROW { $$ = yy.LINETYPE.SOLID_OPEN; }
// | DOTTED_OPEN_ARROW { $$ = yy.LINETYPE.DOTTED_OPEN; }
// | SOLID_ARROW { $$ = yy.LINETYPE.SOLID; }
// | DOTTED_ARROW { $$ = yy.LINETYPE.DOTTED; }
// | SOLID_CROSS { $$ = yy.LINETYPE.SOLID_CROSS; }
// | DOTTED_CROSS { $$ = yy.LINETYPE.DOTTED_CROSS; }
// ;
// text2: TXT {$$ = $1.substring(1).trim().replace(/\\n/gm, "\n");} ;
%% %%

View File

@ -1,7 +1,18 @@
import { logger } from '../../logger'; import { logger } from '../../logger';
let relations = []; const newDoc = () => {
let states = {}; return {
relations: [],
states: {},
documents: {}
};
};
let documents = {
root: newDoc()
};
let currentDocument = documents.root;
let startCnt = 0; let startCnt = 0;
let endCnt = 0; let endCnt = 0;
@ -14,8 +25,9 @@ let endCnt = 0;
* @param style * @param style
*/ */
export const addState = function(id, type) { export const addState = function(id, type) {
if (typeof states[id] === 'undefined') { console.warn('Add state', id);
states[id] = { if (typeof currentDocument.states[id] === 'undefined') {
currentDocument.states[id] = {
id: id, id: id,
descriptions: [], descriptions: [],
type type
@ -24,21 +36,30 @@ export const addState = function(id, type) {
}; };
export const clear = function() { export const clear = function() {
relations = []; documents = {
states = {}; root: newDoc()
};
}; };
export const getState = function(id) { export const getState = function(id) {
return states[id]; return currentDocument.states[id];
};
export const addDocument = id => {
console.warn(currentDocument, documents);
currentDocument.documents[id] = newDoc();
currentDocument.documents[id].parent = currentDocument;
currentDocument = currentDocument.documents[id];
}; };
export const getStates = function() { export const getStates = function() {
return states; return currentDocument.states;
};
export const logDocuments = function() {
console.warn('Documents = ', documents);
}; };
export const getRelations = function() { export const getRelations = function() {
// const relations1 = [{ id1: 'start1', id2: 'state1' }, { id1: 'state1', id2: 'exit1' }]; // const relations1 = [{ id1: 'start1', id2: 'state1' }, { id1: 'state1', id2: 'exit1' }];
// return relations; // return relations;
return relations; return currentDocument.relations;
}; };
export const addRelation = function(_id1, _id2, title) { export const addRelation = function(_id1, _id2, title) {
@ -59,11 +80,11 @@ export const addRelation = function(_id1, _id2, title) {
console.log(id1, id2, title); console.log(id1, id2, title);
addState(id1, type1); addState(id1, type1);
addState(id2, type2); addState(id2, type2);
relations.push({ id1, id2, title }); currentDocument.relations.push({ id1, id2, title });
}; };
export const addDescription = function(id, _descr) { export const addDescription = function(id, _descr) {
const theState = states[id]; const theState = currentDocument.states[id];
let descr = _descr; let descr = _descr;
if (descr[0] === ':') { if (descr[0] === ':') {
descr = descr.substr(1).trim(); descr = descr.substr(1).trim();
@ -72,12 +93,6 @@ export const addDescription = function(id, _descr) {
theState.descriptions.push(descr); theState.descriptions.push(descr);
}; };
export const addMembers = function(className, MembersArr) {
if (Array.isArray(MembersArr)) {
MembersArr.forEach(member => addMember(className, member));
}
};
export const cleanupLabel = function(label) { export const cleanupLabel = function(label) {
if (label.substring(0, 1) === ':') { if (label.substring(0, 1) === ':') {
return label.substr(2).trim(); return label.substr(2).trim();
@ -106,8 +121,9 @@ export default {
getRelations, getRelations,
addRelation, addRelation,
addDescription, addDescription,
addMembers,
cleanupLabel, cleanupLabel,
lineType, lineType,
relationType relationType,
logDocuments,
addDocument
}; };

View File

@ -39,94 +39,6 @@ const getGraphId = function(label) {
* Setup arrow head and define the marker. The result is appended to the svg. * Setup arrow head and define the marker. The result is appended to the svg.
*/ */
const insertMarkers = function(elem) { const insertMarkers = function(elem) {
elem
.append('defs')
.append('marker')
.attr('id', 'extensionStart')
.attr('class', 'extension')
.attr('refX', 0)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 1,7 L18,13 V 1 Z');
elem
.append('defs')
.append('marker')
.attr('id', 'extensionEnd')
.attr('refX', 19)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 28)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 1,1 V 13 L18,7 Z'); // this is actual shape for arrowhead
elem
.append('defs')
.append('marker')
.attr('id', 'compositionStart')
.attr('class', 'extension')
.attr('refX', 0)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
elem
.append('defs')
.append('marker')
.attr('id', 'compositionEnd')
.attr('refX', 19)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 28)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
elem
.append('defs')
.append('marker')
.attr('id', 'aggregationStart')
.attr('class', 'extension')
.attr('refX', 0)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
elem
.append('defs')
.append('marker')
.attr('id', 'aggregationEnd')
.attr('refX', 19)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 28)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 18,7 L9,13 L1,7 L9,1 Z');
elem
.append('defs')
.append('marker')
.attr('id', 'dependencyStart')
.attr('class', 'extension')
.attr('refX', 0)
.attr('refY', 7)
.attr('markerWidth', 190)
.attr('markerHeight', 240)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 5,7 L9,13 L1,7 L9,1 Z');
elem elem
.append('defs') .append('defs')
.append('marker') .append('marker')
@ -434,6 +346,7 @@ const drawState = function(elem, stateDef) {
export const draw = function(text, id) { export const draw = function(text, id) {
parser.yy.clear(); parser.yy.clear();
parser.parse(text); parser.parse(text);
stateDb.logDocuments();
logger.info('Rendering diagram ' + text); logger.info('Rendering diagram ' + text);
// /// / Fetch the default direction, use TD if none was found // /// / Fetch the default direction, use TD if none was found
@ -442,10 +355,11 @@ export const draw = function(text, id) {
// // Layout graph, Create a new directed graph // // Layout graph, Create a new directed graph
const graph = new graphlib.Graph({ const graph = new graphlib.Graph({
multigraph: false multigraph: false,
compound: true
}); });
// // Set an object for the graph label // Set an object for the graph label
graph.setGraph({ graph.setGraph({
isMultiGraph: false isMultiGraph: false
}); });
@ -457,22 +371,41 @@ export const draw = function(text, id) {
const states = stateDb.getStates(); const states = stateDb.getStates();
const keys = Object.keys(states); const keys = Object.keys(states);
total = keys.length; total = keys.length;
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
const stateDef = states[keys[i]]; const stateDef = states[keys[i]];
const node = drawState(diagram, stateDef); const node = drawState(diagram, stateDef);
// const nodeAppendix = drawStartState(diagram, stateDef);
// Add nodes to the graph. The first argument is the node id. The second is // Add nodes to the graph. The first argument is the node id. The second is
// metadata about the node. In this case we're going to add labels to each of // metadata about the node. In this case we're going to add labels to each of
// our nodes. // our nodes.
graph.setNode(node.id, node); graph.setNode(node.id, node);
// graph.setNode(node.id + 'note', nodeAppendix);
// let parent = 'p1';
// if (node.id === 'XState1') {
// parent = 'p2';
// }
// graph.setParent(node.id, parent);
// graph.setParent(node.id + 'note', parent);
// logger.info('Org height: ' + node.height); // logger.info('Org height: ' + node.height);
} }
console.info('Count=', graph.nodeCount());
const relations = stateDb.getRelations(); const relations = stateDb.getRelations();
relations.forEach(function(relation) { relations.forEach(function(relation) {
graph.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), { graph.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), {
relation: relation,
width: 38
});
console.warn(getGraphId(relation.id1), getGraphId(relation.id2), {
relation: relation relation: relation
}); });
// graph.setEdge(getGraphId(relation.id1), getGraphId(relation.id2));
}); });
dagre.layout(graph); dagre.layout(graph);
graph.nodes().forEach(function(v) { graph.nodes().forEach(function(v) {