packages feed

egison-2.3.9: hs-src/Compiler/egisonc.hs

module Main where
import Language.Egison.Core
import Language.Egison.Types
import Language.Egison.Parser
import Control.Monad.Error
import System.Cmd (system)
import System.Console.GetOpt
import System.FilePath (dropExtension)
import System.Environment
import System.Directory (doesFileExist, copyFile, removeFile)
import System.Exit (ExitCode (..), exitWith, exitFailure)
import System.IO
import Paths_egison

templateFile :: String
templateFile = "etc/template.hs"

templateFileForTest :: String
templateFileForTest = "etc/template-for-test.hs"

main :: IO ()
main = do 
  args <- getArgs
  let (actions, nonOpts, _) = getOpt Permute options args
  let opts = foldl (flip id) defaultOptions actions
  case opts of
    Options {optShowHelp = True} -> printHelp
    Options {optShowVersion = True} -> printVersionNumber
    Options {optOutput = output} ->
      if null nonOpts
        then showUsage
        else do
          let inFile = nonOpts !! 0
              outExec = case output of
                Just outFile -> outFile
                Nothing -> dropExtension inFile
          process opts inFile outExec

data Options = Options {
    optShowVersion :: Bool,
    optShowHelp :: Bool,
    optOutput :: Maybe String,
    optProf :: Bool,
    optTest :: Bool
    }

showUsage :: IO ()
showUsage = do
  putStrLn "egisonc: no input files"
  
defaultOptions :: Options
defaultOptions = Options {
    optShowVersion = False,
    optShowHelp = False,
    optOutput = Nothing,
    optProf = False,
    optTest = False
    }

options :: [OptDescr (Options -> Options)]
options = [
  Option ['v', 'V'] ["version"]
    (NoArg (\opts -> opts {optShowVersion = True}))
    "show version number",
  Option ['h', '?'] ["help"]
    (NoArg (\opts -> opts {optShowHelp = True}))
    "show usage information",
  Option ['o'] ["output"]
    (ReqArg (\out opts -> opts {optOutput = Just out})
            "FILE")
    "output file to write",
  Option ['p'] ["prof"]
    (NoArg (\opts -> opts {optProf = True}))
    "use profiling system",
  Option ['t'] ["test"]
    (NoArg (\opts -> opts {optTest = True}))
    "execute test expressions"
  ]

printVersionNumber :: IO ()
printVersionNumber = do
  putStrLn egisonVersion
  exitWith ExitSuccess

printHelp :: IO ()
printHelp = do
  putStrLn "Usage: egisonc [options] file"
  putStrLn ""
  putStrLn "Options:"
  putStrLn "  --help                Display this information"
  putStrLn "  --version             Display egison version information"
  putStrLn "  --output filename     Write executable to the given filename"
  putStrLn "  --prof                Make use of GHC profiling system"
  putStrLn "  --test                Execute test expressions"
  putStrLn ""
  exitWith ExitSuccess

process :: Options -> String -> String -> IO ()
process opts inFile outExec = do
  result <- (runIOThrows $ liftM show $ createHaskellFile opts inFile)
  case result of
   Just errMsg -> putStrLn errMsg
   _ -> compileHaskellFile opts outExec

appendLoadExprForCoreLibraries :: [TopExpr] -> [TopExpr]
appendLoadExprForCoreLibraries topExprs = [(Load "lib/core/base.egi"),
                                           (Load "lib/core/number.egi"),
                                           (Load "lib/core/collection.egi"),
                                           (Load "lib/core/array.egi")
                                           ] ++ topExprs
   
createHaskellFile :: Options -> String -> IOThrowsError ()
createHaskellFile opts inFile =
  case opts of
    Options {optTest = True} -> do
      templatePath <- liftIO $ getDataFileName templateFileForTest
      liftIO $ copyFile templatePath "./_tmp.hs"
      egisonProgram <- liftIO $ readFile inFile
      topExprs <- liftThrows $ readTopExprList egisonProgram
      topExprs2 <- expandLoadExprs opts $ appendLoadExprForCoreLibraries topExprs
      liftIO $ appendFile "./_tmp.hs" "\ntopExprs :: [TopExpr]\n"
      liftIO $ appendFile "./_tmp.hs" "topExprs = "
      liftIO $ appendFile "./_tmp.hs" $ show topExprs2
      return ()
    _ -> do
      templatePath <- liftIO $ getDataFileName templateFile
      liftIO $ copyFile templatePath "./_tmp.hs"
      egisonProgram <- liftIO $ readFile inFile
      topExprs <- liftThrows $ readTopExprList egisonProgram
      topExprs2 <- expandLoadExprs opts $ appendLoadExprForCoreLibraries topExprs
      liftIO $ appendFile "./_tmp.hs" "\ntopExprs :: [TopExpr]\n"
      liftIO $ appendFile "./_tmp.hs" "topExprs = "
      liftIO $ appendFile "./_tmp.hs" $ show topExprs2
      return ()

expandLoadExprs :: Options -> [TopExpr] -> IOThrowsError [TopExpr]
expandLoadExprs _ [] = return []
expandLoadExprs opts ((LoadFile filename):topExprs) = do
  result <- liftIO $ doesFileExist filename
  if result
    then do
      loadProgram <- liftIO $ readFile filename
      loadTopExprs <- liftThrows $ readTopExprList loadProgram
      rets <- expandLoadExprs opts topExprs
      return $ loadTopExprs ++ rets
    else throwError $ Default $ "File does not exist: " ++ filename
expandLoadExprs opts ((Load libname):topExprs) = do
  filename <- liftIO (getDataFileName libname)
  result <- liftIO $ doesFileExist filename
  if result
    then do
      loadProgram <- liftIO $ readFile filename
      loadTopExprs <- liftThrows $ readTopExprList loadProgram
      rets <- expandLoadExprs opts topExprs
      return $ loadTopExprs ++ rets
    else throwError $ Default $ "Library does not exist: " ++ libname
expandLoadExprs opts (topExpr@(Test _):topExprs) =
  case opts of
    Options {optTest = True} -> do
      rets <- expandLoadExprs opts topExprs
      return $ topExpr:rets
    _ -> expandLoadExprs opts topExprs
expandLoadExprs opts (topExpr:topExprs) = do
  rets <- expandLoadExprs opts topExprs
  return $ topExpr:rets
  
compileHaskellFile :: Options -> String -> IO ()
compileHaskellFile opts filename = do
  let ghc = "ghc"
  case opts of
    Options {optProf = True} ->
      system $ ghc ++ " -prof -auto-all -o " ++ filename ++ " _tmp.hs"
    _ ->
      system $ ghc ++ " -O2 -o " ++ filename ++ " _tmp.hs"
  removeFile "./_tmp.hs"
  removeFile "./_tmp.hi"
  removeFile "./_tmp.o"
  return ()