/* Compiler.java */

import java.io.*;
import java.util.Enumeration;
import java6035.tools.CLI.CLI;
import java6035.tools.ASM.MIPSGenerator;
import IR2.*;

public class Compiler {
  private final static int OPT_CP = 0;
  private final static int OPT_DCE = 1;
  private final static int OPT_SPIM = 2;

  public static void main(String args[]) throws java.io.IOException,Exception {
    String optnames[] = {"cp", "dce", "spim"};
    CLI cli = new CLI(args, optnames);

    if (cli.target == CLI.DEFAULT)
      cli.target = CLI.ASSEMBLY;
    if (cli.target != CLI.SCAN && cli.target != CLI.PARSE && 
        cli.target != CLI.INTER && cli.target != CLI.LOWIR &&
        cli.target != CLI.ASSEMBLY) {
      System.err.println("Available targets: scan, parse, inter, " +
                         "lowir, and assembly.");
      return;
    }

    // Check the extras.
    if (cli.extras.contains("-cp")) {
      cli.extras.removeElement("-cp");
      cli.opts[OPT_CP] = true; 
    }
    if (cli.extras.contains("-dce")) { 
      cli.extras.removeElement("-dce");
      cli.opts[OPT_DCE] = true; 
    }
    if (cli.extras.contains("-all")) {
      cli.extras.removeElement("-all");
      cli.opts[OPT_CP] = cli.opts[OPT_DCE] = true;
    }
    // SPIM override?
    if (cli.extras.contains("-spim")) { 
      cli.extras.removeElement("-spim");
      cli.opts[OPT_SPIM] = true; 
    }
    
    if (cli.extras.size() != 0) {
      for (Enumeration e = cli.extras.elements(); e.hasMoreElements(); ) 
        System.err.println("Unrecognized option: " + e.nextElement());  
    }


    String infilename;
    InputStream in;
    OutputStream out;
    if (cli.infile != null) {
      infilename = cli.infile;
      in = new FileInputStream(infilename);
      if (!cli.outfile.equals("-")) {
        out = new FileOutputStream(cli.outfile);
      } else {
        out = System.out;
      }
    } else {
      infilename = "(stdin)";
      in = System.in;
      out = System.out;
    }
    ParseErrorStream error_stream = new ParseErrorStream(infilename);

    Parser parser;
    Program IR;
    switch (cli.target) 
      {
      case CLI.SCAN:
        Yylex lexer = new Yylex(in, error_stream);
        java_cup.runtime.Symbol tok;
        PrintWriter out_writer = new PrintWriter(out);

        do {
          tok = lexer.nextToken();
          out_writer.print(tok.left + "\t" + Token.symnames[tok.sym]);
          if (tok.value != null) {
            out_writer.println("\t" + tok);
          } else {
            out_writer.println();
          }
          out_writer.flush();
        } while (tok.sym != Sym.EOF);
        break;
      case CLI.PARSE:
        parser = new Parser(in, error_stream, cli.debug);
        try {
          if (cli.debug)
            parser.debug_parse();
          else
            parser.parse();
        } catch (Exception e) {
          if (cli.debug)
            throw e;
        }
        break;
      case CLI.INTER:
        parser = new Parser(in, error_stream, false);
        IR = null;
	      
        try {
          IR = (Program)parser.parse().value;
        } catch (Exception e) {
          if (cli.debug)
            throw e;
          else
            error_stream.error(e.getMessage());
        }
	      
        if (cli.debug && IR != null) {
          System.out.print(IR.pretty_print(2, true));
        }
	      
        /*
          java6035.tools.IRVis.IRVisWin viswin = 
          new java6035.tools.IRVis.IRVisWin(IR);
		
          viswin.popup();
          */
        break;
      case CLI.LOWIR:
        parser = new Parser(in, error_stream, false);
	
        try {
          IR = (Program)parser.parse().value;
	  
          // Take Highlevel IR and convert to low IR
          // Symbol table offsets already computed
          // Destructure the methods in Program and put the resulting
          // LowInstruction in each's place in the SymbolTable
	  
          Descriptor d;
          String key;
          LowInstruction i;
          for (Enumeration e = IR.keys(); e.hasMoreElements(); ) {
            key = (String)e.nextElement();
            d = (Descriptor)IR.get(key);
            if (d instanceof MethodDescriptor) {
              i = ((MethodDescriptor)d).destructure(null);
              IR.put(key, i);
              if (cli.debug) {
                System.out.println("\nMethod " + key + ":");
                System.out.print(i.pretty_print_lowIR(4, true));
              }
            }
          }
        } catch (Exception e) {
          if (cli.debug)
            throw e;
          else
            error_stream.error(e.getMessage());
        }
        break;
        
      case CLI.ASSEMBLY:
        parser = new Parser(in, error_stream, false);
	
        try {
          IR = (Program)parser.parse().value;

          Codegen c = new MIPSCodegen( cli.opts[OPT_SPIM] ? 
                                       MIPSGenerator.SPIM_FORMAT : 
                                       MIPSGenerator.NATIVE_FORMAT, out);

          IR.generate_delocalized_code(c);
          IR.allocate_locations(c);

          if (cli.debug) {
            System.out.println("Selected Optimizations:");
            if (!(cli.opts[OPT_CP] || cli.opts[OPT_DCE]))
              System.out.println("   None.");
            else {
              if (cli.opts[OPT_CP]) 
                System.out.println("   Copy Propagation");
              if (cli.opts[OPT_DCE]) 
                System.out.println("   Dead Code Elimination");
            }
          }

          IR.perform_optimizations(cli.opts[OPT_CP], cli.opts[OPT_DCE]);
          IR.generate_cooked_code(c);
          c.output_code();
        } catch (Exception e) {
          if (cli.debug)
            throw e;
          else
            error_stream.error(e.getMessage());
        }
      }

	
    in.close();
    out.close();
  }
}
