
package org.objectweb.asm.jbfc;

import java.io.IOException;
import java.io.Reader;
import java.util.Stack;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;


/**
 * A naive implementation of compiler for Brain**** language.
 * http://www.muppetlabs.com/~breadbox/bf/ * 
 *
 * @author Eugene Kuleshov
 */
public class BFCompiler implements Opcodes {
  private static final int V_IS = 0;
  private static final int V_OS = 1;
  private static final int V_P = 2;
  private static final int V_D = 3;

  public void compile( Reader r, String className, String sourceName, ClassVisitor cv) throws IOException {
    cv.visit( Opcodes.V1_3, ACC_PUBLIC, className.replace( '.', '/'), null, "java/lang/Object", null);
    cv.visitSource( sourceName, null);
    
    MethodVisitor mv;
    {
      mv = cv.visitMethod( ACC_PUBLIC, "<init>", "()V", null, null);
      mv.visitCode();
      mv.visitVarInsn(ALOAD, 0);
      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
      mv.visitInsn(RETURN);
      mv.visitMaxs(1, 1);
      mv.visitEnd();
    }
    
    {
      // Init local vars for BF environment:
      //   0 InputStream
      //   1 OutputStream
      //   2 Data Pointer
      //   3 Data Array (int[ 30000])

      mv = cv.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
      mv.visitCode();

      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "in", "Ljava/io/InputStream;");
      mv.visitVarInsn(ASTORE, V_IS);

      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
      mv.visitVarInsn(ASTORE, V_OS);

      mv.visitInsn(ICONST_0);
      mv.visitVarInsn(ISTORE, V_P);

      mv.visitIntInsn(SIPUSH, 30000);
      mv.visitIntInsn(NEWARRAY, T_INT);
      mv.visitVarInsn(ASTORE, V_D);

      Stack labels = new Stack();
      Stack labele = new Stack();
      
      int d = 0;
      int p = 0;
      
      int c;
      while(( c = r.read())!=-1) {
        switch( c) {
          case '>':
            d = storeD( mv, d);
            p++;  break;

          case '<':
            d = storeD( mv, d);
            p--;  break;

          case '+':
            p = storeP( mv, p);
            d++;  break;
          
          case '-':
            p = storeP( mv, p);
            d--;  break;
          
          case '.':
            p = storeP( mv, p);
            d = storeD( mv, d);
            
            mv.visitVarInsn(ALOAD, V_OS);
            mv.visitVarInsn(ALOAD, V_D);
            mv.visitVarInsn(ILOAD, V_P);
            mv.visitInsn(IALOAD);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/OutputStream", "write", "(I)V");
            break;
          
          case ',':
            p = storeP( mv, p);
            d = storeD( mv, d);
            
            mv.visitVarInsn(ALOAD, V_D);
            mv.visitVarInsn(ILOAD, V_P);
            mv.visitVarInsn(ALOAD, V_IS);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/InputStream", "read", "()I");
            mv.visitInsn(IASTORE);
            break;
          
          case '[':
            p = storeP( mv, p);
            d = storeD( mv, d);

            Label ls = new Label();
            Label le = new Label();
            labels.push( ls);
            labele.push( le);            
            mv.visitJumpInsn( GOTO, le);
            mv.visitLabel( ls);
            break;
          
          case ']':
            p = storeP( mv, p);
            d = storeD( mv, d);

            mv.visitLabel(( Label) labele.pop());
            mv.visitVarInsn( ALOAD, V_D);
            mv.visitVarInsn( ILOAD, V_P);
            mv.visitInsn( IALOAD);
            mv.visitJumpInsn( IFNE, ( Label) labels.pop());
            break;
        }
      }
      
      mv.visitInsn(RETURN);

      mv.visitMaxs(1, 1);
      mv.visitEnd();
    }
    
  }

  private int storeD( MethodVisitor mv, int d) {
    if( d!=0) { 
      mv.visitVarInsn( ALOAD, V_D);
      mv.visitVarInsn( ILOAD, V_P);
      mv.visitInsn( DUP2);
      mv.visitInsn( IALOAD);
      mv.visitIntInsn( SIPUSH, d);
      mv.visitInsn( IADD);
      mv.visitInsn( IASTORE);
    }
    return 0;
  }

  private int storeP( MethodVisitor mv, int p) {
    if( p!=0) {
      mv.visitIincInsn( V_P, p);
    }
    return 0;
  }

}


@@ -1918,42 +1918,8 @@
 ();%0A
-      Stack labele = new Stack();%0A
     
@@ -3311,17 +3311,17 @@
    label
-e
+s
 .push( l
@@ -3564,17 +3564,17 @@
 l) label
-e
+s
 .pop());

use single label stack