type op1 =
  | Neg
  | Not
  | Print of print
  | ReadInt
  | ReadFloat
  | ReadChar
  | Time
  | FNeg
  | FSqrt
  | FSin
  | FCos
  | FExp
  | FLog
  | FRound
  | Float_of_int
  | Int_of_float

and op2 =
  | Plus
  | Minus
  | Times
  | Div
  | Mod
  | And
  | Or
  | Eq
  | Less
  | LessEq
  | Greater
  | GreaterEq
  | FPlus
  | FMinus
  | FTimes
  | FDiv
  | FEq
  | FLess
  | FLessEq
  | FGreater
  | FGreaterEq
  | FMax
  | FMin

and print = PrintInt | PrintBool | PrintFloat | PrintAny | PrintChar of char

let pp_print fmt = function
  | PrintInt -> Format.fprintf fmt "print_int"
  | PrintBool -> Format.fprintf fmt "print_bool"
  | PrintFloat -> Format.fprintf fmt "print_float"
  | PrintAny -> Format.fprintf fmt "print"
  | PrintChar c ->
      let str = String.make 1 c in
      let escaped_str = String.escaped str in
      Format.fprintf fmt "print_char '%s'" escaped_str

let pp_op1 fmt = function
  | Neg -> Format.fprintf fmt "-"
  | Not -> Format.fprintf fmt "not"
  | Print p -> pp_print fmt p
  | ReadInt -> Format.fprintf fmt "read_int"
  | ReadFloat -> Format.fprintf fmt "read_float"
  | ReadChar -> Format.fprintf fmt "read_char"
  | Time -> Format.fprintf fmt "time"
  | FNeg -> Format.fprintf fmt "float_negate"
  | FSqrt -> Format.fprintf fmt "sqrt"
  | FSin -> Format.fprintf fmt "sin"
  | FCos -> Format.fprintf fmt "cos"
  | FExp -> Format.fprintf fmt "exp"
  | FLog -> Format.fprintf fmt "log"
  | FRound -> Format.fprintf fmt "round"
  | Float_of_int -> Format.fprintf fmt "float_of_int"
  | Int_of_float -> Format.fprintf fmt "int_of_float"

let pp_op2 fmt = function
  | Plus -> Format.fprintf fmt "+"
  | Minus -> Format.fprintf fmt "-"
  | Times -> Format.fprintf fmt "*"
  | Div -> Format.fprintf fmt "/"
  | Mod -> Format.fprintf fmt "mod"
  | And -> Format.fprintf fmt "&&"
  | Or -> Format.fprintf fmt "||"
  | Eq -> Format.fprintf fmt "="
  | Less -> Format.fprintf fmt "<"
  | LessEq -> Format.fprintf fmt "<="
  | Greater -> Format.fprintf fmt ">"
  | GreaterEq -> Format.fprintf fmt ">="
  | FPlus -> Format.fprintf fmt "+."
  | FMinus -> Format.fprintf fmt "-."
  | FTimes -> Format.fprintf fmt "*."
  | FDiv -> Format.fprintf fmt "/."
  | FEq -> Format.fprintf fmt "=."
  | FLess -> Format.fprintf fmt "<."
  | FLessEq -> Format.fprintf fmt "<=."
  | FGreater -> Format.fprintf fmt ">."
  | FGreaterEq -> Format.fprintf fmt ">=."
  | FMax -> Format.fprintf fmt "float_max"
  | FMin -> Format.fprintf fmt "float_min"

let pp_prim1 ~pp_e fmt op1 e1 =
  match op1 with
  | Print (PrintChar _ as p) -> Format.fprintf fmt "@[(%a)@]" pp_print p
  | _ -> Format.fprintf fmt "@[(%a %a)@]" pp_op1 op1 pp_e e1

let pp_prim2 ~pp_e fmt op2 e1 e2 =
  match op2 with
  | FMax | FMin ->
      Format.fprintf fmt "@[(%a@;<1 2>%a %a)@]" pp_op2 op2 pp_e e1 pp_e e2
  | _ -> Format.fprintf fmt "@[(%a %a@;<1 2>%a)@]" pp_e e1 pp_op2 op2 pp_e e2
