From cbb08507aea00fd95eaf065a26a8902eb8e412c3 Mon Sep 17 00:00:00 2001 From: Yann Herklotz Date: Sun, 27 Oct 2019 20:05:08 +0000 Subject: Add OptParser to separate option parsing --- src/Verismith.hs | 249 +----------------------------------------- src/Verismith/OptParser.hs | 266 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 245 deletions(-) create mode 100644 src/Verismith/OptParser.hs (limited to 'src') diff --git a/src/Verismith.hs b/src/Verismith.hs index a3d3d03..bde3e2a 100644 --- a/src/Verismith.hs +++ b/src/Verismith.hs @@ -62,6 +62,7 @@ import Verismith.Circuit import Verismith.Config import Verismith.Fuzz import Verismith.Generate +import Verismith.OptParser import Verismith.Reduce import Verismith.Report import Verismith.Result @@ -70,254 +71,12 @@ import Verismith.Tool.Internal import Verismith.Verilog import Verismith.Verilog.Parser (parseSourceInfoFile) -data OptTool = TYosys - | TXST - | TIcarus - -instance Show OptTool where - show TYosys = "yosys" - show TXST = "xst" - show TIcarus = "icarus" - -data Opts = Fuzz { fuzzOutput :: {-# UNPACK #-} !Text - , configFile :: !(Maybe FilePath) - , forced :: !Bool - , keepAll :: !Bool - , num :: {-# UNPACK #-} !Int - } - | Generate { mFileName :: !(Maybe FilePath) - , configFile :: !(Maybe FilePath) - } - | Parse { fileName :: {-# UNPACK #-} !FilePath - } - | Reduce { fileName :: {-# UNPACK #-} !FilePath - , top :: {-# UNPACK #-} !Text - , reduceScript :: !(Maybe FilePath) - , synthesiserDesc :: ![SynthDescription] - , rerun :: Bool - } - | ConfigOpt { writeConfig :: !(Maybe FilePath) - , configFile :: !(Maybe FilePath) - , doRandomise :: !Bool - } - myForkIO :: IO () -> IO (MVar ()) myForkIO io = do mvar <- newEmptyMVar _ <- forkFinally io (\_ -> putMVar mvar ()) return mvar -textOption :: Mod OptionFields String -> Parser Text -textOption = fmap T.pack . strOption - -optReader :: (String -> Maybe a) -> ReadM a -optReader f = eitherReader $ \arg -> case f arg of - Just a -> Right a - Nothing -> Left $ "Cannot parse option: " <> arg - -parseSynth :: String -> Maybe OptTool -parseSynth val | val == "yosys" = Just TYosys - | val == "xst" = Just TXST - | otherwise = Nothing - -parseSynthDesc :: String -> Maybe SynthDescription -parseSynthDesc val - | val == "yosys" = Just $ SynthDescription "yosys" Nothing Nothing Nothing - | val == "vivado" = Just $ SynthDescription "vivado" Nothing Nothing Nothing - | val == "xst" = Just $ SynthDescription "xst" Nothing Nothing Nothing - | val == "quartus" = Just - $ SynthDescription "quartus" Nothing Nothing Nothing - | val == "identity" = Just - $ SynthDescription "identity" Nothing Nothing Nothing - | otherwise = Nothing - -parseSim :: String -> Maybe OptTool -parseSim val | val == "icarus" = Just TIcarus - | otherwise = Nothing - -fuzzOpts :: Parser Opts -fuzzOpts = - Fuzz - <$> textOption - ( long "output" - <> short 'o' - <> metavar "DIR" - <> help "Output directory that the fuzz run takes place in." - <> showDefault - <> value "output" - ) - <*> ( optional - . strOption - $ long "config" - <> short 'c' - <> metavar "FILE" - <> help "Config file for the current fuzz run." - ) - <*> (switch $ long "force" <> short 'f' <> help - "Overwrite the specified directory." - ) - <*> (switch $ long "keep" <> short 'k' <> help - "Keep all the directories." - ) - <*> ( option auto - $ long "num" - <> short 'n' - <> help "The number of fuzz runs that should be performed." - <> showDefault - <> value 1 - <> metavar "INT" - ) - -genOpts :: Parser Opts -genOpts = - Generate - <$> ( optional - . strOption - $ long "output" - <> short 'o' - <> metavar "FILE" - <> help "Output to a verilog file instead." - ) - <*> ( optional - . strOption - $ long "config" - <> short 'c' - <> metavar "FILE" - <> help "Config file for the generation run." - ) - -parseOpts :: Parser Opts -parseOpts = Parse . fromText . T.pack <$> strArgument - (metavar "FILE" <> help "Verilog input file.") - -reduceOpts :: Parser Opts -reduceOpts = - Reduce - . fromText - . T.pack - <$> strArgument (metavar "FILE" <> help "Verilog input file.") - <*> textOption - ( short 't' - <> long "top" - <> metavar "TOP" - <> help "Name of top level module." - <> showDefault - <> value "top" - ) - <*> ( optional - . strOption - $ long "script" - <> metavar "SCRIPT" - <> help - "Script that determines if the current file is interesting, which is determined by the script returning 0." - ) - <*> ( many - . option (optReader parseSynthDesc) - $ short 's' - <> long "synth" - <> metavar "SYNTH" - <> help "Specify synthesiser to use." - ) - <*> ( switch - $ short 'r' - <> long "rerun" - <> help - "Only rerun the current synthesis file with all the synthesisers." - ) - -configOpts :: Parser Opts -configOpts = - ConfigOpt - <$> ( optional - . strOption - $ long "output" - <> short 'o' - <> metavar "FILE" - <> help "Output to a TOML Config file." - ) - <*> ( optional - . strOption - $ long "config" - <> short 'c' - <> metavar "FILE" - <> help "Config file for the current fuzz run." - ) - <*> ( switch - $ long "randomise" - <> short 'r' - <> help - "Randomise the given default config, or the default config by randomly switchin on and off options." - ) - -argparse :: Parser Opts -argparse = - hsubparser - ( command - "fuzz" - (info - fuzzOpts - (progDesc - "Run fuzzing on the specified simulators and synthesisers." - ) - ) - <> metavar "fuzz" - ) - <|> hsubparser - ( command - "generate" - (info - genOpts - (progDesc "Generate a random Verilog program.") - ) - <> metavar "generate" - ) - <|> hsubparser - ( command - "parse" - (info - parseOpts - (progDesc - "Parse a verilog file and output a pretty printed version." - ) - ) - <> metavar "parse" - ) - <|> hsubparser - ( command - "reduce" - (info - reduceOpts - (progDesc - "Reduce a Verilog file by rerunning the fuzzer on the file." - ) - ) - <> metavar "reduce" - ) - <|> hsubparser - ( command - "config" - (info - configOpts - (progDesc - "Print the current configuration of the fuzzer." - ) - ) - <> metavar "config" - ) - -version :: Parser (a -> a) -version = infoOption versionInfo $ mconcat - [long "version", short 'v', help "Show version information.", hidden] - -opts :: ParserInfo Opts -opts = info - (argparse <**> helper <**> version) - ( fullDesc - <> progDesc "Fuzz different simulators and synthesisers." - <> header - "Verismith - A hardware simulator and synthesiser Verilog fuzzer." - ) - getConfig :: Maybe FilePath -> IO Config getConfig s = maybe (return defaultConfig) parseConfigFile $ T.unpack . toTextIgnore <$> s @@ -363,12 +122,12 @@ randomise config@(Config a _ c d e) = do ce = config ^. configProbability . probExpr handleOpts :: Opts -> IO () -handleOpts (Fuzz o configF _ k n) = do +handleOpts (Fuzz o configF f k n nosim noequiv) = do config <- getConfig configF _ <- runFuzz - config + (FuzzOpts (Just $ fromText o) f k n nosim noequiv config) defaultYosys - (fuzzMultiple n k (Just $ fromText o) (proceduralSrc "top" config)) + (fuzzMultiple (proceduralSrc "top" config)) return () handleOpts (Generate f c) = do config <- getConfig c diff --git a/src/Verismith/OptParser.hs b/src/Verismith/OptParser.hs new file mode 100644 index 0000000..1db0d52 --- /dev/null +++ b/src/Verismith/OptParser.hs @@ -0,0 +1,266 @@ +module Verismith.OptParser + ( OptTool (..) + , Opts (..) + , opts + ) +where + +import Control.Applicative ((<|>)) +import Data.Text (Text) +import qualified Data.Text as T +import Options.Applicative (Mod (..), OptionFields (..), Parser (..), + ParserInfo (..), ReadM (..), (<**>)) +import qualified Options.Applicative as Opt +import Prelude hiding (FilePath (..)) +import Shelly (FilePath (..), fromText) +import Verismith.Config (SynthDescription (..), versionInfo) + +data OptTool = TYosys + | TXST + | TIcarus + +instance Show OptTool where + show TYosys = "yosys" + show TXST = "xst" + show TIcarus = "icarus" + +data Opts = Fuzz { fuzzOutput :: {-# UNPACK #-} !Text + , fuzzConfigFile :: !(Maybe FilePath) + , fuzzForced :: !Bool + , fuzzKeepAll :: !Bool + , fuzzNum :: {-# UNPACK #-} !Int + , fuzzNoSim :: !Bool + , fuzzNoEquiv :: !Bool + } + | Generate { generateFilename :: !(Maybe FilePath) + , generateConfigFile :: !(Maybe FilePath) + } + | Parse { parseFilename :: {-# UNPACK #-} !FilePath + } + | Reduce { reduceFilename :: {-# UNPACK #-} !FilePath + , reduceTop :: {-# UNPACK #-} !Text + , reduceScript :: !(Maybe FilePath) + , reduceSynthesiserDesc :: ![SynthDescription] + , reduceRerun :: !Bool + } + | ConfigOpt { configOptWriteConfig :: !(Maybe FilePath) + , configOptConfigFile :: !(Maybe FilePath) + , configOptDoRandomise :: !Bool + } + +textOption :: Mod OptionFields String -> Parser Text +textOption = fmap T.pack . Opt.strOption + +optReader :: (String -> Maybe a) -> ReadM a +optReader f = Opt.eitherReader $ \arg -> case f arg of + Just a -> Right a + Nothing -> Left $ "Cannot parse option: " <> arg + +parseSynth :: String -> Maybe OptTool +parseSynth val | val == "yosys" = Just TYosys + | val == "xst" = Just TXST + | otherwise = Nothing + +parseSynthDesc :: String -> Maybe SynthDescription +parseSynthDesc val + | val == "yosys" = Just $ SynthDescription "yosys" Nothing Nothing Nothing + | val == "vivado" = Just $ SynthDescription "vivado" Nothing Nothing Nothing + | val == "xst" = Just $ SynthDescription "xst" Nothing Nothing Nothing + | val == "quartus" = Just + $ SynthDescription "quartus" Nothing Nothing Nothing + | val == "identity" = Just + $ SynthDescription "identity" Nothing Nothing Nothing + | otherwise = Nothing + +parseSim :: String -> Maybe OptTool +parseSim val | val == "icarus" = Just TIcarus + | otherwise = Nothing + +fuzzOpts :: Parser Opts +fuzzOpts = + Fuzz + <$> textOption + ( Opt.long "output" + <> Opt.short 'o' + <> Opt.metavar "DIR" + <> Opt.help "Output directory that the fuzz run takes place in." + <> Opt.showDefault + <> Opt.value "output" + ) + <*> ( Opt.optional + . Opt.strOption + $ Opt.long "config" + <> Opt.short 'c' + <> Opt.metavar "FILE" + <> Opt.help "Config file for the current fuzz run." + ) + <*> (Opt.switch $ Opt.long "force" <> Opt.short 'f' <> Opt.help + "Overwrite the specified directory." + ) + <*> (Opt.switch $ Opt.long "keep" <> Opt.short 'k' <> Opt.help + "Keep all the directories." + ) + <*> ( Opt.option Opt.auto + $ Opt.long "num" + <> Opt.short 'n' + <> Opt.help "The number of fuzz runs that should be performed." + <> Opt.showDefault + <> Opt.value 1 + <> Opt.metavar "INT" + ) + <*> (Opt.switch $ Opt.long "no-sim" <> Opt.help + "Do not run simulation on the output netlist." + ) + <*> (Opt.switch $ Opt.long "no-equiv" <> Opt.help + "Do not run an equivalence check on the output netlist." + ) + +genOpts :: Parser Opts +genOpts = + Generate + <$> ( Opt.optional + . Opt.strOption + $ Opt.long "output" + <> Opt.short 'o' + <> Opt.metavar "FILE" + <> Opt.help "Output to a verilog file instead." + ) + <*> ( Opt.optional + . Opt.strOption + $ Opt.long "config" + <> Opt.short 'c' + <> Opt.metavar "FILE" + <> Opt.help "Config file for the generation run." + ) + +parseOpts :: Parser Opts +parseOpts = Parse . fromText . T.pack <$> Opt.strArgument + (Opt.metavar "FILE" <> Opt.help "Verilog input file.") + +reduceOpts :: Parser Opts +reduceOpts = + Reduce + . fromText + . T.pack + <$> Opt.strArgument (Opt.metavar "FILE" <> Opt.help "Verilog input file.") + <*> textOption + ( Opt.short 't' + <> Opt.long "top" + <> Opt.metavar "TOP" + <> Opt.help "Name of top level module." + <> Opt.showDefault + <> Opt.value "top" + ) + <*> ( Opt.optional + . Opt.strOption + $ Opt.long "script" + <> Opt.metavar "SCRIPT" + <> Opt.help + "Script that determines if the current file is interesting, which is determined by the script returning 0." + ) + <*> ( Opt.many + . Opt.option (optReader parseSynthDesc) + $ Opt.short 's' + <> Opt.long "synth" + <> Opt.metavar "SYNTH" + <> Opt.help "Specify synthesiser to use." + ) + <*> ( Opt.switch + $ Opt.short 'r' + <> Opt.long "rerun" + <> Opt.help + "Only rerun the current synthesis file with all the synthesisers." + ) + +configOpts :: Parser Opts +configOpts = + ConfigOpt + <$> ( Opt.optional + . Opt.strOption + $ Opt.long "output" + <> Opt.short 'o' + <> Opt.metavar "FILE" + <> Opt.help "Output to a TOML Config file." + ) + <*> ( Opt.optional + . Opt.strOption + $ Opt.long "config" + <> Opt.short 'c' + <> Opt.metavar "FILE" + <> Opt.help "Config file for the current fuzz run." + ) + <*> ( Opt.switch + $ Opt.long "randomise" + <> Opt.short 'r' + <> Opt.help + "Randomise the given default config, or the default config by randomly switchin on and off options." + ) + +argparse :: Parser Opts +argparse = + Opt.hsubparser + ( Opt.command + "fuzz" + (Opt.info + fuzzOpts + (Opt.progDesc + "Run fuzzing on the specified simulators and synthesisers." + ) + ) + <> Opt.metavar "fuzz" + ) + <|> Opt.hsubparser + ( Opt.command + "generate" + (Opt.info + genOpts + (Opt.progDesc "Generate a random Verilog program.") + ) + <> Opt.metavar "generate" + ) + <|> Opt.hsubparser + ( Opt.command + "parse" + (Opt.info + parseOpts + (Opt.progDesc + "Parse a verilog file and output a pretty printed version." + ) + ) + <> Opt.metavar "parse" + ) + <|> Opt.hsubparser + ( Opt.command + "reduce" + (Opt.info + reduceOpts + (Opt.progDesc + "Reduce a Verilog file by rerunning the fuzzer on the file." + ) + ) + <> Opt.metavar "reduce" + ) + <|> Opt.hsubparser + ( Opt.command + "config" + (Opt.info + configOpts + (Opt.progDesc + "Print the current configuration of the fuzzer." + ) + ) + <> Opt.metavar "config" + ) + +version :: Parser (a -> a) +version = Opt.infoOption versionInfo $ mconcat + [Opt.long "version", Opt.short 'v', Opt.help "Show version information.", Opt.hidden] + +opts :: ParserInfo Opts +opts = Opt.info + (argparse <**> Opt.helper <**> version) + ( Opt.fullDesc + <> Opt.progDesc "Fuzz different simulators and synthesisers." + <> Opt.header + "Verismith - A hardware simulator and synthesiser Verilog fuzzer." + ) -- cgit