1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.28 2001/01/18 12:54:16 simonmar Exp $
4 -- GHC Interactive User Interface
6 -- (c) The GHC Team 2000
8 -----------------------------------------------------------------------------
10 module InteractiveUI (interactiveUI) where
12 #include "HsVersions.h"
22 import PprType {- instance Outputable Type; do not delete -}
23 import Panic ( GhcException(..) )
41 -----------------------------------------------------------------------------
44 \ _____ __ __ ____ _________________________________________________\n\
45 \(| || || (| |) GHC Interactive, version 5.00 \n\
46 \|| __ ||___|| || () For Haskell 98. \n\
47 \|| |) ||---|| || || http://www.haskell.org/ghc \n\
48 \|| || || || || (| Bug reports to: glasgow-haskell-bugs@haskell.org \n\
49 \(|___|| || || (|__|) \\\\______________________________________________________\n"
51 commands :: [(String, String -> GHCi Bool)]
53 ("add", keepGoing addModule),
54 ("cd", keepGoing changeDirectory),
55 ("help", keepGoing help),
56 ("?", keepGoing help),
57 ("load", keepGoing loadModule),
58 ("module", keepGoing setContext),
59 ("reload", keepGoing reloadModule),
60 ("set", keepGoing setOptions),
61 ("type", keepGoing typeOfExpr),
62 ("unset", keepGoing unsetOptions),
66 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
67 keepGoing a str = a str >> return False
69 shortHelpText = "use :? for help.\n"
72 \ Commands available from the prompt:\n\
74 \ <expr> evaluate <expr>\n\
75 \ :add <filename> add a module to the current set\n\
76 \ :cd <dir> change directory to <dir>\n\
77 \ :help, :? display this list of commands\n\
78 \ :load <filename> load a module (and it dependents)\n\
79 \ :module <mod> set the context for expression evaluation to <mod>\n\
80 \ :reload reload the current module set\n\
81 \ :set <option> ... set options\n\
82 \ :unset <option> ... unset options\n\
83 \ :type <expr> show the type of <expr>\n\
85 \ :!<command> run the shell command <command>\n\
87 \ Options for `:set' and `:unset':\n\
89 \ +s print timing/memory stats after each evaluation\n\
90 \ +t print type after evaluation\n\
91 \ -<flags> most GHC command line flags can also be set here\n\
92 \ (eg. -v2, -fglasgow-exts, etc.)\n\
95 interactiveUI :: CmState -> Maybe FilePath -> IO ()
96 interactiveUI cmstate mod = do
97 hPutStrLn stdout ghciWelcomeMsg
99 hSetBuffering stdout NoBuffering
101 -- link in the available packages
102 pkgs <- getPackageInfo
103 linkPackages (reverse pkgs)
105 (cmstate', ok, mods) <-
107 Nothing -> return (cmstate, True, [])
108 Just m -> cmLoadModule cmstate m
113 let this_mod = case mods of
114 [] -> defaultCurrentModule
117 (unGHCi uiLoop) GHCiState{ modules = mods,
118 current_module = this_mod,
121 options = [ShowTiming],
129 l <- io (readline (moduleNameUserString (current_module st) ++ "> "))
131 l_ok <- io (hGetLine stdin)
142 if quit then exitGHCi else uiLoop
144 exitGHCi = io $ do putStrLn "Leaving GHCi."
146 -- Top level exception handler, just prints out the exception
148 runCommand :: String -> GHCi Bool
152 -> io (putStrLn (show other_exception)) >> return False
156 PhaseFailed phase code ->
157 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
158 ++ show code ++ ")"))
159 Interrupted -> io (putStrLn "Interrupted.")
160 _ -> io (putStrLn (show (dyn :: GhcException)))
165 doCommand (':' : command) = specialCommand command
167 = do expr_expanded <- expandExpr expr
168 -- io (putStrLn ( "Before: " ++ expr ++ "\nAfter: " ++ expr_expanded))
169 expr_ok <- timeIt (do ok <- evalExpr expr_expanded
170 when ok (evalExpr "PrelHandle.hFlush PrelHandle.stdout" >> return ())
171 when ok (evalExpr "PrelHandle.hFlush PrelHandle.stderr" >> return ())
173 when expr_ok (rememberExpr expr_expanded)
176 -- Returned Bool indicates whether or not the expr was successfully
177 -- parsed, renamed and typechecked.
178 evalExpr :: String -> GHCi Bool
180 | null (filter (not.isSpace) expr)
183 = do st <- getGHCiState
184 dflags <- io (getDynFlags)
185 (new_cmstate, maybe_stuff) <-
186 io (cmGetExpr (cmstate st) dflags (current_module st) expr True)
187 setGHCiState st{cmstate = new_cmstate}
189 Nothing -> return False
190 Just (hv, unqual, ty)
191 -> do io (cmRunExpr hv)
192 b <- isOptionSet ShowType
193 io (when b (printForUser stdout unqual (text "::" <+> ppr ty)))
197 let (mod,'.':str) = break (=='.') expr
198 case cmLookupSymbol (mkOrig varName (mkModuleName mod) (_PK_ str)) (cmstate st) of
199 Nothing -> io (putStrLn "nothing.")
204 specialCommand :: String -> GHCi Bool
205 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
206 specialCommand str = do
207 let (cmd,rest) = break isSpace str
208 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
209 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
210 ++ shortHelpText) >> return False)
211 [(_,f)] -> f (dropWhile isSpace rest)
212 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
213 " matches multiple commands (" ++
214 foldr1 (\a b -> a ++ ',':b) (map fst cs)
215 ++ ")") >> return False)
217 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
219 -----------------------------------------------------------------------------
222 help :: String -> GHCi ()
223 help _ = io (putStr helpText)
225 addModule :: String -> GHCi ()
226 addModule _ = throwDyn (OtherError ":add not implemented")
228 setContext :: String -> GHCi ()
230 = throwDyn (OtherError "syntax: `:m <module>'")
231 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
232 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
234 = do st <- getGHCiState
235 setGHCiState st{current_module = mkModuleName m}
237 changeDirectory :: String -> GHCi ()
238 changeDirectory d = io (setCurrentDirectory d)
240 loadModule :: String -> GHCi ()
241 loadModule path = timeIt (loadModule' path)
243 loadModule' path = do
244 state <- getGHCiState
245 cmstate1 <- io (cmUnload (cmstate state))
246 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
248 let new_state = state{
251 current_module = case mods of
252 [] -> defaultCurrentModule
256 setGHCiState new_state
259 | null mods = text "none."
261 punctuate comma (map (text.moduleNameUserString) mods)) <> text "."
264 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
266 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
268 reloadModule :: String -> GHCi ()
270 state <- getGHCiState
272 Nothing -> io (putStr "no current target\n")
274 -> do (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
276 state{cmstate=new_cmstate,
278 current_module = case mods of
279 [] -> defaultCurrentModule
284 reloadModule _ = noArgs ":reload"
286 typeOfExpr :: String -> GHCi ()
288 = do st <- getGHCiState
289 dflags <- io (getDynFlags)
290 (new_cmstate, maybe_ty) <- io (cmGetExpr (cmstate st) dflags
291 (current_module st) str False)
292 setGHCiState st{cmstate = new_cmstate}
295 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
297 quit :: String -> GHCi Bool
300 shellEscape :: String -> GHCi Bool
301 shellEscape str = io (system str >> return False)
303 ----------------------------------------------------------------------------
306 -- set options in the interpreter. Syntax is exactly the same as the
307 -- ghc command line, except that certain options aren't available (-C,
310 -- This is pretty fragile: most options won't work as expected. ToDo:
311 -- figure out which ones & disallow them.
313 setOptions :: String -> GHCi ()
315 = do st <- getGHCiState
316 let opts = options st
317 io $ putStrLn (showSDoc (
318 text "options currently set: " <>
321 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
324 = do -- first, deal with the GHCi opts (+s, +t, etc.)
326 (minus_opts, rest1) = partition isMinus opts
327 (plus_opts, rest2) = partition isPlus rest1
329 if (not (null rest2))
330 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
333 mapM setOpt plus_opts
335 -- now, the GHC flags
336 io (do leftovers <- processArgs static_flags minus_opts []
337 dyn_flags <- readIORef v_InitDynFlags
338 writeIORef v_DynFlags dyn_flags
339 leftovers <- processArgs dynamic_flags leftovers []
340 dyn_flags <- readIORef v_DynFlags
341 writeIORef v_InitDynFlags dyn_flags
342 if (not (null leftovers))
343 then throwDyn (OtherError ("unrecognised flags: " ++
348 unsetOptions :: String -> GHCi ()
350 = do -- first, deal with the GHCi opts (+s, +t, etc.)
352 (minus_opts, rest1) = partition isMinus opts
353 (plus_opts, rest2) = partition isPlus rest1
355 if (not (null rest2))
356 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
359 mapM unsetOpt plus_opts
361 -- can't do GHC flags for now
362 if (not (null minus_opts))
363 then throwDyn (OtherError "can't unset GHC command-line flags")
366 isMinus ('-':s) = True
369 isPlus ('+':s) = True
373 = case strToGHCiOpt str of
374 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
375 Just o -> setOption o
378 = case strToGHCiOpt str of
379 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
380 Just o -> unsetOption o
382 strToGHCiOpt :: String -> (Maybe GHCiOption)
383 strToGHCiOpt "s" = Just ShowTiming
384 strToGHCiOpt "t" = Just ShowType
385 strToGHCiOpt _ = Nothing
387 optToStr :: GHCiOption -> String
388 optToStr ShowTiming = "s"
389 optToStr ShowType = "t"
392 -----------------------------------------------------------------------------
393 -- Code to do last-expression-entered stuff. (a.k.a the $$ facility)
395 -- Take a string and replace $$s in it with the last expr, if any.
396 expandExpr :: String -> GHCi String
398 = do mle <- getLastExpr
399 return (outside mle str)
401 outside mle ('$':'$':cs)
403 Just le -> " (" ++ le ++ ") " ++ outside mle cs
404 Nothing -> outside mle cs
407 outside mle ('"':str) = '"' : inside2 mle str -- "
408 outside mle ('\'':str) = '\'' : inside1 mle str -- '
409 outside mle (c:cs) = c : outside mle cs
411 inside2 mle ('"':cs) = '"' : outside mle cs -- "
412 inside2 mle (c:cs) = c : inside2 mle cs
415 inside1 mle ('\'':cs) = '\'': outside mle cs
416 inside1 mle (c:cs) = c : inside1 mle cs
420 rememberExpr :: String -> GHCi ()
422 = do let cleaned = (clean . reverse . clean . reverse) str
423 let forget_me_not | null cleaned = Nothing
424 | otherwise = Just cleaned
425 setLastExpr forget_me_not
427 clean = dropWhile isSpace
430 -----------------------------------------------------------------------------
433 data GHCiState = GHCiState
435 modules :: [ModuleName],
436 current_module :: ModuleName,
437 target :: Maybe FilePath,
439 options :: [GHCiOption],
440 last_expr :: Maybe String
443 data GHCiOption = ShowTiming | ShowType deriving Eq
445 defaultCurrentModule = mkModuleName "Prelude"
447 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
449 instance Monad GHCi where
450 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
451 return a = GHCi $ \s -> return (s,a)
453 getGHCiState = GHCi $ \s -> return (s,s)
454 setGHCiState s = GHCi $ \_ -> return (s,())
456 isOptionSet :: GHCiOption -> GHCi Bool
458 = do st <- getGHCiState
459 return (opt `elem` options st)
461 setOption :: GHCiOption -> GHCi ()
463 = do st <- getGHCiState
464 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
466 unsetOption :: GHCiOption -> GHCi ()
468 = do st <- getGHCiState
469 setGHCiState (st{ options = filter (/= opt) (options st) })
471 getLastExpr :: GHCi (Maybe String)
473 = do st <- getGHCiState ; return (last_expr st)
475 setLastExpr :: Maybe String -> GHCi ()
476 setLastExpr last_expr
477 = do st <- getGHCiState ; setGHCiState (st{last_expr = last_expr})
479 io m = GHCi $ \s -> m >>= \a -> return (s,a)
481 ghciHandle h (GHCi m) = GHCi $ \s ->
482 Exception.catch (m s) (\e -> unGHCi (h e) s)
483 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
484 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
486 -----------------------------------------------------------------------------
489 linkPackages :: [Package] -> IO ()
490 linkPackages pkgs = mapM_ linkPackage pkgs
492 linkPackage :: Package -> IO ()
493 -- ignore rts and gmp for now (ToDo; better?)
494 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
496 putStr ("Loading package " ++ name pkg ++ " ... ")
497 let dirs = library_dirs pkg
498 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
499 mapM (linkOneObj dirs) objs
500 putStr "resolving ... "
504 linkOneObj dirs obj = do
505 filename <- findFile dirs obj
508 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
509 findFile (d:ds) obj = do
510 let path = d ++ '/':obj
511 b <- doesFileExist path
512 if b then return path else findFile ds obj
514 -----------------------------------------------------------------------------
515 -- timing & statistics
517 timeIt :: GHCi a -> GHCi a
519 = do b <- isOptionSet ShowTiming
522 else do allocs1 <- io $ getAllocations
523 time1 <- io $ getCPUTime
525 allocs2 <- io $ getAllocations
526 time2 <- io $ getCPUTime
527 io $ printTimes (allocs2 - allocs1) (time2 - time1)
530 foreign import "getAllocations" getAllocations :: IO Int
532 printTimes :: Int -> Integer -> IO ()
533 printTimes allocs psecs
534 = do let secs = (fromIntegral psecs / (10^12)) :: Float
535 secs_str = showFFloat (Just 2) secs
537 parens (text (secs_str "") <+> text "secs" <> comma <+>
538 int allocs <+> text "bytes")))