private function buildTables()

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;
  }