Hashell-0.15: Hashell/Eval.hs
{-
Function to evaluate haskell and command <-> haskell expressions.
Copyright (C) 2005, 2008 Luis Francisco Araujo
-}
module Eval (funcProcess, hEval)
where
import Parse
import Control.Monad
import Data.Maybe (fromJust, isJust)
import System.Exit
import System.IO
import System.Process
import qualified GHC
-- import qualified Outputable
import qualified Packages
import qualified Module
import qualified Control.Exception as E
type Modules = [String]
type Expression = String
type Files = [String]
type Import = String
{- the path of our GHC 6.8 installation; this obviously needs to be edited to
point to the right place - the current value happens to be right for me, but
for you it might be in /usr/lib/, ~/lib, ~/bin/lib... depending on where your
GHC is installed. -}
ghcPath :: FilePath
ghcPath = "/usr/lib/ghc-6.8.2"
-- Good enough
-- path :: [FilePath]
-- path = ["/home/gwern/bin/bin", "/usr/local/bin", "/usr/lib64/ccache/bin", "/usr/bin", "/bin", "/opt/bin", "/usr/x86_64-pc-linux-gnu/gcc-bin/4.2.2", "/usr/games", "/usr/games/bin", "/usr/sbin", "/sbin", "/usr/share/surfraw"]
eval :: String -> [Import] -> IO (Maybe String)
-- eval = undefined
eval src _ = do
-- start a new interactive session using the path specified above
session <- GHC.newSession $ Just ghcPath
-- initialize the default packages
dflags1 <- GHC.getSessionDynFlags session
(dflags2, _) <- Packages.initPackages dflags1
GHC.setSessionDynFlags session dflags2{GHC.hscTarget=GHC.HscInterpreted}
-- loadImports session imps
-- 2) load the standard prelude
let modules = ["Prelude"]
loadModules session modules
result <- GHC.runStmt session src GHC.SingleStep
case result of
GHC.RunOk _ -> return $ Just "RunOK"
_ -> return Nothing
-- where loadImports s as = mapM_ (load s) as
-- load sess i = do target <- GHC.guessTarget (i ++ ".hs") Nothing
-- GHC.addTarget sess target
-- GHC.load sess GHC.LoadAllTargets
-- Perhaps don't blindly load modules and claim they all succeeded...
loadModules :: GHC.Session -> [String] -> IO ()
loadModules session modul = do modules <- mapM (\m -> GHC.findModule session (GHC.mkModuleName m) Nothing) modul
GHC.setContext session [] modules
----------------------------------------------------------------------------------
-- | Parse and evaluate a Haskell expression.
hEval :: Expression -> IO (Maybe String)
hEval expr = (getProcessTokens . getEvalOpt) expr >>= eMFEval
{-
| Split and order the hashell expression into a haskell
expression, a list of haskell modules and a list of files.
-}
-- | Split the hashell expression into Expression , Modules, Files.
getEvalOpt :: Expression -> (Expression, Modules, Files)
getEvalOpt (x:':':'(':xs) = let (ex, modules, files) = breakEvalOpt xs
in
(([x] ++ ex), modules, files)
getEvalOpt ('(':xs) = let (ex, modules, files) = breakEvalOpt xs
in
(ex, modules, files)
getEvalOpt xs = (xs, [], [])
breakEvalOpt :: String -> (Expression, Modules, Files)
breakEvalOpt xs = let (optexpr, ex) = (takeWhile (/= ')') xs, tail $ dropWhile (/= ')') xs)
(modules, files) = splitEvalOpt (break (== '|') optexpr)
in
(ex, modules, files)
splitEvalOpt :: (String, String) -> (Modules, Files)
splitEvalOpt ([], []) = ([], [])
splitEvalOpt (zs, []) = (splitInto ',' zs , [])
splitEvalOpt ([], ys) = ([], splitInto ',' $ tail ys)
splitEvalOpt (zs, ys) = (splitInto ',' zs, splitInto ',' $ tail ys)
-- | Interpret the command haskell expressions, and concatenate its
-- result with the rest of the haskell expression.
getProcessTokens :: (Expression, Modules, Files) ->
IO (Expression, Modules, Files)
getProcessTokens (a, b, xs)
| findSubStr "(-" a && findSubStr "-)" a =
do
s <- iterProcess a
return (s, b, xs)
| otherwise = return (a, b, xs)
iterProcess :: String -> IO String
iterProcess [] = return []
iterProcess e
| findSubStr "(-" e && findSubStr "-)" e
= do
let (b, p, a) = parseCH e
processoutput <- funcProcess p
iter <- iterProcess a
return (b ++ (show processoutput) ++ iter)
| otherwise = return e
-- | Run a commmand and get its output as a string.
-- This is the main function to bring command results into Hashell.
funcProcess :: String -> IO String
funcProcess fcmd = do
(_,out,_,ph) <- runInteractiveProcess (getCmd fcmd) (getArg fcmd)
Nothing Nothing
output <- hGetContents out
E.evaluate (length output)
(waitForProcess ph) `E.catch` (\ _ -> return ExitSuccess)
return output
----------------------------------------------------------------------------------
{- | Evaluate Expression, Modules and Files.
Return a string containing the proper haskell evaluation,
or nothing in case of errors.
All the different kind of evalutions return a string. -}
eMFEval :: (Expression, Modules, Files) -> IO (Maybe String)
eMFEval (ex, modul, files) =
do
hSetBuffering stdout NoBuffering
case (ex `esc` '%') of -- | Clean special percents | --
('s':e)
-> (eval (e) modul) >>= writeStdout
('n':e)
-> (eval ("show $ " ++ e) modul) >>= writeStdout
('p':e)
-> (eval ("show $ " ++ e) modul) >>= writeStdoutLn
('u':e)
-> (eval e modul) >>= writeStdoutLn
e -> (eval (e) modul :: IO (Maybe String)) >>= returnExpr
where
writeStdout = wrtS putStr files []
writeStdoutLn = wrtS putStrLn files "\n"
returnExpr n
| isJust n = if null files
then return n
else writeProcFile (fromJust n) files >> return n
| otherwise = return Nothing
{-
| Functions to determine how to write the eMFEval result
-}
-- | Combine different ways of representing the eMFEval result.
wrtS :: (String -> IO ())
-> [String]
-> String
-> Maybe String
-> IO (Maybe String)
wrtS func files delim evalexpr
| isJust evalexpr = if null files
then func (fromJust evalexpr) >> return Nothing
else writeProcFile ((++ delim) $ fromJust evalexpr) files >> return Nothing
| otherwise = hPutStrLn stderr "error : eval returned Nothing." >> return Nothing
-- | Write expression evaluation to a list of files.
writeProcFile :: String -> (Files -> IO ())
writeProcFile s = mapM_ (flip (writeFile) s . concat . hParse)