% % @(#) $Docid: Feb. 9th 2003 16:35 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % \begin{code} module BasicTypes ( Name , QualName , qName , qOrigName , qModule , qDefModule , mkQualName , toQualName , prefixQName , prefixAppQName , setOrigQName , ScopedName , Inherit , GUID , Size(..) , CallConv(..) , BinaryOp(..) , UnaryOp(..) , ShiftDir(..) , Qualifier(..) , PointerType(..) , ParamDir(..) , isInOut , ppBinaryOp , ppUnaryOp , ppQualifier , ppSize , ppCallConv , ppName , ppQualName , ppDirection , strToCallConv , EnumKind(..) , classifyProgression ) where import PP import Maybe ( fromMaybe ) import Opts ( optNoQualNames, optIntIsInt ) import Utils ( mapMb ) import Int {- BEGIN_GHC_ONLY import GlaExts END_GHC_ONLY -} \end{code} \begin{code} -- a generic.. name. type Name = String -- a qualified.. Name - used throughout the backend to keep -- track of home & name of types & values. data QualName = QName { qName :: Name, qOrigName :: Name, qModule :: Maybe Name, qDefModule :: Maybe Name -- where the name was originally defined. } deriving Eq mkQualName :: Maybe String -> String -> QualName mkQualName md nm = QName nm nm md Nothing toQualName :: String -> QualName toQualName str = case (break (=='.') (reverse str)) of (_,[]) -> mkQualName Nothing str (mn,_:dm) -> mkQualName (Just (reverse dm)) (reverse mn) setOrigQName :: Name -> QualName -> QualName setOrigQName nm qn = qn{qOrigName=nm} prefixQName :: String -> QualName -> QualName prefixQName v qn = qn{qName=v++qName qn , qDefModule=Nothing} prefixAppQName :: String -> QualName -> QualName prefixAppQName v qn = qn{qName=("(" ++v++" "++(qName qn)++")") , qDefModule=Nothing} -- scoped names is OMG CORBA, as it allows -- multi-level qualifiers on names, e.g., a::b::c type ScopedName = [String] -- an OMG interface can inherit from one or more interfaces. -- DCE/MS IDL: just the one (with COM, you get the effect of -- multiple inheritance from IUnknown.) type Inherit = [Name] -- A five element list type GUID = [String] data Size = Short | Natural | Long | LongLong deriving ( Show -- for Lex debugging only , Eq ) data CallConv = Stdcall | Pascal | Cdecl | Fastcall deriving ( Eq, Show ) strToCallConv :: String -> Maybe CallConv strToCallConv "stdcall" = Just Stdcall strToCallConv "cdecl" = Just Cdecl strToCallConv _ = Nothing \end{code} Arithmetic and logical operators allowed in IDL: \begin{code} data BinaryOp = Xor | Or | And | Shift ShiftDir | Add | Sub | Div | Mod | Mul | LogAnd | LogOr | Gt | Ge | Eq | Le | Lt | Ne deriving ( Eq, Show ) data UnaryOp = Minus | Plus | Not | Negate | Deref deriving ( Eq, Show ) data ShiftDir = L | R deriving ( Eq, Show ) data Qualifier = Const | Volatile deriving ( Show , Eq ) data PointerType = Ptr | Ref | Unique deriving ( Eq, Show ) data ParamDir = In | Out | InOut deriving (Eq,Show) -- for Lex debugging only isInOut :: ParamDir -> Bool isInOut InOut = True isInOut _ = False \end{code} \begin{code} ppBinaryOp :: BinaryOp -> PPDoc a ppBinaryOp op = case op of Xor -> char '^' Or -> char '|' And -> char '&' Shift d -> text (case d of { L -> "<<" ; R -> ">>" }) Add -> char '+' Sub -> char '-' Div -> char '/' Mod -> char '%' Mul -> char '*' LogAnd -> text "&&" LogOr -> text "||" Gt -> char '>' Ge -> text ">=" Eq -> text "==" Le -> text "<=" Lt -> char '<' Ne -> text "/=" ppUnaryOp :: UnaryOp -> PPDoc a ppUnaryOp op = case op of Minus -> char '-' Plus -> char '+' Not -> char '~' Negate -> char '!' Deref -> char '*' ppQualifier :: Qualifier -> PPDoc a ppQualifier Const = text "const" ppQualifier Volatile = text "volatile" \end{code} \begin{code} ppSize :: Size -> PPDoc a ppSize Short = text "short" ppSize Long = text "long" ppSize Natural | optIntIsInt = text "int" | otherwise = text "long" ppSize LongLong = text "long long" ppCallConv :: Bool -> CallConv -> PPDoc a ppCallConv for_c c = text $ case c of Stdcall -> if for_c then "__stdcall" else "stdcall" -- it is hard to find definite information on this, but I believe -- that the Pascal calling convention is after all identical to -- Stdcall. (The MSDN docs seems to be utterly confused as to whether -- arguments are pushed L-to-R or R-to-L.) Pascal -> if for_c then "__stdcall" else "stdcall" -- Pascal -> "pascal" -- _cdecl is not provided with gcc, just omit. Cdecl -> if for_c then "" else "ccall" -- Cdecl -> if for_c then "_cdecl" else "ccall" Fastcall -> "fastcall" ppName :: Name -> PPDoc a ppName nm = text nm ppQualName :: QualName -> PPDoc a ppQualName (QName nm _ md def_mod) | optNoQualNames = text nm | otherwise = case def_mod of Nothing -> (fromMaybe empty (mapMb (\ m -> text m <> char '.') md)) <> text nm Just m -> text m <> char '.' <> text nm instance Show QualName where showsPrec _ q = showString (showPPDoc (ppQualName q) ()) \end{code} \begin{code} ppDirection :: ParamDir -> PPDoc a ppDirection d = text $ case d of In -> "in" Out -> "out" InOut -> "in, out" \end{code} A sequence of enumeration tags is classified according to what common class of progression it represent an instance of. Knowing this may help us generate less Haskell code in the end. (by using the "deriving" mechanism or, in the case of ghc, use its support for going straight between tag values and enum dtors.) \begin{code} data EnumKind = EnumProgression Int -- start offset Int -- step. Note: *may* be < 0. | EnumFlags -- 0, 1, 2, 4, 8, .. Int -- start value | Unclassified deriving ( Show -- for debugging , Eq ) -- assume that the tag sequence is appropriately sorted. -- ('weird' int type of the tags is due to the fact that's -- the one we're using in Core.) classifyProgression :: [Int32] -> EnumKind classifyProgression [] = Unclassified classifyProgression [x] = EnumProgression (fromIntegral x) 0 classifyProgression ls@(x:y:_) | x == y = Unclassified | and (zipWith (==) ls (pow2Series x)) = EnumFlags (fromIntegral x) | and (zipWith (==) ls [x,(x+(y-x))..]) = EnumProgression (fromIntegral x) (fromIntegral (y-x)) | otherwise = Unclassified where pow2Series n = n : pow2Series (doub n) doub n | n == 0 = 1 | otherwise = 2*n \end{code}