mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-08 01:56:42 +02:00
chore: Pie to chevrotain
This commit is contained in:
@@ -30,8 +30,7 @@ import infoDb from '../diagrams/info/infoDb';
|
||||
import infoRenderer from '../diagrams/info/infoRenderer';
|
||||
import infoParser from '../diagrams/info/infoParser';
|
||||
import infoStyles from '../diagrams/info/styles';
|
||||
// @ts-ignore
|
||||
import pieParser from '../diagrams/pie/parser/pie';
|
||||
import pieParser from '../diagrams/pie/pieParser';
|
||||
import pieDb from '../diagrams/pie/pieDb';
|
||||
import pieRenderer from '../diagrams/pie/pieRenderer';
|
||||
import pieStyles from '../diagrams/pie/styles';
|
||||
|
@@ -1,106 +0,0 @@
|
||||
/** mermaid
|
||||
* https://knsv.github.io/mermaid
|
||||
* (c) 2015 Knut Sveidqvist
|
||||
* MIT license.
|
||||
*/
|
||||
%lex
|
||||
%options case-insensitive
|
||||
|
||||
%x string
|
||||
%x title
|
||||
%x open_directive
|
||||
%x type_directive
|
||||
%x arg_directive
|
||||
%x close_directive
|
||||
%x acc_title
|
||||
%x acc_descr
|
||||
%x acc_descr_multiline
|
||||
%%
|
||||
\%\%\{ { this.begin('open_directive'); return 'open_directive'; }
|
||||
<open_directive>((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; }
|
||||
<type_directive>":" { this.popState(); this.begin('arg_directive'); return ':'; }
|
||||
<type_directive,arg_directive>\}\%\% { this.popState(); this.popState(); return 'close_directive'; }
|
||||
<arg_directive>((?:(?!\}\%\%).|\n)*) return 'arg_directive';
|
||||
\%\%(?!\{)[^\n]* /* skip comments */
|
||||
[^\}]\%\%[^\n]* /* skip comments */{ /*console.log('');*/ }
|
||||
[\n\r]+ return 'NEWLINE';
|
||||
\%\%[^\n]* /* do nothing */
|
||||
[\s]+ /* ignore */
|
||||
title { this.begin("title");return 'title'; }
|
||||
<title>(?!\n|;|#)*[^\n]* { this.popState(); return "title_value"; }
|
||||
|
||||
accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; }
|
||||
<acc_title>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; }
|
||||
accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; }
|
||||
<acc_descr>(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; }
|
||||
accDescr\s*"{"\s* { this.begin("acc_descr_multiline");}
|
||||
<acc_descr_multiline>[\}] { this.popState(); }
|
||||
<acc_descr_multiline>[^\}]* return "acc_descr_multiline_value";
|
||||
["] { this.begin("string"); }
|
||||
<string>["] { this.popState(); }
|
||||
<string>[^"]* { return "txt"; }
|
||||
"pie" return 'PIE';
|
||||
"showData" return 'showData';
|
||||
":"[\s]*[\d]+(?:\.[\d]+)? return "value";
|
||||
<<EOF>> return 'EOF';
|
||||
|
||||
/lex
|
||||
|
||||
%start start
|
||||
|
||||
%% /* language grammar */
|
||||
|
||||
start
|
||||
: eol start
|
||||
| directive start
|
||||
| PIE document
|
||||
| PIE showData document {yy.setShowData(true);}
|
||||
;
|
||||
|
||||
document
|
||||
: /* empty */
|
||||
| document line
|
||||
;
|
||||
|
||||
line
|
||||
: statement eol { $$ = $1 }
|
||||
;
|
||||
|
||||
statement
|
||||
:
|
||||
| txt value { yy.addSection($1,yy.cleanupValue($2)); }
|
||||
| title title_value { $$=$2.trim();yy.setDiagramTitle($$); }
|
||||
| acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); }
|
||||
| acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); }
|
||||
| acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } | section {yy.addSection($1.substr(8));$$=$1.substr(8);}
|
||||
| directive
|
||||
;
|
||||
|
||||
directive
|
||||
: openDirective typeDirective closeDirective
|
||||
| openDirective typeDirective ':' argDirective closeDirective
|
||||
;
|
||||
|
||||
eol
|
||||
: NEWLINE
|
||||
| ';'
|
||||
| EOF
|
||||
;
|
||||
|
||||
openDirective
|
||||
: open_directive { yy.parseDirective('%%{', 'open_directive'); }
|
||||
;
|
||||
|
||||
typeDirective
|
||||
: type_directive { yy.parseDirective($1, 'type_directive'); }
|
||||
;
|
||||
|
||||
argDirective
|
||||
: arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); }
|
||||
;
|
||||
|
||||
closeDirective
|
||||
: close_directive { yy.parseDirective('}%%', 'close_directive', 'pie'); }
|
||||
;
|
||||
|
||||
%%
|
@@ -1,6 +1,6 @@
|
||||
import pieDb from '../pieDb';
|
||||
import pie from './pie';
|
||||
import { setConfig } from '../../../config';
|
||||
import pieDb from './pieDb';
|
||||
import pie from './pieParser';
|
||||
import { setConfig } from '../../config';
|
||||
|
||||
setConfig({
|
||||
securityLevel: 'strict',
|
||||
@@ -8,19 +8,18 @@ setConfig({
|
||||
|
||||
describe('when parsing pie', function () {
|
||||
beforeEach(function () {
|
||||
pie.parser.yy = pieDb;
|
||||
pie.parser.yy.clear();
|
||||
pieDb.clear();
|
||||
});
|
||||
it('should handle very simple pie', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
it('should handle simple pie', function () {
|
||||
pie.parser.parse(`pie
|
||||
"ash" : 100
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(100);
|
||||
});
|
||||
it('should handle simple pie', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
it('should handle pie', function () {
|
||||
pie.parser.parse(`pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
@@ -28,33 +27,33 @@ describe('when parsing pie', function () {
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
it('should handle simple pie with comments', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
%% comments
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
it('should handle pie with comments', function () {
|
||||
pie.parser.parse(`pie
|
||||
%% comments
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
|
||||
it('should handle simple pie with a directive', function () {
|
||||
const res = pie.parser.parse(`%%{init: {'logLevel':0}}%%
|
||||
pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
it('should handle pie with a directive', function () {
|
||||
pie.parser.parse(`%%{init: {'logLevel':0}}%%
|
||||
pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
});
|
||||
|
||||
it('should handle simple pie with a title', function () {
|
||||
const res = pie.parser.parse(`pie title a 60/40 pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
it('should handle pie with a title', function () {
|
||||
pie.parser.parse(`pie title a 60/40 pie
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
const section1 = sections['ash'];
|
||||
@@ -62,27 +61,25 @@ pie
|
||||
expect(title).toBe('a 60/40 pie');
|
||||
});
|
||||
|
||||
it('should handle simple pie without an acc description (accDescr)', function () {
|
||||
const res = pie.parser.parse(`pie title a neat chart
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
it('should handle pie with an acc title (accTitle)', function () {
|
||||
pie.parser.parse(`pie title a neat chart
|
||||
accTitle: Hello World
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
const description = pieDb.getAccDescription();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60);
|
||||
expect(title).toBe('a neat chart');
|
||||
const title = pieDb.getAccTitle();
|
||||
expect(title).toBe('Hello World');
|
||||
expect(description).toBe('');
|
||||
});
|
||||
|
||||
it('should handle simple pie with an acc description (accDescr)', function () {
|
||||
const res = pie.parser.parse(`pie title a neat chart
|
||||
accDescr: a neat description
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
it('should handle pie with an acc description (accDescr)', function () {
|
||||
pie.parser.parse(`pie title a neat chart
|
||||
accDescr: a neat description
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
@@ -92,15 +89,18 @@ pie
|
||||
expect(title).toBe('a neat chart');
|
||||
expect(description).toBe('a neat description');
|
||||
});
|
||||
it('should handle simple pie with a multiline acc description (accDescr)', function () {
|
||||
const res = pie.parser.parse(`pie title a neat chart
|
||||
accDescr {
|
||||
a neat description
|
||||
on multiple lines
|
||||
}
|
||||
"ash" : 60
|
||||
"bat" : 40
|
||||
`);
|
||||
|
||||
it('should handle pie with a multiline acc description (accDescr)', function () {
|
||||
pie.parser.parse(`pie title a neat chart
|
||||
accDescr {
|
||||
a neat description
|
||||
on multiple lines
|
||||
}
|
||||
"ash" : 60
|
||||
|
||||
"bat" : 40
|
||||
|
||||
`);
|
||||
|
||||
const sections = pieDb.getSections();
|
||||
const title = pieDb.getDiagramTitle();
|
||||
@@ -111,22 +111,31 @@ pie
|
||||
expect(description).toBe('a neat description\non multiple lines');
|
||||
});
|
||||
|
||||
it('should handle simple pie with positive decimal', function () {
|
||||
const res = pie.parser.parse(`pie
|
||||
"ash" : 60.67
|
||||
"bat" : 40
|
||||
`);
|
||||
it('should handle pie with positive decimal', function () {
|
||||
pie.parser.parse(`pie
|
||||
"ash" : 60.67
|
||||
"bat" : 40
|
||||
`);
|
||||
const sections = pieDb.getSections();
|
||||
const section1 = sections['ash'];
|
||||
expect(section1).toBe(60.67);
|
||||
});
|
||||
|
||||
it('should handle simple pie with negative decimal', function () {
|
||||
it('should handle pie with invalid decimal', function () {
|
||||
expect(() => {
|
||||
pie.parser.parse(`pie
|
||||
"ash" : 60.67
|
||||
"bat" : 40..12
|
||||
`);
|
||||
"ash" : 60.67
|
||||
"bat" : 40..12
|
||||
`);
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
it('should handle pie with negative decimal', function () {
|
||||
expect(() => {
|
||||
pie.parser.parse(`pie
|
||||
"ash" : 60.67
|
||||
"bat" : -40.12
|
||||
`);
|
||||
}).toThrowError();
|
||||
});
|
||||
});
|
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import { log } from '../../logger';
|
||||
import mermaidAPI from '../../mermaidAPI';
|
||||
import * as configApi from '../../config';
|
||||
@@ -13,8 +14,6 @@ import {
|
||||
} from '../../commonDb';
|
||||
|
||||
let sections = {};
|
||||
let title = '';
|
||||
let description = '';
|
||||
let showData = false;
|
||||
|
||||
export const parseDirective = function (statement, context, type) {
|
||||
@@ -25,7 +24,7 @@ const addSection = function (id, value) {
|
||||
id = common.sanitizeText(id, configApi.getConfig());
|
||||
if (typeof sections[id] === 'undefined') {
|
||||
sections[id] = value;
|
||||
log.debug('Added new section :', id);
|
||||
log.debug('Added new section : ', id, ' with value:', value);
|
||||
}
|
||||
};
|
||||
const getSections = () => sections;
|
||||
@@ -49,7 +48,6 @@ const cleanupValue = function (value) {
|
||||
|
||||
const clear = function () {
|
||||
sections = {};
|
||||
title = '';
|
||||
showData = false;
|
||||
commonClear();
|
||||
};
|
168
src/diagrams/pie/pieParser.ts
Normal file
168
src/diagrams/pie/pieParser.ts
Normal file
@@ -0,0 +1,168 @@
|
||||
import { createToken, EmbeddedActionsParser, Lexer } from 'chevrotain';
|
||||
import { log } from '../../logger';
|
||||
import pieDb from './pieDb';
|
||||
|
||||
const NewLine = createToken({
|
||||
name: 'NewLine',
|
||||
pattern: /\r?\n/,
|
||||
});
|
||||
const WhiteSpace = createToken({
|
||||
name: 'WhiteSpace',
|
||||
pattern: /\s+/,
|
||||
group: Lexer.SKIPPED,
|
||||
});
|
||||
|
||||
const Colon = createToken({ name: 'Colon', pattern: /:/ });
|
||||
const Text = createToken({ name: 'Text', pattern: /[^\n\r"]+/ });
|
||||
const StringLiteral = createToken({
|
||||
name: 'StringLiteral',
|
||||
pattern: /"(:?[^\\"]|\\(:?[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/,
|
||||
});
|
||||
const NumberLiteral = createToken({
|
||||
name: 'NumberLiteral',
|
||||
pattern: /(0|[1-9]\d*)(\.\d+)?([eE][+-]?\d+)?/,
|
||||
});
|
||||
// TODO: Fix
|
||||
const Comment = createToken({
|
||||
name: 'Comment',
|
||||
pattern: /%%.*\n/,
|
||||
group: Lexer.SKIPPED,
|
||||
});
|
||||
|
||||
const Pie = createToken({ name: 'Pie', pattern: /pie/i });
|
||||
const ShowData = createToken({ name: 'ShowData', pattern: /showData/i });
|
||||
const Title = createToken({ name: 'Title', pattern: /title/i });
|
||||
const AccDescription = createToken({ name: 'AccDescription', pattern: /accDescr/i });
|
||||
const AccTitle = createToken({ name: 'AccTitle', pattern: /accTitle/i });
|
||||
const LeftCurly = createToken({ name: 'LeftCurly', pattern: /{/ });
|
||||
const RightCurly = createToken({ name: 'RightCurly', pattern: /}/ });
|
||||
// TODO: Figure out ordering of tokens
|
||||
const allTokens = [
|
||||
NewLine,
|
||||
WhiteSpace,
|
||||
Colon,
|
||||
LeftCurly,
|
||||
RightCurly,
|
||||
Comment,
|
||||
// Keywords
|
||||
Pie,
|
||||
ShowData,
|
||||
Title,
|
||||
AccTitle,
|
||||
AccDescription,
|
||||
// Literals
|
||||
NumberLiteral,
|
||||
StringLiteral,
|
||||
Text,
|
||||
];
|
||||
const PieLexer = new Lexer(allTokens);
|
||||
|
||||
class PieParser extends EmbeddedActionsParser {
|
||||
constructor() {
|
||||
super(allTokens);
|
||||
this.performSelfAnalysis();
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
super.reset();
|
||||
pieDb.clear();
|
||||
}
|
||||
|
||||
public diagram = this.RULE('diagram', () => {
|
||||
this.SUBRULE(this.header);
|
||||
this.OPTION(() => {
|
||||
this.SUBRULE(this.accTitle);
|
||||
this.CONSUME(NewLine);
|
||||
});
|
||||
this.OPTION2(() => {
|
||||
this.OR([
|
||||
{ ALT: () => this.SUBRULE(this.accDescriptionSingleLine) },
|
||||
{ ALT: () => this.SUBRULE(this.accDescriptionMultiLine) },
|
||||
]);
|
||||
this.CONSUME2(NewLine);
|
||||
});
|
||||
this.AT_LEAST_ONE(() => {
|
||||
this.SUBRULE2(this.row);
|
||||
});
|
||||
});
|
||||
|
||||
public header = this.RULE('header', () => {
|
||||
this.CONSUME(Pie);
|
||||
this.OPTION(() => {
|
||||
this.CONSUME(ShowData);
|
||||
this.ACTION(() => pieDb.setShowData(true));
|
||||
});
|
||||
this.OPTION2(() => {
|
||||
this.SUBRULE(this.title);
|
||||
});
|
||||
this.CONSUME(NewLine);
|
||||
});
|
||||
|
||||
public title = this.RULE('title', () => {
|
||||
this.CONSUME(Title);
|
||||
const titleText = this.CONSUME(Text).image;
|
||||
this.ACTION(() => pieDb.setDiagramTitle(titleText));
|
||||
});
|
||||
|
||||
public accTitle = this.RULE('accTitle', () => {
|
||||
this.CONSUME(AccTitle);
|
||||
this.CONSUME(Colon);
|
||||
const accTitleText = this.CONSUME(Text).image;
|
||||
this.ACTION(() => pieDb.setAccTitle(accTitleText));
|
||||
});
|
||||
|
||||
public accDescriptionSingleLine = this.RULE('accDescriptionSingleLine', () => {
|
||||
this.CONSUME(AccDescription);
|
||||
this.CONSUME(Colon);
|
||||
const accDescrText = this.CONSUME(Text).image;
|
||||
this.ACTION(() => pieDb.setAccDescription(accDescrText));
|
||||
});
|
||||
|
||||
public accDescriptionMultiLine = this.RULE('accDescriptionMultiLine', () => {
|
||||
this.CONSUME(AccDescription);
|
||||
this.CONSUME(LeftCurly);
|
||||
this.MANY(() => this.CONSUME(NewLine));
|
||||
|
||||
const text: string[] = [];
|
||||
this.AT_LEAST_ONE(() => {
|
||||
const line = this.CONSUME(Text);
|
||||
text.push(line.image);
|
||||
this.MANY1(() => this.CONSUME2(NewLine));
|
||||
});
|
||||
this.CONSUME(RightCurly);
|
||||
this.ACTION(() => pieDb.setAccDescription(text.join('\n')));
|
||||
});
|
||||
|
||||
public row = this.RULE('row', () => {
|
||||
this.SUBRULE(this.section);
|
||||
this.MANY(() => {
|
||||
this.CONSUME(NewLine);
|
||||
});
|
||||
});
|
||||
|
||||
public section = this.RULE('section', () => {
|
||||
const quotedKey = this.CONSUME(StringLiteral).image;
|
||||
const key = quotedKey.slice(1, quotedKey.length - 1);
|
||||
this.CONSUME(Colon);
|
||||
const value = parseFloat(this.CONSUME(NumberLiteral).image);
|
||||
this.ACTION(() => pieDb.addSection(key, value));
|
||||
});
|
||||
}
|
||||
|
||||
const parser = new PieParser();
|
||||
|
||||
const parse = (text: string): void => {
|
||||
const lexResult = PieLexer.tokenize(text);
|
||||
parser.input = lexResult.tokens;
|
||||
parser.diagram();
|
||||
|
||||
if (parser.errors.length > 0 || lexResult.errors.length > 0) {
|
||||
log.error(
|
||||
{ parserErrors: parser.errors, lexerErrors: lexResult.errors },
|
||||
'Error parsing info diagram'
|
||||
);
|
||||
throw new Error(`Parser errors: ${parser.errors} Lex errors: ${lexResult.errors}`);
|
||||
}
|
||||
};
|
||||
|
||||
export default { parser: { parse }, parse };
|
Reference in New Issue
Block a user