Skip to content

Commit a4abbed

Browse files
committed
Merge branch 'SMT'
2 parents 1cc7568 + c7dfe11 commit a4abbed

File tree

11 files changed

+544
-17
lines changed

11 files changed

+544
-17
lines changed

gcl.cabal

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cabal-version: 1.12
44
--
55
-- see: https://github.com/sol/hpack
66
--
7-
-- hash: 9737fa07463018d2347f245a6caade6f8a713126c35325b6818059582c74098d
7+
-- hash: 15163c27cdc8efdcaae48404222894f59528b4fa537f86871c765fc874347b94
88

99
name: gcl
1010
version: 0.1.0.0
@@ -67,6 +67,7 @@ library
6767
Server.Monad
6868
Server.Pipeline
6969
Server.SrcLoc
70+
SMT.Prove
7071
Syntax.Abstract
7172
Syntax.Abstract.Instances.Json
7273
Syntax.Abstract.Instances.Located
@@ -113,6 +114,7 @@ library
113114
, prettyprinter
114115
, random
115116
, regex-applicative
117+
, sbv
116118
, srcloc
117119
, template-haskell
118120
, text
@@ -148,6 +150,7 @@ executable gcl
148150
, prettyprinter
149151
, random
150152
, regex-applicative
153+
, sbv
151154
, srcloc
152155
, template-haskell
153156
, text
@@ -213,6 +216,7 @@ test-suite gcl-test
213216
Server.Monad
214217
Server.Pipeline
215218
Server.SrcLoc
219+
SMT.Prove
216220
Syntax.Abstract
217221
Syntax.Abstract.Instances.Json
218222
Syntax.Abstract.Instances.Located
@@ -259,6 +263,7 @@ test-suite gcl-test
259263
, prettyprinter
260264
, random
261265
, regex-applicative
266+
, sbv
262267
, srcloc
263268
, tasty
264269
, tasty-golden

package.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies:
3636
- transformers
3737
- template-haskell
3838
- multistate
39+
- sbv
3940

4041
library:
4142
source-dirs: src

src/GCL/WP/Explanation.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ explainAssignment pre post vars exprs l = Explain
3535

3636
explainAfterLoop :: Pred -> [Expr] -> Loc -> Origin
3737
explainAfterLoop inv guards l = Explain
38-
{ originHeader = "After Loop"
38+
{ originHeader = "InvBase"
3939
, originExplanation = "The loop invariant"
4040
<> (codeE . render) inv
4141
<> "should remain true while all the guards"
@@ -48,7 +48,7 @@ explainAfterLoop inv guards l = Explain
4848

4949
explainTermination :: Pred -> [Expr] -> Expr -> Loc -> Origin
5050
explainTermination inv guards bnd l = Explain
51-
{ originHeader = "Loop Termination"
51+
{ originHeader = "TermBase"
5252
, originExplanation =
5353
"When the loop invariant"
5454
<> (codeE . render) inv

src/SMT/Prove.hs

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
module SMT.Prove (makeProvable, Error(..), provableIsOriginal) where
2+
-- Packages
3+
import Data.SBV
4+
import Data.Text.Internal ( Text )
5+
import qualified Data.Functor as F
6+
import qualified Data.Map as Map
7+
import qualified Data.Set as Set
8+
import Prelude hiding ( Ordering(..) )
9+
import Control.Monad.State.Lazy
10+
import Control.Monad.Except
11+
import Control.Monad.Reader
12+
13+
-- GCL
14+
import Pretty
15+
import GCL.Common
16+
import GCL.Predicate
17+
import GCL.Predicate.Util
18+
import Syntax.Abstract
19+
import Syntax.Common
20+
import Debug.Trace
21+
22+
makeProvable :: PO -> [Expr] -> Symbolic SBool
23+
makeProvable po props = let e1 = toExpr $ poPre po
24+
e2 = toExpr $ poPost po
25+
in makeImplication e1 e2 props
26+
27+
makeImplication :: Expr -> Expr -> [Expr] -> Symbolic SBool
28+
makeImplication e1 e2 props = do
29+
-- inspect the expressions (DEBUG)
30+
lift (print $ pretty e1)
31+
lift (print $ pretty e2)
32+
33+
-- capture free variables
34+
let propVarSet = Set.unions $ map fv props
35+
varList = Set.toList $ Set.map nameToText (fv e1 <> fv e2 <> propVarSet)
36+
len = length varList
37+
38+
-- create symbolic bindings
39+
environment <- generateEnvironment varList len
40+
41+
-- create constraints from global properties
42+
mapM_ (\prop -> constrain' $ runTranslator (toSBool prop) environment) props
43+
constrain' $ runTranslator (toSBool e1) environment
44+
45+
46+
-- return the evaluated expression to prove
47+
-- return $ runReader p1 environment
48+
-- .=> runReader p2 environment
49+
let maybeConclusion = runTranslator (toSBool e2) environment
50+
case maybeConclusion of
51+
(Left e) -> do
52+
lift $ print e
53+
return $ literal False
54+
(Right conclusion) -> do
55+
return conclusion
56+
57+
58+
where constrain' :: Either Error SBool -> SymbolicT IO ()
59+
constrain' (Left _) = return ()
60+
constrain' (Right b) = constrain b
61+
62+
type Name2Num = Map.Map Text SInteger
63+
type Name2Bol = Map.Map Text SBool
64+
type Name2Chr = Map.Map Text SChar
65+
66+
-- Error "function name" "first parameter"
67+
data Error =
68+
NotDefinedError Text |
69+
PONotFoundError |
70+
Z3NotFoundError
71+
deriving (Show)
72+
73+
74+
type Environment = (Name2Num, Name2Bol, Name2Chr)
75+
type Translator = ExceptT Error (Reader Environment)
76+
runTranslator :: Translator a -> Environment -> Either Error a
77+
runTranslator x = runReader (runExceptT x)
78+
79+
provableIsOriginal :: PO -> [Expr] -> Bool
80+
provableIsOriginal po props =
81+
let e1 = toExpr $ poPre po
82+
e2 = toExpr $ poPost po
83+
in foldr (\e b -> checkSBool e && b) (checkSBool e1 && checkSBool e2) props
84+
85+
generateEnvironment :: [Text] -> Int -> SymbolicT IO Environment
86+
generateEnvironment varList len = do
87+
intVars <- mkFreeVars len
88+
bolVars <- mkFreeVars len
89+
chrVars <- mkFreeVars len
90+
let intMap = Map.fromList $ zip varList intVars
91+
bolMap = Map.fromList $ zip varList bolVars
92+
chrMap = Map.fromList $ zip varList chrVars
93+
-- inspect the bindings (DEBUG)
94+
lift (traceIO $ show bolMap)
95+
lift (traceIO $ show intMap)
96+
return (bolMap, intMap, chrMap)
97+
98+
bindNum :: Name -> Translator SInteger
99+
bindNum varName = do
100+
let nameText = nameToText varName
101+
(numMap, _, _) <- ask
102+
return (numMap Map.! nameText)
103+
104+
bindBol :: Name -> Translator SBool
105+
bindBol varName = do
106+
let nameText = nameToText varName
107+
(_, bolMap, _) <- ask
108+
return (bolMap Map.! nameText)
109+
110+
-- takes an expression that evaluates to an integer
111+
-- and convert it to a function of the environment
112+
toSInteger :: Expr -> Translator SInteger
113+
toSInteger (Lit (Num i) _) = return $ literal (fromIntegral i :: Integer)
114+
toSInteger (Var varName _) = bindNum varName
115+
toSInteger (Const varName _) = bindNum varName
116+
toSInteger (App (Op (ArithOp op)) e _) =
117+
handleUnaryArithNumOp op e
118+
toSInteger (App (App (Op (ArithOp op)) e1 _) e2 _) =
119+
handleBinaryArithNumOp op e1 e2
120+
toSInteger e = throwError (NotDefinedError $ toText e <> toText "toSInteger")
121+
122+
-- takes an expression that evaluates to a boolean value
123+
-- and convert it to a function of the environment
124+
toSBool :: Expr -> Translator SBool
125+
toSBool (Lit (Bol b) _) = return $ literal b
126+
toSBool (Var varName _) = bindBol varName
127+
toSBool (Const varName _) = bindBol varName
128+
toSBool (App (App (Op (ChainOp (EQProp l))) e1 _) e2 _) =
129+
handleChainOp (EQProp l) e1 e2
130+
toSBool (App (App (Op (ChainOp (EQPropU l))) e1 _) e2 _) =
131+
handleChainOp (EQProp l) e1 e2
132+
toSBool (App (App (Op (ChainOp op)) e1 _) e2 _) =
133+
handleChainOp op e1 e2
134+
toSBool (App (Op (ArithOp op)) e _) =
135+
handleUnaryArithBolOp op e
136+
toSBool (App (App (Op (ArithOp op)) e1 _) e2 _) =
137+
handleBinaryArithBolOp op e1 e2
138+
toSBool _ = throwError (NotDefinedError $ toText "toSBool")
139+
140+
handleChainOp :: ChainOp -> Expr -> Expr -> Translator SBool
141+
handleChainOp (EQProp _) e1 e2 = do
142+
v1 <- toSBool e1
143+
v2 <- toSBool e2
144+
case e1 of
145+
App (App (Op (ChainOp _)) _ _) e12 _ -> do
146+
b1 <- toSBool e1
147+
v12 <- toSBool e12
148+
let b2 = v12 .== v2
149+
return $ b1 .&& b2
150+
_ -> return $ v1 .== v2
151+
152+
handleChainOp op e1 e2 = do
153+
v2 <- toSInteger e2
154+
case e1 of
155+
App (App (Op (ChainOp op')) _ _) e12 _ -> do
156+
b1 <- toSBool e1
157+
v12 <- toSInteger e12
158+
b2 <- applyChainOp op' v12 v2
159+
return $ b1 .&& b2
160+
_ -> do
161+
v1 <- toSInteger e1
162+
applyChainOp op v1 v2
163+
where applyChainOp :: ChainOp -> SInteger -> SInteger -> Translator SBool
164+
applyChainOp (EQ _) i1 i2 = return $ i1 .== i2
165+
applyChainOp (NEQ _) i1 i2 = return $ i1 ./= i2
166+
applyChainOp (NEQU _) i1 i2 = return $ i1 ./= i2
167+
applyChainOp (LTE _) i1 i2 = return $ i1 .<= i2
168+
applyChainOp (LTEU _) i1 i2 = return $ i1 .<= i2
169+
applyChainOp (GTE _) i1 i2 = return $ i1 .>= i2
170+
applyChainOp (GTEU _) i1 i2 = return $ i1 .>= i2
171+
applyChainOp (LT _) i1 i2 = return $ i1 .< i2
172+
applyChainOp (GT _) i1 i2 = return $ i1 .> i2
173+
applyChainOp (EQProp _) _ _ = throwError (NotDefinedError $ toText "EQProp with int")
174+
applyChainOp (EQPropU _) _ _ = throwError (NotDefinedError $ toText "EQProp with int")
175+
176+
handleUnaryArithNumOp :: ArithOp -> Expr -> Translator SInteger
177+
handleUnaryArithNumOp (NegNum _) e = toSInteger e F.<&> negate
178+
handleUnaryArithNumOp _ _ = throwError (NotDefinedError $ toText "handleUnaryArithNumOp undefined")
179+
180+
handleUnaryArithBolOp :: ArithOp -> Expr -> Translator SBool
181+
handleUnaryArithBolOp (Neg _) e = toSBool e F.<&> sNot
182+
handleUnaryArithBolOp (NegU _) e = toSBool e F.<&> sNot
183+
handleUnaryArithBolOp _ _ = throwError (NotDefinedError $ toText "handleUnaryArithBolOp")
184+
185+
handleBinaryArithNumOp :: ArithOp -> Expr -> Expr -> Translator SInteger
186+
handleBinaryArithNumOp (Add _) e1 e2 = applyBinaryArithNumOp (+) e1 e2
187+
handleBinaryArithNumOp (Sub _) e1 e2 = applyBinaryArithNumOp (-) e1 e2
188+
handleBinaryArithNumOp (Mul _) e1 e2 = applyBinaryArithNumOp (*) e1 e2
189+
handleBinaryArithNumOp (Div _) e1 e2 = applyBinaryArithNumOp sDiv e1 e2
190+
handleBinaryArithNumOp (Mod _) e1 e2 = applyBinaryArithNumOp sMod e1 e2
191+
handleBinaryArithNumOp (Max _) e1 e2 = applyBinaryArithNumOp smax e1 e2
192+
handleBinaryArithNumOp (Min _) e1 e2 = applyBinaryArithNumOp smin e1 e2
193+
--handleBinaryArithNumOp (Exp _) e1 e2 = applyBinaryArithNumOp (.^) e1 e2
194+
handleBinaryArithNumOp _ _ _ = throwError (NotDefinedError $ toText "handleBinaryArithNumOp")
195+
196+
handleBinaryArithBolOp :: ArithOp -> Expr -> Expr -> Translator SBool
197+
handleBinaryArithBolOp (Implies _) e1 e2 = applyBinaryArithBolOp (.=>) e1 e2
198+
handleBinaryArithBolOp (ImpliesU _) e1 e2 = applyBinaryArithBolOp (.=>) e1 e2
199+
handleBinaryArithBolOp (Conj _) e1 e2 = applyBinaryArithBolOp (.&&) e1 e2
200+
handleBinaryArithBolOp (ConjU _) e1 e2 = applyBinaryArithBolOp (.&&) e1 e2
201+
handleBinaryArithBolOp (Disj _) e1 e2 = applyBinaryArithBolOp (.||) e1 e2
202+
handleBinaryArithBolOp (DisjU _) e1 e2 = applyBinaryArithBolOp (.||) e1 e2
203+
handleBinaryArithBolOp _ _ _ = throwError (NotDefinedError $ toText "handleBinaryArithBolOp")
204+
205+
applyBinaryArithNumOp :: (SInteger -> SInteger -> SInteger) -> Expr -> Expr -> Translator SInteger
206+
applyBinaryArithNumOp op e1 e2 = do
207+
f1 <- toSInteger e1
208+
f2 <- toSInteger e2
209+
return $ f1 `op` f2
210+
211+
applyBinaryArithBolOp :: (SBool -> SBool -> SBool) -> Expr -> Expr -> Translator SBool
212+
applyBinaryArithBolOp op e1 e2 = do
213+
f1 <- toSBool e1
214+
f2 <- toSBool e2
215+
return $ f1 `op` f2
216+
217+
218+
-- takes an expression that evaluates to an integer
219+
-- and convert it to a function of the environment
220+
checkSInteger :: Expr -> Bool
221+
checkSInteger (Lit (Num _) _) = True
222+
checkSInteger (Var _ _) = True
223+
checkSInteger (Const _ _) = True
224+
checkSInteger (App (Op (ArithOp op)) e _) =
225+
checkHandleUnaryArithNumOp op e
226+
checkSInteger (App (App (Op (ArithOp op)) e1 _) e2 _) =
227+
checkHandleBinaryArithNumOp op e1 e2
228+
checkSInteger _ = False
229+
230+
-- takes an expression that evaluates to a boolean value
231+
-- and convert it to a function of the environment
232+
checkSBool :: Expr -> Bool
233+
checkSBool (Lit (Bol _) _) = True
234+
checkSBool (Var _ _) = True
235+
checkSBool (Const _ _) = True
236+
checkSBool (App (App (Op (ChainOp (EQProp l))) e1 _) e2 _) =
237+
checkHandleChainOp (EQProp l) e1 e2
238+
checkSBool (App (App (Op (ChainOp (EQPropU l))) e1 _) e2 _) =
239+
checkHandleChainOp (EQProp l) e1 e2
240+
checkSBool (App (App (Op (ChainOp op)) e1 _) e2 _) =
241+
checkHandleChainOp op e1 e2
242+
checkSBool (App (Op (ArithOp op)) e _) =
243+
checkHandleUnaryArithBolOp op e
244+
checkSBool (App (App (Op (ArithOp op)) e1 _) e2 _) =
245+
checkHandleBinaryArithBolOp op e1 e2
246+
checkSBool _ = False
247+
248+
checkHandleChainOp :: ChainOp -> Expr -> Expr -> Bool
249+
checkHandleChainOp (EQProp _) e1 e2 =
250+
case e1 of
251+
App (App (Op (ChainOp _)) _ _) e12 _ ->
252+
checkSBool e12 && checkSBool e2
253+
_ -> checkSBool e1 && checkSBool e2
254+
255+
checkHandleChainOp op e1 e2 = do
256+
let b2 = checkSInteger e2
257+
case e1 of
258+
App (App (Op (ChainOp op')) _ _) e12 _ ->
259+
let b1 = checkSBool e1
260+
b12 = checkSInteger e12
261+
b = checkApplyChainOp op'
262+
in b1 && b2 && b12 && b
263+
_ ->
264+
let b1 = checkSInteger e1
265+
in checkApplyChainOp op && b1 && b2
266+
where checkApplyChainOp :: ChainOp -> Bool
267+
checkApplyChainOp (EQ _) = True
268+
checkApplyChainOp (NEQ _) = True
269+
checkApplyChainOp (NEQU _) = True
270+
checkApplyChainOp (LTE _) = True
271+
checkApplyChainOp (LTEU _) = True
272+
checkApplyChainOp (GTE _) = True
273+
checkApplyChainOp (GTEU _) = True
274+
checkApplyChainOp (LT _) = True
275+
checkApplyChainOp (GT _) = True
276+
checkApplyChainOp (EQProp _) = False
277+
checkApplyChainOp (EQPropU _) = False
278+
279+
checkHandleUnaryArithNumOp :: ArithOp -> Expr -> Bool
280+
checkHandleUnaryArithNumOp (NegNum _) _ = True
281+
checkHandleUnaryArithNumOp _ _ = False
282+
283+
checkHandleUnaryArithBolOp :: ArithOp -> Expr -> Bool
284+
checkHandleUnaryArithBolOp (Neg _) e = checkSBool e
285+
checkHandleUnaryArithBolOp (NegU _) e = checkSBool e
286+
checkHandleUnaryArithBolOp _ _ = False
287+
288+
checkHandleBinaryArithNumOp :: ArithOp -> Expr -> Expr -> Bool
289+
checkHandleBinaryArithNumOp (Add _) e1 e2 = checkApplyBinaryArithNumOp e1 e2
290+
checkHandleBinaryArithNumOp (Sub _) e1 e2 = checkApplyBinaryArithNumOp e1 e2
291+
checkHandleBinaryArithNumOp (Mul _) e1 e2 = checkApplyBinaryArithNumOp e1 e2
292+
checkHandleBinaryArithNumOp (Div _) e1 e2 = checkApplyBinaryArithNumOp e1 e2
293+
checkHandleBinaryArithNumOp (Mod _) e1 e2 = checkApplyBinaryArithNumOp e1 e2
294+
checkHandleBinaryArithNumOp (Max _) e1 e2 = checkApplyBinaryArithNumOp e1 e2
295+
checkHandleBinaryArithNumOp (Min _) e1 e2 = checkApplyBinaryArithNumOp e1 e2
296+
--checkHandleBinaryArithNumOp (Exp _) e1 e2 = applyBinaryArithNumOp (.^) e1 e2
297+
checkHandleBinaryArithNumOp _ _ _ = False
298+
299+
checkHandleBinaryArithBolOp :: ArithOp -> Expr -> Expr -> Bool
300+
checkHandleBinaryArithBolOp (Implies _) e1 e2 = checkApplyBinaryArithBolOp e1 e2
301+
checkHandleBinaryArithBolOp (ImpliesU _) e1 e2 = checkApplyBinaryArithBolOp e1 e2
302+
checkHandleBinaryArithBolOp (Conj _) e1 e2 = checkApplyBinaryArithBolOp e1 e2
303+
checkHandleBinaryArithBolOp (ConjU _) e1 e2 = checkApplyBinaryArithBolOp e1 e2
304+
checkHandleBinaryArithBolOp (Disj _) e1 e2 = checkApplyBinaryArithBolOp e1 e2
305+
checkHandleBinaryArithBolOp (DisjU _) e1 e2 = checkApplyBinaryArithBolOp e1 e2
306+
checkHandleBinaryArithBolOp _ _ _ = False
307+
308+
checkApplyBinaryArithNumOp :: Expr -> Expr -> Bool
309+
checkApplyBinaryArithNumOp e1 e2 = checkSInteger e1 && checkSInteger e2
310+
311+
checkApplyBinaryArithBolOp :: Expr -> Expr -> Bool
312+
checkApplyBinaryArithBolOp e1 e2 = checkSBool e1 && checkSBool e2

0 commit comments

Comments
 (0)