module Main where import Prelude hiding ( catch ) import Control.Exception ( AsyncException(..), catch ) import Control.Monad.Error import qualified Data.Text as T import Data.Version import System.Environment import System.Directory (getHomeDirectory) import System.FilePath (()) import System.Console.Haskeline hiding (handle, catch, throwTo) import System.Console.GetOpt import System.Exit (ExitCode (..), exitWith, exitFailure) import Language.Egison import Language.Egison.Util 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 {optEvalString = mExpr, optExecuteString = mCmd, optLoadFiles = loadFiles, optPrompt = prompt, optShowBanner = bannerFlag, optNoIO = noIOFlag} -> do env <- if noIOFlag then initialEnvNoIO else initialEnv result <- evalEgisonTopExprs env (map LoadFile loadFiles) case result of Left err -> putStrLn $ show err Right env -> do case mExpr of Just expr -> do ret <- runEgisonExpr env expr case ret of Left err -> putStrLn $ show err Right val -> putStrLn $ show val Nothing -> case mCmd of Just cmd -> runEgisonTopExpr env ("(execute " ++ cmd ++ ")") >> return () Nothing -> case nonOpts of [] -> do when bannerFlag showBanner >> repl noIOFlag env prompt >> when bannerFlag showByebyeMessage (file:args) -> do case opts of Options {optTestOnly = True} -> do result <- if noIOFlag then do input <- readFile file runEgisonTopExprsNoIO env input else evalEgisonTopExprsTestOnly env [LoadFile file] either print (const $ return ()) result Options {optTestOnly = False} -> do result <- evalEgisonTopExprs env [LoadFile file, Execute (ApplyExpr (VarExpr "main") (CollectionExpr (map (ElementExpr . StringExpr) (map T.pack args))))] either print (const $ return ()) result data Options = Options { optShowVersion :: Bool, optShowHelp :: Bool, optEvalString :: Maybe String, optExecuteString :: Maybe String, optLoadFiles :: [String], optNoIO :: Bool, optShowBanner :: Bool, optTestOnly :: Bool, optPrompt :: String } defaultOptions :: Options defaultOptions = Options { optShowVersion = False, optShowHelp = False, optEvalString = Nothing, optExecuteString = Nothing, optLoadFiles = [], optNoIO = False, optShowBanner = True, optTestOnly = False, optPrompt = "> " } 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 ['e'] ["eval"] (ReqArg (\expr opts -> opts {optEvalString = Just expr}) "String") "eval the argument string", Option ['c'] ["command"] (ReqArg (\expr opts -> opts {optExecuteString = Just expr}) "String") "execute the argument string", Option ['l'] ["load-file"] (ReqArg (\d opts -> opts {optLoadFiles = optLoadFiles opts ++ [d]}) "[String]") "load files", Option [] ["no-io"] (NoArg (\opts -> opts {optNoIO = True})) "prohibit all io primitives", Option [] ["no-banner"] (NoArg (\opts -> opts {optShowBanner = False})) "do not display banner", Option ['t'] ["test"] (NoArg (\opts -> opts {optTestOnly = True})) "execute only test expressions", Option ['p'] ["prompt"] (ReqArg (\prompt opts -> opts {optPrompt = prompt}) "String") "set prompt string" ] printHelp :: IO () printHelp = do putStrLn "Usage: egison [options]" putStrLn " egison [options] file" putStrLn "" putStrLn "Options:" putStrLn " --help, -h Display this information" putStrLn " --version, -v Display egison version information" putStrLn " --eval, -e string Evaluate the argument string" putStrLn " --command, -c string Execute the argument string" putStrLn " --load-file, -l string Load the argument file" putStrLn " --test, -t Run only test expressions" putStrLn " --prompt string Set prompt of the interpreter" putStrLn " --no-banner Don't show banner" putStrLn " --no-io Prohibit all IO primitives" putStrLn "" exitWith ExitSuccess printVersionNumber :: IO () printVersionNumber = do putStrLn $ showVersion version exitWith ExitSuccess showBanner :: IO () showBanner = do putStrLn $ "Egison Version " ++ showVersion version ++ " (C) 2011-2014 Satoshi Egi" putStrLn $ "http://www.egison.org" putStrLn $ "Welcome to Egison Interpreter!" -- putStrLn $ "** Information **" -- putStrLn $ "We can use the tab key to complete keywords on the interpreter." -- putStrLn $ "If we press the tab key after a closed parenthesis, the next closed parenthesis will be completed." -- putStrLn $ "*****************" showByebyeMessage :: IO () showByebyeMessage = do putStrLn $ "Leaving Egison Interpreter." exitWith ExitSuccess repl :: Bool -> Env -> String -> IO () repl noIOFlag env prompt = do loop env where settings :: MonadIO m => FilePath -> Settings m settings home = setComplete completeEgison $ defaultSettings { historyFile = Just (home ".egison_history") } loop :: Env -> IO () loop env = (do home <- getHomeDirectory input <- liftIO $ runInputT (settings home) $ getEgisonExpr prompt case (noIOFlag, input) of (_, Nothing) -> return () (True, Just (_, (LoadFile _))) -> do putStrLn "error: No IO support" loop env (True, Just (_, (Load _))) -> do putStrLn "error: No IO support" loop env (_, Just (topExpr, _)) -> do result <- liftIO $ runEgisonTopExpr env topExpr case result of Left err -> do liftIO $ putStrLn $ show err loop env Right env' -> loop env') `catch` (\e -> case e of UserInterrupt -> putStrLn "" >> loop env StackOverflow -> putStrLn "Stack over flow!" >> loop env HeapOverflow -> putStrLn "Heap over flow!" >> loop env _ -> putStrLn "error!" >> loop env )