packages feed

haskell-awk-1.1: src/System/Console/Hawk/Context/Base.hs

{-# LANGUAGE PackageImports #-}
-- | Everything we need to know in order to evaluate a user expression,
--   except for the user expression itself.
module System.Console.Hawk.Context.Base
  ( Context(..)
  , getContext
  ) where

import "mtl" Control.Monad.Trans
import Data.Maybe
import System.Directory
import System.IO

import Control.Monad.Trans.Uncertain
import Control.Monad.Trans.State.Persistent
import Data.Cache
import qualified Data.HaskellModule as M
import System.Console.Hawk.Context.Dir
import System.Console.Hawk.Context.Paths
import System.Console.Hawk.UserPrelude
import System.Console.Hawk.Version


data Context = Context
  { contextPaths :: ContextPaths
  , moduleName :: String
  , extensions :: [M.ExtensionName]
  , modules :: [M.QualifiedModule]
  } deriving (Eq, Read, Show)

-- | Obtains a Context, either from the cache or from the user prelude.
-- 
-- Must be called inside a `withLock` block, otherwise the cache file
-- might get accessed by two instances of Hawk at once.
getContext :: FilePath -> UncertainT IO Context
getContext contextDir = do
    createDefaultContextDir paths
    key <- lift $ getKey preludeFile
    
    -- skip `newContext` if the cached copy is still good.
    withPersistentStateT cacheFile [] $ cached cache key
                                      $ lift
                                      $ newContext paths
  where
    paths = mkContextPaths contextDir
    preludeFile = originalPreludePath paths
    cacheFile   = cachedPreludePath paths
    cache = singletonCache assocCache
    
    getKey f = do
        modifiedTime <- getModificationTime f
        fileSize <- withFile f ReadMode hFileSize
        return (versionString, f, modifiedTime, fileSize)

-- | Construct a Context by parsing the user prelude.
newContext :: ContextPaths -> UncertainT IO Context
newContext paths = do
    userPrelude <- readUserPrelude originalFile
    lift $ createDirectoryIfMissing True cacheDir
    compileUserPrelude originalFile canonicalFile userPrelude
    
    return $ Context
           { contextPaths = paths
           , moduleName = fromJust (M.moduleName userPrelude)
           , extensions = M.languageExtensions userPrelude
           , modules = M.importedModules userPrelude
           }
  where
    originalFile  = originalPreludePath paths
    cacheDir      = cacheDirPath paths
    canonicalFile = canonicalPreludePath paths