From ef5113fd479bb46a1e89116cab308786dd745edb Mon Sep 17 00:00:00 2001 From: Ger Hobbelt Date: Sat, 16 Feb 2013 01:54:04 +0100 Subject: [PATCH] - add documentation (as comments) to the generated parser; this includes 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') --- lib/jison.js | 141 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 101 insertions(+), 40 deletions(-) diff --git a/lib/jison.js b/lib/jison.js index 2c6b9cae9..8cc7b9727 100755 --- a/lib/jison.js +++ b/lib/jison.js @@ -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) {' @@ -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]; } }); @@ -897,12 +898,13 @@ 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; @@ -910,7 +912,7 @@ lrGeneratorMixin.generate = function parser_generate (opt) { 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() + @@ -924,14 +926,14 @@ 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; }; @@ -939,7 +941,79 @@ lrGeneratorMixin.generateCommonJSModule = function generateCommonJSModule (opt) 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; @@ -947,7 +1021,7 @@ lrGeneratorMixin.generateModule = function generateModule (opt) { 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})();"; @@ -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); @@ -1119,14 +1194,14 @@ 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; @@ -1134,10 +1209,10 @@ parser.parse = function parse (input) { 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]) { @@ -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, @@ -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]; @@ -1212,7 +1287,6 @@ parser.parse = function parse (input) { } switch (action[0]) { - case 1: // shift //this.shiftCount++; @@ -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]; @@ -1272,7 +1349,8 @@ parser.parse = function parse (input) { stack.push(newState); break; - case 3: // accept + case 3: + // accept return true; } @@ -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(); };