Skip to content

Commit

Permalink
Handle default actions like Bison to allow proper lexical tie-ins.
Browse files Browse the repository at this point in the history
  • Loading branch information
zaach committed Apr 25, 2010
1 parent 24ac406 commit cf6a3b1
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 7 deletions.
43 changes: 36 additions & 7 deletions lib/jison.js
Expand Up @@ -537,6 +537,7 @@ lrGeneratorMixin.buildTable = function buildTable () {

this.states = this.canonicalCollection();
this.table = this.parseTable(this.states);
this.defaultActions = findDefaults(this.table);
};

lrGeneratorMixin.Item = typal.construct({
Expand Down Expand Up @@ -781,6 +782,24 @@ lrGeneratorMixin.parseTable = function parseTable (itemSets) {
return states;
};

// find states with only one action, a reduction
function findDefaults (states) {
var defaults = {};
states.forEach(function (state, k) {
var i = 0;
for (var act in state) {
if ({}.hasOwnProperty.call(state, act)) i++;
}

if (i === 1 && state[act][0] === 2) {
// only one action in state and it's a reduction
defaults[k] = state[act];
}
});

return defaults;
}

// resolves shift-reduce and reduce-reduce conflicts
function resolveConflict (production, op, reduce, shift) {
var sln = {production: production, operator: op, r: reduce, s: shift},
Expand Down Expand Up @@ -875,6 +894,7 @@ lrGeneratorMixin.generateModule_ = function generateModule_ () {
"productions_: " + JSON.stringify(this.productions_),
"performAction: " + String(this.performAction),
"table: " + JSON.stringify(this.table),
"defaultActions: " + JSON.stringify(this.defaultActions),
"parseError: " + String(this.parseError || (this.hasErrorRecovery ? traceParseError : parser.parseError)),
"parse: " + String(parser.parse)
].join(",\n");
Expand Down Expand Up @@ -936,6 +956,7 @@ lrGeneratorMixin.createParser = function createParser () {

p.init({
table: this.table,
defaultActions: this.defaultActions,
productions_: this.productions_,
symbols_: this.symbols_,
terminals_: this.terminals_,
Expand Down Expand Up @@ -999,7 +1020,6 @@ parser.parse = function parse (input) {

function checkRecover (st) {
for (var p in table[st]) if (p == TERROR) {
//print('RECOVER!!');
return true;
}
return false;
Expand All @@ -1016,12 +1036,19 @@ parser.parse = function parse (input) {
};

var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected, recovered = false;
symbol = lex();
while (true) {
// set first input
// retreive state number from top of stack
state = stack[stack.length-1];
// read action for current state and first input
action = table[state] && table[state][symbol];

// use default actions if available
if (this.defaultActions[state]) {
action = this.defaultActions[state];
} else {
if (symbol == null)
symbol = lex();
// read action for current state and first input
action = table[state] && table[state][symbol];
}

// handle parse error
if (typeof action === 'undefined' || !action.length || !action[0]) {
Expand Down Expand Up @@ -1051,7 +1078,7 @@ parser.parse = function parse (input) {
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
symbol = lex();
symbol = lex();
}

// try to recover from error
Expand Down Expand Up @@ -1089,11 +1116,11 @@ parser.parse = function parse (input) {
stack.push(symbol);
vstack.push(this.lexer.yytext); // semantic values or junk only, no terminals
stack.push(a[1]); // push state
symbol = null;
if (!preErrorSymbol) { // normal execution/no error
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
symbol = lex();
if (recovering > 0)
recovering--;
} else { // error just occurred, resume old lookahead f/ before error
Expand Down Expand Up @@ -1142,6 +1169,7 @@ parser.parse = function parse (input) {

parser.init = function parser_init (dict) {
this.table = dict.table;
this.defaultActions = dict.defaultActions;
this.performAction = dict.performAction;
this.productions_ = dict.productions_;
this.symbols_ = dict.symbols_;
Expand Down Expand Up @@ -1200,6 +1228,7 @@ var lalr = generator.beget(lookaheadMixin, lrGeneratorMixin, {
this.unionLookaheads();

this.table = this.parseTable(this.states);
this.defaultActions = findDefaults(this.table);
},

lookAheads: function LALR_lookaheads (state, item) {
Expand Down
21 changes: 21 additions & 0 deletions tests/parser/actions.js
Expand Up @@ -309,3 +309,24 @@ exports["test action include"] = function() {
assert.equal(parser.parse('y'), 1, "semantic action");
};

exports["test next token not shifted if only one action"] = function () {
var lexData = {
rules: [
["\\(", "return '(';"],
["\\)", "return ')';"],
["y", "return yy.xed ? 'yfoo' : 'ybar';"]
]
};
var grammar = {
bnf: {
"prog" :[ 'e ybar' ],
"esub" :[[ '(', "yy.xed = true;" ]],
"e" :[[ 'esub yfoo )', "yy.xed = false;" ]]
}
};

var parser = new Jison.Parser(grammar);
parser.lexer = new RegExpLexer(lexData);
assert.ok(parser.parse('(y)y'), "should parse correctly");
};

0 comments on commit cf6a3b1

Please sign in to comment.