1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.29 2001/01/18 16:30:00 simonmar Exp $
4 -- GHC Interactive User Interface
6 -- (c) The GHC Team 2000
8 -----------------------------------------------------------------------------
10 module InteractiveUI (interactiveUI) where
12 #include "HsVersions.h"
23 import PprType {- instance Outputable Type; do not delete -}
24 import Panic ( GhcException(..) )
42 -----------------------------------------------------------------------------
45 \ _____ __ __ ____ _________________________________________________\n\
46 \(| || || (| |) GHC Interactive, version 5.00 \n\
47 \|| __ ||___|| || () For Haskell 98. \n\
48 \|| |) ||---|| || || http://www.haskell.org/ghc \n\
49 \|| || || || || (| Bug reports to: glasgow-haskell-bugs@haskell.org \n\
50 \(|___|| || || (|__|) \\\\______________________________________________________\n"
52 commands :: [(String, String -> GHCi Bool)]
54 ("add", keepGoing addModule),
55 ("cd", keepGoing changeDirectory),
56 ("help", keepGoing help),
57 ("?", keepGoing help),
58 ("load", keepGoing loadModule),
59 ("module", keepGoing setContext),
60 ("reload", keepGoing reloadModule),
61 ("set", keepGoing setOptions),
62 ("type", keepGoing typeOfExpr),
63 ("unset", keepGoing unsetOptions),
67 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
68 keepGoing a str = a str >> return False
70 shortHelpText = "use :? for help.\n"
73 \ Commands available from the prompt:\n\
75 \ <expr> evaluate <expr>\n\
76 \ :add <filename> add a module to the current set\n\
77 \ :cd <dir> change directory to <dir>\n\
78 \ :help, :? display this list of commands\n\
79 \ :load <filename> load a module (and it dependents)\n\
80 \ :module <mod> set the context for expression evaluation to <mod>\n\
81 \ :reload reload the current module set\n\
82 \ :set <option> ... set options\n\
83 \ :unset <option> ... unset options\n\
84 \ :type <expr> show the type of <expr>\n\
86 \ :!<command> run the shell command <command>\n\
88 \ Options for `:set' and `:unset':\n\
90 \ +s print timing/memory stats after each evaluation\n\
91 \ +t print type after evaluation\n\
92 \ -<flags> most GHC command line flags can also be set here\n\
93 \ (eg. -v2, -fglasgow-exts, etc.)\n\
96 interactiveUI :: CmState -> Maybe FilePath -> IO ()
97 interactiveUI cmstate mod = do
98 hPutStrLn stdout ghciWelcomeMsg
100 hSetBuffering stdout NoBuffering
102 -- link in the available packages
103 pkgs <- getPackageInfo
104 linkPackages (reverse pkgs)
106 (cmstate', ok, mods) <-
108 Nothing -> return (cmstate, True, [])
109 Just m -> cmLoadModule cmstate m
115 prel <- moduleNameToModule defaultCurrentModuleName
116 writeIORef defaultCurrentModule prel
118 let this_mod = case mods of
122 (unGHCi uiLoop) GHCiState{ modules = mods,
123 current_module = this_mod,
126 options = [ShowTiming],
135 l <- io (readline (moduleUserString (current_module st) ++ "> "))
137 l_ok <- io (hGetLine stdin)
148 if quit then exitGHCi else uiLoop
150 exitGHCi = io $ do putStrLn "Leaving GHCi."
152 -- Top level exception handler, just prints out the exception
154 runCommand :: String -> GHCi Bool
158 -> io (putStrLn (show other_exception)) >> return False
162 PhaseFailed phase code ->
163 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
164 ++ show code ++ ")"))
165 Interrupted -> io (putStrLn "Interrupted.")
166 _ -> io (putStrLn (show (dyn :: GhcException)))
171 doCommand (':' : command) = specialCommand command
173 = do expr_expanded <- expandExpr expr
174 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
175 expr_ok <- timeIt (do ok <- evalExpr expr_expanded
176 when ok (evalExpr "PrelHandle.hFlush PrelHandle.stdout" >> return ())
177 when ok (evalExpr "PrelHandle.hFlush PrelHandle.stderr" >> return ())
179 when expr_ok (rememberExpr expr_expanded)
182 -- Returned Bool indicates whether or not the expr was successfully
183 -- parsed, renamed and typechecked.
184 evalExpr :: String -> GHCi Bool
186 | null (filter (not.isSpace) expr)
189 = do st <- getGHCiState
190 dflags <- io (getDynFlags)
191 (new_cmstate, maybe_stuff) <-
192 io (cmGetExpr (cmstate st) dflags (current_module st) expr True)
193 setGHCiState st{cmstate = new_cmstate}
195 Nothing -> return False
196 Just (hv, unqual, ty)
197 -> do io (cmRunExpr hv)
198 b <- isOptionSet ShowType
199 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
203 let (mod,'.':str) = break (=='.') expr
204 case cmLookupSymbol (mkOrig varName (mkModuleName mod) (_PK_ str)) (cmstate st) of
205 Nothing -> io (putStrLn "nothing.")
210 specialCommand :: String -> GHCi Bool
211 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
212 specialCommand str = do
213 let (cmd,rest) = break isSpace str
214 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
215 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
216 ++ shortHelpText) >> return False)
217 [(_,f)] -> f (dropWhile isSpace rest)
218 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
219 " matches multiple commands (" ++
220 foldr1 (\a b -> a ++ ',':b) (map fst cs)
221 ++ ")") >> return False)
223 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
225 -----------------------------------------------------------------------------
228 help :: String -> GHCi ()
229 help _ = io (putStr helpText)
231 addModule :: String -> GHCi ()
232 addModule _ = throwDyn (OtherError ":add not implemented")
234 setContext :: String -> GHCi ()
236 = throwDyn (OtherError "syntax: `:m <module>'")
237 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
238 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
240 = do m <- io (moduleNameToModule (mkModuleName mn))
242 setGHCiState st{current_module = m}
244 moduleNameToModule :: ModuleName -> IO Module
245 moduleNameToModule mn
246 = do maybe_stuff <- findModule mn
248 Nothing -> throwDyn (OtherError ("can't find module `"
249 ++ moduleNameUserString mn ++ "'"))
250 Just (m,_) -> return m
252 changeDirectory :: String -> GHCi ()
253 changeDirectory d = io (setCurrentDirectory d)
255 loadModule :: String -> GHCi ()
256 loadModule path = timeIt (loadModule' path)
258 loadModule' path = do
259 state <- getGHCiState
260 cmstate1 <- io (cmUnload (cmstate state))
261 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
263 def_mod <- io (readIORef defaultCurrentModule)
265 let new_state = state{
268 current_module = case mods of
273 setGHCiState new_state
276 | null mods = text "none."
278 punctuate comma (map (text.moduleUserString) mods)) <> text "."
281 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
283 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
285 reloadModule :: String -> GHCi ()
287 state <- getGHCiState
289 Nothing -> io (putStr "no current target\n")
291 -> do (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
292 def_mod <- io (readIORef defaultCurrentModule)
294 state{cmstate=new_cmstate,
296 current_module = case mods of
302 reloadModule _ = noArgs ":reload"
304 typeOfExpr :: String -> GHCi ()
306 = do st <- getGHCiState
307 dflags <- io (getDynFlags)
308 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags
309 (current_module st) str False)
310 setGHCiState st{cmstate = new_cmstate}
313 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
315 quit :: String -> GHCi Bool
318 shellEscape :: String -> GHCi Bool
319 shellEscape str = io (system str >> return False)
321 ----------------------------------------------------------------------------
324 -- set options in the interpreter. Syntax is exactly the same as the
325 -- ghc command line, except that certain options aren't available (-C,
328 -- This is pretty fragile: most options won't work as expected. ToDo:
329 -- figure out which ones & disallow them.
331 setOptions :: String -> GHCi ()
333 = do st <- getGHCiState
334 let opts = options st
335 io $ putStrLn (showSDoc (
336 text "options currently set: " <>
339 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
342 = do -- first, deal with the GHCi opts (+s, +t, etc.)
344 (minus_opts, rest1) = partition isMinus opts
345 (plus_opts, rest2) = partition isPlus rest1
347 if (not (null rest2))
348 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
351 mapM setOpt plus_opts
353 -- now, the GHC flags
354 io (do leftovers <- processArgs static_flags minus_opts []
355 dyn_flags <- readIORef v_InitDynFlags
356 writeIORef v_DynFlags dyn_flags
357 leftovers <- processArgs dynamic_flags leftovers []
358 dyn_flags <- readIORef v_DynFlags
359 writeIORef v_InitDynFlags dyn_flags
360 if (not (null leftovers))
361 then throwDyn (OtherError ("unrecognised flags: " ++
366 unsetOptions :: String -> GHCi ()
368 = do -- first, deal with the GHCi opts (+s, +t, etc.)
370 (minus_opts, rest1) = partition isMinus opts
371 (plus_opts, rest2) = partition isPlus rest1
373 if (not (null rest2))
374 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
377 mapM unsetOpt plus_opts
379 -- can't do GHC flags for now
380 if (not (null minus_opts))
381 then throwDyn (OtherError "can't unset GHC command-line flags")
384 isMinus ('-':s) = True
387 isPlus ('+':s) = True
391 = case strToGHCiOpt str of
392 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
393 Just o -> setOption o
396 = case strToGHCiOpt str of
397 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
398 Just o -> unsetOption o
400 strToGHCiOpt :: String -> (Maybe GHCiOption)
401 strToGHCiOpt "s" = Just ShowTiming
402 strToGHCiOpt "t" = Just ShowType
403 strToGHCiOpt _ = Nothing
405 optToStr :: GHCiOption -> String
406 optToStr ShowTiming = "s"
407 optToStr ShowType = "t"
410 -----------------------------------------------------------------------------
411 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
413 -- Take a string and replace $$s in it with the last expr, if any.
414 expandExpr :: String -> GHCi String
416 = do mle <- getLastExpr
417 return (outside mle str)
419 outside mle ('$':'$':cs)
421 Just le -> " (" ++ le ++ ") " ++ outside mle cs
422 Nothing -> outside mle cs
425 outside mle ('"':str) = '"' : inside2 mle str -- "
426 outside mle ('\'':str) = '\'' : inside1 mle str -- '
427 outside mle (c:cs) = c : outside mle cs
429 inside2 mle ('"':cs) = '"' : outside mle cs -- "
430 inside2 mle (c:cs) = c : inside2 mle cs
433 inside1 mle ('\'':cs) = '\'': outside mle cs
434 inside1 mle (c:cs) = c : inside1 mle cs
438 rememberExpr :: String -> GHCi ()
440 = do let cleaned = (clean . reverse . clean . reverse) str
441 let forget_me_not | null cleaned = Nothing
442 | otherwise = Just cleaned
443 setLastExpr forget_me_not
445 clean = dropWhile isSpace
448 -----------------------------------------------------------------------------
451 data GHCiState = GHCiState
454 current_module :: Module,
455 target :: Maybe FilePath,
457 options :: [GHCiOption],
458 last_expr :: Maybe String
461 data GHCiOption = ShowTiming | ShowType deriving Eq
463 defaultCurrentModuleName = mkModuleName "Prelude"
464 GLOBAL_VAR(defaultCurrentModule, error "no defaultCurrentModule", Module)
466 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
468 instance Monad GHCi where
469 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
470 return a = GHCi $ \s -> return (s,a)
472 getGHCiState = GHCi $ \s -> return (s,s)
473 setGHCiState s = GHCi $ \_ -> return (s,())
475 isOptionSet :: GHCiOption -> GHCi Bool
477 = do st <- getGHCiState
478 return (opt `elem` options st)
480 setOption :: GHCiOption -> GHCi ()
482 = do st <- getGHCiState
483 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
485 unsetOption :: GHCiOption -> GHCi ()
487 = do st <- getGHCiState
488 setGHCiState (st{ options = filter (/= opt) (options st) })
490 getLastExpr :: GHCi (Maybe String)
492 = do st <- getGHCiState ; return (last_expr st)
494 setLastExpr :: Maybe String -> GHCi ()
495 setLastExpr last_expr
496 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
498 io m = GHCi $ \s -> m >>= \a -> return (s,a)
500 ghciHandle h (GHCi m) = GHCi $ \s ->
501 Exception.catch (m s) (\e -> unGHCi (h e) s)
502 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
503 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
505 -----------------------------------------------------------------------------
508 linkPackages :: [Package] -> IO ()
509 linkPackages pkgs = mapM_ linkPackage pkgs
511 linkPackage :: Package -> IO ()
512 -- ignore rts and gmp for now (ToDo; better?)
513 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
515 putStr ("Loading package " ++ name pkg ++ " ... ")
516 let dirs = library_dirs pkg
517 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
518 mapM (linkOneObj dirs) objs
519 putStr "resolving ... "
523 linkOneObj dirs obj = do
524 filename <- findFile dirs obj
527 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
528 findFile (d:ds) obj = do
529 let path = d ++ '/':obj
530 b <- doesFileExist path
531 if b then return path else findFile ds obj
533 -----------------------------------------------------------------------------
534 -- timing & statistics
536 timeIt :: GHCi a -> GHCi a
538 = do b <- isOptionSet ShowTiming
541 else do allocs1 <- io $ getAllocations
542 time1 <- io $ getCPUTime
544 allocs2 <- io $ getAllocations
545 time2 <- io $ getCPUTime
546 io $ printTimes (allocs2 - allocs1) (time2 - time1)
549 foreign import "getAllocations" getAllocations :: IO Int
551 printTimes :: Int -> Integer -> IO ()
552 printTimes allocs psecs
553 = do let secs = (fromIntegral psecs / (10^12)) :: Float
554 secs_str = showFFloat (Just 2) secs
556 parens (text (secs_str "") <+> text "secs" <> comma <+>
557 int allocs <+> text "bytes")))