% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Jan. 30th 2003 14:19 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Generating code for going between IDL enums and their Haskell equivalent. \begin{code} module MarshallEnum ( marshallEnum , genDerivedEnumInstanceFor ) where import MarshallType import BasicTypes ( BinaryOp(..),QualName, qName, EnumKind(..) ) import Literal ( iLit ) import AbstractH ( HDecl ) import qualified AbstractH as Haskell ( CaseAlt, Expr ) import CgMonad import AbsHUtils import LibUtils hiding ( enumToInt ) import qualified LibUtils ( enumToInt ) import CoreIDL import CoreUtils ( mkHaskellTyConName ) import Attribute ( hasAttributeWithName, filterAttributes ) import Literal ( Literal(..) ) import Opts ( optSmartEnums, optNoVariantInstance, optVariantInstance, optEnumsAsFlags, optGenBitsInstance, optGenNumInstance ) import PpCore ( ppEnumValue, showCore ) \end{code} \begin{code} marshallEnum :: Id -> EnumKind -> Bool -> [EnumValue] -> CgM HDecl marshallEnum tdef_id kind noEnumInstance enums | noEnumInstance && genVariantInstance = return variantInst | noEnumInstance = return emptyDecl | genVariantInstance = return (enumInst `andDecl` variantInst) | otherwise = return enumInst where genVariantInstance = optVariantInstance && not optNoVariantInstance attrs = idAttributes tdef_id tdef_name = idName tdef_id name = mkVarName tdef_name v = var "v" x1 = var "x1" x2 = var "x2" t_ty = tyConst (qName name) -- The size of the integer value an enum tags gets mapped -- to is determined as follows: -- -- - OMG IDL => Int32 -- - MIDL with v1_enum attr => Int32 -- - MIDL => Int16 -- -- However, [v1_enum] (or not) only affects what's transmitted -- across the network, so we can uniformly represent a marshalled -- enum via an Int32. enumInst = addBitsInstance $ hInstance Nothing enumClass t_ty [ methodDef fromEnumName [varPat v] m_rhs , methodDef toEnumName [varPat v] u_rhs ] variantInst = hInstance Nothing variantClass t_ty [ methodDef inVariantName [] inv_rhs , methodDef resVariantName [] resv_rhs , methodDef defaultVariantName [] def_rhs , methodDef vtEltTypeName [] vt_elt_rhs ] addBitsInstance d | withListCon = if optGenBitsInstance || optGenNumInstance then (if optGenNumInstance then numInstance else numInstance `andDecl` bitsInstance) `andDecl` d else flagsInstance `andDecl` d | otherwise = d withListCon = case kind of EnumFlags{} -> True _ -> forceFlag forceFlag = optEnumsAsFlags || attrs `hasAttributeWithName` "hs_flag" numInstance = hInstance Nothing numClass t_ty [ methodDef (mkQVarName prelude "+") [varPat x1, varPat x2] or_rhs ] bitsInstance = hInstance Nothing bitsClass t_ty [ methodDef andName [varPat x1, varPat x2] and_rhs , methodDef orName [varPat x1, varPat x2] or_rhs , methodDef xorName [] xor_rhs , methodDef complementName[] comp_rhs , methodDef shiftName [] shift_rhs , methodDef rotateName [] rot_rhs , methodDef bitSizeName [] bit_rhs , methodDef isSignedName [] isS_rhs ] flagsInstance = hInstance Nothing flagsClass t_ty [ methodDef orFlagName [varPat x1, varPat x2] or_rhs ] inv_rhs = qvar autoLib "inEnum" resv_rhs = qvar autoLib "resEnum" def_rhs = qvar autoLib "defaultEnum" vt_elt_rhs = qvar autoLib "vtTypeEnum" or_rhs = funApp toEnumName [ binOp Add (funApp fromEnumName [x1]) (funApp fromEnumName [x2])] and_rhs = hLet (var "flatten") (lam [patVar "x"] (hCase (var "x") [ alt (conPat (mkConName (tdef_name ++ "List__")) [patVar "xs"]) (funApp concatMapName [var "flatten", var "xs"]) , defaultAlt (Just (mkVarName "x")) (hList [var "x"]) ])) (dataCon (mkConName (tdef_name ++ "List__")) [funApp intersectName [ funApp (mkVarName "flatten") [x1] , funApp (mkVarName "flatten") [x2] ]]) xor_rhs = funApp prelError [ stringLit ("Bits.xor{"++qName name++"}: unimplemented") ] comp_rhs = funApp prelError [ stringLit ("Bits.complement{"++qName name++"}: unimplemented") ] shift_rhs = funApp prelError [ stringLit ("Bits.shift{"++qName name++"}: unimplemented") ] rot_rhs = funApp prelError [ stringLit ("Bits.rotate{"++qName name++"}: unimplemented") ] bit_rhs = funApp prelError [ stringLit ("Bits.bitSize{"++qName name++"}: unimplemented") ] isS_rhs = funApp prelError [ stringLit ("Bits.isSigned{"++qName name++"}: unimplemented") ] -- Marshalling -- m_rhs | not optSmartEnums || kind == Unclassified = hCase v (add_m_list (map (enumToInt True) enums)) | otherwise = case kind of EnumProgression st 1 -> binOp Add (intLit st) (funApp LibUtils.enumToInt [v]) EnumProgression st step -> binOp Add (intLit st) (binOp Mul (intLit step) (funApp LibUtils.enumToInt [v])) EnumFlags 0 -> funApp enumToFlag [v] EnumFlags k -> funApp toIntFlag [intLit k, funApp LibUtils.enumToInt [v]] Unclassified -> error "MarshallEnum.marshallEnum.m_rhs: the impossible happened" add_m_list | withListCon = ((alt (conPat (mkConName (tdef_name ++ "List__")) [patVar "xs"]) rhs) :) | otherwise = id where rhs = funApp orListName [funApp mapListName [varName fromEnumName, var "xs"]] u_name = qName (prefix unmarshallPrefix name) u_rhs | not optSmartEnums || kind == Unclassified = normal_u_rhs | otherwise = case kind of -- solve y = step * x + st EnumProgression st 1 -> funApp tagToEnum [funApp unboxInt [binOp Sub v (intLit st)]] EnumProgression st step -> funApp tagToEnum [funApp unboxInt [binOp Div (binOp Sub v (intLit st)) (intLit step)]] EnumFlags k -> funApp tagToEnum [funApp unboxInt [funApp toIntFlag [intLit k, funApp flagToIntTag [v]]]] Unclassified -> error "MarshallEnum.marshallEnum.u_rhs: the impossible happened" normal_u_rhs = hCase v (add_u_list (map intToEnum enums) ++ [defaultAlt Nothing (giveUp (u_name ++ ": illegal enum value "))]) giveUp msg =funApp prelError [stringLit msg] add_u_list ls = case kind of EnumFlags start -> (ls ++ [alt (patVar "x") (rhsFlg start)]) _ | forceFlag -> (ls ++ [alt (patVar "x") (rhsGen)]) | otherwise -> ls where rhsFlg st= dataCon (mkConName (tdef_name ++ "List__")) [ funApp mapMaybeName [ lam [patVar "val"] (hIf (binOp Eq (binOp And (var "val") (funApp fromIntegralName [var "x"])) (var "val")) (just (funApp toEnumName [funApp fromIntegralName [var "val"]])) nothing) , funApp pow2Series [intLit (length enums), intLit st] ] ] rhsGen = dataCon (mkConName (tdef_name ++ "List__")) [ funApp mapMaybeName [ lam [patVar "val"] (hIf (binOp Eq (binOp And (var "val") (var "x")) (var "val")) (just (funApp toEnumName [var "val"])) nothing) , hList (map (enumToIntExpr fromIntegralName) enums) ] ] \end{code} Helpers: \begin{code} {- UNUSED mkGuard :: Bool -> Haskell.Expr -> EnumValue -> (Haskell.Expr, Haskell.Expr) mkGuard long_enum_tags v (EnumValue i val) = (binOp Eq v val', ret (dataConst (mkConName (mkHaskellTyConName nm)))) where nm = idName i val' = case val of Left il -> intLit il Right e -> funApp (mkQVarName hdirectLib (if long_enum_tags then "toInt32" else "toInt16")) [coreToHaskellExpr e] -} enumToInt :: Bool -> EnumValue -> Haskell.CaseAlt enumToInt long_enum_tags ev@(EnumValue i _) = alt (patKind (mkConName (mkHaskellTyConName nm))) val where patKind | has_args = \ x -> patRec x [] | otherwise = \ x -> conPat x [] has_args = (idAttributes i) `hasAttributeWithName` "hs_tyarg" nm = idName i val = enumToIntExpr coerce ev coerce = mkQVarName hdirectLib (if long_enum_tags then "toInt32" else "toInt16") enumToIntExpr :: QualName -> EnumValue -> Haskell.Expr enumToIntExpr coerce (EnumValue _ v) = case v of Left il -> intLit il Right e -> funApp coerce [coreToHaskellExpr e] -- Assume: always called with (Left i) intToEnum :: EnumValue -> Haskell.CaseAlt intToEnum (EnumValue i (Left v)) = alt (litPat (iLit v)) tag where nm = idName i tag | has_args = dataCon tag_nm def_vals | otherwise = dataConst tag_nm tag_nm = mkConName (mkHaskellTyConName nm) def_vals = (\ x -> case x of [] -> [] xs -> [lit (LitLit xs)]) $ unwords $ map (\ xs -> '(':xs ++ ")") $ map getStr $ filterAttributes (idAttributes i) ["hs_default"] getStr (Attribute _ [ParamLit (StringLit s)]) = s getStr _ = "" has_args = (idAttributes i) `hasAttributeWithName` "hs_tyarg" intToEnum eVal = error ("intToEnum: unhandled enum RHS -- " ++ showCore (ppEnumValue eVal)) \end{code} \begin{code} genDerivedEnumInstanceFor :: EnumKind -> [EnumValue] -> Bool genDerivedEnumInstanceFor (EnumProgression 0 1) vs = not (any (\ x -> idAttributes (enumName x) `hasAttributeWithName` "hs_tyarg") vs) genDerivedEnumInstanceFor _ _ = False \end{code}