1 -----------------------------------------------------------------------------
2 -- $Id: InteractiveUI.hs,v 1.21 2000/12/12 10:02:57 sewardj Exp $
4 -- GHC Interactive User Interface
6 -- (c) The GHC Team 2000
8 -----------------------------------------------------------------------------
10 module InteractiveUI (interactiveUI) where
12 #include "HsVersions.h"
39 -----------------------------------------------------------------------------
42 \ _____ __ __ ____ _________________________________________________\n\
43 \(| || || (| |) GHC Interactive, version 5.00 \n\
44 \|| __ ||___|| || () For Haskell 98. \n\
45 \|| |) ||---|| || || http://www.haskell.org/ghc \n\
46 \|| || || || || (| Bug reports to: glasgow-haskell-bugs@haskell.org \n\
47 \(|___|| || || (|__|) \\\\______________________________________________________\n"
49 commands :: [(String, String -> GHCi Bool)]
51 ("add", keepGoing addModule),
52 ("cd", keepGoing changeDirectory),
53 ("help", keepGoing help),
54 ("?", keepGoing help),
55 ("load", keepGoing loadModule),
56 ("module", keepGoing setContext),
57 ("reload", keepGoing reloadModule),
58 ("set", keepGoing setOptions),
59 ("type", keepGoing typeOfExpr),
60 ("unset", keepGoing unsetOptions),
64 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
65 keepGoing a str = a str >> return False
67 shortHelpText = "use :? for help.\n"
70 \ Commands available from the prompt:\n\
72 \ <expr> evaluate <expr>\n\
73 \ :add <filename> add a module to the current set\n\
74 \ :cd <dir> change directory to <dir>\n\
75 \ :help, :? display this list of commands\n\
76 \ :load <filename> load a module (and it dependents)\n\
77 \ :module <mod> set the context for expression evaluation to <mod>\n\
78 \ :reload reload the current module set\n\
79 \ :set <option> ... set options\n\
80 \ :unset <option> ... unset options\n\
81 \ :type <expr> show the type of <expr>\n\
83 \ :!<command> run the shell command <command>\n\
85 \ Options for `:set' and `:unset':\n\
87 \ +s print timing/memory stats after each evaluation\n\
88 \ +t print type after evaluation\n\
89 \ -<flags> most GHC command line flags can also be set here\n\
90 \ (eg. -v2, -fglasgow-exts, etc.)\n\
93 interactiveUI :: CmState -> Maybe FilePath -> IO ()
94 interactiveUI cmstate mod = do
95 hPutStrLn stdout ghciWelcomeMsg
97 hSetBuffering stdout NoBuffering
99 -- link in the available packages
100 pkgs <- getPackageInfo
101 linkPackages (reverse pkgs)
103 (cmstate', ok, mods) <-
105 Nothing -> return (cmstate, True, [])
106 Just m -> cmLoadModule cmstate m
111 let this_mod = case mods of
112 [] -> defaultCurrentModule
115 (unGHCi uiLoop) GHCiState{ modules = mods,
116 current_module = this_mod,
119 options = [ShowTiming]}
126 l <- io (readline (moduleNameUserString (current_module st) ++ "> "))
128 l_ok <- io (hGetLine stdin)
139 if quit then exitGHCi else uiLoop
141 exitGHCi = io $ do putStrLn "Leaving GHCi."
143 -- Top level exception handler, just prints out the exception
145 runCommand :: String -> GHCi Bool
149 -> io (putStrLn (show other_exception)) >> return False
153 PhaseFailed phase code ->
154 io ( putStrLn ("Phase " ++ phase ++ " failed (code "
155 ++ show code ++ ")"))
156 Interrupted -> io (putStrLn "Interrupted.")
157 _ -> io (putStrLn (show (dyn :: BarfKind)))
162 doCommand (':' : command) = specialCommand command
163 doCommand expr = timeIt (evalExpr expr) >> return False
166 = do st <- getGHCiState
167 dflags <- io (getDynFlags)
168 (new_cmstate, maybe_stuff) <-
169 io (cmGetExpr (cmstate st) dflags (current_module st) expr)
170 setGHCiState st{cmstate = new_cmstate}
173 Just (hv, unqual, ty)
174 -> do io (cmRunExpr hv)
175 b <- isOptionSet ShowType
176 if b then io (printForUser stdout unqual (text "::" <+> ppr ty))
180 let (mod,'.':str) = break (=='.') expr
181 case cmLookupSymbol (mkOrig varName (mkModuleName mod) (_PK_ str)) (cmstate st) of
182 Nothing -> io (putStrLn "nothing.")
187 specialCommand :: String -> GHCi Bool
188 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
189 specialCommand str = do
190 let (cmd,rest) = break isSpace str
191 case [ (s,f) | (s,f) <- commands, prefixMatch cmd s ] of
192 [] -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n"
193 ++ shortHelpText) >> return False)
194 [(_,f)] -> f (dropWhile isSpace rest)
195 cs -> io (hPutStrLn stdout ("prefix " ++ cmd ++
196 " matches multiple commands (" ++
197 foldr1 (\a b -> a ++ ',':b) (map fst cs)
198 ++ ")") >> return False)
200 noArgs c = io (hPutStrLn stdout ("command `:" ++ c ++ "' takes no arguments"))
202 -----------------------------------------------------------------------------
205 help :: String -> GHCi ()
206 help _ = io (putStr helpText)
208 addModule :: String -> GHCi ()
209 addModule _ = throwDyn (OtherError ":add not implemented")
211 setContext :: String -> GHCi ()
213 = throwDyn (OtherError "syntax: `:m <module>'")
214 setContext m | not (isUpper (head m)) || not (all isAlphaNum (tail m))
215 = throwDyn (OtherError ("strange looking module name: `" ++ m ++ "'"))
217 = do st <- getGHCiState
218 setGHCiState st{current_module = mkModuleName m}
220 changeDirectory :: String -> GHCi ()
221 changeDirectory d = io (setCurrentDirectory d)
223 loadModule :: String -> GHCi ()
224 loadModule path = timeIt (loadModule' path)
226 loadModule' path = do
227 state <- getGHCiState
228 cmstate1 <- io (cmUnload (cmstate state))
229 (cmstate2, ok, mods) <- io (cmLoadModule cmstate1 path)
231 let new_state = state{
234 current_module = case mods of
235 [] -> defaultCurrentModule
239 setGHCiState new_state
242 | null mods = text "none."
244 punctuate comma (map (text.moduleNameUserString) mods)) <> text "."
247 io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
249 io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
251 reloadModule :: String -> GHCi ()
253 state <- getGHCiState
255 Nothing -> io (putStr "no current target\n")
257 -> do (new_cmstate, ok, mods) <- io (cmLoadModule (cmstate state) path)
259 state{cmstate=new_cmstate,
261 current_module = case mods of
262 [] -> defaultCurrentModule
267 reloadModule _ = noArgs ":reload"
269 typeOfExpr :: String -> GHCi ()
271 = do st <- getGHCiState
272 dflags <- io (getDynFlags)
273 (st, maybe_ty) <- io (cmGetExpr (cmstate st) dflags
274 (current_module st) str)
277 Just (_, unqual, ty) -> io (printForUser stdout unqual (ppr ty))
279 quit :: String -> GHCi Bool
282 shellEscape :: String -> GHCi Bool
283 shellEscape str = io (system str >> return False)
285 ----------------------------------------------------------------------------
288 -- set options in the interpreter. Syntax is exactly the same as the
289 -- ghc command line, except that certain options aren't available (-C,
292 -- This is pretty fragile: most options won't work as expected. ToDo:
293 -- figure out which ones & disallow them.
295 setOptions :: String -> GHCi ()
297 = do st <- getGHCiState
298 let opts = options st
299 io $ putStrLn (showSDoc (
300 text "options currently set: " <>
303 else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
306 = do -- first, deal with the GHCi opts (+s, +t, etc.)
308 (minus_opts, rest1) = partition isMinus opts
309 (plus_opts, rest2) = partition isPlus rest1
311 if (not (null rest2))
312 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
315 mapM setOpt plus_opts
317 -- now, the GHC flags
318 io (do leftovers <- processArgs static_flags minus_opts []
319 dyn_flags <- readIORef v_InitDynFlags
320 writeIORef v_DynFlags dyn_flags
321 leftovers <- processArgs dynamic_flags leftovers []
322 dyn_flags <- readIORef v_DynFlags
323 writeIORef v_InitDynFlags dyn_flags
324 if (not (null leftovers))
325 then throwDyn (OtherError ("unrecognised flags: " ++
330 unsetOptions :: String -> GHCi ()
332 = do -- first, deal with the GHCi opts (+s, +t, etc.)
334 (minus_opts, rest1) = partition isMinus opts
335 (plus_opts, rest2) = partition isPlus rest1
337 if (not (null rest2))
338 then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
341 mapM unsetOpt plus_opts
343 -- can't do GHC flags for now
344 if (not (null minus_opts))
345 then throwDyn (OtherError "can't unset GHC command-line flags")
348 isMinus ('-':s) = True
351 isPlus ('+':s) = True
355 = case strToGHCiOpt str of
356 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
357 Just o -> setOption o
360 = case strToGHCiOpt str of
361 Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
362 Just o -> unsetOption o
364 strToGHCiOpt :: String -> (Maybe GHCiOption)
365 strToGHCiOpt "s" = Just ShowTiming
366 strToGHCiOpt "t" = Just ShowType
367 strToGHCiOpt _ = Nothing
369 optToStr :: GHCiOption -> String
370 optToStr ShowTiming = "s"
371 optToStr ShowType = "t"
373 -----------------------------------------------------------------------------
376 data GHCiState = GHCiState
378 modules :: [ModuleName],
379 current_module :: ModuleName,
380 target :: Maybe FilePath,
382 options :: [GHCiOption]
385 data GHCiOption = ShowTiming | ShowType deriving Eq
387 defaultCurrentModule = mkModuleName "Prelude"
389 newtype GHCi a = GHCi { unGHCi :: GHCiState -> IO (GHCiState, a) }
391 instance Monad GHCi where
392 (GHCi m) >>= k = GHCi $ \s -> m s >>= \(s,a) -> unGHCi (k a) s
393 return a = GHCi $ \s -> return (s,a)
395 getGHCiState = GHCi $ \s -> return (s,s)
396 setGHCiState s = GHCi $ \_ -> return (s,())
398 isOptionSet :: GHCiOption -> GHCi Bool
400 = do st <- getGHCiState
401 return (opt `elem` options st)
403 setOption :: GHCiOption -> GHCi ()
405 = do st <- getGHCiState
406 setGHCiState (st{ options = opt : filter (/= opt) (options st) })
408 unsetOption :: GHCiOption -> GHCi ()
410 = do st <- getGHCiState
411 setGHCiState (st{ options = filter (/= opt) (options st) })
413 io m = GHCi $ \s -> m >>= \a -> return (s,a)
415 ghciHandle h (GHCi m) = GHCi $ \s ->
416 Exception.catch (m s) (\e -> unGHCi (h e) s)
417 ghciHandleDyn h (GHCi m) = GHCi $ \s ->
418 Exception.catchDyn (m s) (\e -> unGHCi (h e) s)
420 -----------------------------------------------------------------------------
423 linkPackages :: [Package] -> IO ()
424 linkPackages pkgs = mapM_ linkPackage pkgs
426 linkPackage :: Package -> IO ()
427 -- ignore rts and gmp for now (ToDo; better?)
428 linkPackage pkg | name pkg `elem` ["rts", "gmp"] = return ()
430 putStr ("Loading package " ++ name pkg ++ " ... ")
431 let dirs = library_dirs pkg
432 let objs = map (++".o") (hs_libraries pkg ++ extra_libraries pkg)
433 mapM (linkOneObj dirs) objs
434 putStr "resolving ... "
438 linkOneObj dirs obj = do
439 filename <- findFile dirs obj
442 findFile [] obj = throwDyn (OtherError ("can't find " ++ obj))
443 findFile (d:ds) obj = do
444 let path = d ++ '/':obj
445 b <- doesFileExist path
446 if b then return path else findFile ds obj
448 -----------------------------------------------------------------------------
449 -- timing & statistics
451 timeIt :: GHCi a -> GHCi a
453 = do b <- isOptionSet ShowTiming
456 else do allocs1 <- io $ getAllocations
457 time1 <- io $ getCPUTime
459 allocs2 <- io $ getAllocations
460 time2 <- io $ getCPUTime
461 io $ printTimes (allocs2 - allocs1) (time2 - time1)
464 foreign import "getAllocations" getAllocations :: IO Int
466 printTimes :: Int -> Integer -> IO ()
467 printTimes allocs psecs
468 = do let secs = (fromIntegral psecs / (10^12)) :: Float
469 secs_str = showFFloat (Just 2) secs
471 parens (text (secs_str "") <+> text "secs" <> comma <+>
472 int allocs <+> text "bytes")))