type typ =
  | Int of int
  | Unit

let typ_to_string = function
  | Int n -> Printf.sprintf "%d" n
  | Unit -> "()"

type binop =
  | Add | Sub | Mul | Div (* arithmetics *)
  | Mod (* modular operation *)
  | Exp (* exponentation *)

let binop_to_string = function
  | Add -> "+"
  | Sub -> "-"
  | Mul -> "*"
  | Div -> "/"
  | Mod -> "%"
  | Exp -> "^"

type t =
  | Value of typ
  | Binop of t * binop * t
  | Set_binop_pre of binop * t
  | Get_binop_pre of binop

let value v = Value v

let binop left op right =
  Binop (left, op, right)

let set_binop_pre op pre =
  Set_binop_pre (op, pre)

(* print ast LISP style. *)
let print ast =
  let pr = Printf.printf in
  let pv v = pr "%s" @@ typ_to_string v in
  let rec aux = function
    | Value n -> pv n
    | Binop (left, op, right) -> begin
        pr "(%s " @@ binop_to_string op;
        aux left;
        pr " ";
        aux right;
        pr ")";
      end
    | Set_binop_pre (op, pre) ->
      pr "(set_pre %s " (binop_to_string op);
      aux pre;
      pr ")"
    | Get_binop_pre op ->
      pr "(get_pre %s)" (binop_to_string op)
  in
  aux ast; pr "\n"