Automatically discover .mima-flags files

This commit is contained in:
Joscha 2019-11-13 15:22:35 +00:00
parent b554d80aa9
commit 02e23e7d4a
5 changed files with 89 additions and 38 deletions

View file

@ -29,7 +29,7 @@ opts :: ParserInfo Settings
opts = info (helper <*> settingsParser) $ fullDesc <> failureCode 1 opts = info (helper <*> settingsParser) $ fullDesc <> failureCode 1
main :: IO () main :: IO ()
main = doRun $ do main = doRun_ $ do
settings <- lift $ execParser opts settings <- lift $ execParser opts
lift $ putStrLn $ "Loading assembly file at " ++ infile settings lift $ putStrLn $ "Loading assembly file at " ++ infile settings

View file

@ -4,26 +4,29 @@ import Control.Monad
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import Options.Applicative import Options.Applicative
import System.FilePath
import Mima.Flag import Mima.Flag
import Mima.IO import Mima.IO
import Mima.Load import Mima.Load
import Mima.Parser.FlagFile
import Mima.State import Mima.State
import Mima.Util import Mima.Util
import Mima.Word
import PrintState import PrintState
data Settings = Settings data Settings = Settings
-- General guff -- General
{ infile :: FilePath { infile :: FilePath
, outfile :: Maybe FilePath , outfile :: Maybe FilePath
, autodiscover :: Bool , discover :: Bool
, flagfile :: Maybe FilePath , flagFile :: Maybe FilePath
, symbolfile :: Maybe FilePath , symbolFile :: Maybe FilePath
-- Run-specific guff -- Running
, steps :: Maybe Integer , steps :: Maybe Integer
, norun :: Bool , norun :: Bool
-- Output format guff -- Output
, quiet :: Bool , quiet :: Bool
, sparse :: Bool , sparse :: Bool
} deriving (Show) } deriving (Show)
@ -41,9 +44,8 @@ settingsParser = Settings
<> metavar "OUTFILE" <> metavar "OUTFILE"
<> help "If specified, write the memory dump to this file after execution is finished") <> help "If specified, write the memory dump to this file after execution is finished")
<*> flag True False <*> flag True False
(long "autodiscover" (long "nodiscover"
<> short 'a' <> help "Disable the automatic loading of the .mima-flags and .mima-symbols files")
<> help "Automatically try to find the .mima-flags and .mima-symbols files corresponding to the input files")
<*> (optional . strOption) <*> (optional . strOption)
(long "flags" (long "flags"
<> short 'f' <> short 'f'
@ -56,12 +58,10 @@ settingsParser = Settings
<> help "A file containing label names and addresses, specified in the .mima-symbols format") <> help "A file containing label names and addresses, specified in the .mima-symbols format")
<*> (optional . option auto) <*> (optional . option auto)
(long "steps" (long "steps"
<> short 'n'
<> metavar "N" <> metavar "N"
<> help "How many instructions to execute (if not specified, runs until HALT or execution exception)") <> help "How many instructions to execute (if not specified, runs until HALT or execution exception)")
<*> flag False True <*> flag False True
(long "norun" (long "norun"
<> short 'r'
<> help "Don't run the MiMa. Use the initial state for all further actions. Roughly equivalent to -n 0") <> help "Don't run the MiMa. Use the initial state for all further actions. Roughly equivalent to -n 0")
<*> flag False True <*> flag False True
(long "quiet" (long "quiet"
@ -69,7 +69,6 @@ settingsParser = Settings
<> help "Don't print the memory dump") <> help "Don't print the memory dump")
<*> flag False True <*> flag False True
(long "sparse" (long "sparse"
<> short 's'
<> help "Don't print memory locations containing only 0x000000 in the memory dump") <> help "Don't print memory locations containing only 0x000000 in the memory dump")
opts :: ParserInfo Settings opts :: ParserInfo Settings
@ -77,31 +76,56 @@ opts = info (helper <*> settingsParser) $ fullDesc <> failureCode 1
{- Main logic -} {- Main logic -}
runMima :: Settings -> MimaState -> IO MimaState runMima :: Settings -> MimaState -> Flags (MimaAddress -> Bool) -> IO MimaState
runMima settings s = runMima settings s f =
case steps settings of case steps settings of
Nothing -> do Nothing -> do
putStrLn "Running until HALT or execution exception..." putStrLn "Running until HALT or execution exception..."
let (s', e, x) = run noFlags s let (s', e, x) = run f s
putStrLn $ "Ran for " ++ show x ++ " steps" putStrLn $ "Ran for " ++ show x ++ " steps"
T.putStrLn $ toText e T.putStrLn $ toText e
pure s' pure s'
Just n -> do Just n -> do
let (s', me, x) = runN noFlags n s let (s', me, x) = runN f n s
putStrLn $ "Ran for " ++ show x ++ " steps" putStrLn $ "Ran for " ++ show x ++ " steps"
case me of case me of
Nothing -> putStrLn "Encountered no exception" Nothing -> putStrLn "Encountered no exception"
Just e -> T.putStrLn $ toText e Just e -> T.putStrLn $ toText e
pure s' pure s'
loadFlagFile :: FilePath -> Run (Flags (MimaAddress -> Bool))
loadFlagFile filename = flagChecks <$> parseFile parseFlagFile filename
loadFlags :: Settings -> Run (Flags (MimaAddress -> Bool))
loadFlags settings = do
case flagFile settings of
Just filename -> do
lift $ putStrLn $ "Loading flags from specified file: " ++ filename
loadFlagFile filename
Nothing -> do
maybeFlags <- if discover settings then tryLoadDiscovered else pure Nothing
case maybeFlags of
Just flags -> pure flags
Nothing -> do
lift $ putStrLn "Not using flags"
pure noFlags
where
discovered = dropExtension (infile settings) ++ ".mima-flags"
tryLoadDiscovered = do
lift $ putStrLn $ "Loading flags from file: " ++ discovered
tryRun (loadFlagFile discovered)
-- TODO exception handling -- TODO exception handling
main :: IO () main :: IO ()
main = doRun $ do main = doRun_ $ do
settings <- lift $ execParser opts settings <- lift $ execParser opts
lift $ putStrLn $ "Loading memdump at " ++ infile settings lift $ putStrLn $ "Loading memdump at " ++ infile settings
ms <- loadStateFromFile (infile settings) ms <- loadStateFromFile (infile settings)
ms' <- if norun settings then pure ms else lift (runMima settings ms)
flags <- loadFlags settings
ms' <- if norun settings then pure ms else lift (runMima settings ms flags)
unless (quiet settings) $ do unless (quiet settings) $ do
lift $ putStrLn "" lift $ putStrLn ""

View file

@ -24,6 +24,7 @@ dependencies:
- binary - binary
- bytestring - bytestring
- containers - containers
- filepath
- megaparsec - megaparsec
- optparse-applicative - optparse-applicative
- text - text

View file

@ -1,32 +1,57 @@
{-# LANGUAGE MultiWayIf #-}
module Mima.IO module Mima.IO
( Run ( Run
, doRun , doRun
, failWith , doRun_
, tryRun
, readTextFile
, parseFile , parseFile
) where ) where
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Control.Monad.Trans.Except import Control.Monad.Trans.Except
import qualified Data.Text as T
import qualified Data.Text.IO as T import qualified Data.Text.IO as T
import System.IO.Error
import Text.Megaparsec import Text.Megaparsec
import Mima.Parser.Common import Mima.Parser.Common
type Run a = ExceptT String IO a type Run a = ExceptT String IO a
doRun :: Run () -> IO () doRun :: Run a -> IO (Either String a)
doRun r = do doRun = runExceptT
result <- runExceptT r
case result of
Left errorMsg -> putStrLn errorMsg
Right () -> pure ()
failWith :: String -> Run a doRun_ :: Run () -> IO ()
failWith = except . Left doRun_ r = do
result <- doRun r
case result of
Right () -> pure ()
Left e -> putStrLn e
tryRun :: Run a -> Run (Maybe a)
tryRun r = do
result <- lift $ runExceptT r
case result of
Right a -> pure $ Just a
Left e -> do
lift $ putStrLn e
pure Nothing
readTextFile :: FilePath -> Run T.Text
readTextFile filepath = do
eitherContent <- lift $ catchIOError (Right <$> T.readFile filepath) handleError
except eitherContent
where
isRelevantError e = isAlreadyInUseError e || isDoesNotExistError e || isPermissionError e
handleError e = if isRelevantError e
then pure $ Left $ "Can't load file " ++ filepath ++ ": " ++ ioeGetErrorString e
else ioError e -- This error does not concern us
parseFile :: Parser a -> FilePath -> Run a parseFile :: Parser a -> FilePath -> Run a
parseFile parser filepath = do parseFile parser filepath = do
content <- lift $ T.readFile filepath content <- readTextFile filepath
case parse parser filepath content of case parse parser filepath content of
Right a -> pure a Right a -> pure a
Left errorBundle -> failWith $ errorBundlePretty errorBundle Left errorBundle -> throwE $ errorBundlePretty errorBundle

View file

@ -8,6 +8,7 @@ module Mima.Load
import Control.Applicative import Control.Applicative
import Control.Monad import Control.Monad
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Data.Binary import Data.Binary
import qualified Data.ByteString.Lazy as BS import qualified Data.ByteString.Lazy as BS
@ -56,10 +57,10 @@ loadStateFromFile :: FilePath -> Run MimaState
loadStateFromFile path = do loadStateFromFile path = do
bs <- lift $ BS.readFile path bs <- lift $ BS.readFile path
case decodeOrFail bs of case decodeOrFail bs of
Left ( _, _, e) -> failWith e Left ( _, _, e) -> throwE e
Right (bs', _, ldms) Right (bs', _, ldms)
| BS.null bs' -> pure $ unLD ldms | BS.null bs' -> pure $ unLD ldms
| otherwise -> failWith "Input was not consumed fully" | otherwise -> throwE "Input was not consumed fully"
saveStateToFile :: FilePath -> MimaState -> Run () saveStateToFile :: FilePath -> MimaState -> Run ()
saveStateToFile path = lift . BS.writeFile path . encode . LD saveStateToFile path = lift . BS.writeFile path . encode . LD