/** Dumps abstract syntax tree */ function ast_dump($ast, int $options = 0) : string { if ($ast instanceof ast\Node) { $result = ast\get_kind_name($ast->kind); if ($options & AST_DUMP_LINENOS) { $result .= " @ {$ast->lineno}"; if (isset($ast->endLineno)) { $result .= "-{$ast->endLineno}"; } } if (ast\kind_uses_flags($ast->kind)) { $result .= "\n flags: " . format_flags($ast->kind, $ast->flags); } if (isset($ast->name)) { $result .= "\n name: {$ast->name}"; } if (isset($ast->docComment)) { $result .= "\n docComment: {$ast->docComment}"; } foreach ($ast->children as $i => $child) { $result .= "\n {$i}: " . str_replace("\n", "\n ", ast_dump($child, $options)); } return $result; } else { if ($ast === null) { return 'null'; } else { if (is_string($ast)) { return "\"{$ast}\""; } else { return (string) $ast; } } } }
/** * ast_node_type() is for places where an actual type * name appears. This returns that type name. Use node_type() * instead to figure out the type of a node * * @param Context $context * @param null|string|Node $node * * @see \Phan\Deprecated\AST::ast_node_type */ public static function unionTypeFromSimpleNode(Context $context, $node) : UnionType { $type_string = null; if ($node instanceof \ast\Node) { switch ($node->kind) { case \ast\AST_NAME: $type_string = self::qualifiedName($context, $node); break; case \ast\AST_TYPE: if ($node->flags == \ast\flags\TYPE_CALLABLE) { $type_string = 'callable'; } else { if ($node->flags == \ast\flags\TYPE_ARRAY) { $type_string = 'array'; } else { assert(false, "Unknown type: {$node->flags}"); } } break; default: Log::err(Log::EFATAL, "ast_node_type: unknown node type: " . \ast\get_kind_name($node->kind)); break; } } else { $type_string = (string) $node; } return UnionType::fromStringInContext($type_string, $context); }
function ast_dump($ast, $children = true) { if ($ast instanceof \ast\Node) { $result = \ast\get_kind_name($ast->kind); $result .= " @ {$ast->lineno}"; if (isset($ast->endLineno)) { $result .= "-{$ast->endLineno}"; } if (\ast\kind_uses_flags($ast->kind)) { $result .= "\n flags: {$ast->flags}"; } if (isset($ast->name)) { $result .= "\n name: {$ast->name}"; } if (isset($ast->docComment)) { $result .= "\n docComment: {$ast->docComment}"; } if ($children) { foreach ($ast->children as $i => $child) { $result .= "\n {$i}: " . str_replace("\n", "\n ", ast_dump($child)); } } return $result; } else { if ($ast === null) { return 'null'; } else { if (is_string($ast)) { return "\"{$ast}\""; } else { return (string) $ast; } } } }
/** * @param string|Node|null $node * An AST node * * @param int $indent * The indentation level for the string * * @return string * A string representation of an AST node */ public static function nodeToString($node, $name = null, int $indent = 0) : string { $string = str_repeat("\t", $indent); if ($name !== null) { $string .= "{$name} => "; } if (is_string($node)) { return $string . $node . "\n"; } if (!$node) { return $string . 'null' . "\n"; } if (!is_object($node)) { return $string . $node . "\n"; } $string .= \ast\get_kind_name($node->kind); $string .= ' [' . self::astFlagDescription($node->flags ?? 0) . ']'; if (isset($node->lineno)) { $string .= ' #' . $node->lineno; } if (isset($node->endLineno)) { $string .= ':' . $node->endLineno; } if (isset($node->name)) { $string .= ' name:' . $node->name; } $string .= "\n"; foreach ($node->children ?? [] as $name => $child_node) { $string .= self::nodeToString($child_node, $name, $indent + 1); } return $string; }
/** * Exports a syntax tree into two CSV files as * described in https://github.com/jexp/batch-import/ * * @param $ast The AST to export. * @param $nodeline Indicates the nodeline of the parent node. This * is necessary when $ast is a plain value, since * we cannot get back from a plain value to the * parent node to learn the line number. * @param $childnum Indicates that this node is the $childnum'th * child of its parent node (starting at 0). * @param $funcid If the AST to be exported is part of a function, * the node id of that function. * * @return The root node index of the exported AST (i.e., the value * of $this->nodecount at the point in time where this * function was called.) */ public function export($ast, $nodeline = 0, $childnum = 0, $funcid = null) : int { // (1) if $ast is an AST node, print info and recurse // An instance of ast\Node declares: // $kind (integer, name can be retrieved using ast\get_kind_name()) // $flags (integer, corresponding to a set of flags for the current node) // $lineno (integer, starting line number) // $children (array of child nodes) // Additionally, an instance of the subclass ast\Node\Decl declares: // $endLineno (integer, end line number of the declaration) // $name (string, the name of the declared function/class) // $docComment (string, the preceding doc comment) if ($ast instanceof ast\Node) { $nodetype = ast\get_kind_name($ast->kind); $nodeline = $ast->lineno; $nodeflags = ""; if (ast\kind_uses_flags($ast->kind)) { $nodeflags = $this->csv_format_flags($ast->kind, $ast->flags); } // for decl nodes: if (isset($ast->endLineno)) { $nodeendline = $ast->endLineno; } if (isset($ast->name)) { $nodename = $ast->name; } if (isset($ast->docComment)) { $nodedoccomment = $this->quote_and_escape($ast->docComment); } // store node, export all children and store the relationships $rootnode = $this->store_node($nodetype, $nodeflags, $nodeline, null, $childnum, $funcid, $nodeendline, $nodename, $nodedoccomment); // If this node is a function/method/closure declaration, set $funcid. // Note that in particular, the decl node *itself* does not have $funcid set to its own id; // this is intentional. The *declaration* of a function/method/closure itself is part of the // control flow of the outer scope: e.g., a closure declaration is part of the control flow // of the function it is declared in, or a function/method declaration is part of the control flow // of the pseudo-function representing the top-level code it is declared in. if ($ast->kind === ast\AST_FUNC_DECL || $ast->kind === ast\AST_METHOD || $ast->kind === ast\AST_CLOSURE) { $funcid = $rootnode; } foreach ($ast->children as $i => $child) { $childnode = $this->export($child, $nodeline, $i, $funcid); $this->store_rel($rootnode, $childnode, "PARENT_OF"); } } else { if (is_string($ast)) { $nodetype = gettype($ast); // should be string $rootnode = $this->store_node($nodetype, null, $nodeline, $this->quote_and_escape($ast), $childnum, $funcid); } else { if ($ast === null) { $nodetype = gettype($ast); // should be the string "NULL" $rootnode = $this->store_node($nodetype, null, $nodeline, null, $childnum, $funcid); } else { $nodetype = gettype($ast); $nodecode = (string) $ast; $rootnode = $this->store_node($nodetype, null, $nodeline, $nodecode, $childnum, $funcid); } } } return $rootnode; }
private function revertAST($node) : string { if (!$node instanceof Node) { switch (gettype($node)) { case 'integer': case 'double': case 'boolean': return $node; case 'string': return '"' . $this->sanitiseString($node) . '"'; default: // an array, null, etc, should never come through here assert(false, 'Unknown type (' . gettype($node) . ') found.'); } } switch ($node->kind) { case \ast\AST_ARG_LIST: return $this->argList($node); case \ast\AST_ARRAY: return $this->array($node); case \ast\AST_ARRAY_ELEM: return $this->arrayElem($node); case \ast\AST_ASSIGN: return $this->assign($node); case \ast\AST_ASSIGN_OP: return $this->assignOp($node); case \ast\AST_ASSIGN_REF: return $this->assignRef($node); case \ast\AST_BINARY_OP: return $this->binaryOp($node); case \ast\AST_BREAK: return $this->break($node); case \ast\AST_CALL: return $this->call($node); case \ast\AST_CAST: return $this->cast($node); case \ast\AST_CATCH: return $this->catch($node); case \ast\AST_CATCH_LIST: return $this->catchList($node); case \ast\AST_CLASS: return $this->class($node); case \ast\AST_CLASS_CONST: return $this->classConst($node); case \ast\AST_CLASS_CONST_DECL: return $this->classConstDecl($node); case \ast\AST_CLONE: return $this->clone($node); case \ast\AST_CLOSURE: return $this->closure($node); case \ast\AST_CLOSURE_VAR: return $this->closureVar($node); case \ast\AST_COALESCE: return $this->coalesce($node); case \ast\AST_CONDITIONAL: return $this->conditional($node); case \ast\AST_CONST: return $this->const($node); case \ast\AST_CONST_DECL: return $this->constDecl($node); case \ast\AST_CONST_ELEM: return $this->constElem($node); case \ast\AST_CONTINUE: return $this->continue($node); case \ast\AST_DECLARE: return $this->declare($node); case \ast\AST_DIM: return $this->dim($node); case \ast\AST_DO_WHILE: return $this->doWhile($node); case \ast\AST_ECHO: return $this->echo($node); case \ast\AST_EMPTY: return $this->empty($node); case \ast\AST_ENCAPS_LIST: return $this->encapsList($node); case \ast\AST_EXIT: return $this->exit($node); case \ast\AST_EXPR_LIST: return $this->exprList($node); case \ast\AST_FOR: return $this->for($node); case \ast\AST_FOREACH: return $this->foreach($node); case \ast\AST_FUNC_DECL: return $this->funcDecl($node); case \ast\AST_GLOBAL: return $this->global($node); case \ast\AST_GOTO: return $this->goto($node); case \ast\AST_GROUP_USE: return $this->groupUse($node); case \ast\AST_HALT_COMPILER: return $this->haltCompiler($node); case \ast\AST_IF: return $this->if($node); case \ast\AST_IF_ELEM: return $this->ifElem($node); case \ast\AST_INCLUDE_OR_EVAL: return $this->includeOrEval($node); case \ast\AST_INSTANCEOF: return $this->instanceof($node); case \ast\AST_ISSET: return $this->isset($node); case \ast\AST_LABEL: return $this->label($node); case \ast\AST_LIST: return $this->list($node); case \ast\AST_MAGIC_CONST: return $this->magicConst($node); case \ast\AST_METHOD: return $this->method($node); case \ast\AST_METHOD_CALL: return $this->methodCall($node); case \ast\AST_METHOD_REFERENCE: return $this->methodReference($node); case \ast\AST_NAME: return $this->name($node); case \ast\AST_NAMESPACE: return $this->namespace($node); case \ast\AST_NAME_LIST: return $this->nameList($node); case \ast\AST_NEW: return $this->new($node); case \ast\AST_PARAM: return $this->param($node); case \ast\AST_PARAM_LIST: return $this->paramList($node); case \ast\AST_POST_DEC: return $this->postDec($node); case \ast\AST_POST_INC: return $this->postInc($node); case \ast\AST_PRE_DEC: return $this->preDec($node); case \ast\AST_PRE_INC: return $this->preInc($node); case \ast\AST_PRINT: return $this->print($node); case \ast\AST_PROP: return $this->prop($node); case \ast\AST_PROP_DECL: return $this->propDecl($node); case \ast\AST_PROP_ELEM: return $this->propElem($node); case \ast\AST_REF: return $this->ref($node); case \ast\AST_RETURN: return $this->return($node); case \ast\AST_SHELL_EXEC: return $this->shellExec($node); case \ast\AST_STATIC: return $this->static($node); case \ast\AST_STATIC_CALL: return $this->staticCall($node); case \ast\AST_STATIC_PROP: return $this->staticProp($node); case \ast\AST_STMT_LIST: return $this->stmtList($node); case \ast\AST_SWITCH: return $this->switch($node); case \ast\AST_SWITCH_CASE: return $this->switchCase($node); case \ast\AST_SWITCH_LIST: return $this->switchList($node); case \ast\AST_THROW: return $this->throw($node); case \ast\AST_TRAIT_ADAPTATIONS: return $this->traitAdaptations($node); case \ast\AST_TRAIT_ALIAS: return $this->traitAlias($node); case \ast\AST_TRAIT_PRECEDENCE: return $this->traitPrecedence($node); case \ast\AST_TRY: return $this->try($node); case \ast\AST_TYPE: return $this->type($node); case \ast\AST_UNARY_OP: return $this->unaryOp($node); case \ast\AST_UNPACK: return $this->unpack($node); case \ast\AST_UNSET: return $this->unset($node); case \ast\AST_USE: return $this->use($node); case \ast\AST_USE_ELEM: return $this->useElem($node); case \ast\AST_USE_TRAIT: return $this->useTrait($node); case \ast\AST_VAR: return $this->var($node); case \ast\AST_WHILE: return $this->while($node); case \ast\AST_YIELD: return $this->yield($node); case \ast\AST_YIELD_FROM: return $this->yieldFrom($node); default: assert(false, 'Unknown AST kind (' . \ast\get_kind_name($node->kind) . ') found.'); } }