Update Jison for table optimizations.

This commit is contained in:
Zachary Carter 2010-03-07 18:48:15 -05:00
parent 659a5436a5
commit 8adbc75811
15 changed files with 154 additions and 150 deletions

View File

@ -1,5 +1,7 @@
Jison Jison
===== =====
* [issues](http://github.com/zaach/jison/issues)
* [discuss](mailto:jison@librelist.com)
An API for creating parsers in JavaScript An API for creating parsers in JavaScript
----------------------------------------- -----------------------------------------
@ -189,8 +191,8 @@ and calc.jison, language grammar
{$$ = $1/$3;} {$$ = $1/$3;}
| e '^' e | e '^' e
{$$ = Math.pow($1, $3);} {$$ = Math.pow($1, $3);}
| '-' e | '-' e %prec UMINUS
{$$ = -$2;} %prec UMINUS {$$ = -$2;}
| '(' e ')' | '(' e ')'
{$$ = $2;} {$$ = $2;}
| NUMBER | NUMBER
@ -288,14 +290,12 @@ Like Bison, Jison can recognize languages described by LALR(1) grammars, though
**LR(1) mode is currently not practical for use with anything other than toy grammars, but that is entirely a consequence of the algorithm used, and may change in the future.* **LR(1) mode is currently not practical for use with anything other than toy grammars, but that is entirely a consequence of the algorithm used, and may change in the future.*
Real world example Real world examples
------------------ ------------------
I wrote a parser for [Orderly][3] using Jison. Some benefits I found were: * [CoffeeScript](http://github.com/jashkenas/coffee-script) uses Jison in its self-compiler.
* [Orderly.js][3] uses Jison for compilation.
- If modeled after the normative language grammar, it is guaranteed to recognize the correct language.
- Adding new syntax is straight forward.
- It was much faster to develop than if I were to attempt implementing a (top-down) parser from scratch. But for others not used to grammar specifications, this might not be the case.
Contributors Contributors
------------ ------------

View File

@ -24,8 +24,8 @@ e
{$$ = $1/$3;} {$$ = $1/$3;}
| e '^' e | e '^' e
{$$ = Math.pow($1, $3);} {$$ = Math.pow($1, $3);}
| '-' e | '-' e %prec UMINUS
{$$ = -$2;} %prec UMINUS {$$ = -$2;}
| '(' e ')' | '(' e ')'
{$$ = $2;} {$$ = $2;}
| NUMBER | NUMBER

View File

@ -392,10 +392,12 @@ lookaheadMixin.first = function first (symbol) {
} else if (symbol instanceof Array) { } else if (symbol instanceof Array) {
var firsts = []; var firsts = [];
for (var i=0,t;t=symbol[i];++i) { for (var i=0,t;t=symbol[i];++i) {
this.first(t).forEach(function first_forEach (e) { if (!this.nonterminals[t]) {
if (firsts.indexOf(e)===-1) if (firsts.indexOf(t) === -1)
firsts.push(e); firsts.push(t);
}); } else {
Set.union(firsts, this.nonterminals[t].first);
}
if (!this.nullable(t)) if (!this.nullable(t))
break; break;
} }
@ -581,9 +583,9 @@ lrGeneratorMixin.ItemSet = Set.prototype.construct({
contains: function (item) { contains: function (item) {
return this.hash_[item.id]; return this.hash_[item.id];
}, },
toValue: function toValue () { valueOf: function toValue () {
var v = this.items_.sort().join('|'); var v = this._items.map(function (a) {return a.id}).sort().join('|');
return (this.toValue = function toValue_inner() {return v;})(); return (this.valueOf = function toValue_inner() {return v;})();
} }
}); });
@ -630,11 +632,10 @@ lrGeneratorMixin.closureOperation = function closureOperation (itemSet /*, closu
lrGeneratorMixin.gotoOperation = function gotoOperation (itemSet, symbol) { lrGeneratorMixin.gotoOperation = function gotoOperation (itemSet, symbol) {
var gotoSet = new this.ItemSet(), var gotoSet = new this.ItemSet(),
EOF = this.EOF,
self = this; self = this;
itemSet.forEach(function goto_forEach(item, n) { itemSet.forEach(function goto_forEach(item, n) {
if (item.markedSymbol === symbol && symbol !== EOF) { if (item.markedSymbol === symbol) {
gotoSet.push(new self.Item(item.production, item.dotPosition+1, item.follows, n)); gotoSet.push(new self.Item(item.production, item.dotPosition+1, item.follows, n));
} }
}); });
@ -652,10 +653,13 @@ lrGeneratorMixin.canonicalCollection = function canonicalCollection () {
self = this, self = this,
itemSet; itemSet;
states.has = {};
states.has[firstState] = 0;
while (marked !== states.size()) { while (marked !== states.size()) {
itemSet = states.item(marked); marked++; itemSet = states.item(marked); marked++;
itemSet.forEach(function CC_itemSet_forEach(item) { itemSet.forEach(function CC_itemSet_forEach (item) {
if(item.markedSymbol) if (item.markedSymbol && item.markedSymbol !== self.EOF)
self.canonicalCollectionInsert(item.markedSymbol, itemSet, states, marked-1); self.canonicalCollectionInsert(item.markedSymbol, itemSet, states, marked-1);
}); });
} }
@ -670,8 +674,10 @@ lrGeneratorMixin.canonicalCollectionInsert = function canonicalCollectionInsert
g.predecessors = {}; g.predecessors = {};
// add g to que if not empty or duplicate // add g to que if not empty or duplicate
if (!g.isEmpty()) { if (!g.isEmpty()) {
var i = states.indexOf(g); var gv = g.valueOf(),
if (i === -1) { i = states.has[gv];
if (i === -1 || typeof i === 'undefined') {
states.has[gv] = states.size();
itemSet.edges[symbol] = states.size(); // store goto transition for table itemSet.edges[symbol] = states.size(); // store goto transition for table
states.push(g); states.push(g);
g.predecessors[symbol] = [stateNum]; g.predecessors[symbol] = [stateNum];
@ -682,6 +688,7 @@ lrGeneratorMixin.canonicalCollectionInsert = function canonicalCollectionInsert
} }
}; };
var NONASSOC = 0;
lrGeneratorMixin.parseTable = function parseTable (itemSets) { lrGeneratorMixin.parseTable = function parseTable (itemSets) {
var states = [], var states = [],
nonterminals = this.nonterminals, nonterminals = this.nonterminals,
@ -708,7 +715,7 @@ lrGeneratorMixin.parseTable = function parseTable (itemSets) {
state[self.symbols_[stackSymbol]] = gotoState; state[self.symbols_[stackSymbol]] = gotoState;
} else { } else {
//self.trace(k, stackSymbol, 's'+gotoState); //self.trace(k, stackSymbol, 's'+gotoState);
state[self.symbols_[stackSymbol]] = [[s,gotoState]]; state[self.symbols_[stackSymbol]] = [s,gotoState];
} }
} }
}); });
@ -718,7 +725,7 @@ lrGeneratorMixin.parseTable = function parseTable (itemSets) {
itemSet.forEach(function (item, j) { itemSet.forEach(function (item, j) {
if (item.markedSymbol == self.EOF) { if (item.markedSymbol == self.EOF) {
// accept // accept
state[self.symbols_[self.EOF]] = [[a]]; state[self.symbols_[self.EOF]] = [a];
//self.trace(k, self.EOF, state[self.EOF]); //self.trace(k, self.EOF, state[self.EOF]);
} }
}); });
@ -731,11 +738,12 @@ lrGeneratorMixin.parseTable = function parseTable (itemSets) {
var terminals = allterms || self.lookAheads(itemSet, item); var terminals = allterms || self.lookAheads(itemSet, item);
terminals.forEach(function (stackSymbol) { terminals.forEach(function (stackSymbol) {
action = state[self.symbols_[stackSymbol]] || []; action = state[self.symbols_[stackSymbol]];
var op = operators[stackSymbol]; var op = operators[stackSymbol];
// Reading a terminal and current position is at the end of a production, try to reduce // Reading a terminal and current position is at the end of a production, try to reduce
if (action.length) { if (action || action && action.length) {
var sol = resolveConflict(item.production, op, [r,item.production.id], action[0]); var sol = resolveConflict(item.production, op, [r,item.production.id], action[0] instanceof Array ? action[0] : action);
self.resolutions.push([k,stackSymbol,sol]); self.resolutions.push([k,stackSymbol,sol]);
if (sol.bydefault) { if (sol.bydefault) {
self.conflicts++; self.conflicts++;
@ -743,16 +751,20 @@ lrGeneratorMixin.parseTable = function parseTable (itemSets) {
self.warn('Conflict in grammar (state:',k, ', token:',stackSymbol, ")\n ", printAction(sol.r, self), "\n ", printAction(sol.s, self)); self.warn('Conflict in grammar (state:',k, ', token:',stackSymbol, ")\n ", printAction(sol.r, self), "\n ", printAction(sol.s, self));
} }
if (self.options.noDefaultResolve) { if (self.options.noDefaultResolve) {
if (!(action[0] instanceof Array))
action = [action];
action.push(sol.r); action.push(sol.r);
} }
} else { } else {
action = [sol.action]; action = sol.action;
} }
} else { } else {
action.push([r,item.production.id]); action = [r,item.production.id];
} }
if (action && action.length) { if (action && action.length) {
state[self.symbols_[stackSymbol]] = action; state[self.symbols_[stackSymbol]] = action;
} else if (action === NONASSOC) {
state[self.symbols_[stackSymbol]] = undefined;
} }
}); });
}); });
@ -773,7 +785,6 @@ function resolveConflict (production, op, reduce, shift) {
sln.msg = "Resolve R/R conflict (use first production declared in grammar.)"; sln.msg = "Resolve R/R conflict (use first production declared in grammar.)";
sln.action = shift[1] < reduce[1] ? shift : reduce; sln.action = shift[1] < reduce[1] ? shift : reduce;
sln.bydefault = true; sln.bydefault = true;
//print(production, reduce[0]);
return sln; return sln;
} }
@ -793,7 +804,7 @@ function resolveConflict (production, op, reduce, shift) {
sln.action = reduce; sln.action = reduce;
} else if (op.assoc === "nonassoc" ) { } else if (op.assoc === "nonassoc" ) {
sln.msg = "Resolve S/R conflict (no action for non-associative operator.)"; sln.msg = "Resolve S/R conflict (no action for non-associative operator.)";
sln.action = undefined; sln.action = NONASSOC;
} }
} else { } else {
sln.msg = "Resolve conflict (reduce for higher precedent production.)"; sln.msg = "Resolve conflict (reduce for higher precedent production.)";
@ -979,7 +990,7 @@ parser.parse = function parse (input) {
// read action for current state and first input // read action for current state and first input
action = table[state] && table[state][symbol]; action = table[state] && table[state][symbol];
if (typeof action == 'undefined' || !action.length || !action[0]) { if (typeof action === 'undefined' || !action.length || !action[0]) {
expected = []; expected = [];
for (p in table[state]) if (this.terminals_[p] && p != 1) { for (p in table[state]) if (this.terminals_[p] && p != 1) {
expected.push("'"+this.terminals_[p]+"'"); expected.push("'"+this.terminals_[p]+"'");
@ -993,14 +1004,12 @@ parser.parse = function parse (input) {
} }
} }
this.trace('action:',action);
// this shouldn't happen, unless resolve defaults are off // this shouldn't happen, unless resolve defaults are off
if (action.length > 1) { if (action[0] instanceof Array && action.length > 1) {
throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
} }
a = action[0]; a = action;
switch (a[0]) { switch (a[0]) {

View File

@ -1,15 +1,16 @@
// converts json grammar format to Jison grammar format // converts json grammar format to Jison grammar format
function json2jison (grammar) { function json2jison (grammar, options) {
options = options || {};
var s = ""; var s = "";
s += genDecls(grammar); s += genDecls(grammar, options);
s += genBNF(grammar.bnf); s += genBNF(grammar.bnf, options);
return s; return s;
} }
function genDecls (grammar) { function genDecls (grammar, options) {
var s = "", var s = "",
key; key;
@ -34,18 +35,18 @@ function genDecls (grammar) {
return s; return s;
} }
function genBNF (bnf) { function genBNF (bnf, options) {
var s = "%%\n", var s = "%%\n",
sym; sym;
for (sym in bnf) if (bnf.hasOwnProperty(sym)) { for (sym in bnf) if (bnf.hasOwnProperty(sym)) {
s += ["\n",sym,'\n : ', genHandles(bnf[sym]),"\n ;\n"].join(""); s += ["\n",sym,'\n : ', genHandles(bnf[sym], options),"\n ;\n"].join("");
} }
return s; return s;
} }
function genHandles (handle) { function genHandles (handle, options) {
if (typeof handle === 'string') { if (typeof handle === 'string') {
return handle; return handle;
} else { //array } else { //array
@ -56,9 +57,11 @@ function genHandles (handle) {
} else if (handle[i] instanceof Array) { } else if (handle[i] instanceof Array) {
s += (handle[i][0] && quoteSymbols(handle[i][0])); s += (handle[i][0] && quoteSymbols(handle[i][0]));
if (typeof handle[i][1] === 'string') { if (typeof handle[i][1] === 'string') {
s += handle[i][1].match(/\}/) ? if (!options.stripActions) {
"\n {{"+handle[i][1]+(handle[i][1].match(/\}$/) ? ' ' : '')+"}}" : s += handle[i][1].match(/\}/) ?
"\n {"+handle[i][1]+"}"; "\n {{"+handle[i][1]+(handle[i][1].match(/\}$/) ? ' ' : '')+"}}" :
"\n {"+handle[i][1]+"}";
}
if (handle[i][2] && handle[i][2].prec) { if (handle[i][2] && handle[i][2].prec) {
s += " %prec "+handle[i][2].prec; s += " %prec "+handle[i][2].prec;
} }

View File

@ -200,7 +200,7 @@ RegExpLexer.prototype = {
out += p.join(",\n"); out += p.join(",\n");
out += "})"; out += "})";
out += ";\nlexer.performAction = "+String(this.performAction); out += ";\nlexer.performAction = "+String(this.performAction);
out += ";\nlexer.rules = "+uneval(this.rules); out += ";\nlexer.rules = [" + this.rules + "]";
out += ";return lexer;})()"; out += ";return lexer;})()";
return out; return out;
}, },

View File

@ -3,9 +3,9 @@ var bnf = (function(){
var parser = {trace: function trace() { var parser = {trace: function trace() {
}, },
yy: {}, yy: {},
symbols_: {"spec":2,"declaration_list":3,"%%":4,"grammar":5,"EOF":6,"declaration":7,"START":8,"id":9,"operator":10,"associativity":11,"token_list":12,"LEFT":13,"RIGHT":14,"NONASSOC":15,"symbol":16,"production_list":17,"production":18,":":19,"handle_list":20,";":21,"|":22,"handle_action":23,"handle":24,"action":25,"prec":26,"PREC":27,"STRING":28,"ID":29,"ACTION":30,"$accept":0,"$end":1}, symbols_: {"spec":2,"declaration_list":3,"%%":4,"grammar":5,"EOF":6,"declaration":7,"START":8,"id":9,"operator":10,"associativity":11,"token_list":12,"LEFT":13,"RIGHT":14,"NONASSOC":15,"symbol":16,"production_list":17,"production":18,":":19,"handle_list":20,";":21,"|":22,"handle_action":23,"handle":24,"prec":25,"action":26,"PREC":27,"STRING":28,"ID":29,"ACTION":30,"$accept":0,"$end":1},
terminals_: {"4":"%%","6":"EOF","8":"START","13":"LEFT","14":"RIGHT","15":"NONASSOC","19":":","21":";","22":"|","27":"PREC","28":"STRING","29":"ID","30":"ACTION"}, terminals_: {"4":"%%","6":"EOF","8":"START","13":"LEFT","14":"RIGHT","15":"NONASSOC","19":":","21":";","22":"|","27":"PREC","28":"STRING","29":"ID","30":"ACTION"},
productions_: [0,[2,4],[2,5],[3,2],[3,0],[7,2],[7,1],[10,2],[11,1],[11,1],[11,1],[12,2],[12,1],[5,1],[17,2],[17,1],[18,4],[20,3],[20,1],[23,3],[24,2],[24,0],[26,2],[26,0],[16,1],[16,1],[9,1],[25,1],[25,0]], productions_: [0,[2,4],[2,5],[3,2],[3,0],[7,2],[7,1],[10,2],[11,1],[11,1],[11,1],[12,2],[12,1],[5,1],[17,2],[17,1],[18,4],[20,3],[20,1],[23,3],[24,2],[24,0],[25,2],[25,0],[16,1],[16,1],[9,1],[26,1],[26,0]],
performAction: function anonymous(yytext, yyleng, yylineno, yy) { performAction: function anonymous(yytext, yyleng, yylineno, yy) {
var $$ = arguments[5], $0 = arguments[5].length; var $$ = arguments[5], $0 = arguments[5].length;
switch (arguments[4]) { switch (arguments[4]) {
@ -75,12 +75,12 @@ performAction: function anonymous(yytext, yyleng, yylineno, yy) {
break; break;
case 19: case 19:
this.$ = [$$[$0 - 3 + 1 - 1].length ? $$[$0 - 3 + 1 - 1].join(" ") : ""]; this.$ = [$$[$0 - 3 + 1 - 1].length ? $$[$0 - 3 + 1 - 1].join(" ") : ""];
if ($$[$0 - 3 + 2 - 1]) {
this.$.push($$[$0 - 3 + 2 - 1]);
}
if ($$[$0 - 3 + 3 - 1]) { if ($$[$0 - 3 + 3 - 1]) {
this.$.push($$[$0 - 3 + 3 - 1]); this.$.push($$[$0 - 3 + 3 - 1]);
} }
if ($$[$0 - 3 + 2 - 1]) {
this.$.push($$[$0 - 3 + 2 - 1]);
}
if (this.$.length === 1) { if (this.$.length === 1) {
this.$ = this.$[0]; this.$ = this.$[0];
} }
@ -116,7 +116,7 @@ performAction: function anonymous(yytext, yyleng, yylineno, yy) {
default:; default:;
} }
}, },
table: [{"2":1,"3":2,"4":[[2,4]],"15":[[2,4]],"14":[[2,4]],"13":[[2,4]],"8":[[2,4]]},{"1":[[3]]},{"4":[[1,3]],"7":4,"8":[[1,5]],"10":6,"11":7,"13":[[1,8]],"14":[[1,9]],"15":[[1,10]]},{"5":11,"17":12,"18":13,"9":14,"29":[[1,15]]},{"4":[[2,3]],"15":[[2,3]],"14":[[2,3]],"13":[[2,3]],"8":[[2,3]]},{"9":16,"29":[[1,15]]},{"8":[[2,6]],"13":[[2,6]],"14":[[2,6]],"15":[[2,6]],"4":[[2,6]]},{"12":17,"16":18,"9":19,"28":[[1,20]],"29":[[1,15]]},{"29":[[2,8]],"28":[[2,8]]},{"29":[[2,9]],"28":[[2,9]]},{"29":[[2,10]],"28":[[2,10]]},{"6":[[1,21]],"4":[[1,22]]},{"18":23,"9":14,"29":[[1,15]],"6":[[2,13]],"4":[[2,13]]},{"4":[[2,15]],"6":[[2,15]],"29":[[2,15]]},{"19":[[1,24]]},{"19":[[2,26]],"4":[[2,26]],"15":[[2,26]],"14":[[2,26]],"13":[[2,26]],"8":[[2,26]],"28":[[2,26]],"29":[[2,26]],"21":[[2,26]],"22":[[2,26]],"27":[[2,26]],"30":[[2,26]]},{"8":[[2,5]],"13":[[2,5]],"14":[[2,5]],"15":[[2,5]],"4":[[2,5]]},{"16":25,"9":19,"28":[[1,20]],"29":[[1,15]],"4":[[2,7]],"15":[[2,7]],"14":[[2,7]],"13":[[2,7]],"8":[[2,7]]},{"8":[[2,12]],"13":[[2,12]],"14":[[2,12]],"15":[[2,12]],"4":[[2,12]],"28":[[2,12]],"29":[[2,12]]},{"29":[[2,24]],"28":[[2,24]],"4":[[2,24]],"15":[[2,24]],"14":[[2,24]],"13":[[2,24]],"8":[[2,24]],"30":[[2,24]],"27":[[2,24]],"22":[[2,24]],"21":[[2,24]]},{"29":[[2,25]],"28":[[2,25]],"4":[[2,25]],"15":[[2,25]],"14":[[2,25]],"13":[[2,25]],"8":[[2,25]],"30":[[2,25]],"27":[[2,25]],"22":[[2,25]],"21":[[2,25]]},{"1":[[2,1]]},{"6":[[1,26]]},{"4":[[2,14]],"6":[[2,14]],"29":[[2,14]]},{"20":27,"23":28,"24":29,"21":[[2,21]],"22":[[2,21]],"27":[[2,21]],"30":[[2,21]],"28":[[2,21]],"29":[[2,21]]},{"8":[[2,11]],"13":[[2,11]],"14":[[2,11]],"15":[[2,11]],"4":[[2,11]],"28":[[2,11]],"29":[[2,11]]},{"1":[[2,2]]},{"21":[[1,30]],"22":[[1,31]]},{"21":[[2,18]],"22":[[2,18]]},{"25":32,"16":33,"30":[[1,34]],"9":19,"28":[[1,20]],"29":[[1,15]],"21":[[2,28]],"22":[[2,28]],"27":[[2,28]]},{"29":[[2,16]],"6":[[2,16]],"4":[[2,16]]},{"23":35,"24":29,"21":[[2,21]],"22":[[2,21]],"27":[[2,21]],"30":[[2,21]],"28":[[2,21]],"29":[[2,21]]},{"26":36,"27":[[1,37]],"21":[[2,23]],"22":[[2,23]]},{"21":[[2,20]],"22":[[2,20]],"27":[[2,20]],"30":[[2,20]],"28":[[2,20]],"29":[[2,20]]},{"21":[[2,27]],"22":[[2,27]],"27":[[2,27]]},{"21":[[2,17]],"22":[[2,17]]},{"22":[[2,19]],"21":[[2,19]]},{"16":38,"9":19,"28":[[1,20]],"29":[[1,15]]},{"21":[[2,22]],"22":[[2,22]]}], table: [{"2":1,"3":2,"4":[2,4],"8":[2,4],"13":[2,4],"14":[2,4],"15":[2,4]},{"1":[3]},{"4":[1,3],"7":4,"8":[1,5],"10":6,"11":7,"13":[1,8],"14":[1,9],"15":[1,10]},{"5":11,"17":12,"18":13,"9":14,"29":[1,15]},{"4":[2,3],"8":[2,3],"13":[2,3],"14":[2,3],"15":[2,3]},{"9":16,"29":[1,15]},{"15":[2,6],"14":[2,6],"13":[2,6],"8":[2,6],"4":[2,6]},{"12":17,"16":18,"9":19,"28":[1,20],"29":[1,15]},{"28":[2,8],"29":[2,8]},{"28":[2,9],"29":[2,9]},{"28":[2,10],"29":[2,10]},{"6":[1,21],"4":[1,22]},{"18":23,"9":14,"29":[1,15],"6":[2,13],"4":[2,13]},{"4":[2,15],"6":[2,15],"29":[2,15]},{"19":[1,24]},{"19":[2,26],"4":[2,26],"8":[2,26],"13":[2,26],"14":[2,26],"15":[2,26],"29":[2,26],"28":[2,26],"21":[2,26],"22":[2,26],"30":[2,26],"27":[2,26]},{"15":[2,5],"14":[2,5],"13":[2,5],"8":[2,5],"4":[2,5]},{"16":25,"9":19,"28":[1,20],"29":[1,15],"4":[2,7],"8":[2,7],"13":[2,7],"14":[2,7],"15":[2,7]},{"15":[2,12],"14":[2,12],"13":[2,12],"8":[2,12],"4":[2,12],"29":[2,12],"28":[2,12]},{"28":[2,24],"29":[2,24],"4":[2,24],"8":[2,24],"13":[2,24],"14":[2,24],"15":[2,24],"27":[2,24],"30":[2,24],"22":[2,24],"21":[2,24]},{"28":[2,25],"29":[2,25],"4":[2,25],"8":[2,25],"13":[2,25],"14":[2,25],"15":[2,25],"27":[2,25],"30":[2,25],"22":[2,25],"21":[2,25]},{"1":[2,1]},{"6":[1,26]},{"4":[2,14],"6":[2,14],"29":[2,14]},{"20":27,"23":28,"24":29,"21":[2,21],"22":[2,21],"30":[2,21],"27":[2,21],"29":[2,21],"28":[2,21]},{"15":[2,11],"14":[2,11],"13":[2,11],"8":[2,11],"4":[2,11],"29":[2,11],"28":[2,11]},{"1":[2,2]},{"21":[1,30],"22":[1,31]},{"21":[2,18],"22":[2,18]},{"25":32,"16":33,"27":[1,34],"9":19,"28":[1,20],"29":[1,15],"21":[2,23],"22":[2,23],"30":[2,23]},{"29":[2,16],"6":[2,16],"4":[2,16]},{"23":35,"24":29,"21":[2,21],"22":[2,21],"30":[2,21],"27":[2,21],"29":[2,21],"28":[2,21]},{"26":36,"30":[1,37],"21":[2,28],"22":[2,28]},{"21":[2,20],"22":[2,20],"30":[2,20],"27":[2,20],"29":[2,20],"28":[2,20]},{"16":38,"9":19,"28":[1,20],"29":[1,15]},{"21":[2,17],"22":[2,17]},{"22":[2,19],"21":[2,19]},{"21":[2,27],"22":[2,27]},{"21":[2,22],"22":[2,22],"30":[2,22]}],
parseError: function parseError(str, hash) { parseError: function parseError(str, hash) {
throw new Error(str); throw new Error(str);
}, },
@ -140,7 +140,7 @@ parse: function parse(input) {
while (true) { while (true) {
state = stack[stack.length - 1]; state = stack[stack.length - 1];
action = table[state] && table[state][symbol]; action = table[state] && table[state][symbol];
if (typeof action == "undefined" || !action.length || !action[0]) { if (typeof action === "undefined" || !action.length || !action[0]) {
expected = []; expected = [];
for (p in table[state]) { for (p in table[state]) {
if (this.terminals_[p] && p != 1) { if (this.terminals_[p] && p != 1) {
@ -153,11 +153,10 @@ parse: function parse(input) {
parseError("Parse error on line " + (yylineno + 1) + ": Unexpected '" + this.terminals_[symbol] + "'", {text: this.lexer.match, token: this.terminals_[symbol], line: this.lexer.yylineno, expected: expected}); parseError("Parse error on line " + (yylineno + 1) + ": Unexpected '" + this.terminals_[symbol] + "'", {text: this.lexer.match, token: this.terminals_[symbol], line: this.lexer.yylineno, expected: expected});
} }
} }
this.trace("action:", action); if (action[0] instanceof Array && action.length > 1) {
if (action.length > 1) {
throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
} }
a = action[0]; a = action;
switch (a[0]) { switch (a[0]) {
case 1: case 1:
shifts++; shifts++;
@ -301,68 +300,72 @@ lexer.performAction = function anonymous(yy, yy_) {
case 0: case 0:
break; break;
case 1: case 1:
return yy.lexComment(this);
break; break;
case 2: case 2:
return 29; return yy.lexComment(this);
break; break;
case 3: case 3:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2); return 29;
return 28;
break; break;
case 4: case 4:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2); yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 28; return 28;
break; break;
case 5: case 5:
return 19; yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 28;
break; break;
case 6: case 6:
return 21; return 19;
break; break;
case 7: case 7:
return 22; return 21;
break; break;
case 8: case 8:
return 4; return 22;
break; break;
case 9: case 9:
return 27; return 4;
break; break;
case 10: case 10:
return 8; return 27;
break; break;
case 11: case 11:
return 13; return 8;
break; break;
case 12: case 12:
return 14; return 13;
break; break;
case 13: case 13:
return 15; return 14;
break; break;
case 14: case 14:
return 15;
break; break;
case 15: case 15:
return yy.lexAction(this);
break; break;
case 16: case 16:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 30;
break; break;
case 17: case 17:
return yy.lexAction(this);
break;
case 18:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2); yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 30; return 30;
break; break;
case 18:
break;
case 19: case 19:
yy_.yytext = yy_.yytext.substr(2, yy_.yytext.length - 4);
return 30;
break;
case 20:
break;
case 21:
return 6; return 6;
break; break;
default:; default:;
} }
}; };
lexer.rules = [/^\s+/, /^\/\*[^*]*\*/, /^[a-zA-Z][a-zA-Z0-9_-]*/, /^"[^"]+"/, /^'[^']+'/, /^:/, /^;/, /^\|/, /^%%/, /^%prec\b/, /^%start\b/, /^%left\b/, /^%right\b/, /^%nonassoc\b/, /^%[a-zA-Z]+[^\n]*/, /^\{\{[^}]*\}/, /^\{[^}]*\}/, /^<[^>]*>/, /^./, /^$/];return lexer;})() lexer.rules = [/^\s+/, /^\/\/.*/, /^\/\*[^*]*\*/, /^[a-zA-Z][a-zA-Z0-9_-]*/, /^"[^"]+"/, /^'[^']+'/, /^:/, /^;/, /^\|/, /^%%/, /^%prec\b/, /^%start\b/, /^%left\b/, /^%right\b/, /^%nonassoc\b/, /^%[a-zA-Z]+[^\n]*/, /^<[a-zA-Z]*>/, /^\{\{[^}]*\}/, /^\{[^}]*\}/, /^%\{(.|\n)*?%\}/, /^./, /^$/];return lexer;})()
parser.lexer = lexer; parser.lexer = lexer;
return parser; return parser;
})(); })();

File diff suppressed because one or more lines are too long

View File

@ -11,14 +11,14 @@ declaration_list
: declaration_list declaration : declaration_list declaration
{$$ = $1; yy.addDeclaration($$, $2);} {$$ = $1; yy.addDeclaration($$, $2);}
| |
<$$ = {};> {{$$ = {};}}
; ;
declaration declaration
: START id : START id
<$$ = {start: $2};> {{$$ = {start: $2};}}
| operator | operator
<$$ = {operator: $1};> {{$$ = {operator: $1};}}
; ;
operator operator
@ -51,7 +51,7 @@ production_list
: production_list production : production_list production
{$$ = $1; $$[$2[0]] = $2[1];} {$$ = $1; $$[$2[0]] = $2[1];}
| production | production
<$$ = {}; $$[$1[0]] = $1[1];> {{$$ = {}; $$[$1[0]] = $1[1];}}
; ;
production production
@ -67,10 +67,10 @@ handle_list
; ;
handle_action handle_action
: handle action prec : handle prec action
{$$ = [($1.length ? $1.join(' ') : '')]; {$$ = [($1.length ? $1.join(' ') : '')];
if($2) $$.push($2);
if($3) $$.push($3); if($3) $$.push($3);
if($2) $$.push($2);
if ($$.length === 1) $$ = $$[0]; if ($$.length === 1) $$ = $$[0];
} }
; ;
@ -84,7 +84,7 @@ handle
prec prec
: PREC symbol : PREC symbol
<$$ = {prec: $2};> {{$$ = {prec: $2};}}
| |
{$$ = null;} {$$ = null;}
; ;

View File

@ -1,6 +1,7 @@
%% %%
\s+ {/* skip whitespace */} \s+ {/* skip whitespace */}
"//".* {/* skip comment */}
"/*"[^*]*"*" {return yy.lexComment(this);} "/*"[^*]*"*" {return yy.lexComment(this);}
[a-zA-Z][a-zA-Z0-9_-]* {return 'ID';} [a-zA-Z][a-zA-Z0-9_-]* {return 'ID';}
'"'[^"]+'"' {yytext = yytext.substr(1, yyleng-2); return 'STRING';} '"'[^"]+'"' {yytext = yytext.substr(1, yyleng-2); return 'STRING';}
@ -15,9 +16,10 @@
"%right" {return 'RIGHT';} "%right" {return 'RIGHT';}
"%nonassoc" {return 'NONASSOC';} "%nonassoc" {return 'NONASSOC';}
"%"[a-zA-Z]+[^\n]* {/* ignore unrecognized decl */} "%"[a-zA-Z]+[^\n]* {/* ignore unrecognized decl */}
"<"[a-zA-Z]*">" { /* ignore type */}
"{{"[^}]*"}" {return yy.lexAction(this);} "{{"[^}]*"}" {return yy.lexAction(this);}
"{"[^}]*"}" {yytext = yytext.substr(1, yyleng-2); return 'ACTION';} "{"[^}]*"}" {yytext = yytext.substr(1, yyleng-2); return 'ACTION';}
"<"[^>]*">" {yytext = yytext.substr(1, yyleng-2); return 'ACTION';} "%{"(.|\n)*?"%}" {yytext = yytext.substr(2, yytext.length-4);return 'ACTION';}
. {/* ignore bad characters */} . {/* ignore bad characters */}
<<EOF>> {return 'EOF';} <<EOF>> {return 'EOF';}

View File

@ -1,24 +0,0 @@
{
"rules": [
["\\s+", "/* skip whitespace */"],
["\\/\\*[^*]*\\*", "return yy.lexComment(this);"],
["[a-zA-Z][a-zA-Z0-9_-]*", "return 'ID';"],
["\"[^\"]+\"", "yytext = yytext.substr(1, yyleng-2); return 'STRING';"],
["'[^']+'", "yytext = yytext.substr(1, yyleng-2); return 'STRING';"],
[":", "return ':';"],
[";", "return ';';"],
["\\|", "return '|';"],
["%%", "return '%%';"],
["%prec\\b", "return 'PREC';"],
["%start\\b", "return 'START';"],
["%left\\b", "return 'LEFT';"],
["%right\\b", "return 'RIGHT';"],
["%nonassoc\\b", "return 'NONASSOC';"],
["%[a-zA-Z]+[^\\n]*", "/* ignore unrecognized decl */"],
["\\{\\{[^}]*\\}", "return yy.lexAction(this);"],
["\\{[^}]*\\}", "yytext = yytext.substr(1, yyleng-2); return 'ACTION';"],
["<[^>]*>", "yytext = yytext.substr(1, yyleng-2); return 'ACTION';"],
[".", "/* ignore bad characters */"],
["$", "return 'EOF';"]
]
}

View File

@ -1,30 +0,0 @@
{
"rules": [
["\\n+", "yy.freshLine = true;"],
["\\s+", "yy.freshLine = false;"],
["y\\{[^}]*\\}", "yytext = yytext.substr(2, yytext.length-3);return 'ACTION';"],
["[a-zA-Z_][a-zA-Z0-9_-]*", "return 'NAME';"],
["\"(?:[^\"]|\\\\\")*\"", "return 'STRING_LIT';"],
["'(?:[^']|\\\\')*'", "return 'STRING_LIT';"],
["\\|", "return '|';"],
["\\[(?:\\\\\\]|[^\\]])*\\]", "return 'ANY_GROUP_REGEX';"],
["\\(", "return '(';"],
["\\)", "return ')';"],
["\\+", "return '+';"],
["\\*", "return '*';"],
["\\?", "return '?';"],
["\\^", "return '^';"],
["\\/", "return '/';"],
["\\\\[a-zA-Z0]", "return 'ESCAPE_CHAR';"],
["\\$", "return '$';"],
["<<EOF>>", "return '$';"],
["\\.", "return '.';"],
["%%", "return '%%';"],
["\\{\\d+(?:,\\s?\\d+|,)?\\}", "return 'RANGE_REGEX';"],
["(?=\\{)", "if(yy.freshLine){this.input('{');return '{';} else this.unput('y');"],
["\\}", "return '}';"],
["%\\{(?:.|\\n)*?\\}%", "yytext = yytext.substr(2, yytext.length-4);return 'ACTION';"],
[".", "/* ignore bad characters */"],
["$", "return 'EOF';"]
]
}

View File

@ -17,7 +17,7 @@ exports["test classy grammar"] = function () {
}; };
exports["test advanced grammar"] = function () { exports["test advanced grammar"] = function () {
var grammar = "%% test: foo bar {action} | baz ; hello: world %prec UMINUS ;extra: foo {action} %prec '-' ;"; var grammar = "%% test: foo bar {action} | baz ; hello: world %prec UMINUS ;extra: foo %prec '-' {action} ;";
var expected = {bnf: {test: [["foo bar", "action" ], "baz"], hello: [[ "world", {prec:"UMINUS"} ]], extra: [[ "foo", "action", {prec: "-"} ]]}}; var expected = {bnf: {test: [["foo bar", "action" ], "baz"], hello: [[ "world", {prec:"UMINUS"} ]], extra: [[ "foo", "action", {prec: "-"} ]]}};
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly");
@ -37,8 +37,8 @@ exports["test nullable rule with action"] = function () {
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly");
}; };
exports["test nullable rule with < > delimited action"] = function () { exports["test nullable rule with %{ %} delimited action"] = function () {
var grammar = "%% test: foo bar | <action{}>; hello: world ;"; var grammar = "%% test: foo bar | %{action{}%}; hello: world ;";
var expected = {bnf: {test: ["foo bar", [ "", "action{}" ]], hello: ["world"]}}; var expected = {bnf: {test: ["foo bar", [ "", "action{}" ]], hello: ["world"]}};
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly");
@ -57,9 +57,31 @@ exports["test comment"] = function () {
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly");
}; };
exports["test single line comment"] = function () {
var grammar = "//comment \n %% hello: world ;";
var expected = {bnf: {hello: ["world"]}};
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parse comment");
};
exports["test comment with nested *"] = function () { exports["test comment with nested *"] = function () {
var grammar = "/* comment * not done */ %% hello: /* oh hai */ world ;"; var grammar = "/* comment * not done */ %% hello: /* oh hai */ world ;";
var expected = {bnf: {hello: ["world"]}}; var expected = {bnf: {hello: ["world"]}};
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly"); assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly");
}; };
exports["test token"] = function () {
var grammar = "%token blah\n%% test: foo bar | baz ; hello: world ;";
var expected = {bnf: {test: ["foo bar", "baz"], hello: ["world"]}};
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly");
};
exports["test token with type"] = function () {
var grammar = "%type <type> blah\n%% test: foo bar | baz ; hello: world ;";
var expected = {bnf: {test: ["foo bar", "baz"], hello: ["world"]}};
assert.deepEqual(bnf.parse(grammar), expected, "grammar should be parsed correctly");
};

View File

@ -10,14 +10,14 @@ exports["test basic grammar"] = function () {
}; };
exports["test advanced grammar"] = function () { exports["test advanced grammar"] = function () {
var grammar = "%start foo %% test: foo bar | baz ; hello: world {action} %prec UM;"; var grammar = "%start foo %% test: foo bar | baz ; hello: world %prec UM {action};";
var expected = {start: "foo", bnf: {test: ["foo bar", "baz"], hello: [[ "world", "action", {prec: "UM"} ]]}}; var expected = {start: "foo", bnf: {test: ["foo bar", "baz"], hello: [[ "world", "action", {prec: "UM"} ]]}};
assert.deepEqual(json2jison.convert(bnf.parse(grammar)), json2jison.convert(expected), "grammar should be parsed correctly"); assert.deepEqual(json2jison.convert(bnf.parse(grammar)), json2jison.convert(expected), "grammar should be parsed correctly");
}; };
exports["test actions"] = function () { exports["test actions"] = function () {
var grammar = "%start foo %% test: foo bar | baz ; hello: world {{action{} }} %prec UM;"; var grammar = "%start foo %% test: foo bar | baz ; hello: world %prec UM {{action{} }} ;";
var expected = {start: "foo", bnf: {test: ["foo bar", "baz"], hello: [[ "world", "action{}", {prec: "UM"} ]]}}; var expected = {start: "foo", bnf: {test: ["foo bar", "baz"], hello: [[ "world", "action{}", {prec: "UM"} ]]}};
assert.deepEqual(json2jison.convert(bnf.parse(grammar)), json2jison.convert(expected), "grammar should be parsed correctly"); assert.deepEqual(json2jison.convert(bnf.parse(grammar)), json2jison.convert(expected), "grammar should be parsed correctly");

View File

@ -234,3 +234,23 @@ exports["test jison grammar as string"] = function () {
parser.lexer = new Lexer(lexData); parser.lexer = new Lexer(lexData);
assert.ok(parser.parse('xyx'), "parse xyx"); assert.ok(parser.parse('xyx'), "parse xyx");
}; };
exports["test no default resolve"] = function () {
var grammar = {
tokens: [ 'x' ],
startSymbol: "A",
bnf: {
"A" :[ 'x A',
'' ]
}
};
var gen = new Jison.Generator(grammar, {type: "lr0", noDefaultResolve: true});
var parser = gen.createParser();
parser.lexer = new Lexer(lexData);
assert.ok(gen.table.length == 4, "table has 4 states");
assert.ok(gen.conflicts == 2, "encountered 2 conflicts");
assert["throws"](function () {parser.parse("xx")}, "throws parse error for multiple actions");
};

View File

@ -169,9 +169,9 @@ exports["test LR(1) grammar"] = function () {
}; };
exports["test BNF grammar bootstrap"] = function () { exports["test BNF grammar bootstrap"] = function () {
var grammar = "%%\n\nspec\n : declaration_list '%%' grammar EOF\n {$$ = $1; $$.bnf = $3; return $$;}\n | declaration_list '%%' grammar '%%' EOF\n {$$ = $1; $$.bnf = $3; return $$;}\n ;\n\ndeclaration_list\n : declaration_list declaration\n {$$ = $1; yy.addDeclaration($$, $2);}\n | \n <$$ = {};>\n ;\n\ndeclaration\n : START id\n <$$ = {start: $2};>\n | operator\n <$$ = {operator: $1};>\n ;\n\noperator\n : associativity token_list\n {$$ = [$1]; $$.push.apply($$, $2);}\n ;\n\nassociativity\n : LEFT\n {$$ = 'left';}\n | RIGHT\n {$$ = 'right';}\n | NONASSOC\n {$$ = 'nonassoc';}\n ;\n\ntoken_list\n : token_list symbol\n {$$ = $1; $$.push($2);}\n | symbol\n {$$ = [$1];}\n ;\n\ngrammar\n : production_list\n {$$ = $1;}\n ;\n\nproduction_list\n : production_list production\n {$$ = $1; $$[$2[0]] = $2[1];}\n | production\n <$$ = {}; $$[$1[0]] = $1[1];>\n ;\n\nproduction\n : id ':' handle_list ';'\n {$$ = [$1, $3];}\n ;\n\nhandle_list\n : handle_list '|' handle_action\n {$$ = $1; $$.push($3);}\n | handle_action\n {$$ = [$1];}\n ;\n\nhandle_action\n : handle action prec\n {$$ = [($1.length ? $1.join(' ') : '')];\n if($2) $$.push($2);\n if($3) $$.push($3);\n if ($$.length === 1) $$ = $$[0];\n }\n ;\n\nhandle\n : handle symbol\n {$$ = $1; $$.push($2)}\n | \n {$$ = [];}\n ;\n\nprec\n : PREC symbol\n <$$ = {prec: $2};>\n | \n {$$ = null;}\n ;\n\nsymbol\n : id\n {$$ = $1;}\n | STRING\n {$$ = yytext;}\n ;\n\nid\n : ID\n {$$ = yytext;}\n ;\n\naction\n : ACTION\n {$$ = yytext;}\n | \n {$$ = '';}\n ;\n\n"; var grammar = "%%\n\nspec\n : declaration_list '%%' grammar EOF\n {$$ = $1; $$.bnf = $3; return $$;}\n | declaration_list '%%' grammar '%%' EOF\n {$$ = $1; $$.bnf = $3; return $$;}\n ;\n\ndeclaration_list\n : declaration_list declaration\n {$$ = $1; yy.addDeclaration($$, $2);}\n | \n %{$$ = {};%}\n ;\n\ndeclaration\n : START id\n %{$$ = {start: $2};%}\n | operator\n %{$$ = {operator: $1};%}\n ;\n\noperator\n : associativity token_list\n {$$ = [$1]; $$.push.apply($$, $2);}\n ;\n\nassociativity\n : LEFT\n {$$ = 'left';}\n | RIGHT\n {$$ = 'right';}\n | NONASSOC\n {$$ = 'nonassoc';}\n ;\n\ntoken_list\n : token_list symbol\n {$$ = $1; $$.push($2);}\n | symbol\n {$$ = [$1];}\n ;\n\ngrammar\n : production_list\n {$$ = $1;}\n ;\n\nproduction_list\n : production_list production\n {$$ = $1; $$[$2[0]] = $2[1];}\n | production\n %{$$ = {}; $$[$1[0]] = $1[1];%}\n ;\n\nproduction\n : id ':' handle_list ';'\n {$$ = [$1, $3];}\n ;\n\nhandle_list\n : handle_list '|' handle_action\n {$$ = $1; $$.push($3);}\n | handle_action\n {$$ = [$1];}\n ;\n\nhandle_action\n : handle action prec\n {$$ = [($1.length ? $1.join(' ') : '')];\n if($2) $$.push($2);\n if($3) $$.push($3);\n if ($$.length === 1) $$ = $$[0];\n }\n ;\n\nhandle\n : handle symbol\n {$$ = $1; $$.push($2)}\n | \n {$$ = [];}\n ;\n\nprec\n : PREC symbol\n %{$$ = {prec: $2};%}\n | \n {$$ = null;}\n ;\n\nsymbol\n : id\n {$$ = $1;}\n | STRING\n {$$ = yytext;}\n ;\n\nid\n : ID\n {$$ = yytext;}\n ;\n\naction\n : ACTION\n {$$ = yytext;}\n | \n {$$ = '';}\n ;\n\n";
var lex = "\n%%\n\\s+ \t{/* skip whitespace */}\n\"/*\"[^*]*\"*\" \t{return yy.lexComment(this);}\n[a-zA-Z][a-zA-Z0-9_-]* \t{return 'ID';}\n'\"'[^\"]+'\"' \t{yytext = yytext.substr(1, yyleng-2); return 'STRING';}\n\"'\"[^']+\"'\" \t{yytext = yytext.substr(1, yyleng-2); return 'STRING';}\n\":\" \t{return ':';}\n\";\" \t{return ';';}\n\"|\" \t{return '|';}\n\"%%\" \t{return '%%';}\n\"%prec\" \t{return 'PREC';}\n\"%start\" \t{return 'START';}\n\"%left\" \t{return 'LEFT';}\n\"%right\" \t{return 'RIGHT';}\n\"%nonassoc\" \t{return 'NONASSOC';}\n\"%\"[a-zA-Z]+[^\\n]* \t{/* ignore unrecognized decl */}\n\"{{\"[^}]*\"}\" \t{return yy.lexAction(this);}\n\"{\"[^}]*\"}\" \t{yytext = yytext.substr(1, yyleng-2); return 'ACTION';}\n\"<\"[^>]*\">\" \t{yytext = yytext.substr(1, yyleng-2); return 'ACTION';}\n. \t{/* ignore bad characters */}\n<<EOF>> \t{return 'EOF';}\n\n%%\n\n"; var lex = "\n%%\n\\s+ \t{/* skip whitespace */}\n\"/*\"[^*]*\"*\" \t{return yy.lexComment(this);}\n[a-zA-Z][a-zA-Z0-9_-]* \t{return 'ID';}\n'\"'[^\"]+'\"' \t{yytext = yytext.substr(1, yyleng-2); return 'STRING';}\n\"'\"[^']+\"'\" \t{yytext = yytext.substr(1, yyleng-2); return 'STRING';}\n\":\" \t{return ':';}\n\";\" \t{return ';';}\n\"|\" \t{return '|';}\n\"%%\" \t{return '%%';}\n\"%prec\" \t{return 'PREC';}\n\"%start\" \t{return 'START';}\n\"%left\" \t{return 'LEFT';}\n\"%right\" \t{return 'RIGHT';}\n\"%nonassoc\" \t{return 'NONASSOC';}\n\"%\"[a-zA-Z]+[^\\n]* \t{/* ignore unrecognized decl */}\n\"{{\"[^}]*\"}\" \t{return yy.lexAction(this);}\n\"{\"[^}]*\"}\" \t{yytext = yytext.substr(1, yyleng-2); return 'ACTION';}\n\"%{\"(.|\\n)*?\"%}\" {yytext = yytext.substr(2, yytext.length-4);return 'ACTION';} \n. \t{/* ignore bad characters */}\n<<EOF>> \t{return 'EOF';}\n\n%%\n\n";
var gen = new Jison.Generator(grammar, {type: "lalr"}); var gen = new Jison.Generator(grammar, {type: "lalr"});