packages feed

hoe-1.0: src/HOE.hs

{-# LANGUAGE DeriveDataTypeable  #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections       #-}

module Main (main) where

import           Control.Applicative
import           Control.Monad
import           Control.Monad.Catch
import           Data.Version                 (showVersion)
import           Language.Haskell.Interpreter (OptionVal ((:=)))
import           Language.Haskell.Interpreter hiding (Option, name)
import           System.Console.CmdArgs       as CA hiding ((:=))
import           System.Exit                  (exitFailure)
import           System.IO

import           Evaluator
import           Paths_hoe                    (version)

imports :: [String]
imports =
  [ "Prelude"

    -- from base
  , "Control.Applicative"
  , "Control.Arrow"
  , "Control.Monad"
  , "Data.Bits"
  , "Data.Char"
  , "Data.Complex"
  , "Data.Either"
  , "Data.Function"
  , "Data.List"
  , "Data.Maybe"
  , "Data.Monoid"
  , "Data.Ord"
  , "Data.Ratio"
  , "Numeric"
  , "System.IO"
  , "System.IO.Unsafe"
  , "System.Info"
  , "System.Random"
  , "Text.Printf"

    -- other common modules
  , "Data.List.Split"  -- from split
  , "Data.Time"        -- from time
  , "Text.Regex.Posix" -- from regex-posix
  ]

data Option
  = Option
    { inplace    :: Maybe String
    , script     :: String
    , inputFiles :: [String]
    , modules    :: [String]
    }
  deriving (Show, Data, Typeable)

option :: Option
option = Option
  { inplace =
      def &= help "Edit files in place (make backup if EXT supplied)" &= opt "" &= typ "EXT"
  , script =
      def &= argPos 0 &= typ "SCRIPT"
  , inputFiles =
      def &= args &= typ "FILES"
  , modules =
      def &= help "Import a module before running the script"
          &= opt ""
          &= explicit
          &= name "mod"
          &= name "m"
  }
  &= verbosity
  &= program "hoe"
  &= summary ("hoe-" ++ showVersion version ++ " Haskell One-liner Evaluator, (c) Hideyuki Tanaka")
  &= details [ "The Awk like text processor, but it can use Haskell."
             , ""
             ]

printLog :: String -> IO ()
printLog msg = whenLoud $ hPutStrLn stderr msg

main :: IO ()
main = do
  opts <- cmdArgs option
  r <- evalOneLiner opts
  case r of
    Left err -> do
      case err of
        WontCompile errs ->
          hPutStrLn stderr $ "compile error: " ++ unlines (map errMsg errs)
        UnknownError msg ->
          hPutStrLn stderr msg
        _ ->
          hPrint stderr err
      exitFailure
    Right _ ->
      return ()

evalOneLiner :: Option -> IO (Either InterpreterError ())
evalOneLiner opts = runInterpreter $ do
  reset
  setImportsQ $
    [ (m, Nothing) | m <- imports ] ++
    [ (m, Nothing) | m <- modules opts ]
  set [ installedModulesInScope := True ]

  (ty, descr, f) <-
    choice [ (ty, descr, ) <$> compile (script opts)
           | (ty, descr, compile) <- evals
           ]

  liftIO $ printLog $ "Interpret as: " ++ ty ++ " :: " ++ descr
  liftIO $ exec opts f

choice :: [Interpreter a] -> Interpreter a
choice = foldl1 $ \a b -> catch a (\(_e :: SomeException) -> b)

exec :: Main.Option -> Script -> IO ()
exec opts f =
  case (inputFiles opts, inplace opts) of
    ([], _) -> do
        s <- getContents
        putStr =<< f s

    (files, Nothing) ->
      forM_ files $ \file -> do
        s <- readFile file
        putStr =<< f s

    (files, Just ext) ->
      forM_ files $ \file -> do
        s <- readFile file
        when (ext /= "") $
          writeFile (file ++ "." ++ ext) s
        length s `seq` writeFile file =<< f s