hhvm composer require hhvm/hhast
Facebook\HHAST\from_file()
is the main entry point to the library; it will get you an instance of
EditableNode
, most likely a Script
. The main classes are:
EditableNode
: the base class of all AST nodesEditableToken
: the base class of all nodes that are also tokens; this is a subclass ofEditableNode
EditableTrivia
: the base class of all nodes that do not affect runtime behavior, such as whitespace and comments; this is a subclass ofEditableNode
EditableList
: this node merely contains a vector-like list of child nodes; this is a subclass ofEditableNode
Missing
: a singleton; this node is present when an optional node is not present. It is a subclass ofEditableNode
, and instantiated via the functionMissing()
For every type of node in the full fidelity AST, there is a generated class extending EditableNode
, EditableToken
, or EditableTrivia
; for example, class declarations are represented by the ClassishDeclaration
class. These define accessors for every field on the node.
Additionally, several interfaces are defined for convenience, which are not present in the FFP schema:
IControlFlowStatement
: implemented by structures likeif
and looping constructsIFunctionishDeclaration
: implemented by both function and method declarationsILoopStatement
: sub-interface ofIControlFlowStatement
, specifically for looping constructs
These are primarily useful for instanceof
checks, or as a value for classname<EditableNode>
when calling get(Children|Descendants)OfType()
.
For the basic API, see below; for usage examples, see:
->getCode()
: convert an AST node to string code->getChildren()
: return all direct children of the node->getChildrenWhere((function(EditableNode):bool))
: return all direct children of the node where the function returns true->getChildrenOfType(classname<EditableNode>)
: return all direct children of the specified type->traverse()
: return the node and all recursive descendants->traverseWithParents()
: return the node and all recursive descendants, and the stack of the parents->getDescendantsWhere((function(EditableNode$node, vec<EditableNode> $parents):bool))
: return all recursive children of the node where the function returns true->getDescendantsOfType(classname<EditableNode>)
: return all recursive children of the specified type->getFirstToken()
: returns the firstEditableToken
that is part of the current node->getLastToken()
: returns the firstEditableToken
that is part of the current node
The AST does not support in-place editing; instead, all mutation functions return a new node with the specified mutation applied.
->rewrite((function(EditableNode $node, vec<EditableNode> $parents): EditableNode))
: traverses the AST, and applies the specified mapping function to every node. To remove a node, you can specifyMissing()
->rewriteDescendants((function(EditableNode $node, vec<EditableNode> $parents): EditableNode)): this
: traverses the AST, and applies the specified mapping function to every node except itself, so returns a node of the same type.->replace(EditableNode $old, EditableNode $new)
: return a copy of the node with descendant$old
replaced with$new
->removeWhere((function(EditableNode, vec<EditableNode>)))
: returns a copy of the node without any descendants that pass the predicate->remove(EditableNode)
: returns a copy of the node without the specified descendant->insertAfter(EditableNode $target, EditableNode $new)
: returns a copy of the node with the specified$new
node inserted after the$target
node->insertBefore(EditableNode $target, EditableNode $new)
: returns a copy of the node with the specified$new
node inserted before the$target
node
Tokens define:
->getLeading()
- get leading trivia->getLeadingWhitespace()
- specifically, get leading whitespace->getTrailing()
- get leading trivia->getTrailingWhitespace()
- get leading trivia
For every field on AST node, there are at least two accessors; assuming a field called some_field
:
->getSomeFieldUNTYPED()
: always returns anEditableNode
->getSomeField()
: returnsT
or?T
where T is eitherEditableNode
, or a subclass of it
If Missing
is considered a valid value, additional methods are defined:
->hasSomeField()
: returns true or false->getSomeFieldx()
: returnsT
or throws an exception if missing
- install
jq
or another JSON prettifier, and examine files containing AST structures you're interested in withhh_parse --full-fidelity-json $file | jq
- use an IDE with autocompletion; there are far too many AST node types for memorizing the APIs to be practical
- if you are using the write API, write unit tests with various combinations of leading and trailing whitespace and comments