Skip to content

Commit

Permalink
quick manual extract of these:
Browse files Browse the repository at this point in the history
SHA-1: 24e36a5

* adjusted the code to remove the error recovery from the run-time when no error recovery rules are included in the user-provided grammar. (The correction was necessary as the run-time has been altered in the previous commits.)

SHA-1: d48466f

* fixes error recovery logic in the parser run-time (tested & verified using the errorlab.js test file): previously 5 tests would fail, but after this fix, all pass.

The loop which looks for a matching error rule has been abstracted out into the function locateNearestErrorRecoveryRule() because the first cause for failed the tests was the parseError() handler firing before the erorr recovery could kick in: this (and user-defined) parseError handlers need a way to detect whether an error recovery rule is available (via the hash.recoverable boolean).

The tests also uncovered an infinitely loop in error recovery in the new code when the lexer hits EOF. This bug has been fixed.
  • Loading branch information
GerHobbelt committed Feb 16, 2013
1 parent 17f8d26 commit f61dfaf
Showing 1 changed file with 52 additions and 21 deletions.
73 changes: 52 additions & 21 deletions lib/jison.js
Expand Up @@ -962,7 +962,10 @@ function removeErrorRecovery (fn) {
var ast = Reflect.parse(parseFn);

var labeled = JSONSelect.match(':has(:root > .label > .name:val("_handle_error"))', ast);
labeled[0].body.consequent.body = [labeled[0].body.consequent.body[0], labeled[0].body.consequent.body[1]];
var reduced_code = labeled[0].body.consequent.body[3].consequent.body;
reduced_code[0] = labeled[0].body.consequent.body[1]; // remove the line: error_rule_depth = locateNearestErrorRecoveryRule(state);
reduced_code[4].expression.arguments[1].properties.pop(); // remove the line: 'recoverable: error_rule_depth !== false'
labeled[0].body.consequent.body = reduced_code;

return Reflect.stringify(ast).replace(/_handle_error:\s?/,"").replace(/\\\\n/g,"\\n");
} catch (e) {
Expand Down Expand Up @@ -1150,32 +1153,67 @@ 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 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 > 2) {
expected.push("'"+this.terminals_[p]+"'");
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,
{text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
this.parseError(errStr, {
text: this.lexer.match,
token: this.terminals_[symbol] || symbol,
line: this.lexer.yylineno,
loc: yyloc,
expected: expected,
recoverable: (error_rule_depth !== false)
});
} else if (preErrorSymbol !== EOF) {
error_rule_depth = locateNearestErrorRecoveryRule(state);
}

// just recovered from another error
if (recovering == 3) {
if (symbol == EOF) {
throw new Error(errStr || 'Parsing halted.');
if (symbol === EOF || preErrorSymbol === EOF) {
throw new Error(errStr || 'Parsing halted while starting to recover from another error.');
}

// discard current lookahead and grab another
Expand All @@ -1187,19 +1225,12 @@ parser.parse = function parse (input) {
}

// try to recover from error
while (1) {
// check for error recovery rule in this state
if ((TERROR.toString()) in table[state]) {
break;
}
if (state === 0) {
throw new Error(errStr || 'Parsing halted.');
}
popStack(1);
state = stack[stack.length-1];
if (error_rule_depth === false) {
throw new Error(errStr || 'Parsing halted. No suitable error recovery rule available.');
}
popStack(error_rule_depth);

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 Down

0 comments on commit f61dfaf

Please sign in to comment.