1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.38 2001/02/09 15:39:39 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(..) )
47 -----------------------------------------------------------------------------
51 \ / _ \\ /\\ /\\/ __(_) GHC Interactive, version " ++ cProjectVersion ++ ", For Haskell 98. \n\
52 \ / /_\\// /_/ / / | | http://www.haskell.org/ghc \n\
53 \/ /_\\\\/ __ / /___| | Bug reports to: glasgow-haskell-bugs@haskell.org \n\
54 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
56 commands :: [(String, String -> GHCi Bool)]
58 ("add", keepGoing addModule),
59 ("cd", keepGoing changeDirectory),
60 ("help", keepGoing help),
61 ("?", keepGoing help),
62 ("load", keepGoing loadModule),
63 ("module", keepGoing setContext),
64 ("reload", keepGoing reloadModule),
65 ("set", keepGoing setOptions),
66 ("type", keepGoing typeOfExpr),
67 ("unset", keepGoing unsetOptions),
71 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
72 keepGoing a str = a str >> return False
74 shortHelpText = "use :? for help.\n"
77 \ Commands available from the prompt:\n\
79 \ <expr> evaluate <expr>\n\
80 \ :add <filename> add a module to the current set\n\
81 \ :cd <dir> change directory to <dir>\n\
82 \ :help, :? display this list of commands\n\
83 \ :load <filename> load a module (and it dependents)\n\
84 \ :module <mod> set the context for expression evaluation to <mod>\n\
85 \ :reload reload the current module set\n\
86 \ :set <option> ... set options\n\
87 \ :unset <option> ... unset options\n\
88 \ :type <expr> show the type of <expr>\n\
90 \ :!<command> run the shell command <command>\n\
92 \ Options for `:set' and `:unset':\n\
94 \ +s print timing/memory stats after each evaluation\n\
95 \ +t print type after evaluation\n\
96 \ +r revert top-level expressions after each evaluation\n\
97 \ -<flags> most GHC command line flags can also be set here\n\
98 \ (eg. -v2, -fglasgow-exts, etc.)\n\
101 interactiveUI :: CmState -> Maybe FilePath -> IO ()
102 interactiveUI cmstate mod = do
103 hPutStrLn stdout ghciWelcomeMsg
105 hSetBuffering stdout NoBuffering
107 -- link in the available packages
108 pkgs <- getPackageInfo
109 linkPackages (reverse pkgs)
111 (cmstate, ok, mods) <-
113 Nothing -> return (cmstate, True, [])
114 Just m -> cmLoadModule cmstate m
120 prel <- moduleNameToModule defaultCurrentModuleName
121 writeIORef defaultCurrentModule prel
123 dflags <- getDynFlags
125 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
126 "PrelHandle.hFlush PrelHandle.stdout"
129 Just (hv,_,_) -> writeIORef flush_stdout hv
131 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
132 "PrelHandle.hFlush PrelHandle.stdout"
135 Just (hv,_,_) -> writeIORef flush_stderr hv
137 let this_mod = case mods of
141 (unGHCi runGHCi) GHCiState{ modules = mods,
142 current_module = this_mod,
145 options = [ShowTiming],
153 dot_ghci <- io (IO.try (openFile "./.ghci" ReadMode))
156 Right hdl -> fileLoop hdl False
159 home <- io (IO.try (getEnv "HOME"))
163 dot_ghci <- io (IO.try (openFile (dir ++ "/.ghci") ReadMode))
166 Right hdl -> fileLoop hdl False
168 -- read commands from stdin
176 io $ do putStrLn "Leaving GHCi."
179 fileLoop :: Handle -> Bool -> GHCi ()
180 fileLoop hdl prompt = do
182 when prompt (io (hPutStr hdl (moduleUserString (current_module st) ++ "> ")))
183 l <- io (IO.try (hGetLine hdl))
185 Left e | isEOFError e -> return ()
186 | otherwise -> throw e
188 case remove_spaces l of
189 "" -> fileLoop hdl prompt
190 l -> do quit <- runCommand l
191 if quit then return () else fileLoop hdl prompt
194 readlineLoop :: GHCi ()
197 l <- io (readline (moduleUserString (current_module st) ++ "> "))
201 case remove_spaces l of
206 if quit then return () else readlineLoop
209 -- Top level exception handler, just prints out the exception
211 runCommand :: String -> GHCi Bool
213 ghciHandle ( \exception ->
216 case fromDynamic dyn of
217 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
220 PhaseFailed phase code ->
221 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
222 ++ show code ++ ")"))
223 Interrupted -> io (putStrLn "Interrupted.")
224 other -> io (putStrLn (show (ghc_ex :: GhcException)))
226 other -> io (putStrLn ("*** Exception: " ++ show exception))
233 doCommand (':' : command) = specialCommand command
234 doCommand ('-':'-':_) = return False -- comments, useful in scripts
236 = do expr_expanded <- expandExpr expr
237 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
238 expr_ok <- timeIt (do stuff <- evalExpr expr_expanded
239 finishEvalExpr stuff)
240 when expr_ok (rememberExpr expr_expanded)
243 -- possibly print the type and revert CAFs after evaluating an expression
244 finishEvalExpr Nothing = return False
245 finishEvalExpr (Just (unqual,ty))
246 = do b <- isOptionSet ShowType
247 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
248 b <- isOptionSet RevertCAFs
249 io (when b revertCAFs)
252 -- Returned Bool indicates whether or not the expr was successfully
253 -- parsed, renamed and typechecked.
254 evalExpr :: String -> GHCi (Maybe (PrintUnqualified,Type))
256 | null (filter (not.isSpace) expr)
259 = do st <- getGHCiState
260 dflags <- io (getDynFlags)
261 (new_cmstate, maybe_stuff) <-
262 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
263 setGHCiState st{cmstate = new_cmstate}
265 Nothing -> return Nothing
266 Just (hv, unqual, ty) -> do io (cmRunExpr hv)
268 return (Just (unqual,ty))
270 flushEverything :: GHCi ()
272 = io $ do flush_so <- readIORef flush_stdout
274 flush_se <- readIORef flush_stdout
277 specialCommand :: String -> GHCi Bool
278 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
279 specialCommand str = do
280 let (cmd,rest) = break isSpace str
281 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
282 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
283 ++ shortHelpText) >> return False)
284 [(_,f)] -> f (dropWhile isSpace rest)
285 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
286 " matches multiple commands (" ++
287 foldr1 (\a b -> a ++ ',':b) (map fst cs)
288 ++ ")") >> return False)
290 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
292 -----------------------------------------------------------------------------
295 help :: String -> GHCi ()
296 help _ = io (putStr helpText)
298 addModule :: String -> GHCi ()
299 addModule _ = throwDyn (OtherError ":add not implemented")
301 setContext :: String -> GHCi ()
303 = throwDyn (OtherError "syntax: `:m <module>'")
304 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
305 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
307 = do m <- io (moduleNameToModule (mkModuleName mn))
309 if (isHomeModule m && m `notElem` modules st)
310 then throwDyn (OtherError (showSDoc (quotes (ppr (moduleName m))
311 <+> text "is not currently loaded, use :load")))
312 else setGHCiState st{current_module = m}
314 moduleNameToModule :: ModuleName -> IO Module
315 moduleNameToModule mn
316 = do maybe_stuff <- findModule mn
318 Nothing -> throwDyn (OtherError ("can't find module `"
319 ++ moduleNameUserString mn ++ "'"))
320 Just (m,_) -> return m
322 changeDirectory :: String -> GHCi ()
323 changeDirectory d = io (setCurrentDirectory d)
325 loadModule :: String -> GHCi ()
326 loadModule path = timeIt (loadModule' path)
328 loadModule' path = do
329 state <- getGHCiState
330 cmstate1 <- io (cmUnload (cmstate state))
331 io (revertCAFs) -- always revert CAFs on load.
332 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
334 def_mod <- io (readIORef defaultCurrentModule)
336 let new_state = state{
339 current_module = case mods of
344 setGHCiState new_state
347 | null mods = text "none."
349 punctuate comma (map (text.moduleUserString) mods)) <> text "."
352 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
354 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
356 reloadModule :: String -> GHCi ()
358 state <- getGHCiState
360 Nothing -> io (putStr "no current target\n")
362 -> do io (revertCAFs) -- always revert CAFs on reload.
363 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
364 def_mod <- io (readIORef defaultCurrentModule)
366 state{cmstate=new_cmstate,
368 current_module = case mods of
373 reloadModule _ = noArgs ":reload"
375 typeOfExpr :: String -> GHCi ()
377 = do st <- getGHCiState
378 dflags <- io (getDynFlags)
379 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
380 (current_module st) str)
381 setGHCiState st{cmstate = new_cmstate}
384 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
386 quit :: String -> GHCi Bool
389 shellEscape :: String -> GHCi Bool
390 shellEscape str = io (system str >> return False)
392 ----------------------------------------------------------------------------
395 -- set options in the interpreter. Syntax is exactly the same as the
396 -- ghc command line, except that certain options aren't available (-C,
399 -- This is pretty fragile: most options won't work as expected. ToDo:
400 -- figure out which ones & disallow them.
402 setOptions :: String -> GHCi ()
404 = do st <- getGHCiState
405 let opts = options st
406 io $ putStrLn (showSDoc (
407 text "options currently set: " <>
410 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
413 = do -- first, deal with the GHCi opts (+s, +t, etc.)
415 (minus_opts, rest1) = partition isMinus opts
416 (plus_opts, rest2) = partition isPlus rest1
418 if (not (null rest2))
419 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
422 mapM setOpt plus_opts
424 -- now, the GHC flags
425 io (do -- first, static flags
426 leftovers <- processArgs static_flags minus_opts []
428 -- then, dynamic flags
429 dyn_flags <- readIORef v_InitDynFlags
430 writeIORef v_DynFlags dyn_flags
431 leftovers <- processArgs dynamic_flags leftovers []
432 dyn_flags <- readIORef v_DynFlags
433 writeIORef v_InitDynFlags dyn_flags
435 if (not (null leftovers))
436 then throwDyn (OtherError ("unrecognised flags: " ++
441 unsetOptions :: String -> GHCi ()
443 = do -- first, deal with the GHCi opts (+s, +t, etc.)
445 (minus_opts, rest1) = partition isMinus opts
446 (plus_opts, rest2) = partition isPlus rest1
448 if (not (null rest2))
449 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
452 mapM unsetOpt plus_opts
454 -- can't do GHC flags for now
455 if (not (null minus_opts))
456 then throwDyn (OtherError "can't unset GHC command-line flags")
459 isMinus ('-':s) = True
462 isPlus ('+':s) = True
466 = case strToGHCiOpt str of
467 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
468 Just o -> setOption o
471 = case strToGHCiOpt str of
472 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
473 Just o -> unsetOption o
475 strToGHCiOpt :: String -> (Maybe GHCiOption)
476 strToGHCiOpt "s" = Just ShowTiming
477 strToGHCiOpt "t" = Just ShowType
478 strToGHCiOpt "r" = Just RevertCAFs
479 strToGHCiOpt _ = Nothing
481 optToStr :: GHCiOption -> String
482 optToStr ShowTiming = "s"
483 optToStr ShowType = "t"
484 optToStr RevertCAFs = "r"
486 -----------------------------------------------------------------------------
487 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
489 -- Take a string and replace $$s in it with the last expr, if any.
490 expandExpr :: String -> GHCi String
492 = do mle <- getLastExpr
493 return (outside mle str)
495 outside mle ('$':'$':cs)
497 Just le -> " (" ++ le ++ ") " ++ outside mle cs
498 Nothing -> outside mle cs
501 outside mle ('"':str) = '"' : inside2 mle str -- "
502 outside mle ('\'':str) = '\'' : inside1 mle str -- '
503 outside mle (c:cs) = c : outside mle cs
505 inside2 mle ('"':cs) = '"' : outside mle cs -- "
506 inside2 mle (c:cs) = c : inside2 mle cs
509 inside1 mle ('\'':cs) = '\'': outside mle cs
510 inside1 mle (c:cs) = c : inside1 mle cs
514 rememberExpr :: String -> GHCi ()
516 = do let cleaned = (clean . reverse . clean . reverse) str
517 let forget_me_not | null cleaned = Nothing
518 | otherwise = Just cleaned
519 setLastExpr forget_me_not
521 clean = dropWhile isSpace
524 -----------------------------------------------------------------------------
527 data GHCiState = GHCiState
530 current_module :: Module,
531 target :: Maybe FilePath,
533 options :: [GHCiOption],
534 last_expr :: Maybe String
538 = ShowTiming -- show time/allocs after evaluation
539 | ShowType -- show the type of expressions
540 | RevertCAFs -- revert CAFs after every evaluation
543 defaultCurrentModuleName = mkModuleName "Prelude"
544 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
546 GLOBAL_VAR(flush_stdout, error "no flush_stdout", HValue)
547 GLOBAL_VAR(flush_stderr, error "no flush_stdout", HValue)
549 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
551 instance Monad GHCi where
552 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
553 return a = GHCi $ \s -> return (s,a)
555 getGHCiState = GHCi $ \s -> return (s,s)
556 setGHCiState s = GHCi $ \_ -> return (s,())
558 isOptionSet :: GHCiOption -> GHCi Bool
560 = do st <- getGHCiState
561 return (opt `elem` options st)
563 setOption :: GHCiOption -> GHCi ()
565 = do st <- getGHCiState
566 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
568 unsetOption :: GHCiOption -> GHCi ()
570 = do st <- getGHCiState
571 setGHCiState (st{ options = filter (/= opt) (options st) })
573 getLastExpr :: GHCi (Maybe String)
575 = do st <- getGHCiState ; return (last_expr st)
577 setLastExpr :: Maybe String -> GHCi ()
578 setLastExpr last_expr
579 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
581 io m = GHCi $ \s -> m >>= \a -> return (s,a)
583 -----------------------------------------------------------------------------
584 -- recursive exception handlers
586 -- Don't forget to unblock async exceptions in the handler, or if we're
587 -- in an exception loop (eg. let a = error a in a) the ^C exception
588 -- may never be delivered. Thanks to Marcin for pointing out the bug.
590 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
591 ghciHandle h (GHCi m) = GHCi $ \s ->
592 Exception.catch (m s)
593 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
595 ghciUnblock :: GHCi a -> GHCi a
596 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
598 -----------------------------------------------------------------------------
601 linkPackages :: [Package] -> IO ()
602 linkPackages pkgs = mapM_ linkPackage pkgs
604 linkPackage :: Package -> IO ()
605 -- ignore rts and gmp for now (ToDo; better?)
606 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
608 putStr ("Loading package " ++ name pkg ++ " ... ")
609 let dirs = library_dirs pkg
610 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
611 mapM (linkOneObj dirs) objs
612 putStr "resolving ... "
616 linkOneObj dirs obj = do
617 filename <- findFile dirs obj
620 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
621 findFile (d:ds) obj = do
622 let path = d ++ '/':obj
623 b <- doesFileExist path
624 if b then return path else findFile ds obj
626 -----------------------------------------------------------------------------
627 -- timing & statistics
629 timeIt :: GHCi a -> GHCi a
631 = do b <- isOptionSet ShowTiming
634 else do allocs1 <- io $ getAllocations
635 time1 <- io $ getCPUTime
637 allocs2 <- io $ getAllocations
638 time2 <- io $ getCPUTime
639 io $ printTimes (allocs2 - allocs1) (time2 - time1)
642 foreign import "getAllocations" getAllocations :: IO Int
644 printTimes :: Int -> Integer -> IO ()
645 printTimes allocs psecs
646 = do let secs = (fromIntegral psecs / (10^12)) :: Float
647 secs_str = showFFloat (Just 2) secs
649 parens (text (secs_str "") <+> text "secs" <> comma <+>
650 int allocs <+> text "bytes")))
652 -----------------------------------------------------------------------------
655 foreign import revertCAFs :: IO () -- make it "safe", just in case