import java.lang.*;
import java.util.*;
import CompilerError.*;

public class MyLexer {
    
    public MyLexer( char []in, ErrorMsg errorMsg ) {
        this.in = in;
        this.errorMsg = errorMsg;
        lineNumber = 1;
        k = 0;
        commentLevel = 0;
        beginCurrentLine = 0;
        initSymbols();
        next();
    }
    
    
    public int token;
    public String ident;
    public String javaCodeText;
    public String stringValue;
    public String charValue;
    public boolean booleanValue;
    public byte byteValue;
    public int integerValue;
    public long longValue;
    public float realValue;
    public double doubleValue;
    private Hashtable keywordsTable;
    private ErrorMsg errorMsg;
    private char []in;
    private int commentLevel;
      // index for the input. in[k] is the next character to be analyzed    
    private int k; 
    public int startToken;
    private int lineNumber;

      // index in input of the beginning of the current line
    private int beginCurrentLine;
    private int startLineComment;
    
    private final static int java_end_size = 8;


        
    public int getCurrentChar() {
       // return the position of the current character in the input "in"
       return k;
    }


    public int getLineNumber() {
       return lineNumber;
    }

    public String getCurrentLine() {
         // return the current line
       int i = beginCurrentLine;
       StringBuffer line = new StringBuffer();
       while ( in[i] != '\0' && in[i] != '\n' && in[i] != '\r' ) 
          line.append( in[i++] );
       return line.toString();
    }
 

    public int getColumnNumber() {
         // return the column of start of the last token 
       int i = startToken;
       while ( i >= 0 && in[i] != '\n' ) 
          i--;
       return startToken - i;
       //r
       /*
       int j = k - i;
       if ( token != MySym.IDENT ) 
          j = j - stringValue.length();
       else
          if ( ident != null ) 
             j = j - ident.length();
       return j;
       */
    }
    

    private void initSymbols() {
       keywordsTable = new Hashtable();
    
       keywordsTable.put("abstract",   new Integer(MySym.ABSTRACT) );
       keywordsTable.put("after",      new Integer(MySym.AFTER) );
       keywordsTable.put("and",        new Integer(MySym.AND) );
       keywordsTable.put("array",      new Integer(MySym.ARRAY) );
       keywordsTable.put("assert",     new Integer(MySym.ASSERT) );
       keywordsTable.put("before",     new Integer(MySym.BEFORE) );
       keywordsTable.put("begin",      new Integer(MySym.BEGIN) );
       keywordsTable.put("boolean",    new Integer(MySym.BOOLEAN) );
       keywordsTable.put("break",      new Integer(MySym.BREAK) );
       keywordsTable.put("byte",       new Integer(MySym.BYTE) );
       keywordsTable.put("case",       new Integer(MySym.CASE) );
       keywordsTable.put("char",       new Integer(MySym.CHAR) );
       keywordsTable.put("class",      new Integer(MySym.CLASS) );
       keywordsTable.put("const",      new Integer(MySym.CONST) );
       keywordsTable.put("do",         new Integer(MySym.DO) );
       keywordsTable.put("double",     new Integer(MySym.DOUBLE) );
       keywordsTable.put("else",       new Integer(MySym.ELSE) );
       keywordsTable.put("end",        new Integer(MySym.END) );
       keywordsTable.put("endif",      new Integer(MySym.ENDIF) );
       keywordsTable.put("enum",       new Integer(MySym.ENUM) );
       keywordsTable.put("exception",  new Integer(MySym.EXCEPTION) );
       keywordsTable.put("false",      new Integer(MySym.FALSE) );
       keywordsTable.put("for",        new Integer(MySym.FOR) );
       keywordsTable.put("if",         new Integer(MySym.IF) );
       keywordsTable.put("integer",    new Integer(MySym.INTEGER) );
       keywordsTable.put("long",       new Integer(MySym.LONG) );
       keywordsTable.put("loop",       new Integer(MySym.LOOP) );
       keywordsTable.put("Meta",       new Integer(MySym.META) );
       keywordsTable.put("nil",        new Integer(MySym.NIL) );
       keywordsTable.put("not",        new Integer(MySym.NOT) );
       keywordsTable.put("object",     new Integer(MySym.OBJECT) );
       keywordsTable.put("of",         new Integer(MySym.OF) );
       keywordsTable.put("or",         new Integer(MySym.OR) );
       keywordsTable.put("otherwise",  new Integer(MySym.OTHERWISE) );
       keywordsTable.put("proc",       new Integer(MySym.PROC) );
       keywordsTable.put("private",    new Integer(MySym.PRIVATE) );
       keywordsTable.put("public",     new Integer(MySym.PUBLIC) );
       keywordsTable.put("real",       new Integer(MySym.REAL) );
       keywordsTable.put("reflective", new Integer(MySym.REFLECTIVE) );
       keywordsTable.put("repeat",     new Integer(MySym.REPEAT) );
       keywordsTable.put("result",     new Integer(MySym.RESULT) );
       keywordsTable.put("return",     new Integer(MySym.RETURN) );
       keywordsTable.put("self",       new Integer(MySym.SELF) );
       keywordsTable.put("shell",      new Integer(MySym.SHELL) );
       //keywordsTable.put("string",     new Integer(MySym.STRING) );
       keywordsTable.put("subclass",   new Integer(MySym.SUBCLASS) );
       keywordsTable.put("subclassOf", new Integer(MySym.SUBCLASSOF) );
       keywordsTable.put("super",      new Integer(MySym.SUPER) );
       keywordsTable.put("then",       new Integer(MySym.THEN) );
       keywordsTable.put("to",         new Integer(MySym.TO) );
       keywordsTable.put("true",       new Integer(MySym.TRUE) );
       keywordsTable.put("try",        new Integer(MySym.TRY) );
       keywordsTable.put("type",       new Integer(MySym.TYPE) );
       keywordsTable.put("until",      new Integer(MySym.UNTIL) );
       keywordsTable.put("var",        new Integer(MySym.VAR) );
       keywordsTable.put("while",      new Integer(MySym.WHILE) );
       keywordsTable.put("xor",        new Integer(MySym.XOR) );

    
       keywordsTable.put("java$use",   new Integer(MySym.JAVAUSE) );
       keywordsTable.put("java$begin", new Integer(MySym.JAVABEGIN) );
       keywordsTable.put("java$end",   new Integer(MySym.JAVAEND) );
    }

    public void next() {
        while ( Character.isWhitespace(in[k]) ) {
           if ( in[k] == '\n' ) {
              lineNumber++;
              beginCurrentLine = k + 1;
           }
           k++;
        }
        if ( in[k] == '\0' ) {
           token = MySym.EOF;
           return;
        }
        
        if ( in[k] == '/' && in[k + 1] == '/' ) {
            // comment till the end of the line
            k += 2;
            while ( in[k] != '\n' && in[k] != '\0' ) 
               k++;
            if ( in[k] == '\n' ) {
               k++;
               lineNumber++;
               beginCurrentLine = k;
            }
            next();
        }
        else 
            if ( in[k] == '/' && in[k + 1] == '*' ) {
                // start comment
                commentLevel = 1;
                int lineStartComment = lineNumber;
                k += 2;
                while ( commentLevel > 0 ) 
                    if ( in[k] == '*' && in[k + 1] == '/' ) {
                        commentLevel--;
                        k += 2;
                    }
                    else if ( in[k] == '/' && in[k + 1] == '*' ) {
                        commentLevel++;
                        k += 2;
                    } else if ( in[k] == '\0' ) {
                        errorMsg.signal("Comment started at line " + lineStartComment + " does not terminate");
                        token = MySym.EOF;
                        return;
                    } else {
                        if ( in[k] == '\n' ) {
                           lineNumber++;
                           beginCurrentLine = k + 1;
                        }
                        k++;
                    }
                next();
            }
            else {
                startToken = k;
                // not a comment
                if ( Character.isLetter(in[k]) || in[k] == '_' || in[k] == '$' ) {
                    StringBuffer s = new StringBuffer();
                    while ( Character.isLetterOrDigit(in[k]) || in[k] == '_' || in[k] == '$' ) {
                       s.append( in[k] );
                       k++;
                    }
                    ident = s.toString();
                    Object v = keywordsTable.get(ident);
                    if ( v == null ) 
                       // identifier
                       token = MySym.IDENT; 
                    else {
                       // keyword
                       token = ((Integer) v).intValue();
                       if ( token == MySym.JAVABEGIN ) {
                          while ( in[k] != '\0' && in[k] != '\n' )
                             k++;
                          if ( in[k] == '\0' )
                             next();
                          else {
                             k++;
                             int startLine = lineNumber;
                             lineNumber++;
                             beginCurrentLine = k;
                             int offset = k;
                             int j;
                                // search for java$end
                             char javaEnd[] = new char[java_end_size];
                             "java$end".getChars(0, java_end_size, javaEnd, 0);
                             j = 0;
                             while ( in[k] != '\0' ) 
                                if ( in[k] != 'j' ) {
                                   if ( in[k] == '\n' ) {
                                      lineNumber++;
                                      beginCurrentLine = k + 1;
                                   }
                                   k++;
                                }
                                else {
                                   while ( j < java_end_size && in[k] == javaEnd[j] ) {
                                      k++;
                                      j++;
                                   }
                                   if ( j == java_end_size ) {
                                      javaCodeText = new String(in, offset, k - offset - java_end_size);
                                      token = MySym.JAVACODE;
                                      return;
                                   }
                                   else
                                      j = 0;
                                }
                             errorMsg.show("java$begin started on line " + startLine + " is not associated to a java$end");
                             next();
                          }
                       }
                    }
                }
                else if ( in[k] >= '0' && in[k] <= '9' ) {
                   try {
                      StringBuffer strnum = new StringBuffer();
                      int state = 1;
                      while ( state != 100 ) 
                          switch ( state ) {
                              case 1 :
                                 while ( in[k] >= '0' && in[k] <= '9' ) {
                                    strnum.append(in[k]);
                                    k++;
                                  }
                                  if ( in[k] == 'b' ) {
                                     k++;
                                     state = 3;
                                  } 
                                  else if ( in[k] == 'L' ) {
                                      k++;
                                      state = 4;
                                  }
                                  else if ( in[k] == 'r' ) {
                                      k++;
                                      state = 5;
                                  }
                                  else if ( in[k] == 'd' ) {
                                      k++;
                                      state = 6;
                                  }
                                  else 
                                      if ( in[k] == '.' ) {
                                         strnum.append('.');
                                         state = 7;
                                      }
                                      else {
                                         if ( in[k] == 'i' ) 
                                            k++;
                                         state = 2;
                                      }
                                  break;
                               case 2 : 
                                  token = MySym.INTEGERCONST;
                                  stringValue = strnum.toString();
                                  integerValue = Integer.valueOf(stringValue).intValue();
                                  state = 100;
                                  break;
                               case 3 : 
                                  token = MySym.BYTECONST;
                                  stringValue = strnum.toString();
                                  byteValue = (byte ) Integer.valueOf(stringValue).intValue();
                                  state = 100;
                                  break;
                               case 4 : 
                                  token = MySym.LONGCONST;
                                  stringValue = strnum.toString();
                                  longValue = Long.valueOf(stringValue).longValue();
                                  state = 100;
                                  break;
                               case 5 : 
                                  token = MySym.REALCONST;
                                  stringValue = strnum.toString();
                                  realValue = Float.valueOf(stringValue).floatValue();
                                  state = 100;
                                  break;
                               case 6 :
                                  token = MySym.DOUBLECONST;
                                  stringValue = strnum.toString();
                                  doubleValue = Double.valueOf(stringValue).doubleValue();
                                  state = 100;
                                  break;
                               case  7 : 
                                  k++;
                                  if ( in[k] < '0' || in[k] > '9' ) {
                                      if ( ! Character.isLetter(in[k]) && in[k] != '_' )
                                         errorMsg.show("Illegal constant: . should be followed by a number");
                                      strnum = new StringBuffer( strnum.toString().substring(0, strnum.length() - 1) );
                                      token = MySym.INTEGERCONST;
                                      stringValue = strnum.toString();
                                      integerValue = Integer.valueOf(stringValue).intValue();
                                      k--;
                                      state = 100;
                                  }
                                  else  
                                      state = 8;
                                  break;
                               case 8 : 
                                  while ( in[k] >= '0' &&  in[k] <= '9' ) {
                                     strnum.append(in[k]);
                                     k++;
                                  }
                                  if ( in[k] == 'E' || in[k] == 'e' ) {
                                      strnum.append(in[k]);
                                      k++;
                                      state = 9;
                                  }
                                  else if ( in[k] == 'd' ) {
                                      k++;
                                      state = 6;
                                  }
                                  else {
                                      if ( in[k] == 'r' )
                                         k++;
                                      state = 5;
                                  }
                                  break;
                               case 9 :
                                  if ( in[k] == '+' || in[k] == '-' ) {
                                      strnum.append(in[k]);
                                      k++;
                                  } 
                                  else if ( in[k] < '0' || in[k] > '9' ) {
                                      errorMsg.show("Illegal constant: E or e should be followed by a number");
                                      token = MySym.REALCONST;
                                      realValue = 1.0F;
                                      stringValue = "1.0";
                                  }
                                  else 
                                     state = 10;
                                  break;
                               case 10 :
                                  while ( in[k] >= '0' &&  in[k] <= '9' ) {
                                      strnum.append(in[k]);
                                      k++;
                                  }
                                  if ( in[k] == 'd' ) {
                                      k++;
                                      state = 6;
                                  }
                                  else {
                                      if ( in[k] == 'r' ) 
                                         k++;
                                      state = 5;
                                  }
                                  break;
                          }
                   }
                   catch ( NumberFormatException e ) {
                      errorMsg.show("Error in converting number");
                   }
                }
                else {
                   switch ( in[k] ) {
                      case '"' : {
                         StringBuffer s = new StringBuffer();
                         k++;
                         while ( in[k] != '\0' && in[k] != '\n' ) 
                            if ( in[k] == '"' ) 
                               break;
                            else 
                               if ( in[k] == '\\' ) {
                                  if ( in[k+1] == '\\' ) {
                                      s.append("\\\\");
                                      k += 2;
                                  }
                                  else 
                                     if ( in[k+1] != '\n' && in[k+1] != '\0' ) {
                                        s.append(in[k]);
                                        k++;
                                        s.append(in[k]);
                                        k++;
                                     }
                               }
                               else {
                                  s.append(in[k]);
                                  k++;
                               }
                            
                         if ( in[k] == '\0' || in[k] == '\n' ) {
                            errorMsg.show("Nonterminated string");
                            stringValue = "";
                         }
                         else {
                            k++;
                            stringValue = s.toString();
                         }
                         token = MySym.LITERALSTRING;
                         }
                         break;
                      case '\'' : {
                         StringBuffer s = new StringBuffer();
                         k++;
                         if( in[k] == '\\' ) 
                            while ( in[k] != '\'' && in[k] != '\n' && in[k] != '\0' ) {
                                s.append(in[k]);
                                k++;
                            }
                         else {
                            s.append(in[k]);
                            k++;
                         }
                         if ( in[k] != '\'' ) {
                            if ( in[k] == '\n' || in[k] == '\0' ) 
                               errorMsg.show("Non-terminated literal character");
                            else 
                               errorMsg.show("\' expected");
                         }
                         else
                            k++;
                         stringValue = s.toString();
                         charValue = stringValue;
                         }
                         token = MySym.CHARCONST;
                         break;
                      
                      case '@' :
                         k++;
                         token = MySym.AT; 
                         break;
                      case '&' :
                         k++;
                         token = MySym.BINAND;
                         break;
                      case '~' :
                         k++;
                         token = MySym.BINNOT;
                         break;
                      case '|' : 
                         k++;
                         token = MySym.BINOR;
                         break;
                      case '^' :
                         k++;
                         token = MySym.BINXOR;
                         break;
                      case '#' :
                         k++;
                         token = MySym.CANCELA;
                         break;
                      case ':' : 
                         k++;
                         token = MySym.COLON;
                         break;
                      case ',' :
                         k++;
                         token = MySym.COMMA;
                         break;
                      case '.' :
                         if ( in[k+1] == '.' ) {
                            token = MySym.THREEPERIOD;
                            if ( in[k+2] == '.' ) {
                                k += 3;
                            }
                            else {
                                errorMsg.show("..  is not a valid Green symbol. \"...\" assumed");
                                k += 2;
                            }
                         }
                         else {
                            token = MySym.PERIOD;
                            k++;
                         }
                         break;
                      case ';' :
                         k++;
                         token = MySym.SEMICOLON;
                         break;
                      case '+' :
                         k++;
                         if ( in[k] == '+' ) {
                            token = MySym.PLUSPLUS;
                            k++;
                         }
                         else
                            token = MySym.PLUS;
                         break;
                      case '-' :
                         k++;
                         if ( in[k] == '-' ) {
                            token = MySym.MINUSMINUS;
                            k++;
                         }
                         else
                            token = MySym.MINUS;
                         break;
                      case '*' : 
                         k++;
                         token = MySym.MULT;
                         break;
                      case '/' :
                         k++;
                         token = MySym.DIV;
                         break;
                      case '%' :
                         k++;
                         token = MySym.REMAINDER;
                         break;
                      case '(' :
                         k++;
                         token = MySym.LEFTPAR;
                         break;
                      case ')' :
                         k++;
                         token = MySym.RIGHTPAR;
                         break;
                      case '[' :
                         k++;
                         token = MySym.LEFTSB;
                         break;
                      case ']' :
                         k++;
                         token = MySym.RIGHTSB;
                         break;
                      case '>' :
                         k++;
                         if ( in[k] == '>' ) {
                            token = MySym.RIGHTSHIFT;
                            k++;
                         }
                         else if ( in[k] == '=' ) {
                            token = MySym.GE;
                            k++;
                         }
                         else 
                            token = MySym.GT;
                         break;
                      case '<' : 
                         k++;
                         if ( in[k] == '<' ) {
                            token = MySym.LEFTSHIFT;
                            k++;
                         }
                         else if ( in[k] == '>' ) {
                            token = MySym.NEQ;
                            k++;
                         }
                         else if ( in[k] == '=' ) {
                            token = MySym.LE;
                            k++;
                         }
                         else 
                            token = MySym.LT;
                         break;
                      case '=' :
                         k++;
                         if ( in[k] == '=' ) {
                            token = MySym.EQ;
                            k++;
                         }
                         else 
                            token = MySym.ASSIGN;
                         break;
                      default : 
                         k++;
                         errorMsg.show("Unundentified character '" + in[k-1] + "'");
                   }
                }
    }
}
}

    
    
    
                         
