[project @ 2004-10-13 08:48:47 by simonmar]
[ghc-hetmet.git] / ghc / compiler / ghci / InteractiveUI.hs
index 3b0baa2..5b3d1e3 100644 (file)
@@ -1,6 +1,6 @@
 {-# OPTIONS -#include "Linker.h" #-}
 -----------------------------------------------------------------------------
--- $Id: InteractiveUI.hs,v 1.167 2004/07/21 09:25:42 simonpj Exp $
+-- $Id: InteractiveUI.hs,v 1.178 2004/10/13 08:48:47 simonmar Exp $
 --
 -- GHC Interactive User Interface
 --
@@ -12,14 +12,14 @@ module InteractiveUI (
        ghciWelcomeMsg
    ) where
 
-#include "../includes/config.h"
+#include "../includes/ghcconfig.h"
 #include "HsVersions.h"
 
 import CompManager
 import HscTypes                ( TyThing(..), HomeModInfo(hm_linkable), HomePackageTable,
                          isObjectLinkable, GhciMode(..) )
 import IfaceSyn                ( IfaceType, IfaceDecl(..), IfaceClassOp(..), IfaceConDecls(..), IfaceConDecl(..), 
-                         pprIfaceDeclHead, pprParendIfaceType, pprIfaceForAllPart )
+                         IfaceInst(..), pprIfaceDeclHead, pprParendIfaceType, pprIfaceForAllPart )
 import FunDeps         ( pprFundeps )
 import DriverFlags
 import DriverState
@@ -37,6 +37,7 @@ import CmdLineOpts    ( DynFlag(..), DynFlags(..), getDynFlags, saveDynFlags,
                          restoreDynFlags, dopt_unset )
 import Panic           hiding ( showException )
 import Config
+import SrcLoc          ( SrcLoc, isGoodSrcLoc )
 
 #ifndef mingw32_HOST_OS
 import DriverUtil( handle )
@@ -46,7 +47,7 @@ import System.Posix
 #endif
 #endif
 
-#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
+#ifdef USE_READLINE
 import Control.Concurrent      ( yield )       -- Used in readline loop
 import System.Console.Readline as Readline
 #endif
@@ -67,6 +68,7 @@ import System.Directory
 import System.IO as IO
 import Data.Char
 import Control.Monad as Monad
+import Foreign.StablePtr       ( newStablePtr )
 
 import GHC.Exts                ( unsafeCoerce# )
 
@@ -76,12 +78,12 @@ import System.Posix.Internals ( setNonBlockingFD )
 
 -----------------------------------------------------------------------------
 
-ghciWelcomeMsg = "\ 
-\   ___         ___ _\n\ 
-\  / _ \\ /\\  /\\/ __(_)\n\ 
-\ / /_\\// /_/ / /  | |      GHC Interactive, version " ++ cProjectVersion ++ ", for Haskell 98.\n\ 
-\/ /_\\\\/ __  / /___| |      http://www.haskell.org/ghc/\n\ 
-\\\____/\\/ /_/\\____/|_|      Type :? for help.\n"
+ghciWelcomeMsg =
+ "   ___         ___ _\n"++
+ "  / _ \\ /\\  /\\/ __(_)\n"++
+ " / /_\\// /_/ / /  | |      GHC Interactive, version " ++ cProjectVersion ++ ", for Haskell 98.\n"++
+ "/ /_\\\\/ __  / /___| |      http://www.haskell.org/ghc/\n"++
+ "\\____/\\/ /_/\\____/|_|      Type :? for help.\n"
 
 GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
 
@@ -115,42 +117,42 @@ keepGoingPaths a str = a (toArgs str) >> return False
 shortHelpText = "use :? for help.\n"
 
 -- NOTE: spaces at the end of each line to workaround CPP/string gap bug.
-helpText = "\ 
-\ Commands available from the prompt:\n\ 
-\\n\ 
-\   <stmt>                    evaluate/run <stmt>\n\ 
-\   :add <filename> ...        add module(s) to the current target set\n\ 
-\   :browse [*]<module>               display the names defined by <module>\n\ 
-\   :cd <dir>                 change directory to <dir>\n\ 
-\   :def <cmd> <expr>          define a command :<cmd>\n\ 
-\   :help, :?                 display this list of commands\n\ 
-\   :info [<name> ...]         display information about the given names\n\ 
-\   :load <filename> ...       load module(s) and their dependents\n\ 
-\   :module [+/-] [*]<mod> ... set the context for expression evaluation\n\ 
-\   :reload                   reload the current module set\n\ 
-\\n\ 
-\   :set <option> ...         set options\n\ 
-\   :set args <arg> ...               set the arguments returned by System.getArgs\n\ 
-\   :set prog <progname>       set the value returned by System.getProgName\n\ 
-\\n\ 
-\   :show modules             show the currently loaded modules\n\ 
-\   :show bindings            show the current bindings made at the prompt\n\ 
-\\n\ 
-\   :type <expr>              show the type of <expr>\n\ 
-\   :kind <type>              show the kind of <type>\n\ 
-\   :undef <cmd>              undefine user-defined command :<cmd>\n\ 
-\   :unset <option> ...               unset options\n\ 
-\   :quit                     exit GHCi\n\ 
-\   :!<command>                       run the shell command <command>\n\ 
-\\n\ 
-\ Options for `:set' and `:unset':\n\ 
-\\n\ 
-\    +r                        revert top-level expressions after each evaluation\n\ 
-\    +s                 print timing/memory stats after each evaluation\n\ 
-\    +t                        print type after evaluation\n\ 
-\    -<flags>          most GHC command line flags can also be set here\n\ 
-\                         (eg. -v2, -fglasgow-exts, etc.)\n\ 
-\"
+helpText =
+ " Commands available from the prompt:\n" ++
+ "\n" ++
+ "   <stmt>                      evaluate/run <stmt>\n" ++
+ "   :add <filename> ...         add module(s) to the current target set\n" ++
+ "   :browse [*]<module>         display the names defined by <module>\n" ++
+ "   :cd <dir>                   change directory to <dir>\n" ++
+ "   :def <cmd> <expr>           define a command :<cmd>\n" ++
+ "   :help, :?                   display this list of commands\n" ++
+ "   :info [<name> ...]          display information about the given names\n" ++
+ "   :load <filename> ...        load module(s) and their dependents\n" ++
+ "   :module [+/-] [*]<mod> ...  set the context for expression evaluation\n" ++
+ "   :reload                     reload the current module set\n" ++
+ "\n" ++
+ "   :set <option> ...           set options\n" ++
+ "   :set args <arg> ...         set the arguments returned by System.getArgs\n" ++
+ "   :set prog <progname>        set the value returned by System.getProgName\n" ++
+ "\n" ++
+ "   :show modules               show the currently loaded modules\n" ++
+ "   :show bindings              show the current bindings made at the prompt\n" ++
+ "\n" ++
+ "   :type <expr>                show the type of <expr>\n" ++
+ "   :kind <type>                show the kind of <type>\n" ++
+ "   :undef <cmd>                undefine user-defined command :<cmd>\n" ++
+ "   :unset <option> ...         unset options\n" ++
+ "   :quit                       exit GHCi\n" ++
+ "   :!<command>                 run the shell command <command>\n" ++
+ "\n" ++
+ " Options for ':set' and ':unset':\n" ++
+ "\n" ++
+ "    +r            revert top-level expressions after each evaluation\n" ++
+ "    +s            print timing/memory stats after each evaluation\n" ++
+ "    +t            print type after evaluation\n" ++
+ "    -<flags>      most GHC command line flags can also be set here\n" ++
+ "                         (eg. -v2, -fglasgow-exts, etc.)\n"
+
 
 interactiveUI :: [FilePath] -> Maybe String -> IO ()
 interactiveUI srcs maybe_expr = do
@@ -158,6 +160,18 @@ interactiveUI srcs maybe_expr = do
 
    cmstate <- cmInit Interactive dflags;
 
+   -- HACK! If we happen to get into an infinite loop (eg the user
+   -- types 'let x=x in x' at the prompt), then the thread will block
+   -- on a blackhole, and become unreachable during GC.  The GC will
+   -- detect that it is unreachable and send it the NonTermination
+   -- exception.  However, since the thread is unreachable, everything
+   -- it refers to might be finalized, including the standard Handles.
+   -- This sounds like a bug, but we don't have a good solution right
+   -- now.
+   newStablePtr stdin
+   newStablePtr stdout
+   newStablePtr stderr
+
    hFlush stdout
    hSetBuffering stdout NoBuffering
 
@@ -171,7 +185,7 @@ interactiveUI srcs maybe_expr = do
        -- initial context is just the Prelude
    cmstate <- cmSetContext cmstate [] ["Prelude"]
 
-#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
+#ifdef USE_READLINE
    Readline.initialize
 #endif
 
@@ -182,7 +196,7 @@ interactiveUI srcs maybe_expr = do
                   cmstate = cmstate,
                   options = [] }
 
-#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
+#ifdef USE_READLINE
    Readline.resetTerminal Nothing
 #endif
 
@@ -251,7 +265,7 @@ interactiveLoop is_tty show_prompt = do
                        _other      -> return ()) $ do
 
   -- read commands from stdin
-#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
+#ifdef USE_READLINE
   if (is_tty) 
        then readlineLoop
        else fileLoop stdin show_prompt
@@ -317,7 +331,7 @@ stringLoop (s:ss) = do
 mkPrompt toplevs exports
    = concat (intersperse " " (map ('*':) toplevs ++ exports)) ++ "> "
 
-#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
+#ifdef USE_READLINE
 readlineLoop :: GHCi ()
 readlineLoop = do
    cmstate <- getCmState
@@ -414,7 +428,7 @@ specialCommand str = do
   let (cmd,rest) = break isSpace str
   cmds <- io (readIORef commands)
   case [ (s,f) | (s,f) <- cmds, prefixMatch cmd s ] of
-     []      -> io (hPutStr stdout ("unknown command `:" ++ cmd ++ "'\n" 
+     []      -> io (hPutStr stdout ("unknown command ':" ++ cmd ++ "'\n" 
                                    ++ shortHelpText) >> return False)
      [(_,f)] -> f (dropWhile isSpace rest)
      cs      -> io (hPutStrLn stdout ("prefix " ++ cmd ++ 
@@ -422,7 +436,7 @@ specialCommand str = do
                                       foldr1 (\a b -> a ++ ',':b) (map fst cs)
                                         ++ ")") >> return False)
 
-noArgs c = throwDyn (CmdLineError ("command `" ++ c ++ "' takes no arguments"))
+noArgs c = throwDyn (CmdLineError ("command '" ++ c ++ "' takes no arguments"))
 
 
 -----------------------------------------------------------------------------
@@ -471,29 +485,46 @@ help :: String -> GHCi ()
 help _ = io (putStr helpText)
 
 info :: String -> GHCi ()
-info "" = throwDyn (CmdLineError "syntax: `:i <thing-you-want-info-about>'")
+info "" = throwDyn (CmdLineError "syntax: ':i <thing-you-want-info-about>'")
 info s  = do { let names = words s
             ; init_cms <- getCmState
             ; mapM_ (infoThing init_cms) names }
   where
     infoThing cms name
-       = do { stuff <- io (cmInfoThing cms name)
+       = do { stuff <- io (cmGetInfo cms name)
             ; io (putStrLn (showSDocForUser (cmGetPrintUnqual cms) $
-                  vcat (intersperse (text "") (map (showThing name) stuff)))) }
-
-showThing :: String -> (IfaceDecl, Fixity) -> SDoc
-showThing name (thing, fixity) 
-    = vcat [ showDecl (\occ -> name == occNameUserString occ) thing, 
-            showFixity fixity ]
+                  vcat (intersperse (text "") (map showThing stuff)))) }
+
+showThing :: GetInfoResult -> SDoc
+showThing  (wanted_str, (thing, fixity, src_loc, insts)) 
+    = vcat [ showDecl want_name thing, 
+            show_fixity fixity,
+            show_loc src_loc,
+            vcat (map show_inst insts)]
   where
-    showFixity fix 
+    want_name occ = wanted_str == occNameUserString occ
+
+    show_fixity fix 
        | fix == defaultFixity = empty
-       | otherwise            = ppr fix <+> text name
+       | otherwise            = ppr fix <+> text wanted_str
+
+    show_loc loc       -- The ppr function for SrcLocs is a bit wonky
+       | isGoodSrcLoc loc = comment <+> ptext SLIT("Defined at") <+> ppr loc
+       | otherwise        = comment <+> ppr loc
+    comment = ptext SLIT("--")
 
--- Now there is rather a lot of goop just to print declarations in a civilised way
--- with "..." for the parts we are less interested in.
+    show_inst (iface_inst, loc)
+       = hang (ptext SLIT("instance") <+> ppr (ifInstHead iface_inst))
+            2 (char '\t' <> show_loc loc)
+               -- The tab tries to make them line up a bit
+
+-- Now there is rather a lot of goop just to print declarations in a
+-- civilised way with "..." for the parts we are less interested in.
 
 showDecl :: (OccName -> Bool) -> IfaceDecl -> SDoc
+showDecl want_name (IfaceForeign {ifName = tc})
+  = ppr tc <+> ptext SLIT("is a foreign type")
+
 showDecl want_name (IfaceId {ifName = var, ifType = ty})
   = ppr var <+> dcolon <+> ppr ty 
 
@@ -501,17 +532,28 @@ showDecl want_name (IfaceSyn {ifName = tycon, ifTyVars = tyvars, ifSynRhs = mono
   = hang (ptext SLIT("type") <+> pprIfaceDeclHead [] tycon tyvars)
        2 (equals <+> ppr mono_ty)
 
-showDecl want_name (IfaceData {ifCtxt = context, ifName = tycon, 
+showDecl want_name (IfaceData {ifName = tycon, 
                     ifTyVars = tyvars, ifCons = condecls})
   = hang (pp_nd <+> pprIfaceDeclHead context tycon tyvars)
        2 (add_bars (ppr_trim show_con cs))
   where
-    show_con (IfaceConDecl con_name is_infix ex_tvs ex_cxt tys strs flds)
+    show_con (IfVanillaCon { ifConOcc = con_name, ifConInfix = is_infix, ifConArgTys = tys, 
+                            ifConStricts = strs, ifConFields = flds})
        | want_name tycon || want_name con_name || any want_name flds
-       = Just (pprIfaceForAllPart ex_tvs ex_cxt (show_guts con_name is_infix tys_w_strs flds))
+       = Just (show_guts con_name is_infix tys_w_strs flds)
        | otherwise = Nothing
        where
          tys_w_strs = tys `zip` (strs ++ repeat NotMarkedStrict)
+    show_con (IfGadtCon { ifConOcc = con_name, ifConTyVars = tvs, ifConCtxt = theta, 
+                         ifConArgTys = arg_tys, ifConResTys = res_tys, ifConStricts = strs })
+       | want_name tycon || want_name con_name
+       = Just (ppr_bndr con_name <+> colon <+> pprIfaceForAllPart tvs theta pp_tau)
+       | otherwise = Nothing
+       where
+         tys_w_strs = arg_tys `zip` (strs ++ repeat NotMarkedStrict)
+         pp_tau = foldr add pp_res_ty tys_w_strs
+         pp_res_ty = ppr_bndr tycon <+> hsep (map pprParendIfaceType res_tys)
+         add bty pp_ty = ppr_bangty bty <+> arrow <+> pp_ty
 
     show_guts con True [ty1, ty2] flds = sep [ppr_bangty ty1, ppr con, ppr_bangty ty2]
     show_guts con _ tys []   = ppr_bndr con <+> sep (map ppr_bangty tys)
@@ -522,10 +564,11 @@ showDecl want_name (IfaceData {ifCtxt = context, ifName = tycon,
                              = Just (ppr_bndr fld <+> dcolon <+> ppr_bangty bty)
                              | otherwise = Nothing
 
-    (pp_nd, cs) = case condecls of
-                   IfAbstractTyCon -> (ptext SLIT("data"),    [])
-                   IfDataTyCon cs  -> (ptext SLIT("data"),    cs)
-                   IfNewTyCon c    -> (ptext SLIT("newtype"), [c])
+    (pp_nd, context, cs) = case condecls of
+                   IfAbstractTyCon           -> (ptext SLIT("data"), [],   [])
+                   IfDataTyCon (Just cxt) cs -> (ptext SLIT("data"), cxt, cs)
+                   IfDataTyCon Nothing cs    -> (ptext SLIT("data"), [],  cs)
+                   IfNewTyCon c              -> (ptext SLIT("newtype"), [], [c])
 
     add_bars []      = empty
     add_bars [c]     = equals <+> c
@@ -560,16 +603,6 @@ ppr_bndr :: OccName -> SDoc
 ppr_bndr occ | isSymOcc occ = parens (ppr occ)
             | otherwise    = ppr occ
 
-{-
-       -- also print out the source location for home things
-    showSrcLoc name
-       | isHomePackageName name && isGoodSrcLoc loc
-       = hsep [ text ", defined at", ppr loc ]
-       | otherwise
-       = empty
-       where loc = nameSrcLoc name
--}
-
 
 -----------------------------------------------------------------------------
 -- Commands
@@ -591,8 +624,7 @@ changeDirectory :: String -> GHCi ()
 changeDirectory dir = do
   state    <- getGHCiState
   when (targets state /= []) $
-       io $ putStr "Warning: changing directory causes all loaded modules to be unloaded, \n\ 
-       \because the search path has changed.\n"
+       io $ putStr "Warning: changing directory causes all loaded modules to be unloaded,\nbecause the search path has changed.\n"
   cmstate1 <- io (cmUnload (cmstate state))
   setGHCiState state{ cmstate = cmstate1, targets = [] }
   setContextAfterLoad []
@@ -608,7 +640,7 @@ defineMacro s = do
        else do
   if (macro_name `elem` map fst cmds) 
        then throwDyn (CmdLineError 
-               ("command `" ++ macro_name ++ "' is already defined"))
+               ("command '" ++ macro_name ++ "' is already defined"))
        else do
 
   -- give the expression a type signature, so we can be sure we're getting
@@ -633,11 +665,11 @@ undefineMacro macro_name = do
   cmds <- io (readIORef commands)
   if (macro_name `elem` map fst builtin_commands) 
        then throwDyn (CmdLineError
-               ("command `" ++ macro_name ++ "' cannot be undefined"))
+               ("command '" ++ macro_name ++ "' cannot be undefined"))
        else do
   if (macro_name `notElem` map fst cmds) 
        then throwDyn (CmdLineError 
-               ("command `" ++ macro_name ++ "' not defined"))
+               ("command '" ++ macro_name ++ "' not defined"))
        else do
   io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
 
@@ -745,7 +777,7 @@ browseModule m exports_only = do
 
   is_interpreted <- io (cmModuleIsInterpreted cms m)
   when (not is_interpreted && not exports_only) $
-       throwDyn (CmdLineError ("module `" ++ m ++ "' is not interpreted"))
+       throwDyn (CmdLineError ("module '" ++ m ++ "' is not interpreted"))
 
   -- Temporarily set the context to the module we're interested in,
   -- just so we can get an appropriate PrintUnqualified
@@ -788,7 +820,7 @@ separate cmstate []           as bs = return (as,bs)
 separate cmstate (('*':m):ms) as bs = do
    b <- io (cmModuleIsInterpreted cmstate m)
    if b then separate cmstate ms (m:as) bs
-       else throwDyn (CmdLineError ("module `" ++ m ++ "' is not interpreted"))
+       else throwDyn (CmdLineError ("module '" ++ m ++ "' is not interpreted"))
 separate cmstate (m:ms)       as bs = separate cmstate ms as (m:bs)
 
 prel = "Prelude"
@@ -897,7 +929,7 @@ unsetOptions str
           (plus_opts, rest2)  = partition isPlus rest1
 
        if (not (null rest2)) 
-         then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
+         then io (putStrLn ("unknown option: '" ++ head rest2 ++ "'"))
          else do
 
        mapM_ unsetOpt plus_opts
@@ -915,12 +947,12 @@ isPlus _ = False
 
 setOpt ('+':str)
   = case strToGHCiOpt str of
-       Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
+       Nothing -> io (putStrLn ("unknown option: '" ++ str ++ "'"))
        Just o  -> setOption o
 
 unsetOpt ('+':str)
   = case strToGHCiOpt str of
-       Nothing -> io (putStrLn ("unknown option: `" ++ str ++ "'"))
+       Nothing -> io (putStrLn ("unknown option: '" ++ str ++ "'"))
        Just o  -> unsetOption o
 
 strToGHCiOpt :: String -> (Maybe GHCiOption)