% % (c) The Foo Project, University of Glasgow, 1998 % % @(#) $Docid: Mar. 31th 2003 08:52 Sigbjorn Finne $ % @(#) $Contactid: sof@galois.com $ % Toplevel module for converting IDL into abstract Haskell code, ready for dumping out to a file. \begin{code}
module CodeGen ( codeGen ) where

import MarshallUtils
import MarshallStruct
import MarshallEnum
import MarshallType
import MarshallMethod
import MarshallUnion
import MarshallAbstract  ( marshallAbstract )
import MarshallServ
import MarshallCore
import MarshallFun
import MarshallJNI
import MarshallJServ
import Skeleton
import qualified CustomAttributes

import BasicTypes
import Literal
import AbstractH hiding (Type(..), Expr(..),CaseAlt(..))
import qualified AbstractH as Haskell ( HDecl(..),
					TyDecl(..),
					ConDecl(..),
					BangType(..)
				      )
import AbsHUtils
import MkImport      ( mkImportLists )
import LibUtils
import Opts ( optGenHeader, optNoExportList,
	      optOneModulePerInterface, optServer,
	      optUnparamedInterfacePointers,
	      optSubtypedInterfacePointers, optExportAbstractly,
	      optDualVtbl, optDon'tGenBinaryComInterfaces,
	      optSkel, optIgnoreDispInterfaces, optNoLibIds,
	      optIgnoreSourceIfaces, optNoEnumMagic, optHaskellToC,
	      optJNI, optCorba, optIgnoreMethsUpto, optUseStdDispatch,
	      optOutputHTo, optEnumsAsFlags, optGenNumInstance, optGenBitsInstance
	    )
import PpAbstractH ( ppType, showAbstractH )
import PpCore ( showCore, ppDecl )
import CgMonad

import CoreIDL
import CoreUtils
import Attribute
import List  ( partition, intersperse, isPrefixOf )
import Utils ( dropSuffix, trace, basename, split,
	       splitdir, prefixDir, notNull
	     )
import Maybe ( mapMaybe, isJust )
import Monad ( when )

import Env
\end{code} External interface, convert a set of toplevel IDL declarations into their Haskell form. A translation unit can at the toplevel consist of: - one or more modules - one or more libraries - one or more (disp)interfaces/coclasses. IDL grouping declarations map onto Haskell modules as follows: - modules and libraries get their own Haskell module. - optionally, each (disp)interface/coclass can be put into a file of their own. (doing this eases the problem of namespace clashes, relying instead on the Haskell module system to operate satisfactorily..) \begin{code}
codeGen :: (Either String String, Maybe String)
               {- User-supplied output name -}
	-> Env String [(Result, [Param])]
	       {- environment carrying type isomorphic methods info -}
	-> Env String (Maybe Id)
	       {- environment containing ifaces that should be ignored -}
        -> [Decl]
	-> ( [(String, Decl)]		        {- possible header file output  -}
	   , [(String, Bool, [HTopDecl])]       {- Haskell output		-}
	   )
codeGen (o_fname, mname) iso_env iface_env decls =
  ( generateHeader top_module_name decls
  , (\ x -> if optSkel then x ++ cgSkeleton decls else x) $
    liftOut (map (cGen iso_env iface_env) modules)
  )
 where
  liftOut [] = []
  liftOut (m@(nm, has_struct, ds) : ms) =
     case break isHMod ds of
       (ls,[x,HMod md@(HModule nm1 _ _ _ _)]) -> 
		    (nm, has_struct, ls ++ [x]) : 
		    (nm1 ++ ".hs", False, [HMod md]) : liftOut ms
       _ -> m : liftOut ms

  isHMod (HMod _) = True
  isHMod _	  = False

   -- The set of modules to generate. If there's any leftovers at the
   -- top (i.e., stuff that appear outside a module/library/interface etc. grouping),
   -- we put these bits into the top_module. In the one-module-per-interface 
   -- case, this means that for typedefs that appear inside interfaces will
   -- be lifted out and be generated with the outermost module (arguably they
   -- shouldn't be lifted out, but that's how things are organised at the moment.)
  modules = 
   case mods_and_libs of
     [_] -> [one_module] -- make sure we emit everything.
     _   ->
      case rest of
        [] -> mods_and_libs
        _  -> top_module : mods_and_libs

  toStdOut = 
    case o_fname of
      Right "-" -> True
      _		-> False

  -- use output filename to generate a plausible looking
  -- Haskell module name.
  (top_module_name, output_fname) = 
     case mname of
       Just x -> (x, snd ofnames)
       _      -> ofnames
    where
     ofnames =
         case o_fname of
          Right "-" -> 
	  	case mods_and_libs of 
		   ((_,m,_):_) -> (m, "-")
		   _	       -> ("Anon", "-")
	  Left  x   -> let 
	  		(_,base) = splitdir x
			y = mkHaskellTyConName (dropSuffix base)
		       in
		       (y, x)
	  Right x   ->
	    case mods_and_libs of
	      [(_,m,_)] -> let
	                    (dir,_) = splitdir x 
	      		   in (m, prefixDir dir (m ++ ".hs"))

	      _	        -> (mkModName x, x)

     mkModName x  = mkHaskellTyConName (basename (dropSuffix x))

  top_module = ( output_fname
	       , top_module_name
	       , mkTopModule (mkId top_module_name
	       			   top_module_name
				   Nothing
				   [{-no attrs-}])
				   rest
	       )

  one_module = ( output_fname
	       , top_module_name
	       , mkTopModule (mkId top_module_name
	       			   top_module_name
				   Nothing
				   [{-no attrs-}])
				   flattened_decls
	       )

   -- Hoist out some of these. Currently they all have to be at the top,
   -- we don't scan through the whole file looking for them.
  mkTopModule i (d@(HsLiteral _) : xs) = d : mkTopModule i xs
  mkTopModule i (d@(CInclude _) : xs)  = d : mkTopModule i xs
  mkTopModule i (d@(CLiteral _) : xs)  = d : mkTopModule i xs
   -- delayed addition of the attributes for the module itself.
  mkTopModule i ds@(d@Module{} : _)    = [Module i{idAttributes=idAttributes (declId d)} ds]
  mkTopModule i ds@(d@Library{} : _)   = [Module i{idAttributes=idAttributes (declId d)} ds]
  mkTopModule i ds		       = [Module i ds]

  mods_and_libs = map ( \ d -> let (x,y) = mkModuleLibName d in (x, y, [d])) mods_and_libs'
  (mods_and_libs', rest) = partition inSeparateHaskellModule flattened_decls

  flattened_decls = flattenDecls decls

  mkModuleLibName d 
    | toStdOut  = ("-", hnm)
    | otherwise = (hnm ++ ".hs", hnm)
      where
	   hnm = mkHaskellTyConName nm
	   nm_raw = idName (declId d)
	   nm
	    | optServer = nm_raw ++ "Proxy"
	    | otherwise = nm_raw
\end{code} \begin{code}
cGen :: Env String [(Result, [Param])]
     -> Env String (Maybe Id)
     -> (String, String, [Decl]) 
     -> (String, Bool, [HTopDecl])
cGen iso_env iface_env (oname, mod_name, ds) = (oname, flg, ds')
 where
  (ds',flg) = foldr mkHTop ([],False) ds

  mkHTop d (acc, has_structs) = 
   case d of
     Library i ms ->
       case (runCgM iso_env iface_env (withDeclName (idName i) (cgLibrary i ms))) of
        (decl, expo, imps, flg1, has_prim) -> 
	  let qual_imps = mkImportLists mod_name (getHsImports i) [decl]
	      real_imps = qual_imps ++ imps

	  in
	  (modDecl i has_prim [decl] real_imps expo : acc, has_structs || flg1)
     DispInterface i _ _ _ ->
       case (runCgM iso_env iface_env (withIfaceDeclName (idName i) (cgDecl d))) of
         (decl, expo, imps, flg1, has_prim) -> 
	   let qual_imps = mkImportLists mod_name (getHsImports i) [decl] 
	       real_imps = qual_imps ++ imps
	   in
	   (modDecl i has_prim [decl] real_imps expo : acc, has_structs || flg1)
     Interface i is_ref inherit _
       | not is_ref ->
         case (runCgM iso_env iface_env (withIfaceDeclName (idName i) (cgDecl d))) of
           (decl, expo, imps, flg1, has_prim) -> 
	     let qual_imps = mkImportLists mod_name (getHsImports i) [decl] 
	         real_imps = qual_imps ++ imps

		 attrs	    = idAttributes i
		 iface_deps = filterAttributes attrs ["depender"]
		 new_attrs  = filterOutAttributes attrs ["depender"]
		 i'	    = i{idAttributes=new_attrs}
		 (acc', has_structs')
		    | not optOneModulePerInterface || null iface_deps = (acc, has_structs)
		    | otherwise	 = 
			case mkHTop (Interface i' False inherit []) (acc, has_structs) of
			  (HMod (HModule nm a b c d1) : ls, e) ->
			     (HMod (HModule (nm ++ "Ty") a b c d1) : ls , e)
			  x -> x
	     in
	     (modDecl i has_prim [decl] real_imps expo : acc', has_structs' || flg1)
       | otherwise -> (acc, has_structs)
     CoClass{} -> 
       case (runCgM iso_env iface_env (cgDecl d)) of
         (decl, expo, imps, flg1, has_prim) -> 
	   let qual_imps = mkImportLists mod_name (getHsImports (declId d)) [decl]
	       real_imps = qual_imps ++ imps

	   in
	   if isEmptyDecl decl then
	       (acc, has_structs)
	   else
   	       (hModule mod_name
		        has_prim
  		        (map (\ (x,_,y) -> hExport x y) expo)
  		        (map (\ (x,y,z) -> hImport x y z) real_imps)
 		        decl : acc, has_structs || flg1)
     Module i ms -> 
       case (runCgM iso_env iface_env (withDeclName (idName i) (cgModule i ms))) of
         (decl, expo,imps,flg1, has_prim) -> 
	    let qual_imps = mkImportLists mod_name (getHsImports i) [decl]
	        real_imps = qual_imps ++ imps
	    in
	    ( modDecl (i{idName=mod_name}) has_prim [decl] real_imps expo : acc
	    , has_structs || flg1
	    )
     HsLiteral s -> (hMeta s    : acc, has_structs)
     CLiteral s  -> (cMeta s    : acc, has_structs)
     CInclude s  -> (hInclude s : acc, has_structs)
     _		 -> error ("Odd decl: " ++ showCore (ppDecl d))

  modDecl _ mflg decls imps expo
    | optNoExportList =
        hModule mod_name mflg
                (map (\ (x,_,y) -> hExport x y) $ filter (\ (_,x,_) -> x) expo)
                (map (\ (x,y,z) -> hImport x y z) imps)
                (andDecls decls)
    | otherwise =
      hModule mod_name mflg
	      (map (\ (x,_,y) -> hExport x y) expo)
	      (map (\ (x,y,z) -> hImport x y z) imps)
	      (andDecls decls)
\end{code} From the .idl input, we optionally generate C header file information. This is useful when the master copy of the .h file is the .idl specification (or, you don't have the header file already available.) \begin{code}
generateHeader :: String -> [Decl] -> [(String, Decl)]
generateHeader o_fname decls
 | not optGenHeader = []
 | otherwise	    = 
    case optOutputHTo of
      (x:_) -> 
         let
	  nm = dropSuffix x
	 in
	 [(oname, Module (mkId nm nm Nothing []) decls)]
      _ -> 
       case (concatMap mkHeaderDecls decls) of
        []      -> [(oname, Module (mkId oname oname Nothing []) decls)]
        [(_,s)] -> [(oname, s)]
        ls	-> ls
 where
  oname = 
    case optOutputHTo of
      (x:_) -> x
      _ ->
       case o_fname of
          "-" -> "-"
          _   -> dropSuffix o_fname ++ ".h"

  mkHeaderNm i = mkHaskellTyConName (idName i) ++ ".h"

  mkHeaderDecls d =
    case d of
      Module	i _	    -> [(mkHeaderNm i, d)]
      Interface i _ _ _     -> [(mkHeaderNm i, d)]
      DispInterface i _ _ _ -> [(mkHeaderNm i, d)]
      Library i _	    -> [(mkHeaderNm i, d)]
      _			    -> []

\end{code} Generating code for the various Core IDL declarations: \begin{code}

cgDecl :: Decl -> CgM HDecl
cgDecl d =
 case d of
   Typedef n t _	            -> cgTypedef n t
   Constant i t o_t e	            -> cgConstant i t o_t e
   Interface i _ inherit decls -> 
     withIfaceInherit (map fst inherit) $ 
     hoistInClass (idName i)		$ \ mb_cls -> do
     cls_d <-
         {-
	   Check to see if we should include a CLSID declaration
	   as well. Do this in the case where we've got
	     
 	     interface _A { ... }; coclass A { interface _A; };
           
           and A has the only use of _A. Useful in one-module-per-interface mode,
	   as it avoids creating a (v simple) module for the coclass.
	 -}
       case mb_cls of
         Nothing -> return emptyDecl
	 Just ci
	    | notNull deps -> return emptyDecl
	    | otherwise	   -> do
	   let ci'
	        | idName i `isPrefixOf` idName ci = ci{idName=idName i}
		| otherwise			  = ci

           ud <- setInterfaceFlag (ComIDispatch False) (uuidDecl ci' [] Clsid)
           return (infoHeader (CoClass ci' [CoClassInterface i Nothing]) `andDecl` ud)

     forClient <- getClientFlag
     let is_source = hasSourceAttribute (idAttributes i)
     setSourceIfaceFlag is_source $ do
      dserv <- 
        if (is_source && optIgnoreSourceIfaces) then
           return emptyDecl
        else 
          if (is_source && forClient) || (not forClient && not is_source) then
              marshallServ i inherit decls
           else
             cgInterface i inherit decls
      return (cls_d `andDecl` dserv) 
    where
      deps = filterAttributes (idAttributes i) ["depender"]

   Method i cc res ps offs     -> do
     forClient <- getClientFlag
     is_source <- getSourceIfaceFlag
     if (is_source && forClient) || (not forClient && not is_source) then do
        k <- getInterfaceFlag
        case k of
	  StdFFI -> getDeclName $ \ nm ->
		    marshallFun (Just nm) i (FunTy cc res ps) 
          _ | optJNI    ->  cgJServMethod i res ps
	    | otherwise ->  do
	        isInDisp <- isInDispInterface
	    	cgServMethod i res ps is_source (optUseStdDispatch && isInDisp)
      else
	if optJNI then
	   cgJNIMethod i res ps
	 else
  	   cgMethod i cc res ps offs Nothing

   Property i ty _ s g      -> cgProperty i ty s g
   HsLiteral str	    -> return (Haskell str)
   CLiteral str             -> return (CCode str)
   CInclude  fname          -> return (Include fname)
   DispInterface i ii ps ms -> cgDispInterface i ii ps ms
   CoClass i mems	    -> cgCoClass i mems
   Library i decls	    -> cgLibrary i decls
   Module i ds		    -> cgModule i ds
   _			    -> return emptyDecl

\end{code} % % Typedefs