PageRenderTime 52ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/Zoom/Interpreter.hs

http://github.com/iand675/Zoom
Haskell | 117 lines | 91 code | 17 blank | 9 comment | 2 complexity | c2c6f1531adca10077791b82a0d6761d MD5 | raw file
Possible License(s): BSD-3-Clause
  1. {-# LANGUAGE ScopedTypeVariables #-}
  2. module Zoom.Interpreter where
  3. import Language.Haskell.Interpreter
  4. import qualified GHC
  5. import PackageConfig
  6. import UniqFM
  7. import qualified HscTypes as GHC
  8. import Packages
  9. import Control.Monad
  10. import Control.Monad.Trans
  11. import System.Directory
  12. import System.FilePath
  13. import Data.Monoid
  14. import Zoom.Task
  15. import Data.Maybe
  16. import qualified Data.List as L
  17. import Zoom.Demand
  18. import Data.Typeable hiding (typeOf)
  19. ifM :: (Monad m, Monoid md) => m Bool -> md -> m md
  20. ifM m x = do
  21. result <- m
  22. return $ if result
  23. then x
  24. else mempty
  25. qualifyModule x = L.stripPrefix "Zoom.Task." x
  26. qualifyFunctions (m, fs) = map (qualifyFun qualifyAs) fs
  27. where qualifyAs = fromMaybe m (qualifyModule m)
  28. defaultModules = [("Prelude", Nothing), ("Zoom.Task", Just "Zoom.Task"), ("Zoom.Demand", Nothing)]
  29. ghcGetAvailableModules :: GHC.GhcMonad m => m [GHC.ModuleName]
  30. ghcGetAvailableModules = do
  31. dflags <- GHC.getSessionDynFlags
  32. let pkg_db = pkgIdMap (GHC.pkgState dflags)
  33. return $ concat (map exposedModules (filter exposed (eltsUFM pkg_db)))
  34. getAvailableModules :: MonadInterpreter m => m [ModuleName]
  35. getAvailableModules = liftM (map GHC.moduleNameString) $ runGhc ghcGetAvailableModules
  36. -- | entry point for the standard zoom interpreter
  37. interpreterMain :: forall s. (Typeable s, Retrieve s) => [String] -> Interpreter [(String, Task s)]
  38. interpreterMain imports = do
  39. set [ languageExtensions := [TemplateHaskell, QuasiQuotes]
  40. , searchPath := ["./tasks"]]
  41. loadLocalTaskModules
  42. qualified <- importZoomTasks imports
  43. taskStrings <- availableTasks qualified
  44. tasks <- mapM (\x -> interpret x (as :: Task s)) taskStrings
  45. return $ zip taskStrings tasks
  46. qualifyFun q f = q ++ ('.':f)
  47. filterTaskFuns :: [String] -> Interpreter [String]
  48. filterTaskFuns fs = do
  49. tasks <- filterM (\f -> typeOf f >>= \t -> return $ L.isPrefixOf "Zoom.Task.Task" t) fs
  50. return tasks
  51. -- | loads up modules located in the task subdirectory of the current directory.
  52. -- note that this currently needs to be run before loading global tasks.
  53. loadLocalTaskModules :: Interpreter ()
  54. loadLocalTaskModules = do
  55. dirs <- liftIO getTaskDirs
  56. allDirPaths <- liftIO $ mapM getAndQualifyContents dirs
  57. allModulePaths <- liftIO $ filterM (fmap not . doesDirectoryExist) $ join allDirPaths
  58. loadModules allModulePaths
  59. -- | imports both local and global Zoom.Task.* modules.
  60. -- returns the qualified module names of all Zoom.Task.* modules.
  61. importZoomTasks :: [String] -> Interpreter [ModuleName]
  62. importZoomTasks imports = do
  63. localModules <- getLoadedModules
  64. globalModules <- getAvailableModules
  65. let
  66. zoomModules = filter (L.isPrefixOf "Zoom.Task.") (localModules ++ globalModules)
  67. qualifiedModules = defaultModules ++ (zip imports (repeat Nothing)) ++ zip zoomModules (map qualifyModule zoomModules)
  68. setImportsQ qualifiedModules
  69. return zoomModules
  70. getFunctionsFromImports :: [ModuleName] -> Interpreter [(ModuleName, [String])]
  71. getFunctionsFromImports imps = do
  72. exports <- mapM getModuleExports imps
  73. let pairs = zip imps $ map (map name . filter isFunction) exports
  74. return pairs
  75. runZoomInterpreter :: (Typeable s, Retrieve s) => [String] -> IO (Either InterpreterError [(String, Task s)])
  76. runZoomInterpreter imports = runInterpreter $ interpreterMain imports
  77. isFunction x = case x of
  78. Fun _ -> True
  79. _ -> False
  80. printTaskDescription taskName = do
  81. description <- interpret ("Zoom.Task.desc " ++ taskName) (as :: String)
  82. liftIO $ putStrLn description
  83. -- get current working directory
  84. -- TODO recurse all the way to home, getting tasks for each level.
  85. -- TODO also, get them from some global location
  86. getTaskDirs = do
  87. current <- getCurrentDirectory
  88. let taskDir = current </> "tasks"
  89. ifM (doesDirectoryExist taskDir) [taskDir]
  90. getAndQualifyContents dir = do
  91. contents <- getDirectoryContents dir
  92. let realContents = filter (`notElem` [".", ".."]) contents
  93. return $ map (dir </>) realContents
  94. availableTasks :: [String] -> Interpreter [String]
  95. availableTasks qualified = do
  96. modsWithFuns <- getFunctionsFromImports qualified
  97. let qualifiedFuns = join $ map qualifyFunctions modsWithFuns
  98. filterTaskFuns qualifiedFuns
  99. printAvailableTasks taskNames = do
  100. mapM_ (\t -> liftIO (putStr (t ++ ": ")) >> printTaskDescription t) $ taskNames