% % (c) sof 1999- % % @(#) $Docid: Sep. 18th 2001 09:28 Sigbjorn Finne $ % @(#) $Contactid: sof@galconn.com $ % The 'Renamer' monad - carrying around the environments needed to turn a set of IDL decls into a set of uniquely named Haskell decls. \begin{code}
module RnMonad 
	(
	  RnM
	, runRnM           -- :: TypeEnv -> TagEnv -> SourceEnv 
		           -- -> RnM a -> (a, IsoEnv, IfaceNukeEnv)
	, lookupTypeId     -- :: Name -> RnM (Maybe (Maybe String, Type))
	, lookupIface      -- :: Name -> RnM (Maybe Decl)
	, lookupTag        -- :: Name -> RnM (Maybe (Maybe String, String))

	, getMethOffset    -- :: RnM (Maybe Int)
	, setMethOffset    -- :: Maybe Int -> RnM a -> RnM a
	, incMethOffset    -- :: RnM ()

	, withNewVarIdEnv  -- :: RnM a -> RnM a
	, inNewModule      -- :: RnM a -> RnM a

	, isSourceIface           -- :: Name  -> RnM Bool

        , lookupVarIdAndAddEnv    -- :: String -> (String -> RnM a) -> RnM a
        , lookupTypeIdAndAddEnv   -- :: String -> (String -> RnM a) -> RnM a
        , lookupTyConAndAddEnv    -- :: String -> (String -> RnM a) -> RnM a
        , lookupModIdAndAddEnv    -- :: String -> (String -> RnM a) -> RnM a
        , lookupClassIdAndAddEnv  -- :: String -> (String -> RnM a) -> RnM a

        , lookupTyConEnv    -- :: String -> (String -> RnM a) -> RnM a
        , lookupTypeIdEnv   -- :: String -> (String -> RnM a) -> RnM a
        , lookupVarIdEnv    -- :: String -> (String -> RnM a) -> RnM a
	, varIdInScope      -- :: String -> RnM Bool

        , addIface          -- :: String -> Decl -> RnM ()
        , addNukeIface      -- :: String -> Id -> RnM ()
        , addMethod         -- :: String -> (Maybe Int, Result, [Param]) -> RnM ()
        , addIsoMethod      -- :: String -> (Result,[Param]) -> RnM ()
        , lookupMethod      -- :: String -> RnM (Maybe [(Maybe Int, Result, [Param])])

        , setIfaceName      -- :: String -> RnM a -> RnM a
        , setModuleName     -- :: String -> RnM a -> RnM a
        , withDependers     -- :: [String] -> RnM a -> RnM a
        , getIfaceName      -- :: (String -> RnM a) -> RnM a
        , getModuleName     -- :: (String -> RnM a) -> RnM a
        , getDependers      -- :: ([String] -> RnM a) -> RnM a

	, IsoEnv
	, IfaceNukeEnv
	) where

import qualified Env
import DsMonad   ( TypeEnv, SourceEnv, TagEnv, IfaceEnv )
import CoreIDL
import CoreUtils
import BasicTypes
import Maybe ( isJust )
import Utils
\end{code} \begin{code}
newtype RnM a  = RnM (RnEnv -> RnState -> (a, RnState))

type RnEnv = ( String    -- current interface name
             , String    -- current Haskell module name
	     , [String]  -- list of interface/modules depending on
	    		 -- iface/module being currently processed.
			 -- (Need to record this when fighting Haskell modules
			 -- in one-module-per-interface mode.)
	     )

  -- name environments are used to map from the name that occurred
  -- in the IDL input to the unique name&module to use when generating Haskell.
  -- 
  -- while renaming, when a name is encountered, we first check to see whether
  -- it has got a mapping in the NameEnv. If so, reuse it.
type NameEnv       = Env.Env String String

  -- UniqueNameEnvs are used to record how many defns of a name N there has been
  -- /in the current scope/. 
type UniqueNameEnv = Env.Env String Int

  -- the renaming pass will optionally also spot isomorphic methods, that is,
  -- methods with the same name, same method table offset (if any -- IDispatch
  -- methods doesn't have any), result and parameter types.
  -- 
  -- The underlying idea is that in many cases interfaces mapped to the same Haskell
  -- module have identical methods. Spotting the ones that are shared allows us to later
  -- generate just the one stub, rather than N. 
type MethodEnv = Env.Env String [(Maybe Int,Result,[Param])]
type IsoEnv    = Env.Env String [(Result,[Param])]

  -- the 'nuke' interface environment is used for two purposes:
  --   * some interfaces we may simply want to ignore. Period.
  --   * sometimes you see IDL input of the form 
  --            "interface _A {....}; coclass A { interface A; }"
  --     If you've got the one-module-per-iface/class option turned on,
  --     you really don't want to generate two modules for this (assuming
  --     the _A isn't used by anyone else, of course.) We support this by
  --     slurping the interface into the class' Haskell module, and dropping
  --     the generation of the interface's module alltogether.
type IfaceNukeEnv = Env.Env String (Maybe Id)
			    -- True => don't bother generating code for this iface.
			    -- False (or not in env) => do generate.
\end{code} Carry around a set of environments that keep the various namespaces clean. The n-spaces are: + typedef'ed names (turns into type names in Haskell) + constructed tag names (turns into data cons in Haskell) + field labels IDL mimics C's rules for overloading field labels, they only have to be unique within a constructed type declaration, not across all definitions in scope. Since we're mapping field labels to Haskell record field labels, we have to ensure that a label is unique within the scope of one module (best we can do.) => Field labels, method names and constants are all in the same Haskell namespace, so we rename all of these wrt. to one environment. + A method's parameter labels is also renamed, although we can assume that they by this stage have been checked to have unique (IDL) names. Why? Because of the potential clash with Haskell keywords, e.g., void foo([in]int _data, [in]int __data); should turn into void foo([in]int data0, [in]int data1); To this, we use a per-method name mapping environment for these. \begin{code}

type NameSpaceEnv = 
  ( NameEnv        -- current set of forward/unbound IDL names.
  , NameEnv        -- mapping from IDL names to (unique) Haskell name
  , UniqueNameEnv  -- mapping from Haskell names to next unique tag.
  )

-- big,fat&ugly state:
data RnState
 = RnState 
     { type_env		:: TypeEnv
     , tg_env		:: TagEnv
     , src_env		:: SourceEnv
     , tycon_env        :: NameSpaceEnv
     , modid_env        :: NameSpaceEnv
     , varid_env        :: NameSpaceEnv
     , clsid_env        :: NameSpaceEnv  -- a class' scope is essentially global in Haskell
     , tyid_env         :: NameSpaceEnv
     , meth_env		:: MethodEnv
     , iso_meths	:: IsoEnv
     , meth_offset	:: Maybe Int
     , iface_env	:: IfaceEnv
     , iface_nuke_env   :: IfaceNukeEnv
     }

runRnM :: TypeEnv
       -> TagEnv
       -> SourceEnv
       -> IfaceEnv
       -> RnM a -> (a, IsoEnv, IfaceNukeEnv)
runRnM tenv tgenv senv ienv (RnM act) = 
  case (act ("","",[]) envs) of 
    (v, RnState{iso_meths=i,iface_nuke_env=e}) -> (v, i, e)
 where
  n_env = (newINameEnv, newINameEnv, newNameEnv)
  envs = RnState
            { type_env   = tenv
	    , tg_env     = tgenv
	    , src_env    = senv
	    , tycon_env  = n_env
	    , modid_env  = n_env
	    , varid_env  = n_env
	    , clsid_env  = n_env
	    , tyid_env   = n_env
	    , meth_env   = Env.newEnv
	    , iso_meths  = Env.newEnv
	    , meth_offset = Nothing
	    , iface_env  = ienv
	    , iface_nuke_env = Env.newEnv
	    }

\end{code} \begin{code}
lookupTypeId :: Name -> RnM (Maybe (Maybe String, Type))
lookupTypeId nm = RnM $ \ _ st -> 
  ( mapMb (\ (mod,t,_) -> (mod,t))
          (Env.lookupEnv (type_env st) nm)
  , st
  )

lookupIface :: Name -> RnM (Maybe Decl)
lookupIface nm = RnM ( \ _ st -> (Env.lookupEnv (iface_env st) nm, st))

lookupTag :: Name -> RnM (Maybe (Maybe String, String))
lookupTag nm = RnM ( \ _ st -> (Env.lookupEnv (tg_env st) nm, st))

getMethOffset :: RnM (Maybe Int)
getMethOffset = RnM ( \ _ st -> (meth_offset st, st))

setMethOffset :: Maybe Int -> RnM a -> RnM a
setMethOffset no (RnM a) = RnM ( \ env st -> a env (st{meth_offset=no}))

incMethOffset :: RnM ()
incMethOffset = RnM $ \ _ st -> 
	    let
	      st' =
	       case meth_offset st of
	         Nothing -> st
		 Just x  -> st{meth_offset=Just (x+1)}
	    in
	    ((), st')

withNewVarIdEnv :: RnM a -> RnM a
withNewVarIdEnv (RnM act) = RnM $ \ env st -> 
     let old = varid_env st in
     case act env st of
       (v, st') -> (v, st'{varid_env=old})

inNewModule :: RnM a -> RnM a
inNewModule (RnM act) = RnM $ \ env st ->
   let
    ds   = tycon_env st
    vs   = varid_env st
    ts   = tyid_env st
   in
   case act env st of
     (v, new_st) -> (v, new_st{tycon_env=ds,varid_env=vs,tyid_env=ts})

isSourceIface :: Name -> RnM Bool
isSourceIface nm =
  RnM ( \ _ st -> (isJust (Env.lookupEnv (src_env st) nm), st))

newINameEnv :: NameEnv
newINameEnv = Env.newEnv

newNameEnv :: UniqueNameEnv
newNameEnv = Env.addListToEnv Env.newEnv builtins
 where
  builtins = zip builtin_names (repeat 0)
  
  builtin_names = haskellKeywords

haskellKeywords :: [String]
haskellKeywords =
 [ "case", "class", "data", "default", "deriving", "do"
 , "else", "if", "import", "in", "infix", "infixl", "infixr"
 , "instance", "let", "module", "newtype", "of", "then", "type", "where"
 , "do"
 , "as", "qualified", "hiding" -- special ids the last three, so strictly not necessary to include them.
 ]

lookupAndAddEnv2 :: (RnState -> NameSpaceEnv)
		 -> (RnState -> NameSpaceEnv -> RnState)
		 -> String 
		 -> String
		 -> (String -> RnM a) 
		 -> RnM a
lookupAndAddEnv2 get upd nm nm_to_use cont = RnM $ \ rn_env st ->
        let (fwdMap, idlMap, env) = get st in
	case Env.lookupEnv fwdMap nm of
	  Just x -> -- a mention has already been made of this IDL name in
	  	    -- this scope, just reuse it.
		let
		 (RnM act) = cont x
		in
		act rn_env st
          Nothing -> -- no forward mention, try the IDL->HS map.
	     case Env.lookupEnv idlMap nm of
	       Just x -> -- IDL name in scope, use it's unique name. 
	       		let
	                 (RnM act) = cont x
			in
			act rn_env st
	       Nothing ->
	           -- Not seen, add it to the 'forward map', allocating
		   -- a unique name for it.
		case Env.lookupEnv env nm_to_use of
	          Nothing -> 
	            let env'      = Env.addToEnv env nm_to_use 0
		    	fwdMap'   = Env.addToEnv fwdMap nm nm_to_use
	                (RnM act) = cont nm_to_use
		    in
		    act rn_env (upd st (fwdMap', idlMap, env'))
	          Just i -> 
		    -- Find a new unique and use it.
	           let 
		       (env',nm') = addNewName env nm_to_use i
		       fwdMap'    = Env.addToEnv fwdMap nm nm'
	               (RnM act)  = cont nm'
		   in
		   act rn_env (upd st (fwdMap',idlMap,env'))

lookupAndAddEnv :: (RnState -> NameSpaceEnv)
		 -> (RnState -> NameSpaceEnv -> RnState)
		 -> String 
		 -> String
		 -> (String -> RnM a) 
		 -> RnM a
lookupAndAddEnv get upd nm nm_to_use cont = RnM $ \ rn_env st ->
        let (fwdMap,idlMap, env) = get st in
	case Env.lookupEnv fwdMap nm of
	  Just x -> -- a mention has been made of this IDL name in
	  	    -- this scope, incorporate it in the 'IDL map'
		    -- and remove it from the 'forward map'.
		case Env.lookupEnv env x of
		    -- This case could should never happen, as the
		    -- addition of a name to the 'forward map' will
		    -- have allocated a unique name using 'env'. (It's
		    -- no problem to handle it correctly though.)
		  Nothing -> let
		              env'      = Env.addToEnv env x 0
			      fwdMap'   = Env.delFromEnv fwdMap nm
			      idlMap'   = Env.addToEnv idlMap nm x
			      (RnM act) = cont x
			     in
			     act rn_env (upd st (fwdMap',idlMap', env'))
		  Just _ -> 
		    -- 
	           let 
		       fwdMap'   = Env.delFromEnv fwdMap nm
		       idlMap'   = Env.addToEnv idlMap nm x
	               (RnM act) = cont x
		   in
		   act rn_env (upd st (fwdMap',idlMap',env))
          Nothing -> -- there's been no forward reference, 
	  	     -- 'simply' find a unique name and upd. the IDL map.
	     case Env.lookupEnv env nm_to_use of
	        Nothing -> 
	          let env'       = Env.addToEnv env nm_to_use 0
		      idlMap'    = Env.addToEnv idlMap nm nm_to_use
	              (RnM act)  = cont nm_to_use
		  in
		  act rn_env (upd st (fwdMap, idlMap', env'))
	        Just i -> 
	           let (env',nm') = addNewName env nm_to_use i
		       idlMap'    = Env.addToEnv idlMap nm nm'
	               (RnM act)  = cont nm'
		   in
		   act rn_env (upd st (fwdMap,idlMap',env'))

addNewName :: Env.Env String Int
           -> String
	   -> Int
	   -> (Env.Env String Int, String)
addNewName env nm v =
  let nm' = nm ++ show v in
  case Env.lookupEnv env nm' of
   Nothing -> 
	      let env'     = Env.addToEnv env  nm  (v+1)
	          env''    = Env.addToEnv env' nm' 0
              in
	      (env'', nm')
   Just _  -> addNewName env nm (v+1)


lookupVarIdAndAddEnv :: String -> (String -> RnM a) -> RnM a
lookupVarIdAndAddEnv nm cont = 
    lookupAndAddEnv (varid_env) (\ st env' -> st{varid_env=env'}) nm (mkHaskellVarName nm) cont

lookupTypeIdAndAddEnv :: String -> (String -> RnM a) -> RnM a
lookupTypeIdAndAddEnv nm cont = 
    lookupAndAddEnv (tyid_env) (\ st env' -> st{tyid_env=env'}) nm (mkHaskellTyConName nm) cont

lookupTyConAndAddEnv :: String -> (String -> RnM a) -> RnM a
lookupTyConAndAddEnv nm cont = 
    lookupAndAddEnv (tycon_env) (\ st env' -> st{tycon_env=env'}) nm (mkHaskellTyConName nm) cont

lookupModIdAndAddEnv :: String -> (String -> RnM a) -> RnM a
lookupModIdAndAddEnv nm cont = 
    lookupAndAddEnv (modid_env) (\ st env' -> st{modid_env=env'}) nm (mkHaskellTyConName nm) cont

lookupClassIdAndAddEnv :: String -> (String -> RnM a) -> RnM a
lookupClassIdAndAddEnv nm cont = 
    lookupAndAddEnv (clsid_env) (\ st env' -> st{clsid_env=env'}) nm (mkHaskellTyConName nm) cont

lookupTyConEnv :: String -> (String -> RnM a) -> RnM a
lookupTyConEnv nm cont = 
    lookupAndAddEnv2 (tycon_env) (\ st env' -> st{tycon_env=env'}) nm (mkHaskellTyConName nm) cont

lookupTypeIdEnv :: String -> (String -> RnM a) -> RnM a
lookupTypeIdEnv nm cont = 
    lookupAndAddEnv2 (tyid_env) (\ st env' -> st{tyid_env=env'}) nm (mkHaskellTyConName nm) cont

lookupVarIdEnv :: String -> (String -> RnM a) -> RnM a
lookupVarIdEnv nm cont = 
    lookupAndAddEnv2 (varid_env) (\ st env' -> st{varid_env=env'}) nm (mkHaskellVarName nm) cont

varIdInScope :: String -> RnM Bool
varIdInScope nm = RnM $ \ _ st ->
   let (_,idlMap, env) = varid_env st in
   case Env.lookupEnv idlMap nm of
     Nothing -> (isJust (Env.lookupEnv env nm), st)
     Just _  -> (True, st)

addIface :: String -> Decl -> RnM ()
addIface nm d = RnM (\ _ st -> ((), st{iface_env=Env.addToEnv (iface_env st) nm d}))

addNukeIface :: String -> Id -> RnM ()
addNukeIface nm i =
    RnM
      (\ _ st ->
	case Env.lookupEnv (iface_nuke_env st) nm of
	   Nothing -> ((), st{iface_nuke_env=Env.addToEnv (iface_nuke_env st) nm (Just i)})
	   Just _  -> ((), st{iface_nuke_env=Env.addToEnv (iface_nuke_env st) nm Nothing}))

addMethod :: String -> (Maybe Int, Result, [Param]) -> RnM ()
addMethod nm it = RnM (\ _ st -> ((), st{meth_env=Env.addToEnv_C (++) (meth_env st) nm [it]}))

addIsoMethod :: String -> (Result,[Param]) -> RnM ()
addIsoMethod nm it =
   RnM (\ _ st -> 
	  ((), st{iso_meths=Env.addToEnv_C (++) (iso_meths st) nm [it]}))

lookupMethod :: String -> RnM (Maybe [(Maybe Int, Result, [Param])])
lookupMethod nm = RnM (\ _ st -> (Env.lookupEnv (meth_env st) nm, st))

setIfaceName :: String -> RnM a -> RnM a
setIfaceName nm (RnM act) = RnM (\ (_,hmod,ls) st -> act (nm,hmod,ls) st)

setModuleName :: String -> RnM a -> RnM a
setModuleName nm (RnM act) = RnM (\ (inm,_,ls) st -> act (inm,nm,ls) st)

withDependers :: [String] -> RnM a -> RnM a
withDependers nms (RnM act) = RnM (\ (inm,nm,_) st -> act (inm,nm,nms) st)

getIfaceName :: (String -> RnM a) -> RnM a
getIfaceName f = RnM (\ env@(nm,_,_) st -> let (RnM act) = f nm in act env st)

getModuleName :: (String -> RnM a) -> RnM a
getModuleName f = RnM (\ env@(_,nm,_) st -> let (RnM act) = f nm in act env st)

getDependers :: ([String] -> RnM a) -> RnM a
getDependers f = RnM (\ env@(_,_,nms) st -> let (RnM act) = f nms in act env st)

\end{code} And, finally, let's have a look at tomorrow's weather.. \begin{code}
instance Monad RnM where
  (>>=) (RnM m) n =
     RnM (\ env st ->
            case m env st of
	      (v, st') -> let (RnM act) = n v in
	                   act env st')
  return v = RnM (\ _ st -> (v,st))

\end{code}