open Batteries
open Elang
open Prog
open Utils


let dump_binop = function
  | Eadd -> Printf.sprintf "+"
  | Esub -> Printf.sprintf "-"
  | Emul -> Printf.sprintf "*"
  | Ediv -> Printf.sprintf "/"
  | Emod -> Printf.sprintf "%%"
  | Exor -> Printf.sprintf "^"
  | Eclt -> Printf.sprintf "<"
  | Ecle -> Printf.sprintf "<="
  | Ecgt -> Printf.sprintf ">"
  | Ecge -> Printf.sprintf ">="
  | Eceq -> Printf.sprintf "=="
  | Ecne -> Printf.sprintf "!="

let dump_unop = function
  | Eneg -> Printf.sprintf "-"

let rec dump_eexpr = function
  | Ebinop(b, e1, e2) -> Printf.sprintf "(%s %s %s)" (dump_eexpr e1) (dump_binop b) (dump_eexpr e2)
  | Eunop(u, e) -> Printf.sprintf "(%s %s)" (dump_unop u) (dump_eexpr e)
  | Eint i -> Printf.sprintf "%d" i
  | Evar s -> Printf.sprintf "%s" s

let indent_size = 2
let spaces n =
  range (indent_size*n) |> List.map (fun _ -> ' ') |> String.of_list

let print_spaces oc n =
  Format.fprintf oc "%s" (spaces n)

let rec dump_einstr_rec indent oc i =
  match i with
  | Iassign(v, e) ->
    print_spaces oc indent;
    Format.fprintf oc "%s = %s;\n" v (dump_eexpr e)
  | Iif(cond, i1, i2) ->
    print_spaces oc indent;
    Format.fprintf oc "if (%s) %a else %a\n"
                           (dump_eexpr cond) (dump_einstr_rec (indent)) i1 (dump_einstr_rec (indent)) i2
  | Iwhile(cond, i) ->
    print_spaces oc indent;
    Format.fprintf oc "while (%s) %a\n"
                         (dump_eexpr cond) (dump_einstr_rec (indent)) i
  | Iblock(il) ->
    Format.fprintf oc "{\n";
    List.iter (Format.fprintf oc "%a" (dump_einstr_rec (indent + 1))) il;
    print_spaces oc indent;
    Format.fprintf oc "}";
  | Ireturn(e) ->
    print_spaces oc indent;
    Format.fprintf oc "return %s;\n" (dump_eexpr e)
  | Iprint(e) ->
    print_spaces oc indent;
    Format.fprintf oc "print %s;\n" (dump_eexpr e)

let dump_einstr oc i = dump_einstr_rec 0 oc i


let dump_efun oc funname {funargs; funbody} =
  Format.fprintf oc "%s(%s) {\n%a\n}\n"
    funname
    (String.concat "," funargs)
    dump_einstr funbody

let dump_eprog oc = dump_prog dump_efun oc

let dump_e oc p =
  dump_eprog oc p