(*
   assem.ml
   15-411
   by Roland Flury
*)
(* @version $Id: assem.ml,v 1.3 2004/10/30 19:33:21 ajo Exp $ *)

open Types
exception MarkerError of instr

(* Prints a list of instructions *)
let print_assem ali = (
  let print_rat = function
    | TEMP t -> Printf.printf "t%d" t
    | REGISTER t -> Printf.printf "REGISTER %d" t
    | STACKSLOT t -> Printf.printf "STACKSLOT %d" t
  in
  let ps = print_string in
  List.iter (
      function
      | OPER(st, s, d) ->
          ps ("OPER("^st^", [");
          List.iter print_rat s; ps "], [";
          List.iter print_rat d; ps "])\n"
      | LIVEOPER(st, s, d) ->
          ps ("LIVEOPER("^st^", [");
          List.iter print_rat s; ps "], [";
          List.iter print_rat d; ps "])\n"
      | MOVE(st, s, d) ->
          ps ("MOVE("^st^", ");
          print_rat s; ps ", ";
          print_rat d; ps ")\n"
      | JUMP id ->
          ps ("JUMP("^id^")\n")
      | COMMENT id ->
          ps ("COMMENT("^id^")\n")
      | LABEL id ->
          ps ("LABEL "^id^":\n")
    ) ali
  )

(* Returns the list of destinations used in this instruction *)
let getDst = function
  | OPER(_,_,d) | LIVEOPER(_,_,d) -> d
  | MOVE(_,_,d) -> [d]
  | JUMP _ | LABEL _ | COMMENT _ -> []

(* Returns the list of sources used in this instruction *)
let getSrc = function
  | OPER(_,s,_) | LIVEOPER(_,s,_) -> s
  | MOVE(_,s,_) -> [s]
  | JUMP _ | LABEL _ | COMMENT _ -> []

(* Returns the string representing the instruction *)
let getAssem = function
  | OPER(s,_,_) | LIVEOPER(s,_,_) | MOVE(s,_,_) -> s
  | JUMP s | LABEL s | COMMENT s -> s

(* Returns a string representation of the instruction, using
 * a function mapping temps to a string *)
let format (map: temp -> string) instr =
  let str = ref (getAssem instr) in
  let rec replace token list count =
    match list with
    | (TEMP head) :: tail ->
        let tok = token ^ (string_of_int count) in
        let r = Str.regexp tok in
        str := Str.global_replace r (map head) !str;
        replace token tail (count + 1)
    | _ :: tail ->
        replace token tail (count + 1)
    | [] -> ()
  in
  match instr with
  | OPER _ | LIVEOPER _ | MOVE _ ->
      if (!str.[0] = '#') then ""
      else (
        replace "'s" (getSrc instr) 0;
        replace "'d" (getDst instr) 0;
        "\t"^ !str ^"\n"
      )
  | JUMP _ -> "\t"^ !str ^"\n"
  | LABEL _ -> !str^"\n"
  | COMMENT _ -> "#\n"^ !str^"\n#\n"


let marker = function
  JUMP js -> (
      let _ = Str.search_forward
        (Str.regexp "\t[ \t]*\\.\\([A-Za-z_0-9]+\\)") js 0
      in Str.matched_group 1 js
    )
  | LABEL ls -> (
      let _ = Str.search_forward
        (Str.regexp "[ \t]*\\.\\([A-Za-z_0-9]+\\)[\t ]*:") ls 0
      in Str.matched_group 1 ls
    )
  | x -> raise (MarkerError x)

let unconditional = function
  | JUMP js -> (try (
      ignore(Str.search_forward (Str.regexp "jmp") js 0);
      true
    ) with Not_found -> false)
  | _ -> false