packages feed

z3-encoding-0.3.0.0: src/Z3/Z3Pred.hs

{-
  Sealed AST for out-of-box use

  The design choice is to fix all possible *primitive*
  types as an AST, so a sound type checking is possible here.

  Question: How to handle TmApp (which is only instantiated as
  constructors) and potential user functions here? Do we have to
  provide an extra field or user can make up a Encoded Expr which
  layers on top of Term here (while preserving (expected) type soundness
  provided in this module)?

-}
{-# LANGUAGE StandaloneDeriving #-}

module Z3.Z3Pred where

import Z3.Base.Logic
import Z3.Base.Class
import Z3.Base.Atom
import Z3.Context
import Z3.Datatypes
import Z3.Monad

data Term = -- XXX: Verify that TmVar can serve at the same time as
            --      qualified var, bounded var, and zero-arity constructor
            TmVar   String
          | TmInt   Int
          | TmDouble Double
          | TmBool  Bool
          | TmString String
          | TmLE    Term Term
          | TmGE    Term Term
          | TmSub   Term Term
          | TmAdd   Term Term
          | TmMul   Term Term
          | TmDiv   Term Term
          | TmMod   Term Term
          | TmRem   Term Term
          | TmMinus Term
          | TmIf    Term Term Term
          | TmCons String HeteroList Type

deriving instance Eq Term

data Type = TyBool
          | TyInt
          | TyDouble
          | TyString
          | TyMap Type Type
          | TySet Type
          | TyADT String

deriving instance Eq Type

type Z3Pred tm = Pred tm Type ()

instance Z3Encoded Term where
    encode (TmVar x) = fst <$> getValBind x
    encode (TmInt n) = encode n
    encode (TmBool b) = encode b
    encode (TmLE t1 t2) = encode (LessE t1 t2)
    encode (TmGE t1 t2) = encode (GreaterE t1 t2)
    encode (TmAdd t1 t2) = do
        a1 <- encode t1
        a2 <- encode t2
        mkAdd [a1, a2]
    encode (TmSub t1 t2) = do
        a1 <- encode t1
        a2 <- encode t2
        mkSub [a1, a2]
    encode (TmMul t1 t2) = do
        a1 <- encode t1
        a2 <- encode t2
        mkMul [a1, a2]
    encode (TmDiv t1 t2) = do
        a1 <- encode t1
        a2 <- encode t2
        mkDiv a1 a2
    encode (TmMod t1 t2) = do
        a1 <- encode t1
        a2 <- encode t2
        mkMod a1 a2
    encode (TmRem t1 t2) = do
        a1 <- encode t1
        a2 <- encode t2
        mkRem a1 a2
    encode (TmMinus t) = do
        a <- encode t
        mkUnaryMinus a
    encode (TmIf p c a) = do
        a1 <- encode p
        a2 <- encode c
        a3 <- encode a
        mkIte a1 a2 a3
    encode (TmCons fname args retty) = do
      retSort <- sortOf retty
      encodeCons fname args retSort

-- XXX: Maybe if we have type checking this can be neater
instance Z3Sorted Term where
    sortOf (TmVar x) = snd <$> getValBind x
    sortOf (TmInt _) = mkIntSort
    sortOf (TmBool _) = mkBoolSort
    sortOf (TmLE _ _) = mkBoolSort
    sortOf (TmGE _ _) = mkBoolSort
    sortOf (TmAdd _ _) = mkIntSort
    sortOf (TmSub _ _) = mkIntSort
    sortOf (TmMul _ _) = mkIntSort
    sortOf (TmDiv _ _) = mkIntSort
    sortOf (TmMod _ _) = mkIntSort
    sortOf (TmRem _ _) = mkIntSort
    sortOf (TmMinus _) = mkIntSort
    sortOf (TmIf _ c _) = sortOf c
    sortOf (TmCons _ _ retty) = sortOf retty

instance Z3Sorted Type where
    sortOf TyBool     = mkBoolSort
    sortOf TyInt      = mkIntSort
    sortOf TyDouble   = mkRealSort
    sortOf (TyMap ty1 ty2) = do
        s1 <- sortOf ty1
        s2 <- sortOf ty2
        mkArraySort s1 s2
    sortOf (TySet ty) = do
        s <- sortOf ty
        intSort <- mkIntSort
        mkArraySort s intSort
    sortOf (TyADT tyName) = getDataType tyName

instance Z3Encoded () where

checkPre :: Z3Sorted tm => Z3Pred tm -> Z3SMT e (Result, Maybe Model)
checkPre pre = local $ do
    ast <- encode pre
    local (assert ast >> getModel)