From d9b9288da90b22889615c1fda7dd6354f20ccfe8 Mon Sep 17 00:00:00 2001 From: knsv Date: Mon, 3 Nov 2014 00:02:29 +0100 Subject: [PATCH] Initial commit with draft parser inplace. --- bower.json | 24 ++ gulpfile.js | 15 + package.json | 17 ++ src/calc.jison | 60 ++++ src/mermaid.jison | 128 ++++++++ src/mermaid.js | 732 ++++++++++++++++++++++++++++++++++++++++++++++ src/test.js | 34 +++ 7 files changed, 1010 insertions(+) create mode 100644 bower.json create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 src/calc.jison create mode 100644 src/mermaid.jison create mode 100644 src/mermaid.js create mode 100644 src/test.js diff --git a/bower.json b/bower.json new file mode 100644 index 000000000..b039d1997 --- /dev/null +++ b/bower.json @@ -0,0 +1,24 @@ +{ + "name": "mermaid", + "version": "0.0.1", + "authors": [ + "knsv " + ], + "description": "Markdownish syntax for generating diagrams", + "main": "mermaid.js", + "moduleType": [ + "amd" + ], + "keywords": [ + "diagram", + "markdown" + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 000000000..b939e9c72 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,15 @@ +var gulp = require('gulp'); +var jison = require('gulp-jison'); +var shell = require('gulp-shell') + +gulp.task('jison', function() { + return gulp.src('./src/*.jison') + .pipe(jison({ moduleType: 'amd' })) + .pipe(gulp.dest('./src/')); +}); + +gulp.task('shorthand', shell.task([ + 'echo hello', + 'echo world', + 'jison src/mermaid.jison -o src/mermaid.js' +])) \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..3bf155ccd --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "mermaid", + "version": "0.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "BSD-2-Clause", + "devDependencies": { + "gulp": "~3.8.9", + "jison": "~0.4.15", + "jasmine": "~2.0.1", + "gulp-jison": "~1.0.0" + } +} diff --git a/src/calc.jison b/src/calc.jison new file mode 100644 index 000000000..9d1a56ce1 --- /dev/null +++ b/src/calc.jison @@ -0,0 +1,60 @@ +/* description: Parses end executes mathematical expressions. */ + +/* lexical grammar */ +%lex + +%% +\s+ /* skip whitespace */ +[0-9]+("."[0-9]+)?\b return 'NUMBER'; +\#[a-f0-9]+ return 'HEX'; +"*" return '*'; +"/" return '/'; +"-" return '-'; +"+" return '+'; +"^" return '^'; +"(" return '('; +")" return ')'; +"PI" return 'PI'; +"E" return 'E'; +<> return 'EOF'; + +/lex + +/* operator associations and precedence */ + +%left '+' '-' +%left '*' '/' +%left '^' +%left UMINUS + +%start expressions + +%% /* language grammar */ + +expressions + : e EOF + {console.log($1); return $1;} + ; + +e + : e '+' e + {$$ = $1+$3;} + | e '-' e + {$$ = $1-$3;} + | e '*' e + {$$ = $1*$3;} + | e '/' e + {$$ = $1/$3;} + | e '^' e + {$$ = Math.pow($1, $3);} + | '-' e %prec UMINUS + {$$ = -$2;} + | '(' e ')' + {$$ = $2;} + | NUMBER + {$$ = Number(yytext);} + | E + {$$ = Math.E;} + | PI + {$$ = Math.PI;} + ; \ No newline at end of file diff --git a/src/mermaid.jison b/src/mermaid.jison new file mode 100644 index 000000000..fef905e61 --- /dev/null +++ b/src/mermaid.jison @@ -0,0 +1,128 @@ +/* description: Parses end executes mathematical expressions. */ + +/* lexical grammar */ +%lex + +%% +"style" return 'STYLE'; +"background" return 'BKG'; +"red" return 'COLOR'; +"blue" return 'COLOR'; +"black" return 'COLOR'; +\#[a-f0-9]+ return 'HEX'; +[0-9]+ return 'NUM'; +"border" return 'BORDER'; +"dotted" return 'BORDER_STYLE'; +"dashed" return 'BORDER_STYLE'; +"solid" return 'BORDER_STYLE'; +"px" return 'UNIT'; +"pt" return 'UNIT'; +"dot" return 'UNIT'; +":" return 'COLON'; +";" return ';'; +"," return 'COMMA'; +\-\-[x] return 'ARROW_CROSS'; +\-\-">" return 'ARROW_POINT'; +\-\-[o] return 'ARROW_CIRCLE'; +\-\-\- return 'ARROW_OPEN'; +[a-zA-Z]+ return 'ALPHA'; +"|" return 'PIPE'; +"(" return 'PS'; +")" return 'PE'; +"[" return 'SQS'; +"]" return 'SQE'; +"{" return 'DIAMOND_START' +"}" return 'DIAMOND_STOP' +\s return 'SPACE'; +\n return 'NEWLINE'; +<> return 'EOF'; + +/lex + +/* operator associations and precedence */ + +%left '^' + +%start expressions + +%% /* language grammar */ + +expressions + : graph EOF + {return $1;} + ; + +graph + : SPACE graph + { $$ = $2;} + | edge ';' graph + { $$ = $3;} + | edge ';' + { $$ = $1;} + ; + +edge: vertex link vertex PIPE text + { yy.addLink($1,$3,$2,$5);$$ = 'oy'} + | vertex link vertex + { yy.addLink($1,$3,$2);$$ = 'oy'} + | vertex + {$$ = 'yo';} + ; + + +vertex: STYLE SPACE ALPHA SPACE styles + {$$ = $1;yy.addVertex($3,undefined,undefined,$5);} + | ALPHA SQS text SQE + {$$ = $1;yy.addVertex($1,$3,'square');} + | ALPHA PS text PE + {$$ = $1;yy.addVertex($1,$3,'round');} + | ALPHA DIAMOND_START text DIAMOND_STOP + {$$ = $1;yy.addVertex($1,$3,'diamond');} + | ALPHA + {$$ = $1;yy.addVertex($1);} + ; +// Characters and spaces +text: ALPHA SPACE text + {$$ = $1 + ' ' +$3;} + | ALPHA SPACE + {$$ = $1;} + | ALPHA + {$$ = $1;} + ; + +link: ARROW_POINT + {$$ = {"type":"arrow"};} + | ARROW_CIRCLE + {$$ = {"type":"arrow_circle"};} + | ARROW_CROSS + {$$ = {"type":"arrow_cross"};} + | ARROW_OPEN + {$$ = {"type":"arrow_open"};} + ; +styles: + styledef + {$$ = [$1];} + | styles COMMA styledef + {console.log('in styles:'+JSON.stringify($1));$1.push($3);$$ = $1;} + ; +styledef: BKG COLON colordef + {$$={"background":$3}} + | COL COLON COLORDEF + {$$={"color":$3}} + | BORDER COLON borderWidth SPACE borderStyle SPACE colordef + {$$={"border":$3+' '+$5+' '+$7}} + ; + +colordef: COLOR + {$$ = yytext;} + | HEX + {$$ = yytext;} + ; + +borderWidth: NUM UNIT + {$$ = $1+''+$2;} + ; + +borderStyle: BORDER_STYLE + {$$ = $1;} + ; \ No newline at end of file diff --git a/src/mermaid.js b/src/mermaid.js new file mode 100644 index 000000000..4113287ab --- /dev/null +++ b/src/mermaid.js @@ -0,0 +1,732 @@ +/* parser generated by jison 0.4.15 */ +/* + Returns a Parser object of the following structure: + + Parser: { + yy: {} + } + + Parser.prototype: { + yy: {}, + trace: function(), + symbols_: {associative list: name ==> number}, + terminals_: {associative list: number ==> name}, + productions_: [...], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$), + table: [...], + defaultActions: {...}, + parseError: function(str, hash), + parse: function(input), + + lexer: { + EOF: 1, + parseError: function(str, hash), + setInput: function(input), + input: function(), + unput: function(str), + more: function(), + less: function(n), + pastInput: function(), + upcomingInput: function(), + showPosition: function(), + test_match: function(regex_match_array, rule_index), + next: function(), + lex: function(), + begin: function(condition), + popState: function(), + _currentRules: function(), + topState: function(), + pushState: function(condition), + + options: { + ranges: boolean (optional: true ==> token location info will include a .range[] member) + flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match) + backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code) + }, + + performAction: function(yy, yy_, $avoiding_name_collisions, YY_START), + rules: [...], + conditions: {associative list: name ==> set}, + } + } + + + token location info (@$, _$, etc.): { + first_line: n, + last_line: n, + first_column: n, + last_column: n, + range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based) + } + + + the parseError function receives a 'hash' object with these members for lexer and parser errors: { + text: (matched text) + token: (the produced terminal token, if any) + line: (yylineno) + } + while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: { + loc: (yylloc) + expected: (string describing the set of expected tokens) + recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error) + } +*/ +var parser = (function(){ +var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,3],$V1=[1,6],$V2=[1,7],$V3=[8,11,22,23,24,25],$V4=[13,14],$V5=[1,24],$V6=[8,17,19,21],$V7=[1,36],$V8=[1,37],$V9=[1,38],$Va=[8,11,22,23,24,25,27],$Vb=[1,46],$Vc=[1,47]; +var parser = {trace: function trace() { }, +yy: {}, +symbols_: {"error":2,"expressions":3,"graph":4,"EOF":5,"SPACE":6,"edge":7,";":8,"vertex":9,"link":10,"PIPE":11,"text":12,"STYLE":13,"ALPHA":14,"styles":15,"SQS":16,"SQE":17,"PS":18,"PE":19,"DIAMOND_START":20,"DIAMOND_STOP":21,"ARROW_POINT":22,"ARROW_CIRCLE":23,"ARROW_CROSS":24,"ARROW_OPEN":25,"styledef":26,"COMMA":27,"BKG":28,"COLON":29,"colordef":30,"COL":31,"COLORDEF":32,"BORDER":33,"borderWidth":34,"borderStyle":35,"COLOR":36,"HEX":37,"NUM":38,"UNIT":39,"BORDER_STYLE":40,"$accept":0,"$end":1}, +terminals_: {2:"error",5:"EOF",6:"SPACE",8:";",11:"PIPE",13:"STYLE",14:"ALPHA",16:"SQS",17:"SQE",18:"PS",19:"PE",20:"DIAMOND_START",21:"DIAMOND_STOP",22:"ARROW_POINT",23:"ARROW_CIRCLE",24:"ARROW_CROSS",25:"ARROW_OPEN",27:"COMMA",28:"BKG",29:"COLON",31:"COL",32:"COLORDEF",33:"BORDER",36:"COLOR",37:"HEX",38:"NUM",39:"UNIT",40:"BORDER_STYLE"}, +productions_: [0,[3,2],[4,2],[4,3],[4,2],[7,5],[7,3],[7,1],[9,5],[9,4],[9,4],[9,4],[9,1],[12,3],[12,2],[12,1],[10,1],[10,1],[10,1],[10,1],[15,1],[15,3],[26,3],[26,3],[26,7],[30,1],[30,1],[34,2],[35,1]], +performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { +/* this == yyval */ + +var $0 = $$.length - 1; +switch (yystate) { +case 1: +return $$[$0-1]; +break; +case 2: case 3: + this.$ = $$[$0]; +break; +case 4: + this.$ = $$[$0-1]; +break; +case 5: + yy.addLink($$[$0-4],$$[$0-2],$$[$0-3],$$[$0]);this.$ = 'oy' +break; +case 6: + yy.addLink($$[$0-2],$$[$0],$$[$0-1]);this.$ = 'oy' +break; +case 7: +this.$ = 'yo'; +break; +case 8: +this.$ = $$[$0-4];yy.addVertex($$[$0-2],undefined,undefined,$$[$0]); +break; +case 9: +this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'square'); +break; +case 10: +this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'round'); +break; +case 11: +this.$ = $$[$0-3];yy.addVertex($$[$0-3],$$[$0-1],'diamond'); +break; +case 12: +this.$ = $$[$0];yy.addVertex($$[$0]); +break; +case 13: +this.$ = $$[$0-2] + ' ' +$$[$0]; +break; +case 14: +this.$ = $$[$0-1]; +break; +case 15: case 28: +this.$ = $$[$0]; +break; +case 16: +this.$ = {"type":"arrow"}; +break; +case 17: +this.$ = {"type":"arrow_circle"}; +break; +case 18: +this.$ = {"type":"arrow_cross"}; +break; +case 19: +this.$ = {"type":"arrow_open"}; +break; +case 20: +this.$ = [$$[$0]]; +break; +case 21: +console.log('in styles:'+JSON.stringify($$[$0-2]));$$[$0-2].push($$[$0]);this.$ = $$[$0-2]; +break; +case 22: +this.$={"background":$$[$0]} +break; +case 23: +this.$={"color":$$[$0]} +break; +case 24: +this.$={"border":$$[$0-4]+' '+$$[$0-2]+' '+$$[$0]} +break; +case 25: case 26: +this.$ = yytext; +break; +case 27: +this.$ = $$[$0-1]+''+$$[$0]; +break; +} +}, +table: [{3:1,4:2,6:$V0,7:4,9:5,13:$V1,14:$V2},{1:[3]},{5:[1,8]},{4:9,6:$V0,7:4,9:5,13:$V1,14:$V2},{8:[1,10]},{8:[2,7],10:11,22:[1,12],23:[1,13],24:[1,14],25:[1,15]},{6:[1,16]},o($V3,[2,12],{16:[1,17],18:[1,18],20:[1,19]}),{1:[2,1]},{5:[2,2]},{4:20,5:[2,4],6:$V0,7:4,9:5,13:$V1,14:$V2},{9:21,13:$V1,14:$V2},o($V4,[2,16]),o($V4,[2,17]),o($V4,[2,18]),o($V4,[2,19]),{14:[1,22]},{12:23,14:$V5},{12:25,14:$V5},{12:26,14:$V5},{5:[2,3]},{8:[2,6],11:[1,27]},{6:[1,28]},{17:[1,29]},o($V6,[2,15],{6:[1,30]}),{19:[1,31]},{21:[1,32]},{12:33,14:$V5},{15:34,26:35,28:$V7,31:$V8,33:$V9},o($V3,[2,9]),o($V6,[2,14],{12:39,14:$V5}),o($V3,[2,10]),o($V3,[2,11]),{8:[2,5]},o($V3,[2,8],{27:[1,40]}),o($Va,[2,20]),{29:[1,41]},{29:[1,42]},{29:[1,43]},o($V6,[2,13]),{26:44,28:$V7,31:$V8,33:$V9},{30:45,36:$Vb,37:$Vc},{32:[1,48]},{34:49,38:[1,50]},o($Va,[2,21]),o($Va,[2,22]),o($Va,[2,25]),o($Va,[2,26]),o($Va,[2,23]),{6:[1,51]},{39:[1,52]},{35:53,40:[1,54]},{6:[2,27]},{6:[1,55]},{6:[2,28]},{30:56,36:$Vb,37:$Vc},o($Va,[2,24])], +defaultActions: {8:[2,1],9:[2,2],20:[2,3],33:[2,5],52:[2,27],54:[2,28]}, +parseError: function parseError(str, hash) { + if (hash.recoverable) { + this.trace(str); + } else { + throw new Error(str); + } +}, +parse: function parse(input) { + var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + var args = lstack.slice.call(arguments, 1); + var lexer = Object.create(this.lexer); + var sharedState = { yy: {} }; + for (var k in this.yy) { + if (Object.prototype.hasOwnProperty.call(this.yy, k)) { + sharedState.yy[k] = this.yy[k]; + } + } + lexer.setInput(input, sharedState.yy); + sharedState.yy.lexer = lexer; + sharedState.yy.parser = this; + if (typeof lexer.yylloc == 'undefined') { + lexer.yylloc = {}; + } + var yyloc = lexer.yylloc; + lstack.push(yyloc); + var ranges = lexer.options && lexer.options.ranges; + if (typeof sharedState.yy.parseError === 'function') { + this.parseError = sharedState.yy.parseError; + } else { + this.parseError = Object.getPrototypeOf(this).parseError; + } + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + _token_stack: + function lex() { + var token; + token = lexer.lex() || EOF; + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == 'undefined') { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === 'undefined' || !action.length || !action[0]) { + var errStr = ''; + expected = []; + for (p in table[state]) { + if (this.terminals_[p] && p > TERROR) { + expected.push('\'' + this.terminals_[p] + '\''); + } + } + if (lexer.showPosition) { + errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''; + } else { + errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\''); + } + this.parseError(errStr, { + text: lexer.match, + token: this.terminals_[symbol] || symbol, + line: lexer.yylineno, + loc: yyloc, + expected: expected + }); + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(lexer.yytext); + lstack.push(lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = lexer.yyleng; + yytext = lexer.yytext; + yylineno = lexer.yylineno; + yyloc = lexer.yylloc; + if (recovering > 0) { + recovering--; + } + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { + first_line: lstack[lstack.length - (len || 1)].first_line, + last_line: lstack[lstack.length - 1].last_line, + first_column: lstack[lstack.length - (len || 1)].first_column, + last_column: lstack[lstack.length - 1].last_column + }; + if (ranges) { + yyval._$.range = [ + lstack[lstack.length - (len || 1)].range[0], + lstack[lstack.length - 1].range[1] + ]; + } + r = this.performAction.apply(yyval, [ + yytext, + yyleng, + yylineno, + sharedState.yy, + action[1], + vstack, + lstack + ].concat(args)); + if (typeof r !== 'undefined') { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; +}}; +/* generated by jison-lex 0.3.4 */ +var lexer = (function(){ +var lexer = ({ + +EOF:1, + +parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + +// resets the lexer, sets new input +setInput:function (input, yy) { + this.yy = yy || this.yy || {}; + this._input = input; + this._more = this._backtrack = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { + first_line: 1, + first_column: 0, + last_line: 1, + last_column: 0 + }; + if (this.options.ranges) { + this.yylloc.range = [0,0]; + } + this.offset = 0; + return this; + }, + +// consumes and returns one char from the input +input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) { + this.yylloc.range[1]++; + } + + this._input = this._input.slice(1); + return ch; + }, + +// unshifts one char (or a string) into the input +unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) { + this.yylineno -= lines.length - 1; + } + var r = this.yylloc.range; + + this.yylloc = { + first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + + oldLines[oldLines.length - lines.length].length - lines[0].length : + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + this.yyleng = this.yytext.length; + return this; + }, + +// When called from action, caches matched text and appends it on next action +more:function () { + this._more = true; + return this; + }, + +// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. +reject:function () { + if (this.options.backtrack_lexer) { + this._backtrack = true; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + + } + return this; + }, + +// retain first n characters of the match +less:function (n) { + this.unput(this.match.slice(n)); + }, + +// displays already matched input, i.e. for error messages +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, + +// displays upcoming input, i.e. for error messages +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + +// displays the character position where the lexing error occurred, i.e. for error messages +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + +// test the lexed token: return FALSE when not a match, otherwise return token +test_match:function (match, indexed_rule) { + var token, + lines, + backup; + + if (this.options.backtrack_lexer) { + // save context + backup = { + yylineno: this.yylineno, + yylloc: { + first_line: this.yylloc.first_line, + last_line: this.last_line, + first_column: this.yylloc.first_column, + last_column: this.yylloc.last_column + }, + yytext: this.yytext, + match: this.match, + matches: this.matches, + matched: this.matched, + yyleng: this.yyleng, + offset: this.offset, + _more: this._more, + _input: this._input, + yy: this.yy, + conditionStack: this.conditionStack.slice(0), + done: this.done + }; + if (this.options.ranges) { + backup.yylloc.range = this.yylloc.range.slice(0); + } + } + + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno += lines.length; + } + this.yylloc = { + first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? + lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : + this.yylloc.last_column + match[0].length + }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._backtrack = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) { + this.done = false; + } + if (token) { + return token; + } else if (this._backtrack) { + // recover context + for (var k in backup) { + this[k] = backup[k]; + } + return false; // rule action called reject() implying the next rule should be tested instead. + } + return false; + }, + +// return next match in input +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) { + this.done = true; + } + + var token, + match, + tempMatch, + index; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (this.options.backtrack_lexer) { + token = this.test_match(tempMatch, rules[i]); + if (token !== false) { + return token; + } else if (this._backtrack) { + match = false; + continue; // rule action called reject() implying a rule MISmatch. + } else { + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + } else if (!this.options.flex) { + break; + } + } + } + if (match) { + token = this.test_match(match, rules[index]); + if (token !== false) { + return token; + } + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + } + }, + +// return next match that has a token +lex:function lex() { + var r = this.next(); + if (r) { + return r; + } else { + return this.lex(); + } + }, + +// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) +begin:function begin(condition) { + this.conditionStack.push(condition); + }, + +// pop the previously active lexer condition state off the condition stack +popState:function popState() { + var n = this.conditionStack.length - 1; + if (n > 0) { + return this.conditionStack.pop(); + } else { + return this.conditionStack[0]; + } + }, + +// produce the lexer rule set which is active for the currently active lexer condition state +_currentRules:function _currentRules() { + if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + } else { + return this.conditions["INITIAL"].rules; + } + }, + +// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available +topState:function topState(n) { + n = this.conditionStack.length - 1 - Math.abs(n || 0); + if (n >= 0) { + return this.conditionStack[n]; + } else { + return "INITIAL"; + } + }, + +// alias for begin(condition) +pushState:function pushState(condition) { + this.begin(condition); + }, + +// return the number of states currently on the stack +stateStackSize:function stateStackSize() { + return this.conditionStack.length; + }, +options: {}, +performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { +var YYSTATE=YY_START; +switch($avoiding_name_collisions) { +case 0:return 13; +break; +case 1:return 28; +break; +case 2:return 36; +break; +case 3:return 36; +break; +case 4:return 36; +break; +case 5:return 37; +break; +case 6:return 38; +break; +case 7:return 33; +break; +case 8:return 40; +break; +case 9:return 40; +break; +case 10:return 40; +break; +case 11:return 39; +break; +case 12:return 39; +break; +case 13:return 39; +break; +case 14:return 29; +break; +case 15:return 8; +break; +case 16:return 27; +break; +case 17:return 24; +break; +case 18:return 22; +break; +case 19:return 23; +break; +case 20:return 25; +break; +case 21:return 14; +break; +case 22:return 11; +break; +case 23:return 18; +break; +case 24:return 19; +break; +case 25:return 16; +break; +case 26:return 17; +break; +case 27:return 20 +break; +case 28:return 21 +break; +case 29:return 6; +break; +case 30:return 'NEWLINE'; +break; +case 31:return 5; +break; +} +}, +rules: [/^(?:style\b)/,/^(?:background\b)/,/^(?:red\b)/,/^(?:blue\b)/,/^(?:black\b)/,/^(?:#[a-f0-9]+)/,/^(?:[0-9]+)/,/^(?:border\b)/,/^(?:dotted\b)/,/^(?:dashed\b)/,/^(?:solid\b)/,/^(?:px\b)/,/^(?:pt\b)/,/^(?:dot\b)/,/^(?::)/,/^(?:;)/,/^(?:,)/,/^(?:--[x])/,/^(?:-->)/,/^(?:--[o])/,/^(?:---)/,/^(?:[a-zA-Z]+)/,/^(?:\|)/,/^(?:\()/,/^(?:\))/,/^(?:\[)/,/^(?:\])/,/^(?:\{)/,/^(?:\})/,/^(?:\s)/,/^(?:\n)/,/^(?:$)/], +conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"inclusive":true}} +}); +return lexer; +})(); +parser.lexer = lexer; +function Parser () { + this.yy = {}; +} +Parser.prototype = parser;parser.Parser = Parser; +return new Parser; +})(); + + +if (typeof require !== 'undefined' && typeof exports !== 'undefined') { +exports.parser = parser; +exports.Parser = parser.Parser; +exports.parse = function () { return parser.parse.apply(parser, arguments); }; +exports.main = function commonjsMain(args) { + if (!args[1]) { + console.log('Usage: '+args[0]+' FILE'); + process.exit(1); + } + var source = require('fs').readFileSync(require('path').normalize(args[1]), "utf8"); + return exports.parser.parse(source); +}; +if (typeof module !== 'undefined' && require.main === module) { + exports.main(process.argv.slice(1)); +} +} \ No newline at end of file diff --git a/src/test.js b/src/test.js new file mode 100644 index 000000000..a9654fee0 --- /dev/null +++ b/src/test.js @@ -0,0 +1,34 @@ + +//console.log(p.parse('#fcfcfc')); +//console.log(p.parse('background: #fcfcfc')); +//console.log(p.parse('background: black')); +var scope={ + addVertex:function(id,text,type,style){ + console.log('Got node '+id+' '+type+' '+text+' styles: '+JSON.stringify(style)); +}, +addLink:function(start,end,type,linktext){ + console.log('Got link from '+start+' to '+end+' type:'+type.type+' linktext:'+linktext); +} +}; + +var p = require('./mermaid.js'); +p.parser.yy = scope; + + +console.log(p.parse('A-->B;')); +console.log(p.parse('A---B;')); +console.log(p.parse('A--xB;')); +console.log(p.parse('A--oB|apan;')); +console.log(p.parse('A--oB|apan hoppar;\nB-->C|apan hoppar vidare;')); + +console.log(p.parse('A-->B|apan hoppar;\nB-->C;')); +console.log(p.parse('A-->B|apan hoppar;')); +console.log(p.parse('A[chimpansen hoppar]-->C;')); +console.log(p.parse('A(chimpansen hoppar)-->C;')); + +//console.log(p.parse('A{chimpansen hoppar}-->C;')); +console.log(p.parse('style Q background:#fff;')); +console.log(p.parse('style R background:#fff,border:1px solid red;')); +console.log(p.parse('style S background:#aaa;\nstyle T background:#bbb,border:1px solid red;')); +//console.log(p.parse('A;')); +