% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Feb. 9th 2003 15:08 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % The desugar monad helps carry around environments that maps user defined type names to their type and attributes. \begin{code} module DsMonad ( DsM , runDsM -- :: String -- -> Env String TypeInfo -- -> Env String (Bool, [IDL.Attribute]) -- -> String DsM a -- -> IO a , mapDsM -- :: (a -> b) -> DsM a -> DsM b , lookupType -- :: String -> DsM (Maybe (String,Type, [Attribute])) , lookupTypeInfo -- :: String -> DsM (Maybe TypeInfo) , lookupAsf -- :: String -> DsM (Maybe (Bool,[IDL.Attribute])) , lookupConst -- :: String -> DsM (Maybe Int32) , lookupIface -- :: String -> DsM (Maybe Decl) , lookupTag -- :: String -> DsM (Maybe (String, String)) , getAttributes -- :: DsM [Attribute] , propagateAttributes -- :: [Attribute] -> DsM a -> DsM a , withAttributes -- :: [Attribute] -> DsM a -> DsM a , getSrcFilename -- :: DsM String , pushPack -- :: Maybe (Maybe (String, Maybe Int)) -> DsM () , popPack -- :: Maybe (String, Maybe Int) -> DsM () , getCurrentPack -- :: DsM (Maybe Int) , openUpScope -- :: DsM a -> DsM a , addToTypeEnv -- :: String -> (Type, [Attribute]) -> DsM () , addToIfaceEnv -- :: String -> Decl -> DsM () , addToConstEnv -- :: String -> Int32 -> DsM () , getConstEnv -- :: DsM ConstEnv , addToTagEnv -- :: String -> String -> DsM () , addSourceIface -- :: String -> DsM () , getFilename -- :: DsM (Maybe String) , setFilename -- :: Maybe String -> DsM () , getInterface -- :: DsM (Maybe String) , withInterface -- :: String -> DsM a -> DsM a , addToPath -- :: String -> DsM a -> DsM a , getPath -- :: DsM String , inLibrary -- :: DsM a -> DsM a , isInLibrary -- :: DsM Bool , inImportedContext -- :: DsM a -> DsM a , isInImportedContext -- :: DsM Bool , addWarning -- :: String -> DsM () , ioToDsM -- :: IO a -> DsM a , TypeEnv , SourceEnv , ConstEnv , TagEnv , IfaceEnv , TypeInfo ) where import CoreIDL import qualified IDLSyn as IDL ( Attribute ) import CoreUtils ( childAttributes ) import Env import Data.IORef ( IORef, newIORef, readIORef, writeIORef ) import IO ( hPutStrLn, stderr ) import Int ( Int32 ) import Monad ( when ) import Opts ( optVerbose, optDebug ) import Maybe ( catMaybes ) import TypeInfo \end{code} Lots of stuff being plumbed here...maybe I qualify for a plumber's diploma on the grounds of this monad? \begin{code} type TypeEnv = Env String (Maybe String, Type, [Attribute]) type IfaceEnv = Env String Decl{-Decl.{Disp}Interface constructor only-} type ConstEnv = Env String (Either Int32 Expr) type SourceEnv = Env String () type TagEnv = Env String (Maybe String, String) {- struct/union/enum tag to name of typedef -} data EnvChain = EnvChain TypeEnv -- default/builtins [DsEnvs] data DsEnvs = DsEnvs { type_env :: TypeEnv , if_env :: IfaceEnv , co_env :: ConstEnv , tg_env :: TagEnv , src_env :: SourceEnv } freshDsEnv :: DsEnvs freshDsEnv = DsEnvs newEnv newEnv newEnv newEnv newEnv -- As an experiment, this monad uses an environment containing -- IORefs rather than thread the information they carry. It is -- questionable whether this is more efficient, and the result -- is less than pretty => switch to threading the info sometime -- in the future. data DsMEnv = DsMEnv { env_ref :: IORef EnvChain , at_ref :: (IORef [Attribute]) -- the current interface's attributes , fn_ref :: (IORef (Maybe String)) -- current file/module name , current_if :: (IORef (Maybe String)) -- current (disp)interface , in_lib :: (IORef Bool) , in_import :: (IORef Bool) , tinfo_env :: Env String TypeInfo , asf_env :: Env String (Bool,[IDL.Attribute]) , pack_stk :: IORef [(Maybe String, Int)] , nm_path :: String , src_name :: String } newtype DsM a = DsM (DsMEnv -> IO a) \end{code} Types out of the way, here's the big ugly action for performing @DsM@ actions: \begin{code} runDsM :: String -> Env String TypeInfo -> Env String (Bool,[IDL.Attribute]) -> [(String, Maybe String, Type)] -> DsM a -> IO (a, TypeEnv, TagEnv, SourceEnv, IfaceEnv) runDsM srcFileName tInfo aenv defs (DsM m) = do at <- newIORef [] pck <- newIORef [] md <- newIORef Nothing cur_if <- newIORef Nothing in_l <- newIORef False in_im <- newIORef False let tenv = addListToEnv newEnv (map (\(n,mo,t) -> (n, (mo,t,[]))) defs) cha = EnvChain tenv [freshDsEnv] chain <- newIORef cha let denv = DsMEnv chain at md cur_if in_l in_im tInfo aenv pck "" srcFileName a <- m denv cha1 <- readIORef chain let (EnvChain t ds) = cha1 ty = unionEnvs (t:map type_env ds) ta = unionEnvs (map tg_env ds) sr = unionEnvs (map src_env ds) ir = unionEnvs (map if_env ds) return (a, ty, ta, sr, ir) thenDsM :: DsM a -> (a -> DsM b) -> DsM b thenDsM (DsM m) n = DsM (\ te -> do v <- m te case n v of DsM k -> k te) returnDsM :: a -> DsM a returnDsM v = DsM (\ _ -> return v) openUpScope :: DsM a -> DsM a openUpScope (DsM a) = liftDsM $ \ dse -> do let ref = env_ref dse cha <- readIORef ref let (EnvChain t (x:ls)) = cha writeIORef ref (EnvChain t [freshDsEnv]) v <- a dse cha1 <- readIORef ref let (EnvChain t1 nls) = cha1 ls' = x:nls ++ ls writeIORef ref (EnvChain t1 ls') return v liftDsM :: (DsMEnv -> IO a) -> DsM a liftDsM a = DsM a lookupType :: String -> DsM (Maybe (Maybe String, Type, [Attribute])) lookupType str = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do chain <- readIORef ref let (EnvChain t ds) = chain case lookupEnv t str of x@(Just _) -> return x _ -> case catMaybes (map (\ d -> lookupEnv (type_env d) str) ds) of [] -> return Nothing (x:_) -> return (Just x) lookupTypeInfo :: String -> DsM (Maybe TypeInfo) lookupTypeInfo str = liftDsM (\ (DsMEnv{tinfo_env=ti}) -> return (lookupEnv ti str)) lookupAsf :: String -> DsM (Maybe (Bool, [IDL.Attribute])) lookupAsf str = liftDsM (\ (DsMEnv{asf_env=as}) -> return (lookupEnv as str)) lookupConst :: String -> DsM (Maybe (Either Int32 Expr)) lookupConst str = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do chain <- readIORef ref let (EnvChain _ ls) = chain case catMaybes (map (\ d -> lookupEnv (co_env d) str) ls) of [] -> return Nothing (x:_) -> return (Just x) lookupIface :: String -> DsM (Maybe Decl) lookupIface str = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do chain <- readIORef ref let (EnvChain _ ls) = chain case catMaybes (map (\ d -> lookupEnv (if_env d) str) ls) of [] -> return Nothing (x:_) -> return (Just x) lookupTag :: String -> DsM (Maybe (Maybe String, String)) lookupTag str = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do chain <- readIORef ref let (EnvChain _ ls) = chain case catMaybes (map (\ d -> lookupEnv (tg_env d) str) ls) of [] -> return Nothing (x:_) -> return (Just x) getAttributes :: DsM [Attribute] getAttributes = liftDsM (\ (DsMEnv{at_ref=at_v}) -> readIORef at_v) {- UNUSED getInheritedAttributes :: DsM [Attribute] getInheritedAttributes = liftDsM (\ (DsMEnv{at_ref=at_v}) -> do ls <- readIORef at_v return (childAttributes ls)) -} getSrcFilename :: DsM String getSrcFilename = liftDsM (\ (DsMEnv{src_name=s}) -> return s) withAttributes :: [Attribute] -> DsM a -> DsM a withAttributes ats (DsM act) = liftDsM $ \ env@(DsMEnv{at_ref=at_v}) -> do old_at <- readIORef at_v writeIORef at_v ats v <- act env writeIORef at_v old_at return v -- like withAttributes, but filter out the non-inheritable ones. propagateAttributes :: [Attribute] -> DsM a -> DsM a propagateAttributes ats (DsM act) = liftDsM $ \ env@(DsMEnv{at_ref=at_v}) -> do old_at <- readIORef at_v writeIORef at_v (childAttributes ats) v <- act env writeIORef at_v old_at return v {- An IDL specification may import a number of other specs. The meaning of an import is simply to bring the definitions of types and interfaces into scope, no code is generated for the imported entities (you'd use #include to (optionally) do literal code inclusion.) When desugaring, we work our way through the imports, stashing information about types, constants and interfaces into appropriate environments. -} addToTypeEnv :: String -> Maybe String -> (Type, [Attribute]) -> DsM () addToTypeEnv str md (ty,at) = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do -- hPutStrLn stderr ("Adding: " ++ str) chain <- readIORef ref let (EnvChain t (d:ds)) = chain ty_env = type_env d d' =d{type_env=addToEnv ty_env str (md,ty,at)} writeIORef ref (EnvChain t (d':ds)) addToIfaceEnv :: String -> Decl -> DsM () addToIfaceEnv str val = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do chain <- readIORef ref let (EnvChain t (d:ds)) = chain ienv = if_env d d' =d{if_env=addToEnv ienv str val} writeIORef ref (EnvChain t (d':ds)) addSourceIface :: String -> DsM () addSourceIface str = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do chain <- readIORef ref let (EnvChain t (d:ds)) = chain senv = src_env d d' =d{src_env=addToEnv senv str ()} writeIORef ref (EnvChain t (d':ds)) addToConstEnv :: String -> Either Int32 Expr -> DsM () addToConstEnv str val = liftDsM $ \ (DsMEnv{env_ref=ref}) -> do chain <- readIORef ref let (EnvChain t (d:ds)) = chain cenv = co_env d d' =d{co_env=addToEnv cenv str val} writeIORef ref (EnvChain t (d':ds)) addToTagEnv :: String -> String -> DsM () addToTagEnv str val = liftDsM $ \ (DsMEnv{env_ref=ref,fn_ref=fe}) -> do chain <- readIORef ref md <- readIORef fe let (EnvChain t (d:ds)) = chain tenv = tg_env d d' =d{tg_env=addToEnv tenv str (md,val)} writeIORef ref (EnvChain t (d':ds)) getConstEnv :: DsM ConstEnv getConstEnv = liftDsM $ \ DsMEnv{env_ref=ref} -> do chain <- readIORef ref let (EnvChain _ ds) = chain return (unionEnvs (map co_env ds)) getFilename :: DsM (Maybe String) getFilename = liftDsM (\ (DsMEnv{fn_ref=md}) -> readIORef md) setFilename :: Maybe String -> DsM () setFilename nm = liftDsM ( \ (DsMEnv{fn_ref=md}) -> writeIORef md nm) getInterface :: DsM (Maybe String) getInterface = liftDsM ( \ (DsMEnv{current_if=cur_i_ref}) -> readIORef cur_i_ref) getPath :: DsM String getPath = liftDsM ( \ (DsMEnv{nm_path=nm}) -> return nm) setInterface :: Maybe String -> DsM () setInterface nm = liftDsM ( \ (DsMEnv{current_if=cur_i_ref}) -> writeIORef cur_i_ref nm) addToPath :: String -> DsM a -> DsM a addToPath nm (DsM x) = DsM (\ (env@DsMEnv{nm_path=onm}) -> let new_nm = case onm of "" -> nm _ -> onm ++ '.':nm in x (env{nm_path=new_nm})) withInterface :: String -> DsM a -> DsM a withInterface nm act = do old_nm <- getInterface setInterface (Just nm) v <- act setInterface old_nm return v inLibrary :: DsM a -> DsM a inLibrary (DsM act) = liftDsM $ \ env@(DsMEnv{in_lib=in_lib_ref}) -> do writeIORef in_lib_ref True v <- act env writeIORef in_lib_ref False return v isInLibrary :: DsM Bool isInLibrary = liftDsM (\ (DsMEnv{in_lib=in_lib_ref}) -> readIORef in_lib_ref) inImportedContext :: DsM a -> DsM a inImportedContext (DsM act) = liftDsM $ \ env@(DsMEnv{in_import=in_import_ref}) -> do x <- readIORef in_import_ref writeIORef in_import_ref True v <- act env writeIORef in_import_ref x return v isInImportedContext :: DsM Bool isInImportedContext = liftDsM (\ (DsMEnv{in_import=in_import_ref}) -> readIORef in_import_ref) pushPack :: Maybe (Maybe (String, Maybe Int)) -> DsM () pushPack mb_val = liftDsM $ \ (DsMEnv{pack_stk=ps_ref}) -> do ls <- readIORef ps_ref case mb_val of Nothing -> writeIORef ps_ref ((Nothing,8):ls) -- default packing is 8. (ToDo: param out.) Just Nothing -> case ls of ((_,x):_) -> writeIORef ps_ref ((Nothing,x):ls) [] -> writeIORef ps_ref [(Nothing,8)] Just (Just ("", Just x)) -> writeIORef ps_ref ((Nothing,x):ls) Just (Just (nm, Nothing)) -> case ls of ((_,x):_) -> writeIORef ps_ref ((Just nm,x):ls) [] -> writeIORef ps_ref [(Just nm,8)] Just (Just (nm, Just x)) -> writeIORef ps_ref ((Just nm,x):ls) getCurrentPack :: DsM (Maybe Int) getCurrentPack = liftDsM $ \ (DsMEnv{pack_stk=ps_ref}) -> do ls <- readIORef ps_ref case ls of [] -> return Nothing ((_,x):_) -> return (Just x) popPack :: Maybe (String, Maybe Int) -> DsM () popPack mb_i = liftDsM $ \ (DsMEnv{pack_stk=ps_ref}) -> do ls <- readIORef ps_ref let ls' = case mb_i of Nothing -> case ls of { [] -> [] ; (_:xs) -> xs } Just ("", Just v) -> case ls of { [] -> [] ; (_:xs) -> ((Nothing,v):xs) } Just (x,_) -> scramble x ls ls writeIORef ps_ref ls' where scramble _ ls [] = ls scramble x ls ((Nothing,_):xs) = scramble x ls xs scramble x ls ((Just y,_):xs) | x == y = xs | otherwise = scramble x ls xs \end{code} \begin{code} addWarning :: String -> DsM () addWarning msg = liftDsM ( \ _ -> when (optVerbose || optDebug ) (hPutStrLn stderr msg)) ioToDsM :: IO a -> DsM a ioToDsM act = liftDsM ( \ _ -> act) instance Monad DsM where (>>=) = thenDsM return = returnDsM mapDsM :: (a -> b) -> DsM a -> DsM b mapDsM f m = m >>= \ v -> return (f v) \end{code}