1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.68 2001/05/23 10:26:14 simonmar 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 "../includes/config.h"
14 #include "HsVersions.h"
23 import Finder ( flushPackageCache )
27 import CmdLineOpts ( DynFlag(..), dopt_unset )
28 import Panic ( GhcException(..) )
37 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
51 import PrelGHC ( unsafeCoerce# )
53 -----------------------------------------------------------------------------
57 \ / _ \\ /\\ /\\/ __(_)\n\
58 \ / /_\\// /_/ / / | | GHC Interactive, version " ++ cProjectVersion ++ ", for Haskell 98.\n\
59 \/ /_\\\\/ __ / /___| | http://www.haskell.org/ghc/\n\
60 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
62 GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
64 builtin_commands :: [(String, String -> GHCi Bool)]
66 ("add", keepGoing addModule),
67 ("cd", keepGoing changeDirectory),
68 ("def", keepGoing defineMacro),
69 ("help", keepGoing help),
70 ("?", keepGoing help),
71 ("load", keepGoing loadModule),
72 ("module", keepGoing setContext),
73 ("reload", keepGoing reloadModule),
74 ("set", keepGoing setOptions),
75 ("type", keepGoing typeOfExpr),
76 ("unset", keepGoing unsetOptions),
77 ("undef", keepGoing undefineMacro),
81 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
82 keepGoing a str = a str >> return False
84 shortHelpText = "use :? for help.\n"
87 \ Commands available from the prompt:\n\
89 \ <stmt> evaluate/run <stmt>\n\
90 \ :cd <dir> change directory to <dir>\n\
91 \ :def <cmd> <expr> define a command :<cmd>\n\
92 \ :help, :? display this list of commands\n\
93 \ :load <filename> load a module (and its dependents)\n\
94 \ :module <mod> set the context for expression evaluation to <mod>\n\
95 \ :reload reload the current module set\n\
96 \ :set <option> ... set options\n\
97 \ :undef <cmd> undefine user-defined command :<cmd>\n\
98 \ :type <expr> show the type of <expr>\n\
99 \ :unset <option> ... unset options\n\
101 \ :!<command> run the shell command <command>\n\
103 \ Options for `:set' and `:unset':\n\
105 \ +r revert top-level expressions after each evaluation\n\
106 \ +s print timing/memory stats after each evaluation\n\
107 \ +t print type after evaluation\n\
108 \ -<flags> most GHC command line flags can also be set here\n\
109 \ (eg. -v2, -fglasgow-exts, etc.)\n\
111 --ToDo :add <filename> add a module to the current set\n\
113 interactiveUI :: CmState -> Maybe FilePath -> [LibrarySpec] -> IO ()
114 interactiveUI cmstate mod cmdline_libs = do
116 hSetBuffering stdout NoBuffering
118 -- link in the available packages
119 pkgs <- getPackageInfo
121 linkPackages cmdline_libs pkgs
123 (cmstate, ok, mods) <-
125 Nothing -> return (cmstate, True, [])
126 Just m -> cmLoadModule cmstate m
128 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
132 dflags <- getDynFlags
134 (cmstate, maybe_hval)
135 <- cmCompileExpr cmstate dflags "IO.hFlush IO.stderr"
137 Just hval -> writeIORef flush_stderr (unsafeCoerce# hval :: IO ())
138 _ -> panic "interactiveUI:stderr"
140 (cmstate, maybe_hval)
141 <- cmCompileExpr cmstate dflags "IO.hFlush IO.stdout"
143 Just hval -> writeIORef flush_stdout (unsafeCoerce# hval :: IO ())
144 _ -> panic "interactiveUI:stdout"
146 -- replace the current argv/argc with ["<interactive>"].
147 interactive_str <- newCString "<interactive>"
148 poke prog_argc_label 1 -- sets argc to 1
149 argv <- peek prog_argv_label
150 poke argv interactive_str -- sets argv[0] to point to "<interactive>"
152 (unGHCi runGHCi) GHCiState{ target = mod,
157 foreign label "prog_argv" prog_argv_label :: Ptr (Ptr (Ptr CChar))
158 foreign label "prog_argc" prog_argc_label :: Ptr CInt
164 exists <- io (doesFileExist file)
166 dir_ok <- io (checkPerms ".")
167 file_ok <- io (checkPerms file)
168 when (dir_ok && file_ok) $ do
169 either_hdl <- io (IO.try (openFile "./.ghci" ReadMode))
172 Right hdl -> fileLoop hdl False
174 -- Read in $HOME/.ghci
175 either_dir <- io (IO.try (getEnv "HOME"))
179 cwd <- io (getCurrentDirectory)
180 when (dir /= cwd) $ do
181 let file = dir ++ "/.ghci"
182 ok <- io (checkPerms file)
183 either_hdl <- io (IO.try (openFile file ReadMode))
186 Right hdl -> fileLoop hdl False
188 -- read commands from stdin
189 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
196 io $ do putStrLn "Leaving GHCi."
199 -- NOTE: We only read .ghci files if they are owned by the current user,
200 -- and aren't world writable. Otherwise, we could be accidentally
201 -- running code planted by a malicious third party.
203 -- Furthermore, We only read ./.ghci if both . and ./.ghci are
204 -- owned by the current user and aren't writable by anyone else. I
205 -- think this is sufficient: we don't need to check .. and
206 -- ../.. etc. because "." always refers to the same directory while a
207 -- process is running.
209 checkPerms :: String -> IO Bool
211 handle (\_ -> return False) $ do
212 st <- getFileStatus name
214 if fileOwner st /= me then do
215 putStrLn $ "WARNING: " ++ name ++ " is owned by someone else, IGNORING!"
218 let mode = fileMode st
219 if (groupWriteMode == (mode `intersectFileModes` groupWriteMode))
220 || (otherWriteMode == (mode `intersectFileModes` otherWriteMode))
222 putStrLn $ "*** WARNING: " ++ name ++
223 " is writable by someone else, IGNORING!"
227 fileLoop :: Handle -> Bool -> GHCi ()
228 fileLoop hdl prompt = do
230 mod <- io (cmGetContext (cmstate st))
231 when prompt (io (putStr (mod ++ "> ")))
232 l <- io (IO.try (hGetLine hdl))
234 Left e | isEOFError e -> return ()
235 | otherwise -> throw e
237 case remove_spaces l of
238 "" -> fileLoop hdl prompt
239 l -> do quit <- runCommand l
240 if quit then return () else fileLoop hdl prompt
242 stringLoop :: [String] -> GHCi ()
243 stringLoop [] = return ()
244 stringLoop (s:ss) = do
246 case remove_spaces s of
248 l -> do quit <- runCommand l
249 if quit then return () else stringLoop ss
251 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
252 readlineLoop :: GHCi ()
255 mod <- io (cmGetContext (cmstate st))
256 l <- io (readline (mod ++ "> "))
260 case remove_spaces l of
265 if quit then return () else readlineLoop
268 -- Top level exception handler, just prints out the exception
270 runCommand :: String -> GHCi Bool
272 ghciHandle ( \exception ->
275 case fromDynamic dyn of
276 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
279 PhaseFailed phase code ->
280 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
281 ++ show code ++ ")"))
282 Interrupted -> io (putStrLn "Interrupted.")
283 -- omit the location for CmdLineError
284 CmdLineError s -> io (putStrLn s)
285 other -> io (putStrLn (show (ghc_ex :: GhcException)))
287 other -> io (putStrLn ("*** Exception: " ++ show exception))
294 doCommand (':' : command) = specialCommand command
296 = do timeIt (do stuff <- runStmt stmt; finishEvalExpr stuff)
299 -- Returns True if the expr was successfully parsed, renamed and
301 runStmt :: String -> GHCi (Maybe [Name])
303 | null (filter (not.isSpace) stmt)
306 = do st <- getGHCiState
307 dflags <- io (getDynFlags)
308 let dflags' = dopt_unset dflags Opt_WarnUnusedBinds
309 (new_cmstate, names) <- io (cmRunStmt (cmstate st) dflags' stmt)
310 setGHCiState st{cmstate = new_cmstate}
313 -- possibly print the type and revert CAFs after evaluating an expression
314 finishEvalExpr Nothing = return False
315 finishEvalExpr (Just names)
316 = do b <- isOptionSet ShowType
318 when b (mapM_ (showTypeOfName (cmstate st)) names)
320 b <- isOptionSet RevertCAFs
321 io (when b revertCAFs)
325 showTypeOfName :: CmState -> Name -> GHCi ()
326 showTypeOfName cmstate n
327 = do maybe_str <- io (cmTypeOfName cmstate n)
330 Just str -> io (putStrLn (showSDoc (ppr n) ++ " :: " ++ str))
332 flushEverything :: GHCi ()
334 = io $ do flush_so <- readIORef flush_stdout
336 flush_se <- readIORef flush_stdout
340 specialCommand :: String -> GHCi Bool
341 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
342 specialCommand str = do
343 let (cmd,rest) = break isSpace str
344 cmds <- io (readIORef commands)
345 case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
346 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
347 ++ shortHelpText) >> return False)
348 [(_,f)] -> f (dropWhile isSpace rest)
349 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
350 " matches multiple commands (" ++
351 foldr1 (\a b -> a ++ ',':b) (map fst cs)
352 ++ ")") >> return False)
354 noArgs c = throwDyn (CmdLineError ("command `" ++ c ++ "' takes no arguments"))
356 -----------------------------------------------------------------------------
359 help :: String -> GHCi ()
360 help _ = io (putStr helpText)
362 addModule :: String -> GHCi ()
363 addModule _ = throwDyn (InstallationError ":add not implemented")
365 setContext :: String -> GHCi ()
367 = throwDyn (CmdLineError "syntax: `:m <module>'")
368 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
369 = throwDyn (CmdLineError ("strange looking module name: `" ++ m ++ "'"))
371 = do st <- getGHCiState
372 new_cmstate <- io (cmSetContext (cmstate st) str)
373 setGHCiState st{cmstate=new_cmstate}
375 changeDirectory :: String -> GHCi ()
376 changeDirectory ('~':d) = do
377 tilde <- io (getEnv "HOME") -- will fail if HOME not defined
378 io (setCurrentDirectory (tilde ++ '/':d))
379 changeDirectory d = io (setCurrentDirectory d)
381 defineMacro :: String -> GHCi ()
383 let (macro_name, definition) = break isSpace s
384 cmds <- io (readIORef commands)
386 then throwDyn (CmdLineError "invalid macro name")
388 if (macro_name `elem` map fst cmds)
389 then throwDyn (CmdLineError
390 ("command `" ++ macro_name ++ "' is already defined"))
393 -- give the expression a type signature, so we can be sure we're getting
394 -- something of the right type.
395 let new_expr = '(' : definition ++ ") :: String -> IO String"
397 -- compile the expression
399 dflags <- io (getDynFlags)
400 (new_cmstate, maybe_hv) <- io (cmCompileExpr (cmstate st) dflags new_expr)
401 setGHCiState st{cmstate = new_cmstate}
404 Just hv -> io (writeIORef commands --
405 ((macro_name, keepGoing (runMacro hv)) : cmds))
407 runMacro :: HValue{-String -> IO String-} -> String -> GHCi ()
409 str <- io ((unsafeCoerce# fun :: String -> IO String) s)
410 stringLoop (lines str)
412 undefineMacro :: String -> GHCi ()
413 undefineMacro macro_name = do
414 cmds <- io (readIORef commands)
415 if (macro_name `elem` map fst builtin_commands)
416 then throwDyn (CmdLineError
417 ("command `" ++ macro_name ++ "' cannot be undefined"))
419 if (macro_name `notElem` map fst cmds)
420 then throwDyn (CmdLineError
421 ("command `" ++ macro_name ++ "' not defined"))
423 io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
425 loadModule :: String -> GHCi ()
426 loadModule path = timeIt (loadModule' path)
428 loadModule' path = do
429 state <- getGHCiState
430 dflags <- io (getDynFlags)
431 cmstate1 <- io (cmUnload (cmstate state) dflags)
432 io (revertCAFs) -- always revert CAFs on load.
433 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
434 let new_state = state{ cmstate = cmstate2,
437 setGHCiState new_state
438 modulesLoadedMsg ok mods
440 reloadModule :: String -> GHCi ()
442 state <- getGHCiState
444 Nothing -> io (putStr "no current target\n")
446 -> do io (revertCAFs) -- always revert CAFs on reload.
447 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
448 setGHCiState state{ cmstate=new_cmstate }
449 modulesLoadedMsg ok mods
451 reloadModule _ = noArgs ":reload"
454 modulesLoadedMsg ok mods = do
456 | null mods = text "none."
458 punctuate comma (map text mods)) <> text "."
461 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
463 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
466 typeOfExpr :: String -> GHCi ()
468 = do st <- getGHCiState
469 dflags <- io (getDynFlags)
470 (new_cmstate, maybe_tystr) <- io (cmTypeOfExpr (cmstate st) dflags str)
471 setGHCiState st{cmstate = new_cmstate}
474 Just tystr -> io (putStrLn tystr)
476 quit :: String -> GHCi Bool
479 shellEscape :: String -> GHCi Bool
480 shellEscape str = io (system str >> return False)
482 ----------------------------------------------------------------------------
485 -- set options in the interpreter. Syntax is exactly the same as the
486 -- ghc command line, except that certain options aren't available (-C,
489 -- This is pretty fragile: most options won't work as expected. ToDo:
490 -- figure out which ones & disallow them.
492 setOptions :: String -> GHCi ()
494 = do st <- getGHCiState
495 let opts = options st
496 io $ putStrLn (showSDoc (
497 text "options currently set: " <>
500 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
503 = do -- first, deal with the GHCi opts (+s, +t, etc.)
504 let (plus_opts, minus_opts) = partition isPlus (words str)
505 mapM setOpt plus_opts
507 -- now, the GHC flags
508 pkgs_before <- io (readIORef v_Packages)
509 leftovers <- io (processArgs static_flags minus_opts [])
510 pkgs_after <- io (readIORef v_Packages)
512 -- update things if the users wants more packages
513 when (pkgs_before /= pkgs_after) $
514 newPackages (pkgs_after \\ pkgs_before)
516 -- then, dynamic flags
518 dyn_flags <- readIORef v_InitDynFlags
519 writeIORef v_DynFlags dyn_flags
520 leftovers <- processArgs dynamic_flags leftovers []
521 dyn_flags <- readIORef v_DynFlags
522 writeIORef v_InitDynFlags dyn_flags
524 if (not (null leftovers))
525 then throwDyn (CmdLineError ("unrecognised flags: " ++
530 unsetOptions :: String -> GHCi ()
532 = do -- first, deal with the GHCi opts (+s, +t, etc.)
534 (minus_opts, rest1) = partition isMinus opts
535 (plus_opts, rest2) = partition isPlus rest1
537 if (not (null rest2))
538 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
541 mapM unsetOpt plus_opts
543 -- can't do GHC flags for now
544 if (not (null minus_opts))
545 then throwDyn (CmdLineError "can't unset GHC command-line flags")
548 isMinus ('-':s) = True
551 isPlus ('+':s) = True
555 = case strToGHCiOpt str of
556 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
557 Just o -> setOption o
560 = case strToGHCiOpt str of
561 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
562 Just o -> unsetOption o
564 strToGHCiOpt :: String -> (Maybe GHCiOption)
565 strToGHCiOpt "s" = Just ShowTiming
566 strToGHCiOpt "t" = Just ShowType
567 strToGHCiOpt "r" = Just RevertCAFs
568 strToGHCiOpt _ = Nothing
570 optToStr :: GHCiOption -> String
571 optToStr ShowTiming = "s"
572 optToStr ShowType = "t"
573 optToStr RevertCAFs = "r"
575 newPackages new_pkgs = do
576 state <- getGHCiState
577 dflags <- io (getDynFlags)
578 cmstate1 <- io (cmUnload (cmstate state) dflags)
579 setGHCiState state{ cmstate = cmstate1, target = Nothing }
582 pkgs <- getPackageInfo
583 flushPackageCache pkgs
585 new_pkg_info <- getPackageDetails new_pkgs
586 mapM_ (linkPackage False) (reverse new_pkg_info)
588 -----------------------------------------------------------------------------
591 data GHCiState = GHCiState
593 target :: Maybe FilePath,
595 options :: [GHCiOption]
599 = ShowTiming -- show time/allocs after evaluation
600 | ShowType -- show the type of expressions
601 | RevertCAFs -- revert CAFs after every evaluation
604 GLOBAL_VAR(flush_stdout, error "no flush_stdout", IO ())
605 GLOBAL_VAR(flush_stderr, error "no flush_stdout", IO ())
607 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
609 instance Monad GHCi where
610 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
611 return a = GHCi $ \s -> return (s,a)
613 getGHCiState = GHCi $ \s -> return (s,s)
614 setGHCiState s = GHCi $ \_ -> return (s,())
616 isOptionSet :: GHCiOption -> GHCi Bool
618 = do st <- getGHCiState
619 return (opt `elem` options st)
621 setOption :: GHCiOption -> GHCi ()
623 = do st <- getGHCiState
624 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
626 unsetOption :: GHCiOption -> GHCi ()
628 = do st <- getGHCiState
629 setGHCiState (st{ options = filter (/= opt) (options st) })
631 io m = GHCi $ \s -> m >>= \a -> return (s,a)
633 -----------------------------------------------------------------------------
634 -- recursive exception handlers
636 -- Don't forget to unblock async exceptions in the handler, or if we're
637 -- in an exception loop (eg. let a = error a in a) the ^C exception
638 -- may never be delivered. Thanks to Marcin for pointing out the bug.
640 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
641 ghciHandle h (GHCi m) = GHCi $ \s ->
642 Exception.catch (m s)
643 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
645 ghciUnblock :: GHCi a -> GHCi a
646 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
648 -----------------------------------------------------------------------------
651 -- Left: full path name of a .o file, including trailing .o
652 -- Right: "unadorned" name of a .DLL/.so
653 -- e.g. On unix "qt" denotes "libqt.so"
654 -- On WinDoze "burble" denotes "burble.DLL"
655 -- addDLL is platform-specific and adds the lib/.so/.DLL
656 -- prefixes plaform-dependently; we don't do that here.
658 = Either FilePath String
660 showLS (Left nm) = "(static) " ++ nm
661 showLS (Right nm) = "(dynamic) " ++ nm
663 linkPackages :: [LibrarySpec] -> [PackageConfig] -> IO ()
664 linkPackages cmdline_lib_specs pkgs
665 = do sequence_ [ linkPackage (name p `elem` loaded) p | p <- reverse pkgs ]
666 mapM_ preloadLib cmdline_lib_specs
668 -- packages that are already linked into GHCi
669 loaded = [ "concurrent", "posix", "text", "util" ]
672 = do putStr ("Loading object " ++ showLS lib_spec ++ " ... ")
675 -> do b <- doesFileExist static_ish
677 then do putStr "not found.\n"
679 else do loadObj static_ish
682 -> do maybe_errmsg <- addDLL dll_unadorned
683 if maybe_errmsg == nullPtr
684 then putStr "done.\n"
685 else do str <- peekCString maybe_errmsg
686 putStr ("failed (" ++ str ++ ")\n")
689 croak = throwDyn (CmdLineError "user specified .o/.so/.DLL could not be loaded.")
692 linkPackage :: Bool -> PackageConfig -> IO ()
693 -- ignore rts and gmp for now (ToDo; better?)
694 linkPackage loaded_in_ghci pkg
695 | name pkg `elem` ["rts", "gmp"]
698 = do putStr ("Loading package " ++ name pkg ++ " ... ")
699 -- For each obj, try obj.o and if that fails, obj.so.
700 -- Complication: all the .so's must be loaded before any of the .o's.
701 let dirs = library_dirs pkg
702 let objs = hs_libraries pkg ++ extra_libraries pkg
703 classifieds <- mapM (locateOneObj dirs) objs
705 -- Don't load the .so libs if this is a package GHCi is already
706 -- linked against, because we'll already have the .so linked in.
707 let (so_libs, obj_libs) = partition isRight classifieds
708 let sos_first | loaded_in_ghci = obj_libs
709 | otherwise = so_libs ++ obj_libs
711 mapM loadClassified sos_first
712 putStr "linking ... "
716 isRight (Right _) = True
717 isRight (Left _) = False
719 loadClassified :: LibrarySpec -> IO ()
720 loadClassified (Left obj_absolute_filename)
721 = do loadObj obj_absolute_filename
722 loadClassified (Right dll_unadorned)
723 = do maybe_errmsg <- addDLL dll_unadorned
724 if maybe_errmsg == nullPtr
726 else do str <- peekCString maybe_errmsg
727 throwDyn (CmdLineError ("can't find .o or .so/.DLL for: "
728 ++ dll_unadorned ++ " (" ++ str ++ ")" ))
730 locateOneObj :: [FilePath] -> String -> IO LibrarySpec
732 = return (Right obj) -- we assume
733 locateOneObj (d:ds) obj
734 = do let path = d ++ '/':obj ++ ".o"
735 b <- doesFileExist path
736 if b then return (Left path) else locateOneObj ds obj
738 -----------------------------------------------------------------------------
739 -- timing & statistics
741 timeIt :: GHCi a -> GHCi a
743 = do b <- isOptionSet ShowTiming
746 else do allocs1 <- io $ getAllocations
747 time1 <- io $ getCPUTime
749 allocs2 <- io $ getAllocations
750 time2 <- io $ getCPUTime
751 io $ printTimes (allocs2 - allocs1) (time2 - time1)
754 foreign import "getAllocations" getAllocations :: IO Int
756 printTimes :: Int -> Integer -> IO ()
757 printTimes allocs psecs
758 = do let secs = (fromIntegral psecs / (10^12)) :: Float
759 secs_str = showFFloat (Just 2) secs
761 parens (text (secs_str "") <+> text "secs" <> comma <+>
762 int allocs <+> text "bytes")))
764 -----------------------------------------------------------------------------
767 foreign import revertCAFs :: IO () -- make it "safe", just in case