(** 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