% % (c) The Foo Project, Universities of Glasgow & Utrecht, 1997-8 % % @(#) $Docid: Feb. 9th 2003 14:51 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Pretty printing the Core IDL type \begin{code} module PpCore where import CoreIDL import Literal import BasicTypes import PP import Opts ( optDebug, optHaskellToC, optShortHeader, optCompilingMsIDL ) import List ( partition ) import Utils ( mapMb, notNull ) import Attribute ( hasAttributeWithName ) import Maybe ( fromMaybe ) import Char ( isAlphaNum, toUpper ) \end{code} The Doc type when pretty printing core IDL carries around a flag indicating how much info we should print out. \begin{code} type CoreDoc = PPDoc ( Bool -- output debugging information? , Bool -- output as C decls? , Bool -- within a C comment? , Bool -- expand library decls? , String -- type of the 'this' pointer. -- "" => not processing an object interface. ) showCore :: CoreDoc -> String showCore cd = showPPDoc cd (optDebug, False, False, True, "") showHeader :: String -> CoreDoc -> String showHeader fname cd = showPPDoc (text "#ifndef" <+> text fname' $$ text "#define" <+> text fname' $$ cd $$ text "#endif") (False, True{-as C-}, False, True, "") where uu = "__" fname' = uu ++ map canon fname ++ uu canon x | isAlphaNum x = toUpper x | otherwise = '_' ppCore :: [Decl] -> CoreDoc ppCore ls = vsep (map ppDecl ls) $$ text "" ppHeaderDecl :: [Id] -> Decl -> CoreDoc ppHeaderDecl is d = vsep (map forwardDecl is) $$ ppDecl d $$ text "" where forwardDecl i | is_object = let nm = idName i in text "#ifndef __" <> text nm <> text "_FWD_DEFINED__" $$ text "#define __" <> text nm <> text "_FWD_DEFINED__" $$ text "typedef struct" <+> text nm <+> text nm <> semi $$ text "#endif" | otherwise = empty where attrs = idAttributes i is_object = attrs `hasAttributeWithName` "object" || attrs `hasAttributeWithName` "odl" setDebug :: Bool -> CoreDoc -> CoreDoc setDebug deb d = getPPEnv $ \ (_,as_c,comment,flg,str) -> setPPEnv (deb,as_c,comment,flg,str) d getCommentFlag :: (Bool -> CoreDoc) -> CoreDoc getCommentFlag cont = getPPEnv $ \ (_,_,comment,_,_) -> cont comment inComment :: CoreDoc -> CoreDoc inComment d1 = getPPEnv $ \ (deb,c,_,flg,str) -> setPPEnv (deb,c,True,flg,str) d1 setLibFlag :: Bool -> CoreDoc -> CoreDoc setLibFlag flg d = getPPEnv $ \ (deb,as_c,comment,_,str) -> setPPEnv (deb,as_c,comment,flg,str) d ifTopLevLib :: CoreDoc -> CoreDoc -> CoreDoc ifTopLevLib if_true if_false = getPPEnv $ \ (_,_,_,flg,_) -> if flg then if_true else if_false ifC :: CoreDoc -> CoreDoc -> CoreDoc ifC onTrue onFalse = getPPEnv $ \ (_,flg,_,_,_) -> if flg then onTrue else onFalse setThisType :: String -> CoreDoc -> CoreDoc setThisType this_ty d = getPPEnv $ \ (deb,as_c,comment,flg,_) -> setPPEnv (deb,as_c,comment,flg,this_ty) d getThisType :: (String -> CoreDoc) -> CoreDoc getThisType cont = getPPEnv $ \ (_,_,_,_,this_ty) -> cont this_ty whenNotC :: CoreDoc -> CoreDoc whenNotC d = ifC empty d commentOutIfC :: CoreDoc -> CoreDoc commentOutIfC d = ifC (commentOut d) d commentOut :: CoreDoc -> CoreDoc commentOut d = getCommentFlag $ \ flg -> start_comment flg <> inComment d <> end_comment flg where start_comment insideComment | insideComment = empty | otherwise = text "/*" end_comment insideComment | insideComment = empty | otherwise = text "*/" getIfC :: (Bool -> CoreDoc) -> CoreDoc getIfC cont = getPPEnv $ \ (_,flg,_,_,_) -> cont flg ifDebug :: CoreDoc -> CoreDoc -> CoreDoc ifDebug onTrue onFalse = getPPEnv $ \ (flg,_,_,_,_) -> if flg then onTrue else onFalse \end{code} \begin{code} ppDecl :: Decl -> CoreDoc ppDecl (Typedef i t orig_ty) = (if ignorable then commentOutIfC else id) $ case orig_ty of FunTy cc res ps -> text "typedef" <+> ppType (resultType res) <+> parens ( ppCallConv True cc <+> char '*' <> ppId i id) <> ppTuple (map ppParam ps) <> semi _ -> ifC (ppId i (\ x -> text "typedef" <+> x <+> ppType orig_ty)) (ppId i (\ x -> text "typedef" <+> x <+> ppType t)) <> semi where ignorable = idAttributes i `hasAttributeWithName` "ignore" ppDecl (Constant i _ o_t e) = ifC (text "#define") (text "const") <+> ppId i (<> ifC empty (ppType o_t)) <+> ifC empty equals <+> ppExpr e <> ifC empty semi ppDecl (Interface i is_ref inherit decls) | is_ref = ifC empty (ppId i ($+$ (text "interface")) <> semi) | otherwise = ifC pprIface ((hang (ppIdVert i ($+$ (text "interface")) <+> pp_inherit <+> char '{') 3 (ppCoreDecls decls (map ppDecl decls))) $$ char '}' <> semi) where attrs = idAttributes i is_object = attrs `hasAttributeWithName` "object" || attrs `hasAttributeWithName` "odl" pprIface | optShortHeader || pure_dispatch = commentOutIfC (text "interface" <+> text (idOrigName i) <+> text "{};") | not is_object = text "typedef struct" <+> text (idOrigName i) <+> char '*' <> text (idOrigName i) <> semi | otherwise = -- text "typedef struct" <+> text (idOrigName i) <+> text (idOrigName i) <> semi $$ ppCoreDecls non_meth_decls (map ppDecl non_meth_decls) $$ (hang (text "typedef struct" <+> text (idOrigName i ++ "Vtbl") <+> char '{') 3 ((if optHaskellToC then id else setThisType (idOrigName i)) ( ppInhMethodFiller $$ ppCoreDecls meth_decls (map ppDecl the_meth_decls)))) $$ char '}' <+> text (idName i ++ "Vtbl") <> semi $$ text "" $$ hang (text "struct" <+> text (idName i) <+> char '{') 2 (text "struct" <+> text (idName i ++ "Vtbl") <+> char '*' <> text "lpVtbl" <> semi $$ char '}' <> semi) $$ if optHaskellToC then empty else text "#ifdef COBJMACROS" $$ vcat (map (mkObjMacros (idName i)) decls) $$ text "#endif" <+> commentOut (text "COBJMACROS") the_meth_decls = map (removeAttrs) meth_decls removeAttrs m = m{declId=(declId m){idAttributes=[]}} (meth_decls, non_meth_decls) = partition isMethod decls ppInhMethodFiller | is_idispatch || is_iunknown = empty | is_dispatch = ppDecls (map (\ x -> text "void*" <+> text ("reserved"++show x)) [(0::Int)..6]) | otherwise = ppDecls (map (\ x -> text "void*" <+> text ("reserved"++show x)) [(0::Int)..2]) is_dispatch = any (\ x -> qName (fst x) == "IDispatch") inherit (is_idispatch, is_iunknown) = case (idOrigName i) of "IDispatch" -> (True, False) "IUnknown" -> (False, True) _ -> (False, False) pure_dispatch = not is_idispatch && is_dispatch && not has_dual has_dual = (idAttributes i) `hasAttributeWithName` "dual" isMethod (Method _ _ _ _ _) = True isMethod _ = False mkObjMacros if_nm (Method methId _ _ args _) = hang (text "#define" <+> text (if_nm ++ '_':idOrigName methId) <> arg_list <+> char '\\') 5 (text "(This)->lpVtbl->" <> text (idOrigName methId) <> arg_list) where arg_list = parens (hcat (punctuate comma (map text ("This" : map (idName.paramId) args)))) mkObjMacros _ _ = empty pp_inherit | not optDebug && optCompilingMsIDL = case inherit of [] -> empty ((x,_):_) -> char ':' <+> text (qName x) | otherwise = case inherit of [] -> empty ls -> char ':' <+> hsep (punctuate comma (map (\ (x,y) -> ppQualName x <> commentOut (text (show y))) ls)) ppDecl (Module i decls) = ifC (ppCoreDecls decls (map ppDecl decls)) (hang (ppIdVert i ($+$ (text "module")) <+> char '{') 3 (ppCoreDecls decls (map ppDecl decls)) $$ text "};") ppDecl (DispInterface i (Just d) _ _) = ifC (commentOutIfC (text "dispinterface" <+> text (idName i) <+> text "{};")) (hang (ppIdVert i ($+$ (text "dispinterface")) <+> char '{') 3 (ppId (declId d) (\ x -> x <+> text "interface") <> semi $$ ifDebug (ppDecl d) empty) $$ whenNotC (text "};")) ppDecl (DispInterface i _ props meths) = ifC (commentOutIfC (text "dispinterface" <+> text (idName i) <+> text "{};")) (hang (ppIdVert i ($+$ (text "dispinterface")) <+> char '{') 3 (hang (text "properties:") 2 (ppDecls (map ppDecl props)) $$ hang (text "methods:") 2 (ppCoreDecls meths (map ppDecl meths))) $$ text "};") ppDecl (Library i []) | not optDebug = text "importlib" <> parens (text (show (idName i))) <> semi ppDecl (Library i decls) = ifC (commentOutIfC (text "library" <+> text (idOrigName i))) (ppIdVert i ($+$ (text "library"))) $$ ifTopLevLib (commentOutIfC (char '{') $$ setLibFlag False (ppFwdDecls $$ ppCoreDecls decls (map ppDecl decls)) $$ commentOutIfC (text "};")) (commentOutIfC (text "{};")) where ifaces = filter isInterface decls ppFwdDecls = vsep (map ppFwdDecl ifaces) ppFwdDecl (Interface ifaceId _ inherit _) = ifC (if pure_dispatch then empty else text "typedef struct" <+> text (idOrigName ifaceId) <+> text (idOrigName ifaceId) <> semi) (text "interface" <+> text (idOrigName ifaceId) <> semi) where is_idispatch = case (idOrigName ifaceId) of "IDispatch" -> True _ -> False pure_dispatch = not is_idispatch && is_dispatch && not has_dual is_dispatch = any (\ x -> qName (fst x) == "IDispatch") inherit has_dual = (idAttributes ifaceId) `hasAttributeWithName` "dual" ppFwdDecl _ = empty isInterface Interface{} = True isInterface _ = False ppDecl (CoClass i decls) = ifC (commentOutIfC (text "coclass" <+> text (idOrigName i) <+> text "{};")) (hang (ppIdVert i ($+$ (text "coclass")) <+> char '{') 3 (ppDecls (map ppCoClassDecl decls)) $$ commentOutIfC (text "};")) ppDecl (Property i ty _ _ _) = ppId i ($$ (ppType ty)) <> semi ppDecl (Method i cconv res args _) = getThisType $ \ str -> (if (null str) then -- not an object method. ppId i ($$ (ifC (text "extern") empty <+> ppResult res <+> ppCallConv True cconv)) else ppResult res <+> parens ( ppMethodId i (\ x -> ppCallConv True cconv <+> char '*' <> x))) <+> pp_param <> semi where ppMethodId mid cont = ifC (ppId i' cont) (ppId mid cont) attrs = idAttributes i i' | attrs `hasAttributeWithName` "propget" = i{idOrigName="get"++idOrigName i} | attrs `hasAttributeWithName` "propput" = i{idOrigName="put"++idOrigName i} | attrs `hasAttributeWithName` "propputref" = i{idOrigName="put"++idOrigName i} | otherwise = i pp_param = getThisType $ \ str -> let ty = Pointer Ref False (Name str str Nothing Nothing Nothing Nothing) args' | null str = args | otherwise = (Param (Id "This" "This" Nothing []) In ty ty False):args in ppTupleVert (map ppParam args') ppDecl (HsLiteral str) = whenNotC (text "haskell" <> parens (text str) <> semi) ppDecl (CInclude str) = text "include" <+> text str ppDecl (CLiteral str) = ifC (text str) (text "cpp_quote" <> parens (text str)) ppCoreDecls :: [Decl] -> [CoreDoc] -> CoreDoc ppCoreDecls [] [] = empty ppCoreDecls (CInclude _ : as) (b:bs) = b $$ ppCoreDecls as bs ppCoreDecls (CLiteral _ : as) (b:bs) = b $$ ppCoreDecls as bs ppCoreDecls (_:as) (b:bs) = b $$ ppCoreDecls as bs ppCoreDecls [] as = vcat as ppCoreDecls _ [] = empty ppCoClassDecl :: CoClassDecl -> CoreDoc ppCoClassDecl (CoClassInterface i _) = ppId i (<+> whenNotC (text "interface")) ppCoClassDecl (CoClassDispInterface i _) = ppId i (<+> whenNotC (text "dispinterface")) \end{code} @ppId@ takes the extra function argument to allow the attributes to be printed not immediately next to the identifier name. For instance, when printing out an interface decl, the attributes of the interface id should be prefixed to the "interface" keyword rather than next to the id. \begin{code} ppId :: Id -> (CoreDoc -> CoreDoc) -> CoreDoc ppId i ty | notNull attrs = (ty (commentOutIfC (ppList (map ppAttr attrs)))) <+> ppModule i | otherwise = ty empty <+> ppModule i where attrs = idAttributes i -- -- [ a1 -- , a2 -- .. -- ] -- foo ppIdVert :: Id -> (CoreDoc -> CoreDoc) -> CoreDoc ppIdVert i ty | notNull attrs' = (ty (commentOutIfC (ppListVert (map ppAttr attrs')))) <+> ppModule i | otherwise = ty empty <+> ppModule i where attrs = idAttributes i attrs' = filter notIsAny attrs notIsAny (Attribute "any" _) = False notIsAny _ = True -- print the name of an Id, and possibly what -- file/module it is coming from. ppModule :: Id -> CoreDoc ppModule i = ifDebug (text $ case mb_mod of Nothing -> nm Just m -> m++'.':nm) (text (idOrigName i)) where mb_mod = idModule i nm = idName i \end{code} \begin{code} ppAttr :: Attribute -> CoreDoc ppAttr (AttrMode dir) = ppDirection dir ppAttr (Attribute nm []) = text nm ppAttr (Attribute nm ps) = text nm <> ppTuple (map ppAttrParam ps) ppAttr (AttrDependent r ps) = ppDepReason r <> ppTuple (map ppAttrParam ps) ppAttrParam :: AttributeParam -> CoreDoc ppAttrParam (ParamLit l) = ppLit l ppAttrParam (ParamType t) = ppType t ppAttrParam (ParamExpr e) = ppExpr e ppAttrParam (ParamVar n) = text n ppAttrParam ParamVoid = empty ppAttrParam (ParamPtr a) = char '*' <> ppAttrParam a ppDepReason :: DepReason -> CoreDoc ppDepReason r = text $ case r of SizeIs -> "size_is" LengthIs -> "length_is" LastIs -> "last_is" FirstIs -> "first_is" MaxIs -> "max_is" MinIs -> "min_is" SwitchIs -> "switch_is" \end{code} \begin{code} ppType :: Type -> CoreDoc ppType (Integer Natural True) = text "int" ppType (Integer LongLong True) = text "int64" ppType (Integer LongLong False) = text "uint64" ppType (Integer sz signed) = (if signed then empty else text "unsigned") <+> ppSize sz ppType StablePtr = ifC (text "unsigned long") (text "stablePtr") -- (res (*)(args)) ppType (FunTy cc res params) = ppFunTy cc res params ppType (Float sz) = case sz of Short -> text "float" Long -> text "double" LongLong -> text "long double" Natural -> text "float" ppType (Char signed) | signed = text "signed char" | otherwise = text "char" ppType WChar = text "wchar_t" ppType Bool = text "boolean" ppType Octet = text "char" ppType Any = text "any" ppType Object = text "Object" ppType (String _ isUnique mb_expr) = ifC (text "char*") ((if isUnique then text "[unique]" else empty) <> text "string" <> pp_expr) where pp_expr = fromMaybe empty (mapMb (\ e -> char '<' <> ppExpr e <> char '>') mb_expr) ppType (WString isUnique mb_expr) = ifC (text "WCHAR*") ((if isUnique then text "[unique]" else empty) <> text "wstring" <> pp_expr) where pp_expr = fromMaybe empty (mapMb (\ e -> char '<' <> ppExpr e <> char '>') mb_expr) ppType (Sequence t mb_expr _) = text "sequence" <> char '<' <> ppType t <> pp_expr where pp_expr = fromMaybe (char '>') (mapMb (\ e -> comma <> ppExpr e <> char '>') mb_expr) ppType (Fixed e i) = text "fixed" <> char '<' <> ppExpr e <> comma <> ppILit i <> char '>' --ppType (Name nm onm mod attrs (Just ty) _) = ppModule (Id nm nm mod (fromMaybe [] attrs)) <> parens (ppType ty) ppType (Name nm onm md attrs _ _) = ppModule (Id nm onm md (fromMaybe [] attrs)) ppType (Struct i [] _) = text "struct" <+> ppId i id ppType (Struct i fields _) = hang (ppId i (<> text "struct") <+> char '{') 3 (ppDecls (map ppField fields)) $$ char '}' ppType (Enum i _ vals) = (hang (ppId i (<> text "enum") <+> char '{') 3 (vsep (punctuate comma (map ppEnumValue vals)))) $$ char '}' ppType (Union nm switch_ty switch_nm union_nm switches) = hang (ppId nm (<> text "union") <+> commentOutIfC (text "switch" <> parens (ppId switch_nm (<> (ppType switch_ty))) <+> ppId union_nm id) <+> char '{') 3 (ppDecls (map (ppSwitch True) switches)) $$ char '}' ppType (UnionNon tag switches) = hang (ppId tag (<> text "union") <+> char '{') 3 (ppDecls (map (ppSwitch False) switches)) $$ char '}' ppType (CUnion i fields _) = hang (ppId i (<> text "union") <+> char '{') 3 (ppDecls (map ppField fields)) $$ char '}' ppType (Pointer pt _ ty) = ppType ty <> ppPointerType pt <> char '*' ppType (Array t dims) = ppArray empty t dims ppType Void = text "void" ppType (Iface nm md onm _ _ _) = ppModule (Id nm onm md []) ppType (SafeArray t) = text "SAFEARRAY" <> ifC (char '*') (parens (ppType t)) -- In .h mode, we run the risk of nested comments here -- should we have a SAFEARRAY of a SAFEARRAY (yes, it does -- happen!). ppEnumValue :: EnumValue -> CoreDoc ppEnumValue (EnumValue vi (Left val)) = ppId vi id <+> equals <+> text (show val) ppEnumValue (EnumValue vi (Right e)) = ppId vi id <+> equals <+> ppExpr e ppArray :: CoreDoc -> Type -> [Expr] -> CoreDoc ppArray d t dims = ppType t <+> d <> ppArrayDims dims ppPointerType :: PointerType -> CoreDoc ppPointerType pt = ifDebug (char '{' <> p <> char '}') empty where p = char $ case pt of Ref -> 'r' Ptr -> 'p' Unique -> 'u' ppFunTy :: CallConv -> Result -> [Param] -> CoreDoc ppFunTy cc res params = parens (ppResult res <+> parens (ppCallConv True cc <+> char '*') <> ppTuple (map ppParam params)) ppArrayDims :: [Expr] -> CoreDoc ppArrayDims [] = ifC (text "[1]") (text "[]") ppArrayDims [e] = char '[' <> ppExpr e <> char ']' ppArrayDims [l,h] = char '[' <> ppExpr l <+> text ".." <+> ppExpr h <> char ']' ppArrayDims _ = error "PpCore.ppArrayDims: don't know how to handle an interval with more than elts" \end{code} \begin{code} ppExpr :: Expr -> CoreDoc ppExpr e = case e of Binary bop e1 e2 -> ppExpr e1 <+> ppBinaryOp bop <+> ppExpr e2 Cond e1 e2 e3 -> ppExpr e1 <+> char '?' <+> ppExpr e2 <+> char ':' <+> ppExpr e3 Unary op e1 -> parens (ppUnaryOp op <+> ppExpr e1) Var nm -> text nm Lit l -> ppLit l Cast t e1 -> parens (ppType t) <> parens (ppExpr e1) Sizeof t -> text "sizeof" <> parens (ppType t) \end{code} \begin{code} ppParam :: Param -> CoreDoc ppParam (Param i _ (Array t e) _ _) = ppArray (ppId i id) t e ppParam (Param i _ t orig_ty _) = ppId i (<> (ppType t <> ppOrig)) where ppOrig = ifDebug (parens (ppType orig_ty)) empty ppSwitch :: Bool -> Switch -> CoreDoc ppSwitch inEncUn (SwitchEmpty Nothing) | inEncUn = commentOutIfC (text "default: ") | otherwise = commentOutIfC (text "[default] ") ppSwitch inEncUn (SwitchEmpty (Just ls)) = commentOutIfC (ppCaseLabels inEncUn (map fst ls)) ppSwitch inEncUn (Switch i labs t orig_ty) = hang (commentOutIfC $ ppCaseLabels inEncUn labs) 3 (ppId i (<> (ppType t <> ppOrig))) where ppOrig = ifDebug (parens (ppType orig_ty)) empty ppField :: Field -> CoreDoc ppField (Field i (Array t es) _ _ _) = ppArray (ppId i id) t es ppField (Field i t orig_ty mb_sz _) = ppId i (<> (ppType t <> ppOrig)) <> pp_bit_field where pp_bit_field = case mb_sz of Nothing -> empty Just x -> char ':' <+> text (show x) ppOrig = ifDebug (parens (ppType orig_ty)) empty ppResult :: Result -> CoreDoc ppResult (Result red_ty orig_ty) = ifDebug (ppType red_ty <> parens (ppType orig_ty)) (ppType orig_ty) ppCaseLabels :: Bool -> [CaseLabel] -> CoreDoc ppCaseLabels inEncUn ls | inEncUn = hsep (punctuate (text ": ") (map ppCaseLabel ls)) | otherwise = brackets (hsep (punctuate comma (map ppCaseLabel ls))) ppCaseLabel :: CaseLabel -> CoreDoc ppCaseLabel Default = text "default" ppCaseLabel (Case e) = text "case" <+> ppExpr e \end{code}