ntha-0.1.0: src/Refined.hs
module Refined where
import Ast
import Type
import TypeScope
import Z3.Class
import Z3.Logic
import Z3.Context
import Z3.Assertion
import Z3.Monad
import Prelude hiding (lookup)
import Control.Monad (mapM_)
import Control.Monad.IO.Class (liftIO)
genPred :: Term -> Z3Pred
genPred term = case term of
TmLT t1 t2 -> PAssert $ Less t1 t2
TmGT t1 t2 -> PAssert $ Greater t1 t2
TmLE t1 t2 -> PAssert $ LessE t1 t2
TmGE t1 t2 -> PAssert $ GreaterE t1 t2
TmEqual t1 t2 -> PAssert $ Equal t1 t2
TmAnd t1 t2 -> PConj (genPred t1) (genPred t2)
TmOr t1 t2 -> PDisj (genPred t1) (genPred t2)
TmNot t -> PNeg (genPred t)
_ -> error $ "not support term: " ++ show term
replaceRtnTerm :: String -> Term -> Term -> Term
replaceRtnTerm rtnName rtnTerm predTerm = case predTerm of
TmVar n -> if n == rtnName then rtnTerm else predTerm
TmNum _ -> predTerm
TmLT t1 t2 -> TmLT (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmGT t1 t2 -> TmGT (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmLE t1 t2 -> TmLE (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmGE t1 t2 -> TmGE (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmSub t1 t2 -> TmSub (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmAdd t1 t2 -> TmAdd (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmMul t1 t2 -> TmMul (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmDiv t1 t2 -> TmDiv (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmEqual t1 t2 -> TmEqual (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmAnd t1 t2 -> TmAnd (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmOr t1 t2 -> TmOr (replaceRtnTerm' t1) (replaceRtnTerm' t2)
TmNot t -> TmNot (replaceRtnTerm' t)
TmIf t1 t2 t3 -> TmIf (replaceRtnTerm' t1) (replaceRtnTerm' t2) (replaceRtnTerm' t3)
where replaceRtnTerm' = replaceRtnTerm rtnName rtnTerm
genRtnPred :: String -> Term -> Term -> Z3Pred
-- use neg to find counterexamples
genRtnPred rtnName rtnTerm = PNeg . genPred . (replaceRtnTerm rtnName rtnTerm)
convertProg' :: Expr -> Term
convertProg' expr = case expr of
ENum n -> TmNum n
EVar name -> TmVar name
EApp fn arg -> case fn of
EApp (EVar op) arg' -> opConstruct argTerm' argTerm
where argTerm' = convertProg' arg'
argTerm = convertProg' arg
opConstruct = case op of
"+" -> TmAdd
"-" -> TmSub
"*" -> TmMul
"/" -> TmDiv
"<" -> TmLT
">" -> TmGT
"≤" -> TmLE
"≥" -> TmGE
"=" -> TmEqual
"∧" -> TmAnd
"∨" -> TmOr
_ -> error $ "not support op: " ++ op
EVar op -> case op of
"¬" -> let argTerm = convertProg' arg
in TmNot argTerm
_ -> error $ "not support op: " ++ op
_ -> error $ "not support fn: " ++ show fn
EIf cond (thenInstruction:[]) (elseInstruction:[]) -> TmIf condTerm thenTerm elseTerm
where condTerm = convertProg' cond
thenTerm = convertProg' thenInstruction
elseTerm = convertProg' elseInstruction
_ -> error $ "not support expr: " ++ show expr
convertProg :: Expr -> TypeScope -> IO Z3Pred
convertProg expr scope = case expr of
-- only support exists and exists2 for now
EDestructLetBinding main args (instruction:[]) -> do
let name = case main of
IdPattern n -> n ++ "-sig"
_ -> ""
let typeSig = lookup name scope
let argNames = map (\pat -> case pat of
IdPattern n -> n
_ -> show pat)
args
case typeSig of
Just (TSig ta) -> do
let terms = extractTerm ta
let predNames = getPredNames ta
case predNames of
-- (¬ ⊥) always satisfied
[] -> return PFalse
_ -> case (argNames, terms) of
([n], [rtnTerm']) -> return $ PExists n RTInt $ genRtnPred' rtnTerm'
([n1, n2], [rtnTerm']) -> return $ PExists2 n1 n2 RTInt $ genRtnPred' rtnTerm'
([n], [argTerm, rtnTerm']) -> return $ PExists n RTInt $ PConj (genPred argTerm) $ genRtnPred' rtnTerm'
([n1, n2], [argTerm1, argTerm2, rtnTerm']) -> return $ PExists2 n1 n2 RTInt $ PConj (PConj (genPred argTerm1) $ genPred argTerm2) $ genRtnPred' rtnTerm'
_ -> error $ "not support args: " ++ show argNames ++ " and terms: " ++ show terms
where rtnName = last predNames
rtnTerm = convertProg' instruction
genRtnPred' :: Term -> Z3Pred
genRtnPred' = genRtnPred rtnName rtnTerm
-- (¬ ⊥) always satisfied
_ -> return PFalse
EProgram (instruction:_) -> convertProg instruction scope
_ -> error $ "not support expr: " ++ show expr
checkPre :: Z3Pred -> Z3SMT () (Result, Maybe Model)
checkPre pre = local $ do
ast <- encode pre
local (assert ast >> getModel)
checker :: Expr -> TypeScope -> IO ()
checker expr scope = case expr of
EDestructLetBinding _ _ _ -> do
progPred <- convertProg expr scope
-- trade off
let adts = [("", [("", [("", RTInt)])])]
ret <- runSMT adts () $ do
(r, _mm) <- checkPre progPred
case r of
Unsat -> do
core <- getUnsatCore
liftIO $ sequence_ (map print core)
return r
other -> return other
if ret == Right Unsat
then return ()
else error "refined type check failed"
EProgram instructions -> mapM_ (\instr -> checker instr scope) instructions
_ -> return ()