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