xref: /plugin/combo/vendor/antlr/antlr4-php-runtime/src/Tree/Trees.php (revision 37748cd8654635afbeca80942126742f0f4cc346)
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