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 // Class Trees
53 /**
54  * TODO add class description
55  * @uml
56  * A set of utility routines useful for all kinds of ANTLR trees
57  */
58 class Trees
59 {
60 
61     private this()
62     {
63     }
64 
65     /**
66      * @uml
67      * Print out a whole tree in LISP form. {@link #getNodeText} is used on the
68      * node payloads to get the text for the nodes.  Detect
69      * parse trees and extract data appropriately.
70      */
71     public static string toStringTree(Tree t)
72     {
73         return toStringTree(t, cast(string[])null);
74     }
75 
76     /**
77      * @uml
78      * Print out a whole tree in LISP form. {@link #getNodeText} is used on the
79      * node payloads to get the text for the nodes.  Detect
80      * parse trees and extract data appropriately.
81      */
82     public static string toStringTree(Tree t, InterfaceRecognizer recog)
83     {
84         string[] ruleNames = recog !is null ? recog.getRuleNames() : null;
85         string[] ruleNamesList = ruleNames !is null ? ruleNames : null;
86         return t.toStringTree ~ to!string(ruleNamesList);
87     }
88 
89     /**
90      * @uml
91      * rint out a whole tree in LISP form. {@link #getNodeText} is used on the
92      * node payloads to get the text for the nodes.
93      */
94     public static string toStringTree(Tree t, string[] ruleNames)
95     {
96         string s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false);
97         if ( t.getChildCount()==0 ) return s;
98         auto buf = appender!(string);
99 		buf.put("(");
100 		s = Utils.escapeWhitespace(getNodeText(t, ruleNames), false);
101 		buf.put(s);
102 		buf.put(' ');
103 		for (int i = 0; i<t.getChildCount(); i++) {
104 			if ( i>0 ) buf.put(' ');
105 			buf.put(toStringTree(t.getChild(i), ruleNames));
106 		}
107 		buf.put(")");
108 		return buf.data;
109     }
110 
111     public static string getNodeText(Tree t, InterfaceRecognizer recog)
112     {
113         string[] ruleNames = recog !is null ? recog.getRuleNames() : null;
114         string[] ruleNamesList = ruleNames !is null ? ruleNames : null;
115         return getNodeText(t, ruleNamesList);
116 
117     }
118 
119     public static string getNodeText(Tree t, string[] ruleNames)
120     {
121 	if (ruleNames) {
122             if (t.classinfo == RuleContext.classinfo) {
123                 int ruleIndex = (cast(RuleContext)t).getRuleContext().getRuleIndex();
124                 string ruleName = ruleNames[ruleIndex];
125                 int altNumber = (cast(RuleContext) t).getAltNumber();
126                 if (altNumber != ATN.INVALID_ALT_NUMBER) {
127                     return ruleName ~ ":" ~ to!string(altNumber);
128                 }
129                 return ruleName;
130             }
131             else if (t.classinfo == ErrorNode.classinfo) {
132                 return "..."; // t.toString();
133             }
134             else if (t.classinfo == TerminalNode.classinfo) {
135                 Token symbol = (cast(TerminalNode)t).getSymbol();
136                 if (symbol !is null) {
137                     string s = symbol.getText();
138                     return s;
139                 }
140             }
141         }
142         // no recog for rule names
143         Object payload = t.getPayload();
144         if (payload.classinfo == Token.classinfo) {
145             return (cast(Token)payload).getText();
146         }
147         return t.getPayload().toString();
148     }
149 
150     /**
151      * @uml
152      * Return ordered list of all children of this node
153      */
154     public static Tree[] getChildren(Tree t)
155     {
156         Tree[] kids;
157         for (int i=0; i<t.getChildCount(); i++) {
158             kids ~= t.getChild(i);
159         }
160         return kids;
161     }
162 
163     /**
164      * @uml
165      * Return a list of all ancestors of this node.  The first node of
166      * list is the root and the last is the parent of this node.
167      */
168     public static Tree[] getAncestors(Tree t)
169     {
170         Tree[] emptyTrees;
171 	if (t.getParent() is null) return emptyTrees;
172         Tree[] ancestors;
173         t = t.getParent();
174         while (t !is null) {
175             ancestors = t ~ ancestors; // insert at start
176             t = t.getParent();
177         }
178         return ancestors;
179     }
180 
181     /**
182      * @uml
183      * Return true if t is u's parent or a node on path to root from u.
184      * Use == not equals().
185      */
186     public static bool isAncestorOf(Tree t, Tree u)
187     {
188         if (t is null || u is null || t.getParent() is null) return false;
189 		Tree p = u.getParent();
190 		while (p !is null) {
191 			if (t is p) return true;
192 			p = p.getParent();
193 		}
194 		return false;
195     }
196 
197     public static ParseTree[] findAllTokenNodes(ParseTree t, int ttype)
198     {
199         return findAllNodes(t, ttype, true);
200     }
201 
202     public static ParseTree[] findAllRuleNodes(ParseTree t, int ruleIndex)
203     {
204         return findAllNodes(t, ruleIndex, false);
205     }
206 
207     public static ParseTree[] findAllNodes(ParseTree t, int index, bool findTokens)
208     {
209         ParseTree[] nodes;
210         _findAllNodes(t, index, findTokens, nodes);
211         return nodes;
212     }
213 
214     public static void _findAllNodes(ParseTree t, int index, bool findTokens, ParseTree[] nodes)
215     {
216 	// check this node (the root) first
217         if ( findTokens && t.classinfo == TerminalNode.classinfo) {
218             TerminalNode tnode = cast(TerminalNode)t;
219             if (tnode.getSymbol().getType() == index) nodes ~= t;
220         }
221         else if (!findTokens && t.classinfo == ParserRuleContext.classinfo) {
222             ParserRuleContext ctx = cast(ParserRuleContext)t;
223             if ( ctx.getRuleIndex() == index ) nodes ~= t;
224         }
225         // check children
226         for (int i = 0; i < t.getChildCount(); i++){
227             _findAllNodes(t.getChild(i), index, findTokens, nodes);
228         }
229     }
230 
231     /**
232      * @uml
233      * Get all descendents; includes t itself.
234      */
235     public static ParseTree[] getDescendants(ParseTree t)
236     {
237         ParseTree[] nodes;
238         nodes ~= t;
239 
240         int n = t.getChildCount();
241         for (int i = 0 ; i < n ; i++){
242             nodes ~= getDescendants(t.getChild(i));
243         }
244         return nodes;
245     }
246 
247     public static ParseTree[] descendants(ParseTree t)
248     {
249         return getDescendants(t);
250     }
251 
252     /**
253      * @uml
254      * Find smallest subtree of t enclosing range startTokenIndex..stopTokenIndex
255      * inclusively using postorder traversal.  Recursive depth-first-search.
256      */
257     public static ParserRuleContext getRootOfSubtreeEnclosingRegion(ParseTree t, int startTokenIndex,
258         int stopTokenIndex)
259     {
260 	int n = t.getChildCount();
261         for (int i = 0; i<n; i++) {
262             ParseTree child = t.getChild(i);
263             ParserRuleContext r = getRootOfSubtreeEnclosingRegion(child, startTokenIndex, stopTokenIndex);
264             if (r !is null) return r;
265         }
266         if (t.classinfo == ParserRuleContext.classinfo) {
267             ParserRuleContext r = cast(ParserRuleContext)t;
268             if (startTokenIndex >= r.getStart().getTokenIndex() && // is range fully contained in t?
269                 (r.getStop() is null || stopTokenIndex <= r.getStop().getTokenIndex()) )
270                 {
271                     // note: r.getStop()==null likely implies that we bailed out of parser and there's nothing to the right
272                     return r;
273                 }
274         }
275         return null;
276     }
277 
278     /**
279      * @uml
280      * Replace any subtree siblings of root that are completely to left
281      * or right of lookahead range with a CommonToken(Token.INVALID_TYPE,"...")
282      * node. The source interval for t is not altered to suit smaller range!
283      *
284      * WARNING: destructive to t.
285      */
286     public static void stripChildrenOutOfRange(ParserRuleContext t, ParserRuleContext root,
287         int startIndex, int stopIndex)
288     {
289         if (t is null) return;
290         for (int i = 0; i < t.getChildCount(); i++) {
291             ParseTree child = t.getChild(i);
292             Interval range = child.getSourceInterval();
293             if (child.classinfo == ParserRuleContext.classinfo && (range.b < startIndex || range.a > stopIndex) ) {
294                 if (isAncestorOf(child, root)) { // replace only if subtree doesn't have displayed root
295                     CommonToken abbrev = new CommonToken(TokenConstantDefinition.INVALID_TYPE, "...");
296                     t.children[i] =  new TerminalNodeImpl(abbrev);
297                 }
298             }
299         }
300     }
301 
302     /**
303      * @uml
304      * Return first node satisfying the pred
305      */
306     public static Tree findNodeSuchThat(Tree t, Predicate!Tree pred)
307     {
308         if (pred.test(t) ) return t;
309 
310         int n = t.getChildCount();
311         for (int i = 0 ; i < n ; i++){
312             Tree u = findNodeSuchThat(t.getChild(i), pred);
313             if (u !is null ) return u;
314         }
315         return null;
316     }
317 
318 }