1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: MIT AND GPL-3.0-only45module Language.QBE.Simulator.Symbolic.Unwind (unwind) where67import Control.Monad (forM_)8import Control.Monad.State (State, gets, modify, runState)9import Data.Functor ((<&>))10import SimpleSMT qualified as SMT1112data UnwindEnv13 = UnwindEnv14 { assertStack :: [[SMT.SExpr]],15 exprs :: [SMT.SExpr],16 queries :: [[SMT.SExpr]]17 }18 deriving (Show, Eq)1920mkUnwindEnv :: UnwindEnv21mkUnwindEnv = UnwindEnv [] [] []2223------------------------------------------------------------------------2425newAssertLevel :: State UnwindEnv ()26newAssertLevel =27 modify (\s -> s {assertStack = [] : assertStack s})2829popAssertLevel :: State UnwindEnv ()30popAssertLevel = modify go31 where32 go s@UnwindEnv {assertStack = []} = s33 go s@UnwindEnv {assertStack = _ : xs} = s {assertStack = xs}3435addAssertion :: [SMT.SExpr] -> State UnwindEnv ()36addAssertion assertions = do37 stk <- gets assertStack38 let newStk = case stk of39 (x : xs) -> (x ++ assertions) : xs40 [] -> [assertions]41 modify (\s -> s {assertStack = newStk})4243addExpr :: SMT.SExpr -> State UnwindEnv ()44addExpr expr =45 modify (\s -> s {exprs = exprs s ++ [expr]})4647getAsserts :: State UnwindEnv [SMT.SExpr]48getAsserts = gets (concat . assertStack)4950completeQuery :: State UnwindEnv ()51completeQuery =52 modify53 ( \s ->54 s55 { queries = queries s ++ [exprs s],56 exprs = []57 }58 )5960transExpr :: SMT.SExpr -> State UnwindEnv ()61transExpr (SMT.List [SMT.Atom "push", SMT.Atom arg]) = do62 let num = (read arg :: Integer)63 forM_ [1 .. num] (const newAssertLevel)64transExpr (SMT.List [SMT.Atom "pop", SMT.Atom arg]) = do65 let num = (read arg :: Integer)66 forM_ [1 .. num] (const popAssertLevel)67transExpr (SMT.List ((SMT.Atom "assert") : xs)) =68 addAssertion xs69transExpr (SMT.List [SMT.Atom "check-sat"]) = do70 asserts <- getAsserts71 addExpr $ SMT.List [SMT.Atom "check-sat-assuming", SMT.List asserts]72 completeQuery73transExpr (SMT.List ((SMT.Atom "get-value") : _)) = pure ()74transExpr expr = addExpr expr7576transform :: [SMT.SExpr] -> State UnwindEnv ()77transform sexprs = forM_ sexprs transExpr7879------------------------------------------------------------------------8081readSExprs :: String -> [SMT.SExpr]82readSExprs str = go (SMT.readSExpr str)83 where84 go :: Maybe (SMT.SExpr, String) -> [SMT.SExpr]85 go Nothing = []86 go (Just (acc, rest)) = acc : go (SMT.readSExpr rest)8788getQueries :: [SMT.SExpr] -> [[SMT.SExpr]]89getQueries exprs = queries (snd $ runTransform exprs)90 where91 runTransform e = runState (transform e) mkUnwindEnv9293unwind :: FilePath -> IO String94unwind origFp = do95 exprs <- readFile origFp <&> readSExprs96 let queries = getQueries exprs97 pure $ serialize (concat queries)98 where99 serialize :: [SMT.SExpr] -> String100 serialize = unlines . map (`SMT.showsSExpr` "")