import Jakarta.util.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URI;
import java.util.Hashtable;
import java.util.List;
import java.util.Stack;
import java.util.EmptyStackException;
import java.util.Vector;
// end of Main class
//**************************************************
// class AstNode
//**************************************************
public abstract class AstNode implements Cloneable, Serializable {
public AstTokenInterface[] tok = null;
public AstNode[] arg = null;
public AstNode right = null;
public AstNode left = null;
public AstNode up = null;
public int stackMarker;
public abstract boolean[] printorder();
private static Class obj_class;
static public Stack aliasStack = new Stack();
static {
try {
obj_class = Class.forName( "java.lang.Object" );
}
catch ( Exception e ) {}
}
// public AstNode(int n) {}
// the following methods are needed to make
// writing code templates and layer files easier.
// the methods themselves will be overridden by
// the Gscope layer. The problem is that AST
// generates calls to these methods and it shouldn't
// good example for refactoring...
public static AstNode markStack( int size, AstNode n ) {
n.stackMarker = size;
return n;
}
// needed for bootstrapping JakBasic
public AstNode markStack() {
return ( AstNode ) this;
}
public AstNode patch() {
return ( AstNode ) this;
}
//**************************************************
// For conversion to a String
//**************************************************
public String toString() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter( baos );
AstProperties props = new AstProperties();
props.setProperty( "output", pw );
print( props );
pw.flush();
return ( baos.toString() );
}
// safeCopy is used in code escapes -- it either returns
// the code fragment to be plugged in if it is detached,
// or it supplies a copy that is to be plugged in. It is
// included in kernel layer because generated code may
// be part of a JTS-or-AHEAD tool that doesn't have an
// ast layer
public static AstNode safeCopy( AstNode it ) {
if ( it == null )
return null;
if ( kernelConstants.copyLists && it instanceof AstList )
return ( AstNode ) it.clone();
if ( it.up != null || it.left != null || it.right != null ) {
AstNode copy = ( AstNode ) it.clone();
return copy;
}
return it;
}
// this method deals with the awkward situation where an
// object is to be cloned, but just happends to be null
//
public static Object copy( AstNode it ) {
if ( it == null )
return null;
return it.clone();
}
public Object clone() {
AstNode copy;
copy = null;
try {
copy = ( AstNode ) getClass().newInstance();
}
catch ( Exception e ) {
fatalError( "Can't clone "+getClass() + e.getMessage() );
}
initClone( copy );
return ( copy );
}
// Copy of an AstNode copies only it's children
protected void initClone( AstNode copy ) {
int i;
if ( tok != null ) {
copy.tok = new AstTokenInterface[tok.length];
for ( i=0; i < tok.length; i++ )
if ( tok[i] != null )
copy.tok[i] = ( AstTokenInterface ) tok[i].clone();
}
if ( arg != null ) {
copy.arg = new AstNode[arg.length];
for ( i=0; i < arg.length; i++ ) {
if ( arg[i] != null ) {
copy.arg[i] = ( AstNode ) arg[i].clone();
// copy.arg[i].up = copy;
}
}
}
// initialize children pointers, if this hasn't been done
// already. AstListNodes already have been initialized
if ( ! ( copy instanceof AstList ) )
copy.InitChildren();
}
// We would like to locate a given ancestor that is an instance of the
// class specified (classname) or a derived class of that.
public AstNode hasAncestor( String classname ) {
Class ancestorClass;
String ancestorBaseName;
AstNode ancestor;
ancestor = up;
while ( ancestor != null ) {
ancestorClass = ancestor.getClass();
ancestorBaseName = Util.baseType( ancestorClass );
while ( ancestorClass != obj_class ) {
if ( classname.compareTo( ancestorBaseName ) == 0 )
return ( ancestor );
ancestorClass = ancestorClass.getSuperclass();
}
ancestor = ancestor.up;
}
return ( null );
}
// InitChildren() initializes the left, right, and up pointers of
// children nodes.
public void InitChildren() {
int i;
AstNode previous;
up = left = right = null;
// Step 1: init node id
if ( arg[0] == null )
return; // no children, therefore nothing to initialize
// Step 2: initialize up and left for each child
previous = null;
for ( i=0; i<arg.length; i++ ) {
if ( kernelConstants.debugAST ) {
if ( arg[i].up != null )
fatalError( "InitChildren called with non-detached arguments"
+ arg[i].getClass().getName() + " " + arg[i] );
}
arg[i].up = ( AstNode ) this;
arg[i].left = previous;
previous = arg[i];
}
// Step 3: initialize right for each child
previous = null;
for ( i=arg.length-1; i>=0; i-- ) {
arg[i].right = previous;
previous = arg[i];
}
}
public void PrettyDump() {
PDump( "" );
}
public void PDump( String indent ) {
String ind;
int i;
// System.out.println(indent + indent.length() + className() +
// "\t(" + hashCode() + ")");
System.out.println( indent + indent.length() + className() );
if ( arg == null )
return;
else {
ind = indent + " ";
for ( i=0; i<arg.length; i++ )
if ( arg[i] != null )
arg[i].PDump( ind );
}
}
// Replace(k) swaps the current node with node k. Replace returns
// k as a result.
public AstNode Replace( AstNode withnode ) {
// Step 1: adjust right pointer
withnode.right = right;
if ( right != null )
right.left = withnode;
right = null;
// Step 2: adjust left pointer
withnode.left = left;
if ( left != null )
left.right = withnode;
left = null;
// Step 3: adjust up pointer
withnode.up = up;
if ( up != null )
up.ReplaceRef( ( AstNode ) this, withnode );
up = null;
// Step 4: return withnode
return withnode;
}
// ReplaceRef(c,n) replaces reference to child c with Ast node n
public void ReplaceRef( AstNode oldnode, AstNode newnode ) {
int i;
for ( i = 0; i < arg.length; i++ ) {
if ( arg[i] == oldnode ) {
arg[i] = newnode;
return;
}
}
System.out.println( "\nFatal Error - inconsistent AST reference" );
}
// print() unparses the contents of the AST rooted at the current node.
// reduce2java() prints the Java code that is defined by the Jakarta
// AST that is rooted at the current node
// reduce2ast() prints the constructors that are needed to reconstruct
// the ASt that is rooted at the current node.
public void print() {
boolean order[];
int t, n, i;
order = printorder();
t = 0;
n = 0;
for ( i=0; i<order.length; i++ ) {
// if order[i] is true; print token else print nonterminal
if ( order[i] )
tok[t++].print();
else
arg[n++].print();
}
}
public void print( AstProperties props ) {
boolean order[];
int t, n, i;
order = printorder();
t = 0;
n = 0;
for ( i=0; i<order.length; i++ ) {
// if order[i] is true; print token else print nonterminal
if ( order[i] )
tok[t++].print( props );
else
arg[n++].print( props );
}
}
public void reduce2java( AstProperties props ) {
boolean order[];
int t, n, i;
order = printorder();
t = 0;
n = 0;
for ( i=0; i<order.length; i++ ) {
// if order[i] is true; print token else print nonterminal
if ( order[i] )
tok[t++].reduce2java( props );
else
arg[n++].reduce2java( props );
}
}
// Print_Only_Tokens() prints only the tokens of a given node. This
// method is used only for the printing of lists. Similarly,
// Print_Only_Tokens_Ast() prints the ASTs for tokens of a given node.
// It too is used only for the reduction of lists.
public void Print_Only_Tokens( AstProperties props ) {
int i;
if ( tok == null )
return;
for ( i=0; i<tok.length; i++ )
tok[i].print( props );
}
public void Print_Only_Tokens() {
int i;
if ( tok == null )
return;
for ( i=0; i<tok.length; i++ )
tok[i].print();
}
boolean instanceOf( Class myclass ) {
Class c;
for ( c = this.getClass(); c != null; c = c.getSuperclass() )
if ( c == myclass )
return ( true );
return ( false );
}
public boolean instanceOf( Object obj ) {
Class myclass;
myclass = obj.getClass();
return ( instanceOf( myclass ) );
}
public boolean instanceOf( String class_name ) {
Class myclass;
try {
myclass = Class.forName( class_name );
}
catch ( ClassNotFoundException e ) {
System.err.println( "Couldn't find class "+class_name );
return ( false );
}
return ( instanceOf( myclass ) );
}
public String className() {
return ( kernelConstants.globals().LangName + Util.baseType( this ) );
}
public boolean Equ( AstNode x ) {
int i;
// Step 1: check to see if x is null; if so, return false
if ( x == null )
return false;
// Step 2: must have equal types
if ( getClass() != x.getClass() )
return false;
// Step 3: must have equal #s of tokens and equal tokens
if ( tok != x.tok ) {
if ( tok != null && x.tok != null && tok.length != x.tok.length )
return false;
else
for ( i=0; i< tok.length; i++ ) {
if ( tok[i] != x.tok[i] && tok[i] != null &&
!tok[i].Equ( ( AstTokenInterface ) x.tok[i] ) )
return false;
}
}
// Step 4: must have equal #s of arguments and equal arguments
if ( arg != null && x.arg != null && arg.length != x.arg.length )
return false;
else
for ( i=0; i< arg.length; i++ ) {
if ( arg[i] != x.arg[i] && arg[i] != null &&
!arg[i].Equ( x.arg[i] ) )
return false;
}
// Step 6: we got this far, must be equal
return true;
}
// dumpnode() is used for debugging small ASTs. It prints the id and
// type of a node, plus its left,right,up, and arg pointers for all
// nodes in an AST
public void dumpnode() {
int i;
AstList l;
// Step 1: print the nodeid, class, and up, left, and right pointers
System.out.print( hashCode() + " " + className() );
if ( up == null )
System.out.print( "\tup:*" );
else
System.out.print( "\tup:"+up.hashCode() );
if ( left == null )
System.out.print( "\tleft:*" );
else
System.out.print( "\tleft:"+left.hashCode() );
if ( right == null )
System.out.print( "\trite:*" );
else
System.out.print( "\trite:"+right.hashCode() );
// Step 2: print out last (if it exists)
if ( this instanceof AstList ) {
l = ( AstList ) this;
if ( l.last == null )
System.out.print( "\tlast:*" );
else
System.out.print( "\tlast:"+l.last.hashCode() );
}
// Step 2: print out arguments
for ( i = 0; i < arg.length; i++ ) {
if ( arg[i] == null )
System.out.print( "\targ[" + i + "]:*" );
else
System.out.print( "\targ[" + i + "]:"+arg[i].hashCode() );
}
System.out.println( "" );
// Step 3: validate pointers
if ( left != null && left.right != this )
System.out.print( "l<->r pointers invalid" );
if ( right != null && right.left != this )
System.out.print( "r<->l pointers invalid" );
if ( arg != null && arg[0] != null && arg[0].up != this )
System.out.print( "d<->u pointers invalid" );
// Step 4: recurse
for ( i=0; i < arg.length; i++ ) {
if ( arg[i] != null )
arg[i].dumpnode();
}
}
// Delete() the current node. Nodes can be deleted only in the following
// conditions:
// the node is an element of a list (i.e., up instanceof AstListNode)
// the node is optional (i.e., up instanceof AstOptNode)
// the node is a list (i.e., this instanceof AstList)
// if node is a list, AstList::Delete overrides, so all we need to
// do here is handle the first two cases.
public void Delete() { // delete this node
// Step 1: see if parent is an instance of AstList
// if so, remove it instead.
if ( up instanceof AstListNode ) {
( ( AstListNode ) up ).Delete();
return;
}
// Step 2: else, see if parent is an instance of
// AstOptNode. if so, remove it instead
if ( up instanceof AstOptNode ) {
( ( AstOptNode ) up ).Delete();
return;
}
// Step 3: else Fatal error - can only delete nodes
// that are instances of lists
fatalError( "cannot delete non-list node" );
}
// AddAfter(y) adds AstList y after the current node. this is possible
// if the current node's parent (up) is an instance of AstListNode (that
// is, the current node is on a list.
public void AddAfter( AstList tree ) { // add tree after this node
if ( up instanceof AstListNode ) {
( ( AstListNode ) up ).AddAfter( tree );
}
else
fatalError( "illegal AddAfter" );
}
// AddBefore(y) adds AstList y before the current node. this is possible
// if the current node's parent (up) is an instance of AstListNode (that
// is, the current node is on a list.
public void AddBefore( AstList tree ) { // add tree before this node
if ( up instanceof AstListNode ) {
( ( AstListNode ) up ).AddBefore( tree );
}
else
fatalError( "illegal AddAfter" );
}
//**************************************************
// This method adds the comment given by the parameter to
// the first AstToken.
//**************************************************
public AstNode addComment( String comment ) {
return ( addComment( comment, false ) );
}
public AstNode addComment( String comment, boolean replace ) {
boolean order[] = printorder();
int t, n, i;
AstToken ast_token;
t = 0; // terminal index
n = 0; // non-terminal index
for ( i=0; i < order.length; i++ ) {
// if order[i] is true, a terminal is next; else a non-terminal
if ( order[i] ) {
// Terminal can be a token or non-token
if ( tok[t] instanceof AstToken ) {
ast_token = ( AstToken ) tok[t];
if ( replace )
ast_token.white_space = comment;
else
ast_token.white_space = comment +
ast_token.white_space;
return ( ( AstNode ) this );
}
if ( ( ( AstOptToken ) tok[t] ).addComment( comment, replace ) != null )
return ( ( AstNode ) this );
t++;
}
else
if ( ( ( AstNode ) arg[n++] ).addComment( comment, replace ) != null )
return ( ( AstNode ) this );
}
return ( null );
}
// This method gets code generated as the first call to add a comment
// to an AST. It's main purpose is to determine that the AST is
// non-null before calling the addComment of the instance.
public static AstNode addComment( AstNode ast,
String comment ) {
return ( addComment( ast, comment, false ) );
}
public static AstNode addComment( AstNode ast,
String comment,
boolean replace ) {
if ( ast == null )
return ( null );
return ( ast.addComment( comment, replace ) );
}
// This method is used to repair ASTs that share subtrees.
// the idea is to walk the tree looking for nodes that don't
// point to their parent. In such cases, the subtree is cloned
// and the result is that normalized trees don't have shared
// subtrees
public AstNode normalizeTree( AstNode parnt ) {
int i;
if ( arg == null )
return ( AstNode ) this;
for ( i=0; i<arg.length; i++ )
if ( arg[i] != null )
arg[i] = ( AstNode ) arg[i].normalizeTree( ( AstNode ) this );
// if our up pointer != down pointer parnt, we must
// clone ourselves
if ( parnt == null )
return ( AstNode ) this; // at root
if ( up != parnt ) {
return ( AstNode ) this.clone();
}
else
return ( AstNode ) this;
}
public void normalize() {
normalizeTree( ( AstNode ) this );
}
// Detach isolates a subtree from its left, up, and right
// node relations. Useful for detaching a tree from an
// existing tree
public void Detach() {
up = null;
left = null;
right = null;
}
// writeTree serializes an AST to a file
public void writeTree( String fileName ) {
ObjectOutputStream os = null;
try {
os = new ObjectOutputStream( new FileOutputStream( fileName + ".ser" ) );
}
catch( Exception e ) {
fatalError( e.getMessage() );
}
try {
os.writeObject( this );
}
catch( Exception e ) {
fatalError( e.getMessage() );
}
try {
os.close();
}
catch( Exception e ) {
fatalError( e.getMessage() );
}
}
// error and warning reporting messages
// format: <tool> : <file> : <line-number (optional)> : <message>
private static String eformat( String msg ) {
return kernelConstants.PackageName + ": " +
kernelConstants.globals().currentFileName + msg ;
}
static public void report( String msg ) {
Util.report( eformat( msg ) );
}
static public void toolReport( String msg ) {
Util.report( kernelConstants.PackageName + ": " + msg );
System.exit( 1 );
}
static public int errorCount() {
return Util.errorCount();
}
static public void fatalError( AstTokenInterface t, String msg ) {
int lineno = ( ( AstToken ) t ).lineNum();
Util.fatalError( eformat( ":" + lineno + ": Fatal Error " + msg ) );
}
static public void fatalError( Exception e, String msg ) {
Util.report( eformat( ": Fatal Error (" + e + ") " + msg ) );
Util.fatalError( e );
}
static public void fatalError( String msg ) {
Util.fatalError( eformat( ": Fatal Error " + msg ) );
}
static public void parseError( String msg ) {
Util.report( eformat( ": Parse Error " + msg ) );
System.exit( 1 );
}
static public void parseError2( String msg ) {
Util.report( msg );
System.exit( 1 );
}
static public void override( String msg, Object o ) {
Util.fatalError( eformat( msg + " should be overridden in class "
+ o.getClass().getName() ) );
}
static public void override( AstTokenInterface t,
String msg, Object o ) {
int lineno = ( ( AstToken ) t ).lineNum();
Util.fatalError( eformat( ":" + lineno + ": Fatal Error " +
msg + " should be overridden in class " +
o.getClass().getName() ) );
}
static public void error( AstTokenInterface t, String msg ) {
int lineno = ( ( AstToken ) t ).lineNum();
Util.error( eformat( ":" + lineno + ": Error " + msg ) );
}
static public void error( String msg ) {
Util.error( eformat( ": Error " + msg ) );
}
static public void warning( AstTokenInterface t, String msg ) {
int lineno = ( ( AstToken ) t ).lineNum();
Util.warning( eformat( ":" + lineno + ": Warning " + msg ) );
}
static public void warning( String msg ) {
Util.warning( eformat( ": Warning " + msg ) );
}
}