content: 
  -- | Retrieving inliner templates from a list of modules.
  module DDC.Core.Transform.Inline.Templates
          ( InlineSpec(..)
          , lookupTemplateFromModules
          , lookupTemplateFromModule )
  where
  import DDC.Core.Exp
  import DDC.Core.Module
  import DDC.Core.Transform.AnonymizeX
  import Data.List
  import Data.Set                 (Set)
  import Data.Map                 (Map)
  import qualified Data.Map       as Map
  import qualified Data.Set       as Set
  
  
  -- | Inlining specification says what bindings we should inline
  --   from a particular module.
  data InlineSpec n
          -- | Inline all bindings from a module,
          --   but exclude some particulars.
          = InlineSpecAll
          { inlineSpecModuleName  :: ModuleName
          , inlineSpecExclude     :: Set n }
  
          -- | Inline no bindings from a module,
          --   but include some particulars.
          | InlineSpecNone
          { inlineSpecModuleName  :: ModuleName
          , inlineSpecInclude     :: Set n }
          deriving Show
  
  
  -- | Lookup an inliner template from a list of modules.
  ---
  --   This just does a linear search through all the modules.
  --   As we only inline functions defined at top level, we don't need to worry
  --   about lifting indices in templates when we go under binders.
  --
  lookupTemplateFromModules 
          :: (Ord n, Show n)
          => Map ModuleName (InlineSpec n)
                                  -- ^ Inliner specifications for the modules.
          -> [Module a n]         -- ^ Modules to use for inliner templates.
          -> n 
          -> Maybe (Exp a n)
  
  lookupTemplateFromModules specs mm n
   | m : ms <- mm
   = let  -- If there is no inliner spec then don't inline anything.
          spec    = case Map.lookup (moduleName m) specs of
                          Just s  -> s
                          Nothing -> InlineSpecNone (moduleName m) Set.empty
  
     in   case lookupTemplateFromModule spec m n of
                  Nothing -> lookupTemplateFromModules specs ms n
                  Just x  -> Just x
  
   | otherwise
   = Nothing
  
  
  lookupTemplateFromModule 
          :: Ord n
          => InlineSpec n         -- ^ Inliner specification for this module.
          -> Module a n           -- ^ Module to use for inliner templates.
          -> n    
          -> Maybe (Exp a n)
  
  lookupTemplateFromModule spec mm n
          | shouldInline spec n
          , XLet _ (LRec bxs) _  <- moduleBody mm
          , Just (_,x)           <- find (\(BName n' _, _) -> n == n') bxs
          = Just $ anonymizeX x
  
          | otherwise
          = Nothing
  
  
  -- | Decide whether we should inline the binding with this name based on the 
  --   provided inliner specification.
  shouldInline
          :: Ord n
          => InlineSpec n -> n -> Bool
  
  shouldInline spec n
   = case spec of
          InlineSpecAll _ except
           | Set.member n except  -> False
           | otherwise            -> True
  
          InlineSpecNone _ include
           | Set.member n include -> True
           | otherwise            -> False
  
  