1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.47 2001/02/13 17:13:39 sewardj Exp $
4 -- GHC Interactive User Interface
6 -- (c) The GHC Team 2000
8 -----------------------------------------------------------------------------
10 {-# OPTIONS -#include "Linker.h" #-}
11 module InteractiveUI ( interactiveUI, ghciWelcomeMsg ) where
13 #include "HsVersions.h"
27 import PprType {- instance Outputable Type; do not delete -}
28 import Panic ( GhcException(..) )
47 import PrelGHC ( unsafeCoerce# )
48 import PrelPack ( packString )
50 import Foreign ( Ptr, nullPtr )
52 -----------------------------------------------------------------------------
56 \ / _ \\ /\\ /\\/ __(_)\n\
57 \ / /_\\// /_/ / / | | GHC Interactive, version " ++ cProjectVersion ++ ", For Haskell 98.\n\
58 \/ /_\\\\/ __ / /___| | http://www.haskell.org/ghc/\n\
59 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
61 GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
63 builtin_commands :: [(String, String -> GHCi Bool)]
65 ("add", keepGoing addModule),
66 ("cd", keepGoing changeDirectory),
67 ("def", keepGoing defineMacro),
68 ("help", keepGoing help),
69 ("?", keepGoing help),
70 ("load", keepGoing loadModule),
71 ("module", keepGoing setContext),
72 ("reload", keepGoing reloadModule),
73 ("set", keepGoing setOptions),
74 ("type", keepGoing typeOfExpr),
75 ("unset", keepGoing unsetOptions),
76 ("undef", keepGoing undefineMacro),
80 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
81 keepGoing a str = a str >> return False
83 shortHelpText = "use :? for help.\n"
86 \ Commands available from the prompt:\n\
88 \ <expr> evaluate <expr>\n\
89 \ :add <filename> add a module to the current set\n\
90 \ :cd <dir> change directory to <dir>\n\
91 \ :help, :? display this list of commands\n\
92 \ :load <filename> load a module (and it dependents)\n\
93 \ :module <mod> set the context for expression evaluation to <mod>\n\
94 \ :reload reload the current module set\n\
95 \ :set <option> ... set options\n\
96 \ :unset <option> ... unset options\n\
97 \ :type <expr> show the type of <expr>\n\
99 \ :!<command> run the shell command <command>\n\
101 \ Options for `:set' and `:unset':\n\
103 \ +s print timing/memory stats after each evaluation\n\
104 \ +t print type after evaluation\n\
105 \ +r revert top-level expressions after each evaluation\n\
106 \ -<flags> most GHC command line flags can also be set here\n\
107 \ (eg. -v2, -fglasgow-exts, etc.)\n\
110 interactiveUI :: CmState -> Maybe FilePath -> [String] -> IO ()
111 interactiveUI cmstate mod cmdline_libs = do
113 hSetBuffering stdout NoBuffering
115 -- link in the available packages
116 pkgs <- getPackageInfo
118 linkPackages cmdline_libs (reverse pkgs)
120 (cmstate, ok, mods) <-
122 Nothing -> return (cmstate, True, [])
123 Just m -> cmLoadModule cmstate m
129 prel <- moduleNameToModule defaultCurrentModuleName
130 writeIORef defaultCurrentModule prel
132 dflags <- getDynFlags
134 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
135 "PrelHandle.hFlush PrelHandle.stdout"
138 Just (hv,_,_) -> writeIORef flush_stdout hv
140 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
141 "PrelHandle.hFlush PrelHandle.stdout"
144 Just (hv,_,_) -> writeIORef flush_stderr hv
146 let this_mod = case mods of
150 (unGHCi runGHCi) GHCiState{ modules = mods,
151 current_module = this_mod,
154 options = [ShowTiming],
162 dot_ghci <- io (IO.try (openFile "./.ghci" ReadMode))
165 Right hdl -> fileLoop hdl False
168 home <- io (IO.try (getEnv "HOME"))
172 dot_ghci <- io (IO.try (openFile (dir ++ "/.ghci") ReadMode))
175 Right hdl -> fileLoop hdl False
177 -- read commands from stdin
185 io $ do putStrLn "Leaving GHCi."
188 fileLoop :: Handle -> Bool -> GHCi ()
189 fileLoop hdl prompt = do
191 when prompt (io (hPutStr hdl (moduleUserString (current_module st) ++ "> ")))
192 l <- io (IO.try (hGetLine hdl))
194 Left e | isEOFError e -> return ()
195 | otherwise -> throw e
197 case remove_spaces l of
198 "" -> fileLoop hdl prompt
199 l -> do quit <- runCommand l
200 if quit then return () else fileLoop hdl prompt
202 stringLoop :: [String] -> GHCi ()
203 stringLoop [] = return ()
204 stringLoop (s:ss) = do
206 case remove_spaces s of
208 l -> do quit <- runCommand l
209 if quit then return () else stringLoop ss
212 readlineLoop :: GHCi ()
215 l <- io (readline (moduleUserString (current_module st) ++ "> "))
219 case remove_spaces l of
224 if quit then return () else readlineLoop
227 -- Top level exception handler, just prints out the exception
229 runCommand :: String -> GHCi Bool
231 ghciHandle ( \exception ->
234 case fromDynamic dyn of
235 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
238 PhaseFailed phase code ->
239 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
240 ++ show code ++ ")"))
241 Interrupted -> io (putStrLn "Interrupted.")
242 other -> io (putStrLn (show (ghc_ex :: GhcException)))
244 other -> io (putStrLn ("*** Exception: " ++ show exception))
251 doCommand (':' : command) = specialCommand command
252 doCommand ('-':'-':_) = return False -- comments, useful in scripts
254 = do expr_expanded <- expandExpr expr
255 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
256 expr_ok <- timeIt (do stuff <- evalExpr expr_expanded
257 finishEvalExpr stuff)
258 when expr_ok (rememberExpr expr_expanded)
261 -- possibly print the type and revert CAFs after evaluating an expression
262 finishEvalExpr Nothing = return False
263 finishEvalExpr (Just (unqual,ty))
264 = do b <- isOptionSet ShowType
265 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
266 b <- isOptionSet RevertCAFs
267 io (when b revertCAFs)
270 -- Returned Maybe indicates whether or not the expr was successfully
271 -- parsed, renamed and typechecked.
272 evalExpr :: String -> GHCi (Maybe (PrintUnqualified,Type))
274 | null (filter (not.isSpace) expr)
277 = do st <- getGHCiState
278 dflags <- io (getDynFlags)
279 (new_cmstate, maybe_stuff) <-
280 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
281 setGHCiState st{cmstate = new_cmstate}
283 Nothing -> return Nothing
284 Just (hv, unqual, ty) -> do io (cmRunExpr hv)
286 return (Just (unqual,ty))
288 flushEverything :: GHCi ()
290 = io $ do flush_so <- readIORef flush_stdout
292 flush_se <- readIORef flush_stdout
295 specialCommand :: String -> GHCi Bool
296 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
297 specialCommand str = do
298 let (cmd,rest) = break isSpace str
299 cmds <- io (readIORef commands)
300 case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
301 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
302 ++ shortHelpText) >> return False)
303 [(_,f)] -> f (dropWhile isSpace rest)
304 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
305 " matches multiple commands (" ++
306 foldr1 (\a b -> a ++ ',':b) (map fst cs)
307 ++ ")") >> return False)
309 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
311 -----------------------------------------------------------------------------
314 help :: String -> GHCi ()
315 help _ = io (putStr helpText)
317 addModule :: String -> GHCi ()
318 addModule _ = throwDyn (OtherError ":add not implemented")
320 setContext :: String -> GHCi ()
322 = throwDyn (OtherError "syntax: `:m <module>'")
323 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
324 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
326 = do m <- io (moduleNameToModule (mkModuleName mn))
328 if (isHomeModule m && m `notElem` modules st)
329 then throwDyn (OtherError (showSDoc (quotes (ppr (moduleName m))
330 <+> text "is not currently loaded, use :load")))
331 else setGHCiState st{current_module = m}
333 moduleNameToModule :: ModuleName -> IO Module
334 moduleNameToModule mn
335 = do maybe_stuff <- findModule mn
337 Nothing -> throwDyn (OtherError ("can't find module `"
338 ++ moduleNameUserString mn ++ "'"))
339 Just (m,_) -> return m
341 changeDirectory :: String -> GHCi ()
342 changeDirectory d = io (setCurrentDirectory d)
344 defineMacro :: String -> GHCi ()
346 let (macro_name, definition) = break isSpace s
347 cmds <- io (readIORef commands)
349 then throwDyn (OtherError "invalid macro name")
351 if (macro_name `elem` map fst cmds)
352 then throwDyn (OtherError
353 ("command `" ++ macro_name ++ "' already defined"))
356 -- give the expression a type signature, so we can be sure we're getting
357 -- something of the right type.
358 let new_expr = '(' : definition ++ ") :: String -> IO String"
360 -- compile the expression
362 dflags <- io (getDynFlags)
363 (new_cmstate, maybe_stuff) <-
364 io (cmGetExpr (cmstate st) dflags False (current_module st) new_expr)
365 setGHCiState st{cmstate = new_cmstate}
368 Just (hv, unqual, ty)
369 -> io (writeIORef commands
370 ((macro_name, keepGoing (runMacro hv)) : cmds))
372 runMacro :: HValue{-String -> IO String-} -> String -> GHCi ()
374 str <- io ((unsafeCoerce# fun :: String -> IO String) s)
375 stringLoop (lines str)
377 undefineMacro :: String -> GHCi ()
378 undefineMacro macro_name = do
379 cmds <- io (readIORef commands)
380 if (macro_name `elem` map fst builtin_commands)
381 then throwDyn (OtherError
382 ("command `" ++ macro_name ++ "' cannot be undefined"))
384 if (macro_name `notElem` map fst cmds)
385 then throwDyn (OtherError
386 ("command `" ++ macro_name ++ "' not defined"))
388 io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
390 loadModule :: String -> GHCi ()
391 loadModule path = timeIt (loadModule' path)
393 loadModule' path = do
394 state <- getGHCiState
395 cmstate1 <- io (cmUnload (cmstate state))
396 io (revertCAFs) -- always revert CAFs on load.
397 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
399 def_mod <- io (readIORef defaultCurrentModule)
401 let new_state = state{
404 current_module = case mods of
409 setGHCiState new_state
412 | null mods = text "none."
414 punctuate comma (map (text.moduleUserString) mods)) <> text "."
417 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
419 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
421 reloadModule :: String -> GHCi ()
423 state <- getGHCiState
425 Nothing -> io (putStr "no current target\n")
427 -> do io (revertCAFs) -- always revert CAFs on reload.
428 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
429 def_mod <- io (readIORef defaultCurrentModule)
431 state{cmstate=new_cmstate,
433 current_module = case mods of
438 reloadModule _ = noArgs ":reload"
440 typeOfExpr :: String -> GHCi ()
442 = do st <- getGHCiState
443 dflags <- io (getDynFlags)
444 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
445 (current_module st) str)
446 setGHCiState st{cmstate = new_cmstate}
449 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
451 quit :: String -> GHCi Bool
454 shellEscape :: String -> GHCi Bool
455 shellEscape str = io (system str >> return False)
457 ----------------------------------------------------------------------------
460 -- set options in the interpreter. Syntax is exactly the same as the
461 -- ghc command line, except that certain options aren't available (-C,
464 -- This is pretty fragile: most options won't work as expected. ToDo:
465 -- figure out which ones & disallow them.
467 setOptions :: String -> GHCi ()
469 = do st <- getGHCiState
470 let opts = options st
471 io $ putStrLn (showSDoc (
472 text "options currently set: " <>
475 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
478 = do -- first, deal with the GHCi opts (+s, +t, etc.)
480 (minus_opts, rest1) = partition isMinus opts
481 (plus_opts, rest2) = partition isPlus rest1
483 if (not (null rest2))
484 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
487 mapM setOpt plus_opts
489 -- now, the GHC flags
490 io (do -- first, static flags
491 leftovers <- processArgs static_flags minus_opts []
493 -- then, dynamic flags
494 dyn_flags <- readIORef v_InitDynFlags
495 writeIORef v_DynFlags dyn_flags
496 leftovers <- processArgs dynamic_flags leftovers []
497 dyn_flags <- readIORef v_DynFlags
498 writeIORef v_InitDynFlags dyn_flags
500 if (not (null leftovers))
501 then throwDyn (OtherError ("unrecognised flags: " ++
506 unsetOptions :: String -> GHCi ()
508 = do -- first, deal with the GHCi opts (+s, +t, etc.)
510 (minus_opts, rest1) = partition isMinus opts
511 (plus_opts, rest2) = partition isPlus rest1
513 if (not (null rest2))
514 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
517 mapM unsetOpt plus_opts
519 -- can't do GHC flags for now
520 if (not (null minus_opts))
521 then throwDyn (OtherError "can't unset GHC command-line flags")
524 isMinus ('-':s) = True
527 isPlus ('+':s) = True
531 = case strToGHCiOpt str of
532 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
533 Just o -> setOption o
536 = case strToGHCiOpt str of
537 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
538 Just o -> unsetOption o
540 strToGHCiOpt :: String -> (Maybe GHCiOption)
541 strToGHCiOpt "s" = Just ShowTiming
542 strToGHCiOpt "t" = Just ShowType
543 strToGHCiOpt "r" = Just RevertCAFs
544 strToGHCiOpt _ = Nothing
546 optToStr :: GHCiOption -> String
547 optToStr ShowTiming = "s"
548 optToStr ShowType = "t"
549 optToStr RevertCAFs = "r"
551 -----------------------------------------------------------------------------
552 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
554 -- Take a string and replace $$s in it with the last expr, if any.
555 expandExpr :: String -> GHCi String
557 = do mle <- getLastExpr
558 return (outside mle str)
560 outside mle ('$':'$':cs)
562 Just le -> " (" ++ le ++ ") " ++ outside mle cs
563 Nothing -> outside mle cs
566 outside mle ('"':str) = '"' : inside2 mle str -- "
567 outside mle ('\'':str) = '\'' : inside1 mle str -- '
568 outside mle (c:cs) = c : outside mle cs
570 inside2 mle ('"':cs) = '"' : outside mle cs -- "
571 inside2 mle (c:cs) = c : inside2 mle cs
574 inside1 mle ('\'':cs) = '\'': outside mle cs
575 inside1 mle (c:cs) = c : inside1 mle cs
579 rememberExpr :: String -> GHCi ()
581 = do let cleaned = (clean . reverse . clean . reverse) str
582 let forget_me_not | null cleaned = Nothing
583 | otherwise = Just cleaned
584 setLastExpr forget_me_not
586 clean = dropWhile isSpace
589 -----------------------------------------------------------------------------
592 data GHCiState = GHCiState
595 current_module :: Module,
596 target :: Maybe FilePath,
598 options :: [GHCiOption],
599 last_expr :: Maybe String
603 = ShowTiming -- show time/allocs after evaluation
604 | ShowType -- show the type of expressions
605 | RevertCAFs -- revert CAFs after every evaluation
608 defaultCurrentModuleName = mkModuleName "Prelude"
609 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
611 GLOBAL_VAR(flush_stdout, error "no flush_stdout", HValue)
612 GLOBAL_VAR(flush_stderr, error "no flush_stdout", HValue)
614 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
616 instance Monad GHCi where
617 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
618 return a = GHCi $ \s -> return (s,a)
620 getGHCiState = GHCi $ \s -> return (s,s)
621 setGHCiState s = GHCi $ \_ -> return (s,())
623 isOptionSet :: GHCiOption -> GHCi Bool
625 = do st <- getGHCiState
626 return (opt `elem` options st)
628 setOption :: GHCiOption -> GHCi ()
630 = do st <- getGHCiState
631 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
633 unsetOption :: GHCiOption -> GHCi ()
635 = do st <- getGHCiState
636 setGHCiState (st{ options = filter (/= opt) (options st) })
638 getLastExpr :: GHCi (Maybe String)
640 = do st <- getGHCiState ; return (last_expr st)
642 setLastExpr :: Maybe String -> GHCi ()
643 setLastExpr last_expr
644 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
646 io m = GHCi $ \s -> m >>= \a -> return (s,a)
648 -----------------------------------------------------------------------------
649 -- recursive exception handlers
651 -- Don't forget to unblock async exceptions in the handler, or if we're
652 -- in an exception loop (eg. let a = error a in a) the ^C exception
653 -- may never be delivered. Thanks to Marcin for pointing out the bug.
655 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
656 ghciHandle h (GHCi m) = GHCi $ \s ->
657 Exception.catch (m s)
658 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
660 ghciUnblock :: GHCi a -> GHCi a
661 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
663 -----------------------------------------------------------------------------
666 linkPackages :: [String] -> [Package] -> IO ()
667 linkPackages cmdline_libs pkgs
668 = do mapM preloadLib cmdline_libs
669 mapM_ linkPackage pkgs
672 = do putStr ("Loading object " ++ orig_name ++ " ... ")
673 case classify orig_name of
675 -> do b <- doesFileExist static_ish
677 then do putStr "not found.\n"
679 else do loadObj static_ish
682 -> do dll_ok <- ocAddDLL (packString dll_unadorned)
684 then putStr "done.\n"
685 else do putStr "not found.\n"
688 croak = throwDyn (OtherError "user specified .o/.so/.DLL cannot be found.")
691 = let a_libr = reverse a_lib
693 case map toLower a_libr of
697 -> (Right . zap_leading_lib
698 . reverse . drop 3 . reverse) a_lib
700 -> (Right . reverse . drop 4 . reverse) a_lib
702 -> -- Main.beginInteractive should not have let this through
703 pprPanic "linkPackages" (text (show a_lib))
706 = if take 3 str == "lib" then drop 3 str else str
709 linkPackage :: Package -> IO ()
710 -- ignore rts and gmp for now (ToDo; better?)
712 | name pkg `elem` ["rts", "gmp"]
715 = do putStr ("Loading package " ++ name pkg ++ " ... ")
716 -- For each obj, try obj.o and if that fails, obj.so.
717 -- Complication: all the .so's must be loaded before any of the .o's.
718 let dirs = library_dirs pkg
719 let objs = hs_libraries pkg ++ extra_libraries pkg
720 classifieds <- mapM (locateOneObj dirs) objs
721 let sos_first = filter isRight classifieds
722 ++ filter (not.isRight) classifieds
723 mapM loadClassified sos_first
724 putStr "linking ... "
728 isRight (Right _) = True
729 isRight (Left _) = False
731 loadClassified :: Either FilePath String -> IO ()
732 loadClassified (Left obj_absolute_filename)
733 = do loadObj obj_absolute_filename
734 loadClassified (Right dll_unadorned)
735 = do dll_ok <- ocAddDLL (packString dll_unadorned)
738 else throwDyn (OtherError ("can't find .o or .so/.DLL for: "
741 locateOneObj :: [FilePath] -> String -> IO (Either FilePath String)
743 = return (Right obj) -- we assume
744 locateOneObj (d:ds) obj
745 = do let path = d ++ '/':obj ++ ".o"
746 b <- doesFileExist path
747 if b then return (Left path) else locateOneObj ds obj
750 type PackedString = ByteArray Int
751 foreign import "ocAddDLL" unsafe ocAddDLL :: PackedString -> IO Int
753 -----------------------------------------------------------------------------
754 -- timing & statistics
756 timeIt :: GHCi a -> GHCi a
758 = do b <- isOptionSet ShowTiming
761 else do allocs1 <- io $ getAllocations
762 time1 <- io $ getCPUTime
764 allocs2 <- io $ getAllocations
765 time2 <- io $ getCPUTime
766 io $ printTimes (allocs2 - allocs1) (time2 - time1)
769 foreign import "getAllocations" getAllocations :: IO Int
771 printTimes :: Int -> Integer -> IO ()
772 printTimes allocs psecs
773 = do let secs = (fromIntegral psecs / (10^12)) :: Float
774 secs_str = showFFloat (Just 2) secs
776 parens (text (secs_str "") <+> text "secs" <> comma <+>
777 int allocs <+> text "bytes")))
779 -----------------------------------------------------------------------------
782 foreign import revertCAFs :: IO () -- make it "safe", just in case