1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.66 2001/05/04 16:36:38 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(..) )
34 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
48 import PrelGHC ( unsafeCoerce# )
49 import Foreign ( nullPtr )
50 import CString ( peekCString )
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 \ <stmt> evaluate/run <stmt>\n\
89 \ :cd <dir> change directory to <dir>\n\
90 \ :def <cmd> <expr> define a command :<cmd>\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 \ :undef <name> undefine user-defined command :<name>\n\
97 \ :type <expr> show the type of <expr>\n\
98 \ :unset <option> ... unset options\n\
100 \ :!<command> run the shell command <command>\n\
102 \ Options for `:set' and `:unset':\n\
104 \ +r revert top-level expressions after each evaluation\n\
105 \ +s print timing/memory stats after each evaluation\n\
106 \ +t print type after evaluation\n\
107 \ -<flags> most GHC command line flags can also be set here\n\
108 \ (eg. -v2, -fglasgow-exts, etc.)\n\
110 --ToDo :add <filename> add a module to the current set\n\
112 interactiveUI :: CmState -> Maybe FilePath -> [LibrarySpec] -> IO ()
113 interactiveUI cmstate mod cmdline_libs = do
115 hSetBuffering stdout NoBuffering
117 -- link in the available packages
118 pkgs <- getPackageInfo
120 linkPackages cmdline_libs pkgs
122 (cmstate, ok, mods) <-
124 Nothing -> return (cmstate, True, [])
125 Just m -> cmLoadModule cmstate m
127 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
131 dflags <- getDynFlags
133 (cmstate, maybe_hval)
134 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stderr"
136 Just hval -> writeIORef flush_stderr (unsafeCoerce# hval :: IO ())
137 _ -> panic "interactiveUI:stderr"
139 (cmstate, maybe_hval)
140 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stdout"
142 Just hval -> writeIORef flush_stdout (unsafeCoerce# hval :: IO ())
143 _ -> panic "interactiveUI:stdout"
145 (unGHCi runGHCi) GHCiState{ target = mod,
155 exists <- io (doesFileExist file)
157 dir_ok <- io (checkPerms ".")
158 file_ok <- io (checkPerms file)
159 when (dir_ok && file_ok) $ do
160 either_hdl <- io (IO.try (openFile "./.ghci" ReadMode))
163 Right hdl -> fileLoop hdl False
165 -- Read in $HOME/.ghci
166 either_dir <- io (IO.try (getEnv "HOME"))
170 cwd <- io (getCurrentDirectory)
171 when (dir /= cwd) $ do
172 let file = dir ++ "/.ghci"
173 ok <- io (checkPerms file)
174 either_hdl <- io (IO.try (openFile file ReadMode))
177 Right hdl -> fileLoop hdl False
179 -- read commands from stdin
180 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
187 io $ do putStrLn "Leaving GHCi."
190 -- NOTE: We only read .ghci files if they are owned by the current user,
191 -- and aren't world writable. Otherwise, we could be accidentally
192 -- running code planted by a malicious third party.
194 -- Furthermore, We only read ./.ghci if both . and ./.ghci are
195 -- owned by the current user and aren't writable by anyone else. I
196 -- think this is sufficient: we don't need to check .. and
197 -- ../.. etc. because "." always refers to the same directory while a
198 -- process is running.
200 checkPerms :: String -> IO Bool
202 handle (\_ -> return False) $ do
203 st <- getFileStatus name
205 if fileOwner st /= me then do
206 putStrLn $ "WARNING: " ++ name ++ " is owned by someone else, IGNORING!"
209 let mode = fileMode st
210 if (groupWriteMode == (mode `intersectFileModes` groupWriteMode))
211 || (otherWriteMode == (mode `intersectFileModes` otherWriteMode))
213 putStrLn $ "*** WARNING: " ++ name ++
214 " is writable by someone else, IGNORING!"
218 fileLoop :: Handle -> Bool -> GHCi ()
219 fileLoop hdl prompt = do
221 mod <- io (cmGetContext (cmstate st))
222 when prompt (io (putStr (mod ++ "> ")))
223 l <- io (IO.try (hGetLine hdl))
225 Left e | isEOFError e -> return ()
226 | otherwise -> throw e
228 case remove_spaces l of
229 "" -> fileLoop hdl prompt
230 l -> do quit <- runCommand l
231 if quit then return () else fileLoop hdl prompt
233 stringLoop :: [String] -> GHCi ()
234 stringLoop [] = return ()
235 stringLoop (s:ss) = do
237 case remove_spaces s of
239 l -> do quit <- runCommand l
240 if quit then return () else stringLoop ss
242 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
243 readlineLoop :: GHCi ()
246 mod <- io (cmGetContext (cmstate st))
247 l <- io (readline (mod ++ "> "))
251 case remove_spaces l of
256 if quit then return () else readlineLoop
259 -- Top level exception handler, just prints out the exception
261 runCommand :: String -> GHCi Bool
263 ghciHandle ( \exception ->
266 case fromDynamic dyn of
267 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
270 PhaseFailed phase code ->
271 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
272 ++ show code ++ ")"))
273 Interrupted -> io (putStrLn "Interrupted.")
274 -- omit the location for CmdLineError
275 CmdLineError s -> io (putStrLn s)
276 other -> io (putStrLn (show (ghc_ex :: GhcException)))
278 other -> io (putStrLn ("*** Exception: " ++ show exception))
285 doCommand (':' : command) = specialCommand command
287 = do timeIt (do stuff <- runStmt stmt; finishEvalExpr stuff)
290 -- Returns True if the expr was successfully parsed, renamed and
292 runStmt :: String -> GHCi (Maybe [Name])
294 | null (filter (not.isSpace) stmt)
297 = do st <- getGHCiState
298 dflags <- io (getDynFlags)
299 let dflags' = dopt_unset dflags Opt_WarnUnusedBinds
300 (new_cmstate, names) <- io (cmRunStmt (cmstate st) dflags' stmt)
301 setGHCiState st{cmstate = new_cmstate}
304 -- possibly print the type and revert CAFs after evaluating an expression
305 finishEvalExpr Nothing = return False
306 finishEvalExpr (Just names)
307 = do b <- isOptionSet ShowType
309 when b (mapM_ (showTypeOfName (cmstate st)) names)
311 b <- isOptionSet RevertCAFs
312 io (when b revertCAFs)
316 showTypeOfName :: CmState -> Name -> GHCi ()
317 showTypeOfName cmstate n
318 = do maybe_str <- io (cmTypeOfName cmstate n)
321 Just str -> io (putStrLn (showSDoc (ppr n) ++ " :: " ++ str))
323 flushEverything :: GHCi ()
325 = io $ do flush_so <- readIORef flush_stdout
327 flush_se <- readIORef flush_stdout
331 specialCommand :: String -> GHCi Bool
332 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
333 specialCommand str = do
334 let (cmd,rest) = break isSpace str
335 cmds <- io (readIORef commands)
336 case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
337 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
338 ++ shortHelpText) >> return False)
339 [(_,f)] -> f (dropWhile isSpace rest)
340 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
341 " matches multiple commands (" ++
342 foldr1 (\a b -> a ++ ',':b) (map fst cs)
343 ++ ")") >> return False)
345 noArgs c = throwDyn (CmdLineError ("command `" ++ c ++ "' takes no arguments"))
347 -----------------------------------------------------------------------------
350 help :: String -> GHCi ()
351 help _ = io (putStr helpText)
353 addModule :: String -> GHCi ()
354 addModule _ = throwDyn (InstallationError ":add not implemented")
356 setContext :: String -> GHCi ()
358 = throwDyn (CmdLineError "syntax: `:m <module>'")
359 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
360 = throwDyn (CmdLineError ("strange looking module name: `" ++ m ++ "'"))
362 = do st <- getGHCiState
363 new_cmstate <- io (cmSetContext (cmstate st) str)
364 setGHCiState st{cmstate=new_cmstate}
366 changeDirectory :: String -> GHCi ()
367 changeDirectory ('~':d) = do
368 tilde <- io (getEnv "HOME") -- will fail if HOME not defined
369 io (setCurrentDirectory (tilde ++ '/':d))
370 changeDirectory d = io (setCurrentDirectory d)
372 defineMacro :: String -> GHCi ()
374 let (macro_name, definition) = break isSpace s
375 cmds <- io (readIORef commands)
377 then throwDyn (CmdLineError "invalid macro name")
379 if (macro_name `elem` map fst cmds)
380 then throwDyn (CmdLineError
381 ("command `" ++ macro_name ++ "' is already defined"))
384 -- give the expression a type signature, so we can be sure we're getting
385 -- something of the right type.
386 let new_expr = '(' : definition ++ ") :: String -> IO String"
388 -- compile the expression
390 dflags <- io (getDynFlags)
391 (new_cmstate, maybe_hv) <- io (cmCompileExpr (cmstate st) dflags new_expr)
392 setGHCiState st{cmstate = new_cmstate}
395 Just hv -> io (writeIORef commands --
396 ((macro_name, keepGoing (runMacro hv)) : cmds))
398 runMacro :: HValue{-String -> IO String-} -> String -> GHCi ()
400 str <- io ((unsafeCoerce# fun :: String -> IO String) s)
401 stringLoop (lines str)
403 undefineMacro :: String -> GHCi ()
404 undefineMacro macro_name = do
405 cmds <- io (readIORef commands)
406 if (macro_name `elem` map fst builtin_commands)
407 then throwDyn (CmdLineError
408 ("command `" ++ macro_name ++ "' cannot be undefined"))
410 if (macro_name `notElem` map fst cmds)
411 then throwDyn (CmdLineError
412 ("command `" ++ macro_name ++ "' not defined"))
414 io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
416 loadModule :: String -> GHCi ()
417 loadModule path = timeIt (loadModule' path)
419 loadModule' path = do
420 state <- getGHCiState
421 dflags <- io (getDynFlags)
422 cmstate1 <- io (cmUnload (cmstate state) dflags)
423 io (revertCAFs) -- always revert CAFs on load.
424 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
425 let new_state = state{ cmstate = cmstate2,
428 setGHCiState new_state
429 modulesLoadedMsg ok mods
431 reloadModule :: String -> GHCi ()
433 state <- getGHCiState
435 Nothing -> io (putStr "no current target\n")
437 -> do io (revertCAFs) -- always revert CAFs on reload.
438 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
439 setGHCiState state{ cmstate=new_cmstate }
440 modulesLoadedMsg ok mods
442 reloadModule _ = noArgs ":reload"
445 modulesLoadedMsg ok mods = do
447 | null mods = text "none."
449 punctuate comma (map text mods)) <> text "."
452 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
454 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
457 typeOfExpr :: String -> GHCi ()
459 = do st <- getGHCiState
460 dflags <- io (getDynFlags)
461 (new_cmstate, maybe_tystr) <- io (cmTypeOfExpr (cmstate st) dflags str)
462 setGHCiState st{cmstate = new_cmstate}
465 Just tystr -> io (putStrLn tystr)
467 quit :: String -> GHCi Bool
470 shellEscape :: String -> GHCi Bool
471 shellEscape str = io (system str >> return False)
473 ----------------------------------------------------------------------------
476 -- set options in the interpreter. Syntax is exactly the same as the
477 -- ghc command line, except that certain options aren't available (-C,
480 -- This is pretty fragile: most options won't work as expected. ToDo:
481 -- figure out which ones & disallow them.
483 setOptions :: String -> GHCi ()
485 = do st <- getGHCiState
486 let opts = options st
487 io $ putStrLn (showSDoc (
488 text "options currently set: " <>
491 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
494 = do -- first, deal with the GHCi opts (+s, +t, etc.)
495 let (plus_opts, minus_opts) = partition isPlus (words str)
496 mapM setOpt plus_opts
498 -- now, the GHC flags
499 pkgs_before <- io (readIORef v_Packages)
500 leftovers <- io (processArgs static_flags minus_opts [])
501 pkgs_after <- io (readIORef v_Packages)
503 -- update things if the users wants more packages
504 when (pkgs_before /= pkgs_after) $
505 newPackages (pkgs_after \\ pkgs_before)
507 -- then, dynamic flags
509 dyn_flags <- readIORef v_InitDynFlags
510 writeIORef v_DynFlags dyn_flags
511 leftovers <- processArgs dynamic_flags leftovers []
512 dyn_flags <- readIORef v_DynFlags
513 writeIORef v_InitDynFlags dyn_flags
515 if (not (null leftovers))
516 then throwDyn (CmdLineError ("unrecognised flags: " ++
521 unsetOptions :: String -> GHCi ()
523 = do -- first, deal with the GHCi opts (+s, +t, etc.)
525 (minus_opts, rest1) = partition isMinus opts
526 (plus_opts, rest2) = partition isPlus rest1
528 if (not (null rest2))
529 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
532 mapM unsetOpt plus_opts
534 -- can't do GHC flags for now
535 if (not (null minus_opts))
536 then throwDyn (CmdLineError "can't unset GHC command-line flags")
539 isMinus ('-':s) = True
542 isPlus ('+':s) = True
546 = case strToGHCiOpt str of
547 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
548 Just o -> setOption o
551 = case strToGHCiOpt str of
552 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
553 Just o -> unsetOption o
555 strToGHCiOpt :: String -> (Maybe GHCiOption)
556 strToGHCiOpt "s" = Just ShowTiming
557 strToGHCiOpt "t" = Just ShowType
558 strToGHCiOpt "r" = Just RevertCAFs
559 strToGHCiOpt _ = Nothing
561 optToStr :: GHCiOption -> String
562 optToStr ShowTiming = "s"
563 optToStr ShowType = "t"
564 optToStr RevertCAFs = "r"
566 newPackages new_pkgs = do
567 state <- getGHCiState
568 dflags <- io (getDynFlags)
569 cmstate1 <- io (cmUnload (cmstate state) dflags)
570 setGHCiState state{ cmstate = cmstate1, target = Nothing }
573 pkgs <- getPackageInfo
574 flushPackageCache pkgs
576 new_pkg_info <- getPackageDetails new_pkgs
577 mapM_ (linkPackage False) (reverse new_pkg_info)
579 -----------------------------------------------------------------------------
582 data GHCiState = GHCiState
584 target :: Maybe FilePath,
586 options :: [GHCiOption]
590 = ShowTiming -- show time/allocs after evaluation
591 | ShowType -- show the type of expressions
592 | RevertCAFs -- revert CAFs after every evaluation
595 GLOBAL_VAR(flush_stdout, error "no flush_stdout", IO ())
596 GLOBAL_VAR(flush_stderr, error "no flush_stdout", IO ())
598 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
600 instance Monad GHCi where
601 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
602 return a = GHCi $ \s -> return (s,a)
604 getGHCiState = GHCi $ \s -> return (s,s)
605 setGHCiState s = GHCi $ \_ -> return (s,())
607 isOptionSet :: GHCiOption -> GHCi Bool
609 = do st <- getGHCiState
610 return (opt `elem` options st)
612 setOption :: GHCiOption -> GHCi ()
614 = do st <- getGHCiState
615 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
617 unsetOption :: GHCiOption -> GHCi ()
619 = do st <- getGHCiState
620 setGHCiState (st{ options = filter (/= opt) (options st) })
622 io m = GHCi $ \s -> m >>= \a -> return (s,a)
624 -----------------------------------------------------------------------------
625 -- recursive exception handlers
627 -- Don't forget to unblock async exceptions in the handler, or if we're
628 -- in an exception loop (eg. let a = error a in a) the ^C exception
629 -- may never be delivered. Thanks to Marcin for pointing out the bug.
631 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
632 ghciHandle h (GHCi m) = GHCi $ \s ->
633 Exception.catch (m s)
634 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
636 ghciUnblock :: GHCi a -> GHCi a
637 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
639 -----------------------------------------------------------------------------
642 -- Left: full path name of a .o file, including trailing .o
643 -- Right: "unadorned" name of a .DLL/.so
644 -- e.g. On unix "qt" denotes "libqt.so"
645 -- On WinDoze "burble" denotes "burble.DLL"
646 -- addDLL is platform-specific and adds the lib/.so/.DLL
647 -- prefixes plaform-dependently; we don't do that here.
649 = Either FilePath String
651 showLS (Left nm) = "(static) " ++ nm
652 showLS (Right nm) = "(dynamic) " ++ nm
654 linkPackages :: [LibrarySpec] -> [PackageConfig] -> IO ()
655 linkPackages cmdline_lib_specs pkgs
656 = do sequence_ [ linkPackage (name p `elem` loaded) p | p <- reverse pkgs ]
657 mapM_ preloadLib cmdline_lib_specs
659 -- packages that are already linked into GHCi
660 loaded = [ "concurrent", "posix", "text", "util" ]
663 = do putStr ("Loading object " ++ showLS lib_spec ++ " ... ")
666 -> do b <- doesFileExist static_ish
668 then do putStr "not found.\n"
670 else do loadObj static_ish
673 -> do maybe_errmsg <- addDLL dll_unadorned
674 if maybe_errmsg == nullPtr
675 then putStr "done.\n"
676 else do str <- peekCString maybe_errmsg
677 putStr ("failed (" ++ str ++ ")\n")
680 croak = throwDyn (CmdLineError "user specified .o/.so/.DLL could not be loaded.")
683 linkPackage :: Bool -> PackageConfig -> IO ()
684 -- ignore rts and gmp for now (ToDo; better?)
685 linkPackage loaded_in_ghci pkg
686 | name pkg `elem` ["rts", "gmp"]
689 = do putStr ("Loading package " ++ name pkg ++ " ... ")
690 -- For each obj, try obj.o and if that fails, obj.so.
691 -- Complication: all the .so's must be loaded before any of the .o's.
692 let dirs = library_dirs pkg
693 let objs = hs_libraries pkg ++ extra_libraries pkg
694 classifieds <- mapM (locateOneObj dirs) objs
696 -- Don't load the .so libs if this is a package GHCi is already
697 -- linked against, because we'll already have the .so linked in.
698 let (so_libs, obj_libs) = partition isRight classifieds
699 let sos_first | loaded_in_ghci = obj_libs
700 | otherwise = so_libs ++ obj_libs
702 mapM loadClassified sos_first
703 putStr "linking ... "
707 isRight (Right _) = True
708 isRight (Left _) = False
710 loadClassified :: LibrarySpec -> IO ()
711 loadClassified (Left obj_absolute_filename)
712 = do loadObj obj_absolute_filename
713 loadClassified (Right dll_unadorned)
714 = do maybe_errmsg <- addDLL dll_unadorned
715 if maybe_errmsg == nullPtr
717 else do str <- peekCString maybe_errmsg
718 throwDyn (CmdLineError ("can't find .o or .so/.DLL for: "
719 ++ dll_unadorned ++ " (" ++ str ++ ")" ))
721 locateOneObj :: [FilePath] -> String -> IO LibrarySpec
723 = return (Right obj) -- we assume
724 locateOneObj (d:ds) obj
725 = do let path = d ++ '/':obj ++ ".o"
726 b <- doesFileExist path
727 if b then return (Left path) else locateOneObj ds obj
729 -----------------------------------------------------------------------------
730 -- timing & statistics
732 timeIt :: GHCi a -> GHCi a
734 = do b <- isOptionSet ShowTiming
737 else do allocs1 <- io $ getAllocations
738 time1 <- io $ getCPUTime
740 allocs2 <- io $ getAllocations
741 time2 <- io $ getCPUTime
742 io $ printTimes (allocs2 - allocs1) (time2 - time1)
745 foreign import "getAllocations" getAllocations :: IO Int
747 printTimes :: Int -> Integer -> IO ()
748 printTimes allocs psecs
749 = do let secs = (fromIntegral psecs / (10^12)) :: Float
750 secs_str = showFFloat (Just 2) secs
752 parens (text (secs_str "") <+> text "secs" <> comma <+>
753 int allocs <+> text "bytes")))
755 -----------------------------------------------------------------------------
758 foreign import revertCAFs :: IO () -- make it "safe", just in case