internal/dump_fallback_ast.php
#!/usr/bin/env php
<?php
declare(strict_types=1);
/**
* The MIT License (MIT)
*
* Copyright (c) 2017 Tyson Andre
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @phan-file-suppress PhanPluginRemoveDebugEcho
*/
// @phan-file-suppress PhanMissingRequireFile this depends on where Phan is installed
if (file_exists(__DIR__ . "/../../../../vendor/autoload.php")) {
require __DIR__ . "/../../../../vendor/autoload.php";
} else {
require __DIR__ . "/../vendor/autoload.php";
}
use Microsoft\PhpParser\Parser;
use Phan\AST\TolerantASTConverter\TolerantASTConverter;
dump_main();
/**
* Dumps a snippet provided as a command line argument
* @throws Exception if it can't render the AST
*/
function dump_main(): void
{
$print_help = static function (int $exit_code): void {
global $argv;
$help = <<<"EOB"
Usage: php [--help|-h|help] [--php-ast|--php-ast-native|--php-ast-with-placeholders|--tokens] {$argv[0]} 'snippet'
E.g.
{$argv[0]} '2+2;'
{$argv[0]} '<?php function test() {}'
{$argv[0]} "$(cat 'path/to/file.php')"
--php-ast:
Pretty print the ast\Node generated by the polyfill parser.
--php-ast-native:
Pretty print the ast\Node generated by the native php-ast parser.
--php-ast-with-placeholders:
Pretty print the ast\Node generated by the polyfill parser when placeholders are requested (used by code completion in the language server).
--tokens:
Print a readable representation of the tokens parsed by token_get_all().
--help, -h:
Print this help message
EOB;
echo $help;
exit($exit_code);
};
error_reporting(E_ALL);
global $argv;
$as_php_ast = false;
$as_tokens = false;
$as_php_ast_with_placeholders = false;
$as_php_ast_native = false;
foreach ($argv as $i => $arg) {
if ($arg === '--php-ast') {
$as_php_ast = true;
} elseif ($arg === '--php-ast-native') {
$as_php_ast = true;
$as_php_ast_native = true;
} elseif ($arg === '--php-ast-with-placeholders') {
$as_php_ast = true;
$as_php_ast_with_placeholders = true;
} elseif ($arg === '--tokens') {
$as_tokens = true;
} elseif (in_array($argv[$i], ['help', '-h', '--help'], true)) {
$print_help(0);
} else {
continue;
}
unset($argv[$i]);
}
$argv = array_values($argv);
if (count($argv) !== 2) {
$print_help(1);
}
$expr = $argv[1];
if (!is_string($expr)) {
throw new AssertionError("missing 2nd argument");
}
// Guess if this is a snippet or file contents
$add_prefix = ($expr[0] ?? '') !== '<' && \substr($expr, 0, 2) !== '#!';
if ($add_prefix) {
$expr = '<' . '?php ' . $expr;
}
if ($as_tokens) {
$tokens = token_get_all($expr);
if ($add_prefix) {
unset($tokens[0]);
}
dump_tokens($tokens);
} elseif ($as_php_ast) {
dump_expr_as_ast($expr, $as_php_ast_with_placeholders, $as_php_ast_native);
} else {
dump_expr($expr);
}
}
/**
* Dump the list of tokens to the console
* @param array<int, string|array{0:int, 1:string, 2:int}> $tokens
*/
function dump_tokens(array $tokens): void
{
foreach ($tokens as $token) {
if (is_string($token)) {
echo $token . PHP_EOL;
continue;
}
$kind = $token[0];
if ($kind === T_WHITESPACE) {
echo token_name($kind) . ': ' . var_export($token[1], true) . PHP_EOL;
continue;
}
echo token_name($kind) . ': ' . $token[1] . PHP_EOL;
}
}
/**
* Parses $expr and echoes the compact AST representation to stdout.
*/
function dump_expr_as_ast(string $expr, bool $with_placeholders, bool $native): void
{
if ($native) {
$ast_data = ast\parse_code($expr, \Phan\Config::AST_VERSION);
} else {
$converter = new TolerantASTConverter();
$converter->setShouldAddPlaceholders($with_placeholders);
$ast_data = $converter->parseCodeAsPHPAST($expr, \Phan\Config::AST_VERSION);
}
echo \Phan\Debug::nodeToString($ast_data);
}
/**
* Parses $expr and echoes the tolerant-php-parser AST to stdout.
* @throws Exception
*/
function dump_expr(string $expr): void
{
// Instantiate new parser instance
$parser = new Parser();
// Return and print an AST from string contents
$ast_node = $parser->parseSourceFile($expr);
TolerantASTConverter::unlinkDescendantNodes($ast_node);
unset($ast_node->statementList[0]);
$dumper = new \Phan\AST\TolerantASTConverter\NodeDumper($expr);
$dumper->setIncludeTokenKind(true);
$dumper->dumpTree($ast_node);
echo "\n";
// var_export($ast_node->statementList);
}