1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.22 2000/12/12 10:11:21 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 TypeRep {- instance Outputable Type; do not delete -}
40 -----------------------------------------------------------------------------
43 \ _____ __ __ ____ _________________________________________________\n\
44 \(| || || (| |) GHC Interactive, version 5.00 \n\
45 \|| __ ||___|| || () For Haskell 98. \n\
46 \|| |) ||---|| || || http://www.haskell.org/ghc \n\
47 \|| || || || || (| Bug reports to: glasgow-haskell-bugs@haskell.org \n\
48 \(|___|| || || (|__|) \\\\______________________________________________________\n"
50 commands :: [(String, String -> GHCi Bool)]
52 ("add", keepGoing addModule),
53 ("cd", keepGoing changeDirectory),
54 ("help", keepGoing help),
55 ("?", keepGoing help),
56 ("load", keepGoing loadModule),
57 ("module", keepGoing setContext),
58 ("reload", keepGoing reloadModule),
59 ("set", keepGoing setOptions),
60 ("type", keepGoing typeOfExpr),
61 ("unset", keepGoing unsetOptions),
65 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
66 keepGoing a str = a str >> return False
68 shortHelpText = "use :? for help.\n"
71 \ Commands available from the prompt:\n\
73 \ <expr> evaluate <expr>\n\
74 \ :add <filename> add a module to the current set\n\
75 \ :cd <dir> change directory to <dir>\n\
76 \ :help, :? display this list of commands\n\
77 \ :load <filename> load a module (and it dependents)\n\
78 \ :module <mod> set the context for expression evaluation to <mod>\n\
79 \ :reload reload the current module set\n\
80 \ :set <option> ... set options\n\
81 \ :unset <option> ... unset options\n\
82 \ :type <expr> show the type of <expr>\n\
84 \ :!<command> run the shell command <command>\n\
86 \ Options for `:set' and `:unset':\n\
88 \ +s print timing/memory stats after each evaluation\n\
89 \ +t print type after evaluation\n\
90 \ -<flags> most GHC command line flags can also be set here\n\
91 \ (eg. -v2, -fglasgow-exts, etc.)\n\
94 interactiveUI :: CmState -> Maybe FilePath -> IO ()
95 interactiveUI cmstate mod = do
96 hPutStrLn stdout ghciWelcomeMsg
98 hSetBuffering stdout NoBuffering
100 -- link in the available packages
101 pkgs <- getPackageInfo
102 linkPackages (reverse pkgs)
104 (cmstate', ok, mods) <-
106 Nothing -> return (cmstate, True, [])
107 Just m -> cmLoadModule cmstate m
112 let this_mod = case mods of
113 [] -> defaultCurrentModule
116 (unGHCi uiLoop) GHCiState{ modules = mods,
117 current_module = this_mod,
120 options = [ShowTiming]}
127 l <- io (readline (moduleNameUserString (current_module st) ++ "> "))
129 l_ok <- io (hGetLine stdin)
140 if quit then exitGHCi else uiLoop
142 exitGHCi = io $ do putStrLn "Leaving GHCi."
144 -- Top level exception handler, just prints out the exception
146 runCommand :: String -> GHCi Bool
150 -> io (putStrLn (show other_exception)) >> return False
154 PhaseFailed phase code ->
155 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
156 ++ show code ++ ")"))
157 Interrupted -> io (putStrLn "Interrupted.")
158 _ -> io (putStrLn (show (dyn :: BarfKind)))
163 doCommand (':' : command) = specialCommand command
164 doCommand expr = timeIt (evalExpr expr) >> return False
167 = do st <- getGHCiState
168 dflags <- io (getDynFlags)
169 (new_cmstate, maybe_stuff) <-
170 io (cmGetExpr (cmstate st) dflags (current_module st) expr)
171 setGHCiState st{cmstate = new_cmstate}
174 Just (hv, unqual, ty)
175 -> do io (cmRunExpr hv)
176 b <- isOptionSet ShowType
177 if b then io (printForUser stdout unqual (text "::" <+> ppr ty))
181 let (mod,'.':str) = break (=='.') expr
182 case cmLookupSymbol (mkOrig varName (mkModuleName mod) (_PK_ str)) (cmstate st) of
183 Nothing -> io (putStrLn "nothing.")
188 specialCommand :: String -> GHCi Bool
189 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
190 specialCommand str = do
191 let (cmd,rest) = break isSpace str
192 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
193 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
194 ++ shortHelpText) >> return False)
195 [(_,f)] -> f (dropWhile isSpace rest)
196 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
197 " matches multiple commands (" ++
198 foldr1 (\a b -> a ++ ',':b) (map fst cs)
199 ++ ")") >> return False)
201 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
203 -----------------------------------------------------------------------------
206 help :: String -> GHCi ()
207 help _ = io (putStr helpText)
209 addModule :: String -> GHCi ()
210 addModule _ = throwDyn (OtherError ":add not implemented")
212 setContext :: String -> GHCi ()
214 = throwDyn (OtherError "syntax: `:m <module>'")
215 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
216 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
218 = do st <- getGHCiState
219 setGHCiState st{current_module = mkModuleName m}
221 changeDirectory :: String -> GHCi ()
222 changeDirectory d = io (setCurrentDirectory d)
224 loadModule :: String -> GHCi ()
225 loadModule path = timeIt (loadModule' path)
227 loadModule' path = do
228 state <- getGHCiState
229 cmstate1 <- io (cmUnload (cmstate state))
230 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
232 let new_state = state{
235 current_module = case mods of
236 [] -> defaultCurrentModule
240 setGHCiState new_state
243 | null mods = text "none."
245 punctuate comma (map (text.moduleNameUserString) mods)) <> text "."
248 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
250 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
252 reloadModule :: String -> GHCi ()
254 state <- getGHCiState
256 Nothing -> io (putStr "no current target\n")
258 -> do (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
260 state{cmstate=new_cmstate,
262 current_module = case mods of
263 [] -> defaultCurrentModule
268 reloadModule _ = noArgs ":reload"
270 typeOfExpr :: String -> GHCi ()
272 = do st <- getGHCiState
273 dflags <- io (getDynFlags)
274 (st, maybe_ty) <- io (cmGetExpr (cmstate st) dflags
275 (current_module st) str)
278 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
280 quit :: String -> GHCi Bool
283 shellEscape :: String -> GHCi Bool
284 shellEscape str = io (system str >> return False)
286 ----------------------------------------------------------------------------
289 -- set options in the interpreter. Syntax is exactly the same as the
290 -- ghc command line, except that certain options aren't available (-C,
293 -- This is pretty fragile: most options won't work as expected. ToDo:
294 -- figure out which ones & disallow them.
296 setOptions :: String -> GHCi ()
298 = do st <- getGHCiState
299 let opts = options st
300 io $ putStrLn (showSDoc (
301 text "options currently set: " <>
304 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
307 = do -- first, deal with the GHCi opts (+s, +t, etc.)
309 (minus_opts, rest1) = partition isMinus opts
310 (plus_opts, rest2) = partition isPlus rest1
312 if (not (null rest2))
313 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
316 mapM setOpt plus_opts
318 -- now, the GHC flags
319 io (do leftovers <- processArgs static_flags minus_opts []
320 dyn_flags <- readIORef v_InitDynFlags
321 writeIORef v_DynFlags dyn_flags
322 leftovers <- processArgs dynamic_flags leftovers []
323 dyn_flags <- readIORef v_DynFlags
324 writeIORef v_InitDynFlags dyn_flags
325 if (not (null leftovers))
326 then throwDyn (OtherError ("unrecognised flags: " ++
331 unsetOptions :: String -> GHCi ()
333 = do -- first, deal with the GHCi opts (+s, +t, etc.)
335 (minus_opts, rest1) = partition isMinus opts
336 (plus_opts, rest2) = partition isPlus rest1
338 if (not (null rest2))
339 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
342 mapM unsetOpt plus_opts
344 -- can't do GHC flags for now
345 if (not (null minus_opts))
346 then throwDyn (OtherError "can't unset GHC command-line flags")
349 isMinus ('-':s) = True
352 isPlus ('+':s) = True
356 = case strToGHCiOpt str of
357 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
358 Just o -> setOption o
361 = case strToGHCiOpt str of
362 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
363 Just o -> unsetOption o
365 strToGHCiOpt :: String -> (Maybe GHCiOption)
366 strToGHCiOpt "s" = Just ShowTiming
367 strToGHCiOpt "t" = Just ShowType
368 strToGHCiOpt _ = Nothing
370 optToStr :: GHCiOption -> String
371 optToStr ShowTiming = "s"
372 optToStr ShowType = "t"
374 -----------------------------------------------------------------------------
377 data GHCiState = GHCiState
379 modules :: [ModuleName],
380 current_module :: ModuleName,
381 target :: Maybe FilePath,
383 options :: [GHCiOption]
386 data GHCiOption = ShowTiming | ShowType deriving Eq
388 defaultCurrentModule = mkModuleName "Prelude"
390 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
392 instance Monad GHCi where
393 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
394 return a = GHCi $ \s -> return (s,a)
396 getGHCiState = GHCi $ \s -> return (s,s)
397 setGHCiState s = GHCi $ \_ -> return (s,())
399 isOptionSet :: GHCiOption -> GHCi Bool
401 = do st <- getGHCiState
402 return (opt `elem` options st)
404 setOption :: GHCiOption -> GHCi ()
406 = do st <- getGHCiState
407 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
409 unsetOption :: GHCiOption -> GHCi ()
411 = do st <- getGHCiState
412 setGHCiState (st{ options = filter (/= opt) (options st) })
414 io m = GHCi $ \s -> m >>= \a -> return (s,a)
416 ghciHandle h (GHCi m) = GHCi $ \s ->
417 Exception.catch (m s) (\e -> unGHCi (h e) s)
418 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
419 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
421 -----------------------------------------------------------------------------
424 linkPackages :: [Package] -> IO ()
425 linkPackages pkgs = mapM_ linkPackage pkgs
427 linkPackage :: Package -> IO ()
428 -- ignore rts and gmp for now (ToDo; better?)
429 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
431 putStr ("Loading package " ++ name pkg ++ " ... ")
432 let dirs = library_dirs pkg
433 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
434 mapM (linkOneObj dirs) objs
435 putStr "resolving ... "
439 linkOneObj dirs obj = do
440 filename <- findFile dirs obj
443 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
444 findFile (d:ds) obj = do
445 let path = d ++ '/':obj
446 b <- doesFileExist path
447 if b then return path else findFile ds obj
449 -----------------------------------------------------------------------------
450 -- timing & statistics
452 timeIt :: GHCi a -> GHCi a
454 = do b <- isOptionSet ShowTiming
457 else do allocs1 <- io $ getAllocations
458 time1 <- io $ getCPUTime
460 allocs2 <- io $ getAllocations
461 time2 <- io $ getCPUTime
462 io $ printTimes (allocs2 - allocs1) (time2 - time1)
465 foreign import "getAllocations" getAllocations :: IO Int
467 printTimes :: Int -> Integer -> IO ()
468 printTimes allocs psecs
469 = do let secs = (fromIntegral psecs / (10^12)) :: Float
470 secs_str = showFFloat (Just 2) secs
472 parens (text (secs_str "") <+> text "secs" <> comma <+>
473 int allocs <+> text "bytes")))