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.
411 lines
12 KiB
411 lines
12 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/ASTFactory.java#2 $ |
|
*/ |
|
|
|
import java.lang.reflect.Constructor; |
|
import java.util.Hashtable; |
|
|
|
import com.fr.third.antlr.collections.AST; |
|
import com.fr.third.antlr.collections.impl.ASTArray; |
|
|
|
|
|
/** AST Support code shared by TreeParser and Parser. |
|
* We use delegation to share code (and have only one |
|
* bit of code to maintain) rather than subclassing |
|
* or superclassing (forces AST support code to be |
|
* loaded even when you don't want to do AST stuff). |
|
* |
|
* Typically, setASTNodeType is used to specify the |
|
* homogeneous type of node to create, but you can override |
|
* create to make heterogeneous nodes etc... |
|
*/ |
|
public class ASTFactory { |
|
/** Name of AST class to create during tree construction. |
|
* Null implies that the create method should create |
|
* a default AST type such as CommonAST. This is for |
|
* homogeneous nodes. |
|
*/ |
|
protected String theASTNodeType = null; |
|
protected Class theASTNodeTypeClass = null; |
|
|
|
/** How to specify the classname to create for a particular |
|
* token type. Note that ANTLR allows you to say, for example, |
|
* |
|
tokens { |
|
PLUS<AST=PLUSNode>; |
|
... |
|
} |
|
* |
|
* and it tracks everything statically. #[PLUS] will make you |
|
* a PLUSNode w/o use of this table. |
|
* |
|
* For tokens that ANTLR cannot track statically like #[i], |
|
* you can use this table to map PLUS (Integer) -> PLUSNode (Class) |
|
* etc... ANTLR sets the class map from the tokens {...} section |
|
* via the ASTFactory(Hashtable) ctor in antlr.Parser. |
|
*/ |
|
protected Hashtable tokenTypeToASTClassMap = null; |
|
|
|
public ASTFactory() { |
|
} |
|
|
|
/** Create factory with a specific mapping from token type |
|
* to Java AST node type. Your subclasses of ASTFactory |
|
* can override and reuse the map stuff. |
|
*/ |
|
public ASTFactory(Hashtable tokenTypeToClassMap) { |
|
setTokenTypeToASTClassMap(tokenTypeToClassMap); |
|
} |
|
|
|
/** Specify an "override" for the Java AST object created for a |
|
* specific token. It is provided as a convenience so |
|
* you can specify node types dynamically. ANTLR sets |
|
* the token type mapping automatically from the tokens{...} |
|
* section, but you can change that mapping with this method. |
|
* ANTLR does it's best to statically determine the node |
|
* type for generating parsers, but it cannot deal with |
|
* dynamic values like #[LT(1)]. In this case, it relies |
|
* on the mapping. Beware differences in the tokens{...} |
|
* section and what you set via this method. Make sure |
|
* they are the same. |
|
* |
|
* Set className to null to remove the mapping. |
|
* |
|
* @since 2.7.2 |
|
*/ |
|
public void setTokenTypeASTNodeType(int tokenType, String className) |
|
throws IllegalArgumentException |
|
{ |
|
if ( tokenTypeToASTClassMap==null ) { |
|
tokenTypeToASTClassMap = new Hashtable(); |
|
} |
|
if ( className==null ) { |
|
tokenTypeToASTClassMap.remove(new Integer(tokenType)); |
|
return; |
|
} |
|
Class c = null; |
|
try { |
|
c = Utils.loadClass(className); |
|
tokenTypeToASTClassMap.put(new Integer(tokenType), c); |
|
} |
|
catch (Exception e) { |
|
throw new IllegalArgumentException("Invalid class, "+className); |
|
} |
|
} |
|
|
|
/** For a given token type, what is the AST node object type to create |
|
* for it? |
|
* @since 2.7.2 |
|
*/ |
|
public Class getASTNodeType(int tokenType) { |
|
// try node specific class |
|
if ( tokenTypeToASTClassMap!=null ) { |
|
Class c = (Class)tokenTypeToASTClassMap.get(new Integer(tokenType)); |
|
if ( c!=null ) { |
|
return c; |
|
} |
|
} |
|
|
|
// try a global specified class |
|
if (theASTNodeTypeClass != null) { |
|
return theASTNodeTypeClass; |
|
} |
|
|
|
// default to the common type |
|
return CommonAST.class; |
|
} |
|
|
|
/** Add a child to the current AST */ |
|
public void addASTChild(ASTPair currentAST, AST child) { |
|
if (child != null) { |
|
if (currentAST.root == null) { |
|
// Make new child the current root |
|
currentAST.root = child; |
|
} |
|
else { |
|
if (currentAST.child == null) { |
|
// Add new child to current root |
|
currentAST.root.setFirstChild(child); |
|
} |
|
else { |
|
currentAST.child.setNextSibling(child); |
|
} |
|
} |
|
// Make new child the current child |
|
currentAST.child = child; |
|
currentAST.advanceChildToEnd(); |
|
} |
|
} |
|
|
|
/** Create a new empty AST node; if the user did not specify |
|
* an AST node type, then create a default one: CommonAST. |
|
*/ |
|
public AST create() { |
|
return create(Token.INVALID_TYPE); |
|
} |
|
|
|
public AST create(int type) { |
|
Class c = getASTNodeType(type); |
|
AST t = create(c); |
|
if ( t!=null ) { |
|
t.initialize(type, ""); |
|
} |
|
return t; |
|
} |
|
|
|
public AST create(int type, String txt) { |
|
AST t = create(type); |
|
if ( t!=null ) { |
|
t.initialize(type, txt); |
|
} |
|
return t; |
|
} |
|
|
|
/** Create an AST node with the token type and text passed in, but |
|
* with a specific Java object type. Typically called when you |
|
* say @[PLUS,"+",PLUSNode] in an antlr action. |
|
* @since 2.7.2 |
|
*/ |
|
public AST create(int type, String txt, String className) { |
|
AST t = create(className); |
|
if ( t!=null ) { |
|
t.initialize(type, txt); |
|
} |
|
return t; |
|
} |
|
|
|
/** Create a new empty AST node; if the user did not specify |
|
* an AST node type, then create a default one: CommonAST. |
|
*/ |
|
public AST create(AST tr) { |
|
if (tr == null) return null; // create(null) == null |
|
AST t = create(tr.getType()); |
|
if ( t!=null ) { |
|
t.initialize(tr); |
|
} |
|
return t; |
|
} |
|
|
|
public AST create(Token tok) { |
|
AST t = create(tok.getType()); |
|
if ( t!=null ) { |
|
t.initialize(tok); |
|
} |
|
return t; |
|
} |
|
|
|
/** ANTLR generates reference to this when you reference a token |
|
* that has a specified heterogeneous AST node type. This is |
|
* also a special case node creation routine for backward |
|
* compatibility. Before, ANTLR generated "new T(tokenObject)" |
|
* and so I must call the appropriate constructor not T(). |
|
* |
|
* @since 2.7.2 |
|
*/ |
|
public AST create(Token tok, String className) { |
|
AST t = createUsingCtor(tok,className); |
|
return t; |
|
} |
|
|
|
/** |
|
* @since 2.7.2 |
|
*/ |
|
public AST create(String className) { |
|
Class c = null; |
|
try { |
|
c = Utils.loadClass(className); |
|
} |
|
catch (Exception e) { |
|
throw new IllegalArgumentException("Invalid class, "+className); |
|
} |
|
return create(c); |
|
} |
|
|
|
/** |
|
* @since 2.7.2 |
|
*/ |
|
protected AST createUsingCtor(Token token, String className) { |
|
Class c = null; |
|
AST t = null; |
|
try { |
|
c = Utils.loadClass(className); |
|
Class[] tokenArgType = new Class[] { com.fr.third.antlr.Token.class }; |
|
try { |
|
Constructor ctor = c.getConstructor(tokenArgType); |
|
t = (AST)ctor.newInstance(new Object[]{token}); // make a new one |
|
} |
|
catch (NoSuchMethodException e){ |
|
// just do the regular thing if you can't find the ctor |
|
// Your AST must have default ctor to use this. |
|
t = create(c); |
|
if ( t!=null ) { |
|
t.initialize(token); |
|
} |
|
} |
|
} |
|
catch (Exception e) { |
|
throw new IllegalArgumentException("Invalid class or can't make instance, "+className); |
|
} |
|
return t; |
|
} |
|
|
|
/** |
|
* @since 2.7.2 |
|
*/ |
|
protected AST create(Class c) { |
|
AST t = null; |
|
try { |
|
t = (AST)c.newInstance(); // make a new one |
|
} |
|
catch (Exception e) { |
|
error("Can't create AST Node " + c.getName()); |
|
return null; |
|
} |
|
return t; |
|
} |
|
|
|
/** Copy a single node with same Java AST objec type. |
|
* Ignore the tokenType->Class mapping since you know |
|
* the type of the node, t.getClass(), and doing a dup. |
|
* |
|
* clone() is not used because we want all AST creation |
|
* to go thru the factory so creation can be |
|
* tracked. Returns null if t is null. |
|
*/ |
|
public AST dup(AST t) { |
|
if ( t==null ) { |
|
return null; |
|
} |
|
AST dup_t = create(t.getClass()); |
|
dup_t.initialize(t); |
|
return dup_t; |
|
} |
|
|
|
/** Duplicate tree including siblings of root. */ |
|
public AST dupList(AST t) { |
|
AST result = dupTree(t); // if t == null, then result==null |
|
AST nt = result; |
|
while (t != null) { // for each sibling of the root |
|
t = t.getNextSibling(); |
|
nt.setNextSibling(dupTree(t)); // dup each subtree, building new tree |
|
nt = nt.getNextSibling(); |
|
} |
|
return result; |
|
} |
|
|
|
/**Duplicate a tree, assuming this is a root node of a tree-- |
|
* duplicate that node and what's below; ignore siblings of root node. |
|
*/ |
|
public AST dupTree(AST t) { |
|
AST result = dup(t); // make copy of root |
|
// copy all children of root. |
|
if (t != null) { |
|
result.setFirstChild(dupList(t.getFirstChild())); |
|
} |
|
return result; |
|
} |
|
|
|
/** Make a tree from a list of nodes. The first element in the |
|
* array is the root. If the root is null, then the tree is |
|
* a simple list not a tree. Handles null children nodes correctly. |
|
* For example, build(a, b, null, c) yields tree (a b c). build(null,a,b) |
|
* yields tree (nil a b). |
|
*/ |
|
public AST make(AST[] nodes) { |
|
if (nodes == null || nodes.length == 0) return null; |
|
AST root = nodes[0]; |
|
AST tail = null; |
|
if (root != null) { |
|
root.setFirstChild(null); // don't leave any old pointers set |
|
} |
|
// link in children; |
|
for (int i = 1; i < nodes.length; i++) { |
|
if (nodes[i] == null) continue; // ignore null nodes |
|
if (root == null) { |
|
// Set the root and set it up for a flat list |
|
root = tail = nodes[i]; |
|
} |
|
else if (tail == null) { |
|
root.setFirstChild(nodes[i]); |
|
tail = root.getFirstChild(); |
|
} |
|
else { |
|
tail.setNextSibling(nodes[i]); |
|
tail = tail.getNextSibling(); |
|
} |
|
// Chase tail to last sibling |
|
while (tail.getNextSibling() != null) { |
|
tail = tail.getNextSibling(); |
|
} |
|
} |
|
return root; |
|
} |
|
|
|
/** Make a tree from a list of nodes, where the nodes are contained |
|
* in an ASTArray object |
|
*/ |
|
public AST make(ASTArray nodes) { |
|
return make(nodes.array); |
|
} |
|
|
|
/** Make an AST the root of current AST */ |
|
public void makeASTRoot(ASTPair currentAST, AST root) { |
|
if (root != null) { |
|
// Add the current root as a child of new root |
|
root.addChild(currentAST.root); |
|
// The new current child is the last sibling of the old root |
|
currentAST.child = currentAST.root; |
|
currentAST.advanceChildToEnd(); |
|
// Set the new root |
|
currentAST.root = root; |
|
} |
|
} |
|
|
|
public void setASTNodeClass(Class c) { |
|
if ( c!=null ) { |
|
theASTNodeTypeClass = c; |
|
theASTNodeType = c.getName(); |
|
} |
|
} |
|
|
|
public void setASTNodeClass(String t) { |
|
theASTNodeType = t; |
|
try { |
|
theASTNodeTypeClass = Utils.loadClass(t); // get class def |
|
} |
|
catch (Exception e) { |
|
// either class not found, |
|
// class is interface/abstract, or |
|
// class or initializer is not accessible. |
|
error("Can't find/access AST Node type" + t); |
|
} |
|
} |
|
|
|
/** Specify the type of node to create during tree building. |
|
* @deprecated since 2.7.1 |
|
*/ |
|
public void setASTNodeType(String t) { |
|
setASTNodeClass(t); |
|
} |
|
|
|
public Hashtable getTokenTypeToASTClassMap() { |
|
return tokenTypeToASTClassMap; |
|
} |
|
|
|
public void setTokenTypeToASTClassMap(Hashtable tokenTypeToClassMap) { |
|
this.tokenTypeToASTClassMap = tokenTypeToClassMap; |
|
} |
|
|
|
/** To change where error messages go, can subclass/override this method |
|
* and then setASTFactory in Parser and TreeParser. This method removes |
|
* a prior dependency on class antlr.Tool. |
|
*/ |
|
public void error(String e) { |
|
System.err.println(e); |
|
} |
|
}
|
|
|