<?php

/**
 * LL(1) parser generated by the Syntax tool.
 *
 * https://www.npmjs.com/package/syntax-cli
 *
 *   npm install -g syntax-cli
 *
 *   syntax-cli --help
 *
 * To regenerate run:
 *
 *   syntax-cli \
 *     --grammar ~/path-to-grammar-file \
 *     --mode LL1 \
 *     --output ~/ParserClassName.php
 */

{{{NAMESPACE}}}

class SyntaxException extends \Exception {}

{{{MODULE_INCLUDE}}}

class yyparse {
  private static $ps = {{{PRODUCTIONS}}};
  private static $tks = {{{TOKENS}}};
  private static $tbl = {{{TABLE}}};

  private static $s = [];
  private static $__ = null;

  private static $on_parse_begin = null;
  private static $on_parse_end = null;

  public static $yytext = '';
  public static $yyleng = 0;

  const EOF = '$';

  private static $tokenizer = null;

  {{{PRODUCTION_HANDLERS}}}

  public static function setTokenizer($tokenizer) {
    self::$tokenizer = $tokenizer;
  }

  public static function getTokenizer() {
    return self::$tokenizer;
  }

  public static function setOnParseBegin($on_parse_begin) {
    self::$on_parse_begin = $on_parse_begin;
  }

  public static function setOnParseEnd($on_parse_end) {
    self::$on_parse_end = $on_parse_end;
  }

  public static function parse($string) {
    if (self::$on_parse_begin) {
      $on_parse_begin = self::$on_parse_begin;
      $on_parse_begin($string);
    }

    $tokenizer = self::getTokenizer();

    if (!$tokenizer) {
      throw new \Exception(`Tokenizer instance wasn't specified.`);
    }

    $tokenizer->initString($string);

    $s = &self::$s;
    $s = [self::EOF, {{{START}}}];

    $tks = &self::$tks;
    $tbl = &self::$tbl;
    $ps = &self::$ps;

    $t = $tokenizer->getNextToken();
    $st = null;

    $to = null;
    $tt = null;

    do {
      $to = array_pop($s);
      $tt = $tks[$t['type']];

      if ($to === $tt) {
        $t = $tokenizer->getNextToken();
        continue;
      }

      self::der($to, $t, $tt);
    } while ($tokenizer->hasMoreTokens() || count($s) > 1);

    while (count($s) !== 1) {
      self::der(array_pop($s), $t, $tt);
    }

    if ($s[0] !== self::EOF || $t['type'] !== self::EOF) {
      self::parseError('stack is not empty');
    }

    return true;
  }

  private static function der($to, $t, $tt) {
    $npn = self::$tbl[$to][$tt];
    if (!$npn) {
      self::unexpectedToken($t);
    }
    self::$s = array_merge(self::$s, self::$ps[intval($npn)][0]);
  }

  private static function unexpectedToken($token) {
    if ($token['type'] === self::EOF) {
      unexpectedEndOfInput();
    }

    self::getTokenizer()->throwUnexpectedToken(
      $token['value'],
      $token['startLine'],
      $token['startColumn']
    );
  }

  private static function unexpectedEndOfInput() {
    self::parseError('Unexpected end of input.');
  }

  private static function parseError($message) {
    throw new \Exception('SyntaxError: '.$message);
  }
}

{{{TOKENIZER}}}

class {{{PARSER_CLASS_NAME}}} extends yyparse {}
