1 /*
2  * [The "BSD license"]
3  *  Copyright (c) 2013 Terence Parr
4  *  Copyright (c) 2013 Sam Harwell
5  *  Copyright (c) 2017 Egbert Voigt
6  *  All rights reserved.
7  *
8  *  Redistribution and use in source and binary forms, with or without
9  *  modification, are permitted provided that the following conditions
10  *  are met:
11  *
12  *  1. Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *  2. Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *  3. The name of the author may not be used to endorse or promote products
18  *     derived from this software without specific prior written permission.
19  *
20  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 module antlr.v4.runtime.tree.Trees;
33 
34 import std.array;
35 import std.conv;
36 import antlr.v4.runtime.InterfaceRecognizer;
37 import antlr.v4.runtime.ParserRuleContext;
38 import antlr.v4.runtime.RuleContext;
39 import antlr.v4.runtime.CommonToken;
40 import antlr.v4.runtime.Token;
41 import antlr.v4.runtime.TokenConstantDefinition;
42 import antlr.v4.runtime.atn.ATN;
43 import antlr.v4.runtime.tree.TerminalNode;
44 import antlr.v4.runtime.tree.ErrorNode;
45 import antlr.v4.runtime.tree.Tree;
46 import antlr.v4.runtime.tree.TerminalNodeImpl;
47 import antlr.v4.runtime.tree.ParseTree;
48 import antlr.v4.runtime.misc.Utils;
49 import antlr.v4.runtime.misc.Interval;
50 import antlr.v4.runtime.misc.Predicate;
51 
52 /**
53  * TODO add class description
54  * @uml
55  * A set of utility routines useful for all kinds of ANTLR trees
56  */
57 class Trees
58 {
59 
60     private this()
61     {
62     }
63 
64     /**
65      * @uml
66      * Print out a whole tree in LISP form. {@link #getNodeText} is used on the
67      * node payloads to get the text for the nodes.  Detect
68      * parse trees and extract data appropriately.
69      */
70     public static string toStringTree(Tree t)
71     {
72         return toStringTree(t, cast(string[])null);
73     }
74 
75     /**
76      * @uml
77      * Print out a whole tree in LISP form. {@link #getNodeText} is used on the
78      * node payloads to get the text for the nodes.  Detect
79      * parse trees and extract data appropriately.
80      */
81     public static string toStringTree(Tree t, InterfaceRecognizer recog)
82     {
83         string[] ruleNames = recog !is null ? recog.getRuleNames() : null;
84         string[] ruleNamesList = ruleNames !is null ? ruleNames : null;
85         return t.toStringTree ~ to!string(ruleNamesList);
86     }
87 
88     /**
89      * @uml
90      * rint out a whole tree in LISP form. {@link #getNodeText} is used on the
91      * node payloads to get the text for the nodes.
92      */
93     public static string toStringTree(Tree t, string[] ruleNames)
94     {
95         string s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false);
96         if ( t.getChildCount()==0 ) return s;
97         auto buf = appender!(string);
98 		buf.put("(");
99 		s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false);
100 		buf.put(s);
101 		buf.put(' ');
102 		for (int i = 0; i<t.getChildCount(); i++) {
103 			if ( i>0 ) buf.put(' ');
104 			buf.put(toStringTree(t.getChild(i), ruleNames));
105 		}
106 		buf.put(")");
107 		return buf.data;
108     }
109 
110     public static string getNodeText(Tree t, InterfaceRecognizer recog)
111     {
112         string[] ruleNames = recog !is null ? recog.getRuleNames() : null;
113         string[] ruleNamesList = ruleNames !is null ? ruleNames : null;
114         return getNodeText(t, ruleNamesList);
115 
116     }
117 
118     public static string getNodeText(Tree t, string[] ruleNames)
119     {
120 	if (ruleNames) {
121             if (t.classinfo == RuleContext.classinfo) {
122                 int ruleIndex = (cast(RuleContext)t).getRuleContext().getRuleIndex();
123                 string ruleName = ruleNames[ruleIndex];
124                 int altNumber = (cast(RuleContext) t).getAltNumber();
125                 if (altNumber != ATN.INVALID_ALT_NUMBER) {
126                     return ruleName ~ ":" ~ to!string(altNumber);
127                 }
128                 return ruleName;
129             }
130             else if (t.classinfo == ErrorNode.classinfo) {
131                 return "..."; // t.toString();
132             }
133             else if (t.classinfo == TerminalNode.classinfo) {
134                 Token symbol = (cast(TerminalNode)t).getSymbol();
135                 if (symbol !is null) {
136                     string s = symbol.getText();
137                     return s;
138                 }
139             }
140         }
141         // no recog for rule names
142         Object payload = t.getPayload();
143         if (payload.classinfo == Token.classinfo) {
144             return (cast(Token)payload).getText();
145         }
146         return t.getPayload().toString();
147     }
148 
149     /**
150      * @uml
151      * Return ordered list of all children of this node
152      */
153     public static Tree[] getChildren(Tree t)
154     {
155         Tree[] kids;
156         for (int i=0; i<t.getChildCount(); i++) {
157             kids ~= t.getChild(i);
158         }
159         return kids;
160     }
161 
162     /**
163      * @uml
164      * Return a list of all ancestors of this node.  The first node of
165      * list is the root and the last is the parent of this node.
166      */
167     public static Tree[] getAncestors(Tree t)
168     {
169         Tree[] emptyTrees;
170 	if (t.getParent() is null) return emptyTrees;
171         Tree[] ancestors;
172         t = t.getParent();
173         while (t !is null) {
174             ancestors = t ~ ancestors; // insert at start
175             t = t.getParent();
176         }
177         return ancestors;
178     }
179 
180     /**
181      * @uml
182      * Return true if t is u's parent or a node on path to root from u.
183      * Use == not equals().
184      */
185     public static bool isAncestorOf(Tree t, Tree u)
186     {
187         if (t is null || u is null || t.getParent() is null) return false;
188 		Tree p = u.getParent();
189 		while (p !is null) {
190 			if (t is p) return true;
191 			p = p.getParent();
192 		}
193 		return false;
194     }
195 
196     public static ParseTree[] findAllTokenNodes(ParseTree t, int ttype)
197     {
198         return findAllNodes(t, ttype, true);
199     }
200 
201     public static ParseTree[] findAllRuleNodes(ParseTree t, int ruleIndex)
202     {
203         return findAllNodes(t, ruleIndex, false);
204     }
205 
206     public static ParseTree[] findAllNodes(ParseTree t, int index, bool findTokens)
207     {
208         ParseTree[] nodes;
209         _findAllNodes(t, index, findTokens, nodes);
210         return nodes;
211     }
212 
213     public static void _findAllNodes(ParseTree t, int index, bool findTokens, ParseTree[] nodes)
214     {
215 	// check this node (the root) first
216         if ( findTokens && t.classinfo == TerminalNode.classinfo) {
217             TerminalNode tnode = cast(TerminalNode)t;
218             if (tnode.getSymbol().getType() == index) nodes ~= t;
219         }
220         else if (!findTokens && t.classinfo == ParserRuleContext.classinfo) {
221             ParserRuleContext ctx = cast(ParserRuleContext)t;
222             if ( ctx.getRuleIndex() == index ) nodes ~= t;
223         }
224         // check children
225         for (int i = 0; i < t.getChildCount(); i++){
226             _findAllNodes(t.getChild(i), index, findTokens, nodes);
227         }
228     }
229 
230     /**
231      * @uml
232      * Get all descendents; includes t itself.
233      */
234     public static ParseTree[] getDescendants(ParseTree t)
235     {
236         ParseTree[] nodes;
237         nodes ~= t;
238 
239         int n = t.getChildCount();
240         for (int i = 0 ; i < n ; i++){
241             nodes ~= getDescendants(t.getChild(i));
242         }
243         return nodes;
244     }
245 
246     public static ParseTree[] descendants(ParseTree t)
247     {
248         return getDescendants(t);
249     }
250 
251     /**
252      * @uml
253      * Find smallest subtree of t enclosing range startTokenIndex..stopTokenIndex
254      * inclusively using postorder traversal.  Recursive depth-first-search.
255      */
256     public static ParserRuleContext getRootOfSubtreeEnclosingRegion(ParseTree t, int startTokenIndex,
257         int stopTokenIndex)
258     {
259 	int n = t.getChildCount();
260         for (int i = 0; i<n; i++) {
261             ParseTree child = t.getChild(i);
262             ParserRuleContext r = getRootOfSubtreeEnclosingRegion(child, startTokenIndex, stopTokenIndex);
263             if (r !is null) return r;
264         }
265         if (t.classinfo == ParserRuleContext.classinfo) {
266             ParserRuleContext r = cast(ParserRuleContext)t;
267             if (startTokenIndex >= r.getStart().getTokenIndex() && // is range fully contained in t?
268                 (r.getStop() is null || stopTokenIndex <= r.getStop().getTokenIndex()) )
269                 {
270                     // note: r.getStop()==null likely implies that we bailed out of parser and there's nothing to the right
271                     return r;
272                 }
273         }
274         return null;
275     }
276 
277     /**
278      * @uml
279      * Replace any subtree siblings of root that are completely to left
280      * or right of lookahead range with a CommonToken(Token.INVALID_TYPE,"...")
281      * node. The source interval for t is not altered to suit smaller range!
282      *
283      * WARNING: destructive to t.
284      */
285     public static void stripChildrenOutOfRange(ParserRuleContext t, ParserRuleContext root,
286         int startIndex, int stopIndex)
287     {
288         if (t is null) return;
289         for (int i = 0; i < t.getChildCount(); i++) {
290             ParseTree child = t.getChild(i);
291             Interval range = child.getSourceInterval();
292             if (child.classinfo == ParserRuleContext.classinfo && (range.b < startIndex || range.a > stopIndex) ) {
293                 if (isAncestorOf(child, root)) { // replace only if subtree doesn't have displayed root
294                     CommonToken abbrev = new CommonToken(TokenConstantDefinition.INVALID_TYPE, "...");
295                     t.children[i] =  new TerminalNodeImpl(abbrev);
296                 }
297             }
298         }
299     }
300 
301     /**
302      * @uml
303      * Return first node satisfying the pred
304      */
305     public static Tree findNodeSuchThat(Tree t, Predicate!Tree pred)
306     {
307         if (pred.test(t) ) return t;
308 
309         int n = t.getChildCount();
310         for (int i = 0 ; i < n ; i++){
311             Tree u = findNodeSuchThat(t.getChild(i), pred);
312             if (u !is null ) return u;
313         }
314         return null;
315     }
316 
317 }