From 390feae1e9261a5a37690a5b1480ae59e9988247 Mon Sep 17 00:00:00 2001 From: Duncan Ogilvie <mr.exodia.tpodt@gmail.com> Date: Sun, 29 Oct 2017 14:58:05 +0100 Subject: [PATCH] refactor LogicIR.Expr to be smaller and to properly support null arrays --- src/LogicIR/Backend/Pretty.hs | 66 +++++++++--------- src/LogicIR/Backend/Z3.hs | 124 +++++++++++++++++----------------- src/LogicIR/Expr.hs | 63 ++++++++--------- src/LogicIR/Fold.hs | 40 +++++------ src/LogicIR/Frontend/Java.hs | 52 +++++++------- 5 files changed, 164 insertions(+), 181 deletions(-) diff --git a/src/LogicIR/Backend/Pretty.hs b/src/LogicIR/Backend/Pretty.hs index cf793e9..3f82055 100644 --- a/src/LogicIR/Backend/Pretty.hs +++ b/src/LogicIR/Backend/Pretty.hs @@ -15,49 +15,45 @@ prettyType (TPrim PInt32) = "int" prettyType (TPrim PBool) = "bool" prettyType (TArray t) = prettyType t ++ "[]" -prettyCOp :: COp -> String -prettyCOp CEqual = "==" -prettyCOp CNEqual = "!=" -prettyCOp CLess = "<" -prettyCOp CGreater = ">" -prettyCOp CLeq = "<=" -prettyCOp CGeq = ">=" - prettyLBinop :: LBinop -> String +prettyLBinop NAdd = "+" +prettyLBinop NSub = "-" +prettyLBinop NMul = "*" +prettyLBinop NDiv = "/" +prettyLBinop NRem = "%" +prettyLBinop NShl = ">>" +prettyLBinop NShr = "<<" +prettyLBinop NAnd = "&" +prettyLBinop NOr = "|" +prettyLBinop NXor = "^" prettyLBinop LAnd = "&&" prettyLBinop LOr = "||" prettyLBinop LImpl = "->" - -prettyNBinop :: NBinop -> String -prettyNBinop NAdd = "+" -prettyNBinop NSub = "-" -prettyNBinop NMul = "*" -prettyNBinop NDiv = "/" -prettyNBinop NRem = "%" -prettyNBinop NShl = ">>" -prettyNBinop NShr = "<<" -prettyNBinop NAnd = "&" -prettyNBinop NOr = "|" -prettyNBinop NXor = "^" - -prettyNUnop :: NUnop -> String +prettyLBinop CEqual = "==" +prettyLBinop CNEqual = "!=" +prettyLBinop CLess = "<" +prettyLBinop CGreater = ">" +prettyLBinop CLeq = "<=" +prettyLBinop CGeq = ">=" + +prettyNUnop :: LUnop -> String prettyNUnop NNeg = "-" prettyNUnop NNot = "~" +prettyNUnop LNot = "!" prettyVar :: Var -> String prettyVar (Var t n) = prettyType t ++ ":" ++ n prettyLExprAlgebra :: LExprAlgebra String -prettyLExprAlgebra = (flConst, prettyVar, flNot, flBinop, flComp, flQuant, flArray, flNil, fnConst, fnUnop, fnBinop, fnIf, fnLen) where - flConst b = if b then "true" else "false" - flNot a = '!' : a - flBinop a o b = a ++ " " ++ prettyLBinop o ++ " " ++ b - flComp a o b = a ++ " " ++ prettyCOp o ++ " " ++ b - flQuant o v a = '(' : show o ++ " " ++ prettyVar v ++ " . " ++ a ++ ")" - flArray v a = prettyVar v ++ "[" ++ a ++ "]" - flNil = "nil" - fnConst n = show n - fnUnop o a = prettyNUnop o ++ a - fnBinop a o b = a ++ " " ++ prettyNBinop o ++ " " ++ b - fnIf c a b = "(" ++ c ++ ") ? (" ++ a ++ ") : (" ++ b ++ ")" - fnLen v = "len(" ++ prettyVar v ++ ")" \ No newline at end of file +prettyLExprAlgebra = (fConst, prettyVar, fUnop, fBinop, fIf, fQuant, fArray, fIsnull, fLen) where + fConst c = case c of + CBool b -> if b then "true" else "false" + CInt n -> show n + CNil -> "nil" + fUnop o a = prettyNUnop o ++ a + fBinop a o b = a ++ " " ++ prettyLBinop o ++ " " ++ b + fIf c a b = "(" ++ c ++ ") ? (" ++ a ++ ") : (" ++ b ++ ")" + fQuant o v d a = '(' : show o ++ " " ++ prettyVar v ++ ": " ++ d ++ ": " ++ a ++ ")" + fArray v a = prettyVar v ++ "[" ++ a ++ "]" + fIsnull v = prettyVar v ++ " == null" + fLen v = "len(" ++ prettyVar v ++ ")" \ No newline at end of file diff --git a/src/LogicIR/Backend/Z3.hs b/src/LogicIR/Backend/Z3.hs index e7b76e2..2b91407 100644 --- a/src/LogicIR/Backend/Z3.hs +++ b/src/LogicIR/Backend/Z3.hs @@ -10,68 +10,70 @@ lExprToZ3Ast = foldLExpr lExprToZ3AstAlgebra -- TODO: support more types lExprToZ3AstAlgebra :: LExprAlgebra (Z3 AST) -lExprToZ3AstAlgebra = (flConst, flVar, flNot, flBinop, flComp, flQuant, flArray, flNil, fnConst, fnUnop, fnBinop, fnIf, fnLen) where - flConst b = mkBool b - flVar (Var t n) = do symbol <- mkStringSymbol n - case t of - TPrim PInt32 -> mkBvVar symbol 32 - TPrim PBool -> mkBoolVar symbol - TArray (TPrim PInt32) -> do intSort <- mkBvSort 32 - arraySort <- mkArraySort intSort intSort - mkVar symbol arraySort - TArray (TPrim PBool) -> do intSort <- mkBvSort 32 - arraySort <- mkBoolSort >>= mkArraySort intSort +lExprToZ3AstAlgebra = (fConst, fVar, fUnop, fBinop, fIf, fQuant, fArray, fIsnull, fLen) where + fConst c = case c of + CBool b -> mkBool b + CInt n -> mkBitvector 32 (fromIntegral n) + CNil -> do intSort <- mkBvSort 32 + zero <- mkBitvector 32 0x1337 -- (isNull, data, length) TODO: support proper null types + mkConstArray intSort zero + fVar (Var t n) = do symbol <- mkStringSymbol n + case t of + TPrim PInt32 -> mkBvVar symbol 32 + TPrim PBool -> mkBoolVar symbol + TArray (TPrim PInt32) -> do intSort <- mkBvSort 32 + arraySort <- mkArraySort intSort intSort mkVar symbol arraySort - _ -> error $ "unsupported type: " ++ show n - flNot a = a >>= mkNot - flBinop a' o b' = do a <- a' - b <- b' - case o of - LAnd -> mkAnd [a, b] - LOr -> mkOr [a, b] - LImpl -> mkImplies a b - flComp a' o b' = do a <- a' + TArray (TPrim PBool) -> do intSort <- mkBvSort 32 + arraySort <- mkBoolSort >>= mkArraySort intSort + mkVar symbol arraySort + _ -> error $ "unsupported type: " ++ show n + fUnop o a' = do a <- a' + case o of + NNeg -> mkBvneg a + NNot -> mkBvnot a + LNot -> mkNot a + fBinop a' o b' = do a <- a' b <- b' case o of - CEqual -> mkEq a b - CNEqual -> mkEq a b >>= mkNot - CLess -> mkBvslt a b - CGreater -> mkBvsgt a b - CLeq -> mkBvsle a b - CGeq -> mkBvsge a b - flQuant o v@(Var t n) a' = do a <- a' - case t of - TPrim PInt32 -> do vApp <- flVar v >>= toApp - case o of - QAll -> mkForallConst [] [vApp] a - QAny -> mkExistsConst [] [vApp] a - _ -> error $ "unsupported quantifier domain type: " ++ show (o, v) - flArray v a' = do v <- flVar v + NAdd -> mkBvadd a b + NSub -> mkBvsub a b + NMul -> mkBvmul a b + NDiv -> mkBvsdiv a b -- NOTE: signed division + NRem -> mkBvsrem a b -- TODO: check if the correct remainder is taken + NShl -> mkBvshl a b + NShr -> mkBvashr a b -- NOTE: signed shift right will keep the sign + NAnd -> mkBvand a b + NOr -> mkBvor a b + NXor -> mkBvxor a b + + LAnd -> mkAnd [a, b] + LOr -> mkOr [a, b] + LImpl -> mkImplies a b + + CEqual -> mkEq a b + CNEqual -> mkEq a b >>= mkNot + CLess -> mkBvslt a b + CGreater -> mkBvsgt a b + CLeq -> mkBvsle a b + CGeq -> mkBvsge a b + fIf c' a' b' = do c <- c' a <- a' - mkSelect v a - flNil = do intSort <- mkBvSort 32 - zero <- mkBitvector 32 0x1337 -- (isNull, data, length) TODO: support proper null types - mkConstArray intSort zero - fnConst n = mkBitvector 32 (fromIntegral n) - fnUnop o a' = do a <- a' - case o of - NNeg -> mkBvneg a - NNot -> mkBvnot a - fnBinop a' o b' = do a <- a' - b <- b' - case o of - NAdd -> mkBvadd a b - NSub -> mkBvsub a b - NMul -> mkBvmul a b - NDiv -> mkBvsdiv a b -- NOTE: signed division - NRem -> mkBvsrem a b -- TODO: check if the correct remainder is taken - NShl -> mkBvshl a b - NShr -> mkBvashr a b -- NOTE: signed shift right will keep the sign - NAnd -> mkBvand a b - NOr -> mkBvor a b - NXor -> mkBvxor a b - fnIf c' a' b' = do c <- c' - a <- a' - b <- b' - mkIte c a b - fnLen (Var (TArray (TPrim _)) n) = mkStringSymbol (n ++ ".length") >>= flip mkBvVar 32 -- TODO: support proper array lengths + b <- b' + mkIte c a b + + fQuant o v@(Var t n) d' a' = do a <- a' + d <- d' + case t of + TPrim PInt32 -> do vApp <- fVar v >>= toApp + case o of + QAll -> do e <- mkImplies d a + mkForallConst [] [vApp] e + QAny -> do e <- mkAnd [d, a] + mkExistsConst [] [vApp] e + _ -> error $ "unsupported quantifier domain type: " ++ show (o, v) + fArray v a' = do v <- fVar v + a <- a' + mkSelect v a + fIsnull v = undefined + fLen (Var (TArray (TPrim _)) n) = mkStringSymbol (n ++ ".length") >>= flip mkBvVar 32 -- TODO: support proper array lengths diff --git a/src/LogicIR/Expr.hs b/src/LogicIR/Expr.hs index 0d0f589..9387371 100644 --- a/src/LogicIR/Expr.hs +++ b/src/LogicIR/Expr.hs @@ -1,6 +1,7 @@ module LogicIR.Expr where -- Based on previous work: https://github.com/mrexodia/wp/blob/master/Wp.hs +-- Reference: https://en.wikipedia.org/wiki/First-order_logic#Logical_symbols -- The primitive types are bool and int32. data Primitive = PBool @@ -16,13 +17,14 @@ data Type = TPrim Primitive data Var = Var Type String deriving (Show, Eq, Read) --- Numeral unary operators -data NUnop = NNeg -- -n (negation) +-- Unary operators +data LUnop = NNeg -- -n (negation) | NNot -- ~n (binary not) + | LNot -- !n (logical not) deriving (Show, Eq, Read) --- Numeral binary operators -data NBinop = NAdd -- a + b +-- Binary operators +data LBinop = NAdd -- a + b | NSub -- a - b | NMul -- a * b | NDiv -- a / b @@ -32,45 +34,36 @@ data NBinop = NAdd -- a + b | NAnd -- a & b | NOr -- a | b | NXor -- a ^ b - deriving (Show, Eq, Read) --- Reference: https://en.wikipedia.org/wiki/First-order_logic#Logical_symbols + | LAnd -- a && b + | LOr -- a || b + | LImpl -- a => b --- Logical operators -data LBinop = LAnd -- Conjunction - | LOr -- Disjunction - | LImpl -- Implication + | CEqual -- a == b + | CNEqual -- a != b + | CLess -- a < b + | CGreater -- a > b + | CLeq -- a <= b + | CGeq -- a >= b deriving (Show, Eq, Read) -- Quantifier operators data QOp = QAll | QAny deriving (Show, Eq, Read) --- Comparison operators -data COp = CEqual -- a == b - | CNEqual -- a != b - | CLess -- a < b - | CGreater -- a > b - | CLeq -- a <= b - | CGeq -- a >= b - deriving (Show, Eq, Read) - --- Logical and numeral expressions are the same type, but an alias is added for clarity. -type NExpr = LExpr +data LConst = CBool Bool + | CInt Int + | CNil + deriving (Show, Eq, Read) -- Logical expressions -data LExpr = LConst Bool -- True/False - | LVar Var -- Variable - | LNot LExpr -- Logical negation/not - | LBinop LExpr LBinop LExpr -- Logical operator - | LComp NExpr COp NExpr -- Integer comparison - | LQuant QOp Var LExpr -- Logical quantifier - | LArray Var NExpr -- Logical array access - | LNil -- Nil constant - - | NConst Int -- Integer constant (TODO: use Integer instead of Int?) - | NUnop NUnop NExpr -- Unary operator - | NBinop NExpr NBinop NExpr -- Binary operators - | NIf LExpr NExpr NExpr -- if c then a else b - | NLen Var -- len(array) +data LExpr = LConst LConst -- constant + | LVar Var -- variable + | LUnop LUnop LExpr -- unary operator + | LBinop LExpr LBinop LExpr -- binary operator + | LIf LExpr LExpr LExpr -- if c then a else b (ternary operator) + | LQuant QOp Var LExpr LExpr -- quantifier (op bound domain expr) + | LArray Var LExpr -- array access + | LIsnull Var -- var == null + | LLen Var -- len(array) deriving (Show, Eq, Read) \ No newline at end of file diff --git a/src/LogicIR/Fold.hs b/src/LogicIR/Fold.hs index 75ba32c..a9f4b62 100644 --- a/src/LogicIR/Fold.hs +++ b/src/LogicIR/Fold.hs @@ -3,35 +3,27 @@ module LogicIR.Fold where import LogicIR.Expr -- Fold algebra for logical expressions -type LExprAlgebra r = (Bool -> r, -- LConst +type LExprAlgebra r = (LConst -> r, -- LConst Var -> r, -- LVar - r -> r, -- LNot + LUnop -> r -> r, -- LUnop r -> LBinop -> r -> r, -- LBinop - r -> COp -> r -> r, -- LComp - QOp -> Var -> r -> r, -- LQuant + r -> r -> r -> r, -- LIf + QOp -> Var -> r -> r -> r, -- LQuant Var -> r -> r, -- LArray - r, -- LNil - Int -> r, -- NConst - NUnop -> r -> r, -- NUnop - r -> NBinop -> r -> r, -- NBinop - r -> r -> r -> r, -- NIf - Var -> r -- NLen + Var -> r, -- LIsnull + Var -> r -- LLen ) -- Fold for logical expressions foldLExpr :: LExprAlgebra r -> LExpr -> r -foldLExpr (flConst, flVar, flNot, flBinop, flComp, flQuant, flArray, flNil, fnConst, fnUnop, fnBinop, fnIf, fnLen) = fold where +foldLExpr (fConst, fVar, fUnop, fBinop, fIf, fQuant, fArray, fIsnull, fLen) = fold where fold e = case e of - LConst c -> flConst c - LVar v -> flVar v - LNot e -> flNot (fold e) - LBinop a o b -> flBinop (fold a) o (fold b) - LComp a o b -> flComp (fold a) o (fold b) - LQuant o v e -> flQuant o v (fold e) - LArray v e -> flArray v (fold e) - LNil -> flNil - NConst n -> fnConst n - NUnop o e -> fnUnop o (fold e) - NBinop a o b -> fnBinop (fold a) o (fold b) - NIf c a b -> fnIf (fold c) (fold a) (fold b) - NLen v -> fnLen v \ No newline at end of file + LConst c -> fConst c + LVar v -> fVar v + LUnop o a -> fUnop o (fold a) + LBinop a o b -> fBinop (fold a) o (fold b) + LIf c a b -> fIf (fold c) (fold a) (fold b) + LQuant o b d a -> fQuant o b (fold d) (fold a) + LArray v a -> fArray v (fold a) + LIsnull v -> fIsnull v + LLen v -> fLen v \ No newline at end of file diff --git a/src/LogicIR/Frontend/Java.hs b/src/LogicIR/Frontend/Java.hs index 7d63f3c..d0bed50 100644 --- a/src/LogicIR/Frontend/Java.hs +++ b/src/LogicIR/Frontend/Java.hs @@ -26,9 +26,9 @@ nameToVar name env decls = let (arrayType, symbol) = (lookupType decls env name, javaExpToLExprAlgebra :: ExpAlgebra (TypeEnv -> [TypeDecl] -> LExpr) javaExpToLExprAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, fQualInstanceCreation, fArrayCreate, fArrayCreateInit, fFieldAccess, fMethodInv, fArrayAccess, fExpName, fPostIncrement, fPostDecrement, fPreIncrement, fPreDecrement, fPrePlus, fPreMinus, fPreBitCompl, fPreNot, fCast, fBinOp, fInstanceOf, fCond, fAssign, fLambda, fMethodRef) where fLit lit _ _ = case lit of -- TODO: support more type literals? - Boolean b -> LConst b - Int n -> NConst (fromIntegral n) - Null -> LNil + Boolean b -> LConst (CBool b) + Int n -> LConst (CInt (fromIntegral n)) + Null -> LConst CNil _ -> error $ show $ lit fClassLit = error "fClassLit not supported..." fThis = error "fThis not supported..." @@ -60,52 +60,52 @@ javaExpToLExprAlgebra = (fLit, fClassLit, fThis, fThisClass, fInstanceCreation, -> error $ "Unimplemented fMethodInv: " ++ show inv where forall name bound expr = let (i, arr) = (Var (TPrim PInt32) bound, nameToVar name env decls) in - LQuant QAll i (LBinop (LBinop (LComp (LVar i) CGeq (NConst 0)) LAnd (LComp (LVar i) CLess (NLen arr))) LImpl (foldExp javaExpToLExprAlgebra expr env decls)) + LQuant QAll i (LBinop (LBinop (LVar i) CGeq (LConst (CInt 0))) LAnd (LBinop (LVar i) CLess (LLen arr))) (foldExp javaExpToLExprAlgebra expr env decls) exists name bound expr = let (i, arr) = (Var (TPrim PInt32) bound, nameToVar name env decls) in - LQuant QAny i (LBinop (LBinop (LComp (LVar i) CGeq (NConst 0)) LAnd (LComp (LVar i) CLess (NLen arr))) LAnd (foldExp javaExpToLExprAlgebra expr env decls)) + LQuant QAny i (LBinop (LBinop (LVar i) CGeq (LConst (CInt 0))) LAnd (LBinop (LVar i) CLess (LLen arr))) (foldExp javaExpToLExprAlgebra expr env decls) fArrayAccess arrayIndex env decls = case arrayIndex of -- TODO: type checking ArrayIndex (ExpName name) [expr] -> LArray (nameToVar name env decls) (javaExpToLExpr expr env decls) _ -> error $ "Multidimensional arrays are not supported: " ++ show (arrayIndex) fExpName name env decls = case name of -- TODO: type checking + check implicit `this.name` - Name [Ident a, Ident "length"] -> NLen $ nameToVar (Name [Ident a]) env decls + Name [Ident a, Ident "length"] -> LLen $ nameToVar (Name [Ident a]) env decls _ -> LVar $ nameToVar name env decls fPostIncrement = error "fPostIncrement has side effects..." fPostDecrement = error "fPostDecrement has side effects..." fPreIncrement = error "fPreIncrement has side effects..." fPreDecrement = error "fPreDecrement has side effects..." fPrePlus e env decls = e env decls - fPreMinus e env decls = NUnop NNeg (e env decls) - fPreBitCompl e env decls = NUnop NNot (e env decls) - fPreNot e env decls = LNot (e env decls) + fPreMinus e env decls = LUnop NNeg (e env decls) + fPreBitCompl e env decls = LUnop NNot (e env decls) + fPreNot e env decls = LUnop LNot (e env decls) fCast = error "fCast is not supported..." -- TODO: perhaps support cast for some types? fBinOp e1 op e2 env decls = let (a, b) = (e1 env decls, e2 env decls) in -- TODO: type checking? case op of -- Integer - Mult -> NBinop a NMul b - Div -> NBinop a NDiv b - Rem -> NBinop a NRem b - Add -> NBinop a NAdd b - Sub -> NBinop a NSub b - LShift -> NBinop a NShl b - RShift -> NBinop a NShr b + Mult -> LBinop a NMul b + Div -> LBinop a NDiv b + Rem -> LBinop a NRem b + Add -> LBinop a NAdd b + Sub -> LBinop a NSub b + LShift -> LBinop a NShl b + RShift -> LBinop a NShr b RRShift -> undefined - And -> NBinop a NAnd b - Or -> NBinop a NOr b - Xor -> NBinop a NXor b + And -> LBinop a NAnd b + Or -> LBinop a NOr b + Xor -> LBinop a NXor b -- Logical CAnd -> LBinop a LAnd b COr -> LBinop a LOr b -- Comparisons - LThan -> LComp a CLess b - GThan -> LComp a CGreater b - LThanE -> LComp a CLeq b - GThanE -> LComp a CGeq b - Equal -> LComp a CEqual b - NotEq -> LComp a CNEqual b + LThan -> LBinop a CLess b + GThan -> LBinop a CGreater b + LThanE -> LBinop a CLeq b + GThanE -> LBinop a CGeq b + Equal -> LBinop a CEqual b + NotEq -> LBinop a CNEqual b fInstanceOf = error "fInstanceOf is not supported..." - fCond c a b env decls = NIf (c env decls) (a env decls) (b env decls) + fCond c a b env decls = LIf (c env decls) (a env decls) (b env decls) fAssign = error "fAssign has side effects..." fLambda = error "fLambda should be handled by fMethodInv..." fMethodRef = undefined \ No newline at end of file -- GitLab