egison-3.8.0: hs-src/Interpreter/egison.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TupleSections #-}
module Main where
import Control.Exception (AsyncException (..), catch)
import Control.Monad.Except
import Control.Monad.Trans.State
import Prelude hiding (catch)
import Data.Char
import Data.List (intercalate)
import qualified Data.Text as T
import Data.Version
import System.Console.GetOpt
import System.Console.Haskeline hiding (catch, handle, throwTo)
import System.Directory (getHomeDirectory)
import System.Environment
import System.Exit (ExitCode (..), exitFailure,
exitSuccess)
import System.FilePath ((</>))
import System.IO
import Language.Egison
import Language.Egison.Core (recursiveBind)
import Language.Egison.MathOutput
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, optMathExpr = mathExprLang, optSExpr = isSExpr} -> do
coreEnv <- if noIOFlag then initialEnvNoIO else initialEnv
mEnv <- evalEgisonTopExprs coreEnv $ map (Load isSExpr) loadLibs ++ map (LoadFile isSExpr) loadFiles
case mEnv of
Left err -> print err
Right env ->
case mExpr of
Just expr ->
if tsvFlag
then do ret <- runEgisonTopExprs isSExpr env $ "(execute (each (compose show-tsv print) " ++ expr ++ "))"
case ret of
Left err -> hPrint stderr err
Right _ -> return ()
else do ret <- runEgisonExpr isSExpr env expr
case ret of
Left err -> hPrint stderr err >> exitFailure
Right val -> print val >> exitSuccess
Nothing ->
case mCmd of
Just cmd -> do cmdRet <- runEgisonTopExpr isSExpr env ("(execute " ++ cmd ++ ")")
case cmdRet of
Left err -> print err >> exitFailure
_ -> exitSuccess
Nothing ->
case mSub of
Just sub -> do cmdRet <- runEgisonTopExprs isSExpr env ("(load \"lib/core/shell.egi\") (execute (each (compose " ++ (if tsvFlag then "show-tsv" else "show") ++ " print) (let {[$SH.input (SH.gen-input {" ++ unwords (map fst fieldInfo) ++ "} {" ++ unwords (map snd fieldInfo) ++ "})]} (" ++ sub ++ " SH.input))))")
case cmdRet of
Left err -> print err >> exitFailure
_ -> exitSuccess
Nothing ->
case nonOpts of
[] -> when bannerFlag showBanner >> repl noIOFlag isSExpr mathExprLang env prompt >> when bannerFlag showByebyeMessage >> exitSuccess
(file:args) ->
case opts of
Options {optTestOnly = True} -> do
result <- if noIOFlag
then do input <- readFile file
runEgisonTopExprsNoIO isSExpr env input
else evalEgisonTopExprsTestOnly env [LoadFile isSExpr file]
either print (const $ return ()) result
Options {optTestOnly = False} -> do
result <- evalEgisonTopExprs env [LoadFile isSExpr file, Execute (ApplyExpr (VarExpr $ stringToVar "main") (CollectionExpr (map ((ElementExpr . StringExpr) . 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,
optMathExpr :: Maybe String,
optSExpr :: Bool
}
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 = "> ",
optMathExpr = Nothing,
optSExpr = True
}
options :: [OptDescr (Options -> Options)]
options = [
Option ['h', '?'] ["help"]
(NoArg (\opts -> opts {optShowHelp = True}))
"show usage information",
Option ['v', 'V'] ["version"]
(NoArg (\opts -> opts {optShowVersion = True}))
"show version number",
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 ['p'] ["prompt"]
(ReqArg (\prompt opts -> opts {optPrompt = prompt})
"String")
"set prompt string",
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 ['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 ['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 ['T'] ["tsv"]
(NoArg (\opts -> opts {optTsvOutput = True}))
"output in tsv format",
Option ['F'] ["--field"]
(ReqArg (\d opts -> opts {optFieldInfo = optFieldInfo opts ++ [readFieldOption d]})
"String")
"field information",
Option ['M'] ["math"]
(ReqArg (\lang opts -> opts {optMathExpr = Just lang})
"String")
"output in AsciiMath, Latex, Mathematica, or Maxima format",
Option ['N'] ["new-syntax"]
(NoArg (\opts -> opts {optSExpr = False}))
"parse by new syntax"
]
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"
putStrLn ""
putStrLn "Options to change input or output format:"
putStrLn " --tsv, -T Input and output in tsv format"
putStrLn " --field, -F field Specify a field type of input tsv"
putStrLn " --math, -M (asciimath|latex|mathematica|maxima)"
putStrLn " Output in AsciiMath, LaTeX, or Mathematica format (only for interpreter)"
exitSuccess
printVersionNumber :: IO ()
printVersionNumber = do
putStrLn $ showVersion version
exitSuccess
showBanner :: IO ()
showBanner = do
putStrLn $ "Egison Version " ++ showVersion version ++ " (C) 2011-2018 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 -> Bool -> Maybe String -> Env -> String -> IO ()
repl noIOFlag isSExpr mathExprLang env prompt =
loop $ StateT (\defines -> (, defines) <$> recursiveBind env defines)
where
settings :: MonadIO m => FilePath -> Settings m
settings home = setComplete completeEgison $ defaultSettings { historyFile = Just (home </> ".egison_history") }
loop :: StateT [(Var, EgisonExpr)] EgisonM Env -> IO ()
loop st = (do
home <- getHomeDirectory
input <- liftIO $ runInputT (settings home) $ getEgisonExpr isSExpr prompt
case (noIOFlag, input) of
(_, Nothing) -> return ()
(True, Just (_, LoadFile _ _)) -> do
putStrLn "error: No IO support"
loop st
(True, Just (_, Load _ _)) -> do
putStrLn "error: No IO support"
loop st
(_, Just (topExpr, _)) -> do
result <- liftIO $ runEgisonTopExpr' isSExpr st topExpr
case result of
Left err -> do
liftIO $ print err
loop st
Right (Nothing, st') -> loop st'
Right (Just output, st') ->
case mathExprLang of
Nothing -> putStrLn output >> loop st'
(Just "haskell") -> putStrLn (mathExprToHaskell output) >> loop st'
(Just "asciimath") -> putStrLn (mathExprToAsciiMath output) >> loop st'
(Just "latex") -> putStrLn (mathExprToLatex output) >> loop st'
(Just "mathematica") -> putStrLn (mathExprToMathematica output) >> loop st'
(Just "maxima") -> putStrLn (mathExprToMaxima output) >> loop st'
_ -> putStrLn "error: this output lang is not supported"
)
`catch`
(\case
UserInterrupt -> putStrLn "" >> loop st
StackOverflow -> putStrLn "Stack over flow!" >> loop st
HeapOverflow -> putStrLn "Heap over flow!" >> loop st
_ -> putStrLn "error!" >> loop st
)