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