Skip to content

Instantly share code, notes, and snippets.

@joshski
Created September 6, 2011 16:49
Show Gist options
  • Save joshski/1198142 to your computer and use it in GitHub Desktop.
Save joshski/1198142 to your computer and use it in GitHub Desktop.
function EbnfDiagram(gc) {
this.gc = gc;
this.styles = STYLES;
this.style = this.styles["DIAGRAM"];
this.init();
}
EbnfDiagram.prototype.getGC = function() {
return this.gc;
}
//default style
STYLES = {
"SYNTAX": {
"margin" : 10,
"round" : 10,
"background-fill" : "rgba(200,200,100, 0.3)",
"background-stroke" : "rgba(255,255,255, 0)",
"background-weight" : 0,
"separator-stroke" : "rgba( 0, 0, 0, 0.7)",
"separator-weight" : 2,
"title-font" : "30px Arial",
"title-color" : "rgba( 0, 0, 0, 0.7)",
"title-align" : "left",
"comment-font" : "20px Optimer",
"comment-color" : "rgba(0, 0, 0, 0.5)",
"comment-align" : "left"
},
"PRODUCTION_SET": {
},
"PRODUCTION": {
"margin" : 10,
"round" : 10,
"background-fill" : "rgba(255,255,255, 0.75)",
"background-stroke" : "rgba(255,255,255, 0)",
"background-weight" : 0,
"font" : "10px Arial",
"color" : "rgba( 0, 0, 0, 0.7)",
"align" : "left",
"baseline" : "bottom",
"trail" : 75
},
"TERM_SET": {
},
"FACTOR_SET": {
},
"LITERAL": {
"round" : 10,
"background-fill" : "rgba( 20, 20,200, 0.5)",
"background-stroke" : "rgba( 10, 10, 10, 0.8)",
"background-weight" : 3
},
"IDENTIFIER": {
"round" : 0,
"background-fill" : "rgba(200, 20, 20, 0.5)",
"background-stroke" : "rgba( 10, 10, 10, 0.8)",
"background-weight" : 3
},
"GROUP": {
},
"REPEATING": {
},
"OPTIONAL": {
},
"DIAGRAM": {
"line-stroke" : "rgba( 10, 10, 10, 1)",
"line-weight" : 2,
"grid" : 12,
"font-width" : 10,
"font-height" : 24,
"font" : "12px Arial",
"color" : "rgba( 0, 0, 0, 0.7)"
}
}
NODEHANDLERS = {
"SYNTAX": {
"prepare": function(node, dia, gc) {
var s = node.style;
var m = s.margin;
var c = node.set;
dia.prepare(c);
node.height = c.height + 2*m + 2*30 + 2*20; // TODO calculated room for title and comment
node.width = c.width + 2*m;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var m = s.margin;
var ox = node.width / 2;
var oy = node.height / 2;
var ty = 30; //TODO calc
var cy = 20; //TODO calc
GCLIB.setDrawStyle(gc, s, "background");
GCLIB.roundedRect(gc, (node.width - m), (node.height - m), s.round, ox, oy);
GCLIB.setTextStyle(gc, s, "title");
GCLIB.centeredText(gc, node.title, 2*m, ty);
GCLIB.setDrawStyle(gc, node.style, "separator");
GCLIB.line(gc, 2*m, 2*ty, node.width - 2*m, 2*ty);
dia.draw(node.set, m, 2*ty + m);
GCLIB.line(gc, 2*m, node.height - 2*cy, node.width - 2*m, node.height - 2*cy);
GCLIB.setTextStyle(gc, s, "comment");
GCLIB.centeredText(gc, node.comment, 2*m, node.height - cy);
}
},
"PRODUCTION_SET": {
"prepare": function(node, dia, gc) {
var sumH = 0;
var maxW = 0;
var c = node.children;
for (var i = 0; i< c.length; i++){
var ci = c[i];
dia.prepare(ci);
sumH += ci.height;
maxW = Math.max(maxW, ci.width);
}
// align all widths
for (var i = 0; i < c.length; i++){
c[i].width = maxW;
}
node.width = maxW;
node.height = sumH;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var offH = 0;
var c = node.children;
for (var i = 0; i < c.length; i++){
var ci = c[i];
dia.draw(ci, 0, offH);
offH += ci.height;
}
}
},
"PRODUCTION": {
"prepare": function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
var m = s.margin;
var c = node.expr;
dia.prepare(c);
node.width = c.width + 2*m + s.trail;
node.height = c.height+ 2*m;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
var m = s.margin;
var tx = 10;
var ty = 10;
var ox = node.width / 2;
var oy = node.height / 2;
GCLIB.setDrawStyle(gc, s, "background");
GCLIB.roundedRect(gc, (node.width - m), (node.height - m), s.round, ox, oy);
GCLIB.setTextStyle(gc, s);
GCLIB.centeredText(gc, node.id, m + tx, m + ty);
GCLIB.setDrawStyle(gc, ds, "line");
GCLIB.line(gc, m , m+ 2*g, m + s.trail, m+ 2*g);
// delegate to expression
var c = node.expr;
dia.draw(c, m + s.trail, m);
GCLIB.line(gc, m + s.trail + node.expr.width , m+ 2*g, node.width -m, m + 2*g);
GCLIB.line(gc, node.width - m, m+ 1.5 * g, node.width - m, m+ 2.5*g);
}
},
"TERM_SET": {
"prepare": function(node, dia, gc) {
var ds = dia.style;
var g = ds.grid;
var sumH = 0;
var maxW = 0;
var c = node.children;
for (var i = 0; i< c.length; i++){
var ci = c[i];
dia.prepare(ci);
sumH += ci.height;
maxW = Math.max(maxW, ci.width);
}
node.width = maxW + 6 * g; // make room for parallelization lines
node.height = sumH + (c.length - 1) * g; // make room for space between terms
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
var offH = 0;
var c = node.children;
GCLIB.setDrawStyle(gc, ds, "line");
for (var i = 0; i < c.length; i++){
var ci = c[i];
GCLIB.slideIn (gc, g, 0 , 2*g, 3*g, offH);
dia.draw(ci, 3*g, offH);
GCLIB.line(gc, ci.width + 3*g , offH + 2*g, node.width - 3*g, offH + 2*g);
GCLIB.slideOut(gc, g, node.width, 2*g, 3*g, offH);
offH += ci.height + g; //add space between terms
}
}
},
"FACTOR_SET": {
"prepare": function(node, dia, gc) {
var ds = dia.style;
var g = ds.grid;
var sumW = 0;
var maxH = 0;
var c = node.children;
for (var i = 0; i< c.length; i++){
var ci = c[i];
dia.prepare(ci);
sumW += ci.width;
maxH = Math.max(maxH, ci.height);
}
// no need to align the heights
node.width = sumW;
node.height = maxH;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
var offW = 0;
var c = node.children;
for (var i = 0; i < c.length; i++){
var ci = c[i];
dia.draw(ci, offW, 0);
offW += ci.width;
}
}
},
"LITERAL": {
"prepare": function(node, dia, gc) {
var l = Math.max(3, node.txt.length);
var ds = dia.style;
var g = ds.grid;
node.width = l * ds["font-width"] + 2 * g;
node.height = 4 * g ;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
var ox = node.width / 2;
var oy = node.height /2;
GCLIB.setDrawStyle(gc, ds, "line");
GCLIB.line(gc, 0, 2*g, g, 2*g);
GCLIB.line(gc, node.width - g, 2*g, node.width, 2*g);
GCLIB.setDrawStyle(gc,s, "background");
GCLIB.roundedRect(gc, node.width - 2 * g, ds["font-height"], s.round, ox, oy);
GCLIB.setTextStyle(gc, ds);
GCLIB.centeredText(gc, node.txt, ox, oy);
}
},
"IDENTIFIER": {
"prepare": function(node, dia, gc) {
var l = Math.max(3, node.id.length);
var ds = dia.style;
var g = ds.grid;
node.width = l * dia.style["font-width"] + 2*g;
node.height = 4*g ;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
var ox = node.width / 2;
var oy = node.height /2;
GCLIB.setDrawStyle(gc, ds, "line");
GCLIB.line(gc, 0, 2*g, g, 2*g);
GCLIB.line(gc, node.width - g, 2*g, node.width, 2*g);
GCLIB.setDrawStyle(gc,s, "background");
GCLIB.roundedRect(gc, node.width - 2 * g, ds["font-height"], s.round, ox, oy);
GCLIB.setTextStyle(gc, ds);
GCLIB.centeredText(gc, node.id , ox, oy);
}
},
"GROUP": {
"prepare": function(node, dia, gc) {
var ds = dia.style;
var g = ds.grid;
var c = node.expr;
dia.prepare(c);
node.width = c.width + 2 * g;
node.height = c.height;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
GCLIB.setDrawStyle(gc, ds, "line");
GCLIB.line(gc, 0, 2*g, g, 2*g);
dia.draw(node.expr, g, 0);
GCLIB.line(gc, node.width - g, 2*g, node.width, 2*g);
}
},
"REPEATING": {
"prepare": function(node, dia, gc) {
var ds = dia.style;
var g = ds.grid;
var c = node.expr;
dia.prepare(c);
node.width = c.width + 6*g;
node.height = c.height;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
var c = node.expr;
GCLIB.setDrawStyle(gc, ds, "line");
GCLIB.slideOut(gc, g, 2*g, 0, 2*g, 2*g);
GCLIB.line(gc, 2*g , 0, node.width - 2*g, 0);
GCLIB.slideIn (gc, g, node.width - 2*g, 0, 2*g, 2*g);
GCLIB.line(gc, 0, 2*g, 3*g, 2*g);
dia.draw(c, 3*g, 0);
GCLIB.line(gc, node.width - 3*g, 2*g, node.width, 2*g);
GCLIB.loopDown(gc, g, 3*g + c.width, 2*g, - 2*g + c.height);
GCLIB.line(gc, 3*g , c.height, node.width - 3*g, c.height);
GCLIB.loopUp(gc, g, 3*g, 2*g, - 2*g + c.height);
}
},
"OPTIONAL": {
"prepare": function(node, dia, gc) {
var ds = dia.style;
var g = ds.grid;
var c = node.expr;
dia.prepare(c);
node.width = c.width + 6*g;
node.height = c.height + 2*g;
},
"draw" : function(node, dia, gc) {
var s = node.style;
var ds = dia.style;
var g = ds.grid;
GCLIB.setDrawStyle(gc, ds, "line");
GCLIB.line(gc, 0 , 2*g, node.width, 2*g);
GCLIB.slideIn (gc, g, 0 , 2*g, 3*g, 2*g);
dia.draw(node.expr, 3*g, 2*g);
GCLIB.slideOut(gc, g, node.width, 2*g, 3*g, 2*g);
}
}
}
//actual diagram code
EbnfDiagram.prototype.init = function() {
this.height = 5000;
this.width = 10000;
// clear the canvas
this.getGC().clearRect( 0, 0, this.width, this.height);
}
EbnfDiagram.prototype.showErrors = function(err) {
var l = err.length;
for (var i = 0; i++; i<l) {
alert((i+1) + "/" + l + ":\n" + err[i]);
}
}
EbnfDiagram.prototype.setSyntax = function(syn) {
this.prepare(syn, this.styles);
this.init();
var gc = this.getGC();
var scale = Math.min(this.width/syn.width, this.height/syn.height);
gc.save();
try {
gc.scale(scale,scale);
this.draw(syn);
} finally {
gc.restore();
}
}
// Preparation visitor, prepares the complete tree,
// assisted by call-backs from the parent-nodes down to their subnodes
EbnfDiagram.prototype.prepare = function(node) {
var type = node.nodetype;
if (node.handler)
throw "node is already prepared!" + node;
node.handler = NODEHANDLERS[type];
if (!node.handler) throw "cannot handle node of type " + type;
node.style = this.styles[type];
node.handler.prepare(node, this, this.getGC());
}
// Draw visitor, draws the specified node at the specified position
// Requires nodes to be prepared.
EbnfDiagram.prototype.draw = function(node, x, y) {
if (!node.handler)
throw "Request to draw a node that is not prepared yet! nodetype=" + node.nodetype;
var gc = this.getGC();
x = x || 0;
y = y || 0;
gc.save();
try {
gc.translate(x,y);
node.handler.draw(node, this, gc);
} catch(e) {
debugger;
alert(e);
} finally {
gc.restore();
}
}
// Internal GCLIB with standard operations/drawing assistance..
var GCLIB = {};
/** Draws a centered (ie at 0,0) rounded rectangle with the specified width, height and percentage of round-ness on the corners */
GCLIB.roundedRect = function(gc, width, height, r, atx, aty) {
if (! (width && height))
return;
r = r || 0;
atx = atx || 0;
aty = aty || 0;
var w = width/2;
var h = height/2;
gc.beginPath();
if (r) {
gc.arc( atx -w + r, aty -h + r, r, -Math.PI , -Math.PI/2, false);
gc.arc( atx +w - r, aty -h + r, r, -Math.PI/2, 0 , false);
gc.arc( atx +w - r, aty +h - r, r, 0 , Math.PI/2 , false);
gc.arc( atx -w + r, aty +h - r, r, Math.PI/2 , Math.PI , false);
} else {
gc.moveTo( atx -w, aty -h);
gc.lineTo( atx +w, aty -h);
gc.lineTo( atx +w, aty +h);
gc.lineTo( atx -w, aty +h);
}
gc.closePath();
gc.fill();
gc.stroke();
}
/** Draws text centered at 0,0 */
GCLIB.centeredText = function(gc, text, x, y) {
x = x || 0;
y = y || 0;
gc.fillText(text, x, y);
}
/** Draws line from a to b */
GCLIB.line = function(gc, ax, ay, bx, by) {
gc.beginPath();
gc.moveTo( ax, ay);
gc.lineTo( bx, by);
gc.closePath();
gc.stroke();
}
/** Draws slide-in from a to b */
GCLIB.slideIn = function(gc, g, atx , aty, w, h){
w = Math.max(w , 2*g); // minimal width
h = h || 0;
if (h==0) {
GCLIB.line(gc, atx, aty, atx+w, aty);
return;
}
gc.beginPath();
gc.arc( atx , aty + g, g, -Math.PI/2, 0,false);
gc.lineTo( atx + g, aty + h - g);
gc.arc( atx +2*g, aty + h - g, g, Math.PI, Math.PI/2,true);
gc.lineTo(atx + w, aty + h);
gc.stroke();
}
GCLIB.slideOut = function(gc, g, tox, toy, w, h){
w = Math.max(w , 2*g); // minimal width
h = h || 0;
if (h==0) {
gc.beginPath();
gc.moveTo(tox -w, toy);
gc.lineTo( tox , toy);
gc.stroke();
return;
}
gc.beginPath();
gc.moveTo(tox -w, toy + h);
gc.lineTo(tox - 2*g, toy + h);
gc.arc( tox - 2 * g , toy + h - g, g, Math.PI/2, 0, true);
gc.lineTo( tox -g , toy + g);
gc.arc( tox, toy + g, g, -Math.PI, -Math.PI/2, false);
gc.stroke();
}
GCLIB.loopDown = function(gc, g, atx, aty, h){
h = h || 0;
if (h==0)
return;
gc.beginPath();
gc.arc( atx , aty + g, g, -Math.PI/2, 0,false);
gc.lineTo( atx + g, aty + h - g);
gc.arc( atx , aty + h - g, g, 0, Math.PI/2,false);
gc.stroke();
}
GCLIB.loopUp = function(gc, g, tox, toy, h){
h = h || 0;
if (h==0)
return;
gc.beginPath();
gc.arc( tox , toy + h - g, g, Math.PI/2, Math.PI, false);
gc.lineTo( tox - g, toy + g);
gc.arc( tox , toy + g, g, -Math.PI, -Math.PI/2,false);
gc.stroke();
}
var DEFAULT_FILL = "rgb(255,255,255)";
var DEFAULT_STROKE = "rgb( 0, 0, 0)";
var DEFAULT_WEIGHT = 0;
var DEFAULT_FONT = "8px Arial";
var DEFAULT_COLOR = "rgb( 0, 0, 0)";
var DEFAULT_ALIGN = "center";
var DEFAULT_BASELINE = "middle";
/** Sets drawstyles */
GCLIB.setDrawStyle = function(gc, style, pfx) {
pfx = pfx ? pfx + "-" : "";
gc.fillStyle = style[pfx + "fill"] || DEFAULT_FILL;
gc.strokeStyle = style[pfx + "stroke"] || DEFAULT_STROKE;
gc.lineWidth = style[pfx + "weight"] || DEFAULT_WEIGHT;
}
/** Sets font-styles */
GCLIB.setTextStyle = function(gc, style, pfx) {
pfx = pfx ? pfx + "-" : "";
gc.font = style[pfx + "font"] || DEFAULT_FONT;
gc.fillStyle = style[pfx + "color"] || DEFAULT_COLOR;
gc.textAlign = style[pfx + "align"] || DEFAULT_ALIGN;
gc.textBaseline = style[pfx + "baseline"] || DEFAULT_BASELINE;
}
exports = EbnfDiagram;
/*
Default driver template for JS/CC generated parsers for Mozilla/Rhino
WARNING: Do not use for parsers that should run as browser-based JavaScript!
Use driver_web.js_ instead!
Features:
- Parser trace messages
- Step-by-step parsing
- Integrated panic-mode error recovery
- Pseudo-graphical parse tree generation
Written 2007 by Jan Max Meyer, J.M.K S.F. Software Technologies
Modified 2007 from driver.js_ to support Mozilla/Rhino
by Louis P.Santillan <lpsantil@gmail.com>
This is in the public domain.
*/
;
// node structs
function Syntax(set, title, comment) {
this.set = set;
this.title = title || "";
this.comment = comment || "";
}
Syntax.prototype.toString = function() {
var l = "\n---------------------------------------------------------\n";
return l + "-- " + this.title + l + this.set + l +"comment:\n" + this.comment + l;
}
Syntax.prototype.nodetype="SYNTAX";
var PRODUCTION_SET = {
"label" : "@@",
"join" : function(i) { return "\n" + i + "| "; },
"nodetype": "PRODUCTION_SET"
};
var TERM_SET = {
"label" : "",
"join" : function(i) { return (i==0) ? "" : "|";},
"nodetype": "TERM_SET"
};
var FACTOR_SET = {
"label" : "",
"join" : function(i) { return ""; },
"nodetype": "FACTOR_SET"
};
function Set(t) {
this.label = t.label;
this.join = t.join;
this.nodetype = t.nodetype;
this.children = new Array();
}
Set.prototype.addChild = function(node) {
this.children.push(node);
};;
Set.prototype.toString = function() {
var cnt = this.children.length;
var o = this.label;
for (var i = 0; i < cnt; i++)
o += this.join(i) + this.children[i].toString();
return o;
}
function Production(id, expr) {
this.id = id;
this.expr = expr;
}
Production.prototype.toString = function(ind) {
ind = ind || "";
return ind + this.id + "=" + this.expr + " ." ;
}
Production.prototype.nodetype="PRODUCTION";
function Identifier(id) {
this.id = id;
}
Identifier.prototype.toString = function() {
return this.id;
};
Identifier.prototype.nodetype="IDENTIFIER";
function Literal(txt) {
this.txt = txt;
}
Literal.prototype.toString = function() {
return "\"" + this.txt + "\"";
};
Literal.prototype.nodetype="LITERAL";
function Optional(expr) {
this.expr = expr;
}
Optional.prototype.toString = function() {
return "[" + this.expr + "]";
};
Optional.prototype.nodetype="OPTIONAL";
function Repeating(expr) {
this.expr = expr;
}
Repeating.prototype.toString = function() {
return "{" + this.expr + "}";
};
Repeating.prototype.nodetype="REPEATING";
function Group(expr) {
this.expr = expr;
}
Group.prototype.toString = function() {
return "(" + this.expr + ")";
};
Group.prototype.nodetype="GROUP";
// node factories
function createNode(type, childs) {
var children = new Array();
for( var i = 2; i < arguments.length; i++ )
children.push( arguments[i] );
return new Node(type, children);
};
function createSyntax(set, title, comment) {
return new Syntax(set, title, comment);
};
function createSet() {
return new Set(PRODUCTION_SET);
}
function createProduction(id, expr) {
return new Production(id, expr);
}
function createExpression() {
return new Set(TERM_SET);
}
function createTerm() {
return new Set(FACTOR_SET);
}
function createIdentifier(id) {
return new Identifier(id);
}
function createLiteral(txt) {
return new Literal(txt);
}
function createOptional(termset) {
return new Optional(termset);
}
function createRepeating(termset) {
return new Repeating(termset);
}
function createGroup(termset) {
return new Group(termset);
}
var parseComplete = function(syn){};
var _dbg_withparsetree = false;
var _dbg_withtrace = false;
var _dbg_withstepbystep = false;
function __dbg_print( text )
{
print( text );
}
function __dbg_wait()
{
var kbd = new java.io.BufferedReader(
new java.io.InputStreamReader( java.lang.System[ "in" ] ) );
kbd.readLine();
}
function __lex( info )
{
var state = 0;
var match = -1;
var match_pos = 0;
var start = 0;
var pos = info.offset + 1;
do
{
pos--;
state = 0;
match = -2;
start = pos;
if( info.src.length <= start )
return 21;
do
{
switch( state )
{
case 0:
if( ( info.src.charCodeAt( pos ) >= 9 && info.src.charCodeAt( pos ) <= 10 ) || info.src.charCodeAt( pos ) == 13 || info.src.charCodeAt( pos ) == 32 ) state = 1;
else if( info.src.charCodeAt( pos ) == 40 ) state = 2;
else if( info.src.charCodeAt( pos ) == 41 ) state = 3;
else if( info.src.charCodeAt( pos ) == 44 ) state = 4;
else if( info.src.charCodeAt( pos ) == 46 || info.src.charCodeAt( pos ) == 59 ) state = 5;
else if( info.src.charCodeAt( pos ) == 61 ) state = 6;
else if( ( info.src.charCodeAt( pos ) >= 65 && info.src.charCodeAt( pos ) <= 90 ) || info.src.charCodeAt( pos ) == 95 || ( info.src.charCodeAt( pos ) >= 97 && info.src.charCodeAt( pos ) <= 122 ) ) state = 7;
else if( info.src.charCodeAt( pos ) == 91 ) state = 8;
else if( info.src.charCodeAt( pos ) == 93 ) state = 9;
else if( info.src.charCodeAt( pos ) == 123 ) state = 10;
else if( info.src.charCodeAt( pos ) == 124 ) state = 11;
else if( info.src.charCodeAt( pos ) == 125 ) state = 12;
else if( info.src.charCodeAt( pos ) == 34 ) state = 14;
else if( info.src.charCodeAt( pos ) == 39 ) state = 16;
else state = -1;
break;
case 1:
state = -1;
match = 1;
match_pos = pos;
break;
case 2:
state = -1;
match = 9;
match_pos = pos;
break;
case 3:
state = -1;
match = 10;
match_pos = pos;
break;
case 4:
state = -1;
match = 3;
match_pos = pos;
break;
case 5:
state = -1;
match = 11;
match_pos = pos;
break;
case 6:
state = -1;
match = 2;
match_pos = pos;
break;
case 7:
if( ( info.src.charCodeAt( pos ) >= 48 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 65 && info.src.charCodeAt( pos ) <= 90 ) || info.src.charCodeAt( pos ) == 95 || ( info.src.charCodeAt( pos ) >= 97 && info.src.charCodeAt( pos ) <= 122 ) ) state = 7;
else state = -1;
match = 12;
match_pos = pos;
break;
case 8:
state = -1;
match = 7;
match_pos = pos;
break;
case 9:
state = -1;
match = 8;
match_pos = pos;
break;
case 10:
state = -1;
match = 5;
match_pos = pos;
break;
case 11:
state = -1;
match = 4;
match_pos = pos;
break;
case 12:
state = -1;
match = 6;
match_pos = pos;
break;
case 13:
state = -1;
match = 13;
match_pos = pos;
break;
case 14:
if( info.src.charCodeAt( pos ) == 34 ) state = 13;
else if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 14;
else if( info.src.charCodeAt( pos ) == 92 ) state = 17;
else state = -1;
break;
case 15:
if( info.src.charCodeAt( pos ) == 34 ) state = 13;
else if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 14;
else if( info.src.charCodeAt( pos ) == 92 ) state = 17;
else state = -1;
match = 13;
match_pos = pos;
break;
case 16:
if( info.src.charCodeAt( pos ) == 39 ) state = 13;
else if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 38 ) || ( info.src.charCodeAt( pos ) >= 40 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 16;
else if( info.src.charCodeAt( pos ) == 92 ) state = 18;
else state = -1;
break;
case 17:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 14;
else if( info.src.charCodeAt( pos ) == 34 ) state = 15;
else if( info.src.charCodeAt( pos ) == 92 ) state = 17;
else state = -1;
break;
case 18:
if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 254 ) ) state = 16;
else state = -1;
break;
}
pos++;
}
while( state > -1 );
}
while( 1 > -1 && match == 1 );
if( match > -1 )
{
info.att = info.src.substr( start, match_pos - start );
info.offset = match_pos;
switch( match )
{
case 13:
{
;
info.att = info.att.substr( 1, info.att.length - 2 );
info.att = info.att.replace(/\\\'/g,"\'").replace(/\\\"/g,"\"").replace(/\\\\/g,"\\");
}
break;
}
}
else
{
info.att = new String();
match = -1;
}
return match;
}
function __parse( src, err_off, err_la )
{
var sstack = new Array();
var vstack = new Array();
var err_cnt = 0;
var act;
var go;
var la;
var rval;
var parseinfo = new Function( "", "var offset; var src; var att;" );
var info = new parseinfo();
//Visual parse tree generation
var treenode = new Function( "", "var sym; var att; var child;" );
var treenodes = new Array();
var tree = new Array();
var tmptree = null;
/* Pop-Table */
var pop_tab = new Array(
new Array( 0/* root' */, 1 ),
new Array( 15/* root */, 1 ),
new Array( 15/* root */, 0 ),
new Array( 14/* syntax */, 5 ),
new Array( 14/* syntax */, 4 ),
new Array( 14/* syntax */, 4 ),
new Array( 14/* syntax */, 3 ),
new Array( 16/* productionset */, 1 ),
new Array( 16/* productionset */, 2 ),
new Array( 17/* production */, 4 ),
new Array( 18/* expression */, 1 ),
new Array( 18/* expression */, 3 ),
new Array( 19/* term */, 1 ),
new Array( 19/* term */, 2 ),
new Array( 20/* factor */, 1 ),
new Array( 20/* factor */, 1 ),
new Array( 20/* factor */, 3 ),
new Array( 20/* factor */, 3 ),
new Array( 20/* factor */, 3 )
);
/* Action-Table */
var act_tab = new Array(
/* State 0 */ new Array( 13/* "Literal" */,3 , 5/* "{" */,4 , 21/* "$" */,-2 ),
/* State 1 */ new Array( 21/* "$" */,0 ),
/* State 2 */ new Array( 21/* "$" */,-1 ),
/* State 3 */ new Array( 5/* "{" */,5 ),
/* State 4 */ new Array( 12/* "Identifier" */,8 ),
/* State 5 */ new Array( 12/* "Identifier" */,8 ),
/* State 6 */ new Array( 6/* "}" */,11 , 12/* "Identifier" */,8 ),
/* State 7 */ new Array( 6/* "}" */,-7 , 12/* "Identifier" */,-7 ),
/* State 8 */ new Array( 2/* "=" */,12 ),
/* State 9 */ new Array( 6/* "}" */,13 , 12/* "Identifier" */,8 ),
/* State 10 */ new Array( 6/* "}" */,-8 , 12/* "Identifier" */,-8 ),
/* State 11 */ new Array( 13/* "Literal" */,14 , 21/* "$" */,-6 ),
/* State 12 */ new Array( 12/* "Identifier" */,18 , 13/* "Literal" */,19 , 7/* "[" */,20 , 5/* "{" */,21 , 9/* "(" */,22 ),
/* State 13 */ new Array( 13/* "Literal" */,23 , 21/* "$" */,-4 ),
/* State 14 */ new Array( 21/* "$" */,-5 ),
/* State 15 */ new Array( 4/* "|" */,24 , 11/* "Terminator" */,25 ),
/* State 16 */ new Array( 12/* "Identifier" */,18 , 13/* "Literal" */,19 , 7/* "[" */,20 , 5/* "{" */,21 , 9/* "(" */,22 , 11/* "Terminator" */,-10 , 4/* "|" */,-10 , 8/* "]" */,-10 , 6/* "}" */,-10 , 10/* ")" */,-10 ),
/* State 17 */ new Array( 11/* "Terminator" */,-12 , 12/* "Identifier" */,-12 , 13/* "Literal" */,-12 , 7/* "[" */,-12 , 5/* "{" */,-12 , 9/* "(" */,-12 , 4/* "|" */,-12 , 8/* "]" */,-12 , 6/* "}" */,-12 , 10/* ")" */,-12 ),
/* State 18 */ new Array( 11/* "Terminator" */,-14 , 12/* "Identifier" */,-14 , 13/* "Literal" */,-14 , 7/* "[" */,-14 , 5/* "{" */,-14 , 9/* "(" */,-14 , 4/* "|" */,-14 , 8/* "]" */,-14 , 6/* "}" */,-14 , 10/* ")" */,-14 ),
/* State 19 */ new Array( 11/* "Terminator" */,-15 , 12/* "Identifier" */,-15 , 13/* "Literal" */,-15 , 7/* "[" */,-15 , 5/* "{" */,-15 , 9/* "(" */,-15 , 4/* "|" */,-15 , 8/* "]" */,-15 , 6/* "}" */,-15 , 10/* ")" */,-15 ),
/* State 20 */ new Array( 12/* "Identifier" */,18 , 13/* "Literal" */,19 , 7/* "[" */,20 , 5/* "{" */,21 , 9/* "(" */,22 ),
/* State 21 */ new Array( 12/* "Identifier" */,18 , 13/* "Literal" */,19 , 7/* "[" */,20 , 5/* "{" */,21 , 9/* "(" */,22 ),
/* State 22 */ new Array( 12/* "Identifier" */,18 , 13/* "Literal" */,19 , 7/* "[" */,20 , 5/* "{" */,21 , 9/* "(" */,22 ),
/* State 23 */ new Array( 21/* "$" */,-3 ),
/* State 24 */ new Array( 12/* "Identifier" */,18 , 13/* "Literal" */,19 , 7/* "[" */,20 , 5/* "{" */,21 , 9/* "(" */,22 ),
/* State 25 */ new Array( 6/* "}" */,-9 , 12/* "Identifier" */,-9 ),
/* State 26 */ new Array( 11/* "Terminator" */,-13 , 12/* "Identifier" */,-13 , 13/* "Literal" */,-13 , 7/* "[" */,-13 , 5/* "{" */,-13 , 9/* "(" */,-13 , 4/* "|" */,-13 , 8/* "]" */,-13 , 6/* "}" */,-13 , 10/* ")" */,-13 ),
/* State 27 */ new Array( 4/* "|" */,24 , 8/* "]" */,31 ),
/* State 28 */ new Array( 4/* "|" */,24 , 6/* "}" */,32 ),
/* State 29 */ new Array( 4/* "|" */,24 , 10/* ")" */,33 ),
/* State 30 */ new Array( 12/* "Identifier" */,18 , 13/* "Literal" */,19 , 7/* "[" */,20 , 5/* "{" */,21 , 9/* "(" */,22 , 11/* "Terminator" */,-11 , 4/* "|" */,-11 , 8/* "]" */,-11 , 6/* "}" */,-11 , 10/* ")" */,-11 ),
/* State 31 */ new Array( 11/* "Terminator" */,-16 , 12/* "Identifier" */,-16 , 13/* "Literal" */,-16 , 7/* "[" */,-16 , 5/* "{" */,-16 , 9/* "(" */,-16 , 4/* "|" */,-16 , 8/* "]" */,-16 , 6/* "}" */,-16 , 10/* ")" */,-16 ),
/* State 32 */ new Array( 11/* "Terminator" */,-17 , 12/* "Identifier" */,-17 , 13/* "Literal" */,-17 , 7/* "[" */,-17 , 5/* "{" */,-17 , 9/* "(" */,-17 , 4/* "|" */,-17 , 8/* "]" */,-17 , 6/* "}" */,-17 , 10/* ")" */,-17 ),
/* State 33 */ new Array( 11/* "Terminator" */,-18 , 12/* "Identifier" */,-18 , 13/* "Literal" */,-18 , 7/* "[" */,-18 , 5/* "{" */,-18 , 9/* "(" */,-18 , 4/* "|" */,-18 , 8/* "]" */,-18 , 6/* "}" */,-18 , 10/* ")" */,-18 )
);
/* Goto-Table */
var goto_tab = new Array(
/* State 0 */ new Array( 15/* root */,1 , 14/* syntax */,2 ),
/* State 1 */ new Array( ),
/* State 2 */ new Array( ),
/* State 3 */ new Array( ),
/* State 4 */ new Array( 16/* productionset */,6 , 17/* production */,7 ),
/* State 5 */ new Array( 16/* productionset */,9 , 17/* production */,7 ),
/* State 6 */ new Array( 17/* production */,10 ),
/* State 7 */ new Array( ),
/* State 8 */ new Array( ),
/* State 9 */ new Array( 17/* production */,10 ),
/* State 10 */ new Array( ),
/* State 11 */ new Array( ),
/* State 12 */ new Array( 18/* expression */,15 , 19/* term */,16 , 20/* factor */,17 ),
/* State 13 */ new Array( ),
/* State 14 */ new Array( ),
/* State 15 */ new Array( ),
/* State 16 */ new Array( 20/* factor */,26 ),
/* State 17 */ new Array( ),
/* State 18 */ new Array( ),
/* State 19 */ new Array( ),
/* State 20 */ new Array( 18/* expression */,27 , 19/* term */,16 , 20/* factor */,17 ),
/* State 21 */ new Array( 18/* expression */,28 , 19/* term */,16 , 20/* factor */,17 ),
/* State 22 */ new Array( 18/* expression */,29 , 19/* term */,16 , 20/* factor */,17 ),
/* State 23 */ new Array( ),
/* State 24 */ new Array( 19/* term */,30 , 20/* factor */,17 ),
/* State 25 */ new Array( ),
/* State 26 */ new Array( ),
/* State 27 */ new Array( ),
/* State 28 */ new Array( ),
/* State 29 */ new Array( ),
/* State 30 */ new Array( 20/* factor */,26 ),
/* State 31 */ new Array( ),
/* State 32 */ new Array( ),
/* State 33 */ new Array( )
);
/* Symbol labels */
var labels = new Array(
"root'" /* Non-terminal symbol */,
"WHITESPACE" /* Terminal symbol */,
"=" /* Terminal symbol */,
"," /* Terminal symbol */,
"|" /* Terminal symbol */,
"{" /* Terminal symbol */,
"}" /* Terminal symbol */,
"[" /* Terminal symbol */,
"]" /* Terminal symbol */,
"(" /* Terminal symbol */,
")" /* Terminal symbol */,
"Terminator" /* Terminal symbol */,
"Identifier" /* Terminal symbol */,
"Literal" /* Terminal symbol */,
"syntax" /* Non-terminal symbol */,
"root" /* Non-terminal symbol */,
"productionset" /* Non-terminal symbol */,
"production" /* Non-terminal symbol */,
"expression" /* Non-terminal symbol */,
"term" /* Non-terminal symbol */,
"factor" /* Non-terminal symbol */,
"$" /* Terminal symbol */
);
info.offset = 0;
info.src = src;
info.att = new String();
if( !err_off )
err_off = new Array();
if( !err_la )
err_la = new Array();
sstack.push( 0 );
vstack.push( 0 );
la = __lex( info );
while( true )
{
act = 35;
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( act_tab[sstack[sstack.length-1]][i] == la )
{
act = act_tab[sstack[sstack.length-1]][i+1];
break;
}
}
/*
_print( "state " + sstack[sstack.length-1] + " la = " + la + " info.att = >" +
info.att + "< act = " + act + " src = >" + info.src.substr( info.offset, 30 ) + "..." + "<" +
" sstack = " + sstack.join() );
*/
if( _dbg_withtrace && sstack.length > 0 )
{
__dbg_print( "\nState " + sstack[sstack.length-1] + "\n" +
"\tLookahead: " + labels[la] + " (\"" + info.att + "\")\n" +
"\tAction: " + act + "\n" +
"\tSource: \"" + info.src.substr( info.offset, 30 ) + ( ( info.offset + 30 < info.src.length ) ?
"..." : "" ) + "\"\n" +
"\tStack: " + sstack.join() + "\n" +
"\tValue stack: " + vstack.join() + "\n" );
if( _dbg_withstepbystep )
__dbg_wait();
}
//Panic-mode: Try recovery when parse-error occurs!
if( act == 35 )
{
if( _dbg_withtrace )
__dbg_print( "Error detected: There is no reduce or shift on the symbol " + labels[la] );
err_cnt++;
err_off.push( info.offset - info.att.length );
err_la.push( new Array() );
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
err_la[err_la.length-1].push( labels[act_tab[sstack[sstack.length-1]][i]] );
//Remember the original stack!
var rsstack = new Array();
var rvstack = new Array();
for( var i = 0; i < sstack.length; i++ )
{
rsstack[i] = sstack[i];
rvstack[i] = vstack[i];
}
while( act == 35 && la != 21 )
{
if( _dbg_withtrace )
__dbg_print( "\tError recovery\n" +
"Current lookahead: " + labels[la] + " (" + info.att + ")\n" +
"Action: " + act + "\n\n" );
if( la == -1 )
info.offset++;
while( act == 35 && sstack.length > 0 )
{
sstack.pop();
vstack.pop();
if( sstack.length == 0 )
break;
act = 35;
for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( act_tab[sstack[sstack.length-1]][i] == la )
{
act = act_tab[sstack[sstack.length-1]][i+1];
break;
}
}
}
if( act != 35 )
break;
for( var i = 0; i < rsstack.length; i++ )
{
sstack.push( rsstack[i] );
vstack.push( rvstack[i] );
}
la = __lex( info );
}
if( act == 35 )
{
if( _dbg_withtrace )
__dbg_print( "\tError recovery failed, terminating parse process..." );
break;
}
if( _dbg_withtrace )
__dbg_print( "\tError recovery succeeded, continuing" );
}
/*
if( act == 35 )
break;
*/
//Shift
if( act > 0 )
{
//Parse tree generation
if( _dbg_withparsetree )
{
var node = new treenode();
node.sym = labels[ la ];
node.att = info.att;
node.child = new Array();
tree.push( treenodes.length );
treenodes.push( node );
}
if( _dbg_withtrace )
__dbg_print( "Shifting symbol: " + labels[la] + " (" + info.att + ")" );
sstack.push( act );
vstack.push( info.att );
la = __lex( info );
if( _dbg_withtrace )
__dbg_print( "\tNew lookahead symbol: " + labels[la] + " (" + info.att + ")" );
}
//Reduce
else
{
act *= -1;
if( _dbg_withtrace )
__dbg_print( "Reducing by producution: " + act );
rval = void(0);
if( _dbg_withtrace )
__dbg_print( "\tPerforming semantic action..." );
switch( act )
{
case 0:
{
rval = vstack[ vstack.length - 1 ];
}
break;
case 1:
{
parseComplete(vstack[ vstack.length - 1 ]);
}
break;
case 2:
{
rval = vstack[ vstack.length - 0 ];
}
break;
case 3:
{
rval = createSyntax( vstack[ vstack.length - 3 ], vstack[ vstack.length - 5 ], vstack[ vstack.length - 1 ]);
}
break;
case 4:
{
rval = createSyntax( vstack[ vstack.length - 2 ], vstack[ vstack.length - 4 ]);
}
break;
case 5:
{
rval = createSyntax( vstack[ vstack.length - 3 ], undefined, vstack[ vstack.length - 1 ]);
}
break;
case 6:
{
rval = createSyntax( vstack[ vstack.length - 2 ]);
}
break;
case 7:
{
rval = createSet(); rval.addChild(vstack[ vstack.length - 1 ]);
}
break;
case 8:
{
rval = vstack[ vstack.length - 2 ]; rval.addChild(vstack[ vstack.length - 1 ]);
}
break;
case 9:
{
rval = createProduction(vstack[ vstack.length - 4 ], vstack[ vstack.length - 2 ]);
}
break;
case 10:
{
rval = createExpression(); rval.addChild(vstack[ vstack.length - 1 ]);
}
break;
case 11:
{
rval = vstack[ vstack.length - 3 ]; rval.addChild(vstack[ vstack.length - 1 ]);
}
break;
case 12:
{
rval = createTerm(); rval.addChild(vstack[ vstack.length - 1 ]);
}
break;
case 13:
{
rval = vstack[ vstack.length - 2 ]; vstack[ vstack.length - 2 ].addChild(vstack[ vstack.length - 1 ]);
}
break;
case 14:
{
rval = createIdentifier(vstack[ vstack.length - 1 ]);
}
break;
case 15:
{
rval = createLiteral(vstack[ vstack.length - 1 ]);
}
break;
case 16:
{
rval = createOptional(vstack[ vstack.length - 2 ]);
}
break;
case 17:
{
rval = createRepeating(vstack[ vstack.length - 2 ]);
}
break;
case 18:
{
rval = createGroup(vstack[ vstack.length - 2 ]);
}
break;
}
if( _dbg_withparsetree )
tmptree = new Array();
if( _dbg_withtrace )
__dbg_print( "\tPopping " + pop_tab[act][1] + " off the stack..." );
for( var i = 0; i < pop_tab[act][1]; i++ )
{
if( _dbg_withparsetree )
tmptree.push( tree.pop() );
sstack.pop();
vstack.pop();
}
go = -1;
for( var i = 0; i < goto_tab[sstack[sstack.length-1]].length; i+=2 )
{
if( goto_tab[sstack[sstack.length-1]][i] == pop_tab[act][0] )
{
go = goto_tab[sstack[sstack.length-1]][i+1];
break;
}
}
if( _dbg_withparsetree )
{
var node = new treenode();
node.sym = labels[ pop_tab[act][0] ];
node.att = new String();
node.child = tmptree.reverse();
tree.push( treenodes.length );
treenodes.push( node );
}
if( act == 0 )
break;
if( _dbg_withtrace )
__dbg_print( "\tPushing non-terminal " + labels[ pop_tab[act][0] ] );
sstack.push( go );
vstack.push( rval );
}
}
if( _dbg_withtrace )
__dbg_print( "\nParse complete." );
if( _dbg_withparsetree )
{
if( err_cnt == 0 )
{
__dbg_print( "\n\n--- Parse tree ---" );
__dbg_parsetree( 0, treenodes, tree );
}
else
{
__dbg_print( "\n\nParse tree cannot be viewed. There where parse errors." );
}
}
return err_cnt;
}
function __dbg_parsetree( indent, nodes, tree )
{
var str = new String();
for( var i = 0; i < tree.length; i++ )
{
str = "";
for( var j = indent; j > 0; j-- )
str += "\t";
str += nodes[ tree[i] ].sym;
if( nodes[ tree[i] ].att != "" )
str += " >" + nodes[ tree[i] ].att + "<" ;
__dbg_print( str );
if( nodes[ tree[i] ].child.length > 0 )
__dbg_parsetree( indent + 1, nodes, nodes[ tree[i] ].child );
}
}
function ebnfParse(str, fn, errfn) {
parseComplete = fn || function(syn) {
alert(syn.toString());
};
var parseErrors = errfn || function(err) {
alert("errors#" + err.length);
};
var error_cnt = 0;
var error_off = new Array();
var error_la = new Array();
var errors;
if( ( error_cnt = __parse( str, error_off, error_la ) ) > 0 )
{
errors=new Array();
for( var i = 0; i < error_cnt; i++ ) {
var msg = "Parse error near >" + str.substr( error_off[i], 30 ) +
"<, expecting \"" + error_la[i].join() + "\"" ;
errors.push(msg);
}
parseErrors(errors);
}
}
exports.parse = ebnfParse;
"Gherkin" {
space = "\s" .
I18N_Feature = "Feature" ":" .
I18N_Background = "Background" ":" .
I18N_ScenarioOutline = "Scenario Outline" ":" .
I18N_Scenario = "Scenario" ":" .
I18N_Step = ( "Given" | "When" | "Then" | "And" | "But" ) ":" .
I18N_Examples = "Examples" ":" .
FeatureHeadingEnd = EOL { EOL } { space } ( I18N_Feature | I18N_Background | I18N_Scenario | I18N_ScenarioOutline | I18N_Examples | "@" | "#" | "EOF" ) .
ScenarioHeadingEnd = EOL { EOL } { space } ( I18N_Feature | I18N_Background | I18N_Scenario | I18N_ScenarioOutline | I18N_Step | "@" | "#" | "EOF" ) .
ScenarioOutlineHeadingEnd = EOL { EOL } { space } ( I18N_Feature | I18N_Scenario | I18N_Step | "@" | "#" | "EOF" ) .
BackgroundHeadingEnd = EOL { EOL } { space } ( I18N_Feature | I18N_Scenario | I18N_ScenarioOutline | I18N_Step | "@" | "#" | "EOF" ) .
ExamplesHeadingEnd = EOL { EOL } { space } ( I18N_Feature | "|" | "#" ) .
FeatureHeading = { space } I18N_Feature { "^FeatureHeadingEnd" } FeatureHeadingEnd .
ScenarioHeading = { space } I18N_Scenario { "^ScenarioHeadingEnd" } ScenarioHeadingEnd .
ScenarioOutlineHeading = { space } I18N_ScenarioOutline { "^ScenarioOutlineHeadingEnd" } ScenarioOutlineHeadingEnd .
BackgroundHeading = { space } I18N_Background { "^BackgroundHeadingEnd" } BackgroundHeadingEnd .
ExamplesHeading = { space } I18N_Examples { "^ExamplesHeadingEnd" } ExamplesHeadingEnd .
Comment = { space } "#" { "^EOL" } EOL { EOL } .
Tag = "@" "[^@\r\n\t]" { "[^@\r\n\t]" } .
Tags = { space } ( Tag { space } { Tag { space } } ) EOL .
EOL = ("\n" | "\r\n") .
Tokens = { space | EOL } ( Tags | Comment | FeatureHeading | BackgroundHeading | ScenarioHeading | ScenarioOutlineHeading | ExamplesHeading ) .
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment