/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *  Compile.java                                       *
 *  Compiler code for AOSD-09 submission               *
 *                                                     *
 *   - Functional Adaptive Programming                 *
 *   - Bryan Chadwick and Karl Lieberherr              *
 *                                                     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * */

using System;

// Import generated code
using lang;

// Import DemeterF Library
using edu.neu.ccs.demeterf.lib;
using edu.neu.ccs.demeterf;
using edu.neu.ccs.demeterf.parallel;

// IO for file input
using System.IO;

// Main Functional Compiler Class
public class Compile{
    // Shorted print method
    static void p(String s){ Console.WriteLine(s); }

    // List of valid options
    static List<String> valid = List<String>.create("seq","par");

    // Main Method
    public static void Main(String[] args){
        if(args.Length < 1){ 
            p(" usage: Compile <CodeFile> [par|seq]");
            return;
        }

        // Read an expression from the given file and:
        //   1) Simplify/Compile/Eval and print results, or
        //   2) Run timed compile par/seq test based on the
        //        second argument
        exp e = main.parse(new FileStream(args[0], FileMode.Open)).e;
        if(args.Length == 1){
            p("\nExp:\n\n"+e);
            p("\nEval: "+e.eval());
            p("\nEval: "+Eval.eval(e));
            e = ConstProp.simplify(e);
            
            p("\nSimpler:\n\n"+e);
            p("\nEval: "+e.eval());
            
            List<Op> code = parcompile(e);
            p("\nCode:\n"+code.ToString("\n","   "));
            
            ExecStack res = OpList.fromList(code).eval();
            p("\nEval: "+(res.top()));
            p("\nStacks: \n"+res.toStr());
            return;
        }
        
        // Which one... par/seq
        int type = valid.index(args[1]);
        // Start time
        DateTime st = DateTime.Now;
        switch(type){
        case 0:seqcompile(e);p("Seq");break;
        case 1:parcompile(e);p("Par");break;
        }
        // Print the total time
        TimeSpan tm = DateTime.Now - st;
        p(""+(tm.Seconds*1000+tm.Milliseconds));
    }
    // Sequential
    static List<Op> seqcompile(exp e){
        return new Traversal(new Ctrl())
            .traverse<List<Op>>(e, List<ident>.create());
    }
    // Parallel
    static List<Op> parcompile(exp e){
        return new ParTraversal(new Ctrl())
            .traverse<List<Op>>(e, List<ident>.create());
    }
}

// Generate code for the Base Language (Arithmetic)
class Math : ID{
    static List<Op> empty = List<Op>.create();
    public static List<Op> one(Op o){ return empty.append(o); }

    List<Op> combine(sub p){ 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);
    }

}
// Generate code for the Stack operations (push/def/undef)
class Stk : Math{
    List<ident> update(def d, def.bodyF f, List<ident> s){ return s.push(d.id); }
    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> b){
        return e
            .append(new Def())
            .append(b)
            .append(new Undef());
    }
}

// Generate code for "ifz" conditional/control ops
class Ctrl : Stk{
    private int lnum = 0;
    ident fresh(String s)
    { lock(this)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));
    }
}


// Expression simplification/Constant propogation 
class ConstProp : Bc{
    class zero : num{ public zero() : base(0) {}
        public override String ToString(){ return "zero"; }}

    num combine(num n, int i)
    { return (i==0) ? new zero() : n; }

    exp combine(bin b, sub p, exp l, zero r){ return l; }
    exp combine(bin b, sub p, num l, zero r){ return l; }
    exp combine(bin b, sub p, num l, num r){ return new num(l.val-r.val); }

    exp combine(ifz f, zero z, exp t, exp e){ return t; }
    exp combine(ifz f, num n, exp t, exp e){ return e; }

    public static exp simplify(exp e){
        return new Traversal(new ConstProp())
            .traverse<exp>(e);
    }
}