1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.35 2001/02/08 14:58:28 simonmar Exp $
4 -- GHC Interactive User Interface
6 -- (c) The GHC Team 2000
8 -----------------------------------------------------------------------------
10 module InteractiveUI (interactiveUI) where
12 #include "HsVersions.h"
26 import PprType {- instance Outputable Type; do not delete -}
27 import Panic ( GhcException(..) )
45 -----------------------------------------------------------------------------
48 \ _____ __ __ ____ _________________________________________________\n\
49 \(| || || (| |) GHC Interactive, version 5.00 \n\
50 \|| __ ||___|| || () For Haskell 98. \n\
51 \|| |) ||---|| || || http://www.haskell.org/ghc \n\
52 \|| || || || || (| Bug reports to: glasgow-haskell-bugs@haskell.org \n\
53 \(|___|| || || (|__|) \\\\______________________________________________________\n"
55 commands :: [(String, String -> GHCi Bool)]
57 ("add", keepGoing addModule),
58 ("cd", keepGoing changeDirectory),
59 ("help", keepGoing help),
60 ("?", keepGoing help),
61 ("load", keepGoing loadModule),
62 ("module", keepGoing setContext),
63 ("reload", keepGoing reloadModule),
64 ("set", keepGoing setOptions),
65 ("type", keepGoing typeOfExpr),
66 ("unset", keepGoing unsetOptions),
70 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
71 keepGoing a str = a str >> return False
73 shortHelpText = "use :? for help.\n"
76 \ Commands available from the prompt:\n\
78 \ <expr> evaluate <expr>\n\
79 \ :add <filename> add a module to the current set\n\
80 \ :cd <dir> change directory to <dir>\n\
81 \ :help, :? display this list of commands\n\
82 \ :load <filename> load a module (and it dependents)\n\
83 \ :module <mod> set the context for expression evaluation to <mod>\n\
84 \ :reload reload the current module set\n\
85 \ :set <option> ... set options\n\
86 \ :unset <option> ... unset options\n\
87 \ :type <expr> show the type of <expr>\n\
89 \ :!<command> run the shell command <command>\n\
91 \ Options for `:set' and `:unset':\n\
93 \ +s print timing/memory stats after each evaluation\n\
94 \ +t print type after evaluation\n\
95 \ +r revert top-level expressions after each evaluation\n\
96 \ -<flags> most GHC command line flags can also be set here\n\
97 \ (eg. -v2, -fglasgow-exts, etc.)\n\
100 interactiveUI :: CmState -> Maybe FilePath -> IO ()
101 interactiveUI cmstate mod = do
102 hPutStrLn stdout ghciWelcomeMsg
104 hSetBuffering stdout NoBuffering
106 -- link in the available packages
107 pkgs <- getPackageInfo
108 linkPackages (reverse pkgs)
110 (cmstate, ok, mods) <-
112 Nothing -> return (cmstate, True, [])
113 Just m -> cmLoadModule cmstate m
119 prel <- moduleNameToModule defaultCurrentModuleName
120 writeIORef defaultCurrentModule prel
122 dflags <- getDynFlags
124 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
125 "PrelHandle.hFlush PrelHandle.stdout"
128 Just (hv,_,_) -> writeIORef flush_stdout hv
130 (cmstate, maybe_stuff) <- cmGetExpr cmstate dflags False prel
131 "PrelHandle.hFlush PrelHandle.stdout"
134 Just (hv,_,_) -> writeIORef flush_stderr hv
136 let this_mod = case mods of
140 (unGHCi runGHCi) GHCiState{ modules = mods,
141 current_module = this_mod,
144 options = [ShowTiming],
152 dot_ghci <- io (IO.try (openFile "./.ghci" ReadMode))
155 Right hdl -> fileLoop hdl False
158 home <- io (IO.try (getEnv "HOME"))
162 dot_ghci <- io (IO.try (openFile (dir ++ "/.ghci") ReadMode))
165 Right hdl -> fileLoop hdl False
167 -- read commands from stdin
175 io $ do putStrLn "Leaving GHCi."
178 fileLoop :: Handle -> Bool -> GHCi ()
179 fileLoop hdl prompt = do
181 when prompt (io (hPutStr hdl (moduleUserString (current_module st) ++ "> ")))
182 l <- io (IO.try (hGetLine hdl))
184 Left e | isEOFError e -> return ()
185 | otherwise -> throw e
187 case remove_spaces l of
188 "" -> fileLoop hdl prompt
189 l -> do quit <- runCommand l
190 if quit then return () else fileLoop hdl prompt
193 readlineLoop :: GHCi ()
196 l <- io (readline (moduleUserString (current_module st) ++ "> "))
200 case remove_spaces l of
205 if quit then return () else readlineLoop
208 -- Top level exception handler, just prints out the exception
210 runCommand :: String -> GHCi Bool
214 -> io (do putStrLn ("*** Exception: " ++ show other_exception)
219 PhaseFailed phase code ->
220 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
221 ++ show code ++ ")"))
222 Interrupted -> io (putStrLn "Interrupted.")
223 _ -> io (putStrLn (show (dyn :: GhcException)))
228 doCommand (':' : command) = specialCommand command
229 doCommand ('-':'-':_) = return False -- comments, useful in scripts
231 = do expr_expanded <- expandExpr expr
232 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
233 expr_ok <- timeIt (do stuff <- evalExpr expr_expanded
234 finishEvalExpr stuff)
235 when expr_ok (rememberExpr expr_expanded)
238 -- possibly print the type and revert CAFs after evaluating an expression
239 finishEvalExpr Nothing = return False
240 finishEvalExpr (Just (unqual,ty))
241 = do b <- isOptionSet ShowType
242 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
243 b <- isOptionSet RevertCAFs
244 io (when b revertCAFs)
247 -- Returned Bool indicates whether or not the expr was successfully
248 -- parsed, renamed and typechecked.
249 evalExpr :: String -> GHCi (Maybe (PrintUnqualified,Type))
251 | null (filter (not.isSpace) expr)
254 = do st <- getGHCiState
255 dflags <- io (getDynFlags)
256 (new_cmstate, maybe_stuff) <-
257 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
258 setGHCiState st{cmstate = new_cmstate}
260 Nothing -> return Nothing
261 Just (hv, unqual, ty) -> do io (cmRunExpr hv)
263 return (Just (unqual,ty))
265 flushEverything :: GHCi ()
267 = io $ do flush_so <- readIORef flush_stdout
269 flush_se <- readIORef flush_stdout
272 specialCommand :: String -> GHCi Bool
273 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
274 specialCommand str = do
275 let (cmd,rest) = break isSpace str
276 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
277 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
278 ++ shortHelpText) >> return False)
279 [(_,f)] -> f (dropWhile isSpace rest)
280 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
281 " matches multiple commands (" ++
282 foldr1 (\a b -> a ++ ',':b) (map fst cs)
283 ++ ")") >> return False)
285 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
287 -----------------------------------------------------------------------------
290 help :: String -> GHCi ()
291 help _ = io (putStr helpText)
293 addModule :: String -> GHCi ()
294 addModule _ = throwDyn (OtherError ":add not implemented")
296 setContext :: String -> GHCi ()
298 = throwDyn (OtherError "syntax: `:m <module>'")
299 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
300 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
302 = do m <- io (moduleNameToModule (mkModuleName mn))
304 if (isHomeModule m && m `notElem` modules st)
305 then throwDyn (OtherError (showSDoc (quotes (ppr (moduleName m))
306 <+> text "is not currently loaded, use :load")))
307 else setGHCiState st{current_module = m}
309 moduleNameToModule :: ModuleName -> IO Module
310 moduleNameToModule mn
311 = do maybe_stuff <- findModule mn
313 Nothing -> throwDyn (OtherError ("can't find module `"
314 ++ moduleNameUserString mn ++ "'"))
315 Just (m,_) -> return m
317 changeDirectory :: String -> GHCi ()
318 changeDirectory d = io (setCurrentDirectory d)
320 loadModule :: String -> GHCi ()
321 loadModule path = timeIt (loadModule' path)
323 loadModule' path = do
324 state <- getGHCiState
325 cmstate1 <- io (cmUnload (cmstate state))
326 io (revertCAFs) -- always revert CAFs on load.
327 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
329 def_mod <- io (readIORef defaultCurrentModule)
331 let new_state = state{
334 current_module = case mods of
339 setGHCiState new_state
342 | null mods = text "none."
344 punctuate comma (map (text.moduleUserString) mods)) <> text "."
347 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
349 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
351 reloadModule :: String -> GHCi ()
353 state <- getGHCiState
355 Nothing -> io (putStr "no current target\n")
357 -> do io (revertCAFs) -- always revert CAFs on reload.
358 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
359 def_mod <- io (readIORef defaultCurrentModule)
361 state{cmstate=new_cmstate,
363 current_module = case mods of
368 reloadModule _ = noArgs ":reload"
370 typeOfExpr :: String -> GHCi ()
372 = do st <- getGHCiState
373 dflags <- io (getDynFlags)
374 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
375 (current_module st) str)
376 setGHCiState st{cmstate = new_cmstate}
379 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
381 quit :: String -> GHCi Bool
384 shellEscape :: String -> GHCi Bool
385 shellEscape str = io (system str >> return False)
387 ----------------------------------------------------------------------------
390 -- set options in the interpreter. Syntax is exactly the same as the
391 -- ghc command line, except that certain options aren't available (-C,
394 -- This is pretty fragile: most options won't work as expected. ToDo:
395 -- figure out which ones & disallow them.
397 setOptions :: String -> GHCi ()
399 = do st <- getGHCiState
400 let opts = options st
401 io $ putStrLn (showSDoc (
402 text "options currently set: " <>
405 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
408 = do -- first, deal with the GHCi opts (+s, +t, etc.)
410 (minus_opts, rest1) = partition isMinus opts
411 (plus_opts, rest2) = partition isPlus rest1
413 if (not (null rest2))
414 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
417 mapM setOpt plus_opts
419 -- now, the GHC flags
420 io (do -- first, static flags
421 leftovers <- processArgs static_flags minus_opts []
423 -- then, dynamic flags
424 dyn_flags <- readIORef v_InitDynFlags
425 writeIORef v_DynFlags dyn_flags
426 leftovers <- processArgs dynamic_flags leftovers []
427 dyn_flags <- readIORef v_DynFlags
428 writeIORef v_InitDynFlags dyn_flags
430 if (not (null leftovers))
431 then throwDyn (OtherError ("unrecognised flags: " ++
436 unsetOptions :: String -> GHCi ()
438 = do -- first, deal with the GHCi opts (+s, +t, etc.)
440 (minus_opts, rest1) = partition isMinus opts
441 (plus_opts, rest2) = partition isPlus rest1
443 if (not (null rest2))
444 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
447 mapM unsetOpt plus_opts
449 -- can't do GHC flags for now
450 if (not (null minus_opts))
451 then throwDyn (OtherError "can't unset GHC command-line flags")
454 isMinus ('-':s) = True
457 isPlus ('+':s) = True
461 = case strToGHCiOpt str of
462 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
463 Just o -> setOption o
466 = case strToGHCiOpt str of
467 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
468 Just o -> unsetOption o
470 strToGHCiOpt :: String -> (Maybe GHCiOption)
471 strToGHCiOpt "s" = Just ShowTiming
472 strToGHCiOpt "t" = Just ShowType
473 strToGHCiOpt "r" = Just RevertCAFs
474 strToGHCiOpt _ = Nothing
476 optToStr :: GHCiOption -> String
477 optToStr ShowTiming = "s"
478 optToStr ShowType = "t"
479 optToStr RevertCAFs = "r"
481 -----------------------------------------------------------------------------
482 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
484 -- Take a string and replace $$s in it with the last expr, if any.
485 expandExpr :: String -> GHCi String
487 = do mle <- getLastExpr
488 return (outside mle str)
490 outside mle ('$':'$':cs)
492 Just le -> " (" ++ le ++ ") " ++ outside mle cs
493 Nothing -> outside mle cs
496 outside mle ('"':str) = '"' : inside2 mle str -- "
497 outside mle ('\'':str) = '\'' : inside1 mle str -- '
498 outside mle (c:cs) = c : outside mle cs
500 inside2 mle ('"':cs) = '"' : outside mle cs -- "
501 inside2 mle (c:cs) = c : inside2 mle cs
504 inside1 mle ('\'':cs) = '\'': outside mle cs
505 inside1 mle (c:cs) = c : inside1 mle cs
509 rememberExpr :: String -> GHCi ()
511 = do let cleaned = (clean . reverse . clean . reverse) str
512 let forget_me_not | null cleaned = Nothing
513 | otherwise = Just cleaned
514 setLastExpr forget_me_not
516 clean = dropWhile isSpace
519 -----------------------------------------------------------------------------
522 data GHCiState = GHCiState
525 current_module :: Module,
526 target :: Maybe FilePath,
528 options :: [GHCiOption],
529 last_expr :: Maybe String
533 = ShowTiming -- show time/allocs after evaluation
534 | ShowType -- show the type of expressions
535 | RevertCAFs -- revert CAFs after every evaluation
538 defaultCurrentModuleName = mkModuleName "Prelude"
539 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
541 GLOBAL_VAR(flush_stdout, error "no flush_stdout", HValue)
542 GLOBAL_VAR(flush_stderr, error "no flush_stdout", HValue)
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 getLastExpr :: GHCi (Maybe String)
570 = do st <- getGHCiState ; return (last_expr st)
572 setLastExpr :: Maybe String -> GHCi ()
573 setLastExpr last_expr
574 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
576 io m = GHCi $ \s -> m >>= \a -> return (s,a)
578 -- recursive exception handlers
579 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
580 ghciHandle h (GHCi m) = GHCi $ \s ->
581 Exception.catch (m s) (\e -> unGHCi (ghciHandle h (h e)) s)
583 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
584 Exception.catchDyn (m s) (\e -> unGHCi (ghciHandleDyn h (h e)) s)
586 -----------------------------------------------------------------------------
589 linkPackages :: [Package] -> IO ()
590 linkPackages pkgs = mapM_ linkPackage pkgs
592 linkPackage :: Package -> IO ()
593 -- ignore rts and gmp for now (ToDo; better?)
594 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
596 putStr ("Loading package " ++ name pkg ++ " ... ")
597 let dirs = library_dirs pkg
598 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
599 mapM (linkOneObj dirs) objs
600 putStr "resolving ... "
604 linkOneObj dirs obj = do
605 filename <- findFile dirs obj
608 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
609 findFile (d:ds) obj = do
610 let path = d ++ '/':obj
611 b <- doesFileExist path
612 if b then return path else findFile ds obj
614 -----------------------------------------------------------------------------
615 -- timing & statistics
617 timeIt :: GHCi a -> GHCi a
619 = do b <- isOptionSet ShowTiming
622 else do allocs1 <- io $ getAllocations
623 time1 <- io $ getCPUTime
625 allocs2 <- io $ getAllocations
626 time2 <- io $ getCPUTime
627 io $ printTimes (allocs2 - allocs1) (time2 - time1)
630 foreign import "getAllocations" getAllocations :: IO Int
632 printTimes :: Int -> Integer -> IO ()
633 printTimes allocs psecs
634 = do let secs = (fromIntegral psecs / (10^12)) :: Float
635 secs_str = showFFloat (Just 2) secs
637 parens (text (secs_str "") <+> text "secs" <> comma <+>
638 int allocs <+> text "bytes")))
640 -----------------------------------------------------------------------------
643 foreign import revertCAFs :: IO () -- make it "safe", just in case