"use strict"; /** @fileOverview Implements `class page.SLR1.State`. * @author Axel T. Schreiner <ats@cs.rit.edu> * @version 1.5.0 */ /** Creates a representation for a new state of the SLR(1) automaton. * @class Represents a state of the SLR(1) automaton. * @private * * @param {page.SLR1.Config[]} _configs core and closure defining this state. * @param {number} _core number of marked rules in the core. * @param {Map} _actions maps possible next symbols to <tt>null</tt>. * * @property {page.SLR1.Config[]} configs core and closure defining this state. * @property {number} core number of marked rules in the core. * @property {Map} actions maps possible next symbols to actions. */ page.SLR1.State = function (_configs, _core, _actions) { this.configs = _configs; this.core = _core; this.actions = _actions; }; page.baseclass(page.SLR1.State, 'page.SLR1.State'); /** Displays core configurations and actions. * @returns {string} */ page.SLR1.State.prototype.toString = function () { return this.dump(true); }; /** Displays all configurations and actions. * @param {boolean} _core if true, only displays core configurations. * @returns {string} */ page.SLR1.State.prototype.dump = function (_core) { var result = ' ' + (_core ? this.configs.slice(0, this.core) : this.configs).map(function (config) { return config.toString(); }).join('\n ') + '\n'; for (var a in this.actions) result += '\n ' + this.actions[a]; return result; }; /** Compares a core of marked rules to this state's core. * * @param {page.SLR1.Config[]} _core to compare to <tt>this</tt>. * * @returns true if this state has the same core (in any order). */ page.SLR1.State.prototype.equals = function (_core) { this.assert('__WHERE__', _core instanceof Array); _core.forEach(function (config) { this.assert('__WHERE__', config instanceof page.SLR1.Config); }, this); // same core sizes? return this.core == _core.length && // same elements? this.configs.slice(0, this.core).every(function (a) { return _core.some(function (b) { return a.equals(b); }) }); }; /** Creates and populates the state/symbol/action table. Fills in `reduce` for * complete configurations, `shift` for terminals, `accept` * for the end of input terminal, and `goto` for non-terminals. * For conflict resolution see {@link page.SLR1}. * * @param {page.SLR1} _factory the keeper of the state table, factory for new states. * @param {number} _stateNumber this state's number. */ page.SLR1.State.prototype.advance = function (_factory, _stateNumber) { var self = this; self.assert('__WHERE__', _factory instanceof page.SLR1); self.assert('__WHERE__', typeof _stateNumber == 'number'); // following construction, actions maps every literal/token/non-terminal // which can follow this state to null. // create reduce actions for complete configurations self.configs.forEach(function (config) { if (config.complete) { var rule = config.rule; // rule we are in // for each terminal which can follow the rule in the grammar for (var t in rule.nt.follow) { // ordinal number var f = rule.nt.follow[t]; // terminal which can follow if (!(t in self.actions)) { // can it follow in this state? // if t is not in actions it cannot follow this state -> reduce rule.reduced = true; self.actions[t] = _factory.reduce(f, rule); } else if (self.actions[t] == null) // if (t, null) is in action, depending on precedences we might have a s/r conflict if (rule.prec && 'prec' in f) // rule and terminal have defined precedence if (rule.prec.level > f.prec.level) { // rule's precedence is higher -> reduce rule.reduced = true; self.actions[t] = _factory.reduce(f, rule); } else if (rule.prec.level < f.prec.level) { // rule's precedence is lower -> shift (below) } else // equal precedence switch (rule.prec.assoc) { case 'left': // left-associative -> reduce rule.reduced = true; self.actions[t] = _factory.reduce(f, rule); case 'right': // right-associative -> shift break; case 'nonassoc': // non-associative -> error action rule.reduced = true; // avoid message delete self.actions[t]; // i.e. f as input would be an error } else { // no precedence available ++ _factory.sr; self.message('state', _stateNumber, 'has a shift/reduce conflict:', f, 'and rule', '(' + rule + ')'); } // -> shift (below) else { // t is in actions then actions[t] is already set as a reduce self.assert('__WHERE__', self.actions[t] instanceof page.SLR1.Action && self.actions[t].action == 'reduce' && self.actions[t].info instanceof page.BNF.Rule); var r2 = self.actions[t].info; // the conflict ++ _factory.rr; self.message('state', _stateNumber, 'for', f, 'reduce/reduce conflict between', '(' + rule + ')', 'and', '(' + r2 + ')'); // resolve for rule which is first in the grammar if (rule.index < r2.index) self.actions[t].info = rule; } // done with this t } // done with every t which can follow } // done with every complete configuration }); // create accept/shift actions for each next symbol which has none for (var a in self.actions) if (self.actions[a] == null) if (a == _factory.bnf.lit().ord) { // special case: $eof self.actions[a] = _factory.accept(); _factory.bnf.rules[0].reduced = true; } else { // create next core by advancing marker over one symbol var next = [ ]; var symbol = null; self.configs.forEach(function (config) { // find a as next symbol in all configs if (!config.complete && a == config.rule.symbols[config.position].ord) { // remember symbol and push config with marker after symbol symbol = config.rule.symbols[config.position]; next.push(config.advance(_factory)); } }); self.assert('__WHERE__', next.length > 0 && symbol); // add new state with next as core, if any // shift/goto existent or new state if (!_factory.states.some( function (state, s) { if (!state.equals(next)) return false; self.actions[a] = _factory.shift_or_goto(symbol, s); return true; })) { self.actions[a] = _factory.shift_or_goto(symbol, _factory.states.length); _factory.states.push(_factory.state(next)); } } for (var a in self.actions) self.assert('__WHERE__', self.actions[a]); };