%
% (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
, Bool
, Bool
, Bool
, String
)
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, 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 =
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
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
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
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")
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 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))
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}