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
=====
* [issues](http://github.com/zaach/jison/issues)
* [discuss](mailto:jison@librelist.com)
An API for creating parsers in JavaScript
-----------------------------------------
@ -189,8 +191,8 @@ and calc.jison, language grammar
{$$ = $1/$3;}
| e '^' e
{$$ = Math.pow($1, $3);}
| '-' e
{$$ = -$2;} %prec UMINUS
| '-' e %prec UMINUS
{$$ = -$2;}
| '(' e ')'
{$$ = $2;}
| 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.*
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
------------

View File

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

View File

@ -392,10 +392,12 @@ lookaheadMixin.first = function first (symbol) {
} else if (symbol instanceof Array) {
var firsts = [];
for (var i=0,t;t=symbol[i];++i) {
this.first(t).forEach(function first_forEach (e) {
if (firsts.indexOf(e)===-1)
firsts.push(e);
});
if (!this.nonterminals[t]) {
if (firsts.indexOf(t) === -1)
firsts.push(t);
} else {
Set.union(firsts, this.nonterminals[t].first);
}
if (!this.nullable(t))
break;
}
@ -581,9 +583,9 @@ lrGeneratorMixin.ItemSet = Set.prototype.construct({
contains: function (item) {
return this.hash_[item.id];
},
toValue: function toValue () {
var v = this.items_.sort().join('|');
return (this.toValue = function toValue_inner() {return v;})();
valueOf: function toValue () {
var v = this._items.map(function (a) {return a.id}).sort().join('|');
return (this.valueOf = function toValue_inner() {return v;})();
}
});
@ -630,11 +632,10 @@ lrGeneratorMixin.closureOperation = function closureOperation (itemSet /*, closu
lrGeneratorMixin.gotoOperation = function gotoOperation (itemSet, symbol) {
var gotoSet = new this.ItemSet(),
EOF = this.EOF,
self = this;
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));
}
});
@ -652,10 +653,13 @@ lrGeneratorMixin.canonicalCollection = function canonicalCollection () {
self = this,
itemSet;
states.has = {};
states.has[firstState] = 0;
while (marked !== states.size()) {
itemSet = states.item(marked); marked++;
itemSet.forEach(function CC_itemSet_forEach(item) {
if(item.markedSymbol)
itemSet.forEach(function CC_itemSet_forEach (item) {
if (item.markedSymbol && item.markedSymbol !== self.EOF)
self.canonicalCollectionInsert(item.markedSymbol, itemSet, states, marked-1);
});
}
@ -670,8 +674,10 @@ lrGeneratorMixin.canonicalCollectionInsert = function canonicalCollectionInsert
g.predecessors = {};
// add g to que if not empty or duplicate
if (!g.isEmpty()) {
var i = states.indexOf(g);
if (i === -1) {
var gv = g.valueOf(),
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
states.push(g);
g.predecessors[symbol] = [stateNum];
@ -682,6 +688,7 @@ lrGeneratorMixin.canonicalCollectionInsert = function canonicalCollectionInsert
}
};
var NONASSOC = 0;
lrGeneratorMixin.parseTable = function parseTable (itemSets) {
var states = [],
nonterminals = this.nonterminals,
@ -708,7 +715,7 @@ lrGeneratorMixin.parseTable = function parseTable (itemSets) {
state[self.symbols_[stackSymbol]] = gotoState;
} else {
//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) {
if (item.markedSymbol == self.EOF) {
// accept
state[self.symbols_[self.EOF]] = [[a]];
state[self.symbols_[self.EOF]] = [a];
//self.trace(k, self.EOF, state[self.EOF]);
}
});
@ -731,11 +738,12 @@ lrGeneratorMixin.parseTable = function parseTable (itemSets) {
var terminals = allterms || self.lookAheads(itemSet, item);
terminals.forEach(function (stackSymbol) {
action = state[self.symbols_[stackSymbol]] || [];
action = state[self.symbols_[stackSymbol]];
var op = operators[stackSymbol];
// Reading a terminal and current position is at the end of a production, try to reduce
if (action.length) {
var sol = resolveConflict(item.production, op, [r,item.production.id], action[0]);
if (action || action && action.length) {
var sol = resolveConflict(item.production, op, [r,item.production.id], action[0] instanceof Array ? action[0] : action);
self.resolutions.push([k,stackSymbol,sol]);
if (sol.bydefault) {
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));
}
if (self.options.noDefaultResolve) {
if (!(action[0] instanceof Array))
action = [action];
action.push(sol.r);
}
} else {
action = [sol.action];
action = sol.action;
}
} else {
action.push([r,item.production.id]);
action = [r,item.production.id];
}
if (action && action.length) {
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.action = shift[1] < reduce[1] ? shift : reduce;
sln.bydefault = true;
//print(production, reduce[0]);
return sln;
}
@ -793,7 +804,7 @@ function resolveConflict (production, op, reduce, shift) {
sln.action = reduce;
} else if (op.assoc === "nonassoc" ) {
sln.msg = "Resolve S/R conflict (no action for non-associative operator.)";
sln.action = undefined;
sln.action = NONASSOC;
}
} else {
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
action = table[state] && table[state][symbol];
if (typeof action == 'undefined' || !action.length || !action[0]) {
if (typeof action === 'undefined' || !action.length || !action[0]) {
expected = [];
for (p in table[state]) if (this.terminals_[p] && p != 1) {
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
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);
}
a = action[0];
a = action;
switch (a[0]) {

View File

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

View File

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

View File

@ -3,9 +3,9 @@ var bnf = (function(){
var parser = {trace: function trace() {
},
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"},
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) {
var $$ = arguments[5], $0 = arguments[5].length;
switch (arguments[4]) {
@ -75,12 +75,12 @@ performAction: function anonymous(yytext, yyleng, yylineno, yy) {
break;
case 19:
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]) {
this.$.push($$[$0 - 3 + 3 - 1]);
}
if ($$[$0 - 3 + 2 - 1]) {
this.$.push($$[$0 - 3 + 2 - 1]);
}
if (this.$.length === 1) {
this.$ = this.$[0];
}
@ -116,7 +116,7 @@ performAction: function anonymous(yytext, yyleng, yylineno, yy) {
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) {
throw new Error(str);
},
@ -140,7 +140,7 @@ parse: function parse(input) {
while (true) {
state = stack[stack.length - 1];
action = table[state] && table[state][symbol];
if (typeof action == "undefined" || !action.length || !action[0]) {
if (typeof action === "undefined" || !action.length || !action[0]) {
expected = [];
for (p in table[state]) {
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});
}
}
this.trace("action:", action);
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);
}
a = action[0];
a = action;
switch (a[0]) {
case 1:
shifts++;
@ -301,68 +300,72 @@ lexer.performAction = function anonymous(yy, yy_) {
case 0:
break;
case 1:
return yy.lexComment(this);
break;
case 2:
return 29;
return yy.lexComment(this);
break;
case 3:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 28;
return 29;
break;
case 4:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 28;
break;
case 5:
return 19;
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 28;
break;
case 6:
return 21;
return 19;
break;
case 7:
return 22;
return 21;
break;
case 8:
return 4;
return 22;
break;
case 9:
return 27;
return 4;
break;
case 10:
return 8;
return 27;
break;
case 11:
return 13;
return 8;
break;
case 12:
return 14;
return 13;
break;
case 13:
return 15;
return 14;
break;
case 14:
return 15;
break;
case 15:
return yy.lexAction(this);
break;
case 16:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 30;
break;
case 17:
return yy.lexAction(this);
break;
case 18:
yy_.yytext = yy_.yytext.substr(1, yy_.yyleng - 2);
return 30;
break;
case 18:
break;
case 19:
yy_.yytext = yy_.yytext.substr(2, yy_.yytext.length - 4);
return 30;
break;
case 20:
break;
case 21:
return 6;
break;
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;
return parser;
})();

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,6 +1,7 @@
%%
\s+ {/* skip whitespace */}
"//".* {/* skip comment */}
"/*"[^*]*"*" {return yy.lexComment(this);}
[a-zA-Z][a-zA-Z0-9_-]* {return 'ID';}
'"'[^"]+'"' {yytext = yytext.substr(1, yyleng-2); return 'STRING';}
@ -15,9 +16,10 @@
"%right" {return 'RIGHT';}
"%nonassoc" {return 'NONASSOC';}
"%"[a-zA-Z]+[^\n]* {/* ignore unrecognized decl */}
"<"[a-zA-Z]*">" { /* ignore type */}
"{{"[^}]*"}" {return yy.lexAction(this);}
"{"[^}]*"}" {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 */}
<<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 () {
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: "-"} ]]}};
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");
};
exports["test nullable rule with < > delimited action"] = function () {
var grammar = "%% test: foo bar | <action{}>; hello: world ;";
exports["test nullable rule with %{ %} delimited action"] = function () {
var grammar = "%% 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");
@ -57,9 +57,31 @@ exports["test comment"] = function () {
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 () {
var grammar = "/* comment * not done */ %% hello: /* oh hai */ world ;";
var expected = {bnf: {hello: ["world"]}};
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 () {
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"} ]]}};
assert.deepEqual(json2jison.convert(bnf.parse(grammar)), json2jison.convert(expected), "grammar should be parsed correctly");
};
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"} ]]}};
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);
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 () {
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"});