hwk-0.3: Main.hs
{-# LANGUAGE CPP #-}
#if !MIN_VERSION_simple_cmd_args(0,1,3)
import Control.Applicative ((<|>))
#endif
import Control.Monad.Extra
import qualified Data.List.Extra as L
import Data.Version (showVersion)
import Language.Haskell.Interpreter
import SimpleCmdArgs
import System.Directory
import System.FilePath
import System.IO (hPutStrLn, stderr)
import Paths_hwk (getDataDir, version)
data HwkMode = DefaultMode | WholeMode | LineMode | TypeMode
deriving Eq
main :: IO ()
main =
simpleCmdArgs (Just version) "A Haskell awk/sed like tool"
"Simple shell text processing with Haskell" $
runExpr <$> modeOpt <*> strArg "FUNCTION" {-<*> many (strArg "FILE...")-}
where
modeOpt :: Parser HwkMode
modeOpt =
flagWith' TypeMode 't' "type-check" "Print out the type of the given function" <|>
flagWith' WholeMode 'a' "all" "Apply function once to the whole input" <|>
flagWith DefaultMode LineMode 'l' "line" "Apply function to each line"
runExpr :: HwkMode -> String -> {-[FilePath] ->-} IO ()
runExpr mode stmt {-files-} = do
--mapM_ checkFileExists files
input <- getContents
userdir <- getXdgDirectory XdgConfig "hwk"
let usercfg = userdir </> "Hwk.hs"
datadir <- getDataDir
let versionCfg = usercfg ++ "-" ++ showVersion version
unlessM (doesFileExist versionCfg) $
copyFile (datadir </> "Config.hs") versionCfg
unlessM (doesFileExist usercfg) $ do
copyFile (datadir </> "Config.hs") usercfg
warn $ usercfg ++ " created"
r <- runInterpreter (runHint userdir input)
case r of
Left err -> putStrLn $ errorString err
Right () -> return ()
where
runHint :: FilePath -> String -> Interpreter ()
runHint cfgdir input = do
set [searchPath := [cfgdir]]
loadModules ["Hwk"]
setHwkImports ["Prelude"]
imports <- do
haveModules <- typeChecks "userModules"
if haveModules
then interpret "userModules" infer
else return ["Prelude", "Data.List"]
setHwkImports imports
if mode == TypeMode then do
#if MIN_VERSION_hint(0,8,0)
etypchk <- typeChecksWithDetails stmt
case etypchk of
Left err -> liftIO $ mapM_ (putStrLn . errMsg) err
Right typ -> liftIO $ putStrLn (cleanupType typ)
#else
typeOf stmt >>= liftIO . putStrLn . cleanupType
#endif
else do
let polyList = "toList . "
polyString = "toString . "
case mode of
DefaultMode -> do
fn <- interpret (polyList ++ stmt) (as :: [String] -> [String])
liftIO $ mapM_ putStrLn (fn (lines input))
LineMode -> do
fn <- interpret (polyString ++ stmt) (as :: String -> String)
liftIO $ mapM_ (putStrLn . fn) (lines input)
WholeMode -> do
fn <- interpret (polyList ++ stmt) (as :: String -> [String])
liftIO $ mapM_ putStrLn (fn input)
TypeMode -> error "already handled earlier"
where
cleanupType :: String -> String
cleanupType = L.replace "[Char]" "String"
-- FIXME use Set
setHwkImports ms = setImports (L.nub (ms ++ ["Hwk"]))
-- checkFileExists :: FilePath -> IO ()
-- checkFileExists file = do
-- unlessM (doesFileExist file) $
-- error' $ "file not found: " ++ file
errorString :: InterpreterError -> String
errorString (WontCompile es) =
unlines $ "ERROR: Won't compile:" : map errMsg es
errorString e = show e
warn :: String -> IO ()
warn = hPutStrLn stderr