

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 );
        Util.fatalError( eformat( ": Fatal Error (" + e + ") " + msg ), 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 ) );
    }
}
