parser: idStatement returns stmt: 'state'...; add classDef, class statements
This commit is contained in:
parent
15dd60ab85
commit
370806365f
|
@ -0,0 +1,189 @@
|
|||
import stateDb from '../stateDb';
|
||||
import stateDiagram from './stateDiagram';
|
||||
import { setConfig } from '../../../config';
|
||||
|
||||
setConfig({
|
||||
securityLevel: 'strict',
|
||||
});
|
||||
|
||||
describe('ClassDefs and classes when parsing a State diagram', () => {
|
||||
beforeEach(function () {
|
||||
stateDiagram.parser.yy = stateDb;
|
||||
stateDiagram.parser.yy.clear();
|
||||
});
|
||||
|
||||
describe('class for a state (classDef)', () => {
|
||||
describe('defining (classDef)', () => {
|
||||
it('has "classDef" as a keyword, an id, and can set a css style attribute', function () {
|
||||
stateDiagram.parser.parse('stateDiagram-v2\n classDef exampleClass background:#bbb;');
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const styleClasses = stateDb.getClasses();
|
||||
expect(styleClasses['exampleClass'].styles.length).toEqual(1);
|
||||
expect(styleClasses['exampleClass'].styles[0]).toEqual('background:#bbb');
|
||||
});
|
||||
|
||||
it('can define multiple attributes separated by commas', function () {
|
||||
stateDiagram.parser.parse(
|
||||
'stateDiagram-v2\n classDef exampleClass background:#bbb, font-weight:bold, font-style:italic;'
|
||||
);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const styleClasses = stateDb.getClasses();
|
||||
expect(styleClasses['exampleClass'].styles.length).toEqual(3);
|
||||
expect(styleClasses['exampleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(styleClasses['exampleClass'].styles[1]).toEqual('font-weight:bold');
|
||||
expect(styleClasses['exampleClass'].styles[2]).toEqual('font-style:italic');
|
||||
});
|
||||
|
||||
// need to look at what the lexer is doing. see the work on the chevotrain parser
|
||||
it('an attribute can have a dot in the style', function () {
|
||||
stateDiagram.parser.parse(
|
||||
'stateDiagram-v2\n classDef exampleStyleClass background:#bbb,border:1.5px solid red;'
|
||||
);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toBe(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toBe('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toBe('border:1.5px solid red');
|
||||
});
|
||||
|
||||
it('an attribute can have a space in the style', function () {
|
||||
stateDiagram.parser.parse(
|
||||
'stateDiagram-v2\n classDef exampleStyleClass background: #bbb,border:1.5px solid red;'
|
||||
);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toBe(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toBe('background: #bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toBe('border:1.5px solid red');
|
||||
});
|
||||
});
|
||||
|
||||
describe('applying to states in the diagram', () => {
|
||||
it('can apply a class to a state', function () {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n' + '\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a --> b ';
|
||||
diagram += 'class a exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDb.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
const state_a = stateDb.getState('a');
|
||||
expect(state_a.classes.length).toEqual(1);
|
||||
expect(state_a.classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('can be applied to a state with an id containing _', function () {
|
||||
let diagram = '';
|
||||
|
||||
diagram += 'stateDiagram-v2\n' + '\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a_a --> b_b' + '\n';
|
||||
diagram += 'class a_a exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
expect(classes['exampleStyleClass'].styles.length).toBe(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toBe('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toBe('border:1px solid red');
|
||||
|
||||
const state_a_a = stateDiagram.parser.yy.getState('a_a');
|
||||
expect(state_a_a.classes.length).toEqual(1);
|
||||
expect(state_a_a.classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
describe('::: syntax', () => {
|
||||
it('can be applied to a state using ::: syntax', () => {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n' + '\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;' + '\n';
|
||||
diagram += 'a --> b:::exampleStyleClass' + '\n';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const states = stateDiagram.parser.yy.getStates();
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
expect(states['b'].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('can be applied to a [*] state', () => {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += '[*]:::exampleStyleClass --> b\n';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
|
||||
const states = stateDiagram.parser.yy.getStates();
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
expect(states['root_start'].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('can be applied to a comma separated list of states', function () {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a-->b\n';
|
||||
diagram += 'class a,b exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
let classes = stateDiagram.parser.yy.getClasses();
|
||||
let states = stateDiagram.parser.yy.getStates();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
expect(states['a'].classes[0]).toEqual('exampleStyleClass');
|
||||
expect(states['b'].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
|
||||
it('a comma separated list of states may or may not have spaces after commas', function () {
|
||||
let diagram = '';
|
||||
diagram += 'stateDiagram-v2\n\n';
|
||||
diagram += 'classDef exampleStyleClass background:#bbb,border:1px solid red;\n';
|
||||
diagram += 'a-->b\n';
|
||||
diagram += 'class a,b,c, d, e exampleStyleClass';
|
||||
|
||||
stateDiagram.parser.parse(diagram);
|
||||
stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
|
||||
const classes = stateDiagram.parser.yy.getClasses();
|
||||
const states = stateDiagram.parser.yy.getStates();
|
||||
|
||||
expect(classes['exampleStyleClass'].styles.length).toEqual(2);
|
||||
expect(classes['exampleStyleClass'].styles[0]).toEqual('background:#bbb');
|
||||
expect(classes['exampleStyleClass'].styles[1]).toEqual('border:1px solid red');
|
||||
|
||||
const statesList = ['a', 'b', 'c', 'd', 'e'];
|
||||
statesList.forEach((stateId) => {
|
||||
expect(states[stateId].classes[0]).toEqual('exampleStyleClass');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -23,6 +23,10 @@
|
|||
%x acc_title
|
||||
%x acc_descr
|
||||
%x acc_descr_multiline
|
||||
%x CLASSDEF
|
||||
%x CLASSDEFID
|
||||
%x CLASS
|
||||
%x CLASS_STYLE
|
||||
%x NOTE
|
||||
%x NOTE_ID
|
||||
%x NOTE_TEXT
|
||||
|
@ -39,6 +43,8 @@
|
|||
|
||||
%%
|
||||
|
||||
"default" return 'DEFAULT';
|
||||
|
||||
.*direction\s+TB[^\n]* return 'direction_tb';
|
||||
.*direction\s+BT[^\n]* return 'direction_bt';
|
||||
.*direction\s+RL[^\n]* return 'direction_rl';
|
||||
|
@ -69,6 +75,20 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||
<acc_descr_multiline>[\}] { this.popState(); }
|
||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
||||
|
||||
<INITIAL,struct>"classDef"\s+ { this.pushState('CLASSDEF'); return 'classDef'; }
|
||||
<CLASSDEF>DEFAULT\s+ { this.popState(); this.pushState('CLASSDEFID'); return 'DEFAULT_CLASSDEF_ID' }
|
||||
<CLASSDEF>\w+\s+ { this.popState(); this.pushState('CLASSDEFID'); return 'CLASSDEF_ID' }
|
||||
<CLASSDEFID>[^\n]* { this.popState(); return 'CLASSDEF_STYLEOPTS' }
|
||||
|
||||
<INITIAL,struct>"class"\s+ { this.pushState('CLASS'); return 'class'; }
|
||||
<CLASS>(\w+)+((","\s*\w+)*) { this.popState(); this.pushState('CLASS_STYLE'); return 'CLASSENTITY_IDS' }
|
||||
<CLASS_STYLE>[^\n]* { this.popState(); return 'STYLECLASS' }
|
||||
|
||||
"scale"\s+ { this.pushState('SCALE'); /* console.log('Got scale', yytext);*/ return 'scale'; }
|
||||
<SCALE>\d+ return 'WIDTH';
|
||||
<SCALE>\s+"width" {this.popState();}
|
||||
|
||||
|
||||
<INITIAL,struct>"state"\s+ { /*console.log('Starting STATE zxzx'+yy.getDirection());*/this.pushState('STATE'); }
|
||||
<STATE>.*"<<fork>>" {this.popState();yytext=yytext.slice(0,-8).trim(); /*console.warn('Fork Fork: ',yytext);*/return 'FORK';}
|
||||
<STATE>.*"<<join>>" {this.popState();yytext=yytext.slice(0,-8).trim();/*console.warn('Fork Join: ',yytext);*/return 'JOIN';}
|
||||
|
@ -111,10 +131,12 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||
<INITIAL,struct>[^:\n\s\-\{]+ { /*console.log('=>ID=',yytext);*/ return 'ID';}
|
||||
// <INITIAL,struct>\s*":"[^\+\->:\n;]+ { yytext = yytext.trim(); /*console.log('Descr = ', yytext);*/ return 'DESCR'; }
|
||||
<INITIAL,struct>\s*":"[^:\n;]+ { yytext = yytext.trim(); /*console.log('Descr = ', yytext);*/ return 'DESCR'; }
|
||||
|
||||
<INITIAL,struct>"-->" return '-->';
|
||||
<struct>"--" return 'CONCURRENT';
|
||||
<<EOF>> return 'NL';
|
||||
. return 'INVALID';
|
||||
<struct>"--" return 'CONCURRENT';
|
||||
":::" return 'STYLE_SEPARATOR';
|
||||
<<EOF>> return 'NL';
|
||||
. return 'INVALID';
|
||||
|
||||
/lex
|
||||
|
||||
|
@ -124,20 +146,23 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
|
|||
|
||||
%% /* language grammar */
|
||||
|
||||
/* $$ is the value of the symbol being evaluated (= what is to the left of the : in the rule */
|
||||
|
||||
start
|
||||
: SPACE start
|
||||
| NL start
|
||||
| directive start
|
||||
| SD document { /*console.warn('Root document', $2);*/ yy.setRootDoc($2);return $2; }
|
||||
| SD document { /* console.log('--> Root document', $2); */ yy.setRootDoc($2); return $2; }
|
||||
;
|
||||
|
||||
document
|
||||
: /* empty */ { $$ = [] }
|
||||
: /* empty */ { /*console.log('empty document'); */ $$ = [] }
|
||||
| document line {
|
||||
if($2!='nl'){
|
||||
$1.push($2);$$ = $1
|
||||
if($2 !='nl'){
|
||||
/* console.log(' document: 1: ', $1, ' pushing 2: ', $2); */
|
||||
$1.push($2); $$ = $1
|
||||
}
|
||||
// console.warn('Got document',$1, $2);
|
||||
/* console.log('Got document',$1, $2); */
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -148,24 +173,34 @@ line
|
|||
;
|
||||
|
||||
statement
|
||||
: idStatement { /*console.warn('got id and descr', $1);*/$$={ stmt: 'state', id: $1, type: 'default', description: ''};}
|
||||
| idStatement DESCR { /*console.warn('got id and descr', $1, $2.trim());*/$$={ stmt: 'state', id: $1, type: 'default', description: yy.trimColon($2)};}
|
||||
: classDefStatement
|
||||
| cssClassStatement
|
||||
| idStatement { /* console.log('got id', $1); */
|
||||
$$=$1;
|
||||
}
|
||||
| idStatement DESCR {
|
||||
const stateStmt = $1;
|
||||
stateStmt.description = yy.trimColon($2);
|
||||
$$ = stateStmt;
|
||||
}
|
||||
| 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: ''}};
|
||||
}
|
||||
{
|
||||
/* console.info('got ids: 1: ', $1, ' 2:', $2,' 3: ', $3); */
|
||||
// console.log(' idStatement --> idStatement : state1 =', $1, ' state2 =', $3);
|
||||
$$={ stmt: 'relation', state1: $1, state2: $3};
|
||||
}
|
||||
| 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()};
|
||||
}
|
||||
{
|
||||
const relDescription = yy.trimColon($4);
|
||||
/* console.log(' idStatement --> idStatement DESCR : state1 =', $1, ' state2stmt =', $3, ' description: ', relDescription); */
|
||||
$$={ stmt: 'relation', state1: $1, state2: $3, description: relDescription};
|
||||
}
|
||||
| HIDE_EMPTY
|
||||
| scale WIDTH
|
||||
| COMPOSIT_STATE
|
||||
| COMPOSIT_STATE STRUCT_START document STRUCT_STOP
|
||||
{
|
||||
/* console.warn('Adding document for state without id ', $1);*/
|
||||
/* console.log('Adding document for state without id ', $1); */
|
||||
$$={ stmt: 'state', id: $1, type: 'default', description: '', doc: $3 }
|
||||
}
|
||||
| STATE_DESCR AS ID {
|
||||
|
@ -181,7 +216,7 @@ statement
|
|||
}
|
||||
| STATE_DESCR AS ID STRUCT_START document STRUCT_STOP
|
||||
{
|
||||
// console.warn('Adding document for state with id zxzx', $3, $4, yy.getDirection()); yy.addDocument($3);
|
||||
/* console.log('Adding document for state with id zxzx', $3, $4, yy.getDirection()); yy.addDocument($3);*/
|
||||
$$={ stmt: 'state', id: $3, type: 'default', description: $1, doc: $5 }
|
||||
}
|
||||
| FORK {
|
||||
|
@ -208,6 +243,23 @@ statement
|
|||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } ;
|
||||
|
||||
|
||||
classDefStatement
|
||||
: classDef CLASSDEF_ID CLASSDEF_STYLEOPTS {
|
||||
$$ = { stmt: 'classDef', id: $2.trim(), classes: $3.trim() };
|
||||
}
|
||||
| classDef DEFAULT CLASSDEF_STYLEOPTS {
|
||||
$$ = { stmt: 'classDef', id: $2.trim(), classes: $3.trim() };
|
||||
}
|
||||
;
|
||||
|
||||
cssClassStatement
|
||||
: class CLASSENTITY_IDS STYLECLASS {
|
||||
//console.log('apply class: id(s): ',$2, ' style class: ', $3);
|
||||
$$={ stmt: 'applyClass', id: $2.trim(), styleClass: $3.trim() };
|
||||
}
|
||||
;
|
||||
|
||||
directive
|
||||
: openDirective typeDirective closeDirective
|
||||
| openDirective typeDirective ':' argDirective closeDirective
|
||||
|
@ -229,8 +281,22 @@ eol
|
|||
;
|
||||
|
||||
idStatement
|
||||
: ID {$$=$1;}
|
||||
| EDGE_STATE {$$=$1;}
|
||||
: ID
|
||||
{ /* console.log('idStatement id: ', $1); */
|
||||
$$={ stmt: 'state', id: $1.trim(), type: 'default', description: '' };
|
||||
}
|
||||
| EDGE_STATE
|
||||
{ /* console.log('idStatement id: ', $1); */
|
||||
$$={ stmt: 'state', id: $1.trim(), type: 'default', description: '' };
|
||||
}
|
||||
| ID STYLE_SEPARATOR ID
|
||||
{ /*console.log('idStatement ID STYLE_SEPARATOR ID'); */
|
||||
$$={ stmt: 'state', id: $1.trim(), classes: [$3.trim()], type: 'default', description: '' };
|
||||
}
|
||||
| EDGE_STATE STYLE_SEPARATOR ID
|
||||
{ /*console.log('idStatement EDGE_STATE STYLE_SEPARATOR ID'); */
|
||||
$$={ stmt: 'state', id: $1.trim(), classes: [$3.trim()], type: 'default', description: '' };
|
||||
}
|
||||
;
|
||||
|
||||
notePosition
|
||||
|
|
Loading…
Reference in New Issue