{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Fay.Compiler.ModuleT
(
ModuleT
, getModuleInfo
, runModuleT
, MonadModule (..)
, ModName (..)
) where
import Fay.Compiler.Prelude
import Control.Monad.Reader
import Control.Monad.State
import qualified Data.Char as Char (isAlphaNum, isUpper)
import qualified Data.Map as Map
newtype ModuleName = ModuleName [String]
deriving (Eq, Ord, Show)
fromString :: String -> ModuleName
fromString string
| all validModuleComponent components' = ModuleName components'
| otherwise = error $ "ModuleName.fromString: invalid module name " ++ show string
where
components' = split string
split cs = case break (=='.') cs of
(chunk,[]) -> [chunk]
(chunk,_:rest) -> chunk : split rest
validModuleComponent :: String -> Bool
validModuleComponent [] = False
validModuleComponent (c:cs) = Char.isUpper c
&& all validModuleChar cs
validModuleChar :: Char -> Bool
validModuleChar c = Char.isAlphaNum c || c == '_' || c == '\''
class Monad m => MonadModule m where
type ModuleInfo m
lookupInCache :: ModName n => n -> m (Maybe (ModuleInfo m))
insertInCache :: ModName n => n -> ModuleInfo m -> m ()
readModuleInfo :: ModName n => [FilePath] -> n -> m (ModuleInfo m)
class ModName n where
modToString :: n -> String
instance ModName String where
modToString = id
convertModuleName :: ModName n => n -> ModuleName
convertModuleName = fromString . modToString
getModuleInfo :: (MonadModule m, ModName n) => n -> m (Maybe (ModuleInfo m))
getModuleInfo = lookupInCache
newtype ModuleT i m a =
ModuleT
(StateT (Map.Map ModuleName i)
(ReaderT ([FilePath] -> ModuleName -> m i) m) a)
deriving (Functor, Applicative, Monad)
instance MonadTrans (ModuleT i) where
lift = ModuleT . lift . lift
instance MonadIO m => MonadIO (ModuleT i m) where
liftIO = ModuleT . liftIO
instance (Functor m, Monad m) => MonadModule (ModuleT i m) where
type ModuleInfo (ModuleT i m) = i
lookupInCache n = ModuleT $ Map.lookup (convertModuleName n) <$> get
insertInCache n i = ModuleT $ modify $ Map.insert (convertModuleName n) i
readModuleInfo dirs mod' =
lift =<< ModuleT ask <*> pure dirs <*> pure (convertModuleName mod')
runModuleT
:: (Monad m, Monoid i)
=> ModuleT i m a
-> m (a, Map.Map ModuleName i)
runModuleT (ModuleT a) =
runReaderT (runStateT a Map.empty) (\_ _ -> return mempty)