let rec string_of_expression expr =
"(" ^ (string_of_type expr.expr_type) ^ ") " ^
match expr.expr_val with
| Lt(e1, e2) -> string_of_expression e1 ^ " < " ^ string_of_expression e2
| Le(e1, e2) -> string_of_expression e1 ^ " <= " ^ string_of_expression e2
| Eq(e1, e2) -> string_of_expression e1 ^ " = " ^ string_of_expression e2
| Neq(e1, e2) -> string_of_expression e1 ^ " <> " ^ string_of_expression e2
| Plus(e1, e2) -> string_of_expression e1 ^ " + " ^ string_of_expression e2
| Minus(e1, e2) -> string_of_expression e1 ^ " - " ^ string_of_expression e2
| Mult(e1, e2) -> string_of_expression e1 ^ " * " ^ string_of_expression e2
| Div(e1, e2) -> string_of_expression e1 ^ " / " ^ string_of_expression e2
| Mod(e1, e2) -> string_of_expression e1 ^ " mod " ^ string_of_expression e2
| And(e1, e2) -> string_of_expression e1 ^ " and " ^ string_of_expression e2
| Or(e1, e2) -> string_of_expression e1 ^ " or " ^ string_of_expression e2
| Not e -> "not " ^ string_of_expression e
| Uminus e -> "-" ^ string_of_expression e
| Int i -> string_of_int i
| Bool b -> if b then "true" else "false"
| Var(v, []) -> v.var_name
| Var(v, el) -> v.var_name ^ "(" ^ String.concat " " (List.map string_of_expression el) ^ ")"
| Arr(e1, e2) -> string_of_expression e1 ^ "[" ^ (string_of_expression e2) ^ "]"
| Rec(e, f) -> string_of_expression e ^ "." ^ f
| Address_of(e) -> "&" ^ string_of_expression e
| Bang(e) -> string_of_expression e ^ "^"
| New(t) -> "new " ^ string_of_type !t