1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.31 2001/01/26 17:14:58 simonmar Exp $
4 -- GHC Interactive User Interface
6 -- (c) The GHC Team 2000
8 -----------------------------------------------------------------------------
10 module InteractiveUI (interactiveUI) where
12 #include "HsVersions.h"
24 import PprType {- instance Outputable Type; do not delete -}
25 import Panic ( GhcException(..) )
43 -----------------------------------------------------------------------------
46 \ _____ __ __ ____ _________________________________________________\n\
47 \(| || || (| |) GHC Interactive, version 5.00 \n\
48 \|| __ ||___|| || () For Haskell 98. \n\
49 \|| |) ||---|| || || http://www.haskell.org/ghc \n\
50 \|| || || || || (| Bug reports to: glasgow-haskell-bugs@haskell.org \n\
51 \(|___|| || || (|__|) \\\\______________________________________________________\n"
53 commands :: [(String, String -> GHCi Bool)]
55 ("add", keepGoing addModule),
56 ("cd", keepGoing changeDirectory),
57 ("help", keepGoing help),
58 ("?", keepGoing help),
59 ("load", keepGoing loadModule),
60 ("module", keepGoing setContext),
61 ("reload", keepGoing reloadModule),
62 ("set", keepGoing setOptions),
63 ("type", keepGoing typeOfExpr),
64 ("unset", keepGoing unsetOptions),
68 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
69 keepGoing a str = a str >> return False
71 shortHelpText = "use :? for help.\n"
74 \ Commands available from the prompt:\n\
76 \ <expr> evaluate <expr>\n\
77 \ :add <filename> add a module to the current set\n\
78 \ :cd <dir> change directory to <dir>\n\
79 \ :help, :? display this list of commands\n\
80 \ :load <filename> load a module (and it dependents)\n\
81 \ :module <mod> set the context for expression evaluation to <mod>\n\
82 \ :reload reload the current module set\n\
83 \ :set <option> ... set options\n\
84 \ :unset <option> ... unset options\n\
85 \ :type <expr> show the type of <expr>\n\
87 \ :!<command> run the shell command <command>\n\
89 \ Options for `:set' and `:unset':\n\
91 \ +s print timing/memory stats after each evaluation\n\
92 \ +t print type after evaluation\n\
93 \ -<flags> most GHC command line flags can also be set here\n\
94 \ (eg. -v2, -fglasgow-exts, etc.)\n\
97 interactiveUI :: CmState -> Maybe FilePath -> IO ()
98 interactiveUI cmstate mod = do
99 hPutStrLn stdout ghciWelcomeMsg
101 hSetBuffering stdout NoBuffering
103 -- link in the available packages
104 pkgs <- getPackageInfo
105 linkPackages (reverse pkgs)
107 (cmstate', ok, mods) <-
109 Nothing -> return (cmstate, True, [])
110 Just m -> cmLoadModule cmstate m
116 prel <- moduleNameToModule defaultCurrentModuleName
117 writeIORef defaultCurrentModule prel
119 let this_mod = case mods of
123 (unGHCi uiLoop) GHCiState{ modules = mods,
124 current_module = this_mod,
127 options = [ShowTiming],
136 l <- io (readline (moduleUserString (current_module st) ++ "> "))
138 l_ok <- io (hGetLine stdin)
145 case remove_spaces l of {
152 if quit then exitGHCi else uiLoop
156 exitGHCi = io $ do putStrLn "Leaving GHCi."
158 -- Top level exception handler, just prints out the exception
160 runCommand :: String -> GHCi Bool
164 -> io (putStrLn (show other_exception)) >> return False
168 PhaseFailed phase code ->
169 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
170 ++ show code ++ ")"))
171 Interrupted -> io (putStrLn "Interrupted.")
172 _ -> io (putStrLn (show (dyn :: GhcException)))
177 doCommand (':' : command) = specialCommand command
179 = do expr_expanded <- expandExpr expr
180 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
181 expr_ok <- timeIt (do ok <- evalExpr expr_expanded
182 when ok (evalExpr "PrelHandle.hFlush PrelHandle.stdout" >> return ())
183 when ok (evalExpr "PrelHandle.hFlush PrelHandle.stderr" >> return ())
185 when expr_ok (rememberExpr expr_expanded)
188 -- Returned Bool indicates whether or not the expr was successfully
189 -- parsed, renamed and typechecked.
190 evalExpr :: String -> GHCi Bool
192 | null (filter (not.isSpace) expr)
195 = do st <- getGHCiState
196 dflags <- io (getDynFlags)
197 (new_cmstate, maybe_stuff) <-
198 io (cmGetExpr (cmstate st) dflags True (current_module st) expr)
199 setGHCiState st{cmstate = new_cmstate}
201 Nothing -> return False
202 Just (hv, unqual, ty)
203 -> do io (cmRunExpr hv)
204 b <- isOptionSet ShowType
205 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
208 specialCommand :: String -> GHCi Bool
209 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
210 specialCommand str = do
211 let (cmd,rest) = break isSpace str
212 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
213 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
214 ++ shortHelpText) >> return False)
215 [(_,f)] -> f (dropWhile isSpace rest)
216 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
217 " matches multiple commands (" ++
218 foldr1 (\a b -> a ++ ',':b) (map fst cs)
219 ++ ")") >> return False)
221 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
223 -----------------------------------------------------------------------------
226 help :: String -> GHCi ()
227 help _ = io (putStr helpText)
229 addModule :: String -> GHCi ()
230 addModule _ = throwDyn (OtherError ":add not implemented")
232 setContext :: String -> GHCi ()
234 = throwDyn (OtherError "syntax: `:m <module>'")
235 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
236 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
238 = do m <- io (moduleNameToModule (mkModuleName mn))
240 setGHCiState st{current_module = m}
242 moduleNameToModule :: ModuleName -> IO Module
243 moduleNameToModule mn
244 = do maybe_stuff <- findModule mn
246 Nothing -> throwDyn (OtherError ("can't find module `"
247 ++ moduleNameUserString mn ++ "'"))
248 Just (m,_) -> return m
250 changeDirectory :: String -> GHCi ()
251 changeDirectory d = io (setCurrentDirectory d)
253 loadModule :: String -> GHCi ()
254 loadModule path = timeIt (loadModule' path)
256 loadModule' path = do
257 state <- getGHCiState
258 cmstate1 <- io (cmUnload (cmstate state))
259 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
261 def_mod <- io (readIORef defaultCurrentModule)
263 let new_state = state{
266 current_module = case mods of
271 setGHCiState new_state
274 | null mods = text "none."
276 punctuate comma (map (text.moduleUserString) mods)) <> text "."
279 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
281 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
283 reloadModule :: String -> GHCi ()
285 state <- getGHCiState
287 Nothing -> io (putStr "no current target\n")
289 -> do (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
290 def_mod <- io (readIORef defaultCurrentModule)
292 state{cmstate=new_cmstate,
294 current_module = case mods of
300 reloadModule _ = noArgs ":reload"
302 typeOfExpr :: String -> GHCi ()
304 = do st <- getGHCiState
305 dflags <- io (getDynFlags)
306 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags False
307 (current_module st) str)
308 setGHCiState st{cmstate = new_cmstate}
311 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
313 quit :: String -> GHCi Bool
316 shellEscape :: String -> GHCi Bool
317 shellEscape str = io (system str >> return False)
319 ----------------------------------------------------------------------------
322 -- set options in the interpreter. Syntax is exactly the same as the
323 -- ghc command line, except that certain options aren't available (-C,
326 -- This is pretty fragile: most options won't work as expected. ToDo:
327 -- figure out which ones & disallow them.
329 setOptions :: String -> GHCi ()
331 = do st <- getGHCiState
332 let opts = options st
333 io $ putStrLn (showSDoc (
334 text "options currently set: " <>
337 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
340 = do -- first, deal with the GHCi opts (+s, +t, etc.)
342 (minus_opts, rest1) = partition isMinus opts
343 (plus_opts, rest2) = partition isPlus rest1
345 if (not (null rest2))
346 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
349 mapM setOpt plus_opts
351 -- now, the GHC flags
352 io (do -- first, static flags
353 leftovers <- processArgs static_flags minus_opts []
355 -- then, dynamic flags
356 dyn_flags <- readIORef v_InitDynFlags
357 writeIORef v_DynFlags dyn_flags
358 leftovers <- processArgs dynamic_flags leftovers []
359 dyn_flags <- readIORef v_DynFlags
360 writeIORef v_InitDynFlags dyn_flags
362 if (not (null leftovers))
363 then throwDyn (OtherError ("unrecognised flags: " ++
368 unsetOptions :: String -> GHCi ()
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 unsetOpt plus_opts
381 -- can't do GHC flags for now
382 if (not (null minus_opts))
383 then throwDyn (OtherError "can't unset GHC command-line flags")
386 isMinus ('-':s) = True
389 isPlus ('+':s) = True
393 = case strToGHCiOpt str of
394 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
395 Just o -> setOption o
398 = case strToGHCiOpt str of
399 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
400 Just o -> unsetOption o
402 strToGHCiOpt :: String -> (Maybe GHCiOption)
403 strToGHCiOpt "s" = Just ShowTiming
404 strToGHCiOpt "t" = Just ShowType
405 strToGHCiOpt _ = Nothing
407 optToStr :: GHCiOption -> String
408 optToStr ShowTiming = "s"
409 optToStr ShowType = "t"
412 -----------------------------------------------------------------------------
413 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
415 -- Take a string and replace $$s in it with the last expr, if any.
416 expandExpr :: String -> GHCi String
418 = do mle <- getLastExpr
419 return (outside mle str)
421 outside mle ('$':'$':cs)
423 Just le -> " (" ++ le ++ ") " ++ outside mle cs
424 Nothing -> outside mle cs
427 outside mle ('"':str) = '"' : inside2 mle str -- "
428 outside mle ('\'':str) = '\'' : inside1 mle str -- '
429 outside mle (c:cs) = c : outside mle cs
431 inside2 mle ('"':cs) = '"' : outside mle cs -- "
432 inside2 mle (c:cs) = c : inside2 mle cs
435 inside1 mle ('\'':cs) = '\'': outside mle cs
436 inside1 mle (c:cs) = c : inside1 mle cs
440 rememberExpr :: String -> GHCi ()
442 = do let cleaned = (clean . reverse . clean . reverse) str
443 let forget_me_not | null cleaned = Nothing
444 | otherwise = Just cleaned
445 setLastExpr forget_me_not
447 clean = dropWhile isSpace
450 -----------------------------------------------------------------------------
453 data GHCiState = GHCiState
456 current_module :: Module,
457 target :: Maybe FilePath,
459 options :: [GHCiOption],
460 last_expr :: Maybe String
463 data GHCiOption = ShowTiming | ShowType deriving Eq
465 defaultCurrentModuleName = mkModuleName "Prelude"
466 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
468 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
470 instance Monad GHCi where
471 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
472 return a = GHCi $ \s -> return (s,a)
474 getGHCiState = GHCi $ \s -> return (s,s)
475 setGHCiState s = GHCi $ \_ -> return (s,())
477 isOptionSet :: GHCiOption -> GHCi Bool
479 = do st <- getGHCiState
480 return (opt `elem` options st)
482 setOption :: GHCiOption -> GHCi ()
484 = do st <- getGHCiState
485 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
487 unsetOption :: GHCiOption -> GHCi ()
489 = do st <- getGHCiState
490 setGHCiState (st{ options = filter (/= opt) (options st) })
492 getLastExpr :: GHCi (Maybe String)
494 = do st <- getGHCiState ; return (last_expr st)
496 setLastExpr :: Maybe String -> GHCi ()
497 setLastExpr last_expr
498 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
500 io m = GHCi $ \s -> m >>= \a -> return (s,a)
502 ghciHandle h (GHCi m) = GHCi $ \s ->
503 Exception.catch (m s) (\e -> unGHCi (h e) s)
504 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
505 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
507 -----------------------------------------------------------------------------
510 linkPackages :: [Package] -> IO ()
511 linkPackages pkgs = mapM_ linkPackage pkgs
513 linkPackage :: Package -> IO ()
514 -- ignore rts and gmp for now (ToDo; better?)
515 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
517 putStr ("Loading package " ++ name pkg ++ " ... ")
518 let dirs = library_dirs pkg
519 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
520 mapM (linkOneObj dirs) objs
521 putStr "resolving ... "
525 linkOneObj dirs obj = do
526 filename <- findFile dirs obj
529 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
530 findFile (d:ds) obj = do
531 let path = d ++ '/':obj
532 b <- doesFileExist path
533 if b then return path else findFile ds obj
535 -----------------------------------------------------------------------------
536 -- timing & statistics
538 timeIt :: GHCi a -> GHCi a
540 = do b <- isOptionSet ShowTiming
543 else do allocs1 <- io $ getAllocations
544 time1 <- io $ getCPUTime
546 allocs2 <- io $ getAllocations
547 time2 <- io $ getCPUTime
548 io $ printTimes (allocs2 - allocs1) (time2 - time1)
551 foreign import "getAllocations" getAllocations :: IO Int
553 printTimes :: Int -> Integer -> IO ()
554 printTimes allocs psecs
555 = do let secs = (fromIntegral psecs / (10^12)) :: Float
556 secs_str = showFFloat (Just 2) secs
558 parens (text (secs_str "") <+> text "secs" <> comma <+>
559 int allocs <+> text "bytes")))