module Language.C.Pretty (
Pretty (..),
prettyUsingInclude
) where
import Data.List (partition,nub,isSuffixOf)
import qualified Data.Set as Set
import Text.PrettyPrint.HughesPJ
import Debug.Trace
import Language.C.Data
import Language.C.Syntax
class Pretty p where
pretty :: p -> Doc
prettyPrec :: Int -> p -> Doc
pretty = prettyPrec 0
prettyPrec _ = pretty
maybeP :: (p -> Doc) -> Maybe p -> Doc
maybeP = maybe empty
ifP :: Bool -> Doc -> Doc
ifP flag doc = if flag then doc else empty
mlistP :: ([p] -> Doc) -> [p] -> Doc
mlistP pp xs = maybeP pp (if null xs then Nothing else Just xs)
identP :: Ident -> Doc
identP = text . identToString
attrlistP :: [CAttr] -> Doc
attrlistP [] = empty
attrlistP attrs = text "__attribute__" <> parens (parens (hcat . punctuate comma . map pretty $ attrs))
parenPrec :: Int -> Int -> Doc -> Doc
parenPrec prec prec2 t = if prec <= prec2 then t else parens t
ii :: Doc -> Doc
ii = nest 4
instance Pretty CTranslUnit where
pretty (CTranslUnit edecls _) = vcat (map pretty edecls)
prettyUsingInclude :: CTranslUnit -> Doc
prettyUsingInclude (CTranslUnit edecls _) =
includeWarning headerFiles
$$
(vcat $ map (either includeHeader pretty) mappedDecls)
where
(headerFiles,mappedDecls) = foldr addDecl (Set.empty,[]) $ map tagIncludedDecls edecls
tagIncludedDecls edecl | maybe False isHeaderFile (fileOfNode edecl) = Left ((posFile . posOf) edecl)
| otherwise = Right edecl
addDecl decl@(Left headerRef) (headerSet, ds)
| Set.member headerRef headerSet = (headerSet, ds)
| otherwise = (Set.insert headerRef headerSet, decl : ds)
addDecl decl (headerSet,ds) = (headerSet, decl : ds)
includeHeader hFile = text "#include" <+> doubleQuotes (text hFile)
isHeaderFile = (".h" `isSuffixOf`)
includeWarning hs | Set.null hs = empty
| otherwise = text "/* Warning: The #include directives in this file aren't necessarily correct. */"
instance Pretty CExtDecl where
pretty (CDeclExt decl) = pretty decl <> semi
pretty (CFDefExt fund) = pretty fund
pretty (CAsmExt asmStmt _) = text "asm" <> parens (pretty asmStmt) <> semi
instance Pretty CFunDef where
pretty (CFunDef declspecs declr decls stat _) =
hsep (map pretty declspecs)
<+> pretty declr
$+$ (ii . vcat . map (<> semi) . map pretty) decls
$$ prettyPrec (1) stat
instance Pretty CStat where
pretty (CLabel ident stat cattrs _) = identP ident <> text ":" <+> attrlistP cattrs $$ pretty stat
pretty (CCase expr stat _) =
text "case" <+> pretty expr <> text ":" $$ pretty stat
pretty (CCases expr1 expr2 stat _) =
text "case" <+> pretty expr1 <+> text "..."
<+> pretty expr2 <> text ":" $$ pretty stat
pretty (CDefault stat _) = text "default:" $$ pretty stat
pretty (CExpr expr _) = ii $ maybeP pretty expr <> semi
pretty c@(CCompound _ _ _) = prettyPrec 0 c
pretty (CIf expr stat estat _) =
ii $ text "if" <+> parens (pretty expr)
$+$ prettyBody stat
$$ maybeP prettyElse estat
where
prettyBody c@(CCompound _ _ _) = prettyPrec (1) c
prettyBody nonCompound = prettyPrec (1) (CCompound [] [CBlockStmt nonCompound] undefined)
prettyElse (CIf else_if_expr else_if_stat else_stat _) =
text "else if" <+> parens (pretty else_if_expr)
$+$ prettyBody else_if_stat
$$ maybeP prettyElse else_stat
prettyElse else_stmt =
text "else"
$+$ prettyBody else_stmt
pretty (CSwitch expr stat _) =
ii $ text "switch" <+> text "(" <> pretty expr <> text ")"
$+$ prettyPrec (1) stat
pretty (CWhile expr stat False _) =
ii $ text "while" <+> text "(" <> pretty expr <> text ")"
$+$ prettyPrec (1) stat
pretty (CWhile expr stat True _) =
ii $ text "do" $+$ prettyPrec (1) stat
$$ text "while" <+> text "(" <> pretty expr <> text ");"
pretty (CFor for_init cond step stat _) =
ii $ text "for" <+> text "("
<> either (maybeP pretty) pretty for_init <> semi
<+> maybeP pretty cond <> semi
<+> maybeP pretty step <> text ")" $+$ prettyPrec (1) stat
pretty (CGoto ident _) = ii $ text "goto" <+> identP ident <> semi
pretty (CGotoPtr expr _) = ii $ text "goto" <+> text "*" <+> prettyPrec 30 expr <> semi
pretty (CCont _) = ii $ text "continue" <> semi
pretty (CBreak _) = ii $ text "break" <> semi
pretty (CReturn Nothing _) = ii $ text "return" <> semi
pretty (CReturn (Just e) _) = ii $ text "return" <+> pretty e <> semi
pretty (CAsm asmStmt _) = pretty asmStmt
prettyPrec p (CCompound localLabels bis _) =
let inner = text "{" $+$ mlistP ppLblDecls localLabels $+$ vcat (map pretty bis) $$ text "}"
in if p == 1 then inner else ii inner
where ppLblDecls = vcat . map (\l -> text "__label__" <+> identP l <+> semi)
prettyPrec _ p = pretty p
instance Pretty CAsmStmt where
pretty (CAsmStmt tyQual expr outOps inOps clobbers _) =
ii $ text "__asm__" <+>
maybeP pretty tyQual <>
parens asmStmt <> semi
where
asmStmt = pretty expr <+>
(if all null [inOps,outOps] && null clobbers then empty else ops)
ops = text ":" <+> hcat (punctuate comma (map pretty outOps)) <+>
text ":" <+> hcat (punctuate comma (map pretty inOps)) <+>
(if null clobbers then empty else clobs)
clobs = text ":" <+> hcat (punctuate comma (map pretty clobbers))
instance Pretty CAsmOperand where
pretty (CAsmOperand mArgName cnstr expr _) =
maybeP (\argName -> text "[" <> identP argName <> text "]") mArgName <+>
pretty cnstr <+>
parens (pretty expr)
instance Pretty CBlockItem where
pretty (CBlockStmt stat) = pretty stat
pretty (CBlockDecl decl) = ii $ pretty decl <> semi
pretty (CNestedFunDef fundef) = ii $ pretty fundef
instance Pretty CDecl where
pretty (CDecl specs divs _) =
hsep (map pretty checked_specs) <+> hsep (punctuate comma (map p divs))
where
p (declr, initializer, expr) =
maybeP (prettyDeclr False 0) declr <+>
maybeP ((text ":" <+>) . pretty) expr <+>
attrlistP (getAttrs declr) <+>
maybeP ((text "=" <+>) . pretty) initializer
checked_specs =
case any isAttrAfterSUE (zip specs (tail specs)) of
True -> trace
("Warning: AST Invariant violated: __attribute__ specifier following struct/union/enum:"++
(show $ map pretty specs))
specs
False -> specs
isAttrAfterSUE (CTypeSpec ty,CTypeQual (CAttrQual _)) = isSUEDef ty
isAttrAfterSUE _ = False
getAttrs Nothing = []
getAttrs (Just (CDeclr _ _ _ cattrs _)) = cattrs
instance Pretty CDeclSpec where
pretty (CStorageSpec sp) = pretty sp
pretty (CTypeSpec sp) = pretty sp
pretty (CTypeQual qu) = pretty qu
instance Pretty CStorageSpec where
pretty (CAuto _) = text "auto"
pretty (CRegister _) = text "register"
pretty (CStatic _) = text "static"
pretty (CExtern _) = text "extern"
pretty (CTypedef _) = text "typedef"
pretty (CThread _) = text "__thread"
instance Pretty CTypeSpec where
pretty (CVoidType _) = text "void"
pretty (CCharType _) = text "char"
pretty (CShortType _) = text "short"
pretty (CIntType _) = text "int"
pretty (CLongType _) = text "long"
pretty (CFloatType _) = text "float"
pretty (CDoubleType _) = text "double"
pretty (CSignedType _) = text "signed"
pretty (CUnsigType _) = text "unsigned"
pretty (CBoolType _) = text "_Bool"
pretty (CComplexType _) = text "_Complex"
pretty (CSUType union _) = pretty union
pretty (CEnumType enum _) = pretty enum
pretty (CTypeDef ident _) = identP ident
pretty (CTypeOfExpr expr _) =
text "typeof" <> text "(" <> pretty expr <> text ")"
pretty (CTypeOfType decl _) =
text "typeof" <> text "(" <> pretty decl <> text ")"
instance Pretty CTypeQual where
pretty (CConstQual _) = text "const"
pretty (CVolatQual _) = text "volatile"
pretty (CRestrQual _) = text "__restrict"
pretty (CInlineQual _) = text "inline"
pretty (CAttrQual a) = attrlistP [a]
instance Pretty CStructUnion where
pretty (CStruct tag ident Nothing cattrs _) = pretty tag <+> attrlistP cattrs <+> maybeP identP ident
pretty (CStruct tag ident (Just []) cattrs _) =
pretty tag <+> attrlistP cattrs <+> maybeP identP ident <+> text "{ }"
pretty (CStruct tag ident (Just decls) cattrs _) = vcat [
pretty tag <+> attrlistP cattrs <+> maybeP identP ident <+> text "{",
ii $ sep (map (<> semi) (map pretty decls)),
text "}"]
instance Pretty CStructTag where
pretty CStructTag = text "struct"
pretty CUnionTag = text "union"
instance Pretty CEnum where
pretty (CEnum enum_ident Nothing cattrs _) = text "enum" <+> attrlistP cattrs <+> maybeP identP enum_ident
pretty (CEnum enum_ident (Just vals) cattrs _) = vcat [
text "enum" <+> attrlistP cattrs <+> maybeP identP enum_ident <+> text "{",
ii $ sep (punctuate comma (map p vals)),
text "}"] where
p (ident, expr) = identP ident <+> maybeP ((text "=" <+>) . pretty) expr
instance Pretty CDeclr where
prettyPrec prec declr = prettyDeclr True prec declr
prettyDeclr :: Bool -> Int -> CDeclr -> Doc
prettyDeclr show_attrs prec (CDeclr name derived_declrs asmname cattrs _) =
ppDeclr prec (reverse derived_declrs) <+> prettyAsmName asmname <+> ifP show_attrs (attrlistP cattrs)
where
ppDeclr _ [] = maybeP identP name
ppDeclr p (CPtrDeclr quals _ : declrs) =
parenPrec p 5 $ text "*" <+> hsep (map pretty quals) <+> ppDeclr 5 declrs
ppDeclr p (CArrDeclr quals size _ : declrs) =
parenPrec p 6 $ ppDeclr 6 declrs <> brackets (hsep (map pretty quals) <+> pretty size)
ppDeclr _ (CFunDeclr params fun_attrs _ : declrs) =
(if not (null fun_attrs) then parens (attrlistP fun_attrs <+> ppDeclr 5 declrs) else ppDeclr 6 declrs)
<> parens (prettyParams params)
prettyParams (Right (decls, isVariadic)) =
sep (punctuate comma (map pretty decls))
<> (if isVariadic then text "," <+> text "..." else empty)
prettyParams (Left oldStyleIds) =
hsep (punctuate comma (map identP oldStyleIds))
prettyAsmName asm_name_opt
= maybe empty (\asm_name -> text "__asm__" <> parens (pretty asm_name)) asm_name_opt
instance Pretty CArrSize where
pretty (CNoArrSize completeType) = ifP completeType (text "*")
pretty (CArrSize staticMod expr) = ifP staticMod (text "static") <+> pretty expr
instance Pretty CInit where
pretty (CInitExpr expr _) = pretty expr
pretty (CInitList initl _) =
text "{" <+> hsep (punctuate comma (map p initl)) <+> text "}" where
p ([], initializer) = pretty initializer
p (desigs, initializer) = hsep (map pretty desigs) <+> text "=" <+> pretty initializer
instance Pretty CDesignator where
pretty (CArrDesig expr _) = text "[" <> pretty expr <> text "]"
pretty (CMemberDesig ident _) = text "." <> identP ident
pretty (CRangeDesig expr1 expr2 _) =
text "[" <> pretty expr1 <+> text "..." <+> pretty expr2 <> text "]"
instance Pretty CAttr where
pretty (CAttr attrName [] _) = identP attrName
pretty (CAttr attrName attrParams _) = identP attrName <> parens (hsep . punctuate comma . map pretty $ attrParams)
instance Pretty CExpr where
prettyPrec p (CComma exprs _) =
parenPrec p (1) $ hsep (punctuate comma (map (prettyPrec 2) exprs))
prettyPrec p (CAssign op expr1 expr2 _) =
parenPrec p 2 $ prettyPrec 3 expr1 <+> pretty op <+> prettyPrec 2 expr2
prettyPrec p (CCond expr1 expr2 expr3 _) =
parenPrec p 2 $ prettyPrec 4 expr1 <+> text "?"
<+> maybeP pretty expr2 <+> text ":" <+> prettyPrec 4 expr3
prettyPrec p (CBinary op expr1 expr2 _) =
let prec = binPrec op
in parenPrec p prec $ prettyPrec prec expr1
<+> pretty op <+> prettyPrec (prec + 1) expr2
prettyPrec p (CCast decl expr _) =
parenPrec p 25 $ text "(" <> pretty decl <> text ")"
<+> prettyPrec 25 expr
prettyPrec p (CUnary CPostIncOp expr _) =
parenPrec p 26 $ prettyPrec 26 expr <> text "++"
prettyPrec p (CUnary CPostDecOp expr _) =
parenPrec p 26 $ prettyPrec 26 expr <> text "--"
prettyPrec p (CUnary op expr@(CUnary _ _ _) _) =
parenPrec p 25 $ pretty op <+> parens (prettyPrec 25 expr)
prettyPrec p (CUnary op expr _) =
parenPrec p 25 $ pretty op <> prettyPrec 25 expr
prettyPrec p (CSizeofExpr expr _) =
parenPrec p 25 $ text "sizeof" <> parens (pretty expr)
prettyPrec p (CSizeofType decl _) =
parenPrec p 25 $ text "sizeof" <> parens (pretty decl)
prettyPrec p (CAlignofExpr expr _) =
parenPrec p 25 $ text "__alignof" <> parens (pretty expr)
prettyPrec p (CAlignofType decl _) =
parenPrec p 25 $ text "__alignof" <> parens (pretty decl)
prettyPrec p (CComplexReal expr _) =
parenPrec p 25 $ text "__real" <+> prettyPrec 25 expr
prettyPrec p (CComplexImag expr _) =
parenPrec p 25 $ text "__imag" <+> prettyPrec 25 expr
prettyPrec p (CIndex expr1 expr2 _) =
parenPrec p 26 $ prettyPrec 26 expr1
<> text "[" <> pretty expr2 <> text "]"
prettyPrec p (CCall expr args _) =
parenPrec p 30 $ prettyPrec 30 expr <> text "("
<> (sep . punctuate comma . map pretty) args <> text ")"
prettyPrec p (CMember expr ident deref _) =
parenPrec p 26 $ prettyPrec 26 expr
<> text (if deref then "->" else ".") <> identP ident
prettyPrec _p (CVar ident _) = identP ident
prettyPrec _p (CConst constant) = pretty constant
prettyPrec _p (CCompoundLit decl initl _) =
parens (pretty decl) <+> (braces . hsep . punctuate comma) (map p initl) where
p ([], initializer) = pretty initializer
p (mems, initializer) = hcat (punctuate (text ".") (map pretty mems)) <+> text "=" <+> pretty initializer
prettyPrec _p (CStatExpr stat _) =
text "(" <> pretty stat <> text ")"
prettyPrec _p (CLabAddrExpr ident _) = text "&&" <> identP ident
prettyPrec _p (CBuiltinExpr builtin) = pretty builtin
instance Pretty CBuiltin where
pretty (CBuiltinVaArg expr ty_name _) =
text "__builtin_va_arg" <+>
(parens $ pretty expr <> comma <+> pretty ty_name)
pretty (CBuiltinOffsetOf ty_name (CMemberDesig field1 _ : desigs) _) =
text "__builtin_offsetof" <+>
(parens $ pretty ty_name <> comma <+> identP field1 <> hcat (map pretty desigs) )
pretty (CBuiltinOffsetOf _ty_name otherDesigs _) =
error $ "Inconsistent AST: Cannot interpret designators in offsetOf: "++ show (hcat$ map pretty otherDesigs)
pretty (CBuiltinTypesCompatible ty1 ty2 _) =
text "__builtin_types_compatible_p" <+>
(parens $ pretty ty1 <> comma <+> pretty ty2)
instance Pretty CAssignOp where
pretty op = text $ case op of
CAssignOp -> "="
CMulAssOp -> "*="
CDivAssOp -> "/="
CRmdAssOp -> "%="
CAddAssOp -> "+="
CSubAssOp -> "-="
CShlAssOp -> "<<="
CShrAssOp -> ">>="
CAndAssOp -> "&="
CXorAssOp -> "^="
COrAssOp -> "|="
instance Pretty CBinaryOp where
pretty op = text $ case op of
CMulOp -> "*"
CDivOp -> "/"
CRmdOp -> "%"
CAddOp -> "+"
CSubOp -> "-"
CShlOp -> "<<"
CShrOp -> ">>"
CLeOp -> "<"
CGrOp -> ">"
CLeqOp -> "<="
CGeqOp -> ">="
CEqOp -> "=="
CNeqOp -> "!="
CAndOp -> "&"
CXorOp -> "^"
COrOp -> "|"
CLndOp -> "&&"
CLorOp -> "||"
instance Pretty CUnaryOp where
pretty op = text $ case op of
CPreIncOp -> "++"
CPreDecOp -> "--"
CPostIncOp -> "++"
CPostDecOp -> "--"
CAdrOp -> "&"
CIndOp -> "*"
CPlusOp -> "+"
CMinOp -> "-"
CCompOp -> "~"
CNegOp -> "!"
instance Pretty CConst where
pretty (CIntConst int_const _) = text (show int_const)
pretty (CCharConst chr _) = text (show chr)
pretty (CFloatConst flt _) = text (show flt)
pretty (CStrConst str _) = text (show str)
instance Pretty CStrLit where
pretty (CStrLit str _) = text (show str)
binPrec :: CBinaryOp -> Int
binPrec CMulOp = 20
binPrec CDivOp = 20
binPrec CRmdOp = 20
binPrec CAddOp = 19
binPrec CSubOp = 19
binPrec CShlOp = 18
binPrec CShrOp = 18
binPrec CLeOp = 17
binPrec CGrOp = 17
binPrec CLeqOp = 17
binPrec CGeqOp = 17
binPrec CEqOp = 16
binPrec CNeqOp = 16
binPrec CAndOp = 15
binPrec CXorOp = 14
binPrec COrOp = 13
binPrec CLndOp = 12
binPrec CLorOp = 11