module Language.ImProve
(
E
, V
, AllE
, NumE
, Name
, true
, false
, constant
, zero
, ref
, not_
, (&&.)
, (||.)
, and_
, or_
, any_
, all_
, imply
, (==.)
, (/=.)
, (<.)
, (<=.)
, (>.)
, (>=.)
, min_
, minimum_
, max_
, maximum_
, limit
, (*.)
, (/.)
, div_
, mod_
, mux
, linear
, scope
, Stmt
, bool
, bool'
, int
, int'
, float
, float'
, input
, (<==)
, ifelse
, if_
, incr
, decr
, assert
, assume
, verify
, code
) where
import Control.Monad
import Language.ImProve.Core
import qualified Language.ImProve.Verify as V
import qualified Language.ImProve.Code as C
infixl 7 *., /., `div_`, `mod_`
infix 4 ==., /=., <., <=., >., >=.
infixl 3 &&.
infixl 2 ||.
infixr 1 <==
true :: E Bool
true = Const True
false :: E Bool
false = Const False
constant :: AllE a => a -> E a
constant = Const
not_ :: E Bool -> E Bool
not_ = Not
(&&.) :: E Bool -> E Bool -> E Bool
(&&.) = And
(||.) :: E Bool -> E Bool -> E Bool
(||.) = Or
and_ :: [E Bool] -> E Bool
and_ = foldl (&&.) true
or_ :: [E Bool] -> E Bool
or_ = foldl (||.) false
all_ :: (a -> E Bool) -> [a] -> E Bool
all_ f a = and_ $ map f a
any_ :: (a -> E Bool) -> [a] -> E Bool
any_ f a = or_ $ map f a
imply :: E Bool -> E Bool -> E Bool
imply a b = not_ a ||. b
(==.) :: AllE a => E a -> E a -> E Bool
(==.) = Eq
(/=.) :: AllE a => E a -> E a -> E Bool
a /=. b = not_ (a ==. b)
(<.) :: NumE a => E a -> E a -> E Bool
(<.) = Lt
(>.) :: NumE a => E a -> E a -> E Bool
(>.) = Gt
(<=.) :: NumE a => E a -> E a -> E Bool
(<=.) = Le
(>=.) :: NumE a => E a -> E a -> E Bool
(>=.) = Ge
min_ :: NumE a => E a -> E a -> E a
min_ a b = mux (a <=. b) a b
minimum_ :: NumE a => [E a] -> E a
minimum_ = foldl1 min_
max_ :: NumE a => E a -> E a -> E a
max_ a b = mux (a >=. b) a b
maximum_ :: NumE a => [E a] -> E a
maximum_ = foldl1 max_
limit :: NumE a => E a -> E a -> E a -> E a
limit a b i = max_ min $ min_ max i
where
min = min_ a b
max = max_ a b
(*.) :: NumE a => E a -> a -> E a
(*.) = Mul
(/.) :: E Float -> Float -> E Float
_ /. 0 = error "divide by zero (/.)"
a /. b = Div a b
div_ :: E Int -> Int -> E Int
div_ _ 0 = error "divide by zero (div_)"
div_ a b = Div a b
mod_ :: E Int -> Int -> E Int
mod_ _ 0 = error "divide by zero (mod_)"
mod_ a b = Mod a b
linear :: (Float, Float) -> (Float, Float) -> E Float -> E Float
linear (x1, y1) (x2, y2) a = a *. slope + constant inter
where
slope = (y2 y1) / (x2 x1)
inter = y1 slope * x1
ref :: AllE a => V a -> E a
ref = Ref
mux :: AllE a => E Bool -> E a -> E a -> E a
mux = Mux
scope :: Name -> Stmt a -> Stmt a
scope name (Stmt f0) = Stmt f1
where
f1 (path, statement) = (a, (path, statement1))
where
(a, (_, statement1)) = f0 (path ++ [name], statement)
get :: Stmt ([Name], Statement)
get = Stmt $ \ a -> (a, a)
getPath :: Stmt [Name]
getPath = do
(path, _) <- get
return path
var :: AllE a => Name -> a -> Stmt (V a)
var name init = do
path <- getPath
return $ V False (path ++ [name]) init
input :: AllE a => (Name -> a -> Stmt (V a)) -> Name -> Stmt (E a)
input f name = do
path <- getPath
return $ ref $ V True (path ++ [name]) $ zero f
bool :: Name -> Bool -> Stmt (V Bool)
bool = var
bool' :: Name -> E Bool -> Stmt (E Bool)
bool' name value = do
a <- bool name False
a <== value
return $ ref a
int :: Name -> Int -> Stmt (V Int)
int = var
int' :: Name -> E Int -> Stmt (E Int)
int' name value = do
a <- int name 0
a <== value
return $ ref a
float :: Name -> Float -> Stmt (V Float)
float = var
float' :: Name -> E Float -> Stmt (E Float)
float' name value = do
a <- float name 0
a <== value
return $ ref a
incr :: V Int -> Stmt ()
incr a = a <== ref a + 1
decr :: V Int -> Stmt ()
decr a = a <== ref a 1
data Stmt a = Stmt (([Name], Statement) -> (a, ([Name], Statement)))
instance Monad Stmt where
return a = Stmt $ \ s -> (a, s)
(Stmt f1) >>= f2 = Stmt f3
where
f3 s1 = f4 s2
where
(a, s2) = f1 s1
Stmt f4 = f2 a
statement :: Statement -> Stmt ()
statement a = Stmt $ \ (path, statement) -> ((), (path, Sequence statement a))
evalStmt :: [Name] -> Stmt () -> ([Name], Statement)
evalStmt path (Stmt f) = snd $ f (path, Null)
class Assign a where (<==) :: V a -> E a -> Stmt ()
instance Assign Bool where a <== b = statement $ AssignBool a b
instance Assign Int where a <== b = statement $ AssignInt a b
instance Assign Float where a <== b = statement $ AssignFloat a b
assert :: Name -> E Bool -> Stmt ()
assert a b = do
path <- getPath
statement $ Assert (path ++ [a]) b
assume :: Name -> E Bool -> Stmt ()
assume a b = do
path <- getPath
statement $ Assume (path ++ [a]) b
ifelse :: Name -> E Bool -> Stmt () -> Stmt () -> Stmt ()
ifelse name cond onTrue onFalse = do
path <- getPath
let (_, stmt1) = evalStmt path onTrue
(_, stmt2) = evalStmt path onFalse
statement $ Branch (path ++ [name]) cond stmt1 stmt2
if_ :: Name -> E Bool -> Stmt () -> Stmt()
if_ name cond stmt = ifelse name cond stmt $ return ()
verify :: Stmt () -> IO (Maybe Bool)
verify program = V.verify $ snd $ evalStmt [] program
code :: Name -> Stmt () -> IO ()
code name program = C.code name $ snd $ evalStmt [name ++ "Variables"] program