1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.25 2001/01/10 17:19:01 sewardj 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(..) )
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]}
128 l <- io (readline (moduleNameUserString (current_module st) ++ "> "))
130 l_ok <- io (hGetLine stdin)
141 if quit then exitGHCi else uiLoop
143 exitGHCi = io $ do putStrLn "Leaving GHCi."
145 -- Top level exception handler, just prints out the exception
147 runCommand :: String -> GHCi Bool
151 -> io (putStrLn (show other_exception)) >> return False
155 PhaseFailed phase code ->
156 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
157 ++ show code ++ ")"))
158 Interrupted -> io (putStrLn "Interrupted.")
159 _ -> io (putStrLn (show (dyn :: GhcException)))
164 doCommand (':' : command) = specialCommand command
165 doCommand expr = do timeIt (evalExpr expr
166 >> evalExpr "Prelude.putStr \"\n\"")
170 = do st <- getGHCiState
171 dflags <- io (getDynFlags)
172 (new_cmstate, maybe_stuff) <-
173 io (cmGetExpr (cmstate st) dflags (current_module st) expr)
174 setGHCiState st{cmstate = new_cmstate}
177 Just (hv, unqual, ty)
178 -> do io (cmRunExpr hv)
179 b <- isOptionSet ShowType
180 if b then io (printForUser stdout unqual (text "::" <+> ppr ty))
184 let (mod,'.':str) = break (=='.') expr
185 case cmLookupSymbol (mkOrig varName (mkModuleName mod) (_PK_ str)) (cmstate st) of
186 Nothing -> io (putStrLn "nothing.")
191 specialCommand :: String -> GHCi Bool
192 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
193 specialCommand str = do
194 let (cmd,rest) = break isSpace str
195 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
196 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
197 ++ shortHelpText) >> return False)
198 [(_,f)] -> f (dropWhile isSpace rest)
199 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
200 " matches multiple commands (" ++
201 foldr1 (\a b -> a ++ ',':b) (map fst cs)
202 ++ ")") >> return False)
204 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
206 -----------------------------------------------------------------------------
209 help :: String -> GHCi ()
210 help _ = io (putStr helpText)
212 addModule :: String -> GHCi ()
213 addModule _ = throwDyn (OtherError ":add not implemented")
215 setContext :: String -> GHCi ()
217 = throwDyn (OtherError "syntax: `:m <module>'")
218 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
219 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
221 = do st <- getGHCiState
222 setGHCiState st{current_module = mkModuleName m}
224 changeDirectory :: String -> GHCi ()
225 changeDirectory d = io (setCurrentDirectory d)
227 loadModule :: String -> GHCi ()
228 loadModule path = timeIt (loadModule' path)
230 loadModule' path = do
231 state <- getGHCiState
232 cmstate1 <- io (cmUnload (cmstate state))
233 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
235 let new_state = state{
238 current_module = case mods of
239 [] -> defaultCurrentModule
243 setGHCiState new_state
246 | null mods = text "none."
248 punctuate comma (map (text.moduleNameUserString) mods)) <> text "."
251 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
253 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
255 reloadModule :: String -> GHCi ()
257 state <- getGHCiState
259 Nothing -> io (putStr "no current target\n")
261 -> do (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
263 state{cmstate=new_cmstate,
265 current_module = case mods of
266 [] -> defaultCurrentModule
271 reloadModule _ = noArgs ":reload"
273 typeOfExpr :: String -> GHCi ()
275 = do st <- getGHCiState
276 dflags <- io (getDynFlags)
277 (st, maybe_ty) <- io (cmGetExpr (cmstate st) dflags
278 (current_module st) str)
281 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
283 quit :: String -> GHCi Bool
286 shellEscape :: String -> GHCi Bool
287 shellEscape str = io (system str >> return False)
289 ----------------------------------------------------------------------------
292 -- set options in the interpreter. Syntax is exactly the same as the
293 -- ghc command line, except that certain options aren't available (-C,
296 -- This is pretty fragile: most options won't work as expected. ToDo:
297 -- figure out which ones & disallow them.
299 setOptions :: String -> GHCi ()
301 = do st <- getGHCiState
302 let opts = options st
303 io $ putStrLn (showSDoc (
304 text "options currently set: " <>
307 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
310 = do -- first, deal with the GHCi opts (+s, +t, etc.)
312 (minus_opts, rest1) = partition isMinus opts
313 (plus_opts, rest2) = partition isPlus rest1
315 if (not (null rest2))
316 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
319 mapM setOpt plus_opts
321 -- now, the GHC flags
322 io (do leftovers <- processArgs static_flags minus_opts []
323 dyn_flags <- readIORef v_InitDynFlags
324 writeIORef v_DynFlags dyn_flags
325 leftovers <- processArgs dynamic_flags leftovers []
326 dyn_flags <- readIORef v_DynFlags
327 writeIORef v_InitDynFlags dyn_flags
328 if (not (null leftovers))
329 then throwDyn (OtherError ("unrecognised flags: " ++
334 unsetOptions :: String -> GHCi ()
336 = do -- first, deal with the GHCi opts (+s, +t, etc.)
338 (minus_opts, rest1) = partition isMinus opts
339 (plus_opts, rest2) = partition isPlus rest1
341 if (not (null rest2))
342 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
345 mapM unsetOpt plus_opts
347 -- can't do GHC flags for now
348 if (not (null minus_opts))
349 then throwDyn (OtherError "can't unset GHC command-line flags")
352 isMinus ('-':s) = True
355 isPlus ('+':s) = True
359 = case strToGHCiOpt str of
360 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
361 Just o -> setOption o
364 = case strToGHCiOpt str of
365 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
366 Just o -> unsetOption o
368 strToGHCiOpt :: String -> (Maybe GHCiOption)
369 strToGHCiOpt "s" = Just ShowTiming
370 strToGHCiOpt "t" = Just ShowType
371 strToGHCiOpt _ = Nothing
373 optToStr :: GHCiOption -> String
374 optToStr ShowTiming = "s"
375 optToStr ShowType = "t"
377 -----------------------------------------------------------------------------
380 data GHCiState = GHCiState
382 modules :: [ModuleName],
383 current_module :: ModuleName,
384 target :: Maybe FilePath,
386 options :: [GHCiOption]
389 data GHCiOption = ShowTiming | ShowType deriving Eq
391 defaultCurrentModule = mkModuleName "Prelude"
393 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
395 instance Monad GHCi where
396 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
397 return a = GHCi $ \s -> return (s,a)
399 getGHCiState = GHCi $ \s -> return (s,s)
400 setGHCiState s = GHCi $ \_ -> return (s,())
402 isOptionSet :: GHCiOption -> GHCi Bool
404 = do st <- getGHCiState
405 return (opt `elem` options st)
407 setOption :: GHCiOption -> GHCi ()
409 = do st <- getGHCiState
410 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
412 unsetOption :: GHCiOption -> GHCi ()
414 = do st <- getGHCiState
415 setGHCiState (st{ options = filter (/= opt) (options st) })
417 io m = GHCi $ \s -> m >>= \a -> return (s,a)
419 ghciHandle h (GHCi m) = GHCi $ \s ->
420 Exception.catch (m s) (\e -> unGHCi (h e) s)
421 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
422 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
424 -----------------------------------------------------------------------------
427 linkPackages :: [Package] -> IO ()
428 linkPackages pkgs = mapM_ linkPackage pkgs
430 linkPackage :: Package -> IO ()
431 -- ignore rts and gmp for now (ToDo; better?)
432 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
434 putStr ("Loading package " ++ name pkg ++ " ... ")
435 let dirs = library_dirs pkg
436 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
437 mapM (linkOneObj dirs) objs
438 putStr "resolving ... "
442 linkOneObj dirs obj = do
443 filename <- findFile dirs obj
446 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
447 findFile (d:ds) obj = do
448 let path = d ++ '/':obj
449 b <- doesFileExist path
450 if b then return path else findFile ds obj
452 -----------------------------------------------------------------------------
453 -- timing & statistics
455 timeIt :: GHCi a -> GHCi a
457 = do b <- isOptionSet ShowTiming
460 else do allocs1 <- io $ getAllocations
461 time1 <- io $ getCPUTime
463 allocs2 <- io $ getAllocations
464 time2 <- io $ getCPUTime
465 io $ printTimes (allocs2 - allocs1) (time2 - time1)
468 foreign import "getAllocations" getAllocations :: IO Int
470 printTimes :: Int -> Integer -> IO ()
471 printTimes allocs psecs
472 = do let secs = (fromIntegral psecs / (10^12)) :: Float
473 secs_str = showFFloat (Just 2) secs
475 parens (text (secs_str "") <+> text "secs" <> comma <+>
476 int allocs <+> text "bytes")))