module Main where import Control.Applicative ((<$>), (<*>)) import Control.Monad.Error import Data.Version import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lazy.Char8 () import qualified Data.ByteString.Lazy.Char8 as B import Text.Parsec import Text.Parsec.ByteString.Lazy import Text.Regex.Posix import System.IO import System.Environment import System.Directory (getHomeDirectory) import System.FilePath (()) import System.Console.Haskeline import System.Console.GetOpt import System.Exit (ExitCode (..), exitWith, exitFailure) import Language.Egison import qualified Paths_egison_tutorial as P 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 {optPrompt = prompt} -> do env <- primitiveEnv >>= loadLibraries case nonOpts of [] -> showBanner >> repl env prompt _ -> printHelp data Options = Options { optShowVersion :: Bool, optShowHelp :: Bool, optPrompt :: String } defaultOptions :: Options defaultOptions = Options { optShowVersion = False, optShowHelp = 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 ['p'] ["prompt"] (ReqArg (\prompt opts -> opts {optPrompt = prompt}) "String") "set prompt string" ] printHelp :: IO () printHelp = do putStrLn "Usage: egison-tutorial [options]" putStrLn "" putStrLn "Options:" putStrLn " --help Display this information" putStrLn " --version Display egison version information" putStrLn " --prompt string Set prompt of the interpreter" putStrLn "" exitWith ExitSuccess printVersionNumber :: IO () printVersionNumber = do putStrLn $ showVersion P.version exitWith ExitSuccess showBanner :: IO () showBanner = do putStrLn $ "Egison Tutorial for Version " ++ showVersion version ++ " (C) 2013 Satoshi Egi" putStrLn $ "http://www.egison.org" putStrLn $ "Welcome to Egison Tutorial!" showFinishMessage :: IO () showFinishMessage = do putStrLn $ "You finished all tutorials!" putStrLn $ "Thank you!" putStrLn $ "Let's enjoy Egison!" showByebyeMessage :: IO () showByebyeMessage = do putStrLn $ "Leaving Egison Tutorial.\nByebye." askUser :: String -> IO Bool askUser question = do putStr $ question putStr $ " (y/n): " hFlush stdout input <- getLine case input of ('y':_) -> return True ('n':_) -> return False _ -> askUser question repl :: Env -> String -> IO () repl env prompt = do home <- getHomeDirectory liftIO (runInputT (settings home) $ loop env prompt "" tutorials True) where settings :: MonadIO m => FilePath -> Settings m settings home = defaultSettings { historyFile = Just (home ".egison_tutorial_history") } loop :: Env -> String -> String -> [Tutorial] -> Bool -> InputT IO () loop _ _ _ [] _ = do liftIO $ showFinishMessage return () loop env prompt' rest ts@(t:rs) True = do liftIO $ putStrLn t loop env prompt' rest ts False loop env prompt' rest ts@(t:rs) False = do input <- getInputLine prompt' case input of Nothing -> do response1 <- liftIO $ askUser "Do you want to proceed next?" case response1 of True -> loop env prompt' rest rs True False -> do response2 <- liftIO $ askUser "Do you want to quit egison-tutorial?" case response2 of True -> do liftIO $ showByebyeMessage return () False -> loop env prompt' rest ts False Just "quit" -> do liftIO $ showByebyeMessage return () Just "" -> case rest of "" -> do response1 <- liftIO $ askUser "Do you want to proceed next?" case response1 of True -> loop env prompt' rest rs True False -> loop env prompt' rest ts False _ -> loop env (take (length prompt) (repeat ' ')) rest ts False Just input' -> do let newInput = rest ++ input' result <- liftIO $ runEgisonTopExpr env newInput case result of Left err | show err =~ "unexpected end of input" -> do loop env (take (length prompt) (repeat ' ')) (newInput ++ "\n") ts False Left err | show err =~ "expecting (top-level|\"define\")" -> do result <- liftIO $ fromEgisonM (readExpr newInput) >>= either (return . Left) (evalEgisonExpr env) case result of Left err | show err =~ "unexpected end of input" -> do loop env (take (length prompt) (repeat ' ')) (newInput ++ "\n") ts False Left err -> do liftIO $ putStrLn $ show err loop env prompt "" ts False Right val -> do liftIO $ putStrLn $ show val loop env prompt "" ts False Left err -> do liftIO $ putStrLn $ show err loop env prompt "" ts False Right env' -> loop env' prompt "" ts False type Tutorial = String tutorials :: [Tutorial] tutorials = [ "You can do arithmetic operations with `+`, `-`, `*`, `div`. Try them as `(+ 1 2)`.", "You can bind a value to a variable with a `define` expression. Try it as `(define $x 10))`.", "You can get a value you binded to the variable. Try them as `x`." ]