(* Top-level of the LaTenS compiler: scan & parse the input,
   check the resulting AST, generate LLVM IR, and dump the module *)
(*open Sast*)
open Exceptions
open Printf

type action = Ast | Past | Sast | Debug | LLVM_IR | Compile


let _ =
  let med = "intermediate.tens" in
  let file = "stdlib.tens" in
  let in_channel = try (if Array.length Sys.argv > 1 && Array.length Sys.argv < 4 then (open_in Sys.argv.(Array.length Sys.argv - 1)) else 
        (if Array.length Sys.argv == 1 then stdin else raise (ImproperArgsException)))
        with ImproperArgsException -> prerr_endline("Usage:\n./latens.ml <compiler flag> <file>\n./latens.ml <file>"); exit 1
    in let out_channel = open_out med in
      (try
        while true do
          let line = input_line in_channel in
          fprintf out_channel "%s\n" line
        done
      with End_of_file ->
        close_in in_channel);
          let in_channel = open_in file
      in (try
        while true do
          let line = input_line in_channel in
          fprintf out_channel "%s\n" line
        done
      with End_of_file ->
        close_in in_channel);
      close_out out_channel;

      let in_file = open_in med in


  let action = if Array.length Sys.argv > 1 then
    try List.assoc Sys.argv.(1) [ ("-a", Ast);(* Print the AST only *)
      ("-p", Past);(* Pretty Print only *)
      ("-s", Sast); (* Semantically check and print Sast *)
      ("-d", Debug); (* Pretty print the Sast and also the final global environment *)
      ("-l", LLVM_IR);  (* Generate LLVM, don't check *)
      ("-c", Compile);] (* Generate, check LLVM IR *)
    with Not_found -> Compile
  else Compile in
  (try 
    let lexbuf = Lexing.from_channel in_file in
      let ast = Parser.program Scanner.token lexbuf in
      (*let sast = Semant.typify(ast) in*)
    match action with 
      Ast -> print_string (Ast.string_of_program ast)
    | Past -> print_string (Ast.pretty_program ast)
    | Sast -> print_string  (Sast.string_of_program (snd (Semant.typify ast)))
    | Debug -> print_string (Semant.string_of_prog_and_env (Semant.typify ast))
    | LLVM_IR -> print_string (Llvm.string_of_llmodule (Codegen.translate (snd (Semant.typify ast))))
    | Compile -> let m = Codegen.translate (snd (Semant.typify ast)) in
      Llvm_analysis.assert_valid_module m;
      print_string (Llvm.string_of_llmodule m)
  with  UnmatchedQuotationException(line) -> prerr_endline ("Line: " ^ string_of_int line ^  ": Unmatched quotation"); close_in in_file; Sys.remove med; exit 1
      | IllegalCharacterException(line,c) -> prerr_endline ("Line: "^ string_of_int line ^ ": Illegal character: " ^ Char.escaped c); close_in in_file; Sys.remove med; exit 1
      | ParserException(sline, s, eline, e, msg) -> prerr_endline ("Line: " ^ string_of_int sline ^ ": Char " ^ string_of_int s ^ " - Line: " ^ string_of_int eline ^ " Char " ^ string_of_int e ^ ": " ^ msg); close_in in_file; Sys.remove med; exit 1
      | NoExprHasNoLineNumberException -> prerr_endline ("[Debug message] You modified the compiler. Something is wrong. Why do you want the linenum of a noexpr"); close_in in_file; Sys.remove med; exit 1
      | Parsing.Parse_error -> prerr_endline " shouldn't happen "; close_in in_file; Sys.remove med; exit 1
      | NoMainException -> prerr_endline "LaTenS files need a main method."; close_in in_file; Sys.remove med; exit 1
      | StatementAfterReturnException(exp) -> prerr_endline ("Found statement: " ^ exp ^ " after return."); close_in in_file; Sys.remove med; exit 1
      | ExpectedConditionException(line, e) -> prerr_endline ("Line: " ^ line ^ ": Conditional expression expected, but " ^ e ^ " does not evaluate to int."); close_in in_file; Sys.remove med; exit 1
      | BinaryTypeMismatchException(line, t1,t2, op) -> prerr_endline ("Line: " ^ line ^ ": Operator " ^ op ^ " does not support types " ^ t1 ^  " and " ^ t2 ^ "."); close_in in_file; Sys.remove med; exit 1
      | UnaryTypeMismatchException(line, t, op) -> prerr_endline ("Line: " ^ line ^ ": Type " ^ t ^ " not supported by op: " ^ op ^ "."); close_in in_file; Sys.remove med; exit 1
      | BadConcatException(line) -> prerr_endline ("Line: " ^ line ^ ": Concatenation can only be used with strings."); close_in in_file; Sys.remove med; exit 1
      | NotDeclaredInScopeException(line, id) -> prerr_endline ("Line: " ^ line ^ ": " ^ id ^ " not declared in current scope."); close_in in_file; Sys.remove med; exit 1
      | DoubleDeclareException(line, id) -> prerr_endline ("Line: " ^ line ^ ": " ^ id ^ " already declared in current scope."); close_in in_file; Sys.remove med; exit 1
      | FuncReturnTypeException(line, t1, t2) -> prerr_endline ("Line: " ^ line ^ ": Expected return type of " ^ t1 ^ " but found " ^ t2 ^ " instead."); close_in in_file; Sys.remove med; exit 1
      | InvalidTensorImmedException(line) -> prerr_endline("Line: " ^ line ^ ": Invalid tensor immediate."); close_in in_file; Sys.remove med; exit 1
      | InvalidIndexException(line, t) -> prerr_endline ("Line: " ^ line ^ ": Int expected for tensor index. Found: " ^ t ^ "."); close_in in_file; Sys.remove med; exit 1
      | TensorTypeException(line, s, t1, t2) -> prerr_endline ("Line: " ^ line ^ ": " ^ s  ^ " was declared as " ^ t1 ^ " but you used " ^ t2 ^ " indices."); close_in in_file; Sys.remove med; exit 1
      | InvalidTensorElementException(line, t) -> prerr_endline ("Line: " ^ line ^ ": Type " ^ t ^ " is not a valid tensor element."); close_in in_file; Sys.remove med; exit 1
      | EmptyTensorException(line) -> prerr_endline("Line: " ^ line ^ ": Cannot declare a 0-dimensional tensor."); close_in in_file; Sys.remove med; exit 1
      | VoidTensorIndexException(line, expr) -> prerr_endline("Line: " ^ line ^ ": " ^ expr ^ " is void in the current scope and is not a valid tensor index."); close_in in_file; Sys.remove med; exit 1
      | UnspecifiedTensorMultiplicationException(line) -> prerr_endline("Line: " ^ line ^ ": Cannot multiply tensors with no specified indices."); close_in in_file; Sys.remove med; exit 1
      | NoReturnException(t) -> prerr_endline("Expected return statement of type: " ^ t ^ " but no return statement found."); close_in in_file; Sys.remove med; exit 1
      | CodegenException -> prerr_endline("Codegen exception. Gah"); close_in in_file; Sys.remove med; exit 1);
  close_in in_file; Sys.remove med; exit 0

