1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.33 2001/02/06 16:22:12 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 uiLoop) GHCiState{ modules = mods,
141 current_module = this_mod,
144 options = [ShowTiming],
153 l <- io (readline (moduleUserString (current_module st) ++ "> "))
155 l_ok <- io (hGetLine stdin)
162 case remove_spaces l of {
169 if quit then exitGHCi else uiLoop
173 exitGHCi = io $ do putStrLn "Leaving GHCi."
175 -- Top level exception handler, just prints out the exception
177 runCommand :: String -> GHCi Bool
181 -> io (putStrLn (show other_exception)) >> return False
185 PhaseFailed phase code ->
186 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
187 ++ show code ++ ")"))
188 Interrupted -> io (putStrLn "Interrupted.")
189 _ -> io (putStrLn (show (dyn :: GhcException)))
194 doCommand (':' : command) = specialCommand command
195 doCommand ('-':'-':_) = return False -- comments, useful in scripts
197 = do expr_expanded <- expandExpr expr
198 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
199 expr_ok <- timeIt (do stuff <- evalExpr expr_expanded
200 finishEvalExpr stuff)
201 when expr_ok (rememberExpr expr_expanded)
204 -- possibly print the type and revert CAFs after evaluating an expression
205 finishEvalExpr Nothing = return False
206 finishEvalExpr (Just (unqual,ty))
207 = do b <- isOptionSet ShowType
208 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
209 b <- isOptionSet RevertCAFs
210 io (when b revertCAFs)
213 -- Returned Bool indicates whether or not the expr was successfully
214 -- parsed, renamed and typechecked.
215 evalExpr :: String -> GHCi (Maybe (PrintUnqualified,Type))
217 | null (filter (not.isSpace) expr)
220 = do st <- getGHCiState
221 dflags <- io (getDynFlags)
222 (new_cmstate, maybe_stuff) <-
223 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
224 setGHCiState st{cmstate = new_cmstate}
226 Nothing -> return Nothing
227 Just (hv, unqual, ty) -> do io (cmRunExpr hv)
229 return (Just (unqual,ty))
231 flushEverything :: GHCi ()
233 = io $ do flush_so <- readIORef flush_stdout
235 flush_se <- readIORef flush_stdout
238 specialCommand :: String -> GHCi Bool
239 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
240 specialCommand str = do
241 let (cmd,rest) = break isSpace str
242 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
243 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
244 ++ shortHelpText) >> return False)
245 [(_,f)] -> f (dropWhile isSpace rest)
246 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
247 " matches multiple commands (" ++
248 foldr1 (\a b -> a ++ ',':b) (map fst cs)
249 ++ ")") >> return False)
251 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
253 -----------------------------------------------------------------------------
256 help :: String -> GHCi ()
257 help _ = io (putStr helpText)
259 addModule :: String -> GHCi ()
260 addModule _ = throwDyn (OtherError ":add not implemented")
262 setContext :: String -> GHCi ()
264 = throwDyn (OtherError "syntax: `:m <module>'")
265 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
266 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
268 = do m <- io (moduleNameToModule (mkModuleName mn))
270 setGHCiState st{current_module = m}
272 moduleNameToModule :: ModuleName -> IO Module
273 moduleNameToModule mn
274 = do maybe_stuff <- findModule mn
276 Nothing -> throwDyn (OtherError ("can't find module `"
277 ++ moduleNameUserString mn ++ "'"))
278 Just (m,_) -> return m
280 changeDirectory :: String -> GHCi ()
281 changeDirectory d = io (setCurrentDirectory d)
283 loadModule :: String -> GHCi ()
284 loadModule path = timeIt (loadModule' path)
286 loadModule' path = do
287 state <- getGHCiState
288 cmstate1 <- io (cmUnload (cmstate state))
289 io (revertCAFs) -- always revert CAFs on load.
290 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
292 def_mod <- io (readIORef defaultCurrentModule)
294 let new_state = state{
297 current_module = case mods of
302 setGHCiState new_state
305 | null mods = text "none."
307 punctuate comma (map (text.moduleUserString) mods)) <> text "."
310 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
312 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
314 reloadModule :: String -> GHCi ()
316 state <- getGHCiState
318 Nothing -> io (putStr "no current target\n")
320 -> do io (revertCAFs) -- always revert CAFs on reload.
321 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
322 def_mod <- io (readIORef defaultCurrentModule)
324 state{cmstate=new_cmstate,
326 current_module = case mods of
331 reloadModule _ = noArgs ":reload"
333 typeOfExpr :: String -> GHCi ()
335 = do st <- getGHCiState
336 dflags <- io (getDynFlags)
337 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
338 (current_module st) str)
339 setGHCiState st{cmstate = new_cmstate}
342 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
344 quit :: String -> GHCi Bool
347 shellEscape :: String -> GHCi Bool
348 shellEscape str = io (system str >> return False)
350 ----------------------------------------------------------------------------
353 -- set options in the interpreter. Syntax is exactly the same as the
354 -- ghc command line, except that certain options aren't available (-C,
357 -- This is pretty fragile: most options won't work as expected. ToDo:
358 -- figure out which ones & disallow them.
360 setOptions :: String -> GHCi ()
362 = do st <- getGHCiState
363 let opts = options st
364 io $ putStrLn (showSDoc (
365 text "options currently set: " <>
368 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
371 = do -- first, deal with the GHCi opts (+s, +t, etc.)
373 (minus_opts, rest1) = partition isMinus opts
374 (plus_opts, rest2) = partition isPlus rest1
376 if (not (null rest2))
377 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
380 mapM setOpt plus_opts
382 -- now, the GHC flags
383 io (do -- first, static flags
384 leftovers <- processArgs static_flags minus_opts []
386 -- then, dynamic flags
387 dyn_flags <- readIORef v_InitDynFlags
388 writeIORef v_DynFlags dyn_flags
389 leftovers <- processArgs dynamic_flags leftovers []
390 dyn_flags <- readIORef v_DynFlags
391 writeIORef v_InitDynFlags dyn_flags
393 if (not (null leftovers))
394 then throwDyn (OtherError ("unrecognised flags: " ++
399 unsetOptions :: String -> GHCi ()
401 = do -- first, deal with the GHCi opts (+s, +t, etc.)
403 (minus_opts, rest1) = partition isMinus opts
404 (plus_opts, rest2) = partition isPlus rest1
406 if (not (null rest2))
407 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
410 mapM unsetOpt plus_opts
412 -- can't do GHC flags for now
413 if (not (null minus_opts))
414 then throwDyn (OtherError "can't unset GHC command-line flags")
417 isMinus ('-':s) = True
420 isPlus ('+':s) = True
424 = case strToGHCiOpt str of
425 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
426 Just o -> setOption o
429 = case strToGHCiOpt str of
430 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
431 Just o -> unsetOption o
433 strToGHCiOpt :: String -> (Maybe GHCiOption)
434 strToGHCiOpt "s" = Just ShowTiming
435 strToGHCiOpt "t" = Just ShowType
436 strToGHCiOpt "r" = Just RevertCAFs
437 strToGHCiOpt _ = Nothing
439 optToStr :: GHCiOption -> String
440 optToStr ShowTiming = "s"
441 optToStr ShowType = "t"
442 optToStr RevertCAFs = "r"
444 -----------------------------------------------------------------------------
445 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
447 -- Take a string and replace $$s in it with the last expr, if any.
448 expandExpr :: String -> GHCi String
450 = do mle <- getLastExpr
451 return (outside mle str)
453 outside mle ('$':'$':cs)
455 Just le -> " (" ++ le ++ ") " ++ outside mle cs
456 Nothing -> outside mle cs
459 outside mle ('"':str) = '"' : inside2 mle str -- "
460 outside mle ('\'':str) = '\'' : inside1 mle str -- '
461 outside mle (c:cs) = c : outside mle cs
463 inside2 mle ('"':cs) = '"' : outside mle cs -- "
464 inside2 mle (c:cs) = c : inside2 mle cs
467 inside1 mle ('\'':cs) = '\'': outside mle cs
468 inside1 mle (c:cs) = c : inside1 mle cs
472 rememberExpr :: String -> GHCi ()
474 = do let cleaned = (clean . reverse . clean . reverse) str
475 let forget_me_not | null cleaned = Nothing
476 | otherwise = Just cleaned
477 setLastExpr forget_me_not
479 clean = dropWhile isSpace
482 -----------------------------------------------------------------------------
485 data GHCiState = GHCiState
488 current_module :: Module,
489 target :: Maybe FilePath,
491 options :: [GHCiOption],
492 last_expr :: Maybe String
496 = ShowTiming -- show time/allocs after evaluation
497 | ShowType -- show the type of expressions
498 | RevertCAFs -- revert CAFs after every evaluation
501 defaultCurrentModuleName = mkModuleName "Prelude"
502 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
504 GLOBAL_VAR(flush_stdout, error "no flush_stdout", HValue)
505 GLOBAL_VAR(flush_stderr, error "no flush_stdout", HValue)
507 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
509 instance Monad GHCi where
510 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
511 return a = GHCi $ \s -> return (s,a)
513 getGHCiState = GHCi $ \s -> return (s,s)
514 setGHCiState s = GHCi $ \_ -> return (s,())
516 isOptionSet :: GHCiOption -> GHCi Bool
518 = do st <- getGHCiState
519 return (opt `elem` options st)
521 setOption :: GHCiOption -> GHCi ()
523 = do st <- getGHCiState
524 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
526 unsetOption :: GHCiOption -> GHCi ()
528 = do st <- getGHCiState
529 setGHCiState (st{ options = filter (/= opt) (options st) })
531 getLastExpr :: GHCi (Maybe String)
533 = do st <- getGHCiState ; return (last_expr st)
535 setLastExpr :: Maybe String -> GHCi ()
536 setLastExpr last_expr
537 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
539 io m = GHCi $ \s -> m >>= \a -> return (s,a)
541 ghciHandle h (GHCi m) = GHCi $ \s ->
542 Exception.catch (m s) (\e -> unGHCi (h e) s)
543 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
544 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
546 -----------------------------------------------------------------------------
549 linkPackages :: [Package] -> IO ()
550 linkPackages pkgs = mapM_ linkPackage pkgs
552 linkPackage :: Package -> IO ()
553 -- ignore rts and gmp for now (ToDo; better?)
554 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
556 putStr ("Loading package " ++ name pkg ++ " ... ")
557 let dirs = library_dirs pkg
558 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
559 mapM (linkOneObj dirs) objs
560 putStr "resolving ... "
564 linkOneObj dirs obj = do
565 filename <- findFile dirs obj
568 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
569 findFile (d:ds) obj = do
570 let path = d ++ '/':obj
571 b <- doesFileExist path
572 if b then return path else findFile ds obj
574 -----------------------------------------------------------------------------
575 -- timing & statistics
577 timeIt :: GHCi a -> GHCi a
579 = do b <- isOptionSet ShowTiming
582 else do allocs1 <- io $ getAllocations
583 time1 <- io $ getCPUTime
585 allocs2 <- io $ getAllocations
586 time2 <- io $ getCPUTime
587 io $ printTimes (allocs2 - allocs1) (time2 - time1)
590 foreign import "getAllocations" getAllocations :: IO Int
592 printTimes :: Int -> Integer -> IO ()
593 printTimes allocs psecs
594 = do let secs = (fromIntegral psecs / (10^12)) :: Float
595 secs_str = showFFloat (Just 2) secs
597 parens (text (secs_str "") <+> text "secs" <> comma <+>
598 int allocs <+> text "bytes")))
600 -----------------------------------------------------------------------------
603 foreign import revertCAFs :: IO () -- make it "safe", just in case