Merge branch 'develop' into resolve-info-html-assignment

This commit is contained in:
Reda Al Sulais 2023-08-05 19:03:35 +03:00 committed by GitHub
commit ef29388d68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 598 additions and 440 deletions

View File

@ -891,4 +891,27 @@ graph TD
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
); );
}); });
it('66: apply class called default on node called default', () => {
imgSnapshotTest(
`
graph TD
classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff
hello --> default
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
it('67: should be able to style default node independently', () => {
imgSnapshotTest(
`
flowchart TD
classDef default fill:#a34
hello --> default
style default stroke:#000,stroke-width:4px
`,
{ htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' }
);
});
}); });

View File

@ -608,12 +608,12 @@ It is possible to escape characters using the syntax exemplified here.
```mermaid-example ```mermaid-example
flowchart LR flowchart LR
A["A double quote:#quot;"] -->B["A dec char:#9829;"] A["A double quote:#quot;"] --> B["A dec char:#9829;"]
``` ```
```mermaid ```mermaid
flowchart LR flowchart LR
A["A double quote:#quot;"] -->B["A dec char:#9829;"] A["A double quote:#quot;"] --> B["A dec char:#9829;"]
``` ```
Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names.

View File

@ -49,7 +49,7 @@ describe('diagram detection', () => {
"Parse error on line 2: "Parse error on line 2:
graph TD; A--> graph TD; A-->
--------------^ --------------^
Expecting 'AMP', 'ALPHA', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'MINUS', 'BRKT', 'DOT', 'PUNCTUATION', 'UNICODE_TEXT', 'PLUS', 'EQUALS', 'MULT', 'UNDERSCORE', got 'EOF'" Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'EOF'"
`); `);
await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects
.toThrowErrorMatchingInlineSnapshot(` .toThrowErrorMatchingInlineSnapshot(`

View File

@ -6,6 +6,40 @@ setConfig({
securityLevel: 'strict', securityLevel: 'strict',
}); });
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
'kitty',
];
const doubleEndedEdges = [
{ edgeStart: 'x--', edgeEnd: '--x', stroke: 'normal', type: 'double_arrow_cross' },
{ edgeStart: 'x==', edgeEnd: '==x', stroke: 'thick', type: 'double_arrow_cross' },
{ edgeStart: 'x-.', edgeEnd: '.-x', stroke: 'dotted', type: 'double_arrow_cross' },
{ edgeStart: 'o--', edgeEnd: '--o', stroke: 'normal', type: 'double_arrow_circle' },
{ edgeStart: 'o==', edgeEnd: '==o', stroke: 'thick', type: 'double_arrow_circle' },
{ edgeStart: 'o-.', edgeEnd: '.-o', stroke: 'dotted', type: 'double_arrow_circle' },
{ edgeStart: '<--', edgeEnd: '-->', stroke: 'normal', type: 'double_arrow_point' },
{ edgeStart: '<==', edgeEnd: '==>', stroke: 'thick', type: 'double_arrow_point' },
{ edgeStart: '<-.', edgeEnd: '.->', stroke: 'dotted', type: 'double_arrow_point' },
];
describe('[Edges] when parsing', () => { describe('[Edges] when parsing', () => {
beforeEach(function () { beforeEach(function () {
flow.parser.yy = flowDb; flow.parser.yy = flowDb;
@ -39,9 +73,10 @@ describe('[Edges] when parsing', () => {
expect(edges[0].type).toBe('arrow_circle'); expect(edges[0].type).toBe('arrow_circle');
}); });
describe('cross', function () { describe('edges', function () {
it('should handle double edged nodes and edges', function () { doubleEndedEdges.forEach((edgeType) => {
const res = flow.parser.parse('graph TD;\nA x--x B;'); it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () {
const res = flow.parser.parse(`graph TD;\nA ${edgeType.edgeStart}${edgeType.edgeEnd} B;`);
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@ -51,14 +86,15 @@ describe('[Edges] when parsing', () => {
expect(edges.length).toBe(1); expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross'); expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe(''); expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('normal'); expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
expect(edges[0].length).toBe(1);
}); });
it('should handle double edged nodes with text', function () { it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () {
const res = flow.parser.parse('graph TD;\nA x-- text --x B;'); const res = flow.parser.parse(
`graph TD;\nA ${edgeType.edgeStart} text ${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@ -68,14 +104,17 @@ describe('[Edges] when parsing', () => {
expect(edges.length).toBe(1); expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross'); expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe('text'); expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('normal'); expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
expect(edges[0].length).toBe(1);
}); });
it('should handle double edged nodes and edges on thick arrows', function () { it.each(keywords)(
const res = flow.parser.parse('graph TD;\nA x==x B;'); `should handle ${edgeType.stroke} ${edgeType.type} with %s text`,
function (keyword) {
const res = flow.parser.parse(
`graph TD;\nA ${edgeType.edgeStart} ${keyword} ${edgeType.edgeEnd} B;`
);
const vert = flow.parser.yy.getVertices(); const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges(); const edges = flow.parser.yy.getEdges();
@ -85,165 +124,11 @@ describe('[Edges] when parsing', () => {
expect(edges.length).toBe(1); expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross'); expect(edges[0].type).toBe(`${edgeType.type}`);
expect(edges[0].text).toBe(''); expect(edges[0].text).toBe(`${keyword}`);
expect(edges[0].stroke).toBe('thick'); expect(edges[0].stroke).toBe(`${edgeType.stroke}`);
expect(edges[0].length).toBe(1); }
}); );
it('should handle double edged nodes with text on thick arrows', function () {
const res = flow.parser.parse('graph TD;\nA x== text ==x B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('thick');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes and edges on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA x-.-x B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA x-. text .-x B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_cross');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
});
});
describe('circle', function () {
it('should handle double edged nodes and edges', function () {
const res = flow.parser.parse('graph TD;\nA o--o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('normal');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text', function () {
const res = flow.parser.parse('graph TD;\nA o-- text --o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('normal');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes and edges on thick arrows', function () {
const res = flow.parser.parse('graph TD;\nA o==o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('thick');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text on thick arrows', function () {
const res = flow.parser.parse('graph TD;\nA o== text ==o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('thick');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes and edges on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA o-.-o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
});
it('should handle double edged nodes with text on dotted arrows', function () {
const res = flow.parser.parse('graph TD;\nA o-. text .-o B;');
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['A'].id).toBe('A');
expect(vert['B'].id).toBe('B');
expect(edges.length).toBe(1);
expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B');
expect(edges[0].type).toBe('double_arrow_circle');
expect(edges[0].text).toBe('text');
expect(edges[0].stroke).toBe('dotted');
expect(edges[0].length).toBe(1);
}); });
}); });

View File

@ -24,7 +24,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
expect(vert['A'].labelType).toBe('markdown'); expect(vert['A'].labelType).toBe('markdown');
expect(vert['B'].id).toBe('B'); expect(vert['B'].id).toBe('B');
expect(vert['B'].text).toBe('The dog in the hog'); expect(vert['B'].text).toBe('The dog in the hog');
expect(vert['B'].labelType).toBe('text'); expect(vert['B'].labelType).toBe('string');
expect(edges.length).toBe(2); expect(edges.length).toBe(2);
expect(edges[0].start).toBe('A'); expect(edges[0].start).toBe('A');
expect(edges[0].end).toBe('B'); expect(edges[0].end).toBe('B');
@ -35,7 +35,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t
expect(edges[1].end).toBe('C'); expect(edges[1].end).toBe('C');
expect(edges[1].type).toBe('arrow_point'); expect(edges[1].type).toBe('arrow_point');
expect(edges[1].text).toBe('The rat in the mat'); expect(edges[1].text).toBe('The rat in the mat');
expect(edges[1].labelType).toBe('text'); expect(edges[1].labelType).toBe('string');
}); });
it('mardown formatting in subgraphs', function () { it('mardown formatting in subgraphs', function () {
const res = flow.parser.parse(`flowchart LR const res = flow.parser.parse(`flowchart LR

View File

@ -6,6 +6,29 @@ setConfig({
securityLevel: 'strict', securityLevel: 'strict',
}); });
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
];
const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_'];
describe('[Singlenodes] when parsing', () => { describe('[Singlenodes] when parsing', () => {
beforeEach(function () { beforeEach(function () {
flow.parser.yy = flowDb; flow.parser.yy = flowDb;
@ -259,4 +282,90 @@ describe('[Singlenodes] when parsing', () => {
expect(edges.length).toBe(0); expect(edges.length).toBe(0);
expect(vert['i_d'].styles.length).toBe(0); expect(vert['i_d'].styles.length).toBe(0);
}); });
it.each(keywords)('should handle keywords between dashes "-"', function (keyword) {
const res = flow.parser.parse(`graph TD;a-${keyword}-node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`a-${keyword}-node`].text).toBe(`a-${keyword}-node`);
});
it.each(keywords)('should handle keywords between periods "."', function (keyword) {
const res = flow.parser.parse(`graph TD;a.${keyword}.node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`a.${keyword}.node`].text).toBe(`a.${keyword}.node`);
});
it.each(keywords)('should handle keywords between underscores "_"', function (keyword) {
const res = flow.parser.parse(`graph TD;a_${keyword}_node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`a_${keyword}_node`].text).toBe(`a_${keyword}_node`);
});
it.each(keywords)('should handle nodes ending in %s', function (keyword) {
const res = flow.parser.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`);
const vert = flow.parser.yy.getVertices();
expect(vert[`node_${keyword}`].text).toBe(`node_${keyword}`);
expect(vert[`node.${keyword}`].text).toBe(`node.${keyword}`);
expect(vert[`node-${keyword}`].text).toBe(`node-${keyword}`);
});
const errorKeywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'linkStyle',
'interpolate',
'classDef',
'class',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
];
it.each(errorKeywords)('should throw error at nodes beginning with %s', function (keyword) {
const str = `graph TD;${keyword}.node;${keyword}-node;${keyword}/node`;
const vert = flow.parser.yy.getVertices();
expect(() => flow.parser.parse(str)).toThrowError();
});
const workingKeywords = ['default', 'href', 'click', 'call'];
it.each(workingKeywords)('should parse node beginning with %s', function (keyword) {
flow.parser.parse(`graph TD; ${keyword}.node;${keyword}-node;${keyword}/node;`);
const vert = flow.parser.yy.getVertices();
expect(vert[`${keyword}.node`].text).toBe(`${keyword}.node`);
expect(vert[`${keyword}-node`].text).toBe(`${keyword}-node`);
expect(vert[`${keyword}/node`].text).toBe(`${keyword}/node`);
});
it.each(specialChars)(
'should allow node ids of single special characters',
function (specialChar) {
flow.parser.parse(`graph TD; ${specialChar} --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert[`${specialChar}`].text).toBe(`${specialChar}`);
}
);
it.each(specialChars)(
'should allow node ids with special characters at start of id',
function (specialChar) {
flow.parser.parse(`graph TD; ${specialChar}node --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert[`${specialChar}node`].text).toBe(`${specialChar}node`);
}
);
it.each(specialChars)(
'should allow node ids with special characters at end of id',
function (specialChar) {
flow.parser.parse(`graph TD; node${specialChar} --> A`);
const vert = flow.parser.yy.getVertices();
expect(vert[`node${specialChar}`].text).toBe(`node${specialChar}`);
}
);
}); });

View File

@ -26,15 +26,6 @@ describe('[Style] when parsing', () => {
expect(vert['Q'].styles[0]).toBe('background:#fff'); expect(vert['Q'].styles[0]).toBe('background:#fff');
}); });
// log.debug(flow.parser.parse('graph TD;style Q background:#fff;'));
it('should handle styles for edges', function () {
const res = flow.parser.parse('graph TD;a-->b;\nstyle #0 stroke: #f66;');
const edges = flow.parser.yy.getEdges();
expect(edges.length).toBe(1);
});
it('should handle multiple styles for a vortex', function () { it('should handle multiple styles for a vortex', function () {
const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;'); const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;');

View File

@ -305,6 +305,95 @@ describe('[Text] when parsing', () => {
expect(vert['C'].type).toBe('round'); expect(vert['C'].type).toBe('round');
expect(vert['C'].text).toBe('Chimpansen hoppar'); expect(vert['C'].text).toBe('Chimpansen hoppar');
}); });
const keywords = [
'graph',
'flowchart',
'flowchart-elk',
'style',
'default',
'linkStyle',
'interpolate',
'classDef',
'class',
'href',
'call',
'click',
'_self',
'_blank',
'_parent',
'_top',
'end',
'subgraph',
'kitty',
];
const shapes = [
{ start: '[', end: ']', name: 'square' },
{ start: '(', end: ')', name: 'round' },
{ start: '{', end: '}', name: 'diamond' },
{ start: '(-', end: '-)', name: 'ellipse' },
{ start: '([', end: '])', name: 'stadium' },
{ start: '>', end: ']', name: 'odd' },
{ start: '[(', end: ')]', name: 'cylinder' },
{ start: '(((', end: ')))', name: 'doublecircle' },
{ start: '[/', end: '\\]', name: 'trapezoid' },
{ start: '[\\', end: '/]', name: 'inv_trapezoid' },
{ start: '[/', end: '/]', name: 'lean_right' },
{ start: '[\\', end: '\\]', name: 'lean_left' },
{ start: '[[', end: ']]', name: 'subroutine' },
{ start: '{{', end: '}}', name: 'hexagon' },
];
shapes.forEach((shape) => {
it.each(keywords)(`should handle %s keyword in ${shape.name} vertex`, function (keyword) {
const rest = flow.parser.parse(
`graph TD;A_${keyword}_node-->B${shape.start}This node has a ${keyword} as text${shape.end};`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe(`${shape.name}`);
expect(vert['B'].text).toBe(`This node has a ${keyword} as text`);
});
});
it.each(keywords)('should handle %s keyword in rect vertex', function (keyword) {
const rest = flow.parser.parse(
`graph TD;A_${keyword}_node-->B[|borders:lt|This node has a ${keyword} as text];`
);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe('rect');
expect(vert['B'].text).toBe(`This node has a ${keyword} as text`);
});
it('should handle edge case for odd vertex with node id ending with minus', function () {
const res = flow.parser.parse('graph TD;A_node-->odd->Vertex Text];');
const vert = flow.parser.yy.getVertices();
expect(vert['odd-'].type).toBe('odd');
expect(vert['odd-'].text).toBe('Vertex Text');
});
it('should allow forward slashes in lean_right vertices', function () {
const rest = flow.parser.parse(`graph TD;A_node-->B[/This node has a / as text/];`);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe('lean_right');
expect(vert['B'].text).toBe(`This node has a / as text`);
});
it('should allow back slashes in lean_left vertices', function () {
const rest = flow.parser.parse(`graph TD;A_node-->B[\\This node has a \\ as text\\];`);
const vert = flow.parser.yy.getVertices();
const edges = flow.parser.yy.getEdges();
expect(vert['B'].type).toBe('lean_left');
expect(vert['B'].text).toBe(`This node has a \\ as text`);
});
it('should handle åäö and minus', function () { it('should handle åäö and minus', function () {
const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};'); const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};');
@ -484,4 +573,33 @@ describe('[Text] when parsing', () => {
expect(vert['A'].text).toBe(',.?!+-*'); expect(vert['A'].text).toBe(',.?!+-*');
expect(edges[0].text).toBe(',.?!+-*'); expect(edges[0].text).toBe(',.?!+-*');
}); });
it('should throw error at nested set of brackets', function () {
const str = 'graph TD; A[This is a () in text];';
expect(() => flow.parser.parse(str)).toThrowError("got 'PS'");
});
it('should throw error for strings and text at the same time', function () {
const str = 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;';
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
});
it('should throw error for escaping quotes in text state', function () {
//prettier-ignore
const str = 'graph TD; A[This is a \"()\" in text];'; //eslint-disable-line no-useless-escape
expect(() => flow.parser.parse(str)).toThrowError("got 'STR'");
});
it('should throw error for nested quoatation marks', function () {
const str = 'graph TD; A["This is a "()" in text"];';
expect(() => flow.parser.parse(str)).toThrowError("Expecting 'SQE'");
});
it('should throw error', function () {
const str = `graph TD; node[hello ) world] --> works`;
expect(() => flow.parser.parse(str)).toThrowError("got 'PE'");
});
}); });

View File

@ -13,6 +13,12 @@
%x acc_descr_multiline %x acc_descr_multiline
%x dir %x dir
%x vertex %x vertex
%x text
%x ellipseText
%x trapText
%x edgeText
%x thickEdgeText
%x dottedEdgeText
%x click %x click
%x href %x href
%x callbackname %x callbackname
@ -36,28 +42,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multili
<acc_descr_multiline>[\}] { this.popState(); } <acc_descr_multiline>[\}] { this.popState(); }
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value"; <acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
// <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"} // <acc_descr_multiline>.*[^\n]* { return "acc_descr_line"}
["][`] { this.begin("md_string");}
<md_string>[^`"]+ { return "MD_STR";}
<md_string>[`]["] { this.popState();}
["] this.begin("string");
<string>["] this.popState();
<string>[^"]* return "STR";
"style" return 'STYLE';
"default" return 'DEFAULT';
"linkStyle" return 'LINKSTYLE';
"interpolate" return 'INTERPOLATE';
"classDef" return 'CLASSDEF';
"class" return 'CLASS';
/*
---interactivity command---
'href' adds a link to the specified node. 'href' can only be specified when the
line was introduced with 'click'.
'href "<link>"' attaches the specified link to the node that was specified by 'click'.
*/
"href"[\s]+["] this.begin("href");
<href>["] this.popState();
<href>[^"]* return 'HREF';
/* /*
---interactivity command--- ---interactivity command---
@ -74,6 +58,28 @@ Function arguments are optional: 'call <callbackname>()' simply executes 'callba
<callbackargs>\) this.popState(); <callbackargs>\) this.popState();
<callbackargs>[^)]* return 'CALLBACKARGS'; <callbackargs>[^)]* return 'CALLBACKARGS';
<md_string>[^`"]+ { return "MD_STR";}
<md_string>[`]["] { this.popState();}
<*>["][`] { this.begin("md_string");}
<string>[^"]+ return "STR";
<string>["] this.popState();
<*>["] this.pushState("string");
"style" return 'STYLE';
"default" return 'DEFAULT';
"linkStyle" return 'LINKSTYLE';
"interpolate" return 'INTERPOLATE';
"classDef" return 'CLASSDEF';
"class" return 'CLASS';
/*
---interactivity command---
'href' adds a link to the specified node. 'href' can only be specified when the
line was introduced with 'click'.
'href "<link>"' attaches the specified link to the node that was specified by 'click'.
*/
"href"[\s] return 'HREF';
/* /*
'click' is the keyword to introduce a line that contains interactivity commands. 'click' is the keyword to introduce a line that contains interactivity commands.
'click' must be followed by an existing node-id. All commands are attached to 'click' must be followed by an existing node-id. All commands are attached to
@ -112,7 +118,7 @@ that id.
.*direction\s+RL[^\n]* return 'direction_rl'; .*direction\s+RL[^\n]* return 'direction_rl';
.*direction\s+LR[^\n]* return 'direction_lr'; .*direction\s+LR[^\n]* return 'direction_lr';
[0-9]+ { return 'NUM';} [0-9]+ return 'NUM';
\# return 'BRKT'; \# return 'BRKT';
":::" return 'STYLE_SEPARATOR'; ":::" return 'STYLE_SEPARATOR';
":" return 'COLON'; ":" return 'COLON';
@ -120,42 +126,60 @@ that id.
";" return 'SEMI'; ";" return 'SEMI';
"," return 'COMMA'; "," return 'COMMA';
"*" return 'MULT'; "*" return 'MULT';
\s*[xo<]?\-\-+[-xo>]\s* return 'LINK';
\s*[xo<]?\=\=+[=xo>]\s* return 'LINK'; <INITIAL,edgeText>\s*[xo<]?\-\-+[-xo>]\s* { this.popState(); return 'LINK'; }
\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK'; <INITIAL>\s*[xo<]?\-\-\s* { this.pushState("edgeText"); return 'START_LINK'; }
\s*\~\~[\~]+\s* return 'LINK'; <edgeText>[^-]|\-(?!\-)+ return 'EDGE_TEXT';
\s*[xo<]?\-\-\s* return 'START_LINK';
\s*[xo<]?\=\=\s* return 'START_LINK'; <INITIAL,thickEdgeText>\s*[xo<]?\=\=+[=xo>]\s* { this.popState(); return 'LINK'; }
\s*[xo<]?\-\.\s* return 'START_LINK'; <INITIAL>\s*[xo<]?\=\=\s* { this.pushState("thickEdgeText"); return 'START_LINK'; }
"(-" return '(-'; <thickEdgeText>[^=]|\=(?!=) return 'EDGE_TEXT';
"-)" return '-)';
"([" return 'STADIUMSTART'; <INITIAL,dottedEdgeText>\s*[xo<]?\-?\.+\-[xo>]?\s* { this.popState(); return 'LINK'; }
"])" return 'STADIUMEND'; <INITIAL>\s*[xo<]?\-\.\s* { this.pushState("dottedEdgeText"); return 'START_LINK'; }
"[[" return 'SUBROUTINESTART'; <dottedEdgeText>[^\.]|\.(?!-) return 'EDGE_TEXT';
"]]" return 'SUBROUTINEEND';
"[|" return 'VERTEX_WITH_PROPS_START';
"[(" return 'CYLINDERSTART'; <*>\s*\~\~[\~]+\s* return 'LINK';
")]" return 'CYLINDEREND';
"(((" return 'DOUBLECIRCLESTART'; <ellipseText>[-/\)][\)] { this.popState(); return '-)'; }
")))" return 'DOUBLECIRCLEEND'; <ellipseText>[^\(\)\[\]\{\}]|-/!\)+ return "TEXT"
\- return 'MINUS'; <*>"(-" { this.pushState("ellipseText"); return '(-'; }
"." return 'DOT';
[\_] return 'UNDERSCORE'; <text>"])" { this.popState(); return 'STADIUMEND'; }
\+ return 'PLUS'; <*>"([" { this.pushState("text"); return 'STADIUMSTART'; }
\% return 'PCT';
"=" return 'EQUALS'; <text>"]]" { this.popState(); return 'SUBROUTINEEND'; }
\= return 'EQUALS'; <*>"[[" { this.pushState("text"); return 'SUBROUTINESTART'; }
"[|" { return 'VERTEX_WITH_PROPS_START'; }
\> { this.pushState("text"); return 'TAGEND'; }
<text>")]" { this.popState(); return 'CYLINDEREND'; }
<*>"[(" { this.pushState("text") ;return 'CYLINDERSTART'; }
<text>")))" { this.popState(); return 'DOUBLECIRCLEEND'; }
<*>"(((" { this.pushState("text"); return 'DOUBLECIRCLESTART'; }
<trapText>[\\(?=\])][\]] { this.popState(); return 'TRAPEND'; }
<trapText>\/(?=\])\] { this.popState(); return 'INVTRAPEND'; }
<trapText>\/(?!\])|\\(?!\])|[^\\\[\]\(\)\{\}\/]+ return 'TEXT';
<*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; }
<*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; }
"<" return 'TAGSTART'; "<" return 'TAGSTART';
">" return 'TAGEND'; ">" return 'TAGEND';
"^" return 'UP'; "^" return 'UP';
"\|" return 'SEP'; "\|" return 'SEP';
"v" return 'DOWN'; "v" return 'DOWN';
[A-Za-z]+ return 'ALPHA'; "*" return 'MULT';
"\\]" return 'TRAPEND'; "#" return 'BRKT';
"[/" return 'TRAPSTART'; "&" return 'AMP';
"/]" return 'INVTRAPEND'; ([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ return 'NODE_STRING';
"[\\" return 'INVTRAPSTART'; "-" return 'MINUS'
[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION';
[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|
[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|
[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| [\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|
@ -218,13 +242,20 @@ that id.
[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]| [\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|
[\uFFD2-\uFFD7\uFFDA-\uFFDC] [\uFFD2-\uFFD7\uFFDA-\uFFDC]
return 'UNICODE_TEXT'; return 'UNICODE_TEXT';
"|" return 'PIPE';
"(" return 'PS'; <text>"|" { this.popState(); return 'PIPE'; }
")" return 'PE'; <*>"|" { this.pushState("text"); return 'PIPE'; }
"[" return 'SQS';
"]" return 'SQE'; <text>")" { this.popState(); return 'PE'; }
"{" return 'DIAMOND_START' <*>"(" { this.pushState("text"); return 'PS'; }
"}" return 'DIAMOND_STOP'
<text>"]" { this.popState(); return 'SQE'; }
<*>"[" { this.pushState("text"); return 'SQS'; }
<text>(\}) { this.popState(); return 'DIAMOND_STOP' }
<*>"{" { this.pushState("text"); return 'DIAMOND_START' }
<text>[^\[\]\(\)\{\}\|\"]+ return "TEXT";
"\"" return 'QUOTE'; "\"" return 'QUOTE';
(\r?\n)+ return 'NEWLINE'; (\r?\n)+ return 'NEWLINE';
\s return 'SPACE'; \s return 'SPACE';
@ -255,11 +286,11 @@ openDirective
; ;
typeDirective typeDirective
: type_directive { yy.parseDirective($1, 'type_directive'); } : type_directive { yy.parseDirective($type_directive, 'type_directive'); }
; ;
argDirective argDirective
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); } : arg_directive { $arg_directive = $arg_directive.trim().replace(/'/g, '"'); yy.parseDirective($arg_directive, 'arg_directive'); }
; ;
closeDirective closeDirective
@ -275,15 +306,15 @@ document
{ $$ = [];} { $$ = [];}
| document line | document line
{ {
if(!Array.isArray($2) || $2.length > 0){ if(!Array.isArray($line) || $line.length > 0){
$1.push($2); $document.push($line);
} }
$$=$1;} $$=$document;}
; ;
line line
: statement : statement
{$$=$1;} {$$=$statement;}
| SEMI | SEMI
| NEWLINE | NEWLINE
| SPACE | SPACE
@ -296,15 +327,15 @@ graphConfig
| GRAPH NODIR | GRAPH NODIR
{ yy.setDirection('TB');$$ = 'TB';} { yy.setDirection('TB');$$ = 'TB';}
| GRAPH DIR FirstStmtSeperator | GRAPH DIR FirstStmtSeperator
{ yy.setDirection($2);$$ = $2;} { yy.setDirection($DIR);$$ = $DIR;}
// | GRAPH SPACE TAGEND FirstStmtSeperator // | GRAPH SPACE TAGEND FirstStmtSeperator
// { yy.setDirection("LR");$$ = $3;} // { yy.setDirection("LR");$$ = $TAGEND;}
// | GRAPH SPACE TAGSTART FirstStmtSeperator // | GRAPH SPACE TAGSTART FirstStmtSeperator
// { yy.setDirection("RL");$$ = $3;} // { yy.setDirection("RL");$$ = $TAGSTART;}
// | GRAPH SPACE UP FirstStmtSeperator // | GRAPH SPACE UP FirstStmtSeperator
// { yy.setDirection("BT");$$ = $3;} // { yy.setDirection("BT");$$ = $UP;}
// | GRAPH SPACE DOWN FirstStmtSeperator // | GRAPH SPACE DOWN FirstStmtSeperator
// { yy.setDirection("TB");$$ = $3;} // { yy.setDirection("TB");$$ = $DOWN;}
; ;
ending: endToken ending ending: endToken ending
@ -332,7 +363,7 @@ spaceList
statement statement
: verticeStatement separator : verticeStatement separator
{ /* console.warn('finat vs', $1.nodes); */ $$=$1.nodes} { /* console.warn('finat vs', $verticeStatement.nodes); */ $$=$verticeStatement.nodes}
| styleStatement separator | styleStatement separator
{$$=[];} {$$=[];}
| linkStyleStatement separator | linkStyleStatement separator
@ -343,110 +374,121 @@ statement
{$$=[];} {$$=[];}
| clickStatement separator | clickStatement separator
{$$=[];} {$$=[];}
| subgraph SPACE text SQS text SQE separator document end | subgraph SPACE textNoTags SQS text SQE separator document end
{$$=yy.addSubGraph($3,$8,$5);} {$$=yy.addSubGraph($textNoTags,$document,$text);}
| subgraph SPACE text separator document end | subgraph SPACE textNoTags separator document end
{$$=yy.addSubGraph($3,$5,$3);} {$$=yy.addSubGraph($textNoTags,$document,$textNoTags);}
// | subgraph SPACE text separator document end // | subgraph SPACE textNoTags separator document end
// {$$=yy.addSubGraph($3,$5,$3);} // {$$=yy.addSubGraph($textNoTags,$document,$textNoTags);}
| subgraph separator document end | subgraph separator document end
{$$=yy.addSubGraph(undefined,$3,undefined);} {$$=yy.addSubGraph(undefined,$document,undefined);}
| direction | direction
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } | acc_title acc_title_value { $$=$acc_title_value.trim();yy.setAccTitle($$); }
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } | acc_descr acc_descr_value { $$=$acc_descr_value.trim();yy.setAccDescription($$); }
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | acc_descr_multiline_value { $$=$acc_descr_multiline_value.trim();yy.setAccDescription($$); }
; ;
separator: NEWLINE | SEMI | EOF ; separator: NEWLINE | SEMI | EOF ;
verticeStatement: verticeStatement link node verticeStatement: verticeStatement link node
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } { /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } }
| verticeStatement link node spaceList | verticeStatement link node spaceList
{ /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } { /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } }
|node spaceList {/*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }} |node spaceList {/*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }}
|node { /*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }} |node { /*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }}
; ;
node: styledVertex node: styledVertex
{ /* console.warn('nod', $1); */ $$ = [$1];} { /* console.warn('nod', $styledVertex); */ $$ = [$styledVertex];}
| node spaceList AMP spaceList styledVertex | node spaceList AMP spaceList styledVertex
{ $$ = $1.concat($5); /* console.warn('pip', $1[0], $5, $$); */ } { $$ = $node.concat($styledVertex); /* console.warn('pip', $node[0], $styledVertex, $$); */ }
; ;
styledVertex: vertex styledVertex: vertex
{ /* console.warn('nod', $1); */ $$ = $1;} { /* console.warn('nod', $vertex); */ $$ = $vertex;}
| vertex STYLE_SEPARATOR idString | vertex STYLE_SEPARATOR idString
{$$ = $1;yy.setClass($1,$3)} {$$ = $vertex;yy.setClass($vertex,$idString)}
; ;
vertex: idString SQS text SQE vertex: idString SQS text SQE
{$$ = $1;yy.addVertex($1,$3,'square');} {$$ = $idString;yy.addVertex($idString,$text,'square');}
| idString DOUBLECIRCLESTART text DOUBLECIRCLEEND | idString DOUBLECIRCLESTART text DOUBLECIRCLEEND
{$$ = $1;yy.addVertex($1,$3,'doublecircle');} {$$ = $idString;yy.addVertex($idString,$text,'doublecircle');}
| idString PS PS text PE PE | idString PS PS text PE PE
{$$ = $1;yy.addVertex($1,$4,'circle');} {$$ = $idString;yy.addVertex($idString,$text,'circle');}
| idString '(-' text '-)' | idString '(-' text '-)'
{$$ = $1;yy.addVertex($1,$3,'ellipse');} {$$ = $idString;yy.addVertex($idString,$text,'ellipse');}
| idString STADIUMSTART text STADIUMEND | idString STADIUMSTART text STADIUMEND
{$$ = $1;yy.addVertex($1,$3,'stadium');} {$$ = $idString;yy.addVertex($idString,$text,'stadium');}
| idString SUBROUTINESTART text SUBROUTINEEND | idString SUBROUTINESTART text SUBROUTINEEND
{$$ = $1;yy.addVertex($1,$3,'subroutine');} {$$ = $idString;yy.addVertex($idString,$text,'subroutine');}
| idString VERTEX_WITH_PROPS_START ALPHA COLON ALPHA PIPE text SQE | idString VERTEX_WITH_PROPS_START NODE_STRING\[field] COLON NODE_STRING\[value] PIPE text SQE
{$$ = $1;yy.addVertex($1,$7,'rect',undefined,undefined,undefined, Object.fromEntries([[$3, $5]]));} {$$ = $idString;yy.addVertex($idString,$text,'rect',undefined,undefined,undefined, Object.fromEntries([[$field, $value]]));}
| idString CYLINDERSTART text CYLINDEREND | idString CYLINDERSTART text CYLINDEREND
{$$ = $1;yy.addVertex($1,$3,'cylinder');} {$$ = $idString;yy.addVertex($idString,$text,'cylinder');}
| idString PS text PE | idString PS text PE
{$$ = $1;yy.addVertex($1,$3,'round');} {$$ = $idString;yy.addVertex($idString,$text,'round');}
| idString DIAMOND_START text DIAMOND_STOP | idString DIAMOND_START text DIAMOND_STOP
{$$ = $1;yy.addVertex($1,$3,'diamond');} {$$ = $idString;yy.addVertex($idString,$text,'diamond');}
| idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP | idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP
{$$ = $1;yy.addVertex($1,$4,'hexagon');} {$$ = $idString;yy.addVertex($idString,$text,'hexagon');}
| idString TAGEND text SQE | idString TAGEND text SQE
{$$ = $1;yy.addVertex($1,$3,'odd');} {$$ = $idString;yy.addVertex($idString,$text,'odd');}
| idString TRAPSTART text TRAPEND | idString TRAPSTART text TRAPEND
{$$ = $1;yy.addVertex($1,$3,'trapezoid');} {$$ = $idString;yy.addVertex($idString,$text,'trapezoid');}
| idString INVTRAPSTART text INVTRAPEND | idString INVTRAPSTART text INVTRAPEND
{$$ = $1;yy.addVertex($1,$3,'inv_trapezoid');} {$$ = $idString;yy.addVertex($idString,$text,'inv_trapezoid');}
| idString TRAPSTART text INVTRAPEND | idString TRAPSTART text INVTRAPEND
{$$ = $1;yy.addVertex($1,$3,'lean_right');} {$$ = $idString;yy.addVertex($idString,$text,'lean_right');}
| idString INVTRAPSTART text TRAPEND | idString INVTRAPSTART text TRAPEND
{$$ = $1;yy.addVertex($1,$3,'lean_left');} {$$ = $idString;yy.addVertex($idString,$text,'lean_left');}
| idString | idString
{ /*console.warn('h: ', $1);*/$$ = $1;yy.addVertex($1);} { /*console.warn('h: ', $idString);*/$$ = $idString;yy.addVertex($idString);}
; ;
link: linkStatement arrowText link: linkStatement arrowText
{$1.text = $2;$$ = $1;} {$linkStatement.text = $arrowText;$$ = $linkStatement;}
| linkStatement TESTSTR SPACE | linkStatement TESTSTR SPACE
{$1.text = $2;$$ = $1;} {$linkStatement.text = $TESTSTR;$$ = $linkStatement;}
| linkStatement arrowText SPACE | linkStatement arrowText SPACE
{$1.text = $2;$$ = $1;} {$linkStatement.text = $arrowText;$$ = $linkStatement;}
| linkStatement | linkStatement
{$$ = $1;} {$$ = $linkStatement;}
| START_LINK text LINK | START_LINK edgeText LINK
{var inf = yy.destructLink($3, $1); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$2};} {var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText};}
; ;
edgeText: edgeTextToken
{$$={text:$edgeTextToken, type:'text'};}
| edgeText edgeTextToken
{$$={text:$edgeText.text+''+$edgeTextToken, type:$edgeText.type};}
|STR
{$$={text: $STR, type: 'string'};}
| MD_STR
{$$={text:$MD_STR, type:'markdown'};}
;
linkStatement: LINK linkStatement: LINK
{var inf = yy.destructLink($1);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};} {var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};}
; ;
arrowText: arrowText:
PIPE text PIPE PIPE text PIPE
{$$ = $2;} {$$ = $text;}
; ;
text: textToken text: textToken
{ $$={text:$1, type: 'text'};} { $$={text:$textToken, type: 'text'};}
| text textToken | text textToken
{ $$={text:$1.text+''+$2, type: $1.type};} { $$={text:$text.text+''+$textToken, type: $text.type};}
| STR | STR
{ $$={text: $1, type: 'text'};} { $$ = {text: $STR, type: 'string'};}
| MD_STR | MD_STR
{ $$={text: $1, type: 'markdown'};} { $$={text: $MD_STR, type: 'markdown'};}
; ;
@ -456,109 +498,104 @@ keywords
textNoTags: textNoTagsToken textNoTags: textNoTagsToken
{$$=$1;} {$$={text:$textNoTagsToken, type: 'text'};}
| textNoTags textNoTagsToken | textNoTags textNoTagsToken
{$$=$1+''+$2;} {$$={text:$textNoTags.text+''+$textNoTagsToken, type: $textNoTags.type};}
| STR
{ $$={text: $STR, type: 'text'};}
| MD_STR
{ $$={text: $MD_STR, type: 'markdown'};}
; ;
classDefStatement:CLASSDEF SPACE DEFAULT SPACE stylesOpt classDefStatement:CLASSDEF SPACE idString SPACE stylesOpt
{$$ = $1;yy.addClass($3,$5);} {$$ = $CLASSDEF;yy.addClass($idString,$stylesOpt);}
| CLASSDEF SPACE alphaNum SPACE stylesOpt
{$$ = $1;yy.addClass($3,$5);}
; ;
classStatement:CLASS SPACE alphaNum SPACE alphaNum classStatement:CLASS SPACE idString\[vertex] SPACE idString\[class]
{$$ = $1;yy.setClass($3, $5);} {$$ = $CLASS;yy.setClass($vertex, $class);}
; ;
clickStatement clickStatement
: CLICK CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $2);} : CLICK CALLBACKNAME {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);}
| CLICK CALLBACKNAME SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} | CLICK CALLBACKNAME SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);yy.setTooltip($CLICK, $STR);}
| CLICK CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $2, $3);} | CLICK CALLBACKNAME CALLBACKARGS {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);}
| CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setTooltip($1, $5);} | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);yy.setTooltip($CLICK, $STR);}
| CLICK HREF {$$ = $1;yy.setLink($1, $2);} | CLICK HREF STR {$$ = $CLICK;yy.setLink($CLICK, $STR);}
| CLICK HREF SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} | CLICK HREF STR SPACE STR {$$ = $CLICK;yy.setLink($CLICK, $STR1);yy.setTooltip($CLICK, $STR2);}
| CLICK HREF SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} | CLICK HREF STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);}
| CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} | CLICK HREF STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);}
| CLICK alphaNum {$$ = $1;yy.setClickEvent($1, $2);} | CLICK alphaNum {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);}
| CLICK alphaNum SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} | CLICK alphaNum SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);yy.setTooltip($CLICK, $STR);}
| CLICK STR {$$ = $1;yy.setLink($1, $2);} | CLICK STR {$$ = $CLICK;yy.setLink($CLICK, $STR);}
| CLICK STR SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} | CLICK STR\[link] SPACE STR\[tooltip] {$$ = $CLICK;yy.setLink($CLICK, $link);yy.setTooltip($CLICK, $tooltip);}
| CLICK STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} | CLICK STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);}
| CLICK STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} | CLICK STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);}
; ;
styleStatement:STYLE SPACE alphaNum SPACE stylesOpt styleStatement:STYLE SPACE idString SPACE stylesOpt
{$$ = $1;yy.addVertex($3,undefined,undefined,$5);} {$$ = $STYLE;yy.addVertex($idString,undefined,undefined,$stylesOpt);}
| STYLE SPACE HEX SPACE stylesOpt
{$$ = $1;yy.updateLink($3,$5);}
; ;
linkStyleStatement linkStyleStatement
: LINKSTYLE SPACE DEFAULT SPACE stylesOpt : LINKSTYLE SPACE DEFAULT SPACE stylesOpt
{$$ = $1;yy.updateLink([$3],$5);} {$$ = $LINKSTYLE;yy.updateLink([$DEFAULT],$stylesOpt);}
| LINKSTYLE SPACE numList SPACE stylesOpt | LINKSTYLE SPACE numList SPACE stylesOpt
{$$ = $1;yy.updateLink($3,$5);} {$$ = $LINKSTYLE;yy.updateLink($numList,$stylesOpt);}
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
{$$ = $1;yy.updateLinkInterpolate([$3],$7);yy.updateLink([$3],$9);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);yy.updateLink([$DEFAULT],$stylesOpt);}
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt | LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt
{$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);yy.updateLink($numList,$stylesOpt);}
| LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum
{$$ = $1;yy.updateLinkInterpolate([$3],$7);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);}
| LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum | LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum
{$$ = $1;yy.updateLinkInterpolate($3,$7);} {$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);}
; ;
numList: NUM numList: NUM
{$$ = [$1]} {$$ = [$NUM]}
| numList COMMA NUM | numList COMMA NUM
{$1.push($3);$$ = $1;} {$numList.push($NUM);$$ = $numList;}
; ;
stylesOpt: style stylesOpt: style
{$$ = [$1]} {$$ = [$style]}
| stylesOpt COMMA style | stylesOpt COMMA style
{$1.push($3);$$ = $1;} {$stylesOpt.push($style);$$ = $stylesOpt;}
; ;
style: styleComponent style: styleComponent
|style styleComponent |style styleComponent
{$$ = $1 + $2;} {$$ = $style + $styleComponent;}
; ;
styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ; styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ;
/* Token lists */ /* Token lists */
idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | AMP | BRKT | MULT | UNICODE_TEXT;
textToken : textNoTagsToken | TAGSTART | TAGEND | START_LINK | PCT | DEFAULT; textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT;
textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ; textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | AMP | UNICODE_TEXT | COLON | MULT | BRKT | keywords | START_LINK ;
edgeTextToken : EDGE_TEXT | UNICODE_TEXT ;
alphaNumToken : NUM | UNICODE_TEXT | NODE_STRING | DIR | DOWN | MINUS | COMMA | COLON | AMP | BRKT | MULT;
idString idString
:idStringToken :idStringToken
{$$=$1} {$$=$idStringToken}
| idString idStringToken | idString idStringToken
{$$=$1+''+$2} {$$=$idString+''+$idStringToken}
; ;
alphaNum alphaNum
: alphaNumStatement : alphaNumToken
{$$=$1;} {$$=$alphaNumToken;}
| alphaNum alphaNumStatement | alphaNum alphaNumToken
{$$=$1+''+$2;} {$$=$alphaNum+''+$alphaNumToken;}
; ;
alphaNumStatement
: DIR
{$$=$1;}
| alphaNumToken
{$$=$1;}
| DOWN
{$$='v';}
| MINUS
{$$='-';}
;
direction direction
: direction_tb : direction_tb
@ -571,9 +608,4 @@ direction
{ $$={stmt:'dir', value:'LR'};} { $$={stmt:'dir', value:'LR'};}
; ;
alphaNumToken : PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ;
idStringToken : ALPHA|UNDERSCORE |UNICODE_TEXT | NUM| COLON | COMMA | PLUS | MINUS | DOWN |EQUALS | MULT | BRKT | DOT | PUNCTUATION | AMP | DEFAULT;
graphCodeTokens: STADIUMSTART | STADIUMEND | SUBROUTINESTART | SUBROUTINEEND | VERTEX_WITH_PROPS_START | CYLINDERSTART | CYLINDEREND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI;
%% %%

View File

@ -390,7 +390,7 @@ It is possible to escape characters using the syntax exemplified here.
```mermaid-example ```mermaid-example
flowchart LR flowchart LR
A["A double quote:#quot;"] -->B["A dec char:#9829;"] A["A double quote:#quot;"] --> B["A dec char:#9829;"]
``` ```
Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names.