packages feed

ddc-core-salt-0.4.1.1: DDC/Core/Salt/Convert/Super.hs

module DDC.Core.Salt.Convert.Super
        (convSuperM)
where
import DDC.Core.Salt.Convert.Exp
import DDC.Core.Salt.Convert.Type
import DDC.Core.Salt.Convert.Name
import DDC.Core.Salt.Convert.Base
import DDC.Core.Salt.Name
import DDC.Core.Salt.Platform
import DDC.Core.Collect
import DDC.Core.Predicates
import DDC.Core.Compounds
import DDC.Core.Module
import DDC.Core.Exp
import DDC.Type.Env                     (KindEnv, TypeEnv)
import DDC.Base.Pretty
import DDC.Control.Monad.Check          (throw)
import qualified DDC.Type.Env           as Env
import Control.Monad
import Data.Maybe



-- | Convert a supercombinator definition to C source text.
convSuperM 
        :: Show a 
        => Platform                     -- ^ Target platform specifiction.
        -> Module a Name                -- ^ Enclosing module.
        -> KindEnv Name                 -- ^ Top-level kind environment.
        -> TypeEnv Name                 -- ^ Top-level type environment.
        -> Name                         -- ^ Name of the supercombinator.
        -> Type Name                    -- ^ Type of the supercombinator.
        -> Exp a Name                   -- ^ Body expression of the supercombiantor.
        -> ConvertM a Doc

convSuperM     pp mm kenv0 tenv0 nSuper tSuper xx
 = convSuperM' pp mm kenv0 tenv0 nSuper tSuper [] xx

convSuperM'    pp mm kenv  tenv  nSuper tSuper bsParam xx
 
 -- Enter into type abstractions,
 --  adding the bound name to the environment.
 | XLAM _ b x   <- xx
 = convSuperM' pp mm (Env.extend b kenv) tenv 
        nSuper tSuper bsParam x

 -- Enter into value abstractions,
 --  remembering that we're now in a function that has this parameter.
 | XLam _ b x   <- xx
 = convSuperM' pp mm kenv (Env.extend b tenv) 
        nSuper tSuper (bsParam ++ [b]) x

 -- Convert the function body.
 | otherwise
 = do   
        -- Convert the function name.
        let Just nSuper' = seaNameOfSuper 
                                (lookup nSuper (moduleImportValues mm))
                                (lookup nSuper (moduleExportValues mm))
                                nSuper
        
        let (_, tResult) = takeTFunArgResult $ eraseTForalls tSuper

        -- Convert the function parameters.
        bsParam'        <- sequence
                        $ [ convBind kenv tenv i b
                                | b     <- filter keepBind bsParam
                                | i     <- [0..] ]

        -- Convert the result type.
        tResult'        <- convTypeM kenv  $ eraseWitArg tResult

        -- Emit automatic variable definitions for all the local variables
        -- used in the body of the function.
        let (_, bsVal)  = collectBinds xx
        dsVal           <- liftM catMaybes $ mapM (makeVarDecl kenv) 
                        $  filter keepBind bsVal

        -- Convert the body of the function.
        --   We pass in ContextTop to say we're at the top-level of the function,
        ---  so the block must explicitly pass control in the final statement.
        let config      = Config
                        { configPlatform = pp
                        , configModule   = mm }
        
        xBody'          <- convBlockM config ContextTop kenv tenv xx

        -- Paste everything together.
        return  $ vcat
                [ -- Function header.
                  tResult'                      -- Result type.
                         <+> nSuper'            -- Function name.
                         <+> parenss bsParam'   -- Argument list.
                  
                  -- Function body.
                , lbrace
                ,       indent 8 $ vcat dsVal   -- Local variable definitions.
                ,       empty
                ,       indent 8 xBody'         -- Statements that do some things.
                , rbrace
                , empty]
        

-- | Convert a function parameter binding to C source text.
convBind :: KindEnv Name -> TypeEnv Name -> Int -> Bind Name -> ConvertM a Doc
convBind kenv _tenv iPos b
 = case b of 
        -- Anonymous arguments.
        BNone t
         -> do  t'      <- convTypeM kenv t
                return  $ t' <+> (text $ "_arg" ++ show iPos)

        -- Named variables binders.
        BName n t
         | Just n'      <- seaNameOfLocal n
         -> do  t'      <- convTypeM kenv t
                return  $ t' <+> n'

        _       -> throw $ ErrorParameterInvalid b


-- | Make a variable declaration for this binder.
makeVarDecl :: KindEnv Name -> Bind Name -> ConvertM a (Maybe Doc)
makeVarDecl kenv bb
 = case bb of
        BNone{} 
         -> return Nothing

        BName n t
         | Just n'      <- seaNameOfLocal n
         -> do  t'      <- convTypeM kenv t
                return  $ Just (t' <+> n' <+> equals <+> text "0" <> semi)

        _       -> throw $ ErrorParameterInvalid bb


-- | Remove witness arguments from the return type
eraseWitArg :: Type Name -> Type Name
eraseWitArg tt
 = case tt of 
        -- Distinguish between application of witnesses and ptr
        TApp _ t2
         | Just (NamePrimTyCon PrimTyConPtr, _) <- takePrimTyConApps tt -> tt
         | otherwise -> eraseWitArg t2

        -- Pass through all other types
        _ -> tt


-- | Ditch witness bindings
keepBind :: Bind Name -> Bool
keepBind bb
 = case bb of        
        BName _ t
         |  tc : _ <- takeTApps t
         ,  isWitnessType tc
         -> False
         
        _       -> True


parenss :: [Doc] -> Doc
parenss xs = encloseSep lparen rparen (comma <> space) xs