module Main where import qualified PureSyntax as PS import Lexer (scan) import SourceParser (parseLVal) import SugarSyntax import DesugarSI import qualified DesugarIP as IP import qualified PureInterpreter as I import qualified LoggingInterpreter as LI import Unparser import REPL import qualified Data.Map as M import qualified Data.Set as S import System.Exit import System.Environment (getArgs) import System.FilePath import Data.List (intersperse) import Control.Monad.Except import Control.Monad.State.Strict (runStateT) noArgsMessage :: String noArgsMessage = "No arguments supplied. Run 'hwhile -h' for help." badArgsMessage :: String badArgsMessage = "Invalid argument(s) supplied. Run 'hwhile -h' for help." versionMessage :: String versionMessage = "HWhile version 0.1.1.2" helpMessage :: String helpMessage = concat $ (intersperse "\n") $ [ "HWhile: a Haskell implementation of the while language, by Alex Jeffery." , "Usage:" , " hwhile -h - Print this message and exit." , " hwhile -r - Enter interactive mode." , " hwhile -v - Print HWhile's version number and" , " exit." , " hwhile - Run the program in with input" , " . Note that may require" , " surrounding \"double quotes\"." , " Program output will be displayed in" , " the format specified by the chosen" , " ." , "Possible flags:" , " [NOTHING] - Display output as a while tree." , " -i - Display output as an integer. If the output is not a" , " valid integer, 'E' will be displayed." , " -iv - Display output as an integer. If the output is not a" , " valid integer, it will be displayed as a while tree." , " -l - Display output as a list of while trees." , " -li - Display output as a list of integers. Invalid elements" , " will all display as 'E'." , " -liv - Display output as a list of integers. Invalid elements" , " will all display as while trees." , " -L - Display output as a nested list of integers. Valid" , " integers will display as such, and invalid integers will" , " display as nested lists of integers." , " -La - Display output as a nested list of atoms and integers." , " Valid atoms will display as such, invalid atoms that are" , " valid integers will display as integers, and invalid" , " integers will display as nested lists of atoms and" , " integers." , " -d - Display outputs as while trees, with debugging log" , " -di - Display outputs as integers, with debugging log. If" , " outputs are not valid integers, 'E' will be displayed." , " -div - Display outputs as integers. If outputs are not valid" , " integers, they will display as while trees." , " -dl - Display outputs as lists of while trees, with debugging" , " log." , " -dli - Display outputs as lists of integers, with debugging" , " log. Invalid elements will display as 'E'." , " -dliv - Display outputs as lists of integers, with debugging" , " log. Invalid elements will display as while trees." , " -dL - Display outputs as nested lists of integers, with" , " debugging log. Valid integers will display as such, and" , " invalid integers will display as nested lists of" , " integers." , " -dLa - Display outputs as nested lists of atoms and integers," , " with debugging log. Valid atoms will display as such," , " invalid atoms that are valid integers will display as" , " integers, and invalid integers will display as nested" , " lists of atoms and integers." , " -u - Run the unparser on the given program. In this mode, no" , " argument expression should be supplied." ] -- Compute a function to display an expression in a certain way according to the -- given command line argument. getShowFunctionAndInterpreterFunction :: Maybe String -> Maybe (PS.ETree -> String, PS.ETree -> PS.Program -> IO PS.ETree) getShowFunctionAndInterpreterFunction flagStr = case flagStr of Nothing -> Just (show , \t p -> return (I.evalProg t p)) Just "-i" -> Just (\tree -> maybe "E" show $ PS.parseInt tree , \t p -> return (I.evalProg t p)) Just "-iv" -> Just (\tree -> maybe (show tree) show $ PS.parseInt tree , \t p -> return (I.evalProg t p)) Just "-l" -> Just (show . PS.toHaskellList , \t p -> return (I.evalProg t p)) Just "-li" -> Just (PS.showIntListTree False , \t p -> return (I.evalProg t p)) Just "-liv" -> Just (PS.showIntListTree True , \t p -> return (I.evalProg t p)) Just "-L" -> Just (PS.showNestedIntListTree , \t p -> return (I.evalProg t p)) Just "-La" -> Just (PS.showNestedAtomIntListTree , \t p -> return (I.evalProg t p)) Just "-d" -> let sfn = show in Just (sfn, LI.evalProg sfn) Just "-di" -> let sfn = \tree -> maybe "E" show $ PS.parseInt tree in Just (sfn, LI.evalProg sfn) Just "-div" -> let sfn = \tree -> maybe (show tree) show $ PS.parseInt tree in Just (sfn, LI.evalProg sfn) Just "-dl" -> let sfn = show . PS.toHaskellList in Just (sfn, LI.evalProg sfn) Just "-dli" -> let sfn = PS.showIntListTree False in Just (sfn, LI.evalProg sfn) Just "-dliv" -> let sfn = PS.showIntListTree True in Just (sfn, LI.evalProg sfn) Just "-dL" -> let sfn = PS.showNestedIntListTree in Just (sfn, LI.evalProg sfn) Just "-dLa" -> let sfn = PS.showNestedAtomIntListTree in Just (sfn, LI.evalProg sfn) Just _ -> Nothing main :: IO () main = do result <- runExceptT exceptMain case result of Left errorMsg -> do putStrLn errorMsg ; exitFailure Right () -> exitSuccess -- Parse the command structure to pass appropriate arguments to doRun, or quit -- with an error/help message. exceptMain :: ExceptT String IO () exceptMain = do args <- lift getArgs if (length args) == 0 then do lift $ putStrLn noArgsMessage else if (args !! 0) == "-r" then do lift $ runStateT runREPL emptyREPLState return () else if (args !! 0) == "-h" then do lift $ putStrLn helpMessage else if (args !! 0) == "-v" then do lift $ putStrLn versionMessage else if (length args) == 2 && (args !! 0) == "-u" then do let mainFile = args !! 1 doUnparse mainFile else if (length args) == 2 then do let mainFile = args !! 0 let argStr = args !! 1 doRun Nothing mainFile argStr else if (length args) == 3 then do let flagStr = args !! 0 let mainFile = args !! 1 let argStr = args !! 2 doRun (Just flagStr) mainFile argStr else lift $ putStrLn badArgsMessage doUnparse :: FilePath -> ExceptT String IO () doUnparse mainFile = let mainFileDir = takeDirectory mainFile mainFileBaseName = takeBaseName mainFile in do prog <- loadProg mainFileDir mainFileBaseName [] maybe (lift (putStrLn "E")) (lift . putStrLn) (PS.showProgramTree (unparse (IP.desugarProg prog))) doRun :: Maybe String -> FilePath -> String -> ExceptT String IO () doRun flagStr mainFile argStr = let mainFileDir = takeDirectory mainFile mainFileBaseName = takeBaseName mainFile in case getShowFunctionAndInterpreterFunction flagStr of Nothing -> lift $ putStrLn badArgsMessage Just (showFunction, interpreterFunction) -> do result <- runFromParts mainFileDir mainFileBaseName argStr interpreterFunction lift $ putStrLn $ showFunction $ result -- Run a program given the search path, file path, argument string and -- interpreting function runFromParts :: FilePath -> -- The search path file FilePath -> -- The main file to run String -> -- The argument string (PS.ETree -> PS.Program -> IO PS.ETree) -> -- The interpreting function ExceptT String IO PS.ETree -- The result of the execution runFromParts dir fileBaseName argStr interpret = do prog <- loadProg dir fileBaseName [] let argTokens = scan argStr "+IMPL+" argTree <- parseLVal argTokens lift $ interpret argTree (IP.desugarProg prog)