package org.python.antlr; import org.antlr.runtime.CommonToken; import org.antlr.runtime.Token; import org.antlr.runtime.tree.CommonTree; import org.python.core.PyType; import org.python.antlr.ast.Name; import org.python.antlr.ast.VisitorIF; import java.util.ArrayList; import java.util.List; public class PythonTree extends AST { public boolean from_future_checked = false; private int charStartIndex = -1; private int charStopIndex = -1; private CommonTree node; /** Who is the parent node of this node; if null, implies node is root */ private PythonTree parent; public PythonTree() { node = new CommonTree(); } public PythonTree(PyType subType) { super(subType); node = new CommonTree(); } public PythonTree(Token t) { node = new CommonTree(t); } public PythonTree(int ttype, Token t) { CommonToken c = new CommonToken(ttype, t.getText()); c.setLine(t.getLine()); c.setTokenIndex(t.getTokenIndex()); c.setCharPositionInLine(t.getCharPositionInLine()); c.setChannel(t.getChannel()); c.setStartIndex(((CommonToken)t).getStartIndex()); c.setStopIndex(((CommonToken)t).getStopIndex()); node = new CommonTree(c); } public PythonTree(PythonTree tree) { node = new CommonTree(tree.getNode()); charStartIndex = tree.getCharStartIndex(); charStopIndex = tree.getCharStopIndex(); } public CommonTree getNode() { return node; } public Token getToken() { return node.getToken(); } public PythonTree dupNode() { return new PythonTree(this); } public boolean isNil() { return node.isNil(); } public int getAntlrType() { return node.getType(); } public String getText() { return node.getText(); } public int getLine() { if (node.getToken()==null || node.getToken().getLine()==0) { if ( getChildCount()>0 ) { return getChild(0).getLine(); } return 1; } return node.getToken().getLine(); } public int getCharPositionInLine() { Token token = node.getToken(); if (token==null || token.getCharPositionInLine()==-1) { if (getChildCount()>0) { return getChild(0).getCharPositionInLine(); } return 0; } else if (token != null && token.getCharPositionInLine() == -2) { //XXX: yucky fix because CPython's ast uses -1 as a real value // for char pos in certain circumstances (for example, the // char pos of multi-line strings. I would just use -1, // but ANTLR is using -1 in special ways also. return -1; } return token.getCharPositionInLine(); } public int getTokenStartIndex() { return node.getTokenStartIndex(); } public void setTokenStartIndex(int index) { node.setTokenStartIndex(index); } public int getTokenStopIndex() { return node.getTokenStopIndex(); } public void setTokenStopIndex(int index) { node.setTokenStopIndex(index); } public int getCharStartIndex() { if (charStartIndex == -1 && node.getToken() != null) { return ((CommonToken)node.getToken()).getStartIndex(); } return charStartIndex ; } public void setCharStartIndex(int index) { charStartIndex = index; } /* * Adding one to stopIndex from Tokens. ANTLR defines the char position as * being the array index of the actual characters. Most tools these days * define document offsets as the positions between the characters. If you * imagine drawing little boxes around each character and think of the * numbers as pointing to either the left or right side of a character's * box, then 0 is before the first character - and in a Document of 10 * characters, position 10 is after the last character. */ public int getCharStopIndex() { if (charStopIndex == -1 && node.getToken() != null) { return ((CommonToken)node.getToken()).getStopIndex() + 1; } return charStopIndex; } public void setCharStopIndex(int index) { charStopIndex = index; } public int getChildIndex() { return node.getChildIndex(); } public PythonTree getParent() { return parent; } public void setParent(PythonTree t) { this.parent = t; } public void setChildIndex(int index) { node.setChildIndex(index); } /** * Converts a list of Name to a dotted-name string. * Because leading dots are indexable identifiers (referring * to parent directories in relative imports), a Name list * may include leading dots, but not dots between names. */ public static String dottedNameListToString(List<Name> names) { if (names == null) { return ""; } StringBuilder sb = new StringBuilder(); boolean leadingDot = true; for (int i = 0, len = names.size(); i < len; i++) { Name name = names.get(i); String id = name.getInternalId(); if (id == null) { continue; } if (!".".equals(id)) { leadingDot = false; } sb.append(id); if (i < len - 1 && !leadingDot) { sb.append("."); } } return sb.toString(); } @Override public String toString() { if (isNil()) { return "None"; } if ( getAntlrType()==Token.INVALID_TOKEN_TYPE ) { return "<errornode>"; } if ( node.getToken()==null ) { return null; } return node.getToken().getText() + "(" + this.getLine() + "," + this.getCharPositionInLine() + ")"; } public String toStringTree() { if (children == null || children.size() == 0) { return this.toString();// + "[" + this.info() + "]"; } StringBuffer buf = new StringBuffer(); if (!isNil()) { buf.append("("); buf.append(this.toString());// + "[" + this.info() + "]"); buf.append(' '); } for (int i = 0; children != null && i < children.size(); i++) { PythonTree t = children.get(i); if (i > 0) { buf.append(' '); } buf.append(t.toStringTree()); } if (!isNil()) { buf.append(")"); } return buf.toString(); } protected String dumpThis(String s) { return s; } protected String dumpThis(Object o) { if (o instanceof PythonTree) { return ((PythonTree)o).toStringTree(); } return String.valueOf(o); } protected String dumpThis(Object[] s) { StringBuffer sb = new StringBuffer(); if (s == null) { sb.append("null"); } else { sb.append("("); for (int i = 0; i < s.length; i++) { if (i > 0) sb.append(", "); sb.append(dumpThis(s[i])); } sb.append(")"); } return sb.toString(); } public <R> R accept(VisitorIF<R> visitor) throws Exception { throw new RuntimeException("Unexpected node: " + this); } public void traverse(VisitorIF<?> visitor) throws Exception { throw new RuntimeException("Cannot traverse node: " + this); } //XXX: From here down copied from org.antlr.runtime.tree.BaseTree protected List<PythonTree> children; public PythonTree getChild(int i) { if ( children==null || i>=children.size() ) { return null; } return children.get(i); } /** Get the children internal List; note that if you directly mess with * the list, do so at your own risk. */ public List<PythonTree> getChildren() { return children; } public PythonTree getFirstChildWithType(int type) { for (int i = 0; children!=null && i < children.size(); i++) { PythonTree t = children.get(i); if ( t.getAntlrType()==type ) { return t; } } return null; } public int getChildCount() { if ( children==null ) { return 0; } return children.size(); } /** Add t as child of this node. * * Warning: if t has no children, but child does * and child isNil then this routine moves children to t via * t.children = child.children; i.e., without copying the array. */ public void addChild(PythonTree t) { if ( t==null ) { return; // do nothing upon addChild(null) } PythonTree childTree = t; if ( childTree.isNil() ) { // t is an empty node possibly with children if ( this.children!=null && this.children == childTree.children ) { throw new RuntimeException("attempt to add child list to itself"); } // just add all of childTree's children to this if ( childTree.children!=null ) { if ( this.children!=null ) { // must copy, this has children already int n = childTree.children.size(); for (int i = 0; i < n; i++) { PythonTree c = childTree.children.get(i); this.children.add(c); // handle double-link stuff for each child of nil root c.setParent(this); c.setChildIndex(children.size()-1); } } else { // no children for this but t has children; just set pointer // call general freshener routine this.children = childTree.children; this.freshenParentAndChildIndexes(); } } } else { // child is not nil (don't care about children) if ( children==null ) { children = createChildrenList(); // create children list on demand } children.add(t); childTree.setParent(this); childTree.setChildIndex(children.size()-1); } } /** Add all elements of kids list as children of this node */ public void addChildren(List<PythonTree> kids) { for (int i = 0; i < kids.size(); i++) { PythonTree t = kids.get(i); addChild(t); } } public void setChild(int i, PythonTree t) { if ( t==null ) { return; } if ( t.isNil() ) { throw new IllegalArgumentException("Can't set single child to a list"); } if ( children==null ) { children = createChildrenList(); } children.set(i, t); t.setParent(this); t.setChildIndex(i); } public Object deleteChild(int i) { if ( children==null ) { return null; } PythonTree killed = children.remove(i); // walk rest and decrement their child indexes this.freshenParentAndChildIndexes(i); return killed; } /** Delete children from start to stop and replace with t even if t is * a list (nil-root tree). num of children can increase or decrease. * For huge child lists, inserting children can force walking rest of * children to set their childindex; could be slow. */ public void replaceChildren(int startChildIndex, int stopChildIndex, Object t) { if ( children==null ) { throw new IllegalArgumentException("indexes invalid; no children in list"); } int replacingHowMany = stopChildIndex - startChildIndex + 1; int replacingWithHowMany; PythonTree newTree = (PythonTree)t; List<PythonTree> newChildren = null; // normalize to a list of children to add: newChildren if ( newTree.isNil() ) { newChildren = newTree.children; } else { newChildren = new ArrayList<PythonTree>(1); newChildren.add(newTree); } replacingWithHowMany = newChildren.size(); int numNewChildren = newChildren.size(); int delta = replacingHowMany - replacingWithHowMany; // if same number of nodes, do direct replace if ( delta == 0 ) { int j = 0; // index into new children for (int i=startChildIndex; i<=stopChildIndex; i++) { PythonTree child = newChildren.get(j); children.set(i, child); child.setParent(this); child.setChildIndex(i); j++; } } else if ( delta > 0 ) { // fewer new nodes than there were // set children and then delete extra for (int j=0; j<numNewChildren; j++) { children.set(startChildIndex+j, newChildren.get(j)); } int indexToDelete = startChildIndex+numNewChildren; for (int c=indexToDelete; c<=stopChildIndex; c++) { // delete same index, shifting everybody down each time PythonTree killed = children.remove(indexToDelete); } freshenParentAndChildIndexes(startChildIndex); } else { // more new nodes than were there before // fill in as many children as we can (replacingHowMany) w/o moving data for (int j=0; j<replacingHowMany; j++) { children.set(startChildIndex+j, newChildren.get(j)); } int numToInsert = replacingWithHowMany-replacingHowMany; for (int j=replacingHowMany; j<replacingWithHowMany; j++) { children.add(startChildIndex+j, newChildren.get(j)); } freshenParentAndChildIndexes(startChildIndex); } } /** Override in a subclass to change the impl of children list */ protected List<PythonTree> createChildrenList() { return new ArrayList<PythonTree>(); } /** Set the parent and child index values for all child of t */ public void freshenParentAndChildIndexes() { freshenParentAndChildIndexes(0); } public void freshenParentAndChildIndexes(int offset) { int n = getChildCount(); for (int c = offset; c < n; c++) { PythonTree child = getChild(c); child.setChildIndex(c); child.setParent(this); } } }