1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.42 2001/02/12 11:38:49 sewardj 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 import PrelGHC ( unsafeCoerce# )
48 -----------------------------------------------------------------------------
52 \ / _ \\ /\\ /\\/ __(_)\n\
53 \ / /_\\// /_/ / / | | GHC Interactive, version " ++ cProjectVersion ++ ", For Haskell 98.\n\
54 \/ /_\\\\/ __ / /___| | http://www.haskell.org/ghc/\n\
55 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
57 GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
59 builtin_commands :: [(String, String -> GHCi Bool)]
61 ("add", keepGoing addModule),
62 ("cd", keepGoing changeDirectory),
63 ("def", keepGoing defineMacro),
64 ("help", keepGoing help),
65 ("?", keepGoing help),
66 ("load", keepGoing loadModule),
67 ("module", keepGoing setContext),
68 ("reload", keepGoing reloadModule),
69 ("set", keepGoing setOptions),
70 ("type", keepGoing typeOfExpr),
71 ("unset", keepGoing unsetOptions),
72 ("undef", keepGoing undefineMacro),
76 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
77 keepGoing a str = a str >> return False
79 shortHelpText = "use :? for help.\n"
82 \ Commands available from the prompt:\n\
84 \ <expr> evaluate <expr>\n\
85 \ :add <filename> add a module to the current set\n\
86 \ :cd <dir> change directory to <dir>\n\
87 \ :help, :? display this list of commands\n\
88 \ :load <filename> load a module (and it dependents)\n\
89 \ :module <mod> set the context for expression evaluation to <mod>\n\
90 \ :reload reload the current module set\n\
91 \ :set <option> ... set options\n\
92 \ :unset <option> ... unset options\n\
93 \ :type <expr> show the type of <expr>\n\
95 \ :!<command> run the shell command <command>\n\
97 \ Options for `:set' and `:unset':\n\
99 \ +s print timing/memory stats after each evaluation\n\
100 \ +t print type after evaluation\n\
101 \ +r revert top-level expressions after each evaluation\n\
102 \ -<flags> most GHC command line flags can also be set here\n\
103 \ (eg. -v2, -fglasgow-exts, etc.)\n\
106 interactiveUI :: CmState -> Maybe FilePath -> IO ()
107 interactiveUI cmstate mod = do
108 hPutStrLn stdout ghciWelcomeMsg
110 hSetBuffering stdout NoBuffering
112 -- link in the available packages
113 pkgs <- getPackageInfo
115 linkPackages (reverse pkgs)
117 (cmstate, ok, mods) <-
119 Nothing -> return (cmstate, True, [])
120 Just m -> cmLoadModule cmstate m
126 prel <- moduleNameToModule defaultCurrentModuleName
127 writeIORef defaultCurrentModule prel
129 dflags <- getDynFlags
131 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
132 "PrelHandle.hFlush PrelHandle.stdout"
135 Just (hv,_,_) -> writeIORef flush_stdout hv
137 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
138 "PrelHandle.hFlush PrelHandle.stdout"
141 Just (hv,_,_) -> writeIORef flush_stderr hv
143 let this_mod = case mods of
147 (unGHCi runGHCi) GHCiState{ modules = mods,
148 current_module = this_mod,
151 options = [ShowTiming],
159 dot_ghci <- io (IO.try (openFile "./.ghci" ReadMode))
162 Right hdl -> fileLoop hdl False
165 home <- io (IO.try (getEnv "HOME"))
169 dot_ghci <- io (IO.try (openFile (dir ++ "/.ghci") ReadMode))
172 Right hdl -> fileLoop hdl False
174 -- read commands from stdin
182 io $ do putStrLn "Leaving GHCi."
185 fileLoop :: Handle -> Bool -> GHCi ()
186 fileLoop hdl prompt = do
188 when prompt (io (hPutStr hdl (moduleUserString (current_module st) ++ "> ")))
189 l <- io (IO.try (hGetLine hdl))
191 Left e | isEOFError e -> return ()
192 | otherwise -> throw e
194 case remove_spaces l of
195 "" -> fileLoop hdl prompt
196 l -> do quit <- runCommand l
197 if quit then return () else fileLoop hdl prompt
199 stringLoop :: [String] -> GHCi ()
200 stringLoop [] = return ()
201 stringLoop (s:ss) = do
203 case remove_spaces s of
205 l -> do quit <- runCommand l
206 if quit then return () else stringLoop ss
209 readlineLoop :: GHCi ()
212 l <- io (readline (moduleUserString (current_module st) ++ "> "))
216 case remove_spaces l of
221 if quit then return () else readlineLoop
224 -- Top level exception handler, just prints out the exception
226 runCommand :: String -> GHCi Bool
228 ghciHandle ( \exception ->
231 case fromDynamic dyn of
232 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
235 PhaseFailed phase code ->
236 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
237 ++ show code ++ ")"))
238 Interrupted -> io (putStrLn "Interrupted.")
239 other -> io (putStrLn (show (ghc_ex :: GhcException)))
241 other -> io (putStrLn ("*** Exception: " ++ show exception))
248 doCommand (':' : command) = specialCommand command
249 doCommand ('-':'-':_) = return False -- comments, useful in scripts
251 = do expr_expanded <- expandExpr expr
252 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
253 expr_ok <- timeIt (do stuff <- evalExpr expr_expanded
254 finishEvalExpr stuff)
255 when expr_ok (rememberExpr expr_expanded)
258 -- possibly print the type and revert CAFs after evaluating an expression
259 finishEvalExpr Nothing = return False
260 finishEvalExpr (Just (unqual,ty))
261 = do b <- isOptionSet ShowType
262 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
263 b <- isOptionSet RevertCAFs
264 io (when b revertCAFs)
267 -- Returned Bool indicates whether or not the expr was successfully
268 -- parsed, renamed and typechecked.
269 evalExpr :: String -> GHCi (Maybe (PrintUnqualified,Type))
271 | null (filter (not.isSpace) expr)
274 = do st <- getGHCiState
275 dflags <- io (getDynFlags)
276 (new_cmstate, maybe_stuff) <-
277 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
278 setGHCiState st{cmstate = new_cmstate}
280 Nothing -> return Nothing
281 Just (hv, unqual, ty) -> do io (cmRunExpr hv)
283 return (Just (unqual,ty))
285 flushEverything :: GHCi ()
287 = io $ do flush_so <- readIORef flush_stdout
289 flush_se <- readIORef flush_stdout
292 specialCommand :: String -> GHCi Bool
293 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
294 specialCommand str = do
295 let (cmd,rest) = break isSpace str
296 cmds <- io (readIORef commands)
297 case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
298 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
299 ++ shortHelpText) >> return False)
300 [(_,f)] -> f (dropWhile isSpace rest)
301 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
302 " matches multiple commands (" ++
303 foldr1 (\a b -> a ++ ',':b) (map fst cs)
304 ++ ")") >> return False)
306 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
308 -----------------------------------------------------------------------------
311 help :: String -> GHCi ()
312 help _ = io (putStr helpText)
314 addModule :: String -> GHCi ()
315 addModule _ = throwDyn (OtherError ":add not implemented")
317 setContext :: String -> GHCi ()
319 = throwDyn (OtherError "syntax: `:m <module>'")
320 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
321 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
323 = do m <- io (moduleNameToModule (mkModuleName mn))
325 if (isHomeModule m && m `notElem` modules st)
326 then throwDyn (OtherError (showSDoc (quotes (ppr (moduleName m))
327 <+> text "is not currently loaded, use :load")))
328 else setGHCiState st{current_module = m}
330 moduleNameToModule :: ModuleName -> IO Module
331 moduleNameToModule mn
332 = do maybe_stuff <- findModule mn
334 Nothing -> throwDyn (OtherError ("can't find module `"
335 ++ moduleNameUserString mn ++ "'"))
336 Just (m,_) -> return m
338 changeDirectory :: String -> GHCi ()
339 changeDirectory d = io (setCurrentDirectory d)
341 defineMacro :: String -> GHCi ()
343 let (macro_name, definition) = break isSpace s
344 cmds <- io (readIORef commands)
346 then throwDyn (OtherError "invalid macro name")
348 if (macro_name `elem` map fst cmds)
349 then throwDyn (OtherError
350 ("command `" ++ macro_name ++ "' already defined"))
353 -- give the expression a type signature, so we can be sure we're getting
354 -- something of the right type.
355 let new_expr = '(' : definition ++ ") :: String -> IO String"
357 -- compile the expression
359 dflags <- io (getDynFlags)
360 (new_cmstate, maybe_stuff) <-
361 io (cmGetExpr (cmstate st) dflags False (current_module st) new_expr)
362 setGHCiState st{cmstate = new_cmstate}
365 Just (hv, unqual, ty)
366 -> io (writeIORef commands
367 ((macro_name, keepGoing (runMacro hv)) : cmds))
369 runMacro :: HValue{-String -> IO String-} -> String -> GHCi ()
371 str <- io ((unsafeCoerce# fun :: String -> IO String) s)
372 stringLoop (lines str)
374 undefineMacro :: String -> GHCi ()
375 undefineMacro macro_name = do
376 cmds <- io (readIORef commands)
377 if (macro_name `elem` map fst builtin_commands)
378 then throwDyn (OtherError
379 ("command `" ++ macro_name ++ "' cannot be undefined"))
381 if (macro_name `notElem` map fst cmds)
382 then throwDyn (OtherError
383 ("command `" ++ macro_name ++ "' not defined"))
385 io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
387 loadModule :: String -> GHCi ()
388 loadModule path = timeIt (loadModule' path)
390 loadModule' path = do
391 state <- getGHCiState
392 cmstate1 <- io (cmUnload (cmstate state))
393 io (revertCAFs) -- always revert CAFs on load.
394 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
396 def_mod <- io (readIORef defaultCurrentModule)
398 let new_state = state{
401 current_module = case mods of
406 setGHCiState new_state
409 | null mods = text "none."
411 punctuate comma (map (text.moduleUserString) mods)) <> text "."
414 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
416 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
418 reloadModule :: String -> GHCi ()
420 state <- getGHCiState
422 Nothing -> io (putStr "no current target\n")
424 -> do io (revertCAFs) -- always revert CAFs on reload.
425 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
426 def_mod <- io (readIORef defaultCurrentModule)
428 state{cmstate=new_cmstate,
430 current_module = case mods of
435 reloadModule _ = noArgs ":reload"
437 typeOfExpr :: String -> GHCi ()
439 = do st <- getGHCiState
440 dflags <- io (getDynFlags)
441 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
442 (current_module st) str)
443 setGHCiState st{cmstate = new_cmstate}
446 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
448 quit :: String -> GHCi Bool
451 shellEscape :: String -> GHCi Bool
452 shellEscape str = io (system str >> return False)
454 ----------------------------------------------------------------------------
457 -- set options in the interpreter. Syntax is exactly the same as the
458 -- ghc command line, except that certain options aren't available (-C,
461 -- This is pretty fragile: most options won't work as expected. ToDo:
462 -- figure out which ones & disallow them.
464 setOptions :: String -> GHCi ()
466 = do st <- getGHCiState
467 let opts = options st
468 io $ putStrLn (showSDoc (
469 text "options currently set: " <>
472 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
475 = do -- first, deal with the GHCi opts (+s, +t, etc.)
477 (minus_opts, rest1) = partition isMinus opts
478 (plus_opts, rest2) = partition isPlus rest1
480 if (not (null rest2))
481 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
484 mapM setOpt plus_opts
486 -- now, the GHC flags
487 io (do -- first, static flags
488 leftovers <- processArgs static_flags minus_opts []
490 -- then, dynamic flags
491 dyn_flags <- readIORef v_InitDynFlags
492 writeIORef v_DynFlags dyn_flags
493 leftovers <- processArgs dynamic_flags leftovers []
494 dyn_flags <- readIORef v_DynFlags
495 writeIORef v_InitDynFlags dyn_flags
497 if (not (null leftovers))
498 then throwDyn (OtherError ("unrecognised flags: " ++
503 unsetOptions :: String -> GHCi ()
505 = do -- first, deal with the GHCi opts (+s, +t, etc.)
507 (minus_opts, rest1) = partition isMinus opts
508 (plus_opts, rest2) = partition isPlus rest1
510 if (not (null rest2))
511 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
514 mapM unsetOpt plus_opts
516 -- can't do GHC flags for now
517 if (not (null minus_opts))
518 then throwDyn (OtherError "can't unset GHC command-line flags")
521 isMinus ('-':s) = True
524 isPlus ('+':s) = True
528 = case strToGHCiOpt str of
529 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
530 Just o -> setOption o
533 = case strToGHCiOpt str of
534 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
535 Just o -> unsetOption o
537 strToGHCiOpt :: String -> (Maybe GHCiOption)
538 strToGHCiOpt "s" = Just ShowTiming
539 strToGHCiOpt "t" = Just ShowType
540 strToGHCiOpt "r" = Just RevertCAFs
541 strToGHCiOpt _ = Nothing
543 optToStr :: GHCiOption -> String
544 optToStr ShowTiming = "s"
545 optToStr ShowType = "t"
546 optToStr RevertCAFs = "r"
548 -----------------------------------------------------------------------------
549 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
551 -- Take a string and replace $$s in it with the last expr, if any.
552 expandExpr :: String -> GHCi String
554 = do mle <- getLastExpr
555 return (outside mle str)
557 outside mle ('$':'$':cs)
559 Just le -> " (" ++ le ++ ") " ++ outside mle cs
560 Nothing -> outside mle cs
563 outside mle ('"':str) = '"' : inside2 mle str -- "
564 outside mle ('\'':str) = '\'' : inside1 mle str -- '
565 outside mle (c:cs) = c : outside mle cs
567 inside2 mle ('"':cs) = '"' : outside mle cs -- "
568 inside2 mle (c:cs) = c : inside2 mle cs
571 inside1 mle ('\'':cs) = '\'': outside mle cs
572 inside1 mle (c:cs) = c : inside1 mle cs
576 rememberExpr :: String -> GHCi ()
578 = do let cleaned = (clean . reverse . clean . reverse) str
579 let forget_me_not | null cleaned = Nothing
580 | otherwise = Just cleaned
581 setLastExpr forget_me_not
583 clean = dropWhile isSpace
586 -----------------------------------------------------------------------------
589 data GHCiState = GHCiState
592 current_module :: Module,
593 target :: Maybe FilePath,
595 options :: [GHCiOption],
596 last_expr :: Maybe String
600 = ShowTiming -- show time/allocs after evaluation
601 | ShowType -- show the type of expressions
602 | RevertCAFs -- revert CAFs after every evaluation
605 defaultCurrentModuleName = mkModuleName "Prelude"
606 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
608 GLOBAL_VAR(flush_stdout, error "no flush_stdout", HValue)
609 GLOBAL_VAR(flush_stderr, error "no flush_stdout", HValue)
611 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
613 instance Monad GHCi where
614 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
615 return a = GHCi $ \s -> return (s,a)
617 getGHCiState = GHCi $ \s -> return (s,s)
618 setGHCiState s = GHCi $ \_ -> return (s,())
620 isOptionSet :: GHCiOption -> GHCi Bool
622 = do st <- getGHCiState
623 return (opt `elem` options st)
625 setOption :: GHCiOption -> GHCi ()
627 = do st <- getGHCiState
628 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
630 unsetOption :: GHCiOption -> GHCi ()
632 = do st <- getGHCiState
633 setGHCiState (st{ options = filter (/= opt) (options st) })
635 getLastExpr :: GHCi (Maybe String)
637 = do st <- getGHCiState ; return (last_expr st)
639 setLastExpr :: Maybe String -> GHCi ()
640 setLastExpr last_expr
641 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
643 io m = GHCi $ \s -> m >>= \a -> return (s,a)
645 -----------------------------------------------------------------------------
646 -- recursive exception handlers
648 -- Don't forget to unblock async exceptions in the handler, or if we're
649 -- in an exception loop (eg. let a = error a in a) the ^C exception
650 -- may never be delivered. Thanks to Marcin for pointing out the bug.
652 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
653 ghciHandle h (GHCi m) = GHCi $ \s ->
654 Exception.catch (m s)
655 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
657 ghciUnblock :: GHCi a -> GHCi a
658 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
660 -----------------------------------------------------------------------------
663 linkPackages :: [Package] -> IO ()
664 linkPackages pkgs = mapM_ linkPackage pkgs
666 linkPackage :: Package -> IO ()
667 -- ignore rts and gmp for now (ToDo; better?)
668 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
670 putStr ("Loading package " ++ name pkg ++ " ... ")
671 let dirs = library_dirs pkg
672 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
673 mapM (linkOneObj dirs) objs
674 putStr "resolving ... "
678 linkOneObj dirs obj = do
679 filename <- findFile dirs obj
682 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
683 findFile (d:ds) obj = do
684 let path = d ++ '/':obj
685 b <- doesFileExist path
686 if b then return path else findFile ds obj
688 -----------------------------------------------------------------------------
689 -- timing & statistics
691 timeIt :: GHCi a -> GHCi a
693 = do b <- isOptionSet ShowTiming
696 else do allocs1 <- io $ getAllocations
697 time1 <- io $ getCPUTime
699 allocs2 <- io $ getAllocations
700 time2 <- io $ getCPUTime
701 io $ printTimes (allocs2 - allocs1) (time2 - time1)
704 foreign import "getAllocations" getAllocations :: IO Int
706 printTimes :: Int -> Integer -> IO ()
707 printTimes allocs psecs
708 = do let secs = (fromIntegral psecs / (10^12)) :: Float
709 secs_str = showFFloat (Just 2) secs
711 parens (text (secs_str "") <+> text "secs" <> comma <+>
712 int allocs <+> text "bytes")))
714 -----------------------------------------------------------------------------
717 foreign import revertCAFs :: IO () -- make it "safe", just in case