Automatically discover .mima-flags files
This commit is contained in:
parent
b554d80aa9
commit
02e23e7d4a
5 changed files with 89 additions and 38 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 ""
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ dependencies:
|
||||||
- binary
|
- binary
|
||||||
- bytestring
|
- bytestring
|
||||||
- containers
|
- containers
|
||||||
|
- filepath
|
||||||
- megaparsec
|
- megaparsec
|
||||||
- optparse-applicative
|
- optparse-applicative
|
||||||
- text
|
- text
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue