Last active
August 29, 2015 14:19
-
-
Save raine/e8183d42733475784610 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
diff --git a/lib/parser.js b/lib/parser.js | |
index 8e9ca7f..7d61d85 100644 | |
--- a/lib/parser.js | |
+++ b/lib/parser.js | |
@@ -730,15 +730,32 @@ parseError: function parseError(str, hash) { | |
} | |
}, | |
parse: function parse(input) { | |
- var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; | |
+ var self = this, | |
+ stack = [0], | |
+ tstack = [], // token stack | |
+ vstack = [null], // semantic value stack | |
+ lstack = [], // location stack | |
+ table = this.table, | |
+ yytext = '', | |
+ yylineno = 0, | |
+ yyleng = 0, | |
+ recovering = 0, | |
+ TERROR = 2, | |
+ EOF = 1; | |
+ | |
var args = lstack.slice.call(arguments, 1); | |
+ | |
+ //this.reductionCount = this.shiftCount = 0; | |
+ | |
var lexer = Object.create(this.lexer); | |
var sharedState = { yy: {} }; | |
+ // copy state | |
for (var k in this.yy) { | |
- if (Object.prototype.hasOwnProperty.call(this.yy, k)) { | |
- sharedState.yy[k] = this.yy[k]; | |
- } | |
+ if (Object.prototype.hasOwnProperty.call(this.yy, k)) { | |
+ sharedState.yy[k] = this.yy[k]; | |
+ } | |
} | |
+ | |
lexer.setInput(input, sharedState.yy); | |
sharedState.yy.lexer = lexer; | |
sharedState.yy.parser = this; | |
@@ -747,123 +764,207 @@ parse: function parse(input) { | |
} | |
var yyloc = lexer.yylloc; | |
lstack.push(yyloc); | |
+ | |
var ranges = lexer.options && lexer.options.ranges; | |
+ | |
if (typeof sharedState.yy.parseError === 'function') { | |
this.parseError = sharedState.yy.parseError; | |
} else { | |
this.parseError = Object.getPrototypeOf(this).parseError; | |
} | |
- function popStack(n) { | |
+ | |
+ function popStack (n) { | |
stack.length = stack.length - 2 * n; | |
vstack.length = vstack.length - n; | |
lstack.length = lstack.length - n; | |
} | |
- _token_stack: | |
- function lex() { | |
- var token; | |
- token = lexer.lex() || EOF; | |
- if (typeof token !== 'number') { | |
- token = self.symbols_[token] || token; | |
- } | |
- return token; | |
+ | |
+_token_stack: | |
+ function lex() { | |
+ var token; | |
+ token = lexer.lex() || EOF; | |
+ // 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; | |
while (true) { | |
+ // retreive state number from top of stack | |
state = stack[stack.length - 1]; | |
+ | |
+ // use default actions if available | |
if (this.defaultActions[state]) { | |
action = this.defaultActions[state]; | |
} else { | |
if (symbol === null || typeof symbol == 'undefined') { | |
symbol = lex(); | |
} | |
+ // read action for current state and first input | |
action = table[state] && table[state][symbol]; | |
} | |
- if (typeof action === 'undefined' || !action.length || !action[0]) { | |
- var errStr = ''; | |
+ | |
+_handle_error: | |
+ // handle parse error | |
+ if (typeof action === 'undefined' || !action.length || !action[0]) { | |
+ var error_rule_depth; | |
+ var errStr = ''; | |
+ | |
+ // Return the rule stack depth where the nearest error rule can be found. | |
+ // Return FALSE when no error recovery rule was found. | |
+ function locateNearestErrorRecoveryRule(state) { | |
+ var stack_probe = stack.length - 1; | |
+ var depth = 0; | |
+ | |
+ // try to recover from error | |
+ for(;;) { | |
+ // check for error recovery rule in this state | |
+ if ((TERROR.toString()) in table[state]) { | |
+ return depth; | |
+ } | |
+ if (state === 0 || stack_probe < 2) { | |
+ return false; // No suitable error recovery rule available. | |
+ } | |
+ stack_probe -= 2; // popStack(1): [symbol, action] | |
+ state = stack[stack_probe]; | |
+ ++depth; | |
+ } | |
+ } | |
+ | |
+ if (!recovering) { | |
+ // first see if there's any chance at hitting an error recovery rule: | |
+ error_rule_depth = locateNearestErrorRecoveryRule(state); | |
+ | |
+ // Report error | |
expected = []; | |
for (p in table[state]) { | |
if (this.terminals_[p] && p > TERROR) { | |
- expected.push('\'' + this.terminals_[p] + '\''); | |
+ expected.push("'"+this.terminals_[p]+"'"); | |
} | |
} | |
if (lexer.showPosition) { | |
- errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''; | |
+ errStr = 'Parse error on line '+(yylineno+1)+":\n"+lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + (this.terminals_[symbol] || symbol)+ "'"; | |
} else { | |
- errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\''); | |
+ errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + | |
+ (symbol == EOF ? "end of input" : | |
+ ("'"+(this.terminals_[symbol] || symbol)+"'")); | |
} | |
this.parseError(errStr, { | |
text: lexer.match, | |
token: this.terminals_[symbol] || symbol, | |
line: lexer.yylineno, | |
loc: yyloc, | |
- expected: expected | |
+ expected: expected, | |
+ recoverable: (error_rule_depth !== false) | |
}); | |
+ } else if (preErrorSymbol !== EOF) { | |
+ error_rule_depth = locateNearestErrorRecoveryRule(state); | |
} | |
- if (action[0] instanceof Array && action.length > 1) { | |
- throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol); | |
- } | |
- switch (action[0]) { | |
- case 1: | |
- stack.push(symbol); | |
- vstack.push(lexer.yytext); | |
- lstack.push(lexer.yylloc); | |
- stack.push(action[1]); | |
- symbol = null; | |
- if (!preErrorSymbol) { | |
+ | |
+ // just recovered from another error | |
+ if (recovering == 3) { | |
+ if (symbol === EOF || preErrorSymbol === EOF) { | |
+ throw new Error(errStr || 'Parsing halted while starting to recover from another error.'); | |
+ } | |
+ | |
+ // discard current lookahead and grab another | |
yyleng = lexer.yyleng; | |
yytext = lexer.yytext; | |
yylineno = lexer.yylineno; | |
yyloc = lexer.yylloc; | |
- if (recovering > 0) { | |
- recovering--; | |
- } | |
- } else { | |
- symbol = preErrorSymbol; | |
- preErrorSymbol = null; | |
- } | |
- break; | |
- case 2: | |
- len = this.productions_[action[1]][1]; | |
- yyval.$ = vstack[vstack.length - len]; | |
- yyval._$ = { | |
- first_line: lstack[lstack.length - (len || 1)].first_line, | |
- last_line: lstack[lstack.length - 1].last_line, | |
- first_column: lstack[lstack.length - (len || 1)].first_column, | |
- last_column: lstack[lstack.length - 1].last_column | |
- }; | |
- if (ranges) { | |
- yyval._$.range = [ | |
- lstack[lstack.length - (len || 1)].range[0], | |
- lstack[lstack.length - 1].range[1] | |
- ]; | |
- } | |
- r = this.performAction.apply(yyval, [ | |
- yytext, | |
- yyleng, | |
- yylineno, | |
- sharedState.yy, | |
- action[1], | |
- vstack, | |
- lstack | |
- ].concat(args)); | |
- if (typeof r !== 'undefined') { | |
- return r; | |
+ symbol = lex(); | |
} | |
- if (len) { | |
- stack = stack.slice(0, -1 * len * 2); | |
- vstack = vstack.slice(0, -1 * len); | |
- lstack = lstack.slice(0, -1 * len); | |
+ | |
+ // try to recover from error | |
+ if (error_rule_depth === false) { | |
+ throw new Error(errStr || 'Parsing halted. No suitable error recovery rule available.'); | |
} | |
- stack.push(this.productions_[action[1]][0]); | |
- vstack.push(yyval.$); | |
- lstack.push(yyval._$); | |
- newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; | |
- stack.push(newState); | |
- break; | |
- case 3: | |
- return true; | |
+ popStack(error_rule_depth); | |
+ | |
+ 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]; | |
+ recovering = 3; // allow 3 real symbols to be shifted before reporting a new error | |
+ } | |
+ | |
+ // this shouldn't happen, unless resolve defaults are off | |
+ if (action[0] instanceof Array && action.length > 1) { | |
+ throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); | |
+ } | |
+ | |
+ switch (action[0]) { | |
+ case 1: // shift | |
+ //this.shiftCount++; | |
+ | |
+ stack.push(symbol); | |
+ vstack.push(lexer.yytext); | |
+ lstack.push(lexer.yylloc); | |
+ stack.push(action[1]); // push state | |
+ symbol = null; | |
+ if (!preErrorSymbol) { // normal execution/no error | |
+ yyleng = lexer.yyleng; | |
+ yytext = lexer.yytext; | |
+ yylineno = lexer.yylineno; | |
+ yyloc = lexer.yylloc; | |
+ if (recovering > 0) { | |
+ recovering--; | |
+ } | |
+ } else { | |
+ // error just occurred, resume old lookahead f/ before error | |
+ symbol = preErrorSymbol; | |
+ preErrorSymbol = null; | |
+ } | |
+ break; | |
+ | |
+ case 2: | |
+ // reduce | |
+ //this.reductionCount++; | |
+ | |
+ len = this.productions_[action[1]][1]; | |
+ | |
+ // perform semantic action | |
+ yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 | |
+ // default location, uses first token for firsts, last for lasts | |
+ yyval._$ = { | |
+ first_line: lstack[lstack.length-(len||1)].first_line, | |
+ last_line: lstack[lstack.length-1].last_line, | |
+ first_column: lstack[lstack.length-(len||1)].first_column, | |
+ last_column: lstack[lstack.length-1].last_column | |
+ }; | |
+ if (ranges) { | |
+ yyval._$.range = [lstack[lstack.length-(len||1)].range[0], lstack[lstack.length-1].range[1]]; | |
+ } | |
+ r = this.performAction.apply(yyval, [yytext, yyleng, yylineno, sharedState.yy, action[1], vstack, lstack].concat(args)); | |
+ | |
+ if (typeof r !== 'undefined') { | |
+ return r; | |
+ } | |
+ | |
+ // pop off stack | |
+ if (len) { | |
+ stack = stack.slice(0,-1*len*2); | |
+ vstack = vstack.slice(0, -1*len); | |
+ lstack = lstack.slice(0, -1*len); | |
+ } | |
+ | |
+ stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) | |
+ vstack.push(yyval.$); | |
+ lstack.push(yyval._$); | |
+ // goto new state = table[STATE][NONTERMINAL] | |
+ newState = table[stack[stack.length-2]][stack[stack.length-1]]; | |
+ stack.push(newState); | |
+ break; | |
+ | |
+ case 3: | |
+ // accept | |
+ return true; | |
} | |
+ | |
} | |
+ | |
return true; | |
}}; | |
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
~/g/LiveScript git:master ❯❯❯ make build | |
bin/lsc --output lib --bare --compile "src/ast.ls" | |
bin/lsc --output lib --bare --compile "src/browser.ls" | |
bin/lsc --output lib --bare --compile "src/grammar.ls" | |
bin/lsc --output lib --bare --compile "src/index.ls" | |
bin/lsc --output lib --bare --compile "src/lang-ls.ls" | |
bin/lsc --output lib --bare --compile "src/lexer.ls" | |
bin/lsc --output lib --bare --compile "src/mode-ls.ls" | |
bin/lsc --output lib --bare --compile "src/node.ls" | |
bin/lsc --output lib --bare --compile "src/options.ls" | |
bin/lsc --output lib --bare --compile "src/util.ls" | |
./scripts/build-parser > lib/parser.js | |
~/g/LiveScript git:master ❯❯❯ gst | |
M lib/parser.js |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment