1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.69 2001/05/28 03:17:03 sof 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(..) )
31 #ifndef mingw32_TARGET_OS
37 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
51 import PrelGHC ( unsafeCoerce# )
52 import Foreign ( nullPtr )
53 import CString ( peekCString )
55 -----------------------------------------------------------------------------
59 \ / _ \\ /\\ /\\/ __(_)\n\
60 \ / /_\\// /_/ / / | | GHC Interactive, version " ++ cProjectVersion ++ ", for Haskell 98.\n\
61 \/ /_\\\\/ __ / /___| | http://www.haskell.org/ghc/\n\
62 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
64 GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
66 builtin_commands :: [(String, String -> GHCi Bool)]
68 ("add", keepGoing addModule),
69 ("cd", keepGoing changeDirectory),
70 ("def", keepGoing defineMacro),
71 ("help", keepGoing help),
72 ("?", keepGoing help),
73 ("load", keepGoing loadModule),
74 ("module", keepGoing setContext),
75 ("reload", keepGoing reloadModule),
76 ("set", keepGoing setOptions),
77 ("type", keepGoing typeOfExpr),
78 ("unset", keepGoing unsetOptions),
79 ("undef", keepGoing undefineMacro),
83 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
84 keepGoing a str = a str >> return False
86 shortHelpText = "use :? for help.\n"
89 \ Commands available from the prompt:\n\
91 \ <stmt> evaluate/run <stmt>\n\
92 \ :cd <dir> change directory to <dir>\n\
93 \ :def <cmd> <expr> define a command :<cmd>\n\
94 \ :help, :? display this list of commands\n\
95 \ :load <filename> load a module (and its dependents)\n\
96 \ :module <mod> set the context for expression evaluation to <mod>\n\
97 \ :reload reload the current module set\n\
98 \ :set <option> ... set options\n\
99 \ :undef <cmd> undefine user-defined command :<cmd>\n\
100 \ :type <expr> show the type of <expr>\n\
101 \ :unset <option> ... unset options\n\
103 \ :!<command> run the shell command <command>\n\
105 \ Options for `:set' and `:unset':\n\
107 \ +r revert top-level expressions after each evaluation\n\
108 \ +s print timing/memory stats after each evaluation\n\
109 \ +t print type after evaluation\n\
110 \ -<flags> most GHC command line flags can also be set here\n\
111 \ (eg. -v2, -fglasgow-exts, etc.)\n\
113 --ToDo :add <filename> add a module to the current set\n\
115 interactiveUI :: CmState -> Maybe FilePath -> [LibrarySpec] -> IO ()
116 interactiveUI cmstate mod cmdline_libs = do
118 hSetBuffering stdout NoBuffering
120 -- link in the available packages
121 pkgs <- getPackageInfo
123 linkPackages cmdline_libs pkgs
125 (cmstate, ok, mods) <-
127 Nothing -> return (cmstate, True, [])
128 Just m -> cmLoadModule cmstate m
130 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
134 dflags <- getDynFlags
136 (cmstate, maybe_hval)
137 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stderr"
139 Just hval -> writeIORef flush_stderr (unsafeCoerce# hval :: IO ())
140 _ -> panic "interactiveUI:stderr"
142 (cmstate, maybe_hval)
143 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stdout"
145 Just hval -> writeIORef flush_stdout (unsafeCoerce# hval :: IO ())
146 _ -> panic "interactiveUI:stdout"
148 (unGHCi runGHCi) GHCiState{ target = mod,
158 exists <- io (doesFileExist file)
160 dir_ok <- io (checkPerms ".")
161 file_ok <- io (checkPerms file)
162 when (dir_ok && file_ok) $ do
163 either_hdl <- io (IO.try (openFile "./.ghci" ReadMode))
166 Right hdl -> fileLoop hdl False
168 -- Read in $HOME/.ghci
169 either_dir <- io (IO.try (getEnv "HOME"))
173 cwd <- io (getCurrentDirectory)
174 when (dir /= cwd) $ do
175 let file = dir ++ "/.ghci"
176 ok <- io (checkPerms file)
177 either_hdl <- io (IO.try (openFile file ReadMode))
180 Right hdl -> fileLoop hdl False
182 -- read commands from stdin
183 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
190 io $ do putStrLn "Leaving GHCi."
193 -- NOTE: We only read .ghci files if they are owned by the current user,
194 -- and aren't world writable. Otherwise, we could be accidentally
195 -- running code planted by a malicious third party.
197 -- Furthermore, We only read ./.ghci if both . and ./.ghci are
198 -- owned by the current user and aren't writable by anyone else. I
199 -- think this is sufficient: we don't need to check .. and
200 -- ../.. etc. because "." always refers to the same directory while a
201 -- process is running.
203 checkPerms :: String -> IO Bool
205 handle (\_ -> return False) $ do
206 #ifdef mingw32_TARGET_OS
209 st <- getFileStatus name
211 if fileOwner st /= me then do
212 putStrLn $ "WARNING: " ++ name ++ " is owned by someone else, IGNORING!"
215 let mode = fileMode st
216 if (groupWriteMode == (mode `intersectFileModes` groupWriteMode))
217 || (otherWriteMode == (mode `intersectFileModes` otherWriteMode))
219 putStrLn $ "*** WARNING: " ++ name ++
220 " is writable by someone else, IGNORING!"
225 fileLoop :: Handle -> Bool -> GHCi ()
226 fileLoop hdl prompt = do
228 mod <- io (cmGetContext (cmstate st))
229 when prompt (io (putStr (mod ++ "> ")))
230 l <- io (IO.try (hGetLine hdl))
232 Left e | isEOFError e -> return ()
233 | otherwise -> throw e
235 case remove_spaces l of
236 "" -> fileLoop hdl prompt
237 l -> do quit <- runCommand l
238 if quit then return () else fileLoop hdl prompt
240 stringLoop :: [String] -> GHCi ()
241 stringLoop [] = return ()
242 stringLoop (s:ss) = do
244 case remove_spaces s of
246 l -> do quit <- runCommand l
247 if quit then return () else stringLoop ss
249 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
250 readlineLoop :: GHCi ()
253 mod <- io (cmGetContext (cmstate st))
254 l <- io (readline (mod ++ "> "))
258 case remove_spaces l of
263 if quit then return () else readlineLoop
266 -- Top level exception handler, just prints out the exception
268 runCommand :: String -> GHCi Bool
270 ghciHandle ( \exception ->
273 case fromDynamic dyn of
274 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
277 PhaseFailed phase code ->
278 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
279 ++ show code ++ ")"))
280 Interrupted -> io (putStrLn "Interrupted.")
281 -- omit the location for CmdLineError
282 CmdLineError s -> io (putStrLn s)
283 other -> io (putStrLn (show (ghc_ex :: GhcException)))
285 other -> io (putStrLn ("*** Exception: " ++ show exception))
292 doCommand (':' : command) = specialCommand command
294 = do timeIt (do stuff <- runStmt stmt; finishEvalExpr stuff)
297 -- Returns True if the expr was successfully parsed, renamed and
299 runStmt :: String -> GHCi (Maybe [Name])
301 | null (filter (not.isSpace) stmt)
304 = do st <- getGHCiState
305 dflags <- io (getDynFlags)
306 let dflags' = dopt_unset dflags Opt_WarnUnusedBinds
307 (new_cmstate, names) <- io (cmRunStmt (cmstate st) dflags' stmt)
308 setGHCiState st{cmstate = new_cmstate}
311 -- possibly print the type and revert CAFs after evaluating an expression
312 finishEvalExpr Nothing = return False
313 finishEvalExpr (Just names)
314 = do b <- isOptionSet ShowType
316 when b (mapM_ (showTypeOfName (cmstate st)) names)
318 b <- isOptionSet RevertCAFs
319 io (when b revertCAFs)
323 showTypeOfName :: CmState -> Name -> GHCi ()
324 showTypeOfName cmstate n
325 = do maybe_str <- io (cmTypeOfName cmstate n)
328 Just str -> io (putStrLn (showSDoc (ppr n) ++ " :: " ++ str))
330 flushEverything :: GHCi ()
332 = io $ do flush_so <- readIORef flush_stdout
334 flush_se <- readIORef flush_stdout
338 specialCommand :: String -> GHCi Bool
339 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
340 specialCommand str = do
341 let (cmd,rest) = break isSpace str
342 cmds <- io (readIORef commands)
343 case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
344 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
345 ++ shortHelpText) >> return False)
346 [(_,f)] -> f (dropWhile isSpace rest)
347 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
348 " matches multiple commands (" ++
349 foldr1 (\a b -> a ++ ',':b) (map fst cs)
350 ++ ")") >> return False)
352 noArgs c = throwDyn (CmdLineError ("command `" ++ c ++ "' takes no arguments"))
354 -----------------------------------------------------------------------------
357 help :: String -> GHCi ()
358 help _ = io (putStr helpText)
360 addModule :: String -> GHCi ()
361 addModule _ = throwDyn (InstallationError ":add not implemented")
363 setContext :: String -> GHCi ()
365 = throwDyn (CmdLineError "syntax: `:m <module>'")
366 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
367 = throwDyn (CmdLineError ("strange looking module name: `" ++ m ++ "'"))
369 = do st <- getGHCiState
370 new_cmstate <- io (cmSetContext (cmstate st) str)
371 setGHCiState st{cmstate=new_cmstate}
373 changeDirectory :: String -> GHCi ()
374 changeDirectory ('~':d) = do
375 tilde <- io (getEnv "HOME") -- will fail if HOME not defined
376 io (setCurrentDirectory (tilde ++ '/':d))
377 changeDirectory d = io (setCurrentDirectory d)
379 defineMacro :: String -> GHCi ()
381 let (macro_name, definition) = break isSpace s
382 cmds <- io (readIORef commands)
384 then throwDyn (CmdLineError "invalid macro name")
386 if (macro_name `elem` map fst cmds)
387 then throwDyn (CmdLineError
388 ("command `" ++ macro_name ++ "' is already defined"))
391 -- give the expression a type signature, so we can be sure we're getting
392 -- something of the right type.
393 let new_expr = '(' : definition ++ ") :: String -> IO String"
395 -- compile the expression
397 dflags <- io (getDynFlags)
398 (new_cmstate, maybe_hv) <- io (cmCompileExpr (cmstate st) dflags new_expr)
399 setGHCiState st{cmstate = new_cmstate}
402 Just hv -> io (writeIORef commands --
403 ((macro_name, keepGoing (runMacro hv)) : cmds))
405 runMacro :: HValue{-String -> IO String-} -> String -> GHCi ()
407 str <- io ((unsafeCoerce# fun :: String -> IO String) s)
408 stringLoop (lines str)
410 undefineMacro :: String -> GHCi ()
411 undefineMacro macro_name = do
412 cmds <- io (readIORef commands)
413 if (macro_name `elem` map fst builtin_commands)
414 then throwDyn (CmdLineError
415 ("command `" ++ macro_name ++ "' cannot be undefined"))
417 if (macro_name `notElem` map fst cmds)
418 then throwDyn (CmdLineError
419 ("command `" ++ macro_name ++ "' not defined"))
421 io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
423 loadModule :: String -> GHCi ()
424 loadModule path = timeIt (loadModule' path)
426 loadModule' path = do
427 state <- getGHCiState
428 dflags <- io (getDynFlags)
429 cmstate1 <- io (cmUnload (cmstate state) dflags)
430 io (revertCAFs) -- always revert CAFs on load.
431 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
432 let new_state = state{ cmstate = cmstate2,
435 setGHCiState new_state
436 modulesLoadedMsg ok mods
438 reloadModule :: String -> GHCi ()
440 state <- getGHCiState
442 Nothing -> io (putStr "no current target\n")
444 -> do io (revertCAFs) -- always revert CAFs on reload.
445 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
446 setGHCiState state{ cmstate=new_cmstate }
447 modulesLoadedMsg ok mods
449 reloadModule _ = noArgs ":reload"
452 modulesLoadedMsg ok mods = do
454 | null mods = text "none."
456 punctuate comma (map text mods)) <> text "."
459 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
461 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
464 typeOfExpr :: String -> GHCi ()
466 = do st <- getGHCiState
467 dflags <- io (getDynFlags)
468 (new_cmstate, maybe_tystr) <- io (cmTypeOfExpr (cmstate st) dflags str)
469 setGHCiState st{cmstate = new_cmstate}
472 Just tystr -> io (putStrLn tystr)
474 quit :: String -> GHCi Bool
477 shellEscape :: String -> GHCi Bool
478 shellEscape str = io (system str >> return False)
480 ----------------------------------------------------------------------------
483 -- set options in the interpreter. Syntax is exactly the same as the
484 -- ghc command line, except that certain options aren't available (-C,
487 -- This is pretty fragile: most options won't work as expected. ToDo:
488 -- figure out which ones & disallow them.
490 setOptions :: String -> GHCi ()
492 = do st <- getGHCiState
493 let opts = options st
494 io $ putStrLn (showSDoc (
495 text "options currently set: " <>
498 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
501 = do -- first, deal with the GHCi opts (+s, +t, etc.)
502 let (plus_opts, minus_opts) = partition isPlus (words str)
503 mapM setOpt plus_opts
505 -- now, the GHC flags
506 pkgs_before <- io (readIORef v_Packages)
507 leftovers <- io (processArgs static_flags minus_opts [])
508 pkgs_after <- io (readIORef v_Packages)
510 -- update things if the users wants more packages
511 when (pkgs_before /= pkgs_after) $
512 newPackages (pkgs_after \\ pkgs_before)
514 -- then, dynamic flags
516 dyn_flags <- readIORef v_InitDynFlags
517 writeIORef v_DynFlags dyn_flags
518 leftovers <- processArgs dynamic_flags leftovers []
519 dyn_flags <- readIORef v_DynFlags
520 writeIORef v_InitDynFlags dyn_flags
522 if (not (null leftovers))
523 then throwDyn (CmdLineError ("unrecognised flags: " ++
528 unsetOptions :: String -> GHCi ()
530 = do -- first, deal with the GHCi opts (+s, +t, etc.)
532 (minus_opts, rest1) = partition isMinus opts
533 (plus_opts, rest2) = partition isPlus rest1
535 if (not (null rest2))
536 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
539 mapM unsetOpt plus_opts
541 -- can't do GHC flags for now
542 if (not (null minus_opts))
543 then throwDyn (CmdLineError "can't unset GHC command-line flags")
546 isMinus ('-':s) = True
549 isPlus ('+':s) = True
553 = case strToGHCiOpt str of
554 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
555 Just o -> setOption o
558 = case strToGHCiOpt str of
559 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
560 Just o -> unsetOption o
562 strToGHCiOpt :: String -> (Maybe GHCiOption)
563 strToGHCiOpt "s" = Just ShowTiming
564 strToGHCiOpt "t" = Just ShowType
565 strToGHCiOpt "r" = Just RevertCAFs
566 strToGHCiOpt _ = Nothing
568 optToStr :: GHCiOption -> String
569 optToStr ShowTiming = "s"
570 optToStr ShowType = "t"
571 optToStr RevertCAFs = "r"
573 newPackages new_pkgs = do
574 state <- getGHCiState
575 dflags <- io (getDynFlags)
576 cmstate1 <- io (cmUnload (cmstate state) dflags)
577 setGHCiState state{ cmstate = cmstate1, target = Nothing }
580 pkgs <- getPackageInfo
581 flushPackageCache pkgs
583 new_pkg_info <- getPackageDetails new_pkgs
584 mapM_ (linkPackage False) (reverse new_pkg_info)
586 -----------------------------------------------------------------------------
589 data GHCiState = GHCiState
591 target :: Maybe FilePath,
593 options :: [GHCiOption]
597 = ShowTiming -- show time/allocs after evaluation
598 | ShowType -- show the type of expressions
599 | RevertCAFs -- revert CAFs after every evaluation
602 GLOBAL_VAR(flush_stdout, error "no flush_stdout", IO ())
603 GLOBAL_VAR(flush_stderr, error "no flush_stdout", IO ())
605 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
607 instance Monad GHCi where
608 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
609 return a = GHCi $ \s -> return (s,a)
611 getGHCiState = GHCi $ \s -> return (s,s)
612 setGHCiState s = GHCi $ \_ -> return (s,())
614 isOptionSet :: GHCiOption -> GHCi Bool
616 = do st <- getGHCiState
617 return (opt `elem` options st)
619 setOption :: GHCiOption -> GHCi ()
621 = do st <- getGHCiState
622 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
624 unsetOption :: GHCiOption -> GHCi ()
626 = do st <- getGHCiState
627 setGHCiState (st{ options = filter (/= opt) (options st) })
629 io m = GHCi $ \s -> m >>= \a -> return (s,a)
631 -----------------------------------------------------------------------------
632 -- recursive exception handlers
634 -- Don't forget to unblock async exceptions in the handler, or if we're
635 -- in an exception loop (eg. let a = error a in a) the ^C exception
636 -- may never be delivered. Thanks to Marcin for pointing out the bug.
638 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
639 ghciHandle h (GHCi m) = GHCi $ \s ->
640 Exception.catch (m s)
641 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
643 ghciUnblock :: GHCi a -> GHCi a
644 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
646 -----------------------------------------------------------------------------
649 -- Left: full path name of a .o file, including trailing .o
650 -- Right: "unadorned" name of a .DLL/.so
651 -- e.g. On unix "qt" denotes "libqt.so"
652 -- On WinDoze "burble" denotes "burble.DLL"
653 -- addDLL is platform-specific and adds the lib/.so/.DLL
654 -- prefixes plaform-dependently; we don't do that here.
656 = Either FilePath String
658 showLS (Left nm) = "(static) " ++ nm
659 showLS (Right nm) = "(dynamic) " ++ nm
661 linkPackages :: [LibrarySpec] -> [PackageConfig] -> IO ()
662 linkPackages cmdline_lib_specs pkgs
663 = do sequence_ [ linkPackage (name p `elem` loaded) p | p <- reverse pkgs ]
664 mapM_ preloadLib cmdline_lib_specs
666 -- packages that are already linked into GHCi
667 loaded = [ "concurrent", "posix", "text", "util" ]
670 = do putStr ("Loading object " ++ showLS lib_spec ++ " ... ")
673 -> do b <- doesFileExist static_ish
675 then do putStr "not found.\n"
677 else do loadObj static_ish
680 -> do maybe_errmsg <- addDLL dll_unadorned
681 if maybe_errmsg == nullPtr
682 then putStr "done.\n"
683 else do str <- peekCString maybe_errmsg
684 putStr ("failed (" ++ str ++ ")\n")
687 croak = throwDyn (CmdLineError "user specified .o/.so/.DLL could not be loaded.")
690 linkPackage :: Bool -> PackageConfig -> IO ()
691 -- ignore rts and gmp for now (ToDo; better?)
692 linkPackage loaded_in_ghci pkg
693 | name pkg `elem` ["rts", "gmp"]
696 = do putStr ("Loading package " ++ name pkg ++ " ... ")
697 -- For each obj, try obj.o and if that fails, obj.so.
698 -- Complication: all the .so's must be loaded before any of the .o's.
699 let dirs = library_dirs pkg
700 let objs = hs_libraries pkg ++ extra_libraries pkg
701 classifieds <- mapM (locateOneObj dirs) objs
703 -- Don't load the .so libs if this is a package GHCi is already
704 -- linked against, because we'll already have the .so linked in.
705 let (so_libs, obj_libs) = partition isRight classifieds
706 let sos_first | loaded_in_ghci = obj_libs
707 | otherwise = so_libs ++ obj_libs
709 mapM loadClassified sos_first
710 putStr "linking ... "
714 isRight (Right _) = True
715 isRight (Left _) = False
717 loadClassified :: LibrarySpec -> IO ()
718 loadClassified (Left obj_absolute_filename)
719 = do loadObj obj_absolute_filename
720 loadClassified (Right dll_unadorned)
721 = do maybe_errmsg <- addDLL dll_unadorned
722 if maybe_errmsg == nullPtr
724 else do str <- peekCString maybe_errmsg
725 throwDyn (CmdLineError ("can't find .o or .so/.DLL for: "
726 ++ dll_unadorned ++ " (" ++ str ++ ")" ))
728 locateOneObj :: [FilePath] -> String -> IO LibrarySpec
730 = return (Right obj) -- we assume
731 locateOneObj (d:ds) obj
732 = do let path = d ++ '/':obj ++ ".o"
733 b <- doesFileExist path
734 if b then return (Left path) else locateOneObj ds obj
736 -----------------------------------------------------------------------------
737 -- timing & statistics
739 timeIt :: GHCi a -> GHCi a
741 = do b <- isOptionSet ShowTiming
744 else do allocs1 <- io $ getAllocations
745 time1 <- io $ getCPUTime
747 allocs2 <- io $ getAllocations
748 time2 <- io $ getCPUTime
749 io $ printTimes (allocs2 - allocs1) (time2 - time1)
752 foreign import "getAllocations" getAllocations :: IO Int
754 printTimes :: Int -> Integer -> IO ()
755 printTimes allocs psecs
756 = do let secs = (fromIntegral psecs / (10^12)) :: Float
757 secs_str = showFFloat (Just 2) secs
759 parens (text (secs_str "") <+> text "secs" <> comma <+>
760 int allocs <+> text "bytes")))
762 -----------------------------------------------------------------------------
765 foreign import revertCAFs :: IO () -- make it "safe", just in case