Implement basic mima-run functionality

Bugs fixed:
- Print correct file name when load/save error occurs
- Correctly identify invalid file formats
This commit is contained in:
Joscha 2020-03-27 22:07:24 +00:00
parent 362025c8df
commit 6b81fd67b4
5 changed files with 52 additions and 16 deletions

View file

@ -1,10 +1,31 @@
module Main where module Main where
import Options.Applicative import Control.Monad.IO.Class
import qualified Data.Text.IO as T
import Options.Applicative
import Mima.MimaRun.Options import Mima.Format
import Mima.MimaRun.Options
import Mima.Run
import Mima.Vm.State
import Mima.Vm.Storage
main :: IO () main :: IO ()
main = do main = runOrExit 2 $ do
opts <- execParser parserInfo opts <- liftIO $ execParser parserInfo
putStrLn $ "The options are: " ++ show opts initialState <- loadMimaState $ inputFile opts
finalState <- liftIO $ case steps opts of
Nothing -> do
let (finalState, abortReason, stepsMade) = execute initialState
putStrLn $ "Stopped after " ++ show stepsMade ++ " steps, reason:"
T.putStrLn $ toText abortReason
pure finalState
Just n -> do
let (finalState, mAbortReason, stepsMade) = executeN n initialState
putStrLn $ "Stopped after " ++ show stepsMade ++ " steps, reason:"
case mAbortReason of
Nothing -> putStrLn "Ran out of steps"
Just abortReason -> T.putStrLn $ toText abortReason
pure finalState
liftIO $ putStrLn ""
liftIO $ print finalState

View file

@ -3,16 +3,16 @@ module Mima.MimaRun.Options
, parserInfo , parserInfo
) where ) where
import Options.Applicative import Options.Applicative
data Options = Options data Options = Options
{ inputFile :: FilePath { inputFile :: FilePath
, steps :: Maybe Integer , steps :: Maybe Integer
} deriving (Show) } deriving (Show)
parser :: Parser Options parser :: Parser Options
parser = Options parser = Options
<$> strOption <$> strArgument
( help "The .mima file to use" ( help "The .mima file to use"
<> metavar "INPUTFILE" <> metavar "INPUTFILE"
) )

View file

@ -5,6 +5,7 @@ module Mima.Run
-- * The 'Run' monad -- * The 'Run' monad
Run Run
, run , run
, runOrExit
, throw , throw
, catch , catch
, handle , handle
@ -18,8 +19,10 @@ module Mima.Run
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Except import Control.Monad.Trans.Except
import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy as BS
import Data.Maybe
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import System.Exit
import System.IO.Error import System.IO.Error
newtype Run a = Run (ExceptT T.Text IO a) newtype Run a = Run (ExceptT T.Text IO a)
@ -28,6 +31,16 @@ newtype Run a = Run (ExceptT T.Text IO a)
run :: Run a -> IO (Either T.Text a) run :: Run a -> IO (Either T.Text a)
run (Run e) = runExceptT e run (Run e) = runExceptT e
runOrExit :: Int -> Run () -> IO ()
runOrExit exitCode r = do
result <- run r
case result of
Right () -> pure ()
Left e -> do
putStrLn "Encountered an exception:"
T.putStrLn e
exitWith $ ExitFailure exitCode
throw :: T.Text -> Run a throw :: T.Text -> Run a
throw = Run . throwE throw = Run . throwE
@ -43,8 +56,10 @@ isRelevantError e = isAlreadyInUseError e || isDoesNotExistError e || isPermissi
formatRelevantError :: IOError -> Maybe T.Text formatRelevantError :: IOError -> Maybe T.Text
formatRelevantError e formatRelevantError e
| isRelevantError e = Just $ T.pack $ | isRelevantError e = Just $ T.pack $
"Can't open file " <> ioeGetLocation e <> ": " <> ioeGetErrorString e "Can't open file " <> fileName <> ": " <> ioeGetErrorString e
| otherwise = Nothing | otherwise = Nothing
where
fileName = fromMaybe "<unknown>" $ ioeGetFileName e
protect :: IO a -> Run a protect :: IO a -> Run a
protect m = do protect m = do

View file

@ -6,8 +6,8 @@ module Mima.Vm.State
, basicState , basicState
, AbortReason(..) , AbortReason(..)
, step , step
, run , execute
, runN , executeN
) where ) where
import Control.Applicative import Control.Applicative
@ -157,16 +157,16 @@ step ms = do
(SmallInstruction so lv) -> doSmallOpcode so lv ms' (SmallInstruction so lv) -> doSmallOpcode so lv ms'
(LargeInstruction lo sv) -> doLargeOpcode lo sv ms' (LargeInstruction lo sv) -> doLargeOpcode lo sv ms'
run :: MimaState -> (MimaState, AbortReason, Integer) execute :: MimaState -> (MimaState, AbortReason, Integer)
run = helper 0 execute = helper 0
where where
helper completed s = helper completed s =
case step s of case step s of
Left e -> (s, e, completed) Left e -> (s, e, completed)
Right s' -> helper (completed + 1) s' Right s' -> helper (completed + 1) s'
runN :: Integer -> MimaState -> (MimaState, Maybe AbortReason, Integer) executeN :: Integer -> MimaState -> (MimaState, Maybe AbortReason, Integer)
runN n = helper 0 executeN n = helper 0
where where
helper completed s = helper completed s =
if completed >= n if completed >= n

View file

@ -32,7 +32,7 @@ loadMimaState :: FilePath -> Run MimaState
loadMimaState path = do loadMimaState path = do
bs <- readFileBS path bs <- readFileBS path
case B.decodeOrFail bs of case B.decodeOrFail bs of
Right ("", 0, a) -> pure a Right ("", _, a) -> pure a
Right _ -> throw "invalid file format" Right _ -> throw "invalid file format"
Left (_, _, e) -> throw $ T.pack e Left (_, _, e) -> throw $ T.pack e