let pretty_print (prog_name, main) =
  let get_spaces n =
    if n <= 0 then "" else String.make n ' '
  in
  let string_of_var v =
    (string_of_type v.var_type) ^ " " ^ v.var_name
  in
  let string_of_nature = function
    | Varb -> "var"
    | Cst -> "cst"
    | Funct -> "fun"
    | Unknown_nature -> "???"
  in
  let rec string_of_statement d st =
    get_spaces d ^
    (
      match st.stt_value with
        | Assign(v, e) ->
            string_of_expression v ^ " := " ^ string_of_expression e
        | Proc(v, el) ->
            v.var_name ^ " (" ^ String.concat " " (List.map string_of_expression el) ^ ")"
        | Compound stts ->
            string_of_statements d stts
        | If (e, s1, s2) ->
            "if " ^ string_of_expression e ^ " then\n" ^ string_of_statement (d + 2) s1 ^ get_spaces d ^ "else\n" ^ string_of_statement (d + 2) s2
        | Delete e ->
            "delete " ^ string_of_expression e
        | Return e ->
            "return " ^ string_of_expression e
        | While (e, s) ->
            "while " ^ string_of_expression e ^ "\n" ^ string_of_statement (d + 2) s
        | Noop -> "NOP"
    ) ^ "\n"
  and string_of_statements d stts =
    String.concat "" (List.map (string_of_statement (d + 4)) stts)
  in
  let rec string_of_value d = function
    | Constant c -> string_of_expression c
    | Variable -> "<var>"
    | Function f ->
        "<fun>\n" ^ string_of_block (d + 2) f.func_value
    | Type t -> "<type : " ^ (string_of_type t) ^ ">"
  and string_of_block d block =
    (fold_env
       (fun n v s ->
          s ^
          get_spaces d ^ (string_of_nature v.def_nature) ^ " " ^ (string_of_type v.def_type) ^ " " ^ v.def_name ^ " = " ^ (string_of_value d v.def_value) ^ "\n"
       ) "" block.block_env
    ) ^ (string_of_statements d block.block_stts)
  in
    prog_name ^ "\n" ^ (String.make (String.length prog_name) '=') ^ "\n\n" ^ (string_of_block 0 main)