You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
495 lines
15 KiB
495 lines
15 KiB
package com.fr.third.antlr; |
|
|
|
/* ANTLR Translator Generator |
|
* Project led by Terence Parr at http://www.cs.usfca.edu |
|
* Software rights: http://www.antlr.org/license.html |
|
* |
|
* $Id: //depot/code/org.antlr/release/antlr-2.7.7/antlr/BaseAST.java#2 $ |
|
*/ |
|
|
|
|
|
import java.io.Serializable; |
|
import java.io.IOException; |
|
import java.io.Writer; |
|
|
|
import com.fr.third.antlr.collections.AST; |
|
import com.fr.third.antlr.collections.ASTEnumeration; |
|
import com.fr.third.antlr.collections.impl.ASTEnumerator; |
|
import com.fr.third.antlr.collections.impl.Vector; |
|
|
|
/** |
|
* A Child-Sibling Tree. |
|
* |
|
* A tree with PLUS at the root and with two children 3 and 4 is |
|
* structured as: |
|
* |
|
* PLUS |
|
* | |
|
* 3 -- 4 |
|
* |
|
* and can be specified easily in LISP notation as |
|
* |
|
* (PLUS 3 4) |
|
* |
|
* where every '(' starts a new subtree. |
|
* |
|
* These trees are particular useful for translators because of |
|
* the flexibility of the children lists. They are also very easy |
|
* to walk automatically, whereas trees with specific children |
|
* reference fields can't easily be walked automatically. |
|
* |
|
* This class contains the basic support for an AST. |
|
* Most people will create ASTs that are subclasses of |
|
* BaseAST or of CommonAST. |
|
*/ |
|
public abstract class BaseAST implements AST, Serializable { |
|
protected BaseAST down; |
|
protected BaseAST right; |
|
|
|
private static boolean verboseStringConversion = false; |
|
private static String[] tokenNames = null; |
|
|
|
/**Add a node to the end of the child list for this node */ |
|
public void addChild(AST node) { |
|
if (node == null) return; |
|
BaseAST t = this.down; |
|
if (t != null) { |
|
while (t.right != null) { |
|
t = t.right; |
|
} |
|
t.right = (BaseAST)node; |
|
} |
|
else { |
|
this.down = (BaseAST)node; |
|
} |
|
} |
|
|
|
/** How many children does this node have? */ |
|
public int getNumberOfChildren() { |
|
BaseAST t = this.down; |
|
int n = 0; |
|
if (t != null) { |
|
n = 1; |
|
while (t.right != null) { |
|
t = t.right; |
|
n++; |
|
} |
|
return n; |
|
} |
|
return n; |
|
} |
|
|
|
private static void doWorkForFindAll(AST nodeToSearch, |
|
Vector v, |
|
AST target, |
|
boolean partialMatch) |
|
{ |
|
// Start walking sibling lists, looking for matches. |
|
for (AST sibling = nodeToSearch; sibling != null; sibling = sibling |
|
.getNextSibling()) { |
|
if ((partialMatch && sibling.equalsTreePartial(target)) |
|
|| (!partialMatch && sibling.equalsTree(target))) { |
|
v.appendElement(sibling); |
|
} |
|
// regardless of match or not, check any children for matches |
|
if (sibling.getFirstChild() != null) { |
|
doWorkForFindAll(sibling.getFirstChild(), v, target, partialMatch); |
|
} |
|
} |
|
} |
|
|
|
/** Is node t equal to this in terms of token type and text? */ |
|
public boolean equals(AST t) { |
|
if (t == null) return false; |
|
if ( (this.getText()==null && t.getText()!=null) || |
|
(this.getText()!=null && t.getText()==null) ) |
|
{ |
|
return false; |
|
} |
|
if ( this.getText()==null && t.getText()==null ) { |
|
return this.getType() == t.getType(); |
|
} |
|
return this.getText().equals(t.getText()) && |
|
this.getType() == t.getType(); |
|
} |
|
|
|
/** Is t an exact structural and equals() match of this tree. The |
|
* 'this' reference is considered the start of a sibling list. |
|
*/ |
|
public boolean equalsList(AST t) { |
|
AST sibling; |
|
|
|
// the empty tree is not a match of any non-null tree. |
|
if (t == null) { |
|
return false; |
|
} |
|
|
|
// Otherwise, start walking sibling lists. First mismatch, return false. |
|
for (sibling = this; |
|
sibling != null && t != null; |
|
sibling = sibling.getNextSibling(), t = t.getNextSibling()) |
|
{ |
|
// as a quick optimization, check roots first. |
|
if (!sibling.equals(t)) { |
|
return false; |
|
} |
|
// if roots match, do full list match test on children. |
|
if (sibling.getFirstChild() != null) { |
|
if (!sibling.getFirstChild().equalsList(t.getFirstChild())) { |
|
return false; |
|
} |
|
} |
|
// sibling has no kids, make sure t doesn't either |
|
else if (t.getFirstChild() != null) { |
|
return false; |
|
} |
|
} |
|
if (sibling == null && t == null) { |
|
return true; |
|
} |
|
// one sibling list has more than the other |
|
return false; |
|
} |
|
|
|
/** Is 'sub' a subtree of this list? |
|
* The siblings of the root are NOT ignored. |
|
*/ |
|
public boolean equalsListPartial(AST sub) { |
|
AST sibling; |
|
|
|
// the empty tree is always a subset of any tree. |
|
if (sub == null) { |
|
return true; |
|
} |
|
|
|
// Otherwise, start walking sibling lists. First mismatch, return false. |
|
for (sibling = this; |
|
sibling != null && sub != null; |
|
sibling = sibling.getNextSibling(), sub = sub.getNextSibling()) { |
|
// as a quick optimization, check roots first. |
|
if (!sibling.equals(sub)) return false; |
|
// if roots match, do partial list match test on children. |
|
if (sibling.getFirstChild() != null) { |
|
if (!sibling.getFirstChild().equalsListPartial(sub.getFirstChild())) return false; |
|
} |
|
} |
|
if (sibling == null && sub != null) { |
|
// nothing left to match in this tree, but subtree has more |
|
return false; |
|
} |
|
// either both are null or sibling has more, but subtree doesn't |
|
return true; |
|
} |
|
|
|
/** Is tree rooted at 'this' equal to 't'? The siblings |
|
* of 'this' are ignored. |
|
*/ |
|
public boolean equalsTree(AST t) { |
|
// check roots first. |
|
if (!this.equals(t)) return false; |
|
// if roots match, do full list match test on children. |
|
if (this.getFirstChild() != null) { |
|
if (!this.getFirstChild().equalsList(t.getFirstChild())) return false; |
|
} |
|
// sibling has no kids, make sure t doesn't either |
|
else if (t.getFirstChild() != null) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
/** Is 't' a subtree of the tree rooted at 'this'? The siblings |
|
* of 'this' are ignored. |
|
*/ |
|
public boolean equalsTreePartial(AST sub) { |
|
// the empty tree is always a subset of any tree. |
|
if (sub == null) { |
|
return true; |
|
} |
|
|
|
// check roots first. |
|
if (!this.equals(sub)) return false; |
|
// if roots match, do full list partial match test on children. |
|
if (this.getFirstChild() != null) { |
|
if (!this.getFirstChild().equalsListPartial(sub.getFirstChild())) return false; |
|
} |
|
return true; |
|
} |
|
|
|
/** Walk the tree looking for all exact subtree matches. Return |
|
* an ASTEnumerator that lets the caller walk the list |
|
* of subtree roots found herein. |
|
*/ |
|
public ASTEnumeration findAll(AST target) { |
|
Vector roots = new Vector(10); |
|
AST sibling; |
|
|
|
// the empty tree cannot result in an enumeration |
|
if (target == null) { |
|
return null; |
|
} |
|
|
|
doWorkForFindAll(this, roots, target, false); // find all matches recursively |
|
|
|
return new ASTEnumerator(roots); |
|
} |
|
|
|
/** Walk the tree looking for all subtrees. Return |
|
* an ASTEnumerator that lets the caller walk the list |
|
* of subtree roots found herein. |
|
*/ |
|
public ASTEnumeration findAllPartial(AST sub) { |
|
Vector roots = new Vector(10); |
|
AST sibling; |
|
|
|
// the empty tree cannot result in an enumeration |
|
if (sub == null) { |
|
return null; |
|
} |
|
|
|
doWorkForFindAll(this, roots, sub, true); // find all matches recursively |
|
|
|
return new ASTEnumerator(roots); |
|
} |
|
|
|
/** Get the first child of this node; null if not children */ |
|
public AST getFirstChild() { |
|
return down; |
|
} |
|
|
|
/** Get the next sibling in line after this one */ |
|
public AST getNextSibling() { |
|
return right; |
|
} |
|
|
|
/** Get the token text for this node */ |
|
public String getText() { |
|
return ""; |
|
} |
|
|
|
/** Get the token type for this node */ |
|
public int getType() { |
|
return 0; |
|
} |
|
|
|
public int getLine() { |
|
return 0; |
|
} |
|
|
|
public int getColumn() { |
|
return 0; |
|
} |
|
|
|
public abstract void initialize(int t, String txt); |
|
|
|
public abstract void initialize(AST t); |
|
|
|
public abstract void initialize(Token t); |
|
|
|
/** Remove all children */ |
|
public void removeChildren() { |
|
down = null; |
|
} |
|
|
|
public void setFirstChild(AST c) { |
|
down = (BaseAST)c; |
|
} |
|
|
|
public void setNextSibling(AST n) { |
|
right = (BaseAST)n; |
|
} |
|
|
|
/** Set the token text for this node */ |
|
public void setText(String text) { |
|
} |
|
|
|
/** Set the token type for this node */ |
|
public void setType(int ttype) { |
|
} |
|
|
|
public static void setVerboseStringConversion(boolean verbose, String[] names) { |
|
verboseStringConversion = verbose; |
|
tokenNames = names; |
|
} |
|
|
|
/** Return an array of strings that maps token ID to it's text. @since 2.7.3 */ |
|
public static String[] getTokenNames() { |
|
return tokenNames; |
|
} |
|
|
|
public String toString() { |
|
StringBuffer b = new StringBuffer(); |
|
// if verbose and type name not same as text (keyword probably) |
|
if (verboseStringConversion && |
|
getText() != null && |
|
!getText().equalsIgnoreCase(tokenNames[getType()]) && |
|
!getText().equalsIgnoreCase(StringUtils.stripFrontBack(tokenNames[getType()], "\"", "\""))) { |
|
b.append('['); |
|
b.append(getText()); |
|
b.append(",<"); |
|
b.append(tokenNames[getType()]); |
|
b.append(">]"); |
|
return b.toString(); |
|
} |
|
return getText(); |
|
} |
|
|
|
/** Print out a child-sibling tree in LISP notation */ |
|
public String toStringList() { |
|
AST t = this; |
|
String ts = ""; |
|
if (t.getFirstChild() != null) ts += " ("; |
|
ts += " " + this.toString(); |
|
if (t.getFirstChild() != null) { |
|
ts += ((BaseAST)t.getFirstChild()).toStringList(); |
|
} |
|
if (t.getFirstChild() != null) ts += " )"; |
|
if (t.getNextSibling() != null) { |
|
ts += ((BaseAST)t.getNextSibling()).toStringList(); |
|
} |
|
return ts; |
|
} |
|
|
|
public String toStringTree() { |
|
AST t = this; |
|
String ts = ""; |
|
if (t.getFirstChild() != null) ts += " ("; |
|
ts += " " + this.toString(); |
|
if (t.getFirstChild() != null) { |
|
ts += ((BaseAST)t.getFirstChild()).toStringList(); |
|
} |
|
if (t.getFirstChild() != null) ts += " )"; |
|
return ts; |
|
} |
|
|
|
public static String decode(String text) { |
|
char c, c1, c2, c3, c4, c5; |
|
StringBuffer n = new StringBuffer(); |
|
for (int i = 0; i < text.length(); i++) { |
|
c = text.charAt(i); |
|
if (c == '&') { |
|
c1 = text.charAt(i + 1); |
|
c2 = text.charAt(i + 2); |
|
c3 = text.charAt(i + 3); |
|
c4 = text.charAt(i + 4); |
|
c5 = text.charAt(i + 5); |
|
|
|
if (c1 == 'a' && c2 == 'm' && c3 == 'p' && c4 == ';') { |
|
n.append('&'); |
|
i += 5; |
|
} |
|
else if (c1 == 'l' && c2 == 't' && c3 == ';') { |
|
n.append('<'); |
|
i += 4; |
|
} |
|
else if (c1 == 'g' && c2 == 't' && c3 == ';') { |
|
n.append('>'); |
|
i += 4; |
|
} |
|
else if (c1 == 'q' && c2 == 'u' && c3 == 'o' && |
|
c4 == 't' && c5 == ';') { |
|
n.append('"'); |
|
i += 6; |
|
} |
|
else if (c1 == 'a' && c2 == 'p' && c3 == 'o' && |
|
c4 == 's' && c5 == ';') { |
|
n.append('\''); |
|
i += 6; |
|
} |
|
else |
|
n.append('&'); |
|
} |
|
else |
|
n.append(c); |
|
} |
|
return new String(n); |
|
} |
|
|
|
public static String encode(String text) { |
|
char c; |
|
StringBuffer n = new StringBuffer(); |
|
for (int i = 0; i < text.length(); i++) { |
|
c = text.charAt(i); |
|
switch (c) { |
|
case '&': |
|
{ |
|
n.append("&"); |
|
break; |
|
} |
|
case '<': |
|
{ |
|
n.append("<"); |
|
break; |
|
} |
|
case '>': |
|
{ |
|
n.append(">"); |
|
break; |
|
} |
|
case '"': |
|
{ |
|
n.append("""); |
|
break; |
|
} |
|
case '\'': |
|
{ |
|
n.append("'"); |
|
break; |
|
} |
|
default : |
|
{ |
|
n.append(c); |
|
break; |
|
} |
|
} |
|
} |
|
return new String(n); |
|
} |
|
|
|
public void xmlSerializeNode(Writer out) |
|
throws IOException { |
|
StringBuffer buf = new StringBuffer(100); |
|
buf.append('<'); |
|
buf.append(getClass().getName() + " "); |
|
buf.append("text=\"" + encode(getText()) + "\" type=\"" + |
|
getType() + "\"/>"); |
|
out.write(buf.toString()); |
|
} |
|
|
|
public void xmlSerializeRootOpen(Writer out) |
|
throws IOException { |
|
StringBuffer buf = new StringBuffer(100); |
|
buf.append('<'); |
|
buf.append(getClass().getName() + " "); |
|
buf.append("text=\"" + encode(getText()) + "\" type=\"" + |
|
getType() + "\">\n"); |
|
out.write(buf.toString()); |
|
} |
|
|
|
public void xmlSerializeRootClose(Writer out) |
|
throws IOException { |
|
out.write("</" + getClass().getName() + ">\n"); |
|
} |
|
|
|
public void xmlSerialize(Writer out) throws IOException { |
|
// print out this node and all siblings |
|
for (AST node = this; |
|
node != null; |
|
node = node.getNextSibling()) { |
|
if (node.getFirstChild() == null) { |
|
// print guts (class name, attributes) |
|
((BaseAST)node).xmlSerializeNode(out); |
|
} |
|
else { |
|
((BaseAST)node).xmlSerializeRootOpen(out); |
|
|
|
// print children |
|
((BaseAST)node.getFirstChild()).xmlSerialize(out); |
|
|
|
// print end tag |
|
((BaseAST)node).xmlSerializeRootClose(out); |
|
} |
|
} |
|
} |
|
|
|
}
|
|
|