hasmtlib-2.8.0: src/Language/Hasmtlib/Internal/Render.hs
{-# LANGUAGE LambdaCase #-}
module Language.Hasmtlib.Internal.Render where
import Language.Hasmtlib.Type.OMT
import Language.Hasmtlib.Type.SMT
import Language.Hasmtlib.Type.Expr
import Language.Hasmtlib.Type.Value
import Language.Hasmtlib.Type.Option
import Language.Hasmtlib.Type.SMTSort
import Language.Hasmtlib.Type.Bitvec
import Language.Hasmtlib.Type.ArrayMap
import Data.Ratio
import Data.Coerce
import Data.Some.Constraint
import Data.Sequence
import Data.Foldable (foldl')
import Data.Map (size, minViewWithKey)
import Data.ByteString.Builder
import Data.ByteString.Lazy.UTF8 (toString)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text.Enc
import qualified Data.Vector.Sized as V
import Control.Lens hiding (op)
import GHC.TypeNats
render1 :: Render a => Builder -> a -> Builder
render1 op x = "(" <> op <> " " <> render x <> ")"
{-# INLINE render1 #-}
render2 :: (Render a, Render b) => Builder -> a -> b -> Builder
render2 op x y = "(" <> op <> " " <> render x <> " " <> render y <> ")"
{-# INLINE render2 #-}
render3 :: (Render a, Render b, Render c) => Builder -> a -> b -> c -> Builder
render3 op x y z = "(" <> op <> " " <> render x <> " " <> render y <> " " <> render z <> ")"
{-# INLINE render3 #-}
renderN :: Render a => Builder -> [a] -> Builder
renderN op xs = "(" <> op <> renderedXs <> ")"
where
renderedXs = foldl' (\s x -> s <> " " <> render x) mempty xs
{-# INLINE renderN #-}
-- | Render values to their SMTLib2-Lisp form, represented as 'Builder'.
class Render a where
render :: a -> Builder
instance Render Bool where
render b = if b then "true" else "false"
{-# INLINE render #-}
instance Render Nat where
render = integerDec . fromIntegral
{-# INLINE render #-}
instance Render Integer where
render x
| x < 0 = "(- " <> integerDec (abs x) <> ")"
| otherwise = integerDec x
{-# INLINE render #-}
instance Render Double where
render x
| x < 0 = "(- " <> formatDouble standardDefaultPrecision (abs x) <> ")"
| otherwise = formatDouble standardDefaultPrecision x
{-# INLINE render #-}
instance Render Rational where
render x = if num < 0
then "(/ " <> "(- " <> render (abs num) <> ") " <> render denom <> ")"
else "(/ " <> render num <> " " <> render denom <> ")"
where
num = numerator x
denom = denominator x
instance Render Char where
render = char8
{-# INLINE render #-}
instance Render String where
render = string8
{-# INLINE render #-}
instance Render Builder where
render = id
{-# INLINE render #-}
instance Render Text.Text where
render = Text.Enc.encodeUtf8Builder
{-# INLINE render #-}
instance Render (Bitvec enc n) where
render = stringUtf8 . show
{-# INLINE render #-}
instance Render (SMTVar t) where
render v = "var_" <> intDec (coerce @(SMTVar t) @Int v)
{-# INLINE render #-}
instance Show (Value t) where
show = toString . toLazyByteString . render
instance Render (Value t) where
render (IntValue x) = render x
render (RealValue x) = render x
render (BoolValue x) = render x
render (BvValue v) = "#b" <> render v
render (ArrayValue arr) = case minViewWithKey (arr^.stored) of
Nothing -> constRender $ arr^.arrConst
Just ((k,v), stored')
| size (arr^.stored) > 1 -> render $ ArrStore (Constant (wrapValue (arr & stored .~ stored'))) (Constant (wrapValue k)) (Constant (wrapValue v))
| otherwise -> constRender v
where
constRender v = "((as const " <> render (goSing arr) <> ") " <> render (wrapValue v) <> ")"
goSing :: forall k v. (KnownSMTSort k, KnownSMTSort v, Ord (HaskellType k), Ord (HaskellType v)) => ConstArray (HaskellType k) (HaskellType v) -> SSMTSort (ArraySort k v)
goSing _ = sortSing @(ArraySort k v)
render (StringValue x) = "\"" <> render x <> "\""
instance KnownSMTSort t => Show (Expr t) where
show = toString . toLazyByteString . render
instance KnownSMTSort t => Render (Expr t) where
render (Var v) = render v
render (Constant c) = render c
render (Plus x y) = render2 (case sortSing' x of SBvSort _ _ -> "bvadd" ; _ -> "+") x y
render (Minus x y) = render2 (case sortSing' x of SBvSort _ _ -> "bvsub" ; _ -> "-") x y
render (Neg x) = render1 (case sortSing' x of SBvSort _ _ -> "bvneg" ; _ -> "-") x
render (Mul x y) = render2 (case sortSing' x of SBvSort _ _ -> "bvmul" ; _ -> "*") x y
render (Abs x) = render1 "abs" x
render (Mod x y) = render2 opStr x y
where
opStr = case sortSing' x of
SBvSort enc _ -> case bvEncSing' enc of
SUnsigned -> "bvurem"
SSigned -> "bvsmod"
_ -> "mod"
render (Rem x y) = render2 opStr x y
where
opStr = case sortSing' x of
SBvSort enc _ -> case bvEncSing' enc of
SUnsigned -> "bvurem"
SSigned -> "bvsrem"
_ -> "rem"
render (IDiv x y) = render2 opStr x y
where
opStr = case sortSing' x of
SBvSort enc _ -> case bvEncSing' enc of
SUnsigned -> "bvudiv"
SSigned -> "bvsdiv"
_ -> "div"
render (Div x y) = render2 "/" x y
render (LTH x y) = render2 opStr x y
where
opStr = case sortSing' x of
SBvSort enc _ -> case bvEncSing' enc of
SUnsigned -> "bvult"
SSigned -> "bvslt"
SStringSort -> "str.<"
_ -> "<"
render (LTHE x y) = render2 opStr x y
where
opStr = case sortSing' x of
SBvSort enc _ -> case bvEncSing' enc of
SUnsigned -> "bvule"
SSigned -> "bvsle"
SStringSort -> "str.<="
_ -> "<="
render (EQU xs) = renderN "=" $ V.toList xs
render (Distinct xs)= renderN "distinct" $ V.toList xs
render (GTHE x y) = case sortSing' x of
SBvSort enc _ -> case bvEncSing' enc of
SUnsigned -> render2 "bvuge" x y
SSigned -> render2 "bvsge" x y
SStringSort -> render2 "str.<=" y x
_ -> render2 ">=" x y
render (GTH x y) = case sortSing' x of
SBvSort enc _ -> case bvEncSing' enc of
SUnsigned -> render2 "bvugt" x y
SSigned -> render2 "bvsgt" x y
SStringSort -> render2 "str.<" y x
_ -> render2 ">" x y
render (Not x) = render1 (case sortSing' x of SBvSort _ _ -> "bvnot" ; _ -> "not") x
render (And x y) = render2 (case sortSing' x of SBvSort _ _ -> "bvand" ; _ -> "and") x y
render (Or x y) = render2 (case sortSing' x of SBvSort _ _ -> "bvor" ; _ -> "or") x y
render (Impl x y) = render2 "=>" x y
render (Xor x y) = render2 (case sortSing' x of SBvSort _ _ -> "bvxor" ; _ -> "xor") x y
render Pi = "real.pi"
render (Sqrt x) = render1 "sqrt" x
render (Exp x) = render1 "exp" x
render (Sin x) = render1 "sin" x
render (Cos x) = render1 "cos" x
render (Tan x) = render1 "tan" x
render (Asin x) = render1 "arcsin" x
render (Acos x) = render1 "arccos" x
render (Atan x) = render1 "arctan" x
render (ToReal x) = render1 "to_real" x
render (ToInt x) = render1 "to_int" x
render (IsInt x) = render1 "is_int" x
render (Ite p t f) = render3 "ite" p t f
render (BvNand x y) = render2 "bvnand" (render x) (render y)
render (BvNor x y) = render2 "bvnor" (render x) (render y)
render (BvShL x y) = render2 "bvshl" (render x) (render y)
render (BvLShR x y) = render2 "bvlshr" (render x) (render y)
render (BvAShR x y) = render2 "bvashr" (render x) (render y)
render (BvConcat x y) = render2 "concat" (render x) (render y)
render (BvRotL i x) = render1 (render2 "_" ("rotate_left" :: Builder) (render $ toInteger i)) (render x)
render (BvRotR i x) = render1 (render2 "_" ("rotate_right" :: Builder) (render $ toInteger i)) (render x)
render (ArrSelect a i) = render2 "select" (render a) (render i)
render (ArrStore a i v) = render3 "store" (render a) (render i) (render v)
render (StrConcat x y) = render2 "str.++" (render x) (render y)
render (StrLength x) = render1 "str.len" (render x)
render (StrAt x i) = render2 "str.at" (render x) (render i)
render (StrSubstring x i j) = render3 "str.substr" (render x) (render i) (render j)
render (StrPrefixOf x y) = render2 "str.prefixof" (render x) (render y)
render (StrSuffixOf x y) = render2 "str.suffixof" (render x) (render y)
render (StrContains x y) = render2 "str.contains" (render x) (render y)
render (StrIndexOf x y i) = render3 "str.indexof" (render x) (render y) (render i)
render (StrReplace x y y') = render3 "str.replace" (render x) (render y) (render y')
render (StrReplaceAll x y y') = render3 "str.replace_all" (render x) (render y) (render y')
render (ForAll mQvar f) = renderQuantifier "forall" mQvar f
render (Exists mQvar f) = renderQuantifier "exists" mQvar f
renderQuantifier :: forall t. KnownSMTSort t => Builder -> Maybe (SMTVar t) -> (Expr t -> Expr BoolSort) -> Builder
renderQuantifier qname (Just qvar) f =
render2
qname
("(" <> render1 (render qvar) (sortSing @t) <> ")")
expr
where
expr = render $ f $ Var qvar
renderQuantifier _ Nothing _ = mempty
instance Render (SSMTSort t) where
render SBoolSort = "Bool"
render SIntSort = "Int"
render SRealSort = "Real"
render (SBvSort _ p) = render2 "_" ("BitVec" :: Builder) (natVal p)
render (SArraySort k v) = render2 "Array" (sortSing' k) (sortSing' v)
render SStringSort = "String"
{-# INLINE render #-}
instance Render SMTOption where
render (PrintSuccess b) = render2 "set-option" (":print-success" :: Builder) b
render (ProduceModels b) = render2 "set-option" (":produce-models" :: Builder) b
render (Incremental b) = render2 "set-option" (":incremental" :: Builder) b
render (Custom k v) = render2 "set-option" (":" <> render k) (render v)
instance Render SoftFormula where
render sf = "(assert-soft " <> render (sf^.formula) <> " :weight " <> maybe "1" render (sf^.mWeight) <> renderGroupId (sf^.mGroupId) <> ")"
where
renderGroupId Nothing = mempty
renderGroupId (Just groupId) = " :id " <> render groupId
instance KnownSMTSort t => Render (Minimize t) where
render (Minimize expr) = "(minimize " <> render expr <> ")"
instance KnownSMTSort t => Render (Maximize t) where
render (Maximize expr) = "(maximize " <> render expr <> ")"
renderSetLogic :: Builder -> Builder
renderSetLogic = render1 "set-logic"
{-# INLINE renderSetLogic #-}
renderDeclareVar :: forall t. KnownSMTSort t => SMTVar t -> Builder
renderDeclareVar v = render3 "declare-fun" v ("()" :: Builder) (sortSing @t)
{-# INLINE renderDeclareVar #-}
renderAssert :: Expr BoolSort -> Builder
renderAssert = render1 "assert"
{-# INLINE renderAssert #-}
renderGetValue :: SMTVar t -> Builder
renderGetValue x = render1 "get-value" $ "(" <> render x <> ")"
{-# INLINE renderGetValue #-}
renderPush :: Integer -> Builder
renderPush i = "(push "<> render i <>")"
{-# INLINE renderPush #-}
renderPop :: Integer -> Builder
renderPop i = "(pop "<> render i <>")"
{-# INLINE renderPop #-}
renderCheckSat :: Builder
renderCheckSat = "(check-sat)"
{-# INLINE renderCheckSat #-}
renderGetModel :: Builder
renderGetModel = "(get-model)"
{-# INLINE renderGetModel #-}
class RenderProblem s where
renderOptions :: s -> Seq Builder
renderLogic :: s -> Builder
renderDeclareVars :: s -> Seq Builder
renderAssertions :: s -> Seq Builder
renderSoftAssertions :: s -> Seq Builder
renderMinimizations :: s -> Seq Builder
renderMaximizations :: s -> Seq Builder
instance RenderProblem SMT where
renderOptions = fromList . fmap render . view options
renderLogic = maybe mempty (renderSetLogic . stringUtf8) . view mlogic
renderDeclareVars = fmap (\(Some1 v) -> renderDeclareVar v) . view vars
renderAssertions = fmap renderAssert . view formulas
renderSoftAssertions _ = mempty
renderMinimizations _ = mempty
renderMaximizations _ = mempty
instance RenderProblem OMT where
renderOptions = renderOptions . view smt
renderLogic = renderLogic . view smt
renderDeclareVars = renderDeclareVars . view smt
renderAssertions = renderAssertions . view smt
renderSoftAssertions = fmap render . view softFormulas
renderMinimizations = fmap (\case Some1 minExpr -> render minExpr) . view targetMinimize
renderMaximizations = fmap (\case Some1 maxExpr -> render maxExpr) . view targetMaximize