class CommentsRegistry
Comments registry class. Internal class used to manage comments
@author Marco Marchiò <marco.mm89@gmail.com>
Hierarchy
- class \Peast\Syntax\CommentsRegistry
Expanded class hierarchy of CommentsRegistry
File
-
vendor/
mck89/ peast/ lib/ Peast/ Syntax/ CommentsRegistry.php, line 17
Namespace
Peast\SyntaxView source
class CommentsRegistry {
/**
* Map of the indices where nodes start
*
* @var int
*/
protected $nodesStartMap = array();
/**
* Map of the indices where nodes end
*
* @var int
*/
protected $nodesEndMap = array();
/**
* Comments buffer
*
* @var array
*/
protected $buffer = null;
/**
* Last token index
*
* @var int
*/
protected $lastTokenIndex = null;
/**
* Comments registry
*
* @var array
*/
protected $registry = array();
/**
* Class constructor
*
* @param Parser $parser Parser
*/
public function __construct(Parser $parser) {
$parser->getEventsEmitter()
->addListener("NodeCompleted", array(
$this,
"onNodeCompleted",
))
->addListener("EndParsing", array(
$this,
"onEndParsing",
));
$parser->getScanner()
->getEventsEmitter()
->addListener("TokenConsumed", array(
$this,
"onTokenConsumed",
))
->addListener("EndReached", array(
$this,
"onTokenConsumed",
))
->addListener("FreezeState", array(
$this,
"onScannerFreezeState",
))
->addListener("ResetState", array(
$this,
"onScannerResetState",
));
}
/**
* Listener called every time the scanner compose the array that represents
* its current state
*
* @param array $state State
*
* @return void
*/
public function onScannerFreezeState(&$state) {
//Register the current last token index
$state["commentsLastTokenIndex"] = $this->lastTokenIndex;
}
/**
* Listener called every time the scanner reset its state using the given
* array
*
* @param array $state State
*
* @return void
*/
public function onScannerResetState(&$state) {
//Reset the last token index and delete it from the state array
$this->lastTokenIndex = $state["commentsLastTokenIndex"];
unset($state["commentsLastTokenIndex"]);
}
/**
* Listener called every time a token is consumed and when the scanner
* reaches the end of the source
*
* @param Token|null $token Consumed token or null if the end has
* been reached
*
* @return void
*/
public function onTokenConsumed($token = null) {
//Check if it's a comment
if ($token && $token->type === Token::TYPE_COMMENT) {
//If there is not an open comments buffer, create it
if (!$this->buffer) {
$this->buffer = array(
"prev" => $this->lastTokenIndex,
"next" => null,
"comments" => array(),
);
}
//Add the comment token to the buffer
$this->buffer["comments"][] = $token;
}
else {
if ($token) {
$loc = $token->location;
//Store the token end position
$this->lastTokenIndex = $loc->end
->getIndex();
if ($this->buffer) {
//Fill the "next" key on the comments buffer with the token
//start position
$this->buffer["next"] = $loc->start
->getIndex();
}
}
//If there is an open comment buffer, close it and move it to the
//registry
if ($buffer = $this->buffer) {
//Use the location as key to add the group of comments to the
//registry, in this way if comments are reprocessed they won't
//be duplicated
$key = implode("-", array(
$buffer["prev"] !== null ? $buffer["prev"] : "",
$buffer["next"] !== null ? $buffer["next"] : "",
));
$this->registry[$key] = $this->buffer;
$this->buffer = null;
}
}
}
/**
* Listener called every time a node is completed by the parser
*
* @param Node\Node $node Completed node
*
* @return void
*/
public function onNodeCompleted(Node\Node $node) {
//Every time a node is completed, register its start and end indices
//in the relative properties
$loc = $node->location;
foreach (array(
"Start",
"End",
) as $pos) {
$val = $loc->{"get{$pos}"}()
->getIndex();
$map =& $this->{"nodes{$pos}Map"};
if (!isset($map[$val])) {
$map[$val] = array();
}
$map[$val][] = $node;
}
}
/**
* Listener called when parsing process ends
*
* @return void
*/
public function onEndParsing() {
//Return if there are no comments to process
if ($this->registry) {
//Make sure nodes start indices map is sorted
ksort($this->nodesStartMap);
//Loop all comment groups in the registry
foreach ($this->registry as $group) {
$this->findNodeForCommentsGroup($group);
}
}
}
/**
* Finds the node to attach the given comments group
*
* @param array $group Comments group
*
* @return void
*/
public function findNodeForCommentsGroup($group) {
$next = $group["next"];
$prev = $group["prev"];
$comments = $group["comments"];
$leading = true;
//If the group of comments has a next token index that appears
//in the map of start node indices, add the group to the
//corresponding node's leading comments. This associates
//comments that appear immediately before a node.
//For example: /*comment*/ for (;;){}
if (isset($this->nodesStartMap[$next])) {
$nodes = $this->nodesStartMap[$next];
}
elseif (isset($this->nodesEndMap[$prev])) {
$nodes = $this->nodesEndMap[$prev];
$leading = false;
}
else {
//Calculate comments group boundaries
$start = $comments[0]->location->start
->getIndex();
$end = $comments[count($comments) - 1]->location->end
->getIndex();
$nodes = array();
//Loop all the entries in the start index map
foreach ($this->nodesStartMap as $idx => $ns) {
//If the index is higher than the start index of the comments
//group, stop
if ($idx > $start) {
break;
}
foreach ($ns as $node) {
//Check if the comments group is inside node indices range
if ($node->location->end
->getIndex() >= $end) {
$nodes[] = $node;
}
}
}
//If comments can't be associated with any node, associate it as
//leading comments of the program, this happens when the source is
//empty
if (!$nodes) {
$firstNode = array_values($this->nodesStartMap);
$nodes = array(
$firstNode[0][0],
);
}
}
//If there are multiple possible nodes to associate the comments to,
//find the shortest one
if (count($nodes) > 1) {
usort($nodes, array(
$this,
"compareNodesLength",
));
}
$this->associateComments($nodes[0], $comments, $leading);
}
/**
* Compares node length
*
* @param Node\Node $node1 First node
* @param Node\Node $node2 Second node
*
* @return int
*
* @codeCoverageIgnore
*/
public function compareNodesLength($node1, $node2) {
$loc1 = $node1->location;
$length1 = $loc1->end
->getIndex() - $loc1->start
->getIndex();
$loc2 = $node2->location;
$length2 = $loc2->end
->getIndex() - $loc2->start
->getIndex();
//If the nodes have the same length make sure to choose nodes
//different from Program nodes
if ($length1 === $length2) {
if ($node1 instanceof Node\Program) {
$length1 += 1000;
}
elseif ($node2 instanceof Node\Program) {
$length2 += 1000;
}
}
return $length1 < $length2 ? -1 : 1;
}
/**
* Adds comments to the given node
*
* @param Node\Node $node Node
* @param array $comments Array of comments to add
* @param bool $leading True to add comments as leading comments
* or false to add them as trailing comments
*
* @return void
*/
public function associateComments($node, $comments, $leading) {
$fn = ($leading ? "Leading" : "Trailing") . "Comments";
$currentComments = $node->{"get{$fn}"}();
foreach ($comments as $comment) {
$loc = $comment->location;
$commentNode = new Node\Comment();
$commentNode->location->start = $loc->start;
$commentNode->location->end = $loc->end;
$commentNode->setRawText($comment->value);
$currentComments[] = $commentNode;
}
$node->{"set{$fn}"}($currentComments);
}
}
Members
Title Sort descending | Modifiers | Object type | Summary |
---|---|---|---|
CommentsRegistry::$buffer | protected | property | Comments buffer |
CommentsRegistry::$lastTokenIndex | protected | property | Last token index |
CommentsRegistry::$nodesEndMap | protected | property | Map of the indices where nodes end |
CommentsRegistry::$nodesStartMap | protected | property | Map of the indices where nodes start |
CommentsRegistry::$registry | protected | property | Comments registry |
CommentsRegistry::associateComments | public | function | Adds comments to the given node |
CommentsRegistry::compareNodesLength | public | function | Compares node length |
CommentsRegistry::findNodeForCommentsGroup | public | function | Finds the node to attach the given comments group |
CommentsRegistry::onEndParsing | public | function | Listener called when parsing process ends |
CommentsRegistry::onNodeCompleted | public | function | Listener called every time a node is completed by the parser |
CommentsRegistry::onScannerFreezeState | public | function | Listener called every time the scanner compose the array that represents its current state |
CommentsRegistry::onScannerResetState | public | function | Listener called every time the scanner reset its state using the given array |
CommentsRegistry::onTokenConsumed | public | function | Listener called every time a token is consumed and when the scanner reaches the end of the source |
CommentsRegistry::__construct | public | function | Class constructor |