1-- SPDX-FileCopyrightText: 2025-2026 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only45module Main (main) where67import Data.Binary (encodeFile)8import Data.KTest (KTest (KTest), KTestObj, fromAssign)9import Data.String (fromString)10import Language.QBE.Backend.Store qualified as ST11import Language.QBE.Backend.Tracer qualified as T12import Language.QBE.CmdLine qualified as CMD13import Language.QBE.Simulator.Concolic.State (mkEnv)14import Language.QBE.Simulator.Explorer (defSolver, explore, logSolver, newEngine)15import Language.QBE.Types qualified as QBE16import Options.Applicative qualified as OPT17import System.Directory (createDirectoryIfMissing)18import System.FilePath (addExtension, (</>))19import System.IO (IOMode (WriteMode), withFile)20import Text.Printf (printf)2122data Opts = Opts23 { optLog :: Maybe FilePath,24 optSeed :: Maybe Int,25 optTests :: Maybe FilePath,26 optBase :: CMD.BasicArgs27 }2829optsParser :: OPT.Parser Opts30optsParser =31 Opts32 <$> OPT.optional33 ( OPT.strOption34 ( OPT.long "dump-smt2"35 <> OPT.short 'd'36 <> OPT.metavar "FILE"37 <> OPT.help "Output queries as an SMT-LIB file"38 )39 )40 <*> OPT.optional41 ( OPT.option42 OPT.auto43 ( OPT.long "random-seed"44 <> OPT.short 'r'45 <> OPT.help "Initial seed to for the random number generator"46 )47 )48 <*> OPT.optional49 ( OPT.strOption50 ( OPT.long "write-tests"51 <> OPT.short 't'52 <> OPT.metavar "DIR"53 <> OPT.help "Write .ktest files to the given directory"54 )55 )56 <*> CMD.basicArgs5758------------------------------------------------------------------------5960exploreFile :: Opts -> IO [(ST.Assign, T.ExecTrace)]61exploreFile opts@Opts {optBase = base} = do62 (prog, func) <- CMD.parseEntryFile $ CMD.optQBEFile base6364 env <- mkEnv prog (CMD.optMemStart base) (CMD.optMemSize base) (optSeed opts)65 case optLog opts of66 Just fn -> withFile fn WriteMode (exploreWithHandle env func)67 Nothing -> do68 engine <- newEngine <$> defSolver69 explore engine env func params70 where71 params :: [(String, QBE.ExtType)]72 params = []7374 exploreWithHandle env func handle = do75 engine <- newEngine <$> logSolver handle76 explore engine env func params7778writeKTests :: FilePath -> FilePath -> [[KTestObj]] -> IO ()79writeKTests directory fileArg objs = do80 createDirectoryIfMissing True directory81 mapM_ (uncurry writeKTest) $ zip [1 ..] (map mkTestCase objs)82 where83 mkTestCase :: [KTestObj] -> KTest84 mkTestCase = KTest [fromString fileArg]8586 writeKTest :: Int -> KTest -> IO ()87 writeKTest n ktest = do88 flip encodeFile ktest $89 addExtension90 (directory </> ("test" ++ printf "%06d" n))91 ".ktest"9293main :: IO ()94main = do95 args <- OPT.execParser cmd96 paths <- exploreFile args9798 putStrLn $ "\n---\nAmount of paths: " ++ show (length paths)99 case optTests args of100 Just dir ->101 writeKTests dir (CMD.optQBEFile $ optBase args) $102 map (fromAssign . fst) paths103 Nothing -> pure ()104 where105 cmd :: OPT.ParserInfo Opts106 cmd =107 OPT.info108 (optsParser OPT.<**> OPT.helper)109 ( OPT.fullDesc110 <> OPT.progDesc "Symbolic execution of programs in the QBE intermediate language"111 )