{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Language.While.Syntax where
import Data.String (IsString (..))
import Data.Typeable ((:~:) (..))
import Data.SBV
import Data.SBV.Internals (SBV (..))
import Data.Vinyl (Rec (RNil))
import Data.Vinyl.Curry
import Control.Lens hiding ((...), (.>))
import Control.Monad.State
import Data.Map (Map)
import qualified Data.Map as Map
import Language.Expression
import Language.Expression.GeneralOp
import Language.Expression.Pretty
import Language.Expression.Util
import Language.Verification
data WhileOpKind as r where
OpLit :: AlgReal -> WhileOpKind '[] AlgReal
OpAdd, OpSub, OpMul :: WhileOpKind '[AlgReal, AlgReal] AlgReal
OpEq, OpLT, OpLE, OpGT, OpGE :: WhileOpKind '[AlgReal, AlgReal] Bool
OpAnd, OpOr :: WhileOpKind '[Bool , Bool] Bool
OpNot :: WhileOpKind '[Bool] Bool
instance EvalOpAt Identity WhileOpKind where
evalMany = \case
OpLit x -> \_ -> pure x
OpAdd -> runcurryA' (+)
OpSub -> runcurryA' (-)
OpMul -> runcurryA' (*)
OpEq -> runcurryA' (==)
OpLT -> runcurryA' (<)
OpLE -> runcurryA' (<=)
OpGT -> runcurryA' (>)
OpGE -> runcurryA' (>=)
OpAnd -> runcurryA' (&&)
OpOr -> runcurryA' (||)
OpNot -> runcurryA' not
instance EvalOpAt SBV WhileOpKind where
evalMany = \case
OpLit x -> \_ -> literal x
OpAdd -> runcurry (+)
OpSub -> runcurry (-)
OpMul -> runcurry (*)
OpEq -> runcurry (.==)
OpLT -> runcurry (.<)
OpLE -> runcurry (.<=)
OpGT -> runcurry (.>)
OpGE -> runcurry (.>=)
OpAnd -> runcurry (.&&)
OpOr -> runcurry (.||)
OpNot -> runcurry sNot
prettys1Binop ::
(Pretty1 t) =>
Int -> String -> (Int -> t a -> t b -> ShowS)
prettys1Binop prec opStr = \p x y ->
showParen (p > prec) $ prettys1Prec (prec + 1) x
. showString opStr
. prettys1Prec (prec + 1) y
instance PrettyOp WhileOpKind where
prettysPrecOp = flip $ \case
OpAdd -> runcurry . prettys1Binop 5 " + "
OpSub -> runcurry . prettys1Binop 5 " - "
OpMul -> runcurry . prettys1Binop 6 " * "
OpEq -> runcurry . prettys1Binop 4 " = "
OpLT -> runcurry . prettys1Binop 4 " < "
OpLE -> runcurry . prettys1Binop 4 " <= "
OpGT -> runcurry . prettys1Binop 4 " > "
OpGE -> runcurry . prettys1Binop 4 " >= "
OpNot ->
\p ->
runcurry $ \x -> showParen (p > 8) $ showString "! " . prettys1Prec 9 x
OpAnd -> runcurry . prettys1Binop 3 " && "
OpOr -> runcurry . prettys1Binop 2 " || "
OpLit x -> \_ _ -> shows x
type WhileOp = GeneralOp WhileOpKind
data WhileVar l a where
WhileVar :: l -> WhileVar l AlgReal
instance Pretty l => Pretty1 (WhileVar l) where
pretty1 (WhileVar l) = pretty l
instance VerifiableVar (WhileVar String) where
type VarKey (WhileVar String) = String
type VarSym (WhileVar String) = SBV
type VarEnv (WhileVar String) = ()
symForVar (WhileVar x) = const $ symbolic x
varKey (WhileVar x) = x
eqVarTypes (WhileVar _) (WhileVar _) = Just Refl
castVarSym (WhileVar _) (SBV x) = Just (SBV x)
type WhileExpr l = HFree WhileOp (WhileVar l)
instance Num (WhileExpr l AlgReal) where
fromInteger x = HWrap (Op (OpLit (fromInteger x)) RNil)
(+) = HWrap ... rcurry (Op OpAdd)
(*) = HWrap ... rcurry (Op OpMul)
(-) = HWrap ... rcurry (Op OpSub)
abs = error "can't take abs of expressions"
signum = error "can't take signum of expressions"
instance IsString s => IsString (WhileExpr s AlgReal) where
fromString = HPure . WhileVar . fromString
data Command l a
= CAnn a (Command l a)
| CSeq (Command l a) (Command l a)
| CSkip
| CAssign l (WhileExpr l AlgReal)
| CIf (WhileExpr l Bool) (Command l a) (Command l a)
| CWhile (WhileExpr l Bool) (Command l a)
instance (Pretty l, Pretty a) => Pretty (Command l a) where
prettysPrec p = \case
CAnn ann c ->
showParen (p > 10) $ showString "{ "
. prettys ann
. showString " }\n"
. prettys c
CSeq c1 c2 ->
showParen (p > 10) $ prettys c1 . showString ";\n" . prettys c2
CSkip -> showString "()"
CAssign v e ->
showParen (p > 10) $ prettys v . showString " := " . prettys e
CIf cond c1 c2 ->
showParen (p > 10) $ showString "if "
. prettysPrec 11 cond
. showString " then\n"
. prettysPrec 11 c1
. showString "\nelse\n"
. prettysPrec 11 c2
CWhile cond body ->
showParen (p > 10) $ showString "while "
. prettysPrec 11 cond
. showString " do\n"
. prettysPrec 11 body
data StepResult a
= Terminated
| Failed
| Progress a
deriving (Functor)
evalWhileExpr
:: (Applicative f)
=> (forall x. WhileVar l x -> f x)
-> WhileExpr l a -> f a
evalWhileExpr f
= fmap runIdentity
. hfoldTraverse (fmap Identity . f)
oneStep
:: (Ord l)
=> Command l a
-> State (Map l AlgReal) (StepResult (Command l a))
oneStep = \case
CAnn ann c -> fmap (CAnn ann) <$> oneStep c
CSeq c1 c2 ->
do s <- oneStep c1
case s of
Terminated -> return (Progress c2)
Failed -> return Failed
Progress c1' -> return (Progress (c1' `CSeq` c2))
CSkip -> return Terminated
CAssign loc expr ->
do env <- get
if Map.member loc env then
case evalWhileExpr (lookupVar env) expr of
Just val ->
do at loc .= Just val
return Terminated
Nothing -> return Failed
else return Failed
CIf cond c1 c2 ->
do env <- get
case evalWhileExpr (lookupVar env) cond of
Just True -> return (Progress c1)
Just False -> return (Progress c2)
_ -> return Failed
CWhile cond body ->
do env <- get
case evalWhileExpr (lookupVar env) cond of
Just True -> return (Progress (body `CSeq` (CWhile cond body)))
Just False -> return Terminated
_ -> return Failed
runCommand :: (Ord l) => Command l a -> State (Map l AlgReal) Bool
runCommand command =
do s <- oneStep command
case s of
Terminated -> return True
Failed -> return False
Progress command' -> runCommand command'
lookupVar :: (Ord l) => Map l AlgReal -> WhileVar l a -> Maybe a
lookupVar env (WhileVar s) = Map.lookup s env