module Main where import Prelude hiding (catch) import Control.Exception ( AsyncException(..), catch ) import Control.Monad.Except import qualified Data.Text as T import Data.Char import Data.List (intercalate) 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 System.IO 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, optSubstituteString = mSub, optFieldInfo = fieldInfo, optLoadLibs = loadLibs, optLoadFiles = loadFiles, optPrompt = prompt, optShowBanner = bannerFlag, optTsvOutput = tsvFlag, optNoIO = noIOFlag} -> do coreEnv <- if noIOFlag then initialEnvNoIO else initialEnv mEnv <- evalEgisonTopExprs coreEnv $ (map Load loadLibs) ++ (map LoadFile loadFiles) case mEnv of Left err -> putStrLn $ show err Right env -> do case mExpr of Just expr -> if tsvFlag then do ret <- runEgisonTopExprs env ("(execute (each (compose show-tsv print) " ++ expr ++ "))") case ret of Left err -> hPutStrLn stderr $ show err Right _ -> return () else do ret <- runEgisonExpr env expr case ret of Left err -> hPutStrLn stderr (show err) >> exitFailure Right val -> putStrLn (show val) >> exitWith ExitSuccess Nothing -> case mCmd of Just cmd -> do cmdRet <- runEgisonTopExpr env ("(execute " ++ cmd ++ ")") case cmdRet of Left err -> putStrLn (show err) >> exitFailure _ -> exitWith ExitSuccess Nothing -> case mSub of Just sub -> do cmdRet <- runEgisonTopExprs env ("(load \"lib/core/shell.egi\") (execute (each (compose " ++ (if tsvFlag then "show-tsv" else "show") ++ " print) (let {[$SH.input (SH.gen-input {" ++ intercalate " " (map fst fieldInfo) ++ "} {" ++ intercalate " " (map snd fieldInfo) ++ "})]} (" ++ sub ++ " SH.input))))") case cmdRet of Left err -> putStrLn (show err) >> exitFailure _ -> exitWith ExitSuccess Nothing -> case nonOpts of [] -> do when bannerFlag showBanner >> repl noIOFlag env prompt >> when bannerFlag showByebyeMessage >> exitWith ExitSuccess (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, optSubstituteString :: Maybe String, optFieldInfo :: [(String, String)], optLoadLibs :: [String], optLoadFiles :: [String], optTsvOutput :: Bool, optNoIO :: Bool, optShowBanner :: Bool, optTestOnly :: Bool, optPrompt :: String } defaultOptions :: Options defaultOptions = Options { optShowVersion = False, optShowHelp = False, optEvalString = Nothing, optExecuteString = Nothing, optSubstituteString = Nothing, optFieldInfo = [], optLoadLibs = [], optLoadFiles = [], optTsvOutput = False, 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 ['T'] ["tsv"] (NoArg (\opts -> opts {optTsvOutput = True})) "output in tsv format", 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-library"] (ReqArg (\d opts -> opts {optLoadLibs = optLoadLibs opts ++ [d]}) "[String]") "load library", Option ['l'] ["load-file"] (ReqArg (\d opts -> opts {optLoadFiles = optLoadFiles opts ++ [d]}) "[String]") "load file", 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", Option ['s'] ["substitute"] (ReqArg (\expr opts -> opts {optSubstituteString = Just expr}) "String") "substitute strings", Option ['m'] ["map"] (ReqArg (\expr opts -> opts {optSubstituteString = Just ("(map " ++ expr ++ " $)")}) "String") "filter strings", Option ['f'] ["filter"] (ReqArg (\expr opts -> opts {optSubstituteString = Just ("(filter " ++ expr ++ " $)")}) "String") "filter strings", Option ['F'] ["--field"] (ReqArg (\d opts -> opts {optFieldInfo = optFieldInfo opts ++ [(readFieldOption d)]}) "String") "field information" ] readFieldOption :: String -> (String, String) readFieldOption str = let (s, rs) = span isDigit str in case rs of ',':rs' -> let (e, opts) = span isDigit rs' in case opts of ['s'] -> ("{" ++ s ++ " " ++ e ++ "}", "") ['c'] -> ("{}", "{" ++ s ++ " " ++ e ++ "}") ['s', 'c'] -> ("{" ++ s ++ " " ++ e ++ "}", "{" ++ s ++ " " ++ e ++ "}") ['c', 's'] -> ("{" ++ s ++ " " ++ e ++ "}", "{" ++ s ++ " " ++ e ++ "}") ['s'] -> ("{" ++ s ++ "}", "") ['c'] -> ("", "{" ++ s ++ "}") ['s', 'c'] -> ("{" ++ s ++ "}", "{" ++ s ++ "}") ['c', 's'] -> ("{" ++ s ++ "}", "{" ++ s ++ "}") printHelp :: IO () printHelp = do putStrLn "Usage: egison [options]" putStrLn " egison [options] file" putStrLn " egison [options] expr" putStrLn "" putStrLn "Global Options:" putStrLn " --help, -h Display this information" putStrLn " --version, -v Display egison version information" putStrLn "" putStrLn " --load-library, -L file Load the argument library" putStrLn " --load-file, -l file Load the argument file" putStrLn " --no-io Prohibit all IO primitives" putStrLn "" putStrLn "Options as an interactive interpreter:" putStrLn " --prompt string Set prompt of the interpreter" putStrLn " --no-banner Don't show banner" putStrLn "" putStrLn "Options to handle Egison program file:" putStrLn " --test, -t file Run only test expressions" putStrLn "" putStrLn "Options as a shell command:" putStrLn " --eval, -e expr Evaluate the argument expression" putStrLn " --command, -c expr Execute the argument expression" putStrLn "" putStrLn " --substitute, -s expr Substitute input using the argument expression" putStrLn " --map, -m expr Substitute each line of input using the argument expression" putStrLn " --filter, -f expr Filter each line of input using the argument predicate" exitWith ExitSuccess printVersionNumber :: IO () printVersionNumber = do putStrLn $ showVersion version exitWith ExitSuccess showBanner :: IO () showBanner = do putStrLn $ "Egison Version " ++ showVersion version ++ " (C) 2011-2017 Satoshi Egi" putStrLn $ "https://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 = putStrLn $ "Leaving Egison Interpreter." 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 )