1 /* 2 [The "BSD license"] 3 Copyright (c) 2005-2009 Terence Parr 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 1. Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 3. The name of the author may not be used to endorse or promote products 15 derived from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 package org.antlr.runtime.tree; 29 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** A generic tree implementation with no payload. You must subclass to 34 * actually have any user data. ANTLR v3 uses a list of children approach 35 * instead of the child-sibling approach in v2. A flat tree (a list) is 36 * an empty node whose children represent the list. An empty, but 37 * non-null node is called "nil". 38 */ 39 public abstract class BaseTree implements Tree { 40 protected List children; 41 BaseTree()42 public BaseTree() { 43 } 44 45 /** Create a new node from an existing node does nothing for BaseTree 46 * as there are no fields other than the children list, which cannot 47 * be copied as the children are not considered part of this node. 48 */ BaseTree(Tree node)49 public BaseTree(Tree node) { 50 } 51 getChild(int i)52 public Tree getChild(int i) { 53 if ( children==null || i>=children.size() ) { 54 return null; 55 } 56 return (Tree)children.get(i); 57 } 58 59 /** Get the children internal List; note that if you directly mess with 60 * the list, do so at your own risk. 61 */ getChildren()62 public List getChildren() { 63 return children; 64 } 65 getFirstChildWithType(int type)66 public Tree getFirstChildWithType(int type) { 67 for (int i = 0; children!=null && i < children.size(); i++) { 68 Tree t = (Tree) children.get(i); 69 if ( t.getType()==type ) { 70 return t; 71 } 72 } 73 return null; 74 } 75 getChildCount()76 public int getChildCount() { 77 if ( children==null ) { 78 return 0; 79 } 80 return children.size(); 81 } 82 83 /** Add t as child of this node. 84 * 85 * Warning: if t has no children, but child does 86 * and child isNil then this routine moves children to t via 87 * t.children = child.children; i.e., without copying the array. 88 */ addChild(Tree t)89 public void addChild(Tree t) { 90 //System.out.println("add child "+t.toStringTree()+" "+this.toStringTree()); 91 //System.out.println("existing children: "+children); 92 if ( t==null ) { 93 return; // do nothing upon addChild(null) 94 } 95 BaseTree childTree = (BaseTree)t; 96 if ( childTree.isNil() ) { // t is an empty node possibly with children 97 if ( this.children!=null && this.children == childTree.children ) { 98 throw new RuntimeException("attempt to add child list to itself"); 99 } 100 // just add all of childTree's children to this 101 if ( childTree.children!=null ) { 102 if ( this.children!=null ) { // must copy, this has children already 103 int n = childTree.children.size(); 104 for (int i = 0; i < n; i++) { 105 Tree c = (Tree)childTree.children.get(i); 106 this.children.add(c); 107 // handle double-link stuff for each child of nil root 108 c.setParent(this); 109 c.setChildIndex(children.size()-1); 110 } 111 } 112 else { 113 // no children for this but t has children; just set pointer 114 // call general freshener routine 115 this.children = childTree.children; 116 this.freshenParentAndChildIndexes(); 117 } 118 } 119 } 120 else { // child is not nil (don't care about children) 121 if ( children==null ) { 122 children = createChildrenList(); // create children list on demand 123 } 124 children.add(t); 125 childTree.setParent(this); 126 childTree.setChildIndex(children.size()-1); 127 } 128 // System.out.println("now children are: "+children); 129 } 130 131 /** Add all elements of kids list as children of this node */ addChildren(List kids)132 public void addChildren(List kids) { 133 for (int i = 0; i < kids.size(); i++) { 134 Tree t = (Tree) kids.get(i); 135 addChild(t); 136 } 137 } 138 setChild(int i, Tree t)139 public void setChild(int i, Tree t) { 140 if ( t==null ) { 141 return; 142 } 143 if ( t.isNil() ) { 144 throw new IllegalArgumentException("Can't set single child to a list"); 145 } 146 if ( children==null ) { 147 children = createChildrenList(); 148 } 149 children.set(i, t); 150 t.setParent(this); 151 t.setChildIndex(i); 152 } 153 154 /** Insert child t at child position i (0..n-1) by shifting children 155 i+1..n-1 to the right one position. Set parent / indexes properly 156 but does NOT collapse nil-rooted t's that come in here like addChild. 157 */ insertChild(int i, Object t)158 public void insertChild(int i, Object t) { 159 if ( children==null ) return; 160 children.add(i, t); 161 // walk others to increment their child indexes 162 // set index, parent of this one too 163 this.freshenParentAndChildIndexes(i); 164 } 165 deleteChild(int i)166 public Object deleteChild(int i) { 167 if ( children==null ) { 168 return null; 169 } 170 Tree killed = (Tree)children.remove(i); 171 // walk rest and decrement their child indexes 172 this.freshenParentAndChildIndexes(i); 173 return killed; 174 } 175 176 /** Delete children from start to stop and replace with t even if t is 177 * a list (nil-root tree). num of children can increase or decrease. 178 * For huge child lists, inserting children can force walking rest of 179 * children to set their childindex; could be slow. 180 */ replaceChildren(int startChildIndex, int stopChildIndex, Object t)181 public void replaceChildren(int startChildIndex, int stopChildIndex, Object t) { 182 /* 183 System.out.println("replaceChildren "+startChildIndex+", "+stopChildIndex+ 184 " with "+((BaseTree)t).toStringTree()); 185 System.out.println("in="+toStringTree()); 186 */ 187 if ( children==null ) { 188 throw new IllegalArgumentException("indexes invalid; no children in list"); 189 } 190 int replacingHowMany = stopChildIndex - startChildIndex + 1; 191 int replacingWithHowMany; 192 BaseTree newTree = (BaseTree)t; 193 List newChildren = null; 194 // normalize to a list of children to add: newChildren 195 if ( newTree.isNil() ) { 196 newChildren = newTree.children; 197 } 198 else { 199 newChildren = new ArrayList(1); 200 newChildren.add(newTree); 201 } 202 replacingWithHowMany = newChildren.size(); 203 int numNewChildren = newChildren.size(); 204 int delta = replacingHowMany - replacingWithHowMany; 205 // if same number of nodes, do direct replace 206 if ( delta == 0 ) { 207 int j = 0; // index into new children 208 for (int i=startChildIndex; i<=stopChildIndex; i++) { 209 BaseTree child = (BaseTree)newChildren.get(j); 210 children.set(i, child); 211 child.setParent(this); 212 child.setChildIndex(i); 213 j++; 214 } 215 } 216 else if ( delta > 0 ) { // fewer new nodes than there were 217 // set children and then delete extra 218 for (int j=0; j<numNewChildren; j++) { 219 children.set(startChildIndex+j, newChildren.get(j)); 220 } 221 int indexToDelete = startChildIndex+numNewChildren; 222 for (int c=indexToDelete; c<=stopChildIndex; c++) { 223 // delete same index, shifting everybody down each time 224 children.remove(indexToDelete); 225 } 226 freshenParentAndChildIndexes(startChildIndex); 227 } 228 else { // more new nodes than were there before 229 // fill in as many children as we can (replacingHowMany) w/o moving data 230 for (int j=0; j<replacingHowMany; j++) { 231 children.set(startChildIndex+j, newChildren.get(j)); 232 } 233 int numToInsert = replacingWithHowMany-replacingHowMany; 234 for (int j=replacingHowMany; j<replacingWithHowMany; j++) { 235 children.add(startChildIndex+j, newChildren.get(j)); 236 } 237 freshenParentAndChildIndexes(startChildIndex); 238 } 239 //System.out.println("out="+toStringTree()); 240 } 241 242 /** Override in a subclass to change the impl of children list */ createChildrenList()243 protected List createChildrenList() { 244 return new ArrayList(); 245 } 246 isNil()247 public boolean isNil() { 248 return false; 249 } 250 251 /** Set the parent and child index values for all child of t */ freshenParentAndChildIndexes()252 public void freshenParentAndChildIndexes() { 253 freshenParentAndChildIndexes(0); 254 } 255 freshenParentAndChildIndexes(int offset)256 public void freshenParentAndChildIndexes(int offset) { 257 int n = getChildCount(); 258 for (int c = offset; c < n; c++) { 259 Tree child = (Tree)getChild(c); 260 child.setChildIndex(c); 261 child.setParent(this); 262 } 263 } 264 freshenParentAndChildIndexesDeeply()265 public void freshenParentAndChildIndexesDeeply() { 266 freshenParentAndChildIndexesDeeply(0); 267 } 268 freshenParentAndChildIndexesDeeply(int offset)269 public void freshenParentAndChildIndexesDeeply(int offset) { 270 int n = getChildCount(); 271 for (int c = offset; c < n; c++) { 272 BaseTree child = (BaseTree)getChild(c); 273 child.setChildIndex(c); 274 child.setParent(this); 275 child.freshenParentAndChildIndexesDeeply(); 276 } 277 } 278 sanityCheckParentAndChildIndexes()279 public void sanityCheckParentAndChildIndexes() { 280 sanityCheckParentAndChildIndexes(null, -1); 281 } 282 sanityCheckParentAndChildIndexes(Tree parent, int i)283 public void sanityCheckParentAndChildIndexes(Tree parent, int i) { 284 if ( parent!=this.getParent() ) { 285 throw new IllegalStateException("parents don't match; expected "+parent+" found "+this.getParent()); 286 } 287 if ( i!=this.getChildIndex() ) { 288 throw new IllegalStateException("child indexes don't match; expected "+i+" found "+this.getChildIndex()); 289 } 290 int n = this.getChildCount(); 291 for (int c = 0; c < n; c++) { 292 CommonTree child = (CommonTree)this.getChild(c); 293 child.sanityCheckParentAndChildIndexes(this, c); 294 } 295 } 296 297 /** BaseTree doesn't track child indexes. */ getChildIndex()298 public int getChildIndex() { 299 return 0; 300 } setChildIndex(int index)301 public void setChildIndex(int index) { 302 } 303 304 /** BaseTree doesn't track parent pointers. */ getParent()305 public Tree getParent() { 306 return null; 307 } 308 setParent(Tree t)309 public void setParent(Tree t) { 310 } 311 312 /** Walk upwards looking for ancestor with this token type. */ hasAncestor(int ttype)313 public boolean hasAncestor(int ttype) { return getAncestor(ttype)!=null; } 314 315 /** Walk upwards and get first ancestor with this token type. */ getAncestor(int ttype)316 public Tree getAncestor(int ttype) { 317 Tree t = this; 318 t = t.getParent(); 319 while ( t!=null ) { 320 if ( t.getType()==ttype ) return t; 321 t = t.getParent(); 322 } 323 return null; 324 } 325 326 /** Return a list of all ancestors of this node. The first node of 327 * list is the root and the last is the parent of this node. 328 */ getAncestors()329 public List getAncestors() { 330 if ( getParent()==null ) return null; 331 List ancestors = new ArrayList(); 332 Tree t = this; 333 t = t.getParent(); 334 while ( t!=null ) { 335 ancestors.add(0, t); // insert at start 336 t = t.getParent(); 337 } 338 return ancestors; 339 } 340 341 /** Print out a whole tree not just a node */ toStringTree()342 public String toStringTree() { 343 if ( children==null || children.size()==0 ) { 344 return this.toString(); 345 } 346 StringBuffer buf = new StringBuffer(); 347 if ( !isNil() ) { 348 buf.append("("); 349 buf.append(this.toString()); 350 buf.append(' '); 351 } 352 for (int i = 0; children!=null && i < children.size(); i++) { 353 Tree t = (Tree)children.get(i); 354 if ( i>0 ) { 355 buf.append(' '); 356 } 357 buf.append(t.toStringTree()); 358 } 359 if ( !isNil() ) { 360 buf.append(")"); 361 } 362 return buf.toString(); 363 } 364 getLine()365 public int getLine() { 366 return 0; 367 } 368 getCharPositionInLine()369 public int getCharPositionInLine() { 370 return 0; 371 } 372 373 /** Override to say how a node (not a tree) should look as text */ toString()374 public abstract String toString(); 375 } 376