/** * @param mixed[] $tokens * Tokens from token_get_all(). * @param int $i * Before: Position of the opening '{'. * After (success): Position after the closing '}'. * After (failure): Same as before. * * @return \Donquixote\HastyPhpAst\Ast\ClassLikeBody\AstClassLikeBodyInterface|false */ function parse(array $tokens, &$i) { $iStart = $i; if ('{' !== $tokens[$i]) { # return FALSE; // Wrong usage of this parser. The calling code is responsible! throw new \InvalidArgumentException('Class body must begin with "{".'); } ++$i; $memberNodes = array(); while (TRUE) { $memberNode = $this->memberParser->parse($tokens, $i); if (FALSE === $memberNode) { $id = ParserUtil::nextSubstantialIncl($tokens, $i); if ('}' === $id) { // End of class body. ++$i; break; } $i = $iStart; return FALSE; } if (NULL !== $memberNode) { $memberNodes[] = $memberNode; } } return new AstClassLikeBody($memberNodes); }
/** * @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 string[]|bool */ static function parseIdentifierList(array $tokens, &$i) { $list = array(); while (TRUE) { $id = ParserUtil::nextSubstantialIncl($tokens, $i); if (T_STRING !== $id) { return FALSE; } $list[] = $tokens[$i][1]; $id = ParserUtil::nextSubstantialExcl($tokens, $i); if (',' !== $id) { break; } ++$i; } return $list; }
/** * @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 * @param int $i * * @return \Donquixote\HastyPhpAst\Ast\File\AstFileInterface|false */ function parse(array $tokens, &$i) { if (T_OPEN_TAG !== $tokens[$i][0]) { return FALSE; } ++$i; $nodes = array(); while (TRUE) { $node = $this->parser->parse($tokens, $i); if (FALSE === $node) { $id = ParserUtil::nextSubstantialIncl($tokens, $i); if ('#' === $id) { // End of file/stream. break; } return FALSE; } if (NULL !== $node) { $nodes[] = $node; } } return new AstFile($nodes); }
/** * @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 of T_TRAIT * After (success): Position after the closing ';' or '}'. * After (failure): Random position. * * @return \Donquixote\HastyPhpAst\Ast\UseTrait\AstUseTrait|false */ static function parseUseTrait(array $tokens, &$i) { ++$i; $names = ParserUtil::parseIdentifierList($tokens, $i); if (FALSE === $names) { return FALSE; } $id = ParserUtil::nextSubstantialIncl($tokens, $i); if (';' === $id) { ++$i; return new AstUseTrait($names); } elseif ('{' === $id) { ParserUtil::skipCurly($tokens, $i); return new AstUseTrait($names); } else { return FALSE; } }