type t =
  | Syntax of syntax
  | Codegen of codegen
  | Ty of ty
  | Elaborate of elaborate

and syntax =
  | UnexpectedToken of string
  | ParsingError of string
  | UnboundVariable of string
  | UnterminatedComment
  | InvalidCharLiteral of string

and codegen = UninitializedModule of string | UnexpectedTerm of string
and ty = IncompatibleType of string
and elaborate = string

exception Error of (Loc.t option * t)

let error err = Error (None, err)
let error_with_loc err loc = Error (Some loc, err)

let rec pp fmt = function
  | Syntax syntax -> Format.fprintf fmt "Syntax error, %a" pp_syntax syntax
  | Codegen codegen -> Format.fprintf fmt "Codegen error, %a" pp_codegen codegen
  | Ty ty -> Format.fprintf fmt "Type error, %a" pp_ty ty
  | Elaborate elaborate -> Format.fprintf fmt "Elaboration error, %s" elaborate

and pp_syntax fmt = function
  | UnexpectedToken s -> Format.fprintf fmt "unexpected token: '%s'" s
  | ParsingError s -> Format.fprintf fmt "%s" s
  | UnboundVariable s -> Format.fprintf fmt "unbound variable: '%s'" s
  | UnterminatedComment -> Format.fprintf fmt "unterminated comment"
  | InvalidCharLiteral s -> Format.fprintf fmt "invalid char literal: '%s'" s

and pp_codegen fmt = function
  | UninitializedModule s -> Format.fprintf fmt "%s" s
  | UnexpectedTerm s -> Format.fprintf fmt "unexpected term: %s" s

and pp_ty fmt = function IncompatibleType s -> Format.fprintf fmt "%s" s

and pp_loc fmt ((loc, t) : Loc.t option * t) =
  match loc with
  | Some loc -> Format.fprintf fmt "In %a:\n%a\n" Loc.pp loc pp t
  | None -> Format.fprintf fmt "%a\n" pp t
