1*37748cd8SNickeau<?php 2*37748cd8SNickeau 3*37748cd8SNickeaudeclare(strict_types=1); 4*37748cd8SNickeau 5*37748cd8SNickeaunamespace Antlr\Antlr4\Runtime\Tree; 6*37748cd8SNickeau 7*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Atn\ATN; 8*37748cd8SNickeauuse Antlr\Antlr4\Runtime\ParserRuleContext; 9*37748cd8SNickeauuse Antlr\Antlr4\Runtime\RuleContext; 10*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Token; 11*37748cd8SNickeauuse Antlr\Antlr4\Runtime\Utils\StringUtils; 12*37748cd8SNickeau 13*37748cd8SNickeau/** 14*37748cd8SNickeau * A set of utility routines useful for all kinds of ANTLR trees. 15*37748cd8SNickeau */ 16*37748cd8SNickeauclass Trees 17*37748cd8SNickeau{ 18*37748cd8SNickeau /** 19*37748cd8SNickeau * Print out a whole tree in LISP form. {@see Trees::getNodeText()} is used 20*37748cd8SNickeau * on the node payloads to get the text for the nodes. Detect parse trees 21*37748cd8SNickeau * and extract data appropriately. 22*37748cd8SNickeau * 23*37748cd8SNickeau * @param array<string>|null $ruleNames 24*37748cd8SNickeau */ 25*37748cd8SNickeau public static function toStringTree(Tree $tree, ?array $ruleNames = null) : string 26*37748cd8SNickeau { 27*37748cd8SNickeau $string = self::getNodeText($tree, $ruleNames); 28*37748cd8SNickeau $string = StringUtils::escapeWhitespace($string, false); 29*37748cd8SNickeau 30*37748cd8SNickeau $childCount = $tree->getChildCount(); 31*37748cd8SNickeau 32*37748cd8SNickeau if ($childCount === 0) { 33*37748cd8SNickeau return $string; 34*37748cd8SNickeau } 35*37748cd8SNickeau 36*37748cd8SNickeau $result = '(' . $string . ' '; 37*37748cd8SNickeau 38*37748cd8SNickeau for ($i = 0; $i < $childCount; $i++) { 39*37748cd8SNickeau $child = $tree->getChild($i); 40*37748cd8SNickeau 41*37748cd8SNickeau if ($child !== null) { 42*37748cd8SNickeau $result .= ($i > 0 ? ' ' : '') . self::toStringTree($child, $ruleNames); 43*37748cd8SNickeau } 44*37748cd8SNickeau } 45*37748cd8SNickeau 46*37748cd8SNickeau return $result . ')'; 47*37748cd8SNickeau } 48*37748cd8SNickeau 49*37748cd8SNickeau /** 50*37748cd8SNickeau * @param array<string>|null $ruleNames 51*37748cd8SNickeau */ 52*37748cd8SNickeau public static function getNodeText(Tree $tree, ?array $ruleNames) : string 53*37748cd8SNickeau { 54*37748cd8SNickeau if ($ruleNames !== null) { 55*37748cd8SNickeau if ($tree instanceof RuleContext) { 56*37748cd8SNickeau $ruleIndex = $tree->getRuleContext()->getRuleIndex(); 57*37748cd8SNickeau $ruleName = $ruleNames[$ruleIndex]; 58*37748cd8SNickeau $altNumber = $tree->getAltNumber(); 59*37748cd8SNickeau 60*37748cd8SNickeau if ($altNumber !== ATN::INVALID_ALT_NUMBER) { 61*37748cd8SNickeau return \sprintf('%s:%s', $ruleName, $altNumber); 62*37748cd8SNickeau } 63*37748cd8SNickeau 64*37748cd8SNickeau return $ruleName; 65*37748cd8SNickeau } 66*37748cd8SNickeau 67*37748cd8SNickeau if ($tree instanceof ErrorNode) { 68*37748cd8SNickeau return (string) $tree; 69*37748cd8SNickeau } 70*37748cd8SNickeau 71*37748cd8SNickeau if ($tree instanceof TerminalNode && $tree->getSymbol() !== null) { 72*37748cd8SNickeau return $tree->getSymbol()->getText() ?? ''; 73*37748cd8SNickeau } 74*37748cd8SNickeau } 75*37748cd8SNickeau 76*37748cd8SNickeau // no recog for rule names 77*37748cd8SNickeau $payload = $tree->getPayload(); 78*37748cd8SNickeau 79*37748cd8SNickeau if ($payload instanceof Token) { 80*37748cd8SNickeau return $payload->getText() ?? ''; 81*37748cd8SNickeau } 82*37748cd8SNickeau 83*37748cd8SNickeau return (string) $tree->getPayload(); 84*37748cd8SNickeau } 85*37748cd8SNickeau 86*37748cd8SNickeau /** 87*37748cd8SNickeau * Return ordered list of all children of this node 88*37748cd8SNickeau * 89*37748cd8SNickeau * @return array<Tree|null> 90*37748cd8SNickeau */ 91*37748cd8SNickeau public static function getChildren(Tree $tree) : array 92*37748cd8SNickeau { 93*37748cd8SNickeau $list = []; 94*37748cd8SNickeau for ($i=0; $i < $tree->getChildCount(); $i++) { 95*37748cd8SNickeau $list[] = $tree->getChild($i); 96*37748cd8SNickeau } 97*37748cd8SNickeau 98*37748cd8SNickeau return $list; 99*37748cd8SNickeau } 100*37748cd8SNickeau 101*37748cd8SNickeau /** 102*37748cd8SNickeau * Return a list of all ancestors of this node. The first node of list 103*37748cd8SNickeau * is the root and the last is the parent of this node. 104*37748cd8SNickeau * 105*37748cd8SNickeau * @return array<Tree> 106*37748cd8SNickeau */ 107*37748cd8SNickeau public static function getAncestors(Tree $tree) : array 108*37748cd8SNickeau { 109*37748cd8SNickeau $ancestors = []; 110*37748cd8SNickeau $tree = $tree->getParent(); 111*37748cd8SNickeau 112*37748cd8SNickeau while ($tree !== null) { 113*37748cd8SNickeau \array_unshift($ancestors, $tree); 114*37748cd8SNickeau 115*37748cd8SNickeau $tree = $tree->getParent(); 116*37748cd8SNickeau } 117*37748cd8SNickeau 118*37748cd8SNickeau return $ancestors; 119*37748cd8SNickeau } 120*37748cd8SNickeau 121*37748cd8SNickeau /** 122*37748cd8SNickeau * @return array<ParseTree> 123*37748cd8SNickeau */ 124*37748cd8SNickeau public static function findAllTokenNodes(ParseTree $tree, int $ttype) : array 125*37748cd8SNickeau { 126*37748cd8SNickeau return self::findAllNodes($tree, $ttype, true); 127*37748cd8SNickeau } 128*37748cd8SNickeau 129*37748cd8SNickeau /** 130*37748cd8SNickeau * @return array<ParseTree> 131*37748cd8SNickeau */ 132*37748cd8SNickeau public static function findAllRuleNodes(ParseTree $tree, int $ruleIndex) : array 133*37748cd8SNickeau { 134*37748cd8SNickeau return self::findAllNodes($tree, $ruleIndex, false); 135*37748cd8SNickeau } 136*37748cd8SNickeau 137*37748cd8SNickeau /** 138*37748cd8SNickeau * @return array<ParseTree> 139*37748cd8SNickeau */ 140*37748cd8SNickeau public static function findAllNodes(ParseTree $tree, int $index, bool $findTokens) : array 141*37748cd8SNickeau { 142*37748cd8SNickeau return self::findNodesInTree($tree, $index, $findTokens, []); 143*37748cd8SNickeau } 144*37748cd8SNickeau 145*37748cd8SNickeau /** 146*37748cd8SNickeau * @param array<ParseTree> $nodes 147*37748cd8SNickeau * 148*37748cd8SNickeau * @return array<ParseTree> 149*37748cd8SNickeau */ 150*37748cd8SNickeau private static function findNodesInTree(ParseTree $tree, int $index, bool $findTokens, array $nodes) : array 151*37748cd8SNickeau { 152*37748cd8SNickeau // check this node (the root) first 153*37748cd8SNickeau if ($findTokens && $tree instanceof TerminalNode && $tree->getSymbol()->getType() === $index) { 154*37748cd8SNickeau $nodes[] = $tree; 155*37748cd8SNickeau } elseif (!$findTokens && $tree instanceof ParserRuleContext && $tree->getRuleIndex() === $index) { 156*37748cd8SNickeau $nodes[] = $tree; 157*37748cd8SNickeau } 158*37748cd8SNickeau 159*37748cd8SNickeau // check children 160*37748cd8SNickeau for ($i = 0; $i < $tree->getChildCount(); $i++) { 161*37748cd8SNickeau $child = $tree->getChild($i); 162*37748cd8SNickeau 163*37748cd8SNickeau if ($child !== null) { 164*37748cd8SNickeau $nodes = self::findNodesInTree($child, $index, $findTokens, $nodes); 165*37748cd8SNickeau } 166*37748cd8SNickeau } 167*37748cd8SNickeau 168*37748cd8SNickeau return $nodes; 169*37748cd8SNickeau } 170*37748cd8SNickeau 171*37748cd8SNickeau /** 172*37748cd8SNickeau * @return array<ParseTree> 173*37748cd8SNickeau */ 174*37748cd8SNickeau public static function descendants(ParseTree $tree) : array 175*37748cd8SNickeau { 176*37748cd8SNickeau $nodes = [$tree]; 177*37748cd8SNickeau for ($i = 0; $i < $tree->getChildCount(); $i++) { 178*37748cd8SNickeau $child = $tree->getChild($i); 179*37748cd8SNickeau 180*37748cd8SNickeau if ($child !== null) { 181*37748cd8SNickeau $nodes[] = self::descendants($child); 182*37748cd8SNickeau } 183*37748cd8SNickeau } 184*37748cd8SNickeau 185*37748cd8SNickeau return \array_merge(...$nodes); 186*37748cd8SNickeau } 187*37748cd8SNickeau} 188