1313--
1414-----------------------------------------------------------------------------
1515
16- {-# LANGUAGE DoAndIfThenElse, FlexibleContexts #-}
16+ {-# LANGUAGE DoAndIfThenElse, FlexibleContexts, GeneralizedNewtypeDeriving #-}
1717
1818module Main where
1919
@@ -24,29 +24,36 @@ import Control.Monad
2424import Control.Monad.Trans.Class
2525import Control.Monad.Trans.Maybe (MaybeT (.. ), runMaybeT )
2626import Control.Monad.Trans.State.Strict
27+ import Control.Monad.Error (ErrorT (.. ), MonadError )
28+ import Control.Monad.Error.Class (MonadError (.. ))
2729
2830import Data.List (intercalate , isPrefixOf , nub , sortBy )
2931import Data.Maybe (mapMaybe )
3032import Data.Foldable (traverse_ )
31- import Data.Traversable (traverse )
3233import Data.Version (showVersion )
34+ import Data.Traversable (traverse )
3335
3436import Parser
3537
38+ import System.IO.Error (tryIOError )
3639import System.Console.Haskeline
37- import System.Directory (doesFileExist , findExecutable , getHomeDirectory , getCurrentDirectory )
40+ import System.Directory
41+ (createDirectoryIfMissing , getModificationTime , doesFileExist ,
42+ findExecutable , getHomeDirectory , getCurrentDirectory )
43+ import System.Process (readProcessWithExitCode )
3844import System.Exit
3945import System.Environment.XDG.BaseDir
40- import System.FilePath ((</>) , isPathSeparator )
46+ import System.FilePath
47+ (pathSeparator , takeDirectory , (</>) , isPathSeparator )
4148import qualified System.Console.CmdTheLine as Cmd
42- import System.Process
4349
4450import Text.Parsec (ParseError )
4551
4652import qualified Data.Map as M
4753import qualified Language.PureScript as P
4854import qualified Paths_purescript as Paths
49- import qualified System.IO.UTF8 as U (print , readFile )
55+ import qualified System.IO.UTF8 as U
56+ (writeFile , putStrLn , print , readFile )
5057
5158-- |
5259-- The PSCI state.
@@ -57,7 +64,7 @@ import qualified System.IO.UTF8 as U (print, readFile)
5764data PSCiState = PSCiState
5865 { psciImportedFilenames :: [FilePath ]
5966 , psciImportedModuleNames :: [P. ModuleName ]
60- , psciLoadedModules :: [P. Module ]
67+ , psciLoadedModules :: [( FilePath , P. Module) ]
6168 , psciLetBindings :: [P. Value -> P. Value ]
6269 }
6370
@@ -78,7 +85,7 @@ updateImports name st = st { psciImportedModuleNames = name : psciImportedModule
7885-- |
7986-- Updates the state to have more loaded files.
8087--
81- updateModules :: [P. Module ] -> PSCiState -> PSCiState
88+ updateModules :: [( FilePath , P. Module) ] -> PSCiState -> PSCiState
8289updateModules modules st = st { psciLoadedModules = psciLoadedModules st ++ modules }
8390
8491-- |
@@ -168,7 +175,7 @@ completion = completeWord Nothing " \t\n\r" findCompletions
168175 where
169176 findCompletions :: String -> StateT PSCiState IO [Completion ]
170177 findCompletions str = do
171- ms <- psciLoadedModules <$> get
178+ ms <- map snd . psciLoadedModules <$> get
172179 files <- listFiles str
173180 let matches = filter (isPrefixOf str) (names ms)
174181 return $ sortBy sorter $ map simpleCompletion matches ++ files
@@ -197,7 +204,39 @@ completion = completeWord Nothing " \t\n\r" findCompletions
197204-- | Compilation options.
198205--
199206options :: P. Options
200- options = P. Options False True False True (Just " Main" ) True " PS" [] [] False
207+ options = P. Options False True False True Nothing True Nothing [] [] False
208+
209+ -- |
210+ -- PSCI monad
211+ --
212+ newtype PSCI a = PSCI { runPSCI :: InputT (StateT PSCiState IO ) a } deriving (Functor , Applicative , Monad )
213+
214+ psciIO :: IO a -> PSCI a
215+ psciIO io = PSCI (lift (lift io))
216+
217+ newtype Make a = Make { unMake :: ErrorT String IO a } deriving (Functor , Applicative , Monad , MonadError String )
218+
219+ runMake :: Make a -> IO (Either String a )
220+ runMake = runErrorT . unMake
221+
222+ makeIO :: IO a -> Make a
223+ makeIO = Make . ErrorT . fmap (either (Left . show ) Right ) . tryIOError
224+
225+ instance P. MonadMake Make where
226+ getTimestamp path = makeIO $ do
227+ exists <- doesFileExist path
228+ case exists of
229+ True -> Just <$> getModificationTime path
230+ False -> return Nothing
231+ readTextFile path = makeIO $ U. readFile path
232+ writeTextFile path text = makeIO $ do
233+ mkdirp path
234+ U. writeFile path text
235+ liftError = either throwError return
236+ progress s = unless (s == " Compiling Main" ) $ makeIO . U. putStrLn $ s
237+
238+ mkdirp :: FilePath -> IO ()
239+ mkdirp = createDirectoryIfMissing True . takeDirectory
201240
202241-- |
203242-- Makes a volatile module to execute the current expression.
@@ -217,34 +256,45 @@ createTemporaryModule exec PSCiState{psciImportedModuleNames = imports, psciLetB
217256 in
218257 P. Module moduleName ((importDecl `map` imports) ++ decls) Nothing
219258
259+ modulesDir :: FilePath
260+ modulesDir = " psci_modules" ++ pathSeparator : " node_modules"
261+
262+ indexFile :: FilePath
263+ indexFile = " psci_modules" ++ pathSeparator : " index.js"
264+
220265-- |
221266-- Takes a value declaration and evaluates it with the current state.
222267--
223- handleDeclaration :: P. Value -> PSCiState -> InputT (StateT PSCiState IO ) ()
224- handleDeclaration value st = do
268+ handleDeclaration :: P. Value -> PSCI ()
269+ handleDeclaration value = do
270+ st <- PSCI $ lift get
225271 let m = createTemporaryModule True st value
226- case P. compile options (psciLoadedModules st ++ [m]) of
227- Left err -> outputStrLn err
228- Right (js, _, _) -> do
229- process <- lift . lift $ findNodeProcess
230- result <- lift . lift $ traverse (\ node -> readProcessWithExitCode node [] js) process
272+ e <- psciIO . runMake $ P. make modulesDir options (psciLoadedModules st ++ [(" Main.purs" , m)])
273+ case e of
274+ Left err -> PSCI $ outputStrLn err
275+ Right _ -> do
276+ psciIO $ writeFile indexFile $ " require('Main').main();"
277+ process <- psciIO findNodeProcess
278+ result <- psciIO $ traverse (\ node -> readProcessWithExitCode node [indexFile] " " ) process
231279 case result of
232- Just (ExitSuccess , out, _) -> outputStrLn out
233- Just (ExitFailure _, _, err) -> outputStrLn err
234- Nothing -> outputStrLn " Couldn't find node.js"
280+ Just (ExitSuccess , out, _) -> PSCI $ outputStrLn out
281+ Just (ExitFailure _, _, err) -> PSCI $ outputStrLn err
282+ Nothing -> PSCI $ outputStrLn " Couldn't find node.js"
235283
236284-- |
237285-- Takes a value and prints its type
238286--
239- handleTypeOf :: P. Value -> PSCiState -> InputT (StateT PSCiState IO ) ()
240- handleTypeOf value st = do
287+ handleTypeOf :: P. Value -> PSCI ()
288+ handleTypeOf value = do
289+ st <- PSCI $ lift get
241290 let m = createTemporaryModule False st value
242- case P. compile options { P. optionsMain = Nothing } (psciLoadedModules st ++ [m]) of
243- Left err -> outputStrLn err
244- Right (_, _, env') ->
291+ e <- psciIO . runMake $ P. make modulesDir options (psciLoadedModules st ++ [(" Main.purs" , m)])
292+ case e of
293+ Left err -> PSCI $ outputStrLn err
294+ Right env' ->
245295 case M. lookup (P. ModuleName [P. ProperName " Main" ], P. Ident " it" ) (P. names env') of
246- Just (ty, _) -> outputStrLn . P. prettyPrintType $ ty
247- Nothing -> outputStrLn " Could not find type"
296+ Just (ty, _) -> PSCI . outputStrLn . P. prettyPrintType $ ty
297+ Nothing -> PSCI $ outputStrLn " Could not find type"
248298
249299-- Commands
250300
@@ -265,27 +315,31 @@ getCommand = do
265315-- |
266316-- Performs an action for each meta-command given, and also for expressions..
267317--
268- handleCommand :: Command -> InputT ( StateT PSCiState IO ) ()
269- handleCommand (Expression val) = lift get >>= handleDeclaration val
270- handleCommand Help = outputStrLn helpMessage
271- handleCommand (Import moduleName) = lift $ modify (updateImports moduleName)
272- handleCommand (Let l) = lift $ modify (updateLets l)
318+ handleCommand :: Command -> PSCI ()
319+ handleCommand (Expression val) = handleDeclaration val
320+ handleCommand Help = PSCI $ outputStrLn helpMessage
321+ handleCommand (Import moduleName) = PSCI $ lift $ modify (updateImports moduleName)
322+ handleCommand (Let l) = PSCI $ lift $ modify (updateLets l)
273323handleCommand (LoadFile filePath) = do
274- absPath <- lift . lift $ expandTilde filePath
275- exists <- lift . lift $ doesFileExist absPath
324+ absPath <- psciIO $ expandTilde filePath
325+ exists <- psciIO $ doesFileExist absPath
276326 if exists then do
277- lift $ modify (updateImportedFiles absPath)
278- either outputStrLn (lift . modify . updateModules) =<< (lift . lift $ loadModule absPath)
327+ PSCI . lift $ modify (updateImportedFiles absPath)
328+ m <- psciIO $ loadModule absPath
329+ case m of
330+ Left err -> PSCI $ outputStrLn err
331+ Right mods -> PSCI . lift $ modify (updateModules (map ((,) absPath) mods))
279332 else
280- outputStrLn $ " Couldn't locate: " ++ filePath
333+ PSCI . outputStrLn $ " Couldn't locate: " ++ filePath
281334handleCommand Reset = do
282- files <- psciImportedFilenames <$> lift get
283- modulesOrFirstError <- fmap concat . sequence <$> mapM (lift . lift . loadModule) files
335+ files <- psciImportedFilenames <$> PSCI (lift get)
336+ filesAndModules <- mapM (\ file -> fmap (fmap (map ((,) file))) . psciIO . loadModule $ file) files
337+ let modulesOrFirstError = fmap concat $ sequence filesAndModules
284338 case modulesOrFirstError of
285- Left err -> lift . lift $ putStrLn err >> exitFailure
286- Right modules -> lift $ put (PSCiState files defaultImports modules [] )
287- handleCommand (TypeOf val) = lift get >>= handleTypeOf val
288- handleCommand _ = outputStrLn " Unknown command"
339+ Left err -> psciIO $ putStrLn err >> exitFailure
340+ Right modules -> PSCI . lift $ put (PSCiState files defaultImports modules [] )
341+ handleCommand (TypeOf val) = handleTypeOf val
342+ handleCommand _ = PSCI $ outputStrLn " Unknown command"
289343
290344inputFiles :: Cmd. Term [FilePath ]
291345inputFiles = Cmd. value $ Cmd. posAny [] $ Cmd. posInfo { Cmd. posName = " file(s)"
@@ -311,15 +365,16 @@ loop :: [FilePath] -> IO ()
311365loop files = do
312366 config <- loadUserConfig
313367 preludeFilename <- getPreludeFilename
314- modulesOrFirstError <- fmap concat . sequence <$> mapM loadModule (preludeFilename : files)
368+ filesAndModules <- mapM (\ file -> fmap (fmap (map ((,) file))) . loadModule $ file) (preludeFilename : files)
369+ let modulesOrFirstError = fmap concat $ sequence filesAndModules
315370 case modulesOrFirstError of
316371 Left err -> putStrLn err >> exitFailure
317372 Right modules -> do
318373 historyFilename <- getHistoryFilename
319374 let settings = defaultSettings {historyFile = Just historyFilename}
320375 flip evalStateT (PSCiState (preludeFilename : files) defaultImports modules [] ) . runInputT (setComplete completion settings) $ do
321376 outputStrLn prologueMessage
322- traverse_ (mapM_ handleCommand) config
377+ traverse_ (mapM_ (runPSCI . handleCommand) ) config
323378 go
324379 where
325380 go :: InputT (StateT PSCiState IO ) ()
@@ -329,7 +384,7 @@ loop files = do
329384 Left err -> outputStrLn (show err) >> go
330385 Right Nothing -> go
331386 Right (Just Quit ) -> outputStrLn quitMessage
332- Right (Just c') -> handleCommand c' >> go
387+ Right (Just c') -> runPSCI ( handleCommand c') >> go
333388
334389term :: Cmd. Term (IO () )
335390term = loop <$> inputFiles
@@ -343,4 +398,3 @@ termInfo = Cmd.defTI
343398
344399main :: IO ()
345400main = Cmd. run (term, termInfo)
346-
0 commit comments