1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.37 2001/02/09 15:33:51 simonmar Exp $
4 -- GHC Interactive User Interface
6 -- (c) The GHC Team 2000
8 -----------------------------------------------------------------------------
10 module InteractiveUI (interactiveUI) where
12 #include "HsVersions.h"
26 import PprType {- instance Outputable Type; do not delete -}
27 import Panic ( GhcException(..) )
46 -----------------------------------------------------------------------------
50 \ / _ \\ /\\ /\\/ __(_) GHC Interactive, version 5.00, For Haskell 98. \n\
51 \ / /_\\// /_/ / / | | http://www.haskell.org/ghc \n\
52 \/ /_\\\\/ __ / /___| | Bug reports to: glasgow-haskell-bugs@haskell.org \n\
53 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
55 commands :: [(String, String -> GHCi Bool)]
57 ("add", keepGoing addModule),
58 ("cd", keepGoing changeDirectory),
59 ("help", keepGoing help),
60 ("?", keepGoing help),
61 ("load", keepGoing loadModule),
62 ("module", keepGoing setContext),
63 ("reload", keepGoing reloadModule),
64 ("set", keepGoing setOptions),
65 ("type", keepGoing typeOfExpr),
66 ("unset", keepGoing unsetOptions),
70 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
71 keepGoing a str = a str >> return False
73 shortHelpText = "use :? for help.\n"
76 \ Commands available from the prompt:\n\
78 \ <expr> evaluate <expr>\n\
79 \ :add <filename> add a module to the current set\n\
80 \ :cd <dir> change directory to <dir>\n\
81 \ :help, :? display this list of commands\n\
82 \ :load <filename> load a module (and it dependents)\n\
83 \ :module <mod> set the context for expression evaluation to <mod>\n\
84 \ :reload reload the current module set\n\
85 \ :set <option> ... set options\n\
86 \ :unset <option> ... unset options\n\
87 \ :type <expr> show the type of <expr>\n\
89 \ :!<command> run the shell command <command>\n\
91 \ Options for `:set' and `:unset':\n\
93 \ +s print timing/memory stats after each evaluation\n\
94 \ +t print type after evaluation\n\
95 \ +r revert top-level expressions after each evaluation\n\
96 \ -<flags> most GHC command line flags can also be set here\n\
97 \ (eg. -v2, -fglasgow-exts, etc.)\n\
100 interactiveUI :: CmState -> Maybe FilePath -> IO ()
101 interactiveUI cmstate mod = do
102 hPutStrLn stdout ghciWelcomeMsg
104 hSetBuffering stdout NoBuffering
106 -- link in the available packages
107 pkgs <- getPackageInfo
108 linkPackages (reverse pkgs)
110 (cmstate, ok, mods) <-
112 Nothing -> return (cmstate, True, [])
113 Just m -> cmLoadModule cmstate m
119 prel <- moduleNameToModule defaultCurrentModuleName
120 writeIORef defaultCurrentModule prel
122 dflags <- getDynFlags
124 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
125 "PrelHandle.hFlush PrelHandle.stdout"
128 Just (hv,_,_) -> writeIORef flush_stdout hv
130 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
131 "PrelHandle.hFlush PrelHandle.stdout"
134 Just (hv,_,_) -> writeIORef flush_stderr hv
136 let this_mod = case mods of
140 (unGHCi runGHCi) GHCiState{ modules = mods,
141 current_module = this_mod,
144 options = [ShowTiming],
152 dot_ghci <- io (IO.try (openFile "./.ghci" ReadMode))
155 Right hdl -> fileLoop hdl False
158 home <- io (IO.try (getEnv "HOME"))
162 dot_ghci <- io (IO.try (openFile (dir ++ "/.ghci") ReadMode))
165 Right hdl -> fileLoop hdl False
167 -- read commands from stdin
175 io $ do putStrLn "Leaving GHCi."
178 fileLoop :: Handle -> Bool -> GHCi ()
179 fileLoop hdl prompt = do
181 when prompt (io (hPutStr hdl (moduleUserString (current_module st) ++ "> ")))
182 l <- io (IO.try (hGetLine hdl))
184 Left e | isEOFError e -> return ()
185 | otherwise -> throw e
187 case remove_spaces l of
188 "" -> fileLoop hdl prompt
189 l -> do quit <- runCommand l
190 if quit then return () else fileLoop hdl prompt
193 readlineLoop :: GHCi ()
196 l <- io (readline (moduleUserString (current_module st) ++ "> "))
200 case remove_spaces l of
205 if quit then return () else readlineLoop
208 -- Top level exception handler, just prints out the exception
210 runCommand :: String -> GHCi Bool
212 ghciHandle ( \exception ->
215 case fromDynamic dyn of
216 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
219 PhaseFailed phase code ->
220 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
221 ++ show code ++ ")"))
222 Interrupted -> io (putStrLn "Interrupted.")
223 other -> io (putStrLn (show (ghc_ex :: GhcException)))
225 other -> io (putStrLn ("*** Exception: " ++ show exception))
232 doCommand (':' : command) = specialCommand command
233 doCommand ('-':'-':_) = return False -- comments, useful in scripts
235 = do expr_expanded <- expandExpr expr
236 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
237 expr_ok <- timeIt (do stuff <- evalExpr expr_expanded
238 finishEvalExpr stuff)
239 when expr_ok (rememberExpr expr_expanded)
242 -- possibly print the type and revert CAFs after evaluating an expression
243 finishEvalExpr Nothing = return False
244 finishEvalExpr (Just (unqual,ty))
245 = do b <- isOptionSet ShowType
246 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
247 b <- isOptionSet RevertCAFs
248 io (when b revertCAFs)
251 -- Returned Bool indicates whether or not the expr was successfully
252 -- parsed, renamed and typechecked.
253 evalExpr :: String -> GHCi (Maybe (PrintUnqualified,Type))
255 | null (filter (not.isSpace) expr)
258 = do st <- getGHCiState
259 dflags <- io (getDynFlags)
260 (new_cmstate, maybe_stuff) <-
261 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
262 setGHCiState st{cmstate = new_cmstate}
264 Nothing -> return Nothing
265 Just (hv, unqual, ty) -> do io (cmRunExpr hv)
267 return (Just (unqual,ty))
269 flushEverything :: GHCi ()
271 = io $ do flush_so <- readIORef flush_stdout
273 flush_se <- readIORef flush_stdout
276 specialCommand :: String -> GHCi Bool
277 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
278 specialCommand str = do
279 let (cmd,rest) = break isSpace str
280 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
281 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
282 ++ shortHelpText) >> return False)
283 [(_,f)] -> f (dropWhile isSpace rest)
284 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
285 " matches multiple commands (" ++
286 foldr1 (\a b -> a ++ ',':b) (map fst cs)
287 ++ ")") >> return False)
289 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
291 -----------------------------------------------------------------------------
294 help :: String -> GHCi ()
295 help _ = io (putStr helpText)
297 addModule :: String -> GHCi ()
298 addModule _ = throwDyn (OtherError ":add not implemented")
300 setContext :: String -> GHCi ()
302 = throwDyn (OtherError "syntax: `:m <module>'")
303 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
304 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
306 = do m <- io (moduleNameToModule (mkModuleName mn))
308 if (isHomeModule m && m `notElem` modules st)
309 then throwDyn (OtherError (showSDoc (quotes (ppr (moduleName m))
310 <+> text "is not currently loaded, use :load")))
311 else setGHCiState st{current_module = m}
313 moduleNameToModule :: ModuleName -> IO Module
314 moduleNameToModule mn
315 = do maybe_stuff <- findModule mn
317 Nothing -> throwDyn (OtherError ("can't find module `"
318 ++ moduleNameUserString mn ++ "'"))
319 Just (m,_) -> return m
321 changeDirectory :: String -> GHCi ()
322 changeDirectory d = io (setCurrentDirectory d)
324 loadModule :: String -> GHCi ()
325 loadModule path = timeIt (loadModule' path)
327 loadModule' path = do
328 state <- getGHCiState
329 cmstate1 <- io (cmUnload (cmstate state))
330 io (revertCAFs) -- always revert CAFs on load.
331 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
333 def_mod <- io (readIORef defaultCurrentModule)
335 let new_state = state{
338 current_module = case mods of
343 setGHCiState new_state
346 | null mods = text "none."
348 punctuate comma (map (text.moduleUserString) mods)) <> text "."
351 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
353 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
355 reloadModule :: String -> GHCi ()
357 state <- getGHCiState
359 Nothing -> io (putStr "no current target\n")
361 -> do io (revertCAFs) -- always revert CAFs on reload.
362 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
363 def_mod <- io (readIORef defaultCurrentModule)
365 state{cmstate=new_cmstate,
367 current_module = case mods of
372 reloadModule _ = noArgs ":reload"
374 typeOfExpr :: String -> GHCi ()
376 = do st <- getGHCiState
377 dflags <- io (getDynFlags)
378 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
379 (current_module st) str)
380 setGHCiState st{cmstate = new_cmstate}
383 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
385 quit :: String -> GHCi Bool
388 shellEscape :: String -> GHCi Bool
389 shellEscape str = io (system str >> return False)
391 ----------------------------------------------------------------------------
394 -- set options in the interpreter. Syntax is exactly the same as the
395 -- ghc command line, except that certain options aren't available (-C,
398 -- This is pretty fragile: most options won't work as expected. ToDo:
399 -- figure out which ones & disallow them.
401 setOptions :: String -> GHCi ()
403 = do st <- getGHCiState
404 let opts = options st
405 io $ putStrLn (showSDoc (
406 text "options currently set: " <>
409 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
412 = do -- first, deal with the GHCi opts (+s, +t, etc.)
414 (minus_opts, rest1) = partition isMinus opts
415 (plus_opts, rest2) = partition isPlus rest1
417 if (not (null rest2))
418 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
421 mapM setOpt plus_opts
423 -- now, the GHC flags
424 io (do -- first, static flags
425 leftovers <- processArgs static_flags minus_opts []
427 -- then, dynamic flags
428 dyn_flags <- readIORef v_InitDynFlags
429 writeIORef v_DynFlags dyn_flags
430 leftovers <- processArgs dynamic_flags leftovers []
431 dyn_flags <- readIORef v_DynFlags
432 writeIORef v_InitDynFlags dyn_flags
434 if (not (null leftovers))
435 then throwDyn (OtherError ("unrecognised flags: " ++
440 unsetOptions :: String -> GHCi ()
442 = do -- first, deal with the GHCi opts (+s, +t, etc.)
444 (minus_opts, rest1) = partition isMinus opts
445 (plus_opts, rest2) = partition isPlus rest1
447 if (not (null rest2))
448 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
451 mapM unsetOpt plus_opts
453 -- can't do GHC flags for now
454 if (not (null minus_opts))
455 then throwDyn (OtherError "can't unset GHC command-line flags")
458 isMinus ('-':s) = True
461 isPlus ('+':s) = True
465 = case strToGHCiOpt str of
466 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
467 Just o -> setOption o
470 = case strToGHCiOpt str of
471 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
472 Just o -> unsetOption o
474 strToGHCiOpt :: String -> (Maybe GHCiOption)
475 strToGHCiOpt "s" = Just ShowTiming
476 strToGHCiOpt "t" = Just ShowType
477 strToGHCiOpt "r" = Just RevertCAFs
478 strToGHCiOpt _ = Nothing
480 optToStr :: GHCiOption -> String
481 optToStr ShowTiming = "s"
482 optToStr ShowType = "t"
483 optToStr RevertCAFs = "r"
485 -----------------------------------------------------------------------------
486 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
488 -- Take a string and replace $$s in it with the last expr, if any.
489 expandExpr :: String -> GHCi String
491 = do mle <- getLastExpr
492 return (outside mle str)
494 outside mle ('$':'$':cs)
496 Just le -> " (" ++ le ++ ") " ++ outside mle cs
497 Nothing -> outside mle cs
500 outside mle ('"':str) = '"' : inside2 mle str -- "
501 outside mle ('\'':str) = '\'' : inside1 mle str -- '
502 outside mle (c:cs) = c : outside mle cs
504 inside2 mle ('"':cs) = '"' : outside mle cs -- "
505 inside2 mle (c:cs) = c : inside2 mle cs
508 inside1 mle ('\'':cs) = '\'': outside mle cs
509 inside1 mle (c:cs) = c : inside1 mle cs
513 rememberExpr :: String -> GHCi ()
515 = do let cleaned = (clean . reverse . clean . reverse) str
516 let forget_me_not | null cleaned = Nothing
517 | otherwise = Just cleaned
518 setLastExpr forget_me_not
520 clean = dropWhile isSpace
523 -----------------------------------------------------------------------------
526 data GHCiState = GHCiState
529 current_module :: Module,
530 target :: Maybe FilePath,
532 options :: [GHCiOption],
533 last_expr :: Maybe String
537 = ShowTiming -- show time/allocs after evaluation
538 | ShowType -- show the type of expressions
539 | RevertCAFs -- revert CAFs after every evaluation
542 defaultCurrentModuleName = mkModuleName "Prelude"
543 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
545 GLOBAL_VAR(flush_stdout, error "no flush_stdout", HValue)
546 GLOBAL_VAR(flush_stderr, error "no flush_stdout", HValue)
548 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
550 instance Monad GHCi where
551 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
552 return a = GHCi $ \s -> return (s,a)
554 getGHCiState = GHCi $ \s -> return (s,s)
555 setGHCiState s = GHCi $ \_ -> return (s,())
557 isOptionSet :: GHCiOption -> GHCi Bool
559 = do st <- getGHCiState
560 return (opt `elem` options st)
562 setOption :: GHCiOption -> GHCi ()
564 = do st <- getGHCiState
565 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
567 unsetOption :: GHCiOption -> GHCi ()
569 = do st <- getGHCiState
570 setGHCiState (st{ options = filter (/= opt) (options st) })
572 getLastExpr :: GHCi (Maybe String)
574 = do st <- getGHCiState ; return (last_expr st)
576 setLastExpr :: Maybe String -> GHCi ()
577 setLastExpr last_expr
578 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
580 io m = GHCi $ \s -> m >>= \a -> return (s,a)
582 -----------------------------------------------------------------------------
583 -- recursive exception handlers
585 -- Don't forget to unblock async exceptions in the handler, or if we're
586 -- in an exception loop (eg. let a = error a in a) the ^C exception
587 -- may never be delivered. Thanks to Marcin for pointing out the bug.
589 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
590 ghciHandle h (GHCi m) = GHCi $ \s ->
591 Exception.catch (m s)
592 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
594 ghciUnblock :: GHCi a -> GHCi a
595 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
597 -----------------------------------------------------------------------------
600 linkPackages :: [Package] -> IO ()
601 linkPackages pkgs = mapM_ linkPackage pkgs
603 linkPackage :: Package -> IO ()
604 -- ignore rts and gmp for now (ToDo; better?)
605 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
607 putStr ("Loading package " ++ name pkg ++ " ... ")
608 let dirs = library_dirs pkg
609 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
610 mapM (linkOneObj dirs) objs
611 putStr "resolving ... "
615 linkOneObj dirs obj = do
616 filename <- findFile dirs obj
619 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
620 findFile (d:ds) obj = do
621 let path = d ++ '/':obj
622 b <- doesFileExist path
623 if b then return path else findFile ds obj
625 -----------------------------------------------------------------------------
626 -- timing & statistics
628 timeIt :: GHCi a -> GHCi a
630 = do b <- isOptionSet ShowTiming
633 else do allocs1 <- io $ getAllocations
634 time1 <- io $ getCPUTime
636 allocs2 <- io $ getAllocations
637 time2 <- io $ getCPUTime
638 io $ printTimes (allocs2 - allocs1) (time2 - time1)
641 foreign import "getAllocations" getAllocations :: IO Int
643 printTimes :: Int -> Integer -> IO ()
644 printTimes allocs psecs
645 = do let secs = (fromIntegral psecs / (10^12)) :: Float
646 secs_str = showFFloat (Just 2) secs
648 parens (text (secs_str "") <+> text "secs" <> comma <+>
649 int allocs <+> text "bytes")))
651 -----------------------------------------------------------------------------
654 foreign import revertCAFs :: IO () -- make it "safe", just in case