{-# LANGUAGE RecordWildCards #-}
import Control.Monad (when)
import System.Environment
import System.FilePath
import System.Console.GetOpt
import System.IO
import Language.SSVM.Types
import Language.SSVM.Binary
import Language.SSVM.Interpreter
import Language.Pascal.TypeCheck
import Language.Pascal.CodeGen
data Flag =
Mode Mode
| Output FilePath
| HelpFlag
deriving (Eq, Show)
data Mode =
Compile
| Assembler
| Interpret
deriving (Eq, Show)
data Options =
Help
| Run {
mode :: Mode,
inputFile :: FilePath,
outputFile :: Maybe FilePath }
deriving (Eq, Show)
defaultOptions :: Options
defaultOptions = Run {
mode = Compile,
inputFile = "-",
outputFile = Nothing }
options :: [OptDescr Flag]
options = [
Option "c" ["compile"] (NoArg $ Mode Compile) "compile source code to bytecode",
Option "i" ["interpret"] (NoArg $ Mode Interpret) "compile and interpred compiled bytecode",
Option "S" [] (NoArg $ Mode Assembler) "dump SSVM assembler code",
Option "o" ["output"] (ReqArg Output "FILE") "set output file name",
Option "h" ["help"] (NoArg HelpFlag) "show this help and exit" ]
usage :: String
usage = usageInfo header options
where
header = "Usage: spc [MODE] [-o OUTPUT] FILE.pas"
flags2options :: [Flag] -> Options
flags2options = foldl go defaultOptions
where
go acc (Mode m) = acc {mode = m}
go acc (Output path) = acc {outputFile = Just path}
go _ HelpFlag = Help
parseCmdLine :: [String] -> Either String Options
parseCmdLine args =
case getOpt Permute options args of
(flags, [], []) | HelpFlag `elem` flags -> Right Help
| otherwise -> Left "No input file"
(flags, [file], []) -> Right (flags2options flags) {inputFile = file}
(_, (_:_:_), []) -> Left "More than one input file"
(_, _, errs) -> Left $ unlines errs ++ usage
main = do
args <- getArgs
case parseCmdLine args of
Left err -> error err
Right Help -> putStrLn usage
Right (Run {..}) -> do
let dst = case outputFile of
Nothing -> replaceExtension inputFile ".bytecode"
Just x -> x
prog <- checkSource inputFile
let codeRev = runCodeGen (generate prog)
code = codeRev {cCode = reverse (cCode codeRev)}
case mode of
Compile -> dumpCode dst code
Assembler -> putStrLn $ showCode code
Interpret -> do
term <- hIsTerminalDevice stdout
when term $
hSetBuffering stdout NoBuffering
runVM (interpret code)