1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.63 2001/04/26 11:08:32 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 "../includes/config.h"
14 #include "HsVersions.h"
26 import CmdLineOpts ( DynFlag(..), dopt_unset )
27 import Panic ( GhcException(..) )
32 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
46 import PrelGHC ( unsafeCoerce# )
47 import Foreign ( nullPtr )
48 import CString ( peekCString )
50 -----------------------------------------------------------------------------
54 \ / _ \\ /\\ /\\/ __(_)\n\
55 \ / /_\\// /_/ / / | | GHC Interactive, version " ++ cProjectVersion ++ ", For Haskell 98.\n\
56 \/ /_\\\\/ __ / /___| | http://www.haskell.org/ghc/\n\
57 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
59 GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
61 builtin_commands :: [(String, String -> GHCi Bool)]
63 ("add", keepGoing addModule),
64 ("cd", keepGoing changeDirectory),
65 ("def", keepGoing defineMacro),
66 ("help", keepGoing help),
67 ("?", keepGoing help),
68 ("load", keepGoing loadModule),
69 ("module", keepGoing setContext),
70 ("reload", keepGoing reloadModule),
71 ("set", keepGoing setOptions),
72 ("type", keepGoing typeOfExpr),
73 ("unset", keepGoing unsetOptions),
74 ("undef", keepGoing undefineMacro),
78 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
79 keepGoing a str = a str >> return False
81 shortHelpText = "use :? for help.\n"
84 \ Commands available from the prompt:\n\
86 \ <stmt> evaluate/run <stmt>\n\
87 \ :cd <dir> change directory to <dir>\n\
88 \ :def <cmd> <expr> define a command :<cmd>\n\
89 \ :help, :? display this list of commands\n\
90 \ :load <filename> load a module (and it dependents)\n\
91 \ :module <mod> set the context for expression evaluation to <mod>\n\
92 \ :reload reload the current module set\n\
93 \ :set <option> ... set options\n\
94 \ :undef <name> undefine user-defined command :<name>\n\
95 \ :type <expr> show the type of <expr>\n\
96 \ :unset <option> ... unset options\n\
98 \ :!<command> run the shell command <command>\n\
100 \ Options for `:set' and `:unset':\n\
102 \ +r revert top-level expressions after each evaluation\n\
103 \ +s print timing/memory stats after each evaluation\n\
104 \ +t print type after evaluation\n\
105 \ -<flags> most GHC command line flags can also be set here\n\
106 \ (eg. -v2, -fglasgow-exts, etc.)\n\
108 --ToDo :add <filename> add a module to the current set\n\
110 interactiveUI :: CmState -> Maybe FilePath -> [LibrarySpec] -> 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
125 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
129 dflags <- getDynFlags
131 (cmstate, maybe_hval)
132 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stderr"
134 Just hval -> writeIORef flush_stderr (unsafeCoerce# hval :: IO ())
135 _ -> panic "interactiveUI:stderr"
137 (cmstate, maybe_hval)
138 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stdout"
140 Just hval -> writeIORef flush_stdout (unsafeCoerce# hval :: IO ())
141 _ -> panic "interactiveUI:stdout"
143 (unGHCi runGHCi) GHCiState{ target = mod,
152 dot_ghci <- io (IO.try (openFile "./.ghci" ReadMode))
155 Right hdl -> fileLoop hdl False
158 home <- io (IO.try (getEnv "HOME"))
162 cwd <- io (getCurrentDirectory)
163 when (dir /= cwd) $ do
164 dot_ghci <- io (IO.try (openFile (dir ++ "/.ghci") ReadMode))
167 Right hdl -> fileLoop hdl False
169 -- read commands from stdin
170 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
177 io $ do putStrLn "Leaving GHCi."
180 fileLoop :: Handle -> Bool -> GHCi ()
181 fileLoop hdl prompt = do
183 mod <- io (cmGetContext (cmstate st))
184 when prompt (io (hPutStr hdl (mod ++ "> ")))
185 l <- io (IO.try (hGetLine hdl))
187 Left e | isEOFError e -> return ()
188 | otherwise -> throw e
190 case remove_spaces l of
191 "" -> fileLoop hdl prompt
192 l -> do quit <- runCommand l
193 if quit then return () else fileLoop hdl prompt
195 stringLoop :: [String] -> GHCi ()
196 stringLoop [] = return ()
197 stringLoop (s:ss) = do
199 case remove_spaces s of
201 l -> do quit <- runCommand l
202 if quit then return () else stringLoop ss
204 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
205 readlineLoop :: GHCi ()
208 mod <- io (cmGetContext (cmstate st))
209 l <- io (readline (mod ++ "> "))
213 case remove_spaces l of
218 if quit then return () else readlineLoop
221 -- Top level exception handler, just prints out the exception
223 runCommand :: String -> GHCi Bool
225 ghciHandle ( \exception ->
228 case fromDynamic dyn of
229 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
232 PhaseFailed phase code ->
233 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
234 ++ show code ++ ")"))
235 Interrupted -> io (putStrLn "Interrupted.")
236 -- omit the location for CmdLineError
237 CmdLineError s -> io (putStrLn s)
238 other -> io (putStrLn (show (ghc_ex :: GhcException)))
240 other -> io (putStrLn ("*** Exception: " ++ show exception))
247 doCommand (':' : command) = specialCommand command
249 = do timeIt (do stuff <- runStmt stmt; finishEvalExpr stuff)
252 -- Returns True if the expr was successfully parsed, renamed and
254 runStmt :: String -> GHCi (Maybe [Name])
256 | null (filter (not.isSpace) stmt)
259 = do st <- getGHCiState
260 dflags <- io (getDynFlags)
261 let dflags' = dopt_unset dflags Opt_WarnUnusedBinds
262 (new_cmstate, names) <- io (cmRunStmt (cmstate st) dflags' stmt)
263 setGHCiState st{cmstate = new_cmstate}
266 -- possibly print the type and revert CAFs after evaluating an expression
267 finishEvalExpr Nothing = return False
268 finishEvalExpr (Just names)
269 = do b <- isOptionSet ShowType
271 when b (mapM_ (showTypeOfName (cmstate st)) names)
273 b <- isOptionSet RevertCAFs
274 io (when b revertCAFs)
278 showTypeOfName :: CmState -> Name -> GHCi ()
279 showTypeOfName cmstate n
280 = do maybe_str <- io (cmTypeOfName cmstate n)
283 Just str -> io (putStrLn (showSDoc (ppr n) ++ " :: " ++ str))
285 flushEverything :: GHCi ()
287 = io $ do flush_so <- readIORef flush_stdout
289 flush_se <- readIORef flush_stdout
293 specialCommand :: String -> GHCi Bool
294 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
295 specialCommand str = do
296 let (cmd,rest) = break isSpace str
297 cmds <- io (readIORef commands)
298 case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
299 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
300 ++ shortHelpText) >> return False)
301 [(_,f)] -> f (dropWhile isSpace rest)
302 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
303 " matches multiple commands (" ++
304 foldr1 (\a b -> a ++ ',':b) (map fst cs)
305 ++ ")") >> return False)
307 noArgs c = throwDyn (CmdLineError ("command `" ++ c ++ "' takes no arguments"))
309 -----------------------------------------------------------------------------
312 help :: String -> GHCi ()
313 help _ = io (putStr helpText)
315 addModule :: String -> GHCi ()
316 addModule _ = throwDyn (InstallationError ":add not implemented")
318 setContext :: String -> GHCi ()
320 = throwDyn (CmdLineError "syntax: `:m <module>'")
321 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
322 = throwDyn (CmdLineError ("strange looking module name: `" ++ m ++ "'"))
324 = do st <- getGHCiState
325 new_cmstate <- io (cmSetContext (cmstate st) str)
326 setGHCiState st{cmstate=new_cmstate}
328 changeDirectory :: String -> GHCi ()
329 changeDirectory ('~':d) = do
330 tilde <- io (getEnv "HOME") -- will fail if HOME not defined
331 io (setCurrentDirectory (tilde ++ '/':d))
332 changeDirectory d = io (setCurrentDirectory d)
334 defineMacro :: String -> GHCi ()
336 let (macro_name, definition) = break isSpace s
337 cmds <- io (readIORef commands)
339 then throwDyn (CmdLineError "invalid macro name")
341 if (macro_name `elem` map fst cmds)
342 then throwDyn (CmdLineError
343 ("command `" ++ macro_name ++ "' is already defined"))
346 -- give the expression a type signature, so we can be sure we're getting
347 -- something of the right type.
348 let new_expr = '(' : definition ++ ") :: String -> IO String"
350 -- compile the expression
352 dflags <- io (getDynFlags)
353 (new_cmstate, maybe_hv) <- io (cmCompileExpr (cmstate st) dflags new_expr)
354 setGHCiState st{cmstate = new_cmstate}
357 Just hv -> io (writeIORef commands --
358 ((macro_name, keepGoing (runMacro hv)) : cmds))
360 runMacro :: HValue{-String -> IO String-} -> String -> GHCi ()
362 str <- io ((unsafeCoerce# fun :: String -> IO String) s)
363 stringLoop (lines str)
365 undefineMacro :: String -> GHCi ()
366 undefineMacro macro_name = do
367 cmds <- io (readIORef commands)
368 if (macro_name `elem` map fst builtin_commands)
369 then throwDyn (CmdLineError
370 ("command `" ++ macro_name ++ "' cannot be undefined"))
372 if (macro_name `notElem` map fst cmds)
373 then throwDyn (CmdLineError
374 ("command `" ++ macro_name ++ "' not defined"))
376 io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
378 loadModule :: String -> GHCi ()
379 loadModule path = timeIt (loadModule' path)
381 loadModule' path = do
382 state <- getGHCiState
383 cmstate1 <- io (cmUnload (cmstate state))
384 io (revertCAFs) -- always revert CAFs on load.
385 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
386 let new_state = state{ cmstate = cmstate2,
389 setGHCiState new_state
390 modulesLoadedMsg ok mods
392 reloadModule :: String -> GHCi ()
394 state <- getGHCiState
396 Nothing -> io (putStr "no current target\n")
398 -> do io (revertCAFs) -- always revert CAFs on reload.
399 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
400 setGHCiState state{ cmstate=new_cmstate }
401 modulesLoadedMsg ok mods
403 reloadModule _ = noArgs ":reload"
406 modulesLoadedMsg ok mods = do
408 | null mods = text "none."
410 punctuate comma (map text mods)) <> text "."
413 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
415 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
418 typeOfExpr :: String -> GHCi ()
420 = do st <- getGHCiState
421 dflags <- io (getDynFlags)
422 (new_cmstate, maybe_tystr) <- io (cmTypeOfExpr (cmstate st) dflags str)
423 setGHCiState st{cmstate = new_cmstate}
426 Just tystr -> io (putStrLn tystr)
428 quit :: String -> GHCi Bool
431 shellEscape :: String -> GHCi Bool
432 shellEscape str = io (system str >> return False)
434 ----------------------------------------------------------------------------
437 -- set options in the interpreter. Syntax is exactly the same as the
438 -- ghc command line, except that certain options aren't available (-C,
441 -- This is pretty fragile: most options won't work as expected. ToDo:
442 -- figure out which ones & disallow them.
444 setOptions :: String -> GHCi ()
446 = do st <- getGHCiState
447 let opts = options st
448 io $ putStrLn (showSDoc (
449 text "options currently set: " <>
452 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
455 = do -- first, deal with the GHCi opts (+s, +t, etc.)
457 (minus_opts, rest1) = partition isMinus opts
458 (plus_opts, rest2) = partition isPlus rest1
460 if (not (null rest2))
461 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
464 mapM setOpt plus_opts
466 -- now, the GHC flags
467 io (do -- first, static flags
468 leftovers <- processArgs static_flags minus_opts []
470 -- then, dynamic flags
471 dyn_flags <- readIORef v_InitDynFlags
472 writeIORef v_DynFlags dyn_flags
473 leftovers <- processArgs dynamic_flags leftovers []
474 dyn_flags <- readIORef v_DynFlags
475 writeIORef v_InitDynFlags dyn_flags
477 if (not (null leftovers))
478 then throwDyn (CmdLineError ("unrecognised flags: " ++
483 unsetOptions :: String -> GHCi ()
485 = do -- first, deal with the GHCi opts (+s, +t, etc.)
487 (minus_opts, rest1) = partition isMinus opts
488 (plus_opts, rest2) = partition isPlus rest1
490 if (not (null rest2))
491 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
494 mapM unsetOpt plus_opts
496 -- can't do GHC flags for now
497 if (not (null minus_opts))
498 then throwDyn (CmdLineError "can't unset GHC command-line flags")
501 isMinus ('-':s) = True
504 isPlus ('+':s) = True
508 = case strToGHCiOpt str of
509 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
510 Just o -> setOption o
513 = case strToGHCiOpt str of
514 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
515 Just o -> unsetOption o
517 strToGHCiOpt :: String -> (Maybe GHCiOption)
518 strToGHCiOpt "s" = Just ShowTiming
519 strToGHCiOpt "t" = Just ShowType
520 strToGHCiOpt "r" = Just RevertCAFs
521 strToGHCiOpt _ = Nothing
523 optToStr :: GHCiOption -> String
524 optToStr ShowTiming = "s"
525 optToStr ShowType = "t"
526 optToStr RevertCAFs = "r"
528 -----------------------------------------------------------------------------
531 data GHCiState = GHCiState
533 target :: Maybe FilePath,
535 options :: [GHCiOption]
539 = ShowTiming -- show time/allocs after evaluation
540 | ShowType -- show the type of expressions
541 | RevertCAFs -- revert CAFs after every evaluation
544 GLOBAL_VAR(flush_stdout, error "no flush_stdout", IO ())
545 GLOBAL_VAR(flush_stderr, error "no flush_stdout", IO ())
547 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
549 instance Monad GHCi where
550 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
551 return a = GHCi $ \s -> return (s,a)
553 getGHCiState = GHCi $ \s -> return (s,s)
554 setGHCiState s = GHCi $ \_ -> return (s,())
556 isOptionSet :: GHCiOption -> GHCi Bool
558 = do st <- getGHCiState
559 return (opt `elem` options st)
561 setOption :: GHCiOption -> GHCi ()
563 = do st <- getGHCiState
564 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
566 unsetOption :: GHCiOption -> GHCi ()
568 = do st <- getGHCiState
569 setGHCiState (st{ options = filter (/= opt) (options st) })
571 io m = GHCi $ \s -> m >>= \a -> return (s,a)
573 -----------------------------------------------------------------------------
574 -- recursive exception handlers
576 -- Don't forget to unblock async exceptions in the handler, or if we're
577 -- in an exception loop (eg. let a = error a in a) the ^C exception
578 -- may never be delivered. Thanks to Marcin for pointing out the bug.
580 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
581 ghciHandle h (GHCi m) = GHCi $ \s ->
582 Exception.catch (m s)
583 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
585 ghciUnblock :: GHCi a -> GHCi a
586 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
588 -----------------------------------------------------------------------------
591 -- Left: full path name of a .o file, including trailing .o
592 -- Right: "unadorned" name of a .DLL/.so
593 -- e.g. On unix "qt" denotes "libqt.so"
594 -- On WinDoze "burble" denotes "burble.DLL"
595 -- addDLL is platform-specific and adds the lib/.so/.DLL
596 -- prefixes plaform-dependently; we don't do that here.
598 = Either FilePath String
600 showLS (Left nm) = "(static) " ++ nm
601 showLS (Right nm) = "(dynamic) " ++ nm
603 linkPackages :: [LibrarySpec] -> [PackageConfig] -> IO ()
604 linkPackages cmdline_lib_specs pkgs
605 = do mapM_ linkPackage pkgs
606 mapM_ preloadLib cmdline_lib_specs
609 = do putStr ("Loading object " ++ showLS lib_spec ++ " ... ")
612 -> do b <- doesFileExist static_ish
614 then do putStr "not found.\n"
616 else do loadObj static_ish
619 -> do maybe_errmsg <- addDLL dll_unadorned
620 if maybe_errmsg == nullPtr
621 then putStr "done.\n"
622 else do str <- peekCString maybe_errmsg
623 putStr ("failed (" ++ str ++ ")\n")
626 croak = throwDyn (CmdLineError "user specified .o/.so/.DLL could not be loaded.")
629 linkPackage :: PackageConfig -> IO ()
630 -- ignore rts and gmp for now (ToDo; better?)
632 | name pkg `elem` ["rts", "gmp"]
635 = do putStr ("Loading package " ++ name pkg ++ " ... ")
636 -- For each obj, try obj.o and if that fails, obj.so.
637 -- Complication: all the .so's must be loaded before any of the .o's.
638 let dirs = library_dirs pkg
639 let objs = hs_libraries pkg ++ extra_libraries pkg
640 classifieds <- mapM (locateOneObj dirs) objs
641 let sos_first = filter isRight classifieds
642 ++ filter (not.isRight) classifieds
643 mapM loadClassified sos_first
644 putStr "linking ... "
648 isRight (Right _) = True
649 isRight (Left _) = False
651 loadClassified :: LibrarySpec -> IO ()
652 loadClassified (Left obj_absolute_filename)
653 = do loadObj obj_absolute_filename
654 loadClassified (Right dll_unadorned)
655 = do maybe_errmsg <- addDLL dll_unadorned
656 if maybe_errmsg == nullPtr
658 else do str <- peekCString maybe_errmsg
659 throwDyn (CmdLineError ("can't find .o or .so/.DLL for: "
660 ++ dll_unadorned ++ " (" ++ str ++ ")" ))
662 locateOneObj :: [FilePath] -> String -> IO LibrarySpec
664 = return (Right obj) -- we assume
665 locateOneObj (d:ds) obj
666 = do let path = d ++ '/':obj ++ ".o"
667 b <- doesFileExist path
668 if b then return (Left path) else locateOneObj ds obj
670 -----------------------------------------------------------------------------
671 -- timing & statistics
673 timeIt :: GHCi a -> GHCi a
675 = do b <- isOptionSet ShowTiming
678 else do allocs1 <- io $ getAllocations
679 time1 <- io $ getCPUTime
681 allocs2 <- io $ getAllocations
682 time2 <- io $ getCPUTime
683 io $ printTimes (allocs2 - allocs1) (time2 - time1)
686 foreign import "getAllocations" getAllocations :: IO Int
688 printTimes :: Int -> Integer -> IO ()
689 printTimes allocs psecs
690 = do let secs = (fromIntegral psecs / (10^12)) :: Float
691 secs_str = showFFloat (Just 2) secs
693 parens (text (secs_str "") <+> text "secs" <> comma <+>
694 int allocs <+> text "bytes")))
696 -----------------------------------------------------------------------------
699 foreign import revertCAFs :: IO () -- make it "safe", just in case