import org.apache.bcel.generic.*;
import org.apache.bcel.Constants;
import java.util.*;
import java.io.*;

/**
 * Simple Byte code assembly language based off of the BCEL.
 * 
 * @author Matthew W. Coan (July 1, 2002) matthewcoan@hotmail.com
 */
public class asm implements InstructionConstants {
   private InstructionList _il;
   private ConstantPoolGen _cp;
   private ClassGen  _cg;
   private MethodGen _mg;
   private LocalVariableGen _lg;
   private String _fileName;
   private String _className;
   private BufferedReader _br;
   private LinkedList _code;
   private LinkedList _lineno;
   private int _linenoIndex;
   private Hashtable _symTbl;
   private Hashtable _gotoTbl;
   private Hashtable _nullTbl;
   
   
   private void _loadCode()
   throws IOException {
      String line;
      int lineno = 0;
      while((line = _br.readLine()) != null) {
         lineno++;
         line = line.trim();
         if(line.length() == 0 || line.charAt(0) == ';')
            continue;
         _code.add(line);
         _lineno.add(new Integer(lineno));
      }
   }
   
   private void _expected(String op, int n, int got) {
      if(n != got)
         _error(op + " expects " + (n-1) + " arguments!");
   }
   
   private String _decodeString(String s) {
      StringBuffer buf = new StringBuffer();
      char ch;
      for(int i = 0; i < s.length(); i++) {
         ch = s.charAt(i);
         if(ch == '\\') {
            if(s.charAt(i+1) == '\\') {
               i++;
               buf.append('\\');
            }
            else {
               i++;
               ch = s.charAt(i);
               switch (ch) {
               case '0':

               case '1':

               case '2':

               case '3':

               case '4':

               case '5':

               case '6':

               case '7':
                   char leadch = ch;
                   int oct = Character.digit(ch, 8);
                   i++;
                   ch = s.charAt(i);
                   if ('0' <= ch && ch <= '7') {
                       oct = oct * 8 + Character.digit(ch, 8);
                       i++;
                       ch = s.charAt(i);
                       if (leadch <= '3' && '0' <= ch && ch <= '7') {
                           oct = oct * 8 + Character.digit(ch, 8);
                       }
                   }
                   buf.append((char) oct);
                   break;

               case 'b':
                   buf.append('\b');
                   break;

               case 't':
                   buf.append('\t');
                   break;

               case 'n':
                   buf.append('\n');
                   break;

               case 'f':
                   buf.append('\f');
                   break;

               case 'r':
                   buf.append('\r');
                   break;

               case '\'':
                   buf.append('\'');
                   break;

               case '\"':
                   buf.append('\"');
                   break;

               case '\\':
                   buf.append('\\');
                   break;

               default:
                   _error("Bad string constant!");
               }
            }
         }
         else
            buf.append((char)ch);
      }
      return buf.toString();
   }

   private LinkedList _tokenizeInstruction(String line) {
      LinkedList l = new LinkedList();
      StringBuffer buf = new StringBuffer();
      char ch;
      boolean inString = false;
      for(int i = 0; i < line.length(); i++) {
         ch = line.charAt(i);
         if(Character.isWhitespace(ch) && !inString) {
            if(buf.length() > 0) {
               l.add(buf.toString());
               buf = new StringBuffer();
            }
         }
         else if(Character.isWhitespace(ch))
            buf.append(ch);
         else
            buf.append(ch);
         if(ch == '\"' && !inString)
            inString = true;
         else if(ch == '\"') {
            if((i-1) >= 0) {
               if(line.charAt(i-1) == '\\')
                  ;
               else
                  inString = false;
            }
         }
      }
      if(buf.length() > 0)
         l.add(buf.toString());
      return l;
   }
   
   private void _codeInstruction(String line) {
      LinkedList data = _tokenizeInstruction(line);
      if(data.size() == 0)
         _error("Instruction must have an opcode!");
      String op = data.get(0).toString();
      String lbl = null;
      InstructionHandle h = null;
      while(op.startsWith("L")) {
         lbl = op;
         h = _il.append(new NOP());
         //_gotoTbl.put(lbl, h);
         data.remove(0);
         if(data.size() == 0) {
            _error("Instruction must have an opcode!");
         }
         op = data.get(0).toString();
      }
      if(op.compareTo("fieldref") == 0) {
         _expected(op, 5, data.size());
         String type = data.get(1).toString();
         String field = data.get(2).toString();
         String sig = data.get(3).toString();
         String name = data.get(4).toString();
         int address = _cp.addFieldref(type, field, sig);
         _symTbl.put(name, new Integer(address));
      }
      else if(op.compareTo("fpush") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         float farg = 0;
         try {
            farg = new Float(arg).floatValue();
         }
         catch(NumberFormatException nfe) {
            _error("fpush expects a float constant argument.");
         }
         h = _il.append(new PUSH(_cp, farg));
      }
      else if(op.compareTo("ipush") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         int iarg = 0;
         try {
            iarg = Integer.parseInt(arg);
         }
         catch(NumberFormatException nfe) {
            _error("ipush expects an integer constant argument.");
         }
         h = _il.append(new PUSH(_cp, iarg));
      }
      else if(op.compareTo("istore") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("istore expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new ISTORE(address.intValue()));
      }
      else if(op.compareTo("fstore") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("fstore expects a variable argument, \""
                   + arg + "\" not found!");
         }
         h = _il.append(new FSTORE(address.intValue()));      
      }
      else if(op.compareTo("astore") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("astore expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new ASTORE(address.intValue()));      }
      else if(op.compareTo("iload") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("iload expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new ILOAD(address.intValue()));     }
      else if(op.compareTo("fload") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("iload expects a variable argument, \""
                   + arg + "\" not found!");
         }
         h = _il.append(new FLOAD(address.intValue()));     }

      else if(op.compareTo("aload") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("aload expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new ALOAD(address.intValue()));      }
      else if(op.compareTo("iadd") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IADD());
      }
      else if(op.compareTo("isub") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new ISUB());
      }
      else if(op.compareTo("imul") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IMUL());
      }
      else if(op.compareTo("idiv") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IDIV());
      }
      else if(op.compareTo("ineg") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new INEG());
      }
      else if(op.compareTo("irem") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IREM());
      }
      else if(op.compareTo("ixor") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IXOR());
      }
      else if(op.compareTo("iand") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IAND());
      }
      else if(op.compareTo("ior") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IOR());
      }
      else if(op.compareTo("i2b") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(I2B);
      }
      else if(op.compareTo("i2c") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new I2C());
      }
      else if(op.compareTo("i2d") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new I2D());
      }
      else if(op.compareTo("i2f") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new I2F());
      }
      else if(op.compareTo("i2l") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new I2L());
      }
      else if(op.compareTo("i2s") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new I2S());
      }
      else if(op.compareTo("d2f") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new D2F());
      }
      else if(op.compareTo("d2i") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new D2I());
      }
      else if(op.compareTo("d2l") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new D2L());
      }
      else if(op.compareTo("dadd") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new DADD());
      }
      else if(op.compareTo("dpush") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         double darg = 0.0;
         try {
            darg = new Double(arg).doubleValue();
         }
         catch(NumberFormatException nfe) {
            _error("dpush expects a double constant argument.");
         }
         h = _il.append(new PUSH(_cp, darg));
      }
      else if(op.compareTo("ddiv") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new DDIV());
      }
      else if(op.compareTo("dstore") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("dstore expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new DSTORE(address.intValue()));      }
      else if(op.compareTo("dload") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("dload expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new DLOAD(address.intValue()));      }
      else if(op.compareTo("dmul") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new DMUL());
      }
      else if(op.compareTo("fmul") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new FMUL());
      }
      else if(op.compareTo("dneg") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new DNEG());
      }
      else if(op.compareTo("drem") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new DREM());
      }
      else if(op.compareTo("dreturn") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new DRETURN());
      }
      else if(op.compareTo("ireturn") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new IRETURN());
      }
      else if(op.compareTo("dsub") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new DSUB());
      }
      else if(op.compareTo("fsub") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new FSUB());
      }
      else if(op.compareTo("f2d") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new F2D());
      }
      else if(op.compareTo("f2i") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new F2I());
      }
      else if(op.compareTo("f2l") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new F2L());
      }
      else if(op.compareTo("fadd") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new FADD());
      }
      else if(op.compareTo("fdiv") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new FDIV());
      }
      else if(op.compareTo("l2d") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new L2D());
      }
      else if(op.compareTo("l2f") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new L2F());
      }
      else if(op.compareTo("l2i") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new L2I());
      }
      else if(op.compareTo("ladd") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LADD());
      }
      else if(op.compareTo("land") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LAND());
      }
      else if(op.compareTo("lpush") == 0) {
         _expected(op, 1, data.size());
         String arg = data.get(1).toString();
         long larg = 0;
         try {
            larg = new Long(arg).longValue();
         }
         catch(NumberFormatException nfe) {
            _error("lpush expects a long constant argument.");
         }
         h = _il.append(new PUSH(_cp, larg));
      }
      else if(op.compareTo("lstore") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("lstore expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new LSTORE(address.intValue()));      }
      else if(op.compareTo("lload") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("lload expects a variable argument, \"" 
                   + arg + "\" not found!");
         }
         h = _il.append(new LLOAD(address.intValue()));      }
      else if(op.compareTo("ldiv") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LDIV());
      }
      else if(op.compareTo("lmul") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LMUL());
      }
      else if(op.compareTo("lneg") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LNEG());
      }
      else if(op.compareTo("lor") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LOR());
      }
      else if(op.compareTo("lrem") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LREM());
      }
      else if(op.compareTo("lreturn") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LRETURN());
      }
      else if(op.compareTo("lsub") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LSUB());
      }
      else if(op.compareTo("lxor") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(new LXOR());
      }
      else if(op.compareTo("spush") == 0) {
         _expected(op, 2, data.size());
         String s = data.get(1).toString();
         if(s.charAt(0) != '\"' || s.charAt(s.length()-1) != '\"')
            _error("Bad string constant!");
         s = _decodeString(s);
         h = _il.append(new PUSH(_cp, s.substring(1,s.length()-1)));
      }
      else if(op.compareTo("getstatic") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         if(address == null) {
            _error("getstatic expects a variable argument.");
         }
         h = _il.append(new GETSTATIC(address.intValue()));      }
      else if(op.compareTo("invokevirtual") == 0) {
         _expected(op, 4, data.size());
         String type = data.get(1).toString();
         String method = data.get(2).toString();
         String sig = data.get(3).toString();
         h = _il.append(new INVOKEVIRTUAL(_cp.addMethodref(type,                                                       method,                                                       sig)));
      }
      else if(op.compareTo("invokespecial") == 0) {
         _expected(op, 4, data.size());
         h = _il.append(new INVOKESPECIAL(_cp.addMethodref(data.get(1).toString(),                                                       data.get(2).toString(),                                                       data.get(3).toString())));
      }
      else if(op.compareTo("invokestatic") == 0) {
         _expected(op, 4, data.size());
         h = _il.append(new INVOKESTATIC(_cp.addMethodref(data.get(1).toString(),                                                       data.get(2).toString(),                                                       data.get(3).toString())));
      }
      else if(op.compareTo("getfield") == 0) {
         _expected(op, 2, data.size());
         Integer i = (Integer)_symTbl.get(data.get(1).toString());
         if(i == null)
            _error("Expected variable name and got: " + data.get(1).toString());
         h = _il.append(new GETFIELD(i.intValue()));
      }
      else if(op.compareTo("putfield") == 0) {
         _expected(op, 2, data.size());
         Integer i = (Integer)_symTbl.get(data.get(1).toString());
         if(i == null)
            _error("Expected variable name and got: " + data.get(1).toString());
         h = _il.append(new PUTFIELD(i.intValue()));
      }
      else if(op.compareTo("return") == 0) {
         _expected(op, 1, data.size());
         h = _il.append(RETURN);
      }
      else if(op.compareTo("new") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         int addr = _cp.addClass(arg);
         h = _il.append(new NEW(addr));
      }
      else if(op.compareTo("this") == 0) {
         h = _il.append(THIS);
      }
      else if(op.compareTo("dup") == 0) {
         h = _il.append(DUP);
      }
      else if(op.compareTo("goto") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            GOTO g = new GOTO(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new GOTO(h));
      }
      else if(op.compareTo("nop") == 0) {
         _il.append(new NOP());
      }
      else if(op.compareTo("iinc") == 0) {
         String arg = data.get(1).toString();
         Integer address = (Integer)_symTbl.get(arg);
         _il.append(new IINC(address.intValue(),1));
      }
      else if(op.compareTo("if_icmplt") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            IF_ICMPLT g = new IF_ICMPLT(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new IF_ICMPLT(h));
      }
      else if(op.compareTo("if_icmpgt") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            IF_ICMPGT g = new IF_ICMPGT(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new IF_ICMPGT(h));
      }
      else if(op.compareTo("if_icmplteq") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            IF_ICMPLE g = new IF_ICMPLE(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new IF_ICMPLE(h));
      }
      else if(op.compareTo("if_icmpgteq") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            IF_ICMPGE g = new IF_ICMPGE(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new IF_ICMPGE(h));
      }
      else if(op.compareTo("if_icmpeq") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            IF_ICMPEQ g = new IF_ICMPEQ(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new IF_ICMPEQ(h));
      }

      else if(op.compareTo("if_icmpneq") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            IF_ICMPNE g = new IF_ICMPNE(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new IF_ICMPNE(h));
      }


      else if(op.compareTo("if_acmpeq") == 0) {
         _expected(op, 2, data.size());
         String arg = data.get(1).toString();
         h = (InstructionHandle)_gotoTbl.get(arg);
         if(h == null) {
            IF_ACMPEQ g = new IF_ACMPEQ(null);
            h = _il.append(g);
            LinkedList l = (LinkedList)_nullTbl.get(arg);
            if(l == null)
               l = new LinkedList();
            l.add(g);
            _nullTbl.put(arg, l);
         }
         else
            h = _il.append(new IF_ACMPEQ(h));
      }
      else if(op.compareTo("aconst_null") == 0) {
         h = _il.append(new ACONST_NULL());
      }
      else {
         _error("Unknown instruction: " + op);
      }
      if(lbl != null && h != null) {
         LinkedList gotoList = (LinkedList)_nullTbl.get(lbl);
         if(gotoList != null) {
            Iterator it = gotoList.iterator();
            BranchInstruction br;
            while(it.hasNext()) {
               br = (BranchInstruction)it.next();
               br.setTarget(h);
            }
         }
         _gotoTbl.put(lbl, h);
      }
   }
   
   private void _codeData(String line) {
      StringTokenizer sTok = new StringTokenizer(line, " ");
      LinkedList data = new LinkedList();
      while(sTok.hasMoreTokens())
         data.add(sTok.nextToken());
      if(data.size() != 2) {
         _error("Data can only be: <type name> <id>");
      }
      String type = data.get(0).toString();
      String name = data.get(1).toString();
      if(_symTbl.get(name) != null)
         _error(name + ": is in already in the symbol table.");
      int address;
      if(type.compareTo("int") == 0)
         _lg = _mg.addLocalVariable(name, Type.INT, null, null);
      else if(type.compareTo("float") == 0)
         _lg = _mg.addLocalVariable(name, Type.FLOAT, null, null);
      else if(type.compareTo("double") == 0)
         _lg = _mg.addLocalVariable(name, Type.DOUBLE, null, null);
      else if(type.compareTo("long") == 0)
         _lg = _mg.addLocalVariable(name, Type.LONG, null, null);
      else if(type.compareTo("short") == 0)
         _lg = _mg.addLocalVariable(name, Type.SHORT, null, null);
      else if(type.compareTo("char") == 0)
         _lg = _mg.addLocalVariable(name, Type.CHAR, null, null);
      else if(type.compareTo("boolean") == 0)
         _lg = _mg.addLocalVariable(name, Type.BOOLEAN, null, null);
      else if(type.compareTo("byte") == 0)
         _lg = _mg.addLocalVariable(name, Type.BYTE, null, null);
      else
         _lg = _mg.addLocalVariable(name, new ObjectType(type), null, null);
      address = _lg.getIndex();
      _symTbl.put(name, new Integer(address));
   }
   
   private void _error(String msg) {
      System.err.println("line number: " + ((Integer)_lineno.get(_linenoIndex)).intValue());
      System.err.println(msg);
      System.exit(1);
   }
   
   private void _loadCodeIntoClass() {
      Iterator p = _code.iterator();
      String line, name, access, mod, sig, args;
      int i, acc, maxStack = 0;
      Type retType, argTypeArray[];
      StringTokenizer sTok;
      LinkedList list;
      String argNameArray[];
      i = 0;
      while(p.hasNext() && i < 5) {
         line = p.next().toString();
         i++;
      }
      MAINLOOP: while(p.hasNext()) {
         line = p.next().toString();
         _linenoIndex++;
         if(line.indexOf(".method ") == 0) {
            _symTbl = new Hashtable();
            _gotoTbl = new Hashtable();
            _nullTbl = new Hashtable();
            i = line.indexOf(' ');
            if(i < 0)
               _error("Expected method name!");
            name = line.substring(i);
            name = name.trim();
            if(!p.hasNext())
               _error("Expected: .maxStack");
            line = p.next().toString();
            _linenoIndex++;
            i = line.indexOf(' ');
            if(i < 0) 
               _error("Expected integer for maxStack method opiton!");
            try {
               maxStack = Integer.parseInt(line.substring(i).trim());
            }
            catch(NumberFormatException nfe) {
               _error(".maxStack must have an integer argument!");
            }
            if(!p.hasNext())
               _error("Expected: .access");
            line = p.next().toString();
            _linenoIndex++;
            i = line.indexOf(' ');
            if(i < 0)
               access = "";
            else
               access = line.substring(i).trim();
            if(!p.hasNext())
               _error("Expected .modifyers");
            line = p.next().toString();
            _linenoIndex++;
            i = line.indexOf(' ');
            if(i < 0)
               mod = "";
            else
               mod = line.substring(i).trim();
            acc = this._getModiyers(access, mod, _METHOD);
            if(!p.hasNext())
               _error("Expected: .signature");
            line = p.next().toString();
            _linenoIndex++;
            i = line.indexOf(' ');
            if(i < 0)
               _error("Expected: method signature");
            sig = line.substring(i).trim();
            retType = Type.getReturnType(sig);
            argTypeArray = Type.getArgumentTypes(sig);
            if(!p.hasNext())
               _error("Expected: .args");
            line = p.next().toString();
            _linenoIndex++;
            i = line.indexOf(' ');
            if(i < 0)
               args = "";
            else 
               args = line.substring(i).trim();
            sTok = new StringTokenizer(args, " ,");
            list = new LinkedList();
            while(sTok.hasMoreTokens())
               list.add(sTok.nextToken());
            argNameArray = new String[list.size()];
            for(i = 0; i < argNameArray.length; i++) {
               argNameArray[i] = list.get(i).toString();
               if((acc & Constants.ACC_STATIC) == Constants.ACC_STATIC)
                  _symTbl.put(argNameArray[i], new Integer(i));
               else
                  _symTbl.put(argNameArray[i], new Integer(i+1));
            }
            if(!((acc & Constants.ACC_STATIC) == Constants.ACC_STATIC))
               _symTbl.put("this", new Integer(0));
            _il = new InstructionList();
            _mg = new MethodGen(acc,
                                retType,
                                argTypeArray, 
                                argNameArray,
                                name, 
                                _className, 
                                _il, 
                                _cp);
            if(!p.hasNext())
               _error("Expected: .data");
            line = p.next().toString();
            _linenoIndex++;
            if(line.compareTo(".data") == 0) {
               DATALOOP: while(p.hasNext()) { // .data
                  line = p.next().toString();
                  _linenoIndex++;
                  if(line.compareTo(".code") == 0) {
                     while(p.hasNext()) { // .code
                        line = p.next().toString();
                        _linenoIndex++;
                        if(line.compareTo(".end") == 0)
                           break DATALOOP;
                        _codeInstruction(line);
                     }
                     break DATALOOP;
                  }
                  else 
                     _codeData(line);
               }
            }
            _mg.setMaxStack(maxStack);
            _cg.addMethod(_mg.getMethod());
         }
         else if(line.indexOf(".field ") == 0) {
            i = line.indexOf(' ');
            if(i < 0)
               _error("Expected filed name!");
            name = line.substring(i).trim();
            if(!p.hasNext())
               _error("Expected .access");
            access = p.next().toString(); // .access
            _linenoIndex++;
            i = access.indexOf(' ');
            if(i < 0)
               access = "";
            else 
               access = access.substring(i).trim();
            if(!p.hasNext())
               _error("Expected .modifyers");
            line = p.next().toString(); // .modifyers 
            _linenoIndex++;
            i = line.indexOf(' ');
            if(i < 0)
               mod = "";
            else
               mod = line.substring(i).trim();
            acc = this._getModiyers(access, mod, _FIELD);
            if(!p.hasNext())
               _error("Expected: .type");
            line = p.next().toString(); // .type
            _linenoIndex++;
            i = line.indexOf(' ');
            if(i < 0)
               _error("Expected a type name!");
            line = line.substring(i).trim();
            FieldGen fg = new FieldGen(acc, Type.getType(line), name, _cp);
            _cg.addField(fg.getField());
         }
         else
            _error("expected .method or .field got: " + line);
      }
   }
   
   private static final int _METHOD = 1;
   private static final int _CONSTRUCTOR = 2;
   private static final int _FIELD = 3;
   private static final int _CLASS = 4;
   
   private int _getModiyers(String acc, String mod, int which) {
      int ret = 0;
      if(acc.compareTo("public") == 0)
         ret |= Constants.ACC_PUBLIC;
      else if(acc.compareTo("private") == 0)
         ret |= Constants.ACC_PRIVATE;
      else if(acc.compareTo("protected") == 0)
         ret |= Constants.ACC_PROTECTED;
      else if(acc.length() == 0)
         ;
      else {
         _error("Bad access code!");
      }
      StringTokenizer sTok = new StringTokenizer(mod, " ");
      while(sTok.hasMoreTokens()) {
         mod = sTok.nextToken();
         if(mod.compareTo("static") == 0)
            ret |= Constants.ACC_STATIC;
         else if(mod.compareTo("abstract") == 0 && which == _METHOD)
            ret |= Constants.ACC_ABSTRACT;
         else if(mod.compareTo("final") == 0)
            ret |= Constants.ACC_FINAL;
         else if(mod.compareTo("native") == 0 && which == _METHOD)
            ret |= Constants.ACC_NATIVE;
         else if(mod.compareTo("super") == 0 && which == _CLASS)
            ret |= Constants.ACC_SUPER;
         else if(mod.compareTo("strict") == 0)
            ret |= Constants.ACC_STRICT;
         else if(mod.compareTo("synchronized") == 0) 
            ret |= Constants.ACC_SYNCHRONIZED;
         else if(mod.compareTo("transient") == 0 && which == _FIELD)
            ret |= Constants.ACC_TRANSIENT;
         else if(mod.compareTo("volatile") == 0 && which == _FIELD)
            ret |= Constants.ACC_VOLATILE;
         else {
            _error("Bad modifyer: \"" + mod + "\"");
         }
      }
      return ret;
   }
   
   public asm(String fileName) 
   throws IOException {
      _fileName = fileName;
      _br = new BufferedReader(new FileReader(_fileName));
      _code = new LinkedList();
      _lineno = new LinkedList();
      _loadCode();
      _linenoIndex = 0;
      if(_code.size() == 0)
         _error("Empty program!");
      
      _className = _code.get(0).toString();
      _linenoIndex++;
      int i = _className.lastIndexOf(' ');
      _className = _className.substring(i+1);
      String access = _code.get(1).toString();
      i = access.indexOf(' ');
      if(i >= 0) {
         access = access.substring(i);
         access = access.trim();
      }
      String modifyers = _code.get(2).toString();
      i = modifyers.indexOf(' ');
      if(i >= 0) {
         modifyers = modifyers.substring(i);
         modifyers = modifyers.trim();
      }
      else 
         modifyers = "";
      int accessFlags = _getModiyers(access, modifyers, _CLASS);
      String ext = _code.get(3).toString();
      i = ext.indexOf(' ');
      _linenoIndex = 3;
      if(i < 0)
         _error("New class must have a base class!");
      ext = ext.substring(i);
      ext = ext.trim();
      String imp = _code.get(4).toString();
      i = imp.indexOf(' ');
      String iArray[] = null;
      if(i >= 0) {
         imp = imp.substring(i);
         imp = imp.trim();
         StringTokenizer sTok = new StringTokenizer(imp, " ");
         LinkedList l = new LinkedList();
         while(sTok.hasMoreTokens())
            l.add(sTok.nextToken());
         iArray = new String[l.size()];
         for(i = 0; i < l.size(); i++) 
            iArray[i] = l.get(i).toString();
      }
      _linenoIndex++;
      
      _cg = new ClassGen(_className, 
                         ext,
                         _fileName, 
                         accessFlags,
                         iArray);
      _cp = _cg.getConstantPool();
      _loadCodeIntoClass();
      //_cg.addEmptyConstructor(Constants.ACC_PUBLIC);
      _il.dispose(); 
      try {
         _cg.getJavaClass().dump(_className + ".class");
      }       catch(java.io.IOException e) {          System.err.println(e.getMessage()); 
         System.exit(1);      }      
   }
   
   public static void main(String args[]) {
      if(args.length != 1) {
         System.err.println("Usage: asm <asm file name>");
         System.exit(1);
      }
      try {
         new asm(args[0]);
      }
      catch(Throwable t) {
         System.err.println("Unable to assemble file!");
         System.err.println("Message: " + t.getMessage());
         t.printStackTrace();
         System.exit(1);
      }
  }
}
