package 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 antlr.collections.
AST;
import 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[] { 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);
}
}