import compile.*;
import asm.*;
import type.*;

import edu.neu.ccs.demeterf.demfgen.lib.*;
import edu.neu.ccs.demeterf.*;
import edu.neu.ccs.demeterf.util.Util;

import java.io.*;

// Main Functional Compiler Class
public class Compile{
    static void p(String s){ System.out.print(s); }
    public static void main(String[] args) throws Exception{
        if(args.length < 1){ 
            p(" usage: Compile <CodeFile>\n");
            System.exit(1);
        }        
        Util.addBuiltIn(ident.class);

        E e = MainC.parse(new FileInputStream(args[0])).e;
        try{
            p("\n    Exp: "+e.print()+"\n");
            p("\n   Type: "+TypeCheck.check(e).print()+"\n");

            OpList code = new Compile().compile(e);
            p("\n   Code:\n"+code.print()+"\n");
            
            ExecStack res = code.eval();
            p("\n   Eval: "+(res.top())+"\n");
            p("\n Stacks: \n"+res+"\n");
        }catch(TypeCheck.TE ex){
            p("\n TypeError: "+ex.getMessage()+"\n\n");
        }
        
    }
    public OpList compile(E e){
        Traversal comp = new Traversal(new Code3());
        return comp.traverse(e, List.<ident>create());
    }    
}

// Generate Code for the Base Language (Arithmetic)
class Code extends ID{
    static OpList empty = new OpEmpty();
    static OpList single(Opcode o){ return empty.append(o); }

    OpList combine(P p){ return single(new Plus()); }
    OpList combine(M m){ return single(new Minus()); }
    OpList combine(T t){ return single(new Times()); }
    OpList combine(D d){ return single(new Divide()); }

    OpList combine(S s, int i){ return single(new Push(i)); }
    OpList combine(C c, OpList op, OpList left, OpList right){
        return right.append(left).append(op);
    }
}

// Generate Code with the Let/de Bruijn Extension
class Code2 extends Code{
    List<ident> update(L let, L.body f, List<ident> s){ return s.push(let.id); }
    OpList combine(V v, ident id, List<ident> s){ return single(new Load(s.index(id))); }
    OpList combine(L let, ident id, OpList e, OpList body){
        return e.append(new Def()).append(body).append(new Undef());
    }
}

// Generate Code for Boolean Functions and If expressions
class Code3 extends Code2{
    private int lnum = 0;
    synchronized int fresh(){ return lnum++; }

    OpList combine(LT l){ return single(new Less()); }
    OpList combine(GT g){ return single(new Greater()); }
    OpList combine(EQ e){ return single(new Equal()); }
    OpList combine(AndF a){ return single(new And()); }
    OpList combine(OrF o){ return single(new Or()); }

    OpList combine(I f, OpList c, OpList t, OpList e){
        ident l1 = new ident("else_"+fresh()),
              l2 = new ident("done_"+fresh());
        return c.append(new IfZ(l1)).append(t)
            .append(new Jmp(l2)).append(new Label(l1))
            .append(e).append(new Label(l2));
    }
}