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