-
-
Save adiel/1198395 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function EbnfDiagram(gc,width,height) { | |
this.gc = gc; | |
this.styles = STYLES; | |
this.style = this.styles["DIAGRAM"]; | |
this.height = height; | |
this.width = width; | |
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() { | |
// 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; | |
} | |
module.exports = EbnfDiagram; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
http = require('http') | |
Canvas = require('canvas') | |
EbnfDiagram = require('./ebnfdiagram.js') | |
ebnf = require('./ebnf.js') | |
fs = require('fs') | |
width = 2000 | |
height = 6000 | |
canvas = new Canvas(width, height) | |
ctx = canvas.getContext('2d') | |
diagram = new EbnfDiagram(ctx, width, height) | |
fs.readFile(__dirname + '/gherkin.ebnf', 'utf8', (err,gherkin) -> | |
setSyntax = (syn) -> diagram.setSyntax(syn) | |
logErr = (err) -> console.log(err) | |
ebnf.parse(gherkin, setSyntax, logErr) | |
out = fs.createWriteStream(__dirname + '/gherkin.png') | |
stream = canvas.createPNGStream(); | |
stream.on('data', (chunk) -> out.write(chunk)) | |
stream.on('end', () -> console.log('saved png')) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 <[email protected]> | |
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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