/** * Encodes the handle-finding FSA as a LR parse table. * * @param \Dissect\Parser\LALR1\Analysis\Automaton $automaton * * @return array The parse table. */ protected function buildParseTable(Automaton $automaton, Grammar $grammar) { $nonterminals = array_keys($grammar->getGroupedRules()); $conflictsMode = $grammar->getConflictsMode(); $conflicts = array(); // initialize the table $table = array('action' => array(), 'goto' => array()); foreach ($automaton->getTransitionTable() as $num => $transitions) { foreach ($transitions as $trigger => $destination) { if (!in_array($trigger, $nonterminals)) { // terminal implies shift $table['action'][$num][$trigger] = $destination; } else { // nonterminal goes in the goto table $table['goto'][$num][$trigger] = $destination; } } } foreach ($automaton->getStates() as $num => $state) { if (!isset($table['action'][$num])) { $table['action'][$num] = array(); } foreach ($state->getItems() as $item) { if ($item->isReduceItem()) { $ruleNumber = $item->getRule()->getNumber(); foreach ($item->getLookahead() as $token) { if (array_key_exists($token, $table['action'][$num])) { // conflict $instruction = $table['action'][$num][$token]; if ($instruction > 0) { // s/r if ($conflictsMode & Grammar::SHIFT) { $conflicts[] = array('state' => $num, 'lookahead' => $token, 'rule' => $item->getRule(), 'resolution' => Grammar::SHIFT); continue; } else { throw new ShiftReduceConflictException($num, $item->getRule(), $token, $automaton); } } else { // r/r $originalRule = $grammar->getRule(-$instruction); $newRule = $item->getRule(); if ($conflictsMode & Grammar::LONGER_REDUCE) { $count1 = count($originalRule->getComponents()); $count2 = count($newRule->getComponents()); if ($count1 > $count2) { // original rule is longer $resolvedRules = array($originalRule, $newRule); $conflicts[] = array('state' => $num, 'lookahead' => $token, 'rules' => $resolvedRules, 'resolution' => Grammar::LONGER_REDUCE); continue; } elseif ($count2 > $count1) { // new rule is longer $table['action'][$num][$token] = -$ruleNumber; $resolvedRules = array($newRule, $originalRule); $conflicts[] = array('state' => $num, 'lookahead' => $token, 'rules' => $resolvedRules, 'resolution' => Grammar::LONGER_REDUCE); continue; } } if ($conflictsMode & Grammar::EARLIER_REDUCE) { if (-$instruction < $ruleNumber) { // original rule was earlier $resolvedRules = array($originalRule, $newRule); $conflicts[] = array('state' => $num, 'lookahead' => $token, 'rules' => $resolvedRules, 'resolution' => Grammar::EARLIER_REDUCE); continue; } else { // new rule was earlier $table['action'][$num][$token] = -$ruleNumber; $conflicts[] = array('state' => $num, 'lookahead' => $token, 'rules' => $resolvedRules, 'resolution' => Grammar::EARLIER_REDUCE); $resolvedRules = array($newRule, $originalRule); continue; } } // everything failed, throw an exception throw new ReduceReduceConflictException($num, $originalRule, $newRule, $token, $automaton); } } $table['action'][$num][$token] = -$ruleNumber; } } } } return array($table, $conflicts); }