module Main where
import IO
import System
import Char
import Directory
import List
import Maybe
import Config -- from src/hmake
import Compiler -- from src/hmake
import HiConfig
import SimpleLineEditor (delChars, getLineEdited, initialise, restore)
import LexModule
import Unlit
import Platform (shell)
--debug x = putStrLn ("DEBUG: "++x)
debug x = return ()
done = return ()
data State = S { options :: [String]
, config :: PersonalConfig
, compiler :: CompilerConfig
, cfgfile :: Maybe FilePath -- the hmake configfile
, modules :: [String]
, scope :: Maybe String -- could be `elem` modules
, scopeText :: Maybe String -- modified text of module
}
main = do
options <- getArgs
(cfg,file,options) <-
case options of
("-f":file:opts) -> do cfg <- readPersonalConfig (file,Nothing)
return (cfg, Just file, opts)
_ -> do cfg <- readPersonalConfig (defaultConfigLocation False)
return (cfg,Nothing,options)
defaultComp <- unDyn (usualCompiler cfg)
let opts = options ++ extraHiOptions defaultComp
putStrLn banner
putStrLn (replicate 43 ' '++
"... Using compiler "++compilerPath defaultComp++" ...\n")
putStrLn ("Type :? for help")
SimpleLineEditor.initialise
let state = S { options=opts, config=cfg, compiler=defaultComp, cfgfile=file
, modules=["Prelude"], scope=Nothing, scopeText=Nothing }
load state "Prelude" (toplevel state)
putStrLn "[Cannot continue without Prelude...]"
SimpleLineEditor.restore
exitWith (ExitFailure 1)
quit = do
putStrLn "[Leaving hmake interactive...]"
mapM_ (\f-> catch (removeFile f) (\e->done))
[tmpfile, tmpfile++".hs", tmpfile++".hi", tmpfile++".o"]
SimpleLineEditor.restore
exitWith ExitSuccess
toplevel :: State -> IO ()
toplevel state = do
let prompt = case scope state of
Nothing -> head (modules state)++ "> "
Just mod -> "["++mod++"]> "
s <- getLineEdited prompt
case s of
Nothing -> quit
Just s -> do
if (null s || all isSpace s) then done else
case head s of
':' -> let ws = words (tail s) in
if (null ws) then done else
case head (head ws) of
'!' -> shell (unwords ((tail (head ws)):tail ws)) >> done
_ -> commands ws state
_ -> evaluate s (words s) state
toplevel state
evaluate :: String -> [String] -> State -> IO ()
evaluate expr ("main":args) state =
case scope state of
Nothing ->
let mod = head (modules state) in
compile True mod state (run ("./"++mod) args)
Just _ -> do
f <- openFile (tmpfile++".hs") WriteMode
hPutStr f (fromJust (scopeText state) ++ "\nmain = _ain\n")
hClose f
compile True tmpfile state (run tmpfile args)
evaluate expr _ state = do
let modtext = fromMaybe "" (scopeText state)
let scopem = maybeToList (scope state)
f <- openFile (tmpfile++".hs") WriteMode
hPutStr f (
"module Main where\n\n" ++
concatMap (\m-> "import "++m++"\n") (modules state \\ scopem) ++
"\n" ++ nonstdCoerceImport (compilerStyle (compiler state)) ++
modtext ++
"\n" ++ nonstdCoerce (compilerStyle (compiler state)) ++
"\n" ++ nonstdShow (compilerStyle (compiler state)) ++
"\nmain = let expr = (" ++ expr ++ ")" ++
"\n shown = show expr" ++
"\n in case shown of" ++
"\n ('<':'<':'I':'O':_) -> coerce expr" ++
"\n _ -> putStrLn shown" ++
"\n")
hClose f
compile True tmpfile state (run tmpfile [])
showtype expr ("main":args) state =
case scope state of
Nothing ->
let mod = head (modules state) in
compile False mod (state{options=(options state)++["-showtype"]}) done
Just _ -> do
f <- openFile (tmpfile++".hs") WriteMode
hPutStr f (fromJust (scopeText state) ++ "\nmain = _ain\n")
hClose f
compile False tmpfile
(state{options=(options state)++["-showtype"]}) done
showtype expr _ state = do
let modtext = fromMaybe "" (scopeText state)
let scopem = maybeToList (scope state)
f <- openFile (tmpfile++".hs") WriteMode
hPutStr f (
"module Main where\n\n" ++
concatMap (\m-> "import "++m++"\n") (modules state \\ scopem) ++
modtext ++ "\nmain = "++ expr ++ "\n")
hClose f
compile False tmpfile
(state{options=(options state)++["-showtype"]}) done
compile flag file state continue = do
if flag then putStr "[Compiling..." else done
ok <- shell (hmake ++" -hc="++compilerPath (compiler state)++" -I. "
++(case cfgfile state of {Just f-> ("-f "++f++" "); _->"";})
++unwords (options state)++" "++file++" >/dev/null")
case ok of
ExitSuccess -> do if flag then delChars "[Compiling..." else done
continue
_ -> if flag then putStrLn "...failed]" else done
run file args = do
ok <- shell (file++" "++unwords args)
case ok of
ExitFailure e -> putStrLn ("Expression failed with exit code "++show e)
_ -> return ()
done
commands :: [String] -> State -> IO ()
commands ws state = let target = tail ws in do
command "quit" quit
command "Quit" quit
command "load"
(let mods = if null target then ["Prelude"]
else nub (reverse ("Prelude":target))
newstate = state {modules=mods, scope=Nothing, scopeText=Nothing}
in loadAll newstate (toplevel newstate) -- explicitly return new modules
)
command "also"
(let mods = if null target then ["Prelude"]
else nub (reverse target ++ modules state)
newstate = state {modules=mods} -- retain scope if applicable
in loadAll newstate (toplevel newstate) -- explicitly return new modules
)
command "reload" (loadAll state done)
command "freshen"
(do makeclean ".o" (modules state)
loadAll state done)
command "module"
(if null target || length target > 1 then do
putStrLn "You must give a single module name, in whose scope to evaluate."
else let mod = head target in do
loadScope state mod (\text-> toplevel (state { scope=(Just mod)
, scopeText=(Just text)}))
)
--command "observe"
-- (if null target || length target > 1 then do
-- putStrLn "You must give a single function name (from this module) to observe."
-- else let fn = head target in do
-- makeObserve state fn (\text-> toplevel (state { scope=(Just mod)
-- , scopeText=(Just text)}))
-- )
command "edit"
(if null target then
case scope state of
Nothing -> let mod = head (modules state) in
edit state mod (load state mod done)
Just mod -> edit state mod (loadScope state mod (\_->done))
else do e <- shell ("${EDITOR-vi} " ++ unwords target)
loadAll state done
)
command "type"
(if compilerStyle (compiler state) == Nhc98
then showtype (unwords target) target state
else putStrLn ":type command only supported for nhc98 compiler")
{-
(do f <- openFile (tmpfile++".hs") WriteMode
hPutStr f (
"module Main where\n\n" ++
concatMap (\m-> "import "++m++"\n") (modules state) ++
"\nmain = let expr = (" ++ unwords target ++ ")" ++
"\n in putStrLn ("++ nonstdShowsType (compiler state)++" expr \"\")"++
"\n")
hClose f
compile True tmpfile state (run tmpfile [])
)
-}
command "cd"
(if null target then do
dir <- getCurrentDirectory
putStrLn ("Current directory: "++dir)
else catch (setCurrentDirectory (head target)) print
)
command "dir" (getDirectoryContents "." >>= indent)
command "ls" (getDirectoryContents "." >>= indent)
command "pwd" (getCurrentDirectory >>= putStrLn)
command "set"
(do let newopts = (options state) ++ target
putStrLn ("Current settings:\n "++unwords newopts)
toplevel (state {options=newopts}) )
command "unset"
(do let newopts = (options state) \\ target
putStrLn ("Current settings:\n "++unwords newopts)
toplevel (state {options=newopts}) )
command "hc"
(if null target then do
putStrLn ("Current compiler: "++compilerPath (compiler state)
++" ("++compilerVersion (compiler state)++")")
putStr "Compilers available:\n "
kcs <- (mapM unDyn . knownComps . config) state
putStrLn ((concat . intersperse ("\n ")
. reverse . sort
. map (\cc->compilerPath cc++"\t("++compilerVersion cc++")")
) kcs)
else if compilerKnown (head target) (config state) then do
newcomp <- unDyn (matchCompiler (head target) (config state))
let newopts = ((options state)
\\ extraHiOptions (compiler state))
++ extraHiOptions newcomp
makeclean ".o" (modules state)
makeclean ".hi" (modules state)
let newstate = state {options=newopts, compiler=newcomp}
loadAll newstate (toplevel newstate) -- explicit return
else do
putStrLn ("Compiler "++head target++" not known/configured")
putStrLn ("Current compiler: "++compilerPath (compiler state))
)
--command "trace"
-- (if null target || head target `notElem` ["on","off"] then do
-- putStr ("Tracing currently: ")
-- putStrLn (if "-T" `elem` options state then "on" else "off")
-- else
-- let newopts =
-- case head target of
-- "on" -> ((options state) \\ extraCompilerFlags (compiler state))
-- ++ extraCompilerFlags "nhc98" ++ ["-T"]
-- "off" -> (options state) \\ ["-T"]
-- in do
-- makeclean ".o" (modules state)
-- makeclean ".hi" (modules state)
-- let newstate = state {options=newopts, compiler=Nhc98}
-- loadAll newstate (toplevel newstate)) -- explicit return
command "version"
(putStrLn ("hmake version: "++hmakeVersion++"\t("++hmake++")"))
command "?" (putStrLn help)
putStrLn ("[Unknown command :"++head ws++"]")
where
command :: String -> IO () -> IO ()
command name action =
if head ws `isPrefixOf` name then action >> toplevel state else done
indent = mapM_ (\x-> putStrLn (" "++x))
-- find file (a generalisation of earlier implementation of 'load')
findF :: (Bool->String -> (a->IO ()) -> IO ()) -- action when file is found
-> (String -> (a->IO ()) -> IO ()) -- action when .hi is found
-> State
-> String -- module name
-> (a -> IO ()) -- success continuation
-> IO ()
findF action hiaction state mod success = do
normal True ".lhs" (
normal False ".hs" (
normal False ".gc" (
foldr prelude
(putStrLn ("[Module "++mod++" not found...]"))
(includePaths (compiler state)))))
where
--normal :: Bool -> String -> IO b -> IO b
normal lit ext continue = let file = mod++ext in do
exist <- doesFileExist file
if exist then action lit file success else continue
--prelude :: String -> IO b -> IO b
prelude pp continue = let hifile = pp++"/"++mod++".hi" in do
hi <- doesFileExist hifile
if hi then hiaction hifile success else continue
loadAll :: State -> IO () -> IO ()
loadAll state success = foldr (load state) success (reverse (modules state))
load :: State -> String -> IO () -> IO ()
load state mod success =
findF (\lit file success->
do putStr ("[Found module... "++file++"] ")
compile True file state (putChar '\n')
success ())
(\hifile success->
do putStrLn ("[Std module... "++hifile++"]")
success ())
state mod (\()->success)
loadScope :: State -> String -> (String->IO ()) -> IO ()
loadScope state =
findF (\lit file success-> let litf = if lit then (unlit file) else id in
do putStrLn ("[Entering scope of module... "++file++"]")
compile True file state (do content <- readFile file
success (lexmodule (litf content))))
(\hifile success->
putStrLn ("[Cannot enter std module... "++hifile++"]"))
state
edit :: State -> String -> IO () -> IO ()
edit state mod success =
findF (\lit file success-> shell ("${EDITOR-vi} " ++ file) >> success ())
(\hifile success->
putStrLn ("[Cannot edit system file... "++hifile++"]"))
state mod (\()->success)
--makeclean ".o" modules = shell ("hmake -clean -nhc98 "++unwords modules)
--makeclean ".hi" modules = shell ("hmake -realclean -nhc98 "++unwords modules)
makeclean ext modules = mapM_ (clean ext) modules
where
clean ext mod = let file = mod++ext in do
exist <- doesFileExist file
if exist then do
putStrLn ("[Removing ... "++file++"]")
catch (removeFile file) print
else done
--fromOpt prefix opt =
-- if prefix `isPrefixOf` opt then Just (drop (length prefix) opt) else Nothing
banner = "\
\__ __ __ _____________________________________\n\
\|| || ______ ___ || _ ____ hmake interactive (hi):\n\
\||___|| || || || ___|| ||/ ||__|| Copyright (c) May 2000\n\
\||---|| || || || ||__|| ||\\_ ||__ http://www.cs.york.ac.uk/fp/hmake/\n\
\|| || Report bugs to: malcolm@cs.york.ac.uk\n\
\|| || Version: "++hmakeVersion++" -------------------------------------"
help = "\
\Commands (can be abbreviated to first letter):\n\
\ <expr> evaluate expression\n\
\ :type <expr> show type of expression [nhc98 only]\n\
\ :quit quit\n\
\ :Quit quit\n\
\ :load mod [mod...] load modules (note, not filenames)\n\
\ :load clear all modules\n\
\ :also mod [mod...] load additional modules (note, not filenames)\n\
\ :reload repeat last load command\n\
\ :freshen remove, recompile, and reload all current modules\n\
\ :module mod set module scope for evaluating expressions\n\
\ :edit file edit filename\n\
\ :edit edit current module\n\
\ :cd dir change directory\n\
\ :cd show current directory\n\
\ :dir list current directory\n\
\ :hc compiler set Haskell compiler to use\n\
\ :hc show current compiler and other available compilers\n\
\ :set options set hmake/compiler options\n\
\ :unset options remove hmake/compiler options\n\
\ :observe name debug function 'name' with 'Hood' [coming soon]\n\
\ :trace [on|off] switch on/off debugging with 'Hat' [nhc98 only]\n\
\ :!command shell escape\n\
\ :version show hmake version\n\
\ :? display this list of commands"
|