import java.io.*;
import java.util.*;

class SymDec {
 
/************************************************************************/
/*             Symbol file reading/writing                              */
/************************************************************************/
  // Collected syntax ---
  // 
  // SymFile    = Header [String (falSy | truSy)]
  //		{Import | Constant | Variable | Type | Procedure} 
  //		TypeList Key.
  // Header     = magic modSy Name.
  // Import     = impSy Name [String] Key.
  // Constant   = conSy Name Literal.
  // Variable   = varSy Name TypeOrd.
  // Type       = typSy Name TypeOrd.
  // Procedure  = prcSy Name [String] [truSy] FormalType.
  // Method     = mthSy Name Byte Byte TypeOrd [String] FormalType.
  // FormalType = [retSy TypeOrd] frmSy {parSy Byte TypeOrd} endFm.
  // TypeOrd    = ordinal.
  // TypeHeader = tDefS Ord [fromS Ord Name].
  // TypeList   = start {Array | Record | Pointer | ProcType | 
  //                     Enum | NamedType} close.
  // Array      = TypeHeader arrSy TypeOrd (Byte | Number | ) endAr.
  // Pointer    = TypeHeader ptrSy TypeOrd.
  // ProcType   = TypeHeader pTpSy FormalType.
  // EventType  = TypeHeader evtSy FormalType.
  // Record     = TypeHeader recSy recAtt [truSy | falSy] [basSy TypeOrd] 
  //	  [iFcSy {basSy TypeOrd}]{Name TypeOrd} {Method} {Statics} endRc.
  //   some extension for interfaces?
  // Statics    = ( Constant | Variable | Procedure ).
  // Enum       = TypeHeader eTpSy { Constant } endRc.
  // NamedType  = TypeHeader.
  // Name	      = namSy byte UTFstring.
//   some extension for class names?
  // Literal    = Number | String | Set | Char | Real | falSy | truSy.
  // Byte       = bytSy byte.
  // String     = strSy UTFstring.
  // Number     = numSy java.lang.long.
  // Real       = fltSy java.lang.double.
  // Set        = setSy java.lang.int.
  // Key        = keySy java.lang.int.
  // Char       = chrSy java.lang.char.
  //
  // Notes on the syntax:
  // All record types must have a Name field, even though this is often
  // redundant.  The issue is that every record type (including those that
  // are anonymous in CP) corresponds to a Java class, and the definer 
  // and the user of the class _must_ agree on the JVM name of the class.
  // The same reasoning applies to procedure types, which must have equal
  // interface names in all modules.
  //

  static final String[] mthAtt = {"", ",NEW", ",ABSTRACT", ",NEW,ABSTRACT",
                                  ",EMPTY", ",NEW,EMPTY",
                                  ",EXTENSIBLE", ",NEW,EXTENSIBLE"};
  static final String[] recAtt = {"RECORD ", "ABSTRACT RECORD ",
                                  "LIMITED RECORD ", "EXTENSIBLE RECORD ",
                                  "INTERFACE RECORD "};
  static final String[] mark = {"", "*", "-", "!"};
  static final String[] varMark = {"", "IN", "OUT", "VAR"};

  private static final String spaces = "         ";
  private static final String recEndSpace = "      "; 
  static final int modSy = (int) 'H';
  static final int namSy = (int) '$';
  static final int bytSy = (int) '\\';
  static final int numSy = (int) '#';
  static final int chrSy = (int) 'c';
  static final int strSy = (int) 's';
  static final int fltSy = (int) 'r';
  static final int falSy = (int) '0';
  static final int truSy = (int) '1';
  static final int impSy = (int) 'I';
  static final int setSy = (int) 'S';
  static final int keySy = (int) 'K';
  static final int conSy = (int) 'C';
  static final int typSy = (int) 'T';
  static final int tDefS = (int) 't';
  static final int prcSy = (int) 'P';
  static final int retSy = (int) 'R';
  static final int mthSy = (int) 'M';
  static final int varSy = (int) 'V';
  static final int parSy = (int) 'p';
  
  static final int start = (int) '&';
  static final int close = (int) '!';
  
  static final int recSy = (int) '{';
  static final int endRc = (int) '}';
  static final int frmSy = (int) '(';
  static final int fromS = (int) '@';
  static final int endFm = (int) ')';
  static final int arrSy = (int) '[';
  static final int endAr = (int) ']';
  static final int pTpSy = (int) '%';
  static final int evtSy = (int) 'v';
  static final int ptrSy = (int) '^';
  static final int basSy = (int) '+';
  static final int iFcSy = (int) '~';
  static final int eTpSy = (int) 'e';
  
  static final int magic    = 0xdeadd0d0;
  static final int sysMagic = 0xd0d0dead;

  static final int prvMode = 0;
  static final int pubMode = 1;
  static final int rdoMode = 2;
  static final int protect = 3;

  private static int sSym = 0;
  private static int acc = 0;
  private static String name;
  private static int iVal;
  private static long lVal;
  private static int tOrd;
  private static char cVal;
  private static double dVal;
  private static DataInputStream in;

// Symbol file reading 

  static Vector imports = new Vector();

  private static int readOrd() throws IOException {
    int b1 = in.readUnsignedByte();
    if (b1 <= 0x7f) { return b1; }
    else { int b2 = in.readByte();
           return b1 - 128 + b2 * 128; }
  }

  private static void GetSym() throws IOException {
    sSym = in.readByte();
    switch (sSym) {
      case namSy : acc = in.readByte();         // fall through 
      case strSy : name = in.readUTF(); break;

      case arrSy :
      case ptrSy :
      case retSy : 
      case fromS : 
      case tDefS : 
      case basSy : tOrd = readOrd(); break;

      case bytSy : iVal = in.readByte(); break;

      case keySy : 
      case setSy : iVal = in.readInt(); break;

      case numSy : lVal = in.readLong(); break;

      case fltSy : dVal = in.readDouble(); break;

      case chrSy : cVal = in.readChar(); break;

      case modSy : 
      case impSy : 
      case conSy : 
      case varSy : 
      case typSy : 
      case prcSy : 
      case mthSy : 
      case parSy : 
      case start : 
      case close : 
      case falSy : 
      case truSy : 
      case frmSy : 
      case endFm : 
      case recSy : 
      case endRc : 
      case endAr : 
      case eTpSy : 
      case iFcSy : 
      case evtSy : 
      case pTpSy : break;
  
      default:  System.out.println("Bad symbol file format, got " + sSym);
                System.exit(1);
    }
  }

  private static void Expect(int expSym) throws IOException {
    if (expSym != sSym) {
      System.out.println("Error in symbol file:  expecting " + 
      String.valueOf((char) expSym) + " got " +
      String.valueOf((char) sSym));
      System.exit(1);
    }
    GetSym();
  }

  private static void Check(int expSym) {
    if (expSym != sSym) {
      System.out.println("Error in symbol file:  expecting " + 
      String.valueOf((char) expSym) + " got " +
      String.valueOf((char) sSym));
      System.exit(1);
    }
  }

  private static void SkipTo(int sym) throws IOException {
    while (sSym != sym) { GetSym(); }
  }

  private static void GetConstant(boolean inRec) throws IOException {
  // Constant = conSy Name Literal.
  // Literal  = Number | String | Set | Char | Real | falSy | truSy.
    Expect(conSy);
    if (inRec) { 
      System.out.print(spaces);
    } else {
      System.out.print("CONST ");
    }
    System.out.print(name + mark[acc] + " = " );
    Expect(namSy); 
    switch (sSym) {
      case numSy : System.out.println(lVal + ";"); break;
      case strSy : System.out.println(name + ";"); break;
      case setSy : System.out.println(iVal + ";"); break;
      case chrSy : System.out.println(cVal + ";"); break;
      case fltSy : System.out.println(dVal + ";"); break;
      case falSy : System.out.println("FALSE;"); break;
      case truSy : System.out.println("TRUE;"); break;
    }
    if (!inRec) { System.out.println(); }
    GetSym();
  }

  private static void GetVar(boolean inRec) throws IOException {
  // Variable = varSy Name TypeOrd.
    Expect(varSy);
    if (inRec) {
      System.out.print(spaces);
    } else {
      System.out.print("VAR ");
    }
    System.out.print(name + mark[acc] + " : T");
    Check(namSy);
    System.out.println(readOrd() + ";");
    GetSym();
  }

  private static void GetType() throws IOException {
  // Type = typSy Name TypeOrd.
    Expect(typSy);
    System.out.print("TYPE " + name + mark[acc] + " = ");
    Check(namSy);
    int tNum = readOrd();
    System.out.println("T" + tNum + ";");
    GetSym();
  }

  private static void GetFormalType() throws IOException {
  // FormalType = [retSy TypeOrd] frmSy {parSy Byte TypeOrd} endFm.
    int retType = 0;
    if (sSym == retSy) { retType = tOrd; GetSym();} 
    Expect(frmSy);
    System.out.print("(");
    while (sSym != endFm) {
      Check(parSy);
      System.out.print(varMark[in.readByte()] + "T" + readOrd());  
      GetSym();
      if (sSym != endFm) { System.out.print(","); }
    }  
    Expect(endFm);
    System.out.print(") ");
    if (retType != 0) { System.out.print(" : T" + retType + " "); }
  }

  private static void GetMethod() throws IOException {
  // Method = mthSy Name Byte Byte TypeOrd [String] FormalType.
    String jName = null;
    Expect(mthSy);
    Check(namSy);
    String nam = name; 
    int pAcc = acc;
    int attr = in.readByte();
    int recMode = in.readByte();  
    System.out.print(spaces + "PROCEDURE (" + varMark[recMode] + "T" + 
                     readOrd() + ") " + nam + mark[acc]);  
    GetSym();
    if (sSym == strSy) { 
      System.out.print(" [" + name + "]"); GetSym();  
    }
    GetFormalType();
    System.out.println(" " + mthAtt[attr]);
  }

  private static void GetProc(boolean inRec) throws IOException {
  // Proc = prcSy Name [String] [truSy] FormalType.
    String jName = null;
    boolean isConstructor = false;
    Expect(prcSy);
    String nam = name; 
    int pAcc = acc;
    Expect(namSy); 
    if (inRec) { System.out.print(spaces); }
    System.out.print("PROCEDURE " + nam + mark[acc]);  
    if (sSym == strSy) { System.out.print(" [" + name + "]"); GetSym();  }
    if (sSym == truSy) { isConstructor = true; GetSym(); } 
    GetFormalType();
    if (isConstructor) { System.out.print(",CONSTRUCTOR"); }
    System.out.println();
  }

  public static void ReadSymbolFile(File symFile) 
                                     throws FileNotFoundException, IOException {
    int magNm;
    int maxInNum = 0;
    FileInputStream fIn = new FileInputStream(symFile);
    in = new DataInputStream(fIn);
    magNm = in.readInt();
    if (magNm == sysMagic) {
        System.out.println(symFile.getName() + " is a SYSTEM symbol file.");
    } else if (magNm != magic) {
        System.out.println(symFile.getName() + " is not a valid symbol file.");
        System.exit(1);
    }
    GetSym();
    Expect(modSy);
    System.out.print("MODULE " + name);
    Expect(namSy);
    if (sSym == strSy) { 
      System.out.print("[" + name + "]");
      GetSym();
      if (sSym == truSy) { 
        System.out.print(" (Interface)"); 
      }
      GetSym();
    }
    System.out.println(); System.out.println();
    if (sSym == numSy) {
      System.out.print("version info: " + (lVal >>> 32)); 
      System.out.print(" : " + (lVal & 0xffffffff)); GetSym();
      System.out.print(" : " + (lVal >>> 32)); 
      System.out.print(" : " + (lVal & 0xffffffff)); GetSym();
      System.out.print(" (" + lVal + ')'); GetSym();
      System.out.println(); System.out.println();
    }
    while (sSym != start) {
      switch (sSym) {
        case impSy : GetSym(); 
                     System.out.print("IMPORT " + name); 
                     imports.addElement(name);
                     GetSym(); 
                     if (sSym == strSy) { 
                       System.out.println("[" + name + "]"); GetSym(); 
                     }
                     Expect(keySy); 
                     break;
        case conSy : GetConstant(false); break;
        case varSy : GetVar(false); break;
        case typSy : GetType(); break;
        case prcSy : GetProc(false); break;
        default    : System.out.println("Unknown symbol: " + sSym); 
                     GetSym();
// System.exit(1);
      }
      System.out.println();
    }
    System.out.println("TYPELIST"); System.out.println();
    Expect(start);
    while (sSym != close) {
      Check(tDefS); 
      int tNum = tOrd; GetSym(); 
      System.out.print("T" + tNum + " = ");
      if (tNum > maxInNum) { maxInNum = tNum; }
      if (sSym == fromS) { 
        int impNum = tOrd;
        GetSym();
        if (impNum != 0) {
          System.out.print((String)imports.elementAt(impNum-1));
          System.out.print(".");
        } 
        System.out.println(name);
        System.out.print("      ");
        GetSym();
      }
      switch (sSym) { 
        case arrSy : System.out.print("ARRAY "); 
                     GetSym();
                     if (sSym == bytSy) { 
                       System.out.print(iVal); GetSym();
                     } else if (sSym == numSy) { 
                       System.out.print(lVal); GetSym();
                     }
                     System.out.println(" OF T" + tOrd); 
                     Expect(endAr);
                     break; 
        case ptrSy : System.out.println("POINTER TO T" + tOrd + ";");
                     GetSym();
                     break; 
        case recSy : int rAtt = in.readByte(); boolean noArgConst = true;
                     boolean valueRec = false;
                     if (rAtt >= 16) { valueRec = true; rAtt -= 16; }
                     if (rAtt >= 8) { noArgConst = false; rAtt -= 8; }
                     if (valueRec) { System.out.print("VALUE "); }
                     System.out.print(recAtt[rAtt]); GetSym();
                     if (!noArgConst) { 
                       System.out.print("[NO noArgConstructor]");
                     }
                     if (sSym == truSy){ 
                       System.out.print("[java interface]"); GetSym();
                     } else if (sSym == falSy) { 
                       System.out.print("[java class]"); GetSym(); }
                     boolean gotBase = false;
                     if (sSym == basSy) { 
                       gotBase = true;
                       System.out.print(" (T" + tOrd ); GetSym();
                     }
                     if (sSym == iFcSy) {
                       GetSym();
                       if (!gotBase) { gotBase = true; System.out.print("("); }
                       while (sSym == basSy) {
                         System.out.print(" + T" + tOrd);
                         GetSym();
                       }
                     }
                     if (gotBase) { System.out.print(")"); }
                     System.out.println();
                     while (sSym == namSy) {
                       System.out.print(spaces + name + mark[acc] + " : " );
                       System.out.println("T" + readOrd() + ";"); 
                       GetSym();
                     } 
                     while ((sSym == mthSy) || (sSym == prcSy) ||
                            (sSym == varSy) || (sSym == conSy)) {
                       switch (sSym) {
                         case mthSy : GetMethod(); break;
                         case prcSy : GetProc(true); break;
                         case varSy : GetVar(true); break;
                         case conSy : GetConstant(true); break;
                       }
                     } 
                     Expect(endRc);
                     System.out.println("      END;");
                     break; 
        case pTpSy : System.out.print("PROCEDURE "); GetSym();
                     GetFormalType(); 
                     break;
        case evtSy : System.out.print("EVENT "); GetSym();
                     GetFormalType(); 
                     break;
        case eTpSy : GetSym();
                     System.out.println("ENUM ");
                     while (sSym == conSy) {
                       GetConstant(true);
                     }
                     Expect(endRc);
                     System.out.println("      END;");
                     break;
        case tDefS :
        case close : System.out.println(); break;
        default : char ch = (char) sSym;
                  System.out.println("UNRECOGNISED TYPE! " + sSym + "  " + ch);
                  System.exit(1);
      }
      System.out.println();
    }
    Expect(close);
    Check(keySy); 
    System.out.println("KEY = " + iVal);
    fIn.close();
  }

  public static void main (String args[]) 
                                throws IOException, FileNotFoundException {
    if (args.length == 0) {
      System.err.println("usage:  symdec <symbolfilename> ");
      System.exit(0);
    }
    String filename = args[0];
    File symFile = new File(System.getProperty("user.dir"),filename); 
    ReadSymbolFile(symFile);
  }

}


