1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.32 2001/02/06 11:57:30 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
196 = do expr_expanded <- expandExpr expr
197 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
198 expr_ok <- timeIt (do stuff <- evalExpr expr_expanded
199 finishEvalExpr stuff)
200 when expr_ok (rememberExpr expr_expanded)
203 -- possibly print the type and revert CAFs after evaluating an expression
204 finishEvalExpr Nothing = return False
205 finishEvalExpr (Just (unqual,ty))
206 = do b <- isOptionSet ShowType
207 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
208 b <- isOptionSet RevertCAFs
209 io (when b revertCAFs)
212 -- Returned Bool indicates whether or not the expr was successfully
213 -- parsed, renamed and typechecked.
214 evalExpr :: String -> GHCi (Maybe (PrintUnqualified,Type))
216 | null (filter (not.isSpace) expr)
219 = do st <- getGHCiState
220 dflags <- io (getDynFlags)
221 (new_cmstate, maybe_stuff) <-
222 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
223 setGHCiState st{cmstate = new_cmstate}
225 Nothing -> return Nothing
226 Just (hv, unqual, ty) -> do io (cmRunExpr hv)
228 return (Just (unqual,ty))
230 flushEverything :: GHCi ()
232 = io $ do flush_so <- readIORef flush_stdout
234 flush_se <- readIORef flush_stdout
237 specialCommand :: String -> GHCi Bool
238 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
239 specialCommand str = do
240 let (cmd,rest) = break isSpace str
241 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
242 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
243 ++ shortHelpText) >> return False)
244 [(_,f)] -> f (dropWhile isSpace rest)
245 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
246 " matches multiple commands (" ++
247 foldr1 (\a b -> a ++ ',':b) (map fst cs)
248 ++ ")") >> return False)
250 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
252 -----------------------------------------------------------------------------
255 help :: String -> GHCi ()
256 help _ = io (putStr helpText)
258 addModule :: String -> GHCi ()
259 addModule _ = throwDyn (OtherError ":add not implemented")
261 setContext :: String -> GHCi ()
263 = throwDyn (OtherError "syntax: `:m <module>'")
264 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
265 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
267 = do m <- io (moduleNameToModule (mkModuleName mn))
269 setGHCiState st{current_module = m}
271 moduleNameToModule :: ModuleName -> IO Module
272 moduleNameToModule mn
273 = do maybe_stuff <- findModule mn
275 Nothing -> throwDyn (OtherError ("can't find module `"
276 ++ moduleNameUserString mn ++ "'"))
277 Just (m,_) -> return m
279 changeDirectory :: String -> GHCi ()
280 changeDirectory d = io (setCurrentDirectory d)
282 loadModule :: String -> GHCi ()
283 loadModule path = timeIt (loadModule' path)
285 loadModule' path = do
286 state <- getGHCiState
287 cmstate1 <- io (cmUnload (cmstate state))
288 io (revertCAFs) -- always revert CAFs on load.
289 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
291 def_mod <- io (readIORef defaultCurrentModule)
293 let new_state = state{
296 current_module = case mods of
301 setGHCiState new_state
304 | null mods = text "none."
306 punctuate comma (map (text.moduleUserString) mods)) <> text "."
309 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
311 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
313 reloadModule :: String -> GHCi ()
315 state <- getGHCiState
317 Nothing -> io (putStr "no current target\n")
319 -> do io (revertCAFs) -- always revert CAFs on reload.
320 (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
321 def_mod <- io (readIORef defaultCurrentModule)
323 state{cmstate=new_cmstate,
325 current_module = case mods of
330 reloadModule _ = noArgs ":reload"
332 typeOfExpr :: String -> GHCi ()
334 = do st <- getGHCiState
335 dflags <- io (getDynFlags)
336 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
337 (current_module st) str)
338 setGHCiState st{cmstate = new_cmstate}
341 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
343 quit :: String -> GHCi Bool
346 shellEscape :: String -> GHCi Bool
347 shellEscape str = io (system str >> return False)
349 ----------------------------------------------------------------------------
352 -- set options in the interpreter. Syntax is exactly the same as the
353 -- ghc command line, except that certain options aren't available (-C,
356 -- This is pretty fragile: most options won't work as expected. ToDo:
357 -- figure out which ones & disallow them.
359 setOptions :: String -> GHCi ()
361 = do st <- getGHCiState
362 let opts = options st
363 io $ putStrLn (showSDoc (
364 text "options currently set: " <>
367 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
370 = do -- first, deal with the GHCi opts (+s, +t, etc.)
372 (minus_opts, rest1) = partition isMinus opts
373 (plus_opts, rest2) = partition isPlus rest1
375 if (not (null rest2))
376 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
379 mapM setOpt plus_opts
381 -- now, the GHC flags
382 io (do -- first, static flags
383 leftovers <- processArgs static_flags minus_opts []
385 -- then, dynamic flags
386 dyn_flags <- readIORef v_InitDynFlags
387 writeIORef v_DynFlags dyn_flags
388 leftovers <- processArgs dynamic_flags leftovers []
389 dyn_flags <- readIORef v_DynFlags
390 writeIORef v_InitDynFlags dyn_flags
392 if (not (null leftovers))
393 then throwDyn (OtherError ("unrecognised flags: " ++
398 unsetOptions :: String -> GHCi ()
400 = do -- first, deal with the GHCi opts (+s, +t, etc.)
402 (minus_opts, rest1) = partition isMinus opts
403 (plus_opts, rest2) = partition isPlus rest1
405 if (not (null rest2))
406 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
409 mapM unsetOpt plus_opts
411 -- can't do GHC flags for now
412 if (not (null minus_opts))
413 then throwDyn (OtherError "can't unset GHC command-line flags")
416 isMinus ('-':s) = True
419 isPlus ('+':s) = True
423 = case strToGHCiOpt str of
424 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
425 Just o -> setOption o
428 = case strToGHCiOpt str of
429 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
430 Just o -> unsetOption o
432 strToGHCiOpt :: String -> (Maybe GHCiOption)
433 strToGHCiOpt "s" = Just ShowTiming
434 strToGHCiOpt "t" = Just ShowType
435 strToGHCiOpt "r" = Just RevertCAFs
436 strToGHCiOpt _ = Nothing
438 optToStr :: GHCiOption -> String
439 optToStr ShowTiming = "s"
440 optToStr ShowType = "t"
441 optToStr RevertCAFs = "r"
443 -----------------------------------------------------------------------------
444 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
446 -- Take a string and replace $$s in it with the last expr, if any.
447 expandExpr :: String -> GHCi String
449 = do mle <- getLastExpr
450 return (outside mle str)
452 outside mle ('$':'$':cs)
454 Just le -> " (" ++ le ++ ") " ++ outside mle cs
455 Nothing -> outside mle cs
458 outside mle ('"':str) = '"' : inside2 mle str -- "
459 outside mle ('\'':str) = '\'' : inside1 mle str -- '
460 outside mle (c:cs) = c : outside mle cs
462 inside2 mle ('"':cs) = '"' : outside mle cs -- "
463 inside2 mle (c:cs) = c : inside2 mle cs
466 inside1 mle ('\'':cs) = '\'': outside mle cs
467 inside1 mle (c:cs) = c : inside1 mle cs
471 rememberExpr :: String -> GHCi ()
473 = do let cleaned = (clean . reverse . clean . reverse) str
474 let forget_me_not | null cleaned = Nothing
475 | otherwise = Just cleaned
476 setLastExpr forget_me_not
478 clean = dropWhile isSpace
481 -----------------------------------------------------------------------------
484 data GHCiState = GHCiState
487 current_module :: Module,
488 target :: Maybe FilePath,
490 options :: [GHCiOption],
491 last_expr :: Maybe String
495 = ShowTiming -- show time/allocs after evaluation
496 | ShowType -- show the type of expressions
497 | RevertCAFs -- revert CAFs after every evaluation
500 defaultCurrentModuleName = mkModuleName "Prelude"
501 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
503 GLOBAL_VAR(flush_stdout, error "no flush_stdout", HValue)
504 GLOBAL_VAR(flush_stderr, error "no flush_stdout", HValue)
506 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
508 instance Monad GHCi where
509 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
510 return a = GHCi $ \s -> return (s,a)
512 getGHCiState = GHCi $ \s -> return (s,s)
513 setGHCiState s = GHCi $ \_ -> return (s,())
515 isOptionSet :: GHCiOption -> GHCi Bool
517 = do st <- getGHCiState
518 return (opt `elem` options st)
520 setOption :: GHCiOption -> GHCi ()
522 = do st <- getGHCiState
523 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
525 unsetOption :: GHCiOption -> GHCi ()
527 = do st <- getGHCiState
528 setGHCiState (st{ options = filter (/= opt) (options st) })
530 getLastExpr :: GHCi (Maybe String)
532 = do st <- getGHCiState ; return (last_expr st)
534 setLastExpr :: Maybe String -> GHCi ()
535 setLastExpr last_expr
536 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
538 io m = GHCi $ \s -> m >>= \a -> return (s,a)
540 ghciHandle h (GHCi m) = GHCi $ \s ->
541 Exception.catch (m s) (\e -> unGHCi (h e) s)
542 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
543 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
545 -----------------------------------------------------------------------------
548 linkPackages :: [Package] -> IO ()
549 linkPackages pkgs = mapM_ linkPackage pkgs
551 linkPackage :: Package -> IO ()
552 -- ignore rts and gmp for now (ToDo; better?)
553 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
555 putStr ("Loading package " ++ name pkg ++ " ... ")
556 let dirs = library_dirs pkg
557 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
558 mapM (linkOneObj dirs) objs
559 putStr "resolving ... "
563 linkOneObj dirs obj = do
564 filename <- findFile dirs obj
567 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
568 findFile (d:ds) obj = do
569 let path = d ++ '/':obj
570 b <- doesFileExist path
571 if b then return path else findFile ds obj
573 -----------------------------------------------------------------------------
574 -- timing & statistics
576 timeIt :: GHCi a -> GHCi a
578 = do b <- isOptionSet ShowTiming
581 else do allocs1 <- io $ getAllocations
582 time1 <- io $ getCPUTime
584 allocs2 <- io $ getAllocations
585 time2 <- io $ getCPUTime
586 io $ printTimes (allocs2 - allocs1) (time2 - time1)
589 foreign import "getAllocations" getAllocations :: IO Int
591 printTimes :: Int -> Integer -> IO ()
592 printTimes allocs psecs
593 = do let secs = (fromIntegral psecs / (10^12)) :: Float
594 secs_str = showFFloat (Just 2) secs
596 parens (text (secs_str "") <+> text "secs" <> comma <+>
597 int allocs <+> text "bytes")))
599 -----------------------------------------------------------------------------
602 foreign import revertCAFs :: IO () -- make it "safe", just in case