mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-19 15:30:03 +02:00
Updated syntax and fixed comments from review
This commit is contained in:
@@ -1,36 +1,55 @@
|
||||
/** mermaid */
|
||||
%lex
|
||||
TOKEN \w+
|
||||
NUM \d+(.\d+)?
|
||||
|
||||
%options case-insensitive
|
||||
%options easy_keyword_rules
|
||||
%options easy_keword_rules
|
||||
|
||||
%s link_value
|
||||
|
||||
// when we are inside [] section we are defining attrubutes
|
||||
%x attributes
|
||||
// or if we use "" we are expecting a string containing value
|
||||
%x attr_value
|
||||
%x string
|
||||
%x value
|
||||
|
||||
%%
|
||||
//--------------------------------------------------------------
|
||||
// skip all whitespace EXCEPT newlines, but not within a string
|
||||
<INITIAL,attributes,value>[^\S\r\n]+ {}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// main
|
||||
<INITIAL,link_value,attributes,attr_value>[^\S\r\n]+ {}
|
||||
|
||||
//--------------
|
||||
// basic tokens
|
||||
//--------------
|
||||
|
||||
(<<EOF>>|[\n;])+ { return 'EOS'; } // end of statement is semicolon ; new line \n or end of file
|
||||
"sankey" { return 'SANKEY'; }
|
||||
\d+(.\d+)? { return 'AMOUNT'; }
|
||||
"->" { return 'ARROW'; }
|
||||
\w+ { return 'NODE'; }
|
||||
(?:<<EOF>>|[\n;])+ { return 'EOS'; } // end of statement is semicolon ; new line \n or end of file
|
||||
<INITIAL>{TOKEN} { return 'NODE_ID'; }
|
||||
<link_value>{NUM} { return 'AMOUNT'; }
|
||||
"->" {
|
||||
if(this.topState()!=='link_value') this.pushState('link_value');
|
||||
else this.popState();
|
||||
return 'ARROW';
|
||||
}
|
||||
//------------
|
||||
// attributes
|
||||
//------------
|
||||
|
||||
"[" { this.pushState('attributes'); return 'OPEN_ATTRIBUTES'; }
|
||||
<attributes>"]" { this.popState(); return 'CLOSE_ATTRIBUTES'; }
|
||||
<attributes>\w+ { return 'ATTRIBUTE'; }
|
||||
<attributes>\= { this.pushState('value'); return 'EQUAL'; }
|
||||
<value>\w+ { this.popState(); return 'VALUE'; }
|
||||
<attributes>{TOKEN} { return 'ATTRIBUTE'; }
|
||||
<attributes>\= { this.pushState('attr_value'); return 'EQUAL'; }
|
||||
<attr_value>{TOKEN} { this.popState(); return 'VALUE'; }
|
||||
|
||||
//------------
|
||||
// strings
|
||||
<INITIAL,attributes,value>\" { this.pushState('string'); return 'OPEN_STRING'; }
|
||||
//------------
|
||||
|
||||
<INITIAL,attributes,attr_value>\" { this.pushState('string'); return 'OPEN_STRING'; }
|
||||
<string>(?!\\)\" {
|
||||
if(this.topState()==='string') this.popState();
|
||||
if(this.topState()==='value') this.popState();
|
||||
if(this.topState()==='attr_value') this.popState();
|
||||
return 'CLOSE_STRING';
|
||||
}
|
||||
<string>([^"\\]|\\\"|\\\\)+ { return 'STRING'; }
|
||||
@@ -53,8 +72,8 @@ document
|
||||
;
|
||||
|
||||
line
|
||||
: stream optional_attributes EOS
|
||||
| node optional_attributes EOS
|
||||
: node optional_attributes EOS
|
||||
| stream optional_attributes EOS
|
||||
| EOS
|
||||
;
|
||||
|
||||
@@ -65,20 +84,22 @@ attribute: ATTRIBUTE EQUAL value | ATTRIBUTE;
|
||||
|
||||
value: VALUE | OPEN_STRING STRING CLOSE_STRING;
|
||||
|
||||
stream: node[source] ARROW amount ARROW tail[target] {
|
||||
stream
|
||||
: node\[source] ARROW amount ARROW tail\[target] {
|
||||
$$=$source;
|
||||
yy.addLink($source, $target, $amount);
|
||||
};
|
||||
|
||||
amount: AMOUNT { $$=parseFloat($AMOUNT); };
|
||||
}
|
||||
;
|
||||
|
||||
tail
|
||||
: stream { $$ = $stream }
|
||||
| node { $$ = $node; }
|
||||
;
|
||||
|
||||
amount: AMOUNT { $$=parseFloat($AMOUNT); };
|
||||
|
||||
node
|
||||
: NODE { $$ = yy.addNode($NODE); }
|
||||
| OPEN_STRING STRING[title] CLOSE_STRING { $$ = yy.addNode($title); /* TODO: add title and id separately?*/ }
|
||||
: NODE_ID { $$ = yy.findOrCreateNode($NODE_ID); }
|
||||
| OPEN_STRING STRING\[node_label] CLOSE_STRING { $$ = yy.findOrCreateNode($node_label); }
|
||||
;
|
||||
|
||||
|
@@ -50,6 +50,15 @@ describe('Sankey diagram', function () {
|
||||
});
|
||||
|
||||
describe('while attributes parsing', () => {
|
||||
it('recognized node and attribute ids starting with numbers', () => {
|
||||
const str = `
|
||||
sankey
|
||||
1st -> 200 -> 2nd -> 180 -> 3rd;
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
});
|
||||
|
||||
it('parses different quotless variations', () => {
|
||||
const str = `
|
||||
sankey
|
||||
@@ -149,6 +158,7 @@ describe('Sankey diagram', function () {
|
||||
"Wave" -> 19.013 -> "Electricity grid"
|
||||
"Wind" -> 289.366 -> "Electricity grid"
|
||||
`;
|
||||
|
||||
parser.parse(str);
|
||||
});
|
||||
});
|
||||
|
@@ -12,16 +12,13 @@ import {
|
||||
clear as commonClear,
|
||||
} from '../../commonDb.js';
|
||||
|
||||
// export const parseDirective = function (statement, context, type) {
|
||||
// mermaidAPI.parseDirective(this, statement, context, type);
|
||||
// };
|
||||
|
||||
// export const cleanupComments = (text: string): string => {
|
||||
// return text.trimStart().replace(/^\s*%%(?!{)[^\n]+\n?/gm, '');
|
||||
// };
|
||||
let links: Array<Link> = [];
|
||||
let nodes: Array<Node> = [];
|
||||
let nodesHash: Record<string, Node> = {};
|
||||
// Variables where graph data is stored
|
||||
// Sankey diagram represented by nodes and links between those nodes
|
||||
// We have to track nodes uniqueness (by ID), thats why we need hash also
|
||||
//
|
||||
let links: Array<SankeyLink> = [];
|
||||
let nodes: Array<SankeyNode> = [];
|
||||
let nodesHash: Record<string, SankeyNode> = {};
|
||||
|
||||
const clear = function () {
|
||||
links = [];
|
||||
@@ -30,71 +27,35 @@ const clear = function () {
|
||||
commonClear();
|
||||
};
|
||||
|
||||
type Nullable<T> = T | null;
|
||||
|
||||
// interface ILink {
|
||||
// source?: Node;
|
||||
// target?: Node;
|
||||
// value?: number;
|
||||
// }
|
||||
|
||||
class Link {
|
||||
source: Nullable<Node>;
|
||||
target: Nullable<Node>;
|
||||
value: Nullable<number>;
|
||||
constructor() {
|
||||
this.source = null;
|
||||
this.target = null;
|
||||
this.value = 0;
|
||||
}
|
||||
class SankeyLink {
|
||||
constructor(public source: SankeyNode, public target: SankeyNode, public value: number = 0) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a link between two elements on the diagram
|
||||
*
|
||||
* @param source - Node where the link starts
|
||||
* @param target - Node where the link ends
|
||||
* @param value - number, float or integer, describes the amount to be passed
|
||||
*/
|
||||
// const addLink = ({ source, target, amount }: ILink = {}): Link => {
|
||||
const addLink = function (source?: Node, target?: Node, value?: number): Link {
|
||||
const link: Link = new Link();
|
||||
|
||||
// TODO: make attribute setters
|
||||
if (source !== undefined) {
|
||||
link.source = source;
|
||||
}
|
||||
if (target !== undefined) {
|
||||
link.target = target;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
link.value = value;
|
||||
}
|
||||
const addLink = function (source: SankeyNode, target: SankeyNode, value: number): SankeyLink {
|
||||
const link: SankeyLink = new SankeyLink(source, target, value);
|
||||
|
||||
links.push(link);
|
||||
|
||||
return link;
|
||||
};
|
||||
|
||||
class Node {
|
||||
ID: string;
|
||||
title: string;
|
||||
constructor(ID: string, title: string = ID) {
|
||||
this.ID = ID;
|
||||
this.title = title;
|
||||
}
|
||||
class SankeyNode {
|
||||
constructor(public ID: string, public label: string = ID) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds or creates a new Node by ID
|
||||
*
|
||||
* @param id - The id Node
|
||||
* @param ID - The id of the node
|
||||
*/
|
||||
const addNode = function (ID: string): Node {
|
||||
const findOrCreateNode = function (ID: string): SankeyNode {
|
||||
ID = common.sanitizeText(ID, configApi.getConfig());
|
||||
let node: Node;
|
||||
let node: SankeyNode;
|
||||
if (nodesHash[ID] === undefined) {
|
||||
node = new Node(ID);
|
||||
node = new SankeyNode(ID);
|
||||
nodesHash[ID] = node;
|
||||
nodes.push(node);
|
||||
} else {
|
||||
@@ -103,16 +64,27 @@ const addNode = function (ID: string): Node {
|
||||
return node;
|
||||
};
|
||||
|
||||
// TODO: this will be better using getters in typescript
|
||||
const getNodes = () => nodes;
|
||||
const getLinks = () => links;
|
||||
|
||||
const getGraph = () => ({
|
||||
nodes: nodes.map((node) => ({ id: node.ID, label: node.label })),
|
||||
links: links.map((link) => ({
|
||||
source: link.source.ID,
|
||||
target: link.target.ID,
|
||||
value: link.value,
|
||||
})),
|
||||
});
|
||||
|
||||
export default {
|
||||
nodesHash,
|
||||
getConfig: () => configApi.getConfig().sankey,
|
||||
getNodes,
|
||||
getLinks,
|
||||
getGraph,
|
||||
addLink,
|
||||
addNode,
|
||||
findOrCreateNode,
|
||||
// TODO: If this is a must this probably should be an interface
|
||||
getAccTitle,
|
||||
setAccTitle,
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { DiagramDefinition } from '../../diagram-api/types.js';
|
||||
// @ts-ignore: TODO Fix ts errors
|
||||
// @ts-ignore: jison doesn't export types
|
||||
import parser from './parser/sankey.jison';
|
||||
import db from './sankeyDB.js';
|
||||
import styles from './styles.js';
|
||||
|
@@ -2,6 +2,7 @@
|
||||
import { Diagram } from '../../Diagram.js';
|
||||
// import { log } from '../../logger.js';
|
||||
import * as configApi from '../../config.js';
|
||||
|
||||
import {
|
||||
select as d3select,
|
||||
scaleOrdinal as d3scaleOrdinal,
|
||||
@@ -19,9 +20,7 @@ import {
|
||||
sankeyJustify as d3SankeyJustify,
|
||||
} from 'd3-sankey';
|
||||
import { configureSvgSize } from '../../setupGraphViewbox.js';
|
||||
import sankeyDB from './sankeyDB.js';
|
||||
import { db } from '../info/infoDb.js';
|
||||
import { debug } from 'console';
|
||||
// import { debug } from 'console';
|
||||
|
||||
/**
|
||||
* Draws a sequenceDiagram in the tag with id: id based on the graph definition in text.
|
||||
@@ -51,7 +50,7 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
||||
const { securityLevel, sequence: conf } = configApi.getConfig();
|
||||
let sandboxElement;
|
||||
if (securityLevel === 'sandbox') {
|
||||
sandboxElement = select('#i' + id);
|
||||
sandboxElement = d3select('#i' + id);
|
||||
}
|
||||
const root =
|
||||
securityLevel === 'sandbox'
|
||||
@@ -86,35 +85,8 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
||||
// ]
|
||||
// };
|
||||
//
|
||||
const graph = {
|
||||
nodes: [],
|
||||
links: [],
|
||||
};
|
||||
|
||||
diagObj.db.getNodes().forEach((node) => {
|
||||
graph.nodes.push({ id: node.ID, title: node.title });
|
||||
});
|
||||
|
||||
diagObj.db.getLinks().forEach((link) => {
|
||||
graph.links.push({ source: link.source.ID, target: link.target.ID, value: link.value });
|
||||
});
|
||||
|
||||
// debugger;
|
||||
// const graph = {
|
||||
// nodes: [
|
||||
// { id: 'Alice' },
|
||||
// { id: 'Bob' },
|
||||
// { id: 'Carol' },
|
||||
// { id: 'Andrew' },
|
||||
// { id: 'Peter' }
|
||||
// ],
|
||||
// links: [
|
||||
// { source: 'Alice', target: 'Andrew', value: 11 },
|
||||
// { source: 'Alice', target: 'Bob', value: 23 },
|
||||
// { source: 'Bob', target: 'Carol', value: 43 },
|
||||
// { source: 'Peter', target: 'Carol', value: 15 },
|
||||
// ],
|
||||
// };
|
||||
const graph = diagObj.db.getGraph();
|
||||
|
||||
// Construct and configure a Sankey generator
|
||||
// That will be a function that calculates nodes and links dimensions
|
||||
@@ -145,11 +117,10 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
||||
// Get color scheme for the graph
|
||||
const color = d3scaleOrdinal(d3schemeTableau10);
|
||||
|
||||
// Creates the groups for nodes
|
||||
// Create groups for nodes
|
||||
svg
|
||||
.append('g')
|
||||
.attr('class', 'nodes')
|
||||
.attr('stroke', '#000')
|
||||
.selectAll('.node')
|
||||
.data(graph.nodes)
|
||||
.join('g')
|
||||
@@ -166,7 +137,7 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
||||
.attr('width', (d) => d.x1 - d.x0)
|
||||
.attr('fill', (d) => color(d.id));
|
||||
|
||||
// Create text for nodes
|
||||
// Create labels for nodes
|
||||
svg
|
||||
.append('g')
|
||||
.attr('class', 'node-labels')
|
||||
@@ -179,7 +150,7 @@ export const draw = function (text: string, id: string, _version: string, diagOb
|
||||
.attr('y', (d) => (d.y1 + d.y0) / 2)
|
||||
.attr('dy', '0.35em')
|
||||
.attr('text-anchor', (d) => (d.x0 < width / 2 ? 'start' : 'end'))
|
||||
.text((d) => d.title);
|
||||
.text((d) => d.label);
|
||||
|
||||
// Creates the paths that represent the links.
|
||||
const link_g = svg
|
||||
|
Reference in New Issue
Block a user