-- | Adding code to initialise the runtime system.
module DDC.Core.Salt.Convert.Init
        (initRuntime)
where
import DDC.Core.Salt.Compounds
import DDC.Core.Salt.Runtime
import DDC.Core.Salt.Name
import DDC.Core.Module
import DDC.Core.Exp
import Data.List


-- | If this it the Main module, 
--   then add code to the 'main' function to initialise the runtime system.
--
--   Returns Nothing if this is the Main module, 
--      but there is no main function.
initRuntime
        :: Config
        -> Module a Name
        -> Maybe (Module a Name)

initRuntime config mm@ModuleCore{}
        | isMainModule mm
        = case initRuntimeTopX config (moduleBody mm) of
                Nothing -> Nothing
                Just x' -> Just $ mm { moduleBody = x'}

        | otherwise     
        = Just mm


-- | Takes the top-level let-bindings of amodule
--      and add code to initialise the runtime system.
initRuntimeTopX :: Config -> Exp a Name -> Maybe (Exp a Name)
initRuntimeTopX config xx
        | XLet a (LRec bxs) x2  <- xx
        , Just (bMain, xMain)   <- find   (isMainBind . fst) bxs
        , bxs_cut               <- filter (not . isMainBind . fst) bxs
        = let   
                -- Initial size of the heap.
                bytes   = configHeapSize config

                xMain'  = hackBodyX (XLet a (LLet (BNone tVoid) 
                                                  (xCreate a bytes))) 
                                    xMain

          in    Just $ XLet a (LRec $ bxs_cut ++ [(bMain, xMain')]) x2

        -- This was supposed to be the main Module,
        -- but there was no 'main' function for the program entry point.
        | otherwise
        = Nothing


-- | Apply a worker to the body of some function.
--   Enters into enclosing lambdas.
hackBodyX :: (Exp a n -> Exp a n) -> Exp a n -> Exp a n
hackBodyX f xx
 = case xx of
        XLAM a b x      -> XLAM a b $ hackBodyX f x
        XLam a b x      -> XLam a b $ hackBodyX f x
        _               -> f xx


-- | Check whether this is the bind for the 'main' function.
isMainBind :: Bind Name -> Bool
isMainBind bb
  = case bb of
        (BName (NameVar "main") _)      -> True
        _                               -> False