mpri-funcprog-project/src/kremlin/PrintC.ml

355 lines
11 KiB
OCaml

(** Pretty-printer that conforms with C syntax. Also defines the grammar of
* concrete C syntax, as opposed to our idealistic, well-formed C*. *)
open PPrint
open PrintCommon
open C
(* Pretty-printing of actual C syntax *****************************************)
let p_storage_spec = function
| Typedef -> string "typedef"
| Extern -> string "extern"
| Static -> string "static"
let rec p_type_spec = function
| Int w -> print_width w ^^ string "_t"
| Void -> string "void"
| Named s -> string s
| Union (name, decls) ->
group (string "union" ^/^
(match name with Some name -> string name ^^ break1 | None -> empty)) ^^
braces_with_nesting (separate_map hardline (fun p -> group (p_declaration p ^^ semi)) decls)
| Struct (name, decls) ->
group (string "struct" ^/^
(match name with Some name -> string name | None -> empty)) ^^
(match decls with
| Some decls ->
break1 ^^ braces_with_nesting (separate_map hardline (fun p -> group (p_declaration p ^^ semi)) decls)
| None ->
empty)
| Enum (name, tags) ->
group (string "enum" ^/^
(match name with Some name -> string name ^^ break1 | None -> empty)) ^^
braces_with_nesting (separate_map (comma ^^ break1) string tags)
and p_type_declarator d =
let rec p_noptr = function
| Ident n ->
string n
| Array (d, s) ->
p_noptr d ^^ lbracket ^^ p_expr s ^^ rbracket
| Function (cc, d, params) ->
let cc = match cc with Some cc -> print_cc cc ^^ break1 | None -> empty in
group (cc ^^ p_noptr d ^^ parens_with_nesting (separate_map (comma ^^ break 1) (fun (spec, decl) ->
group (p_type_spec spec ^/^ p_any decl)
) params))
| d ->
lparen ^^ p_any d ^^ rparen
and p_any = function
| Pointer d ->
star ^^ p_any d
| d ->
p_noptr d
in
p_any d
and p_type_name (spec, decl) =
match decl with
| Ident "" ->
p_type_spec spec
| _ ->
p_type_spec spec ^^ space ^^ p_type_declarator decl
(* http:/ /en.cppreference.com/w/c/language/operator_precedence *)
and prec_of_op2 op =
let open Constant in
match op with
| AddW | SubW | MultW | DivW -> failwith "[prec_of_op]: should've been desugared"
| Add -> 4, 4, 4
| Sub -> 4, 4, 3
| Div -> 3, 3, 2
| Mult -> 3, 3, 3
| Mod -> 3, 3, 2
| BOr -> 10, 10, 10
| BAnd ->
8, 8, 8
| BXor | Xor -> 9, 9, 9
| BShiftL | BShiftR ->
5, 5, 4
| Eq | Neq -> 7, 7, 7
| Lt | Lte | Gt | Gte -> 6, 6, 5
| And -> 11, 11, 11
| Or -> 12, 12, 12
| Assign -> 14, 13, 14
| Comma -> 15, 15, 14
| PreIncr | PostIncr | PreDecr | PostDecr | Not | BNot -> raise (Invalid_argument "prec_of_op2")
and prec_of_op1 op =
let open Constant in
match op with
| PreDecr | PreIncr | Not | BNot -> 2
| PostDecr | PostIncr -> 1
| _ -> raise (Invalid_argument "prec_of_op1")
and is_prefix op =
let open Constant in
match op with
| PreDecr | PreIncr | Not | BNot -> true
| PostDecr | PostIncr -> false
| _ -> raise (Invalid_argument "is_prefix")
(* The precedence level [curr] is the maximum precedence the current node should
* have. If it doesn't, then it should be parenthesized. Lower numbers bind
* tighter. *)
and paren_if curr mine doc =
if curr < mine then
group (lparen ^^ doc ^^ rparen)
else
doc
(* [e] is an operand of [op]; is this likely to trigger GCC's -Wparentheses? If
* so, downgrade the current precedence to 0 to force parenthses. *)
and defeat_Wparentheses op e prec =
let open Constant in
if not !Options.parentheses then
prec
else match op, e with
| (BShiftL | BShiftR | BXor | BOr | BAnd), Op2 ((Add | Sub | BOr | BXor), _, _) ->
0
| _ ->
prec
and p_expr' curr = function
| Op1 (op, e1) ->
let mine = prec_of_op1 op in
let e1 = p_expr' mine e1 in
paren_if curr mine (if is_prefix op then print_op op ^^ e1 else e1 ^^ print_op op)
| Op2 (op, e1, e2) ->
let mine, left, right = prec_of_op2 op in
let left = defeat_Wparentheses op e1 left in
let right = defeat_Wparentheses op e2 right in
let e1 = p_expr' left e1 in
let e2 = p_expr' right e2 in
paren_if curr mine (e1 ^/^ print_op op ^^ jump e2)
| Index (e1, e2) ->
let mine, left, right = 1, 1, 15 in
let e1 = p_expr' left e1 in
let e2 = p_expr' right e2 in
paren_if curr mine (e1 ^^ lbracket ^^ e2 ^^ rbracket)
| Assign (e1, e2) ->
let mine, left, right = 14, 13, 14 in
let e1 = p_expr' left e1 in
let e2 = p_expr' right e2 in
paren_if curr mine (group (e1 ^/^ equals) ^^ jump e2)
| Call (e, es) ->
let mine, left, arg = 1, 1, 14 in
let e = p_expr' left e in
let es = nest 2 (separate_map (comma ^^ break 1) (fun e -> group (p_expr' arg e)) es) in
paren_if curr mine (e ^^ lparen ^^ es ^^ rparen)
| Literal s ->
dquote ^^ string s ^^ dquote
| Constant (w, s) ->
string s ^^ if K.is_unsigned w then string "U" else empty
| Name s ->
string s
| Cast (t, e) ->
let mine, right = 2, 2 in
let e = group (p_expr' right e) in
let t = p_type_name t in
paren_if curr mine (lparen ^^ t ^^ rparen ^^ e)
| Type t ->
p_type_name t
| Sizeof e ->
let mine, right = 2, 2 in
let e = p_expr' right e in
paren_if curr mine (string "sizeof" ^^ space ^^ e)
| SizeofType t ->
string "sizeof" ^^ group (lparen ^^ p_type_spec t ^^ rparen)
| Address e ->
let mine, right = 2, 2 in
let e = p_expr' right e in
paren_if curr mine (ampersand ^^ e)
| Deref e ->
let mine, right = 2, 2 in
let e = p_expr' right e in
paren_if curr mine (star ^^ e)
| Member _ | MemberP _ ->
failwith "[p_expr']: not implemented"
| Bool b ->
string (string_of_bool b)
| CompoundLiteral (t, init) ->
(* NOTE: always parenthesize compound literal no matter what, because GCC
* parses an application of a function to a compound literal as an n-ary
* application. *)
parens_with_nesting (
lparen ^^ p_type_name t ^^ rparen ^^
braces_with_nesting (separate_map (comma ^^ break1) p_init init)
)
| MemberAccess (expr, member) ->
p_expr' 1 expr ^^ dot ^^ string member
| MemberAccessPointer (expr, member) ->
p_expr' 1 expr ^^ string "->" ^^ string member
| InlineComment (s, e, s') ->
surround 2 1 (p_comment s) (p_expr' curr e) (p_comment s')
and p_comment s =
(* TODO: escape *)
string "/* " ^^ nest 2 (flow space (words s)) ^^ string " */"
and p_expr e = p_expr' 15 e
and p_init (i: init) =
match i with
| Designated (designator, i) ->
group (p_designator designator ^^ space ^^ equals ^^ space ^^ p_init i)
| InitExpr e ->
p_expr' 14 e
| Initializer inits ->
let inits =
if List.length inits > 4 then
flow (comma ^^ break1) (List.map p_init inits)
else
separate_map (comma ^^ break1) p_init inits
in
braces_with_nesting inits
and p_designator = function
| Dot ident ->
dot ^^ string ident
| Bracket i ->
lbracket ^^ int i ^^ rbracket
and p_decl_and_init (decl, init) =
group (p_type_declarator decl ^^ match init with
| Some init ->
space ^^ equals ^^ jump (p_init init)
| None ->
empty)
and p_declaration (spec, stor, decl_and_inits) =
let stor = match stor with Some stor -> p_storage_spec stor ^^ space | None -> empty in
stor ^^ group (p_type_spec spec) ^/^
separate_map (comma ^^ break 1) p_decl_and_init decl_and_inits
(* This is abusing the definition of a compound statement to ensure it is printed with braces. *)
let nest_if f stmt =
match stmt with
| Compound _ ->
hardline ^^ f stmt
| _ ->
nest 2 (hardline ^^ f stmt)
(* A note on the classic dangling else problem. Remember that this is how things
* are parsed (note the indentation):
*
* if (foo)
* if (bar)
* ...
* else
* ...
*
* And remember that this needs braces:
*
* if (foo) {
* if (bar)
* ...
* } else
* ...
*
* [protect_solo_if] adds braces to the latter case. However, GCC, unless
* -Wnoparentheses is given, will produce a warning for the former case.
* [protect_ite_if_needed] adds braces to the former case, when the user has
* requested the extra, unnecessary parentheses needed to silence -Wparentheses.
* *)
let protect_solo_if s =
match s with
| If _ -> Compound [ s ]
| _ -> s
let protect_ite_if_needed s =
match s with
| IfElse _ when !Options.parentheses -> Compound [ s ]
| _ -> s
let p_or p x =
match x with
| Some x -> p x
| None -> empty
let rec p_stmt (s: stmt) =
(* [p_stmt] is responsible for appending [semi] and calling [group]! *)
match s with
| Compound stmts ->
lbrace ^^ nest 2 (hardline ^^ separate_map hardline p_stmt stmts) ^^
hardline ^^ rbrace
| Expr expr ->
group (p_expr expr ^^ semi)
| Comment s ->
group (string "/*" ^/^ separate break1 (words s) ^/^ string "*/")
| For (decl, e2, e3, stmt) ->
let init = match decl with
| `Decl decl -> p_declaration decl
| `Expr expr -> p_expr expr
| `Skip -> empty
in
group (string "for" ^/^ lparen ^^ nest 2 (
init ^^ semi ^^ break1 ^^
p_expr e2 ^^ semi ^^ break1 ^^
p_expr e3
) ^^ rparen) ^^ nest_if p_stmt stmt
| If (e, stmt) ->
group (string "if" ^/^ lparen ^^ p_expr e ^^ rparen) ^^
nest_if p_stmt (protect_ite_if_needed stmt)
| IfElse (e, s1, s2) ->
group (string "if" ^/^ lparen ^^ p_expr e ^^ rparen) ^^
nest_if p_stmt (protect_solo_if s1) ^^ hardline ^^
string "else" ^^
(match s2 with
| If _ | IfElse _ ->
space ^^ p_stmt s2
| _ ->
nest_if p_stmt s2)
| While (e, s) ->
group (string "while" ^/^ lparen ^^ p_expr e ^^ rparen) ^^
nest_if p_stmt s
| Return None ->
group (string "return" ^^ semi)
| Return (Some e) ->
group (string "return" ^^ jump (p_expr e ^^ semi))
| DeclStmt d ->
group (p_declaration d ^^ semi)
| Switch (e, branches, default) ->
group (string "switch" ^/^ lparen ^^ p_expr e ^^ rparen) ^/^
braces_with_nesting (
separate_map hardline (fun (e, s) ->
group (string "case" ^/^ p_expr e ^^ colon) ^^ nest 2 (
hardline ^^ p_stmt s
)
) branches ^^ hardline ^^
string "default:" ^^ nest 2 (
hardline ^^ p_stmt default
)
)
| Break ->
string "break" ^^ semi
let p_comments cs =
separate_map hardline (fun c -> string ("/*\n" ^ c ^ "\n*/")) cs ^^
if List.length cs > 0 then hardline else empty
let p_decl_or_function (df: declaration_or_function) =
match df with
| Decl (comments, d) ->
p_comments comments ^^
group (p_declaration d ^^ semi)
| Function (comments, inline, d, stmt) ->
p_comments comments ^^
let inline = if inline then string "inline" ^^ space else empty in
inline ^^ group (p_declaration d) ^/^ p_stmt stmt
let print_files =
PrintCommon.print_files p_decl_or_function