

module K = Constant
open Common

(* This pretty-printer based on: http:/ /en.cppreference.com/w/c/language/declarations
 * Many cases are omitted from this bare-bones C grammar; hopefully, to be extended. *)
type type_spec =
  | Int of Constant.width
  | Void
  | Named of ident
  | Struct of ident option * declaration list option
      (** Note: the option allows for zero-sized structs (GCC's C, C++) but as
       * of 2017/05/14 we never generate these. *)
  | Union of ident option * declaration list
  | Enum of ident option * ident list
    (** not encoding all the invariants here *)
  [@@deriving show { with_path = false }]

and storage_spec =
  | Typedef
  | Extern
  | Static

and declarator_and_init =
  declarator * init option

and declarator_and_inits =
  declarator_and_init list

and declarator =
  | Ident of ident
  | Pointer of declarator
  | Array of declarator * expr
  | Function of calling_convention option * declarator * params

and expr =
  | Op1 of K.op * expr
  | Op2 of K.op * expr * expr
  | Index of expr * expr
  | Deref of expr
  | Address of expr
  | Member of expr * expr
  | MemberP of expr * expr
  | Assign of expr * expr
    (** not covering *=, +=, etc. *)
  | Call of expr * expr list
  | Name of ident
  | Cast of type_name * expr
  | Literal of string
  | Constant of K.t
  | Bool of bool
  | Sizeof of expr
  | SizeofType of type_spec
  | CompoundLiteral of type_name * init list
  | MemberAccess of expr * ident
  | MemberAccessPointer of expr * ident
  | InlineComment of string * expr * string
  | Type of type_name
    (** note: these two not in the C grammar *)

(** this is a WILD approximation of the notion of "type name" in C _and_ a hack
 * because there's the invariant that the ident found at the bottom of the
 * [declarator] is irrelevant... *)
and type_name =
  type_spec * declarator

and params =
  (* No support for old syntax, e.g. K&R, or [void f(void)]. *)
  param list

and param =
  (** Also approximate. *)
  type_spec * declarator

and declaration =
  type_spec * storage_spec option * declarator_and_inits

and ident =
  string

and init =
  | InitExpr of expr
  | Designated of designator * init
  | Initializer of init list

and designator =
  | Dot of ident
  | Bracket of int

(** Note: according to http:/ /en.cppreference.com/w/c/language/statements,
 * declarations can only be part of a compound statement... we do not enforce
 * this invariant via the type [stmt], but rather check it at runtime (see
 * [mk_compound_if]), as the risk of messing things up, naturally. *)
type stmt =
  | Compound of stmt list
  | DeclStmt of declaration
  | Expr of expr
  | If of expr * stmt
  | IfElse of expr * stmt * stmt
  | While of expr * stmt
  | For of declaration_or_expr * expr * expr * stmt
  | Return of expr option
  | Switch of expr * (expr * stmt) list * stmt
    (** the last component is the default statement *)
  | Break
  | Comment of string
    (** note: this is not in the C grammar *)

and program =
  declaration_or_function list

and comment =
  string

and declaration_or_function =
  | Decl of comment list * declaration
  | Function of comment list * bool * declaration * stmt
    (** [stmt] _must_ be a compound statement; boolean is inline *)

and declaration_or_expr = [
  | `Decl of declaration
  | `Expr of expr
  | `Skip
]
[@@deriving show { with_path = false }]
