Skip to content

Commit

Permalink
- add documentation (as comments) to the generated parser; this inclu…
Browse files Browse the repository at this point in the history
…des a description of the whole structure in a comment at the top of the generated file. (People who don't want this [should] pull the code through a minifier/obfuscator anyway.)

- trivial code duplication removal at the end of the source file.

- use EOF and TERROR constants where applicable for much improved legibility

- some whitespace and semicolon goodness ('pedantic fixes')
  • Loading branch information
GerHobbelt committed Feb 16, 2013
1 parent 17f8d26 commit ef5113f
Showing 1 changed file with 101 additions and 40 deletions.
141 changes: 101 additions & 40 deletions lib/jison.js
Expand Up @@ -186,6 +186,7 @@ function processOperators (ops) {

generator.buildProductions = function buildProductions(bnf, productions, nonterminals, symbols, operators) {
var actions = [
'/* this == yyval */',
this.actionInclude || '',
'var $0 = $$.length - 1;',
'switch (yystate) {'
Expand Down Expand Up @@ -838,7 +839,7 @@ function findDefaults (states) {
}

if (i === 1 && state[act][0] === 2) {
// only one action in state and it's a reduction
// only one action in state and it's a reduction
defaults[k] = state[act];
}
});
Expand Down Expand Up @@ -897,20 +898,21 @@ lrGeneratorMixin.generate = function parser_generate (opt) {
switch (opt.moduleType) {
case "js":
code = this.generateModule(opt);
break;
break;
case "amd":
code = this.generateAMDModule(opt);
break;
break;
default:
code = this.generateCommonJSModule(opt);
break;
}

return code;
};

lrGeneratorMixin.generateAMDModule = function generateAMDModule(opt){
opt = typal.mix.call({}, this.options, opt);
var out = 'define([], function(){'
var out = '\n\ndefine([], function(){'
+ '\nvar parser = '+ this.generateModule_(opt)
+ (this.lexer && this.lexer.generateModule ?
'\n' + this.lexer.generateModule() +
Expand All @@ -924,30 +926,102 @@ lrGeneratorMixin.generateCommonJSModule = function generateCommonJSModule (opt)
opt = typal.mix.call({}, this.options, opt);
var moduleName = opt.moduleName || "parser";
var out = this.generateModule(opt)
+ "\nif (typeof require !== 'undefined' && typeof exports !== 'undefined') {"
+ "\n\n\nif (typeof require !== 'undefined' && typeof exports !== 'undefined') {"
+ "\nexports.parser = "+moduleName+";"
+ "\nexports.Parser = "+moduleName+".Parser;"
+ "\nexports.parse = function () { return "+moduleName+".parse.apply("+moduleName+", arguments); };"
+ "\nexports.main = "+ String(opt.moduleMain || commonjsMain) + ";"
+ "\nif (typeof module !== 'undefined' && require.main === module) {\n"
+ " exports.main(process.argv.slice(1));\n}"
+ "\n}"
+ "\n}";

return out;
};

lrGeneratorMixin.generateModule = function generateModule (opt) {
opt = typal.mix.call({}, this.options, opt);
var moduleName = opt.moduleName || "parser";
var out = "/* parser generated by jison " + version + " */\n";
var out = "/* parser generated by jison " + version + " */\n"
+ "/*\n"
+ " Returns a Parser object of the following structure:\n"
+ "\n"
+ " Parser: {\n"
+ " yy: {}\n"
+ " }\n"
+ "\n"
+ " Parser.prototype: {\n"
+ " yy: {},\n"
+ " trace: function(),\n"
+ " symbols_: {associative list: name ==> number},\n"
+ " terminals_: {associative list: number ==> name},\n"
+ " productions_: [...],\n"
+ " performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),\n"
+ " table: [...],\n"
+ " defaultActions: {...},\n"
+ " parseError: function(str, hash),\n"
+ " parse: function(input),\n"
+ "\n"
+ " lexer: {\n"
+ " EOF: 1,\n"
+ " parseError: function(str, hash),\n"
+ " setInput: function(input),\n"
+ " input: function(),\n"
+ " unput: function(str),\n"
+ " more: function(),\n"
+ " less: function(n),\n"
+ " pastInput: function(),\n"
+ " upcomingInput: function(),\n"
+ " showPosition: function(),\n"
+ " test_match: function(regex_match_array, rule_index),\n"
+ " next: function(),\n"
+ " lex: function(),\n"
+ " begin: function(condition),\n"
+ " popState: function(),\n"
+ " _currentRules: function(),\n"
+ " topState: function(),\n"
+ " pushState: function(condition),\n"
+ "\n"
+ " options: {\n"
+ " ranges: boolean (optional: true ==> token location info will include a .range[] member)\n"
+ " flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)\n"
+ " backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)\n"
+ " },\n"
+ "\n"
+ " performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),\n"
+ " rules: [...],\n"
+ " conditions: {associative list: name ==> set},\n"
+ " }\n"
+ " }\n"
+ "\n"
+ "\n"
+ " token location info (@$, _$, etc.): {\n"
+ " first_line: n,\n"
+ " last_line: n,\n"
+ " first_column: n,\n"
+ " last_column: n,\n"
+ " range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based)\n"
+ " }\n"
+ "\n"
+ "\n"
+ " the parseError function receives a 'hash' object with these members for lexer and parser errors: {\n"
+ " text: (matched text)\n"
+ " token: (the produced terminal token, if any)\n"
+ " line: (yylineno)\n"
+ " }\n"
+ " while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {\n"
+ " loc: (yylloc)\n"
+ " expected: (string describing the set of expected tokens)\n"
+ " recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)\n"
+ " }\n"
+ "*/\n";
out += (moduleName.match(/\./) ? moduleName : "var "+moduleName)+" = (function(){";
out += "\nvar parser = "+this.generateModule_();
out += "\n"+this.moduleInclude;
if (this.lexer && this.lexer.generateModule) {
out += this.lexer.generateModule();
out += "\nparser.lexer = lexer;";
}
out += "\nfunction Parser () { this.yy = {}; }"
out += "\nfunction Parser () {\n this.yy = {};\n}\n"
+ "Parser.prototype = parser;"
+ "parser.Parser = Parser;"
+ "\nreturn new Parser;\n})();";
Expand Down Expand Up @@ -1108,8 +1182,9 @@ parser.parse = function parse (input) {
this.lexer.yy = this.yy;
this.yy.lexer = this.lexer;
this.yy.parser = this;
if (typeof this.lexer.yylloc == 'undefined')
if (typeof this.lexer.yylloc == 'undefined') {
this.lexer.yylloc = {};
}
var yyloc = this.lexer.yylloc;
lstack.push(yyloc);

Expand All @@ -1119,25 +1194,25 @@ parser.parse = function parse (input) {
this.parseError = this.yy.parseError;

function popStack (n) {
stack.length = stack.length - 2*n;
stack.length = stack.length - 2 * n;
vstack.length = vstack.length - n;
lstack.length = lstack.length - n;
}

function lex() {
var token;
token = self.lexer.lex() || 1; // $end = 1
token = self.lexer.lex() || EOF; // $end = 1
// if token isn't its numeric value, convert
if (typeof token !== 'number') {
token = self.symbols_[token] || token;
}
return token;
}

var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
while (true) {
// retreive state number from top of stack
state = stack[stack.length-1];
state = stack[stack.length - 1];

// use default actions if available
if (this.defaultActions[state]) {
Expand All @@ -1150,22 +1225,22 @@ parser.parse = function parse (input) {
action = table[state] && table[state][symbol];
}

_handle_error:
// handle parse error
_handle_error:
if (typeof action === 'undefined' || !action.length || !action[0]) {

var errStr = '';
if (!recovering) {
// Report error
expected = [];
for (p in table[state]) if (this.terminals_[p] && p > 2) {
for (p in table[state]) if (this.terminals_[p] && p > TERROR) {
expected.push("'"+this.terminals_[p]+"'");
}
if (this.lexer.showPosition) {
errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + (this.terminals_[symbol] || symbol)+ "'";
} else {
errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
(symbol == 1 /*EOF*/ ? "end of input" :
(symbol == EOF ? "end of input" :
("'"+(this.terminals_[symbol] || symbol)+"'"));
}
this.parseError(errStr,
Expand Down Expand Up @@ -1199,7 +1274,7 @@ parser.parse = function parse (input) {
state = stack[stack.length-1];
}

preErrorSymbol = symbol == 2 ? null : symbol; // save the lookahead token
preErrorSymbol = (symbol == TERROR ? null : symbol); // save the lookahead token
symbol = TERROR; // insert generic error symbol as new lookahead
state = stack[stack.length-1];
action = table[state] && table[state][TERROR];
Expand All @@ -1212,7 +1287,6 @@ parser.parse = function parse (input) {
}

switch (action[0]) {

case 1: // shift
//this.shiftCount++;

Expand All @@ -1226,15 +1300,18 @@ parser.parse = function parse (input) {
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.lexer.yylloc;
if (recovering > 0)
if (recovering > 0) {
recovering--;
} else { // error just occurred, resume old lookahead f/ before error
}
} else {
// error just occurred, resume old lookahead f/ before error
symbol = preErrorSymbol;
preErrorSymbol = null;
}
break;

case 2: // reduce
case 2:
// reduce
//this.reductionCount++;

len = this.productions_[action[1]][1];
Expand Down Expand Up @@ -1272,7 +1349,8 @@ parser.parse = function parse (input) {
stack.push(newState);
break;

case 3: // accept
case 3:
// accept
return true;
}

Expand Down Expand Up @@ -1580,24 +1658,7 @@ Jison.Generator = function Jison_Generator (g, options) {
};

return function Parser (g, options) {
var opt = typal.mix.call({}, g.options, options);
var gen;
switch (opt.type) {
case 'lr0':
gen = new LR0Generator(g, opt);
break;
case 'slr':
gen = new SLRGenerator(g, opt);
break;
case 'lr':
gen = new LR1Generator(g, opt);
break;
case 'll':
gen = new LLGenerator(g, opt);
break;
default:
gen = new LALRGenerator(g, opt);
}
var gen = Jison.Generator(g, options);
return gen.createParser();
};

Expand Down

0 comments on commit ef5113f

Please sign in to comment.