import java.util.*;
import java.lang.*;
import AST.*;
import CompilerError.*;
import Interface.*;
import java.io.*;


public class MyParser {


   public MyParser(
        MyLexer lexer,
        ErrorMsg errorMsg,
        ClassTypeInterface currentClassInterface,
        TypeInterfaceList til
        ) {

      this.errorMsg = errorMsg;
      this.lexer = lexer;
      this.currentClassInterface = currentClassInterface;
      this.til = til;
      canUseResult = false;
   }



    private GenericClass          currentClass;
    private Method                currentMethod;
    private ClassObject           currentClassObject;


    private MyLexer               lexer;
    private ErrorMsg              errorMsg;
    private ClassTypeInterface    currentClassInterface;
    private ClassObjectTypeInterface currentClassObjectInterface;
    private TypeInterfaceList     til;

    private TypeInterface         currentInterface;
    private SymbolTable           st;

    private boolean inClassObject, canUseResult;
    private int lastMethodNumber; // last method number of a class object
    
       // if inside a loop, loopNesting == 1. If inside two loops, loopNesting == 2 
    private int loopNesting;
    
    static private Hashtable classesWithOperators, wrapperClassType;
    static {
          // Assume that all classes that have operator are basic types (String excluded)
        classesWithOperators = new Hashtable();
        classesWithOperators.put("char", "");
        classesWithOperators.put("boolean", "");
        classesWithOperators.put("byte", "");
        classesWithOperators.put("integer", "");
        classesWithOperators.put("long", "");
        classesWithOperators.put("real", "");
        classesWithOperators.put("double", "");
        
        wrapperClassType = new Hashtable();
        wrapperClassType.put("Char", "");
        wrapperClassType.put("Boolean", "");
        wrapperClassType.put("Byte", "");
        wrapperClassType.put("Integer", "");
        wrapperClassType.put("Long", "");
        wrapperClassType.put("Real", "");
        wrapperClassType.put("Double", "");
    }
        


   private Expr expr() {


      Expr left = orExpr();
      while ( lexer.token == MySym.ASSIGN ) {
         lexer.next();
         if ( lexer.token == MySym.CANCELA ) {
               // array initialization
               //   anArray = #(4, 5, 5, 9, 3);
            lexer.next();
            if ( ! (left.getType() instanceof ArrayType) )
               errorMsg.show("Left-hand side should be an array");

            if ( lexer.token != MySym.LEFTPAR )
              errorMsg.signal("( expected");
            lexer.next();
            ConstExprList anExprList = constExprList(left.getType());
            if ( lexer.token != MySym.RIGHTPAR )
               errorMsg.signal(") expected");
            lexer.next();
            return new ArrayInitExprList(left, anExprList);
         }
         else {
            Expr right = orExpr();
            if ( left.getType() == null || left.getType().getTypeInterface() == null )
               errorMsg.show("Left-hand side of the assignment cannot be assigned to");
            if ( right.getType() == null ) 
               errorMsg.show("Assignment from a non-expression");
            if ( ! left.getType().isSupertypeOf(right.getType()) ) {
               boolean inError = false;
               Type leftType, rightType;
               leftType = left.getType();
               rightType = right.getType();
               if ( leftType instanceof BasicType ) {
                  BasicType leftBasicType = (BasicType ) leftType;
                  Type wrapperType = leftBasicType.getWrapperType();
                  if ( ! wrapperType.isSupertypeOf(rightType) )
                     inError = true;
                  else {
                     MethodInterface mi = wrapperType
                         .getTypeInterface()
                             .publicSearch(
                               TypeInterface.buildMethodSearchName("get", 
                                  new ParameterList()));
                     if ( mi == null )
                        errorMsg.show("Internal error: get not found in a wrapper type");
                     ExprList exprList = new ExprList();
                     right = new MessageSendExpr(right, mi, exprList);
                  }
               }
               else if ( rightType instanceof BasicType ) {
                   BasicType t = (BasicType ) rightType;
                   if ( t.getWrapperName().compareTo(leftType.getName()) != 0 )
                      inError = true;
                   else {
                      ParameterList pl = new ParameterList();
                      Parameter p = new Parameter("x");
                      p.setType( rightType );
                      pl.add(p);
                      ClassObjectTypeInterface coti = 
                        ((ClassTypeInterface ) leftType
                          .getTypeInterface()).getClassObjectInterface();
                      MethodInterface mi = coti.publicSearch(
                                TypeInterface.buildMethodSearchName("new", pl));
                      if ( mi == null )
                         errorMsg.show("Internal error: new not found in a wrapper class object");
                      ExprList exprList = new ExprList();
                      exprList.add(right);
                      right = new MessageSendExpr(
                         new ClassObjectExpr(
                            new ClassObjectType(coti) ), mi, exprList);
                   }
               }
               if ( inError )
                  errorMsg.show("Type error: left-hand side should be supertype of the right-hand side of the assignment");
            }
            left = new AssignmentExpr(left, right);
         }
      }
      return left;
   }

   private Expr orExpr() {
      Expr left = xorExpr();
      while ( lexer.token == MySym.OR ) {
         if ( left.getType() != Type.booleanType )
            errorMsg.show("boolean value expected");
         lexer.next();
         Expr right = xorExpr();
         if ( right.getType() != Type.booleanType )
            errorMsg.show("boolean value expected");
         left = new BooleanCompositeExpr(left, MySym.OR, right);
      }
      return left;
   }



   private Expr xorExpr() {
      Expr left = andExpr();
      while ( lexer.token == MySym.XOR ) {
         if ( left.getType() != Type.booleanType )
            errorMsg.show("boolean value expected");
         lexer.next();
         Expr right = andExpr();
         if ( right.getType() != Type.booleanType )
            errorMsg.show("boolean value expected");
         left = new BooleanCompositeExpr(left, MySym.XOR, right);
      }
      return left;
   }

   private Expr andExpr() {
      Expr left = relExpr();
      while ( lexer.token == MySym.AND) {
         if ( left.getType() != Type.booleanType )
            errorMsg.show("boolean value expected");
         lexer.next();
         Expr right = relExpr();
         if ( right.getType() != Type.booleanType )
            errorMsg.show("boolean value expected");
         left = new BooleanCompositeExpr(left, MySym.AND, right);
      }
      return left;
   }

   private Expr relExpr() {
      Expr left = addExpr();
      int symbol = relation();
      if ( symbol >= 0 ) {
         lexer.next();
         Expr right = addExpr();
         left = new RelationExpr(left, symbol, right);
      }
      return left;
   }

   private Expr addExpr() {
      Expr left = multExpr();
      
      if ( left.getType() != null && left.getType().getName().compareTo("String") == 0 )
         while ( lexer.token == MySym.PLUS ) {
            lexer.next();
            Expr right = postfixExpr();
            if ( right.getType().getName().compareTo("String") != 0 ) {
               errorMsg.show("String expression expected");
            }
            left = new StringCompositeExpr(left, right);
         }
      else
         while ( lexer.token == MySym.PLUS || lexer.token == MySym.MINUS ) {
            checkArithmeticType(left.getType());
            int op = lexer.token;
            lexer.next();
            Expr right = multExpr();
            checkArithmeticType(right.getType());
            if ( left.getType() != right.getType() )
               errorMsg.show("Type error: mixing different types");
            left = new ArithmeticCompositeExpr(left, op, right);
         }
      return left;
   }


   private Expr multExpr() {
      Expr left = binOrExpr();
      while ( lexer.token == MySym.MULT || lexer.token == MySym.DIV ||
              lexer.token == MySym.REMAINDER ) {
         checkArithmeticType(left.getType());
         int op = lexer.token;
         lexer.next();
         Expr right = binOrExpr();
         checkArithmeticType(right.getType());
         if ( left.getType() != right.getType() )
            errorMsg.show("Type error: mixing different types");
         left = new ArithmeticCompositeExpr(left, op, right);
      }
      return left;
   }



   private Expr binOrExpr() {
      Expr left = binXorExpr();
      while ( lexer.token == MySym.BINOR ) {
         checkIntegralType(left.getType());
         lexer.next();
         Expr right = binXorExpr();
         checkIntegralType(right.getType());
         if ( left.getType() != right.getType() )
            errorMsg.show("Type error: mixing different types");
         left = new BinCompositeExpr(left, MySym.BINOR, right);
      }
      return left;
   }



   private Expr binXorExpr() {
      Expr left = binAndExpr();
      while ( lexer.token == MySym.BINXOR ) {
         checkIntegralType(left.getType());
         lexer.next();
         Expr right = binAndExpr();
         checkIntegralType(right.getType());
         if ( left.getType() != right.getType() )
            errorMsg.show("Type error: mixing different types");
         left = new BinCompositeExpr(left, MySym.BINXOR, right);
      }
      return left;
   }



   private Expr binAndExpr() {
      Expr left = shiftExpr();
      while ( lexer.token == MySym.BINAND ) {
         checkIntegralType(left.getType());
         lexer.next();
         Expr right = shiftExpr();
         checkIntegralType(right.getType());
         if ( left.getType() != right.getType() )
            errorMsg.show("Type error: mixing different types");
         left = new BinCompositeExpr(left, MySym.BINAND, right);
      }
      return left;
   }


   private Expr shiftExpr() {
      Expr left = unaryExpr();
      int symbol = lexer.token;
      if ( symbol == MySym.LEFTSHIFT || symbol == MySym.RIGHTSHIFT ) {
         checkIntegralType(left.getType());
         lexer.next();
         Expr right = unaryExpr();
         if ( right.getType() != Type.byteType &&
              right.getType() != Type.integerType ) 
            errorMsg.show("byte or integer expression expected after >> or <<");
         left = new ArithmeticCompositeExpr(left, symbol, right);
      }
      return left;
   }



   private Expr unaryExpr() {
      Expr left, right;

      switch ( lexer.token ) {
         case MySym.NOT :
            lexer.next();
            right = postfixExpr();
            if ( right.getType() != Type.booleanType ) 
               errorMsg.show("boolean expression expected after not");
            left = new NotUnaryExpr(right);
            break;
         case MySym.MINUS :
            lexer.next();
            right = postfixExpr();
            checkIntegralType(right.getType());
            left = new ArithmeticUnaryExpr(MySym.MINUS, right);
            break;
         case MySym.PLUS :
            lexer.next();
            right = postfixExpr();
            checkIntegralType(right.getType());
            left = new ArithmeticUnaryExpr(MySym.PLUS, right);
            break;
         case MySym.BINNOT :
            lexer.next();
            right = postfixExpr();
            checkIntegralType(right.getType());
            left = new BinNotUnaryExpr(right);
            break;
         case MySym.PLUSPLUS :
            lexer.next();
            right = postfixExpr();
            if ( ! right.getIsLeftValue() )
               errorMsg.show("++ can only be applied to variables");
            if ( ! isArithmeticType(right.getType()) && right.getType() != Type.charType )
               errorMsg.show("++ can only be applied to variables of all basic types but boolean");
            left = new ArithmeticUnaryExpr(MySym.PLUSPLUS, right);
            break;
         case MySym.MINUSMINUS :
            lexer.next();
            right = postfixExpr();
            if ( ! right.getIsLeftValue() )
               errorMsg.show("-- can only be applied to variables");
            if ( ! isArithmeticType(right.getType()) && right.getType() != Type.charType )
               errorMsg.show("-- can only be applied to variables of all basic types but boolean");
            left = new ArithmeticUnaryExpr(MySym.MINUSMINUS, right);
            break;
         default :
            left = postfixExpr();
      }
      return left;
   }


   private Expr postfixExpr() {
      Expr left;
      MethodInterface mi;
      Hashtable mv;

      left = primaryExpr();
      while ( lexer.token == MySym.PERIOD || lexer.token == MySym.LEFTSB )
         switch ( lexer.token ) {
            case MySym.PERIOD :
               lexer.next();
               if ( lexer.token != MySym.IDENT ) {
                  errorMsg.show("Identifier expected");
                  return new InvalidExpr();
               }
               else {
                  String afterPeriod = lexer.ident;
                  lexer.next();
                  
                                    
                  if ( lexer.token == MySym.LEFTPAR ) {
                         // message send
                     lexer.next();
                     ExprList anExprList = exprList();
                     if ( lexer.token != MySym.RIGHTPAR )
                        errorMsg.signal(") expected");
                     else
                        lexer.next();

                     if ( left instanceof SelfExpr ) {
                        if ( (mi = currentInterface.fullSearch(afterPeriod, anExprList)) == null ) {
                           mv = currentInterface.multipleFullSearch(afterPeriod, anExprList);
                           if ( mv.size() == 1 ) {
                              Enumeration g = mv.elements();
                              mi = (MethodInterface ) g.nextElement();
                           }
                           else if ( mv.size() > 1 )  
                              errorMsg.show("Ambiguous method call");
                           else 
                              errorMsg.show("No method matching " + afterPeriod);
                        }
                        // found a method
                        checkParameterPassing(mi, anExprList);
                        return new MessageSendNoReceiverExpr( mi, anExprList );
                        
                     }
                     else { 
                        TypeInterface ti = left.getType().getTypeInterface();
                        if ( ti == null )
                           errorMsg.show("Internal error: ti == null");
                        if ( (mi = ti.publicSearch(afterPeriod, anExprList)) == null ) {
                           mv = ti.multiplePublicSearch(afterPeriod, anExprList);
                           if ( mv.size() == 0 ) {
                              errorMsg.show("Type error: method " + afterPeriod + " not found in class " + ti.getName());
                              return null;
                           }
                           else if ( mv.size() > 1 ) {
                              ti.publicSearch(afterPeriod, anExprList);
                              ti.multiplePublicSearch(afterPeriod, anExprList);
                              errorMsg.show("Ambiguous call to method " + afterPeriod);
                              return null;
                           }
                           Enumeration f = mv.elements();
                           mi = (MethodInterface ) f.nextElement();
                           if ( mi == null ) 
                              errorMsg.show("Internal error at postfixExpr");
                        }
                        checkParameterPassing(mi, anExprList);
                        left = new MessageSendExpr(left, mi, anExprList);
                     }
                  }
                  else {
                     Identifier id;
                     if ( left instanceof SelfExpr ) {
                        id = st.getInClass(afterPeriod);
                        if ( id == null ) 
                           errorMsg.show("Identifier " + afterPeriod + " not found");
                        else {
                           if ( id instanceof Variable )
                              left = new VariableExpr( (Variable ) id );
                           else if ( id instanceof Constant )
                              left = new ConstantExpr( (Constant ) id );
                           else
                              errorMsg.show("Internal error: postfixExpr");
                        }
                        
                     }
                     else {
                        Constant c;
                        if ( ! (left instanceof ClassObjectExpr) )
                           errorMsg.show("Class expected before . or attempt to use a private instance variable");
                        TypeInterface ti = ((ClassObjectExpr ) left).getClassObjectType().getTypeInterface();
                        if ( ti.getName().compareTo(currentInterface.getName()) == 0 )
                           c = ti.fullSearchConstant(afterPeriod);
                        else
                           c = ti.searchConstant(afterPeriod);
                        if ( c == null )
                           errorMsg.show("Constant " + afterPeriod + " not found in class object " + left.getType().getTypeInterface() );
                        left = new ClassConstantExpr( (ClassObjectTypeInterface ) ti, c );
                     }
                  }
               }
               break;
            case MySym.LEFTSB :
               lexer.next();
               Expr index = expr();
               if ( index.getType() != Type.integerType )
                  errorMsg.show( "Index must be an integer expression" );
               if ( lexer.token != MySym.RIGHTSB )
                  errorMsg.signal("] expected");
               else
                  lexer.next();
               if ( ! (left.getType() instanceof ArrayType) )
                  errorMsg.show("Attempt to index a non-array expression");
               left = new ArrayIndexing(left, index);
               break;
         }
      return left;
   }



   private Expr primaryExpr() {
      String ident;
      ExprList anExprList;
      MethodInterface mi;
      Hashtable mv;

      switch ( lexer.token ) {
         case MySym.IDENT :
            ident = lexer.ident;
            lexer.next();
            switch ( lexer.token ) {
               case MySym.LEFTPAR :
                  lexer.next();
                  anExprList = exprList();
                  if ( lexer.token != MySym.RIGHTPAR )
                     errorMsg.show("( expected");
                  else
                     lexer.next();
                     // ident == method name
                  if ( (mi = currentInterface.fullSearch(ident, anExprList)) == null ) {
                     mv = currentInterface.multipleFullSearch(ident, anExprList);
                     if ( mv.size() == 1 ) {
                        Enumeration g = mv.elements();
                        mi = (MethodInterface ) g.nextElement();
                     }
                     else if ( mv.size() > 1 )  
                        errorMsg.show("Ambiguous method call");
                     else {
                        // is ident a parameterized class ?
                        String className = TypeInterface.buildClassName(ident, anExprList);
                        ClassTypeInterface cti;
                        if ( className == null || (cti = til.get(className)) == null ) {
                           errorMsg.show("No method matching " + ident );
                           return null;
                        }
                        else
                           return new ClassObjectExpr(cti.getClassObjectInterface().getClassObjectType());
                     }
                  }
                  // found a method
                  checkParameterPassing(mi, anExprList);
                  return new MessageSendNoReceiverExpr( mi, anExprList );
               case MySym.CANCELA :
                  lexer.next();
                  if ( lexer.token != MySym.IDENT || lexer.ident.compareTo("init") != 0 ) {
                      errorMsg.show("'ident' expected");
                      return new InvalidExpr();
                  }
                  else {
                     lexer.next();
                     if ( lexer.token != MySym.LEFTPAR )
                        errorMsg.show("( expected");
                     else
                        lexer.next();
                     anExprList = exprList();
                     if ( lexer.token != MySym.RIGHTPAR )
                        errorMsg.show(") expected");
                     else
                        lexer.next();
                     Identifier id = st.get(ident);
                     if ( id == null )
                        errorMsg.show("Identifier " + ident + " was not declared");
                     else
                        if ( ! (id instanceof Variable) )
                           errorMsg.show(ident + " should be a variable");
                        else {
                           Variable v = (Variable ) id;
                           ClassTypeInterface cti;
                           if ( ! (v.getType().getTypeInterface() instanceof ClassTypeInterface) )
                              errorMsg.show("Illegal constructor call");
                           else {
                              cti = (ClassTypeInterface ) v.getType().getTypeInterface();
                              mi = cti.getInitMethodList().
                                    get(TypeInterface.buildMethodSearchName(ident, anExprList));
                              if ( mi == null ) {
                                 mv = new Hashtable();
                                 cti.getInitMethodList().multipleGet(
                                    TypeInterface.buildAnySearchName("init", anExprList), mv);
                                 if ( mv.size() == 0 ) {
                                    errorMsg.show("Constructor was not found");
                                 }
                                 else if ( mv.size() > 1 ) 
                                    errorMsg.show("Ambiguous call to constructor");
                                 else {
                                    Enumeration g = mv.elements();
                                    mi = (MethodInterface ) g.nextElement();
                                 }
                                 if ( mi == null ) 
                                    errorMsg.show("Internal error at postfixExpr");
                                 
                              }
                              checkParameterPassing(mi, anExprList);
                              return new InitCallStatement(mi, v, anExprList);
                           }
                        }
                  }
                  break;
               default :
                  Identifier id;
                  id = st.get(ident);
                  if ( id != null ) {
                     // variable or constant
                     if ( id instanceof Variable )
                        return new VariableExpr( (Variable ) id );
                     else if ( id instanceof Constant )
                        return new ConstantExpr( (Constant ) id );
                     else
                        errorMsg.show("Internal error: primaryExpr");
                  }
                  else {
                     ClassTypeInterface cti;
                     if ( (cti = til.get(ident)) == null ) 
                        errorMsg.show("Identifier " + ident + " was not declared");
                     else
                        return new ClassObjectExpr(cti.getClassObjectInterface().getClassObjectType());
                  }
                  
            }
         case MySym.LITERALSTRING :
            ident = lexer.stringValue;
            lexer.next();
            return new LiteralStringExpr(ident);
         case MySym.SELF :
            lexer.next();
            return new SelfExpr(currentInterface);
         case MySym.RESULT :
            if ( ! canUseResult ) 
               errorMsg.show("result cannot be used outside after of an assert clause");
            lexer.next();
            return new ResultExpr(currentMethod.getReturnType());
         case MySym.NIL :
            lexer.next();
            return Type.nilExpr;
         case MySym.EXCEPTION :
            lexer.next();
            if ( lexer.token != MySym.PERIOD )
               errorMsg.show(". expected");
            else
               lexer.next();
            if ( lexer.token != MySym.IDENT ) {
                errorMsg.show("Identifier expected");
                return new InvalidExpr();
            }
            else {
                ident = lexer.ident;
                lexer.next();
                if ( lexer.token != MySym.LEFTPAR )
                   errorMsg.show("( expected");
                else
                   lexer.next();
                Expr anExpr = expr();
                if ( lexer.token != MySym.RIGHTPAR )
                   errorMsg.show(") expected");
                else
                   lexer.next();
                
                anExprList = new ExprList();
                anExprList.add(anExpr);
                String msn = TypeInterface.buildMethodSearchName(ident, anExprList);
                if ( (mi = st.getMethodOfCatch(msn)) == null ) {
                   Hashtable v = st.multipleGetMethodOfCatch(TypeInterface.buildAnySearchName(ident, anExprList));
                   if ( v.size() == 0 ) {
                      errorMsg.show("Method not found");
                      return null;
                   }
                   else if ( v.size() > 1 ) {
                      errorMsg.show("Ambiguous call to method " + ident);
                      return null;
                   }
                   Enumeration g = v.elements();
                   mi = (MethodInterface ) g.nextElement();
                   if ( mi == null ) 
                      errorMsg.show("Internal error at postfixExpr");
                }
                checkParameterPassing(mi, anExprList);
                return new ExceptionExpr(mi, anExpr);
            }
         case MySym.ARRAY :
            ArrayType anArray = arrayType();
            anArray.setRealJavaName();
            return new ClassObjectExpr( ((ClassTypeInterface ) anArray.getTypeInterface())
                          .getClassObjectInterface().getClassObjectType() );
         case MySym.LEFTPAR :
            lexer.next();
            Expr anExpr = expr();
            if ( lexer.token != MySym.RIGHTPAR )
               errorMsg.show(") expected");
            else
               lexer.next();
            return new ParenthesisExpr(anExpr);
            
         case MySym.SUPER :
            lexer.next();
            if ( lexer.token != MySym.PERIOD )
               errorMsg.show(". expected");
            else
               lexer.next();
            if ( lexer.token != MySym.IDENT ) {
                errorMsg.show("Identifier expected");
                return new InvalidExpr();
            }
            else {
                ident = lexer.ident;
                lexer.next();
                if ( lexer.token != MySym.LEFTPAR )
                   errorMsg.show("( expected");
                else
                   lexer.next();
                anExprList = exprList();
                if ( lexer.token != MySym.RIGHTPAR )
                   errorMsg.show(") expected");
                else
                   lexer.next();
                if ( inClassObject ) 
                   errorMsg.show("super used in a class object");
                ClassTypeInterface cti = currentClassInterface.getSuperclass();
                if ( cti == null )
                   errorMsg.show("Current class does not have a superclass");
                if ( (mi = cti.subclassPublicSearch( 
                         TypeInterface.buildMethodSearchName(ident, anExprList) ) ) == null ) {
                   Hashtable v = cti.multipleSubclassPublicSearch( 
                      TypeInterface.buildAnySearchName(ident, anExprList) );
                   if ( v.size() == 0 ) {
                    
                     cti.subclassPublicSearch( 
                         TypeInterface.buildMethodSearchName(ident, anExprList) );
                     cti.multipleSubclassPublicSearch( 
                      TypeInterface.buildAnySearchName(ident, anExprList) );
                      
                      errorMsg.show("Method " + ident + " not found");
                      return null;
                   }
                   else if ( v.size() > 1 ) {
                      errorMsg.show("Ambiguous call to method " + ident);
                   }
                   Enumeration g = v.elements();
                   mi = (MethodInterface ) g.nextElement();
                   if ( mi == null ) 
                      errorMsg.show("Internal error at primaryExpr");
                }
                checkParameterPassing(mi, anExprList);
                return new MessageSendToSuperExpr( mi, anExprList );
            }

         default :
            Expr left = literalConstExpr();
            if ( left != null )
               lexer.next();
            else {
               BasicType aClass = (BasicType ) basicType();
               if ( aClass != null ) {
                  left = new ClassObjectExpr( 
                               ((ClassTypeInterface ) aClass.getTypeInterface())
                                   .getClassObjectInterface()
                                       .getClassObjectType() );
                  lexer.next();
               }
               else {
                  errorMsg.show("Expression expected");
                  return new InvalidExpr();
               }
            }
            return left;
      }
   }

   private boolean isArithmeticType(Type type) {
      return type == Type.byteType || type == Type.integerType ||
             type == Type.longType || type == Type.realType ||
             type == Type.doubleType;
   }
      
   private void checkArithmeticType(Type type) {
      if ( ! isArithmeticType(type) ) 
         errorMsg.show("Operator is not supported by type " + type.getName());
   }

   private boolean isIntegralType( Type type ) {
      return type == Type.byteType || type == Type.integerType ||
             type == Type.longType;
   }

   private void checkIntegralType( Type type ) {
      if ( ! isIntegralType(type) ) 
         errorMsg.show("Operator is not supported by type " + type.getName());
   }
 
 
   private boolean startExpr( int symbol ) {
      switch ( symbol ) {
         case MySym.TRUE :
         case MySym.FALSE :
         case MySym.BYTECONST :
         case MySym.CHARCONST :
         case MySym.DOUBLECONST :
         case MySym.INTEGERCONST :
         case MySym.LONGCONST :
         case MySym.REALCONST :
         case MySym.BOOLEAN :
         case MySym.BYTE :
         case MySym.CHAR :
         case MySym.INTEGER :
         case MySym.LONG :
         case MySym.REAL :
         case MySym.DOUBLE :
         case MySym.EXCEPTION :
         case MySym.ARRAY :
         case MySym.LITERALSTRING :
         case MySym.SELF :
         case MySym.RESULT :
         case MySym.NIL :
         case MySym.NOT :
         case MySym.MINUS :
         case MySym.PLUS :
         case MySym.BINNOT :
         case MySym.PLUSPLUS :
         case MySym.MINUSMINUS :
         case MySym.SUPER :
         case MySym.LEFTPAR :
         case MySym.IDENT :
         case MySym.CANCELA :
            return true;
         default :
            return false;
      }
   }

   private ExprList exprList() {
      ExprList anExprList = new ExprList();

      if ( startExpr(lexer.token) ) {
         Expr anExpr = expr();
         anExprList.add(anExpr);
         while ( lexer.token == MySym.COMMA ) {
            lexer.next();
            anExpr = expr();
            anExprList.add(anExpr);
         }
      }
      return anExprList;
   }



   private void assertClause() {
      Expr beforeExpr, afterExpr;
      AssertVariableList list;

        // lexer.token == ASSERT
      lexer.next();

      beforeExpr = afterExpr = null;
      list = null;
      if ( lexer.token == MySym.BEFORE ) {
         lexer.next();
         beforeExpr = expr();
         if ( beforeExpr.getType() != Type.booleanType ) 
            errorMsg.show("boolean expression expected");
         if ( lexer.token != MySym.SEMICOLON )
            errorMsg.show("; expected");
         else
            lexer.next();
      }
      list = assertVarDec();
      Enumeration e = list.elements();
      while ( e.hasMoreElements() ) 
         st.putInLocal( (AssertVariable ) e.nextElement() );
         
      if ( lexer.token == MySym.AFTER ) {
         lexer.next();
         canUseResult = true;
         afterExpr = expr();
         currentMethod.setHasAssertAfter(true);
         canUseResult = false;
         if ( afterExpr.getType() != Type.booleanType ) 
            errorMsg.show("boolean expression expected");
         if ( lexer.token != MySym.SEMICOLON )
            errorMsg.show("; expected");
         else
            lexer.next();
      }
      if ( lexer.token != MySym.END )
         errorMsg.show("'end' expected");
      else
         lexer.next();
         
      e = list.elements();
      while ( e.hasMoreElements() )
         st.removeLocal( ((AssertVariable ) e.nextElement()).getSearchName() );
         
      currentMethod.setAssertClause(
           new AssertClause(beforeExpr, afterExpr, list) );
   }



   private AssertVariableList assertVarDec() {
      AssertVariableList list = new AssertVariableList();
      String ident;

      while ( lexer.token == MySym.VAR ) {
         lexer.next();
         if ( lexer.token != MySym.IDENT ) {
            errorMsg.show("Identifier expected");
            ident = "nameless";
         }
         else {
            ident = lexer.ident;
            lexer.next();
            if ( st.getInLocal(ident) != null )
               errorMsg.show("Redeclaration of identifier " + ident);
         }
         if ( lexer.token != MySym.COLON )
            errorMsg.show(": expected");
         else
            lexer.next();
         Type t = type();
         if ( lexer.token != MySym.ASSIGN )
            errorMsg.show("= expected");
         else
            lexer.next();
         Expr anExpr = expr();
         if ( anExpr.getType() == null ) 
            errorMsg.show("Assignment from a non-expression");
         if ( ! t.isSupertypeOf( anExpr.getType() ) )
            errorMsg.show("Type error in assignment");
         list.add( new AssertVariable(ident, t, anExpr) );
         if ( lexer.token != MySym.SEMICOLON )
            errorMsg.show("; expected");
         else
            lexer.next();
      }
      return list;
   }


   private void checkParameterPassing( MethodInterface mi, ExprList exprList ) {
    
      ParameterList pl = mi.getParamList();
      if ( pl.getSize() != exprList.getSize() ) 
         errorMsg.show("Wrong number of parameters in call");
      Enumeration enumPar, enumExpr;
      Expr expr;
      Parameter p;
      enumPar = pl.elements();
      enumExpr = exprList.elements();
      while ( enumPar.hasMoreElements() ) {
         p = (Parameter ) enumPar.nextElement();
         expr = (Expr ) enumExpr.nextElement();
         if ( expr.getType() == null )
            errorMsg.show("Attempt to pass a non-expression as parameter");
         if ( ! p.getType().isSupertypeOf(expr.getType()) )
            errorMsg.show("Type error in method call");
      }
   }
   

   private Type basicType() {
      switch ( lexer.token ) {
         case MySym.BOOLEAN :
            return Type.booleanType;
         case MySym.BYTE :
            return Type.byteType;
         case MySym.CHAR :
            return Type.charType;
         case MySym.INTEGER :
            return Type.integerType;
         case MySym.LONG:
            return Type.longType;
         case MySym.REAL :
            return Type.realType;
         case MySym.DOUBLE :
            return Type.doubleType;
         default :
            return null;
      }
   }

   private BlockStatement blockStat() {
        // lexer.token == MySym.BEGIN
      lexer.next();
      StatementList list = statementList();
      if ( lexer.token != MySym.END )
         errorMsg.show("'end' expected");
      else
         lexer.next();
      return new BlockStatement(list);
   }


   private BreakStatement breakStat() {
        // lexer.token == MySym.BREAK
      if ( loopNesting < 1 )
         errorMsg.show("break outside a loop-end statement");
      lexer.next();
      return new BreakStatement();
   }


   private CaseStatement caseStat() {
      OtherwiseCase anOtherwise;
      Vector eachCaseList = new Vector();
      EachCase aCase;

         // lexer.token == CASE
      lexer.next();
      Expr anExpr = expr();
      Type shouldBeType = anExpr.getType();
         
      if ( shouldBeType != Type.charType && shouldBeType != Type.booleanType && 
           shouldBeType != Type.byteType && shouldBeType != Type.integerType &&
           shouldBeType != Type.longType && 
           ! shouldBeType.getTypeInterface().isSupertypeOf(Type.anyClassTypeInterface) )
           
         errorMsg.show("Wrong type for a case expression");
      
      if ( lexer.token != MySym.OF )
         errorMsg.show("'of' expected");
      else
         lexer.next();
      while ( lexer.token != MySym.OTHERWISE &&
              lexer.token != MySym.END ) {
         aCase = eachCase(shouldBeType);
         eachCaseList.addElement( aCase );
      }
      anOtherwise = null;
      if ( lexer.token == MySym.OTHERWISE )  {
         Statement s = unStatBlock();
         anOtherwise = new OtherwiseCase(s);
      }
      if ( lexer.token != MySym.END )
         errorMsg.show("'end' expected");
      else
         lexer.next();
      return new CaseStatement(anExpr, eachCaseList, anOtherwise);
   }




   private ClassType parse_class() {
      TypeList aTypeList = null;
      String ident;

      if ( lexer.token != MySym.IDENT ) {
         errorMsg.show("Identifier expected");
         ident = lexer.ident;
      }
      else {
         ident = lexer.ident;
         lexer.next();
      }
      if ( lexer.token == MySym.LEFTPAR ) {
         lexer.next();
         if ( lexer.token == MySym.EXCEPTION )
            errorMsg.show("Probably exception clause put after a method return type");
         aTypeList = typeList();
         if ( lexer.token != MySym.RIGHTPAR )
             errorMsg.show(") expected");
         else
            lexer.next();
      }

      TypeInterface ti;
      ClassType aClass;
      aClass = new ClassType(ident, aTypeList);

      aClass.setRealJavaName();
      if ( (ti = til.get( aClass.getInterfaceName() )) == null )
         errorMsg.show("Class " + aClass.getInterfaceName() + " was not found");
      aClass.setTypeInterface(ti);
      return aClass;
      
   }

   private void addClassIdentToST() {
      Enumeration e = currentClassInterface.getInstanceVariableList().elements();
      while ( e.hasMoreElements() ) 
         st.putInClass( (InstanceVariable ) e.nextElement() );
   }
   
   private void addClassObjectIdentToST() {
      Enumeration e = currentClassObjectInterface.getClassObjectVariableList().elements();
      while ( e.hasMoreElements() ) 
         st.putInClass( (ClassObjectVariable ) e.nextElement() );
         
      e = currentClassObjectInterface.getPublicConstList().elements();
      while ( e.hasMoreElements() ) 
         st.putInClass( (Constant ) e.nextElement() );
      e = currentClassObjectInterface.getPrivateConstList().elements();
      while ( e.hasMoreElements() ) 
         st.putInClass( (Constant ) e.nextElement() );
      
   }

   public GenericClass parse() {

      st = new SymbolTable();
      try {
         classDec();
         if ( lexer.token != MySym.EOF )
            errorMsg.show("End of file expected");
      } catch ( CompilerException e ) {
         return null;
      }
      return currentClass;
   }



   public boolean checkReturnType( ClassTypeInterface superclass, Method m ) {
    
      MethodInterface mi = m.getMethodInterface();
      MethodInterface other = superclass.subclassPublicSearch(mi.getSearchName());
      boolean ok = true;
      if ( other != null ) {
         Type otherReturnType = other.getReturnType();
         Type miReturnType = mi.getReturnType();
         if ( otherReturnType == null && miReturnType != null )
            ok = false;
         if ( otherReturnType != null && miReturnType == null )
            ok = false;
         if ( otherReturnType != null && miReturnType != null ) 
            if ( otherReturnType.getName().compareTo(miReturnType.getName()) != 0 )
               ok = false;
         m.setIsRedefinition(true);
      }
      return ok;
            
   }




   private void classDec() {
    
      ClassType superclass = null;
      boolean withClassObject = false;
      int currentMethodNumber = 0;
      Method m;
      MethodList methodList;
      boolean acceptOperator = false;
      String className;
      boolean isShellClass = false;

  
  
      lastMethodNumber = 0;
      inClassObject = false;
      currentClassObjectInterface = currentClassInterface.getClassObjectInterface();
      currentClassObject = new ClassObject(currentClassObjectInterface);
      currentClass = new NormalClass(currentClassObject, currentClassInterface);
      currentClassObject.setAssociatedClass( (NormalClass ) currentClass );

      if ( lexer.token == MySym.SHELL ) {
         currentClass.setIsShellClass(true);
         isShellClass = true;
         lexer.next();
      }
      else {

         if ( lexer.token == MySym.OBJECT ) {
            inClassObject = true;
            classObjectDec();
            withClassObject = true;
            inClassObject = false;
         }
         if ( lexer.token == MySym.ABSTRACT ) {
            currentClass.setIsAbstract(true);
            lexer.next();
         }
         if ( lexer.token == MySym.REFLECTIVE ) {
            currentClass.setIsReflective(true);
            lexer.next();
         }
      }

      if ( lexer.token != MySym.CLASS ) {
         if ( ! withClassObject )
            errorMsg.show("'class' expected");
      }
      else {
         lexer.next();
         
         currentInterface = currentClassInterface;
         if ( lexer.token != MySym.IDENT && basicType() == null ) {
            errorMsg.show("Class name expected");
         }
         else
            if ( ! isShellClass )  {
               if ( currentClass.getName() != null &&
                    currentClass.getName().compareTo(lexer.ident) != 0 )
                  errorMsg.show("Class object and class name are not equal");
               if ( TypeInterface.basicTypes.get(lexer.ident) != null ) {
                    // lexer.ident.compareTo("AnyValue") == 0 ) {
                  acceptOperator = true;
                  currentClass.setIsBasicType(true);
                  currentClass.setSuperclass( Type.anyValueType );
               }
            }
         
         lexer.next();
   
         if ( isShellClass ) {
            if ( lexer.token != MySym.LEFTPAR )
               errorMsg.show("( expected");
            else
               lexer.next();
            ClassType attachedType = parse_class();
            /* if ( ! ((ClassTypeInterface ) attachedType.getTypeInterface()).getIsReflective() )
               errorMsg.show("Class " + attachedType.getName() + " should be reflective"); */
            if ( lexer.token != MySym.RIGHTPAR )
               errorMsg.show(") expected");
            else
               lexer.next();
            currentClass.setAttachedType(attachedType);
         }
   
         if ( lexer.token == MySym.SUBCLASSOF ) {
            lexer.next();
            superclass = parse_class();
            currentClass.setSuperclass(superclass);
            /* check if the current clas is not a direct of indirect superclass of superclass */
            ClassTypeInterface cti = (ClassTypeInterface ) superclass.getTypeInterface();
            while ( cti != null ) 
               if ( cti.getName().compareTo( currentClassInterface.getName() ) == 0 )
                  errorMsg.show("Circularity in inheritance");
               else
                  cti = cti.getSuperclass();
         }
         else if ( isShellClass ) 
            currentClass.setSuperclass( currentClass.getAttachedType() );
         else if ( currentClass.getIsReflective() )
            currentClass.setSuperclass(Type.reflectiveAnyClassType);
         
         
         addClassIdentToST();
   
            // init methods
         InitMethodList initMethodList = currentClass.getInitMethodList();
   
         while ( lexer.token == MySym.PROC ) {
            m = methodDec(true, currentMethodNumber++, Method.subclass_v, false);
            m.setVisibility(Method.subclass_v);
            initMethodList.add( (InitMethod ) m );
         }
         currentClass.setInitMethodList(initMethodList);
   
         methodList = currentClass.getPublicMethodList();
         if ( lexer.token == MySym.PUBLIC ) {
            lexer.next();
            if ( lexer.token != MySym.COLON )
               errorMsg.show(": expected");
            else
               lexer.next();
            while ( lexer.token == MySym.PROC ||
                    lexer.token == MySym.ABSTRACT ) {
               m = methodDec(false, currentMethodNumber++, Method.public_v, acceptOperator );
                 // if a method with the same signature has been declared in the superclass,
                 // both should have the same return value
               //if ( superclass != null ) 
               //    checkReturnType(superclass, m );
   
               m.setVisibility(Method.public_v);
               methodList.add(m);
            }
         }
   
         
         currentClass.setPublicMethodList(methodList);
         
         if ( superclass != null ) {
            ClassTypeInterface supercti = (ClassTypeInterface ) superclass.getTypeInterface();
            if ( supercti.getIsAbstract() && ! currentClass.getIsAbstract() ) {
               // check if current class defines all abstract public methods of the superclass
               
               MethodInterfaceList ml = supercti.getPublicMethodList();
               Enumeration e = ml.elements();
               MethodInterface mi, othermi;
               while ( e.hasMoreElements() ) {
                  mi = (MethodInterface ) ml.get( (String ) e.nextElement() );
                  if ( mi.getIsAbstract() ) {
                     if ( (othermi = currentClassInterface.getPublicMethodList().get(mi.getSearchName())) == null )
                        errorMsg.show("Class should be abstract: inherited abstract method " + 
                            mi.getName() + " was not defined");
                  }
               }
            }
         }
         
         methodList = currentClass.getSubclassMethodList();
         if ( lexer.token == MySym.SUBCLASS) {
            lexer.next();
            if ( lexer.token != MySym.COLON )
               errorMsg.show(": expected");
            else
               lexer.next();
            methodList = new MethodList();
            while ( lexer.token == MySym.PROC ||
                    lexer.token == MySym.ABSTRACT ) {
               m = methodDec(false, currentMethodNumber++, Method.subclass_v, false);
               //if ( superclass != null ) 
               //    checkReturnType(superclass, m );
                   
               m.setVisibility(Method.subclass_v);
               methodList.add(m);
            }
         }
         currentClass.setSubclassMethodList(methodList);
         
         if ( superclass != null ) {
            ClassTypeInterface supercti = (ClassTypeInterface ) superclass.getTypeInterface();
            if ( supercti.getIsAbstract() && ! currentClass.getIsAbstract() ) {
               // check if current class defines all abstract public methods of the superclass
               
               Enumeration e = supercti.getSubclassMethodList().elements();
               MethodInterface mi, othermi;
               while ( e.hasMoreElements() ) {
                  mi = (MethodInterface ) e.nextElement();
                  if ( mi.getIsAbstract() )
                     if ( (othermi = currentClassInterface
                             .getSubclassMethodList().get(mi.getSearchName())) == null ||
                           ! othermi.getIsAbstract() )
                        errorMsg.show("Class should be abstract: inherited abstract method " + 
                            mi.getName() + " was not defined");
               }
            }
         }
         
         
         methodList = currentClass.getPrivateMethodList();
         InstanceVariableList instVarList = currentClass.getInstVarList();
         if ( lexer.token == MySym.PRIVATE) {
            lexer.next();
            if ( lexer.token != MySym.COLON )
               errorMsg.show(": expected");
            else
               lexer.next();
            int currentVariableNumber = 0;
            while ( lexer.token == MySym.PROC ||
                    lexer.token == MySym.ABSTRACT ||
                    lexer.token == MySym.VAR )
               if ( lexer.token == MySym.VAR )
                  currentVariableNumber = instVarDecList(instVarList, currentVariableNumber);
               else {
                  if ( lexer.token == MySym.ABSTRACT ) {
                     errorMsg.show("Private methods cannot be abstract");
                     lexer.next();
                  }
                  m = methodDec(false, currentMethodNumber++, Method.subclass_v, false);
                  m.setVisibility(Method.subclass_v);
                  methodList.add(m);
               }
   
         }
         currentClass.setInstVarList(instVarList);
         currentClass.setPrivateMethodList(methodList);
         
         String javaCode = javaCodeOp();
         if ( javaCode != null )
            currentClass.addJavaCode(javaCode);
   
         if ( lexer.token != MySym.END )
            errorMsg.show("'end' expected");
         else
            lexer.next();
      }

      className = currentClass.getName();
      if ( className.compareTo("Any") == 0 ||
           className.compareTo("AnyValue") == 0 )
         currentClass.setSuperclass(null);
      else if ( className.compareTo("AnyClass") == 0 )
         currentClass.setSuperclass(Type.anyType);
      else if ( currentClass.getSuperclass() == null )
         currentClass.setSuperclass(Type.anyClassType);


      currentInterface = currentClassInterface;
      methodList = currentClass.getPublicMethodList();
      MyLexer normalLexer = lexer;
      lexer = new MyLexer( currentClassInterface.getToAddTextPublic(), errorMsg);
      while ( lexer.token == MySym.PROC ||
              lexer.token == MySym.ABSTRACT ) {
         m = methodDec(false, currentMethodNumber++, Method.public_v, acceptOperator);
         m.setVisibility(Method.public_v);
         methodList.add(m);
      }
      lexer = normalLexer;
         
      currentInterface = currentClassObjectInterface;
      methodList = currentClassObject.getPublicMethodList();
      inClassObject = true;

      currentMethodNumber = lastMethodNumber;
      normalLexer = lexer;
      lexer = new MyLexer(currentInterface.getToAddTextPublic(), errorMsg);
      while ( lexer.token == MySym.PROC ) {
         m = methodDec(false, currentMethodNumber++, Method.public_v, false);
         m.setVisibility(Method.public_v);
         methodList.add(m);
      }
      lexer = normalLexer;
      

      
   }
   

   private void classObjectDec() {
         // lexer.token == MySym.OBJECT
      lexer.next();

      Method m;
      boolean inError;
      String ident;
      int currentMethodNumber = 0;
      MethodList methodList;
      ConstList constList;


      currentInterface = currentClassInterface.getClassObjectInterface();
      
      if ( lexer.token != MySym.IDENT && basicType() == null ) {
         errorMsg.show("Class object name expected");
         ident = "Nameless";
      }
      else {
         ident = lexer.ident;
         lexer.next();
      }
      if ( ident.compareTo(currentInterface.getName()) != 0 ) 
         errorMsg.show("Class and file name are different");

      addClassObjectIdentToST();
      
      if ( lexer.token == MySym.PROC ) {
         m = methodDec(true, currentMethodNumber++, Method.private_v, false);
         currentClassObject.setInitMethod( (InitMethod ) m);
         m.setVisibility(Method.private_v);
      }

      
      methodList = currentClassObject.getPublicMethodList();
      constList = currentClassObject.getPublicConstList();
      
      if ( lexer.token == MySym.PUBLIC ) {
         lexer.next();
         if ( lexer.token != MySym.COLON )
            errorMsg.show(": expected");
         else
            lexer.next();
         inError = false;
         do {
            switch ( lexer.token ) {
               case MySym.CONST :
                  constDec(constList, Method.public_v);
                  break;
               case MySym.ENUM :
                  enumDec(constList, Method.public_v);
                  break;
               case MySym.PROC :
               case MySym.ABSTRACT :
                  m = methodDec(false, currentMethodNumber++, Method.public_v, false);
                  m.setVisibility(Method.public_v);
                  methodList.add(m);
                  break;
               default :
                  inError = true;
            }
         } while ( lexer.token == MySym.PROC ||
                   lexer.token == MySym.CONST ||
                   lexer.token == MySym.ABSTRACT ||
                   lexer.token == MySym.ENUM  );

      }
      currentClassObject.setPublicConstList(constList);
      currentClassObject.setPublicMethodList(methodList);
      

      ClassObjectVariableList   varList;

      methodList = currentClassObject.getPrivateMethodList();
      constList = currentClassObject.getPrivateConstList();
      varList = currentClassObject.getVarList();
      if ( lexer.token == MySym.PRIVATE ) {
         lexer.next();
         if ( lexer.token != MySym.COLON )
            errorMsg.show(": expected");
         else
            lexer.next();

         inError = false;
         int currentVariableNumber = 0;
         do {
            switch ( lexer.token ) {
               case MySym.VAR :
                  currentVariableNumber = objVarDecList(varList, currentVariableNumber);
                  break;
               case MySym.CONST :
                  constDec(constList, Method.private_v);
                  break;
               case MySym.ENUM :
                  enumDec(constList, Method.private_v);
                  break;
               case MySym.ABSTRACT :
               case MySym.PROC :
                  m = methodDec(false, currentMethodNumber++, Method.public_v, false);
                  m.setVisibility(Method.public_v);
                  methodList.add(m);
                  break;
               case MySym.JAVACODE :
               case MySym.JAVAUSE :
                  String javaCode = javaCodeOp();
                  if ( javaCode != null )
                     currentClassObject.addJavaCode(javaCode);
                  break;
               default :
                  if ( lexer.token != MySym.END ) {
                     errorMsg.show("Instance variable or method declaration expected");
                     inError = true;
                  }
            }
         } while ( ! inError && lexer.token != MySym.END );
      }
      currentClassObject.setPrivateMethodList(methodList);
      currentClassObject.setPrivateConstList(constList);
      currentClassObject.setVarList(varList);
      
      if ( lexer.token != MySym.END )
         errorMsg.show("'end' expected");
      else
         lexer.next();
      lastMethodNumber = currentMethodNumber;
         
   }




   private void constDec( ConstList constList, int visibility ) {
        // lexer.token == MySym.CONST
      lexer.next();
      constList.add( constItem(visibility) );
      while ( lexer.token == MySym.COMMA ) {
         lexer.next();
         constList.add( constItem(visibility) );
      }
      if ( lexer.token != MySym.SEMICOLON )
         errorMsg.show("; expected");
      else
         lexer.next();
   }

   private Constant constItem(int visibility) {
      String ident;
      Type type;

      if ( lexer.token != MySym.IDENT ) {
         errorMsg.show("Identifier expected");
         ident = "nameless";
      }
      else {
         ident = lexer.ident;
         lexer.next();
      }
      type = null;
      if ( lexer.token == MySym.COLON ) {
         lexer.next();
         type = constType();
      }
      if ( lexer.token != MySym.ASSIGN )
         errorMsg.show("= expected");
      else
         lexer.next();
      Expr value = expr();
      Constant c;
      if ( type == null )
         c = new Constant(ident, value.getType(), value, visibility);
      else {
         c = new Constant(ident, type, value, visibility);
         if ( type != value.getType() ) 
            errorMsg.show("Type error in constant declaration");
      }
      return c;
   }





   private Type constType() {
      Type result = basicType();
      if ( result == null ) {
         if ( lexer.ident.compareTo("String") == 0 )
            errorMsg.show("Basic type or String expected");
         else
            lexer.next();
         result = Type.stringType;
      }
      else
         lexer.next();
      return result;
   }


   private LiteralBooleanConst booleanConst() {
      switch ( lexer.token ) {
         case MySym.TRUE :
            return LiteralBooleanConst.booleanTrue;
         case MySym.FALSE :
            return LiteralBooleanConst.booleanFalse;
         default :
            return null;
      }
   }

   private LiteralConstExpr literalConstExpr() {
      switch ( lexer.token ) {
         case MySym.TRUE :
         case MySym.FALSE :
            return booleanConst();
         case MySym.BYTECONST :
            return new LiteralByteConst(lexer.byteValue);
         case MySym.CHARCONST :
            return new LiteralCharConst(lexer.charValue);
         case MySym.DOUBLECONST :
            return new LiteralDoubleConst(lexer.doubleValue);
         case MySym.INTEGERCONST :
            return new LiteralIntegerConst(lexer.integerValue);
         case MySym.LONGCONST :
            return new LiteralLongConst(lexer.longValue);
         case MySym.REALCONST :
            return new LiteralRealConst(lexer.realValue);
         default :
            return null;
      }
   }

   private ConstExprList constExprList(Type shouldBeType) {
      Expr anExpr;
      ConstExprList exprList = new ConstExprList();
      anExpr = expr();
      if ( anExpr.getType() == null )
         errorMsg.show("Expression expected");
      if ( ! shouldBeType.isSupertypeOf(anExpr.getType()) ) 
         errorMsg.show("Type error: type of expression should be subtype of " + shouldBeType.getName());
      exprList.add(anExpr);
      while ( lexer.token == MySym.COMMA ) {
         lexer.next();
         anExpr = expr();
         if ( anExpr.getType() == null )
            errorMsg.show("Expression expected");
         if ( ! shouldBeType.isSupertypeOf(anExpr.getType()) ) 
            errorMsg.show("Type error: type of expression should be subtype of " + shouldBeType.getName());
         exprList.add(anExpr);
      }
      return exprList;
   }


   private EachCase eachCase( Type shouldBeType ) {
      ConstExprList constExprList = constExprList(shouldBeType);
      if ( lexer.token != MySym.COLON )
         errorMsg.show(": expected");
      else
         lexer.next();
      Statement statement = unStatBlock();
      return new EachCase(constExprList, statement);
   }


   private void enumDec( ConstList constList, int visibility ) {
      String ident;
        // lexer.token == MySym.ENUM
      lexer.next();
      if ( lexer.token != MySym.LEFTPAR )
         errorMsg.show("( expected");
      else
         lexer.next();

      int currentEnumValue = 0;

      while ( true ) {
         if ( lexer.token != MySym.IDENT ) {
            errorMsg.show("Identifier expected");
            ident = "nameless";
         }
         else {
            ident = lexer.ident;
            lexer.next();
         }
         if ( lexer.token == MySym.ASSIGN ) {
            lexer.next();
            Expr anExpr = expr();
            if ( anExpr.getType() != Type.integerType ) 
               errorMsg.show("Integer constant expected");
            currentEnumValue = ((Integer ) anExpr.getValue()).intValue();
         }
         Constant c = new Constant(ident, Type.integerType,
                        new LiteralIntegerConst(currentEnumValue++), visibility);
         constList.add(c);
         
         if ( lexer.token == MySym.COMMA )
            lexer.next();
         else if ( lexer.token == MySym.RIGHTPAR ) {
            lexer.next();
            if ( lexer.token != MySym.SEMICOLON )
                errorMsg.show("; expected");
            else
                lexer.next();
            break;
         }
         else {
            errorMsg.show(", or ) expected");
            break;
         }
      }
   }

   private void exceptionClause() {
         // lexer.token == MySym.LEFTPAR
      lexer.next();
      if ( lexer.token != MySym.EXCEPTION )
         errorMsg.show("'exception' expected");
      else
         lexer.next();
      if ( lexer.token != MySym.COLON )
         errorMsg.show(": expected");
      else
         lexer.next();
      Type t = type();
      st.deleteTryList();
      st.putTryType(t);
      if ( lexer.token != MySym.RIGHTPAR )
         errorMsg.show(") expected");
      else
         lexer.next();
      currentMethod.setExceptionType(t);
   }


   private void formalParamDecList( ParameterList methodParamList ) {
      formalParamDec(methodParamList);
      while ( lexer.token == MySym.SEMICOLON ) {
         lexer.next();
         formalParamDec(methodParamList);
      }
   }

   private void formalParamDec( ParameterList methodParamList ) {
      Vector localList = new Vector();
      boolean isVariableNumber;
      Parameter p;

      while ( true ) {
         if ( lexer.token != MySym.IDENT ) {
            errorMsg.show("Identifier expected");
            return;
         }
         else {
            p = new Parameter(lexer.ident);
            methodParamList.add(p);
            localList.addElement(p);
            if ( st.getInLocal(p.getName()) != null ) 
               errorMsg.show("Redeclaration of variable " + p.getName());
            st.putInLocal(p);            
            lexer.next();
         }
         if ( lexer.token == MySym.COMMA )
            lexer.next();
         else
            break;
      }
      if ( lexer.token != MySym.COLON )
         errorMsg.show(": expected");
      else
         lexer.next();
      if ( lexer.token == MySym.THREEPERIOD ) {
         isVariableNumber = true;
         lexer.next();
      }
      else
         isVariableNumber = false;
      Type t = type();
      Enumeration e = localList.elements();
      while ( e.hasMoreElements() )
         ((Parameter ) e.nextElement()).setType(t);

      if ( isVariableNumber )
        if ( localList.size() != 1 )
          errorMsg.show("... used with more than one parameter");
        else
          p.setIsVariableNumber(true);

   }




   private Statement forStat() {
      LocalVariable lv = null;
      boolean declareVariable = false;
     
        // lexer.token == MySym.FOR
      lexer.next();
      if ( lexer.token != MySym.IDENT )
         errorMsg.show("Identifier expected");
      String ident = lexer.ident;
      Variable forVariable;
      Identifier id;
      Type typeVar;
      lexer.next();
      if ( lexer.token == MySym.COLON ) {
         lexer.next();
         Type t = basicType();
         if ( t == null )
            errorMsg.show("Type expected");
         lv = new LocalVariable(ident);
         lv.setType(t);
         st.putInLocal(lv);
         lexer.next();
         declareVariable = true;
      }
      if ( (id = st.getInLocal(ident)) == null ) {
         if ( st.getInClass(ident) != null )
            errorMsg.show("For variable should be a local variable");
         else
            errorMsg.show("Variable " + ident + " was not declared");
         return null;
      }
      else 
         if ( !(id instanceof Variable) ) {
            errorMsg.show("Variable expected in a for statement");
            return null;
         }
         else {
            forVariable = (Variable ) id;
            typeVar = forVariable.getType();
            if ( typeVar != Type.integerType &&
                 typeVar != Type.byteType &&
                 typeVar != Type.longType &&
                 typeVar != Type.charType &&
                 typeVar != Type.booleanType )
               errorMsg.show("Variable " + id.getName() + " should have type char, byte, integer, or long");
         }
      if ( lexer.token != MySym.ASSIGN )
         errorMsg.show("= expected");
      else
         lexer.next();
      Expr exprFrom, exprTo;
      exprFrom = expr();
      if ( exprFrom.getType() != typeVar )
         errorMsg.show( typeVar.getName() + " expression expected");
      if ( lexer.token != MySym.TO )
         errorMsg.show("'to' expected");
      else
         lexer.next();
      exprTo = expr();
      if ( exprTo.getType() != typeVar )
         errorMsg.show( typeVar.getName() + " expression expected");
      if ( lexer.token != MySym.DO )
         errorMsg.show("'do' expected");
      else
         lexer.next();
      Statement s = unStatBlock();
      if ( lv != null )
         st.removeLocal(lv.getName());
      return new ForStatement( forVariable, exprFrom, exprTo, s, declareVariable );
   }


   private Statement ifStat() {
        // lexer.token == MySym.IF
      lexer.next();
      Expr cond;
      cond = expr();
      if ( cond.getType() != Type.booleanType )
         errorMsg.show("boolean expression expected");
         
      if ( lexer.token != MySym.THEN )
         errorMsg.show("'then' expected");
      else
         lexer.next();
      StatementList thenStat, elseStat;
      thenStat = statementList();
      if ( lexer.token == MySym.ELSE ) {
         lexer.next();
         elseStat = statementList();
      }
      else
         elseStat = null;
      if ( lexer.token != MySym.ENDIF )
         errorMsg.show("'endif' expected");
      else
         lexer.next();
      return  new IfStatement(cond, thenStat, elseStat);
   }



   private int instVarDec( InstanceVariableList instVarList,
                           int currentVariableNumber) {
      Vector localList = new Vector();
      InstanceVariable p;
      while ( true ) {
         if ( lexer.token != MySym.IDENT ) {
            errorMsg.show("Identifier expected");
            return currentVariableNumber;
         }
         else {
            p = new InstanceVariable(lexer.ident, currentVariableNumber++);
            instVarList.add(p);
            localList.addElement(p);
            lexer.next();
         }
         if ( lexer.token == MySym.COMMA )
            lexer.next();
         else
            break;
      }
      if ( lexer.token != MySym.COLON )
         errorMsg.show(": expected");
      else
         lexer.next();
      Type t = typeExt();
      if ( lexer.token != MySym.SEMICOLON )
         errorMsg.show("; expected");
      else
         lexer.next();

      Enumeration e = localList.elements();
      while ( e.hasMoreElements() ) {
         p = (InstanceVariable ) e.nextElement();
         p.setType(t);
      }
      return currentVariableNumber;
   }


   private int instVarDecList( InstanceVariableList instVarList,
                                int currentVariableNumber) {
        // lexer.token == MySym.VAR
      lexer.next();
      currentVariableNumber = instVarDec(instVarList, currentVariableNumber);
      while ( lexer.token == MySym.IDENT )
         currentVariableNumber = instVarDec(instVarList, currentVariableNumber);
      return currentVariableNumber;
   }


   private String javaCodeOp() {

      while ( lexer.token == MySym.JAVAUSE ) {
         lexer.next();
         if ( lexer.token != MySym.LITERALSTRING )
            errorMsg.show("String expected");
         else {
            String importString = lexer.stringValue;
            lexer.next();
            if ( inClassObject )
               currentClassObject.addJavaImport(importString);
            else
               currentClass.addJavaImport(importString);
         }
      }
      if ( lexer.token == MySym.JAVACODE ) {
         String s = lexer.javaCodeText;
         lexer.next();
         return s;
      }
      else
         return null;
   }


   private void localDec(LocalVariableList localVariableList) {
        // lexer.token == MySym.VAR
      lexer.next();
      while ( true ) {
         localVarDec(localVariableList);
         if ( lexer.token != MySym.IDENT )
            break;
      }
   }


   private void localVarDec(LocalVariableList localVariableList) {
      LocalVariable localVariable;
      Vector localList = new Vector();
      while ( true )
         if ( lexer.token != MySym.IDENT ) {
            errorMsg.show("Identifier expected");
            break;
         }
         else {
            localVariable = new LocalVariable(lexer.ident);
            localVariableList.add(localVariable);
            localList.addElement(localVariable);
            if ( st.getInLocal(localVariable.getName()) != null )
               errorMsg.show("Variable " + localVariable.getName() + " is been redeclared");
            else
               st.putInLocal(localVariable);
            lexer.next();
            if ( lexer.token == MySym.COMMA )
               lexer.next();
            else
               break;
         }
      if ( lexer.token != MySym.COLON )
         errorMsg.show(": expected");
      else
         lexer.next();
      Type t = typeExt();
      if ( lexer.token != MySym.SEMICOLON )
         errorMsg.show("; expected");
      else
         lexer.next();
      Enumeration e = localList.elements();
      while ( e.hasMoreElements() )
         ((LocalVariable ) e.nextElement()).setType(t);
   }


   private Statement loopStat() {
        // lexer.token == MySym.LOOP
      lexer.next();
      loopNesting++;
      StatementList sl = statementList();
      if ( lexer.token != MySym.END )
         errorMsg.show("'end' expected");
      else
         lexer.next();
      loopNesting--;
      return new LoopStatement(sl);
   }



   private int objVarDecList( ClassObjectVariableList varList,
                              int currentVariableNumber) {
        // lexer.token == MySym.VAR
      lexer.next();
      while ( true ) {
         currentVariableNumber = objVarDec(varList, currentVariableNumber);
         if ( lexer.token != MySym.IDENT )
            break;
      }
      return currentVariableNumber;
   }



   private int objVarDec( ClassObjectVariableList varList,
                          int currentVariableNumber) {
      ClassObjectVariable v;
      Expr anExpr;
      Vector localList = new Vector();

      while ( true )
         if ( lexer.token != MySym.IDENT ) {
            errorMsg.show("Identifier expected");
            return currentVariableNumber;
         }
         else {
            v = new ClassObjectVariable(lexer.ident, currentVariableNumber++);
            localList.addElement(v);
            lexer.next();
            if ( lexer.token == MySym.COMMA )
               lexer.next();
            else
               break;
         }
      if ( lexer.token != MySym.COLON ) {
         errorMsg.show(": expected");
         return currentVariableNumber;
      }
      else
         lexer.next();
      Type t = typeExt();
      if ( lexer.token == MySym.ASSIGN ) {
         if ( localList.size() > 1 ) 
            errorMsg.show("Assignment to more than one variable");
         lexer.next();
         anExpr = expr();
      }
      else
         anExpr = null;
      if ( lexer.token != MySym.SEMICOLON )
         errorMsg.show("; expected");
      else
         lexer.next();
      if ( anExpr != null ) {
         if ( anExpr.getType() == null )
            errorMsg.show("Assignment to a non-expression");
         if ( ! t.isSupertypeOf(anExpr.getType()) )
            errorMsg.show("Type error: variable and expression don't match");
         v.setExpr(anExpr);
      }

      Enumeration e = localList.elements();
      while ( e.hasMoreElements() ) {
          ClassObjectVariable iv = (ClassObjectVariable ) e.nextElement();
          iv.setType(t);
          varList.add(iv);
      }
      return currentVariableNumber;

   }

   private String parse_operator() {
      switch ( lexer.token ) {
         case MySym.PLUSPLUS :
            return "++";
         case MySym.MINUSMINUS :
            return "--";
         case MySym.BINNOT :
            return "~";
         case MySym.PLUS :
            return "+";
         case MySym.MINUS :
            return "-";
         case MySym.MULT :
            return "*";
         case MySym.DIV :
            return "/";
         case MySym.REMAINDER :
            return "%";
         case MySym.BINAND :
            return "&";
         case MySym.BINOR :
            return "|";
         case MySym.BINXOR :
            return "^";
         case MySym.LEFTSHIFT :
            return "<<";
         case MySym.RIGHTSHIFT :
            return ">>";
         case MySym.EQ :
            return "==";
         case MySym.LT :
            return "<";
         case MySym.GT :
            return ">";
         case MySym.LE :
            return "<=";
         case MySym.GE :
            return ">=";
         case MySym.NEQ :
            return "<>";
         case MySym.AND :
            return "and";
         case MySym.OR :
            return "or";
         case MySym.XOR :
            return "xor";
         case MySym.NOT :
            return "not";
         default :
            return null;
      }
   }



   private Method methodDec(
                boolean isInitMethod,
                int currentMethodNumber,
                int visibility,
                boolean acceptOperator
                ) {

      String methodName;
      boolean isAbstract, bodyless;
      boolean isOperator;
      bodyless = false;


      if ( lexer.token == MySym.ABSTRACT ) {
         if ( inClassObject ) 
            errorMsg.show("Class objects cannot have abstract methods");
         else 
            if ( ! currentClass.getIsAbstract() )
               errorMsg.show("An abstract method can belong only to an abstract class");
         isAbstract = true;
         lexer.next();
      }
      else
         isAbstract = false;
      if ( lexer.token != MySym.PROC )
         errorMsg.show("'proc' expected");
      else
         lexer.next();
      if ( lexer.token != MySym.IDENT ) {
           methodName = parse_operator();
           if ( methodName == null ) {
              errorMsg.show("Identifier expected");
              return null;
           }
           bodyless = true;
           isOperator = true;
           if ( ! acceptOperator ) 
              errorMsg.show("Only basic classes (char, byte, ...) can have operators");
           lexer.next();
      }
      else {
         methodName = lexer.ident;
         isOperator = false;
         lexer.next();
      }
      if ( isInitMethod ) {
         currentMethod = new InitMethod(currentMethodNumber);
         if ( methodName.compareTo("init") != 0 )
            errorMsg.show("Methods before the public part should be named 'init'");
      }
      else {
         if ( methodName.compareTo("init") == 0 )
            errorMsg.show("init cannot be a method name");
         currentMethod = new Method(methodName, isAbstract,
                currentMethodNumber);
      }
         
      if ( lexer.token != MySym.LEFTPAR )
         errorMsg.show("( expected");
      else
         lexer.next();
      ParameterList methodParamList = new ParameterList();
      if ( lexer.token != MySym.RIGHTPAR && lexer.token != MySym.COLON
           && lexer.token != MySym.VAR )
         formalParamDecList(methodParamList);
      if ( lexer.token != MySym.RIGHTPAR )
         errorMsg.show(") expected");
      else
         lexer.next();
      if ( isInitMethod && inClassObject && methodParamList.getSize() > 0 )
         errorMsg.show("init method of class objects should not have parameters");
         
      currentMethod.setParamList(methodParamList);

      boolean hadExceptionClause = false;
      if ( lexer.token == MySym.LEFTPAR ) {
         exceptionClause();
         hadExceptionClause = true;
         if ( isInitMethod )
            errorMsg.show("init methods cannot throw exceptions");
      }

      String searchName = TypeInterface.buildMethodSearchName(methodName, methodParamList);
      MethodInterface mi = currentInterface.fullSearch ( searchName );
             
      if ( mi == null ) {
             currentInterface.fullSearch ( 
               TypeInterface.buildMethodSearchName(methodName, methodParamList) );
         errorMsg.show("Internal error: mi == null");
      }
      currentMethod.setMethodInterface(mi);             
     
      if ( currentClassInterface.getIsShellClass()  &&
           currentInterface instanceof ClassTypeInterface &&
           methodName.compareTo("interceptAll") != 0 ) {
         ClassTypeInterface attachedInterface = (ClassTypeInterface ) 
            currentClassInterface.getAttachedType().getTypeInterface();
         MethodInterface ami = attachedInterface.publicSearch(searchName);
         if ( ami == null ) 
            errorMsg.show("Method " + methodName + " was not defined in class " + attachedInterface.getName() );
      }

      if ( lexer.token == MySym.COLON ) {
         lexer.next();
         Type t = type();
         currentMethod.setReturnType(t);
         if ( isInitMethod )
            errorMsg.show("init methods cannot return values");
      }

      if ( ! isInitMethod && visibility != Method.private_v ) 
         if ( inClassObject ) {
            if ( ! checkReturnType( Type.anyClassObjectTypeInterface, currentMethod ) ) 
               errorMsg.show("Method should have the same return type as the same method of AnyClassObject");
         }
         else 
            if ( currentClassInterface.getSuperclass() != null ) {
               if ( ! checkReturnType( currentClassInterface.getSuperclass(), currentMethod ) ) 
                  errorMsg.show("Method should have the same return type as in superclass");
            }
            
      /*
            MethodInterface superMethod = currentClassInterface.getSuperclass().subclassPublicSearch(mi.getSearchName());
            if ( superMethod != null ) {
               if ( superMethod.getVisibility() != visibility )
                  errorMsg.show("Subclass and superclass methods " + methodName + " should be defined both in the public or subclass sections");
               if ( superMethod.getReturnType().getJavaName().compareTo(currentMethod.getJavaName()) != 0 )
                  errorMsg.show("Method " + methodName + " should have the same return type as the method of superclass");
               currentMethod.setIsRedefinition(true);
            }
         }
      */
      
      // currentMethod.setRealJavaName();
      
      currentMethod.setIsOperator(isOperator);

      if ( isOperator ) {
         currentMethod.setBlockStatement(new BlockStatement(null));
      }
      else {
         if ( lexer.token == MySym.ASSERT )
            assertClause();
         if ( ! isAbstract ) {
            LocalVariableList localVariableList = currentMethod.getLocalVariableList();
            if ( lexer.token == MySym.VAR ) {
               localDec(localVariableList);
            }
            // currentMethod.setLocalVariableList(localVariableList);
            if ( lexer.token != MySym.BEGIN )
               errorMsg.show("'begin' expected");
            else {
               loopNesting = 0;
               BlockStatement b = blockStat();
               currentMethod.setBlockStatement(b);
            }
         }
      }
      st.deleteLocalIdent();
      return currentMethod;
   }




   private int relation() {
      switch( lexer.token ) {
         case MySym.EQ :
         case MySym.LT :
         case MySym.GT :
         case MySym.LE :
         case MySym.GE :
         case MySym.NEQ :
            return lexer.token;
         default :
            return -1;
      }
   }

   private Statement repeatStat() {
        // lexer.token == MySym.REPEAT
      lexer.next();
      StatementList sl = statementList();
      if ( lexer.token != MySym.UNTIL )
         errorMsg.show("'until' expected");
      else
         lexer.next();
      Expr anExpr = expr();
      if ( anExpr.getType() != Type.booleanType )
         errorMsg.show("boolean expression expected");
      return new RepeatStatement(sl, anExpr);
   }

   private Statement returnStat() {
        // lexer.token == MySym.RETURN
      lexer.next();
      Expr anExpr;
      if ( startExpr(lexer.token) ) {
         anExpr = expr();
         if ( currentMethod.getReturnType() == null ) 
            errorMsg.show("Method should not return a value");
         else {
            if ( anExpr.getType() == null ) 
               errorMsg.show("Attempt to return a non-expression");
            if ( ! currentMethod.getReturnType().isSupertypeOf(anExpr.getType()) ) {
               errorMsg.show("Type error: return value type is not subtype of the method return type");
            }
         }
      }
      else {
         anExpr = null;
         if ( currentMethod.getReturnType() != null )
            errorMsg.show("Method should return a value");
      }
      return new ReturnStatement(anExpr, currentMethod.getHasAssertAfter() );
   }




   private Statement statement() {
      Statement s;
      switch ( lexer.token ) {
         case MySym.SEMICOLON :
            lexer.next();
            return new NullStatement();
         case MySym.IF :
            return ifStat();
         case MySym.WHILE :
            return whileStat();
         case MySym.RETURN :
            s = returnStat();
            if ( lexer.token != MySym.SEMICOLON )
               errorMsg.show("; expected");
            else
               lexer.next();
            return s;
         case MySym.LOOP :
            return loopStat();
         case MySym.BREAK :
            return breakStat();
         case MySym.REPEAT :
            return repeatStat();
         case MySym.CASE :
            return caseStat();
         case MySym.VAR :
            s = statVarDec();
            if ( lexer.token != MySym.SEMICOLON )
               errorMsg.show("; expected");
            else
               lexer.next();
            return s;
         case MySym.FOR :
            return forStat();
         case MySym.TRY :
            return tryStat();
         case MySym.META :
            return metaStat();
         case MySym.JAVAUSE :
         case MySym.JAVACODE :
            String javaCode = javaCodeOp();
            if ( javaCode != null )
               return new JavaStatement(javaCode);
            else
               return statement();
         default :
            if ( ! startExpr(lexer.token) ) {
               return null;
            }
            else {
               Expr anExpr = expr();
               if ( lexer.token != MySym.SEMICOLON )
                  errorMsg.show("; expected");
               else
                  lexer.next();
               return new ExprStatement(anExpr);
            }
      }
   }


   private StatementList statementList() {
      StatementList sl = new StatementList();
      while ( true ) {
         Statement s = statement();
         if ( s == null )
            break;
         else
            sl.add(s);
      }
      return sl;
   }

   private Statement statVarDec() {
        // lexer.token == MySym.VAR
      lexer.next();

      LocalVariable localVariable;
      
      String ident;
      if ( lexer.token != MySym.IDENT ) {
         errorMsg.show("Identifier expected");
         return null;
      }
      else {
         ident = lexer.ident;
         if ( st.getInLocal(ident) != null ) {
            errorMsg.show("Variable " + ident + " is been redeclared");
            return null;
         }
         else {
            localVariable = new LocalVariable(ident);
            st.putInLocal(localVariable);
         }
         lexer.next();
      }
      if ( lexer.token != MySym.COLON )
         errorMsg.show(": expected");
      else
         lexer.next();
      Type t = type();
      localVariable.setType(t);
      Expr anExpr;
      if ( lexer.token == MySym.ASSIGN ) {
          lexer.next();
          anExpr = expr();
          if ( anExpr.getType() == null )
             errorMsg.show("Assignment from a non-expression");
          if ( ! t.isSupertypeOf(anExpr.getType()) )
             errorMsg.show("Type error");
      }
      else
         anExpr = null;
      return new VarDecStatement(localVariable, anExpr);
   }


   private void checkTryType( Type t ) {
    
      TypeInterface ti = t.getTypeInterface();
      Enumeration e = ti.getPublicMethodList().anyElements();
      MethodInterface mi;
      
      while ( e.hasMoreElements() ) {
         mi = (MethodInterface ) e.nextElement();
         if ( mi.getName().compareTo("throw") == 0 ||
              mi.getName().compareTo("raise") == 0 ) {
            if ( mi.getParamList().getSize() != 1 )
               errorMsg.show("throw and raise methods should have one and only one parameter");
            if ( mi.getName().compareTo("throw") == 0 &&
                 mi.getReturnType() != null )
               errorMsg.show("throw methods cannot return values");
         }
      }
   }
   
   private Statement tryStat() {
        // lexer.token == MySym.TRY
      lexer.next();
      if ( lexer.token != MySym.LEFTPAR )
         errorMsg.show("( expected");
      else
         lexer.next();
      Expr anExpr = expr();
      checkTryType(anExpr.getType());
      st.putTryType(anExpr.getType());
      if ( lexer.token != MySym.RIGHTPAR )
         errorMsg.show(") expected");
      else
         lexer.next();
      StatementList sl = statementList();
      if ( lexer.token != MySym.END )
         errorMsg.show("'end' expected");
      else
         lexer.next();
      st.removeTryType();
      return new TryStatement(anExpr, sl);
   }


   private MetaStatement metaStat() {
      ClassType shellType = null;
      
      lexer.next();
      if ( lexer.token != MySym.PERIOD ) 
         errorMsg.show(". expected");
      lexer.next();
      if ( lexer.token != MySym.IDENT )
         errorMsg.show("Identifier expected");
      String methodName = lexer.ident;
      boolean isAttach = methodName.compareTo("attachShell") == 0;
      if ( ! isAttach &&
           methodName.compareTo("removeShell") != 0 )
         errorMsg.show("attachShell or removeShell expected");
      lexer.next();
      if ( lexer.token != MySym.LEFTPAR ) 
         errorMsg.show("( expected");
      lexer.next();
      Expr anExpr = expr();
      if ( ! ( anExpr instanceof VariableExpr ) )
         errorMsg.show("Variable expected");
      Variable variable = ((VariableExpr ) anExpr).getVariable();
      if ( isAttach ) {
         if ( lexer.token != MySym.COMMA )
            errorMsg.show(", expected");
         lexer.next();
         shellType = parse_class();
         if ( ! ((ClassTypeInterface ) shellType.getTypeInterface()).getIsShellClass() )
            errorMsg.show("Shell class expected");
      }
      if ( lexer.token != MySym.RIGHTPAR )
         errorMsg.show(") expected");
      lexer.next();
      if ( lexer.token != MySym.SEMICOLON )
         errorMsg.show("; expected");
      lexer.next();
      if ( isAttach )  
         return new AttachShellStatement(variable, shellType);
      else
         return new RemoveShellStatement(variable);
   }

   private Type type() {
      Type t = basicType();
      if ( t != null ) {
         lexer.next();
         return t;
      }
      else
         switch ( lexer.token ) {
            case MySym.TYPE :
               return typeFun();
            case MySym.ARRAY :
               return arrayType();
            default :
               return parse_class();
         }
   }


   private ArrayType arrayType() {
        // lexer.token == MySym.ARRAY
      lexer.next();
      if ( lexer.token != MySym.LEFTPAR )
         errorMsg.show("( expected");
      else
         lexer.next();
      Type t = type();
      if ( lexer.token != MySym.RIGHTPAR )
         errorMsg.show(") expected");
      else
         lexer.next();
      int n = 0;
      do {
         if ( lexer.token != MySym.LEFTSB )
            errorMsg.show("[ expected");
         else
            lexer.next();
         if ( lexer.token != MySym.RIGHTSB )
            errorMsg.show("] expected");
         else
            lexer.next();
         n++;
      } while ( lexer.token == MySym.LEFTSB );
      if ( n > 1 )
         errorMsg.show("Green currently does not support arrays of more than one dimension");
      ArrayType anArray;
      anArray = new ArrayType(t, n);
      TypeInterface ti;
      if ( (ti = til.get(anArray.getInterfaceName())) == null )
         errorMsg.show("Class " + anArray.getGreenName() + " not found");
      anArray.setTypeInterface(ti);
      return anArray;
   }


   private Type typeFun() {
        // lexer.token == MySym.TYPE
      lexer.next();


      if ( lexer.token != MySym.LEFTPAR )
         errorMsg.show("( expected");
      else
         lexer.next();
         
      Type returnValue = null;
      if ( lexer.token != MySym.IDENT ) {
         returnValue = ((ClassTypeInterface ) type().getTypeInterface())
             .getClassObjectInterface().getClassObjectType();
         errorMsg.show("Identifier expected");
      }
      else {
         Identifier id = st.get(lexer.ident);
         if ( id != null ) {
            if ( id instanceof Variable )
               returnValue = ((Variable ) id).getType();
            else if ( id instanceof Constant )
               returnValue = ((Constant ) id).getType();
            else 
               errorMsg.show("Internal error at typeFun");
            lexer.next();
         }
         else {
            // id == null
            ClassType classType = parse_class();
            returnValue = ((ClassTypeInterface ) classType.getTypeInterface())
                 .getClassObjectInterface().getClassObjectType();
         }
      }
      
      if ( lexer.token != MySym.RIGHTPAR )
         errorMsg.show(") expected");
      else
         lexer.next();
         
      return returnValue;
   }


   private Type typeExt() {
      if ( lexer.token == MySym.AT ) {
         lexer.next();
         Type t = type();
         return new AtType(t);
      }
      else
         return type();
   }
   
   
   private TypeList typeList() {
       TypeList typeList = new TypeList(false);

       while ( true ) {
           Type t = type();
           typeList.add(t);
           if ( lexer.token == MySym.COMMA )
              lexer.next();
           else
              break;
       }
       return typeList;
   }


   private Statement unStatBlock() {
      if ( lexer.token == MySym.BEGIN ) {
         lexer.next();
         StatementList sl = statementList();
         if ( lexer.token != MySym.END )
            errorMsg.show("'end' expected");
         else
            lexer.next();
         return new BlockStatement(sl);
      }
      else {
         Statement s = statement();
         return s;
      }
   }


   private Statement whileStat() {
        // lexer.token == MySym.WHILE
      lexer.next();
      Expr anExpr = expr();
      if ( anExpr.getType() != Type.booleanType )
         errorMsg.show("boolean expression expected");
      if ( lexer.token != MySym.DO )
         errorMsg.show("'do' expected");
      else
         lexer.next();
      Statement s = unStatBlock();
      return new WhileStatement( anExpr, s);
   }


   /*
   private char []newMethodsToClass() {
         // return new methods to be added to class object  plus the cast method
         
      StringWriter outString = new StringWriter();
      PrintWriter out = new PrintWriter( outString );
      
      MethodInterfaceList mil = currentClassInterface.getInitMethodList();
      Enumeration e = mil.elements();
      Parameter p;
      
      while ( e.hasMoreElements() ) {
         MethodInterface mi = mil.get( (String ) e.nextElement());
         if ( mi.getName().compareTo("init") == 0 ) {
            out.println();
            out.print("proc new(");
            Enumeration f = mi.getParamList().elements();
            int size = mi.getParamList().getSize();
            while ( f.hasMoreElements() ) {
               p = (Parameter ) f.nextElement();
               out.print( p.getName() + " : " + p.getType().getName() );
               if ( --size > 0 )
                  out.print("; ");
            }
            out.println(") : " + currentClassInterface.getName());
            out.println("begin");
            out.println("java$begin");
            out.println( currentClassInterface.getJavaName() + " r = new " + 
                         currentClassInterface.getJavaName() + "();" );
            out.print("r.init(");
            f = mi.getParamList().elements();
            size = mi.getParamList().getSize();
            while ( f.hasMoreElements() ) {
               p = (Parameter ) f.nextElement();
               out.print( p.getName() );
               if ( --size > 0 )
                  out.print(", ");
            }
            out.println(");");
            out.println("return r");
            out.println("java$end");
            out.println("end");
         }
      }
      out.println();
      out.println("      proc cast( x : Any )");
      out.println("               ( exception : CatchTypeErrorException ) : " 
           + currentClassInterface.getName() );
      out.println("         begin");
      out.println("         java$begin");
      out.println("         if ( x instanceof " + currentClassInterface.getJavaName() + " )");
      out.println("            return (" + currentClassInterface.getJavaName() + ") x;");
      out.println("         throw new _TypeErrorException();");
      out.println("         return null;");
      out.println("         java$end");
      out.println("         end");
      out.println();
      out.print("  ");
      char []output = outString.toString().toCharArray();
      output[output.length - 1] = '\0';
      return output;

   }
   */
   
   //private char []codeToBeAddedToClass() {
   //}

   
   private char []codeToBeAddedToClassObject() {
         // add methods deepClone, shallowClone, shallowCopy, and shallowEqual
         
      StringWriter outString = new StringWriter();
      PrintWriter out = new PrintWriter( outString );
      

   
      String name = currentInterface.getName();
      String javaName = "co$" + name;
      ClassObjectVariableList covl = currentClassObjectInterface.getClassObjectVariableList();
      Enumeration e;
      ClassObjectVariable cov;
      
      // test if class object has a shallowClone method
      WhiteSpace.printMember(out);
      out.println( "proc shallowClone() : type(" + name + ")" );
      WhiteSpace.printStat(out);
      out.println( "begin" );
      WhiteSpace.printStat(out);
      out.println( "java$begin" );
      WhiteSpace.printStat(out);
      out.println( javaName + " newCopy = new " + javaName + "();" );
      WhiteSpace.printStat(out);
      e = covl.elements();
      while ( e.hasMoreElements() ) {
         cov = (ClassObjectVariable ) e.nextElement();
         WhiteSpace.printStat(out);
         out.println( "newCopy." + cov.getJavaName() + " = " + cov.getJavaName() + ";" );
      }
      WhiteSpace.printStat(out);
      out.println( "return newCopy;" );
      WhiteSpace.printStat(out);
      out.println( "java$end" );
      WhiteSpace.printMember(out);
      out.println( "end" );
   
   
      // test if class object has a shallowCopy method
   
      WhiteSpace.printMember(out);
      out.println( "proc shallowCopy( other : Any ) : boolean" );
      WhiteSpace.printStat(out);
      out.println( "begin" );
      WhiteSpace.printStat(out);
      out.println( "java$begin" );
      WhiteSpace.printStat(out);
      out.println( "if ( _other.getClass() != " + javaName + ".class )" );
      WhiteSpace.printMember(out);
      out.println( "return false;" );
      WhiteSpace.printStat(out);
      out.println( "return true" );
      WhiteSpace.printStat(out);
      out.println( javaName + " from = (" + javaName + " ) _other;" );
      e = covl.elements();
      while ( e.hasMoreElements() ) {
         cov = (ClassObjectVariable ) e.nextElement();
         WhiteSpace.printStat(out);
         out.println( cov.getJavaName() + " = from." + cov.getJavaName() + ";" );
      }
   
      WhiteSpace.printStat(out);
      out.println( "java$end" );
      WhiteSpace.printStat(out);
      out.println( "end" );
   
      // test if class object has a method shallowEqual
   
      WhiteSpace.printMember(out);
      out.println( "proc shallowEqual( other : Any ) : boolean" );
      WhiteSpace.printStat(out);
      out.println( "begin" );
      WhiteSpace.printStat(out);
      out.println( "java$begin" );
      WhiteSpace.printStat(out);
      out.println( "if ( _other.getClass() != " + javaName + ".class )" );
      WhiteSpace.printMember(out);
      out.println( "return false;" );
      WhiteSpace.printStat(out);
      out.println( javaName + " from = (" + javaName + " ) _other;" );
      e = covl.elements();
      WhiteSpace.printStat(out);
      int size = covl.getSize();
      int i = 0;
      out.println( "return" );
      while ( e.hasMoreElements() ) {
         cov = (ClassObjectVariable ) e.nextElement();
         WhiteSpace.printMember(out);
         out.print( cov.getJavaName() + " == from." + cov.getJavaName() );
         if ( ++i < size )
            out.print(" &&");
         out.println();
      }
      WhiteSpace.printStat(out);
      out.println( "java$end" );
      WhiteSpace.printStat(out);
      out.println( "end" );
   
   
      // test if class object has a deepClone method
      WhiteSpace.printMember(out);
      out.println( "proc deepClone() : type(" + javaName + ")" );
      WhiteSpace.printStat(out);
      out.println( "begin" );
      WhiteSpace.printStat(out);
      out.println( "java$begin" );
      WhiteSpace.printStat(out);
      out.println( "return $deepClone( new Hashtable() ); " );
      WhiteSpace.printStat(out);
      out.println( "java$end" );
      WhiteSpace.printStat(out);
      out.println( "end" );
   
   
      WhiteSpace.printMember(out);
      out.println( "java$begin" );
   
      WhiteSpace.printIndent(out);
      out.println( "public " + javaName + " $deepClone( Hashtable t ) {" );
      WhiteSpace.printMember(out);
      out.println( javaName + " newCopy = new " + javaName + "();" );
      e = covl.elements();
      while ( e.hasMoreElements() ) {
         cov = (ClassObjectVariable ) e.nextElement();
         if ( cov.getType() instanceof BasicType )  {
            WhiteSpace.printMember(out);
            out.println( "newCopy." + cov.getJavaName() + " = " + cov.getJavaName() + ";" );
         }
         else {
            WhiteSpace.printMember(out);
            out.println( "if (" );
            out.println( cov.getJavaName() + " == null )" );
            WhiteSpace.printStat(out);
            out.println( "newCopy." + cov.getJavaName() + " = null;" );
            WhiteSpace.printMember(out);
            out.println( "else {" );
            WhiteSpace.printStat(out);
            out.println( "Object obj = t.get(" + cov.getJavaName() + ");" );
            WhiteSpace.printStat(out);
            out.println( "if ( obj == null ) {" );
            WhiteSpace.print(out, 3*WhiteSpace.indent);
            out.println( "newCopy." + cov.getJavaName() + " = " + cov.getJavaName() + ".$deepClone(t);" );
            WhiteSpace.print(out, 3*WhiteSpace.indent);
            out.println( "t.put(" + cov.getJavaName() + ", newCopy." + cov.getJavaName() + ");" );
            WhiteSpace.printStat(out);
            out.println( "}" );
            WhiteSpace.printStat(out);
            out.println( "else" );
            WhiteSpace.print(out, 3*WhiteSpace.indent);
            out.println( "newCopy." + cov.getJavaName() + " = (" + cov.getType().getJavaName() + " ) obj;" );
            WhiteSpace.printMember(out);
            out.println( "}" );
         }
      }
      WhiteSpace.printIndent(out);
      out.println( "}" );
   
      WhiteSpace.printMember(out);
      out.println( "java$end" );
   
      
      out.print("  ");
      char []output = outString.toString().toCharArray();
      output[output.length - 1] = '\0';
      return output;
   }

   
}







