1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.58 2001/03/27 16:55:03 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 "HsVersions.h"
25 import CmdLineOpts ( DynFlag(..), dopt_unset )
26 import Panic ( GhcException(..) )
45 import PrelGHC ( unsafeCoerce# )
46 import Foreign ( nullPtr )
47 import CString ( peekCString )
49 -----------------------------------------------------------------------------
53 \ / _ \\ /\\ /\\/ __(_)\n\
54 \ / /_\\// /_/ / / | | GHC Interactive, version " ++ cProjectVersion ++ ", For Haskell 98.\n\
55 \/ /_\\\\/ __ / /___| | http://www.haskell.org/ghc/\n\
56 \\\____/\\/ /_/\\____/|_| Type :? for help.\n"
58 GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
60 builtin_commands :: [(String, String -> GHCi Bool)]
62 ("add", keepGoing addModule),
63 ("cd", keepGoing changeDirectory),
64 ("def", keepGoing defineMacro),
65 ("help", keepGoing help),
66 ("?", keepGoing help),
67 ("load", keepGoing loadModule),
68 ("module", keepGoing setContext),
69 ("reload", keepGoing reloadModule),
70 ("set", keepGoing setOptions),
71 ("type", keepGoing typeOfExpr),
72 ("unset", keepGoing unsetOptions),
73 ("undef", keepGoing undefineMacro),
77 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
78 keepGoing a str = a str >> return False
80 shortHelpText = "use :? for help.\n"
83 \ Commands available from the prompt:\n\
85 \ <stmt> evaluate/run <stmt>\n\
86 \ :cd <dir> change directory to <dir>\n\
87 \ :def <cmd> <expr> define a command :<cmd>\n\
88 \ :help, :? display this list of commands\n\
89 \ :load <filename> load a module (and it dependents)\n\
90 \ :module <mod> set the context for expression evaluation to <mod>\n\
91 \ :reload reload the current module set\n\
92 \ :set <option> ... set options\n\
93 \ :undef <name> undefine user-defined command :<name>\n\
94 \ :type <expr> show the type of <expr>\n\
95 \ :unset <option> ... unset options\n\
97 \ :!<command> run the shell command <command>\n\
99 \ Options for `:set' and `:unset':\n\
101 \ +r revert top-level expressions after each evaluation\n\
102 \ +s print timing/memory stats after each evaluation\n\
103 \ +t print type after evaluation\n\
104 \ -<flags> most GHC command line flags can also be set here\n\
105 \ (eg. -v2, -fglasgow-exts, etc.)\n\
107 --ToDo :add <filename> add a module to the current set\n\
109 interactiveUI :: CmState -> Maybe FilePath -> [LibrarySpec] -> IO ()
110 interactiveUI cmstate mod cmdline_libs = do
112 hSetBuffering stdout NoBuffering
114 -- link in the available packages
115 pkgs <- getPackageInfo
117 linkPackages cmdline_libs (reverse pkgs)
119 (cmstate, ok, mods) <-
121 Nothing -> return (cmstate, True, [])
122 Just m -> cmLoadModule cmstate m
128 dflags <- getDynFlags
130 (cmstate, maybe_hval)
131 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stderr"
133 Just hval -> writeIORef flush_stderr (unsafeCoerce# hval :: IO ())
134 _ -> panic "interactiveUI:stderr"
136 (cmstate, maybe_hval)
137 <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stdout"
139 Just hval -> writeIORef flush_stdout (unsafeCoerce# hval :: IO ())
140 _ -> panic "interactiveUI:stdout"
142 (unGHCi runGHCi) GHCiState{ target = mod,
144 options = [ShowTiming] }
151 dot_ghci <- io (IO.try (openFile "./.ghci" ReadMode))
154 Right hdl -> fileLoop hdl False
157 home <- io (IO.try (getEnv "HOME"))
161 cwd <- io (getCurrentDirectory)
162 when (dir /= cwd) $ do
163 dot_ghci <- io (IO.try (openFile (dir ++ "/.ghci") ReadMode))
166 Right hdl -> fileLoop hdl False
168 -- read commands from stdin
176 io $ do putStrLn "Leaving GHCi."
179 fileLoop :: Handle -> Bool -> GHCi ()
180 fileLoop hdl prompt = do
182 mod <- io (cmGetContext (cmstate st))
183 when prompt (io (hPutStr hdl (mod ++ "> ")))
184 l <- io (IO.try (hGetLine hdl))
186 Left e | isEOFError e -> return ()
187 | otherwise -> throw e
189 case remove_spaces l of
190 "" -> fileLoop hdl prompt
191 l -> do quit <- runCommand l
192 if quit then return () else fileLoop hdl prompt
194 stringLoop :: [String] -> GHCi ()
195 stringLoop [] = return ()
196 stringLoop (s:ss) = do
198 case remove_spaces s of
200 l -> do quit <- runCommand l
201 if quit then return () else stringLoop ss
204 readlineLoop :: GHCi ()
207 mod <- io (cmGetContext (cmstate st))
208 l <- io (readline (mod ++ "> "))
212 case remove_spaces l of
217 if quit then return () else readlineLoop
220 -- Top level exception handler, just prints out the exception
222 runCommand :: String -> GHCi Bool
224 ghciHandle ( \exception ->
227 case fromDynamic dyn of
228 Nothing -> io (putStrLn ("*** Exception: (unknown)"))
231 PhaseFailed phase code ->
232 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
233 ++ show code ++ ")"))
234 Interrupted -> io (putStrLn "Interrupted.")
235 other -> io (putStrLn (show (ghc_ex :: GhcException)))
237 other -> io (putStrLn ("*** Exception: " ++ show exception))
244 doCommand (':' : command) = specialCommand command
246 = do timeIt (do stuff <- runStmt stmt; finishEvalExpr stuff)
249 -- Returns True if the expr was successfully parsed, renamed and
251 runStmt :: String -> GHCi (Maybe [Name])
253 | null (filter (not.isSpace) stmt)
256 = do st <- getGHCiState
257 dflags <- io (getDynFlags)
258 let dflags' = dopt_unset dflags Opt_WarnUnusedBinds
259 (new_cmstate, names) <- io (cmRunStmt (cmstate st) dflags' stmt)
260 setGHCiState st{cmstate = new_cmstate}
263 -- possibly print the type and revert CAFs after evaluating an expression
264 finishEvalExpr Nothing = return False
265 finishEvalExpr (Just names)
266 = do b <- isOptionSet ShowType
268 when b (mapM_ (showTypeOfName (cmstate st)) names)
270 b <- isOptionSet RevertCAFs
271 io (when b revertCAFs)
275 showTypeOfName :: CmState -> Name -> GHCi ()
276 showTypeOfName cmstate n
277 = do maybe_str <- io (cmTypeOfName cmstate n)
280 Just str -> io (putStrLn (showSDoc (ppr n) ++ " :: " ++ str))
282 flushEverything :: GHCi ()
284 = io $ do flush_so <- readIORef flush_stdout
286 flush_se <- readIORef flush_stdout
290 specialCommand :: String -> GHCi Bool
291 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
292 specialCommand str = do
293 let (cmd,rest) = break isSpace str
294 cmds <- io (readIORef commands)
295 case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
296 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
297 ++ shortHelpText) >> return False)
298 [(_,f)] -> f (dropWhile isSpace rest)
299 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
300 " matches multiple commands (" ++
301 foldr1 (\a b -> a ++ ',':b) (map fst cs)
302 ++ ")") >> return False)
304 noArgs c = throwDyn (OtherError ("command `" ++ c ++ "' takes no arguments"))
306 -----------------------------------------------------------------------------
309 help :: String -> GHCi ()
310 help _ = io (putStr helpText)
312 addModule :: String -> GHCi ()
313 addModule _ = throwDyn (OtherError ":add not implemented")
315 setContext :: String -> GHCi ()
317 = throwDyn (OtherError "syntax: `:m <module>'")
318 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
319 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
321 = do st <- getGHCiState
322 new_cmstate <- io (cmSetContext (cmstate st) str)
323 setGHCiState st{cmstate=new_cmstate}
325 changeDirectory :: String -> GHCi ()
326 changeDirectory ('~':d) = do
327 tilde <- io (getEnv "HOME") -- will fail if HOME not defined
328 io (setCurrentDirectory (tilde ++ '/':d))
329 changeDirectory d = io (setCurrentDirectory d)
331 defineMacro :: String -> GHCi ()
333 let (macro_name, definition) = break isSpace s
334 cmds <- io (readIORef commands)
336 then throwDyn (OtherError "invalid macro name")
338 if (macro_name `elem` map fst cmds)
339 then throwDyn (OtherError
340 ("command `" ++ macro_name ++ "' is already defined"))
343 -- give the expression a type signature, so we can be sure we're getting
344 -- something of the right type.
345 let new_expr = '(' : definition ++ ") :: String -> IO String"
347 -- compile the expression
349 dflags <- io (getDynFlags)
350 (new_cmstate, maybe_hv) <- io (cmCompileExpr (cmstate st) dflags new_expr)
351 setGHCiState st{cmstate = new_cmstate}
354 Just hv -> io (writeIORef commands --
355 ((macro_name, keepGoing (runMacro hv)) : cmds))
357 runMacro :: HValue{-String -> IO String-} -> String -> GHCi ()
359 str <- io ((unsafeCoerce# fun :: String -> IO String) s)
360 stringLoop (lines str)
362 undefineMacro :: String -> GHCi ()
363 undefineMacro macro_name = do
364 cmds <- io (readIORef commands)
365 if (macro_name `elem` map fst builtin_commands)
366 then throwDyn (OtherError
367 ("command `" ++ macro_name ++ "' cannot be undefined"))
369 if (macro_name `notElem` map fst cmds)
370 then throwDyn (OtherError
371 ("command `" ++ macro_name ++ "' not defined"))
373 io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
375 loadModule :: String -> GHCi ()
376 loadModule path = timeIt (loadModule' path)
378 loadModule' path = do
379 state <- getGHCiState
380 cmstate1 <- io (cmUnload (cmstate state))
381 io (revertCAFs) -- always revert CAFs on load.
382 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
383 let new_state = state{ cmstate = cmstate2,
386 setGHCiState new_state
387 modulesLoadedMsg ok mods
389 reloadModule :: String -> GHCi ()
391 state <- getGHCiState
393 Nothing -> io (putStr "no current target\n")
395 -> do io (revertCAFs) -- always revert CAFs on reload.
396 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
397 setGHCiState state{ cmstate=new_cmstate }
398 modulesLoadedMsg ok mods
400 reloadModule _ = noArgs ":reload"
403 modulesLoadedMsg ok mods = do
405 | null mods = text "none."
407 punctuate comma (map text mods)) <> text "."
410 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
412 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
415 typeOfExpr :: String -> GHCi ()
417 = do st <- getGHCiState
418 dflags <- io (getDynFlags)
419 (new_cmstate, maybe_tystr) <- io (cmTypeOfExpr (cmstate st) dflags str)
420 setGHCiState st{cmstate = new_cmstate}
423 Just tystr -> io (putStrLn tystr)
425 quit :: String -> GHCi Bool
428 shellEscape :: String -> GHCi Bool
429 shellEscape str = io (system str >> return False)
431 ----------------------------------------------------------------------------
434 -- set options in the interpreter. Syntax is exactly the same as the
435 -- ghc command line, except that certain options aren't available (-C,
438 -- This is pretty fragile: most options won't work as expected. ToDo:
439 -- figure out which ones & disallow them.
441 setOptions :: String -> GHCi ()
443 = do st <- getGHCiState
444 let opts = options st
445 io $ putStrLn (showSDoc (
446 text "options currently set: " <>
449 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
452 = do -- first, deal with the GHCi opts (+s, +t, etc.)
454 (minus_opts, rest1) = partition isMinus opts
455 (plus_opts, rest2) = partition isPlus rest1
457 if (not (null rest2))
458 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
461 mapM setOpt plus_opts
463 -- now, the GHC flags
464 io (do -- first, static flags
465 leftovers <- processArgs static_flags minus_opts []
467 -- then, dynamic flags
468 dyn_flags <- readIORef v_InitDynFlags
469 writeIORef v_DynFlags dyn_flags
470 leftovers <- processArgs dynamic_flags leftovers []
471 dyn_flags <- readIORef v_DynFlags
472 writeIORef v_InitDynFlags dyn_flags
474 if (not (null leftovers))
475 then throwDyn (OtherError ("unrecognised flags: " ++
480 unsetOptions :: String -> GHCi ()
482 = do -- first, deal with the GHCi opts (+s, +t, etc.)
484 (minus_opts, rest1) = partition isMinus opts
485 (plus_opts, rest2) = partition isPlus rest1
487 if (not (null rest2))
488 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
491 mapM unsetOpt plus_opts
493 -- can't do GHC flags for now
494 if (not (null minus_opts))
495 then throwDyn (OtherError "can't unset GHC command-line flags")
498 isMinus ('-':s) = True
501 isPlus ('+':s) = True
505 = case strToGHCiOpt str of
506 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
507 Just o -> setOption o
510 = case strToGHCiOpt str of
511 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
512 Just o -> unsetOption o
514 strToGHCiOpt :: String -> (Maybe GHCiOption)
515 strToGHCiOpt "s" = Just ShowTiming
516 strToGHCiOpt "t" = Just ShowType
517 strToGHCiOpt "r" = Just RevertCAFs
518 strToGHCiOpt _ = Nothing
520 optToStr :: GHCiOption -> String
521 optToStr ShowTiming = "s"
522 optToStr ShowType = "t"
523 optToStr RevertCAFs = "r"
525 -----------------------------------------------------------------------------
528 data GHCiState = GHCiState
530 target :: Maybe FilePath,
532 options :: [GHCiOption]
536 = ShowTiming -- show time/allocs after evaluation
537 | ShowType -- show the type of expressions
538 | RevertCAFs -- revert CAFs after every evaluation
541 GLOBAL_VAR(flush_stdout, error "no flush_stdout", IO ())
542 GLOBAL_VAR(flush_stderr, error "no flush_stdout", IO ())
544 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
546 instance Monad GHCi where
547 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
548 return a = GHCi $ \s -> return (s,a)
550 getGHCiState = GHCi $ \s -> return (s,s)
551 setGHCiState s = GHCi $ \_ -> return (s,())
553 isOptionSet :: GHCiOption -> GHCi Bool
555 = do st <- getGHCiState
556 return (opt `elem` options st)
558 setOption :: GHCiOption -> GHCi ()
560 = do st <- getGHCiState
561 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
563 unsetOption :: GHCiOption -> GHCi ()
565 = do st <- getGHCiState
566 setGHCiState (st{ options = filter (/= opt) (options st) })
568 io m = GHCi $ \s -> m >>= \a -> return (s,a)
570 -----------------------------------------------------------------------------
571 -- recursive exception handlers
573 -- Don't forget to unblock async exceptions in the handler, or if we're
574 -- in an exception loop (eg. let a = error a in a) the ^C exception
575 -- may never be delivered. Thanks to Marcin for pointing out the bug.
577 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
578 ghciHandle h (GHCi m) = GHCi $ \s ->
579 Exception.catch (m s)
580 (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
582 ghciUnblock :: GHCi a -> GHCi a
583 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
585 -----------------------------------------------------------------------------
588 -- Left: full path name of a .o file, including trailing .o
589 -- Right: "unadorned" name of a .DLL/.so
590 -- e.g. On unix "qt" denotes "libqt.so"
591 -- On WinDoze "burble" denotes "burble.DLL"
592 -- addDLL is platform-specific and adds the lib/.so/.DLL
593 -- prefixes plaform-dependently; we don't do that here.
595 = Either FilePath String
597 showLS (Left nm) = "(static) " ++ nm
598 showLS (Right nm) = "(dynamic) " ++ nm
600 linkPackages :: [LibrarySpec] -> [PackageConfig] -> IO ()
601 linkPackages cmdline_lib_specs pkgs
602 = do mapM_ linkPackage pkgs
603 mapM_ preloadLib cmdline_lib_specs
606 = do putStr ("Loading object " ++ showLS lib_spec ++ " ... ")
609 -> do b <- doesFileExist static_ish
611 then do putStr "not found.\n"
613 else do loadObj static_ish
616 -> do maybe_errmsg <- addDLL dll_unadorned
617 if maybe_errmsg == nullPtr
618 then putStr "done.\n"
619 else do str <- peekCString maybe_errmsg
620 putStr ("failed (" ++ str ++ ")\n")
623 croak = throwDyn (OtherError "user specified .o/.so/.DLL could not be loaded.")
626 linkPackage :: PackageConfig -> IO ()
627 -- ignore rts and gmp for now (ToDo; better?)
629 | name pkg `elem` ["rts", "gmp"]
632 = do putStr ("Loading package " ++ name pkg ++ " ... ")
633 -- For each obj, try obj.o and if that fails, obj.so.
634 -- Complication: all the .so's must be loaded before any of the .o's.
635 let dirs = library_dirs pkg
636 let objs = hs_libraries pkg ++ extra_libraries pkg
637 classifieds <- mapM (locateOneObj dirs) objs
638 let sos_first = filter isRight classifieds
639 ++ filter (not.isRight) classifieds
640 mapM loadClassified sos_first
641 putStr "linking ... "
645 isRight (Right _) = True
646 isRight (Left _) = False
648 loadClassified :: LibrarySpec -> IO ()
649 loadClassified (Left obj_absolute_filename)
650 = do loadObj obj_absolute_filename
651 loadClassified (Right dll_unadorned)
652 = do maybe_errmsg <- addDLL dll_unadorned
653 if maybe_errmsg == nullPtr
655 else do str <- peekCString maybe_errmsg
656 throwDyn (OtherError ("can't find .o or .so/.DLL for: "
657 ++ dll_unadorned ++ " (" ++ str ++ ")" ))
659 locateOneObj :: [FilePath] -> String -> IO LibrarySpec
661 = return (Right obj) -- we assume
662 locateOneObj (d:ds) obj
663 = do let path = d ++ '/':obj ++ ".o"
664 b <- doesFileExist path
665 if b then return (Left path) else locateOneObj ds obj
667 -----------------------------------------------------------------------------
668 -- timing & statistics
670 timeIt :: GHCi a -> GHCi a
672 = do b <- isOptionSet ShowTiming
675 else do allocs1 <- io $ getAllocations
676 time1 <- io $ getCPUTime
678 allocs2 <- io $ getAllocations
679 time2 <- io $ getCPUTime
680 io $ printTimes (allocs2 - allocs1) (time2 - time1)
683 foreign import "getAllocations" getAllocations :: IO Int
685 printTimes :: Int -> Integer -> IO ()
686 printTimes allocs psecs
687 = do let secs = (fromIntegral psecs / (10^12)) :: Float
688 secs_str = showFFloat (Just 2) secs
690 parens (text (secs_str "") <+> text "secs" <> comma <+>
691 int allocs <+> text "bytes")))
693 -----------------------------------------------------------------------------
696 foreign import revertCAFs :: IO () -- make it "safe", just in case