in src/parser/PhutilParserGenerator.php [684:790]
private function buildTables() {
$action = array();
$goto = array();
$end = $this->getEndSymbol();
$eof = $this->getEOFSymbol();
$init = $this->getInitSymbol();
foreach ($this->states as $state => $items) {
$shift = array();
$reduce = array();
$accept = false;
foreach ($items as $item) {
$next = $this->rules[$item[0]][$item[1]][$item[2]];
if ($next == $end) {
if ($item[0] !== $init) {
$reduce[$item[3]][] = $item;
} else if ($item[0] === $init && $item[3] === $eof) {
$accept = $item;
}
} else if ($this->isTerminal($next)) {
$shift[$next] = $item;
} else {
$goto[$state][$next] = $this->successor[$state][$next];
}
}
foreach ($reduce as $next => $reductions) {
if (count($reductions) > 1) {
$ways = array();
foreach ($reductions as $reduction) {
$ways[] = "{$reduction[0]}/{$reduction[1]}";
}
$ways = implode('; ', $ways);
// TODO: As below, we should have more explicit handling of
// reduce/reduce conflicts. For now, just pick the first one.
if (false) {
throw new Exception(
pht(
"Reduce/reduce conflict: from state '%s', when a ".
"'%s' is encountered, it may be reduced in multiple ".
"ways: %s",
$state,
$next,
$ways));
}
}
$reduce[$next] = head($reductions);
}
$srconflicts = array_intersect_key($shift, $reduce);
foreach ($srconflicts as $next => $ignored) {
// TODO: We should probably have better or more explicit handling of
// shift/reduce conflicts. For now, we just shift.
if (false) {
$what = $reduce[$next][0];
throw new Exception(
pht(
"Shift/reduce conflict: from state '%s', when a '%s' ".
"is encountered, shifting conflicts with reducing '%s'.",
$state,
$next,
$what));
} else {
// Resolve the shift/reduce by shifting.
$reduce = array();
}
}
if ($accept && isset($shift[$eof])) {
throw new Exception(pht('Accept/shift conflict!'));
}
if ($accept && isset($reduce[$eof])) {
throw new Exception(pht('Accept/reduce conflict!'));
}
foreach ($reduce as $next => $item) {
$action[$state][$next] = array(
'R',
array(
$item[0],
$item[1],
count($this->rules[$item[0]][$item[1]]) - 1,
),
);
}
foreach ($shift as $next => $item) {
$action[$state][$next] = array(
'S',
$this->successor[$state][$next],
);
}
if ($accept) {
$action[$state][$eof] = array('A');
}
}
$this->actionTable = $action;
$this->gotoTable = $goto;
}