/**
  * @param array $tokens
  *   The tokens from token_get_all()
  * @param int $iParent
  *   Before: Position on T_FUNCTION.
  *   After, success: Position directly after the closing '}' or ';'.
  *   After, failure: Same as before.
  * @param true[] $modifiers
  *   E.g. array(T_ABSTRACT => true, T_PRIVATE => true)
  * @param string $docComment
  *
  * @return false|\Donquixote\HastyPhpAst\Ast\FunctionLike\AstFunctionLike
  */
 function parse(array $tokens, &$iParent, array $modifiers = array(), $docComment = NULL)
 {
     $i = $iParent;
     $id = ParserUtil::nextSubstantialExcl($tokens, $i);
     if ('&' === $id) {
         $modifiers['&'] = TRUE;
         $id = ParserUtil::nextSubstantialExcl($tokens, $i);
     }
     if (T_STRING !== $id) {
         return FALSE;
     }
     $name = $tokens[$i][1];
     $id = ParserUtil::nextSubstantialExcl($tokens, $i);
     if ('(' !== $id) {
         return FALSE;
     }
     // Skip the signature.
     ParserUtil::skipCurvy($tokens, $i);
     $id = ParserUtil::nextSubstantialIncl($tokens, $i);
     if (';' === $id) {
         $iParent = $i + 1;
         return new AstFunctionLike($docComment, $modifiers, $name);
     } elseif ('{' === $id) {
         ParserUtil::skipCurly($tokens, $i);
         $iParent = $i;
         return new AstFunctionLike($docComment, $modifiers, $name);
     } else {
         return FALSE;
     }
 }
 /**
  * @param mixed[] $tokens
  * @param int $i
  *
  * @return mixed
  */
 private function doParse(array $tokens, &$i)
 {
     $id = ParserUtil::nextSubstantialIncl($tokens, $i);
     if (T_USE === $id) {
         return ParserUtil::parseUseStatementGroup($tokens, $i);
     } elseif (T_NAMESPACE === $id) {
         return ParserUtil::parseNamespaceDeclaration($tokens, $i);
     } elseif (T_FINAL === $id || T_ABSTRACT === $id) {
         if (T_DOC_COMMENT === $tokens[$i - 2][0]) {
             $docComment = $tokens[$i - 2][1];
         } else {
             $docComment = NULL;
         }
         $modifiers = array($id => TRUE);
         while (TRUE) {
             $id = ParserUtil::nextSubstantialExcl($tokens, $i);
             $modifiers[$id] = TRUE;
             if (T_CLASS === $id || T_INTERFACE === $id || T_TRAIT === $id) {
                 return $this->classLikeParser->parse($tokens, $i, $modifiers, $docComment);
             } elseif (T_FINAL === $id || T_ABSTRACT === $id) {
                 $modifiers[$id] = TRUE;
             } else {
                 // Something other than the above does not belong here.
                 return FALSE;
             }
         }
         throw new \RuntimeException('Unreachable code.');
     } elseif (T_CLASS === $id || T_INTERFACE === $id || T_TRAIT === $id) {
         if (T_DOC_COMMENT === $tokens[$i - 2][0]) {
             $docComment = $tokens[$i - 2][1];
         } else {
             $docComment = NULL;
         }
         return $this->classLikeParser->parse($tokens, $i, array($id => TRUE), $docComment);
     } elseif ('#' === $id) {
         return FALSE;
     } else {
         return self::ignoreStatement($tokens, $i);
     }
 }
 /**
  * @param array $tokens
  *   The tokens from token_get_all()
  * @param int $iParent
  *   Before: Position of the T_CLASS or T_INTERFACE or T_TRAIT.
  *   After, success: Position after the closing '}' or ';'.
  *   After, failure: Same as before.
  * @param true[] $modifiers
  *   E.g. array(T_ABSTRACT => true, T_INTERFACE => true, T_PRIVATE => true)
  * @param string $docComment
  *   Doc comment collected in calling code.
  *
  * @return mixed|false|null
  *   FALSE, if this parser does not match.
  *   NULL, if the parsed element can be skipped in the result.
  *   A parse subtree, otherwise.
  *
  * @throws ParseError
  *   If a syntax error is found in the code.
  *
  * @see token_get_all()
  */
 function parse(array $tokens, &$iParent, array $modifiers = array(), $docComment = NULL)
 {
     $i = $iParent;
     $id = ParserUtil::nextSubstantialExcl($tokens, $i);
     if (T_STRING !== $id) {
         return FALSE;
     }
     $shortName = $tokens[$i][1];
     $id = ParserUtil::nextSubstantialExcl($tokens, $i);
     $extendsAliases = array();
     if (T_EXTENDS === $id) {
         ++$i;
         $extendsAliases = ParserUtil::parseIdentifierList($tokens, $i);
         if (FALSE === $extendsAliases) {
             return FALSE;
         }
         $id = ParserUtil::nextSubstantialIncl($tokens, $i);
     }
     $implementsAliases = array();
     if (T_IMPLEMENTS === $id) {
         ++$i;
         $implementsAliases = ParserUtil::parseIdentifierList($tokens, $i);
         if (FALSE === $implementsAliases) {
             return FALSE;
         }
         $id = ParserUtil::nextSubstantialIncl($tokens, $i);
     }
     if ('{' !== $id) {
         return FALSE;
     }
     $body = $this->classBodyParser->parse($tokens, $i);
     if (FALSE === $body) {
         return FALSE;
     }
     $iParent = $i;
     return new AstClassLike($docComment, $modifiers, $shortName, $extendsAliases, $implementsAliases, $body);
 }
 /**
  * @param mixed[] $tokens
  * @param int $i
  *   Before: Position on the T_NAMESPACE.
  *   After (success): Position after the ';'.
  *
  * @return bool|\Donquixote\HastyPhpAst\Ast\Namespace_\AstNamespaceDeclaration
  */
 static function parseNamespaceDeclaration(array $tokens, &$i)
 {
     $id = ParserUtil::nextSubstantialExcl($tokens, $i);
     if (T_STRING !== $id) {
         return FALSE;
     }
     $fqcn = Ptk_Qcn::parse($tokens, $i);
     if (FALSE === $fqcn) {
         return FALSE;
     }
     return new AstNamespaceDeclaration($fqcn);
 }