81 lines
2.2 KiB
Haskell
81 lines
2.2 KiB
Haskell
|
-- | A Compiler manages targets and dependencies between targets.
|
||
|
--
|
||
|
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||
|
module Hakyll.Core.Compiler
|
||
|
( Dependencies
|
||
|
, CompilerM
|
||
|
, Compiler
|
||
|
, runCompiler
|
||
|
, require
|
||
|
, target
|
||
|
) where
|
||
|
|
||
|
import Control.Arrow (second)
|
||
|
import Control.Applicative (Applicative, (<$>))
|
||
|
import Control.Monad.State (State, modify, runState)
|
||
|
import Control.Monad.Reader (ReaderT, ask, runReaderT)
|
||
|
import Data.Set (Set)
|
||
|
import qualified Data.Set as S
|
||
|
|
||
|
import Hakyll.Core.Identifier
|
||
|
import Hakyll.Core.Target.Internal
|
||
|
|
||
|
-- | A set of dependencies
|
||
|
--
|
||
|
type Dependencies = Set Identifier
|
||
|
|
||
|
-- | Add one dependency
|
||
|
--
|
||
|
addDependency :: Identifier -> CompilerM a ()
|
||
|
addDependency dependency = CompilerM $ modify $ addDependency'
|
||
|
where
|
||
|
addDependency' x = x
|
||
|
{ compilerDependencies = S.insert dependency $ compilerDependencies x
|
||
|
}
|
||
|
|
||
|
-- | Environment in which a compiler runs
|
||
|
--
|
||
|
data CompilerEnvironment a = CompilerEnvironment
|
||
|
{ compilerIdentifier :: Identifier -- ^ Target identifier
|
||
|
}
|
||
|
|
||
|
-- | State carried along by a compiler
|
||
|
--
|
||
|
data CompilerState = CompilerState
|
||
|
{ compilerDependencies :: Dependencies
|
||
|
}
|
||
|
|
||
|
-- | The compiler monad
|
||
|
--
|
||
|
newtype CompilerM a b = CompilerM
|
||
|
{ unCompilerM :: ReaderT (CompilerEnvironment a) (State CompilerState) b
|
||
|
} deriving (Monad, Functor, Applicative)
|
||
|
|
||
|
-- | Simplified type for a compiler generating a target (which covers most
|
||
|
-- cases)
|
||
|
--
|
||
|
type Compiler a = CompilerM a (TargetM a a)
|
||
|
|
||
|
-- | Run a compiler, yielding the resulting target and it's dependencies
|
||
|
--
|
||
|
runCompiler :: Compiler a -> Identifier -> (TargetM a a, Dependencies)
|
||
|
runCompiler compiler identifier = second compilerDependencies $
|
||
|
runState (runReaderT (unCompilerM compiler) env) state
|
||
|
where
|
||
|
env = CompilerEnvironment {compilerIdentifier = identifier}
|
||
|
state = CompilerState S.empty
|
||
|
|
||
|
-- | Require another target. Using this function ensures automatic handling of
|
||
|
-- dependencies
|
||
|
--
|
||
|
require :: Identifier
|
||
|
-> Compiler a
|
||
|
require identifier = do
|
||
|
addDependency identifier
|
||
|
return $ TargetM $ flip targetDependencyLookup identifier <$> ask
|
||
|
|
||
|
-- | Construct a target inside a compiler
|
||
|
--
|
||
|
target :: TargetM a a -> Compiler a
|
||
|
target = return
|