/**********************************************
 *  AOSD 10 Submission Files                  *
 *  Compile.java :                            *
 *    Runs performance tests for the compile  *
 *    function                                *
 *                                            *
 *********************************************/

import gen.*;
import edu.neu.ccs.demeterf.lib.*;
import edu.neu.ccs.demeterf.*;
import java.io.FileInputStream;

public class Compile extends Cond{
    public ident combine(ident o){ return o; }
    public int combine(int o){ return o; }

    static void p(String s){ System.out.println(s); }
    public static void main(String[] args) throws Exception{
        String names[] = {"Hand","Vis","Inln","Reflect"};
        if(args.length != 2){
            System.err.println("usage: java Compile <file> <which>");
            return;
        }
        Exp e = Exp.parse(new FileInputStream(args[0]));
        int what = Integer.parseInt(args[1]);
        List<Op> ops = null;
        long st = System.currentTimeMillis();
        switch(what){
        case 0:ops = new Compile().compileHand(e);break;
        case 1:ops = new Compile().compileVis(e);break;
        case 2:ops = new Compile().compileInln(e);break;
        case 3:ops = new Compile().compileRefl(e);break;
        }
        p("Compile["+names[what]+"]: "+(System.currentTimeMillis()-st));
        //p("Result:\n"+ops.toString("\n","   "));
    }

    List<Op> compileHand(Exp e) throws Exception
    { return e.compile(List.<ident>create()); }
    List<Op> compileVis(Exp e) throws Exception
    { return e.accept(new CompEVis(List.<ident>create())); }
    List<Op> compileInln(Exp e) throws Exception
    { return new InlineCompile(this).traverse(e, List.<ident>create()); }
    List<Op> compileRefl(Exp e) throws Exception
    { return new Traversal(this).traverse(e, List.<ident>create()); }
}

class Arith extends ID{
    static List<Op> empty = List.<Op>create();
    static List<Op> one(Op o){ return empty.append(o); }

    List<Op> combine(Sub s){ return one(new Minus()); }
    List<Op> combine(Num n, int i)
    { return one(new Push(i)); }
  
    List<Op> combine(Bin b, List<Op> o, List<Op> l,
                     List<Op> r)
    { return r.append(l).append(o); }
}


class Defs extends Arith{
    List<ident> update(Def d, Def.body f, List<ident> s)
    { return s.push(d.getId()); }
    List<Op> combine(Var v, ident id, List<ident> s)
    { return one(new Load(s.index(id))); }

    List<Op> combine(Def d, ident id, List<Op> e,
                     List<Op> bdy){
        return e.append(new Define()).append(bdy)
            .append(new Undef());
    }
}


class Cond extends Defs{
    int lnum = 0;
    synchronized ident fresh(String s)
    { return new ident(s+"_"+lnum++); }

    List<Op> combine(Ifz f, List<Op> c, List<Op> t,
                     List<Op> e){
        ident le = fresh("else"),
            ld = fresh("done");
        return c.append(new IfNZ(le)).append(t)
            .append(new Jmp(ld))
            .append(new Label(le)).append(e)
            .append(new Label(ld));
    }
}

class CompEVis extends Exp.Vis<List<Op>>{
    static List<Op> empty = List.<Op>create();
    static List<Op> one(Op o){ return empty.append(o); }

    List<ident> env;
    CompEVis(List<ident> ev){ env = ev; }

    public List<Op> visit(Num n){ return one(new Push(n.getVal())); }
  
    public List<Op> visit(Bin b){
        return b.getRight().accept(this)
            .append(b.getLeft().accept(this))
            .append(b.getOp().accept(new Oper.Vis<List<Op>>(){
                        public List<Op> visit(Sub s){
                            return List.<Op>create(new Minus());
                        }
                    }));
    }
    public List<Op> visit(Var v){ return one(new Load(env.index(v.getId()))); }

    public List<Op> visit(Def d){
        return d.getE().accept(this)
            .append(new Define())
            .append(d.getBody().accept(new CompEVis(env.push(d.getId()))))
            .append(new Undef());
    }

    static int lnum = 0;
    synchronized ident fresh(String s){ return new ident(s+"_"+lnum++); }

    public List<Op> visit(Ifz f){
        ident le = fresh("else"),
            ld = fresh("done");
        return f.getCnd().accept(this)
            .append(new IfNZ(le)).append(f.getThn().accept(this))
            .append(new Jmp(ld))
            .append(new Label(le)).append(f.getEls().accept(this))
            .append(new Label(ld));
    }
}