#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 "Another Long state description" as XState2
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 }
);

View File

@ -89,30 +89,52 @@
start
: SPACE start
| NL start
| SD document { return $2; }
| SD document { console.warn('Root document', $2); return $2; }
;
document
: /* empty */ { $$ = [] }
| document line {$1.push($2);$$ = $1}
| document line {
if($2!='nl'){
$1.push($2);$$ = $1
}
console.warn('Got document',$1, $2);
}
;
line
: SPACE statement { console.log('here');$$ = $2 }
| statement {console.log('line', $1); $$ = $1 }
| NL { $$=[];}
: SPACE statement { console.warn('here');$$ = $2 }
| statement {console.warn('line', $1); $$ = $1 }
| NL { console.warn('NL'); $$='nl';}
;
statement
: idStatement DESCR {yy.addState($1, 'default');yy.addDescription($1, $2.trim());}
| idStatement '-->' idStatement {yy.addRelation($1, $3);}
| idStatement '-->' idStatement DESCR {yy.addRelation($1, $3, $4.substr(1).trim());}
: idStatement DESCR { $$={ stmt: 'state', id: $1, type: 'default', description: $2.trim()};}
| idStatement '-->' idStatement
{
/*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
| scale WIDTH
| COMPOSIT_STATE
| 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
{
//console.warn('Adding document for state with id ', $3, $4); yy.addDocument($3);
$$={ stmt: 'state', id: $3, type: 'default', description: $1, doc: $5 }
}
| FORK
| JOIN
| CONCURRENT
@ -129,112 +151,5 @@ notePosition
: left_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';
let relations = [];
let states = {};
const newDoc = () => {
return {
relations: [],
states: {},
documents: {}
};
};
let documents = {
root: newDoc()
};
let currentDocument = documents.root;
let startCnt = 0;
let endCnt = 0;
@ -14,8 +25,9 @@ let endCnt = 0;
* @param style
*/
export const addState = function(id, type) {
if (typeof states[id] === 'undefined') {
states[id] = {
console.warn('Add state', id);
if (typeof currentDocument.states[id] === 'undefined') {
currentDocument.states[id] = {
id: id,
descriptions: [],
type
@ -24,21 +36,30 @@ export const addState = function(id, type) {
};
export const clear = function() {
relations = [];
states = {};
documents = {
root: newDoc()
};
};
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() {
return states;
return currentDocument.states;
};
export const logDocuments = function() {
console.warn('Documents = ', documents);
};
export const getRelations = function() {
// const relations1 = [{ id1: 'start1', id2: 'state1' }, { id1: 'state1', id2: 'exit1' }];
// return relations;
return relations;
return currentDocument.relations;
};
export const addRelation = function(_id1, _id2, title) {
@ -59,11 +80,11 @@ export const addRelation = function(_id1, _id2, title) {
console.log(id1, id2, title);
addState(id1, type1);
addState(id2, type2);
relations.push({ id1, id2, title });
currentDocument.relations.push({ id1, id2, title });
};
export const addDescription = function(id, _descr) {
const theState = states[id];
const theState = currentDocument.states[id];
let descr = _descr;
if (descr[0] === ':') {
descr = descr.substr(1).trim();
@ -72,12 +93,6 @@ export const addDescription = function(id, _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) {
if (label.substring(0, 1) === ':') {
return label.substr(2).trim();
@ -106,8 +121,9 @@ export default {
getRelations,
addRelation,
addDescription,
addMembers,
cleanupLabel,
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.
*/
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
.append('defs')
.append('marker')
@ -434,6 +346,7 @@ const drawState = function(elem, stateDef) {
export const draw = function(text, id) {
parser.yy.clear();
parser.parse(text);
stateDb.logDocuments();
logger.info('Rendering diagram ' + text);
// /// / 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
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({
isMultiGraph: false
});
@ -457,22 +371,41 @@ export const draw = function(text, id) {
const states = stateDb.getStates();
const keys = Object.keys(states);
total = keys.length;
for (let i = 0; i < keys.length; i++) {
const stateDef = states[keys[i]];
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
// metadata about the node. In this case we're going to add labels to each of
// our nodes.
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);
}
console.info('Count=', graph.nodeCount());
const relations = stateDb.getRelations();
relations.forEach(function(relation) {
graph.setEdge(getGraphId(relation.id1), getGraphId(relation.id2), {
relation: relation,
width: 38
});
console.warn(getGraphId(relation.id1), getGraphId(relation.id2), {
relation: relation
});
// graph.setEdge(getGraphId(relation.id1), getGraphId(relation.id2));
});
dagre.layout(graph);
graph.nodes().forEach(function(v) {