improve-0.0.5: Language/ImProve.hs
module Language.ImProve
(
-- * Types
E
, V
, AllE
, NumE
, Name
-- * Expressions
-- ** Constants
, true
, false
, constant
, zero
-- ** Variable Reference
, ref
-- ** Logical Operations
, not_
, (&&.)
, (||.)
, and_
, or_
, any_
, all_
, imply
-- ** Equality and Comparison
, (==.)
, (/=.)
, (<.)
, (<=.)
, (>.)
, (>=.)
-- ** Min, Max, and Limiting
, min_
, minimum_
, max_
, maximum_
, limit
-- ** Arithmetic Operations
, (*.)
, (/.)
, div_
, mod_
-- ** Conditional Operator
, mux
-- ** Lookups
, linear
-- * Statements
, Stmt
-- ** Hierarchical Scope and Annotations
, scope
, annotate
-- ** Variable Declarations
, bool
, bool'
, int
, int'
, float
, float'
, input
-- ** Variable Assignment
, (<==)
-- ** Conditional Execution
, ifelse
, if_
-- ** Incrementing and decrementing.
, incr
, decr
-- ** Assertions and Assumptions
, assert
, assume
-- * Verification
, verify
-- * Code Generation
, 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 term.
true :: E Bool
true = Const True
-- | False term.
false :: E Bool
false = Const False
-- | Arbitrary constants.
constant :: AllE a => a -> E a
constant = Const
-- | Logical negation.
not_ :: E Bool -> E Bool
not_ = Not
-- | Logical AND.
(&&.) :: E Bool -> E Bool -> E Bool
(&&.) = And
-- | Logical OR.
(||.) :: E Bool -> E Bool -> E Bool
(||.) = Or
-- | The conjunction of a E Bool list.
and_ :: [E Bool] -> E Bool
and_ = foldl (&&.) true
-- | The disjunction of a E Bool list.
or_ :: [E Bool] -> E Bool
or_ = foldl (||.) false
-- | True iff the predicate is true for all elements.
all_ :: (a -> E Bool) -> [a] -> E Bool
all_ f a = and_ $ map f a
-- | True iff the predicate is true for any element.
any_ :: (a -> E Bool) -> [a] -> E Bool
any_ f a = or_ $ map f a
-- Logical implication (if a then b).
imply :: E Bool -> E Bool -> E Bool
imply a b = not_ a ||. b
-- | Equal.
(==.) :: AllE a => E a -> E a -> E Bool
(==.) = Eq
-- | Not equal.
(/=.) :: AllE a => E a -> E a -> E Bool
a /=. b = not_ (a ==. b)
-- | Less than.
(<.) :: NumE a => E a -> E a -> E Bool
(<.) = Lt
-- | Greater than.
(>.) :: NumE a => E a -> E a -> E Bool
(>.) = Gt
-- | Less than or equal.
(<=.) :: NumE a => E a -> E a -> E Bool
(<=.) = Le
-- | Greater than or equal.
(>=.) :: NumE a => E a -> E a -> E Bool
(>=.) = Ge
-- | Returns the minimum of two numbers.
min_ :: NumE a => E a -> E a -> E a
min_ a b = mux (a <=. b) a b
-- | Returns the minimum of a list of numbers.
minimum_ :: NumE a => [E a] -> E a
minimum_ = foldl1 min_
-- | Returns the maximum of two numbers.
max_ :: NumE a => E a -> E a -> E a
max_ a b = mux (a >=. b) a b
-- | Returns the maximum of a list of numbers.
maximum_ :: NumE a => [E a] -> E a
maximum_ = foldl1 max_
-- | Limits between min and 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
-- | Multiplication.
(*.) :: NumE a => E a -> a -> E a
(*.) = Mul
-- | Floating point division.
(/.) :: E Float -> Float -> E Float
_ /. 0 = error "divide by zero (/.)"
a /. b = Div a b
-- | Integer division.
div_ :: E Int -> Int -> E Int
div_ _ 0 = error "divide by zero (div_)"
div_ a b = Div a b
-- | Modulo.
mod_ :: E Int -> Int -> E Int
mod_ _ 0 = error "divide by zero (mod_)"
mod_ a b = Mod a b
-- | Linear interpolation and extrapolation between two points.
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
-- | References a variable.
ref :: AllE a => V a -> E a
ref = Ref
-- | Conditional expression.
--
-- > mux test onTrue onFalse
mux :: AllE a => E Bool -> E a -> E a -> E a
mux = Mux
-- | Creates a hierarchical scope.
scope :: Name -> Stmt a -> Stmt a
scope name stmt = do
(path0, stmt0) <- get
put (path0 ++ [name], stmt0)
a <- stmt
(_, stmt1) <- get
put (path0, stmt1)
return a
-- | Add an annotation to a statement. Useful for requirement trace tags.
annotate :: Name -> Stmt a -> Stmt a
annotate name stmt = do
(path0, stmt0) <- get
put (path0, Null)
a <- stmt
(_, stmt1) <- get
put (path0, stmt0)
statement $ Annotate name stmt1
return a
get :: Stmt ([Name], Statement)
get = Stmt $ \ a -> (a, a)
put :: ([Name], Statement) -> Stmt ()
put s = Stmt $ \ _ -> ((), s)
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 variable declaration. Input variables are initialized to 0.
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
-- | Boolean variable declaration.
bool :: Name -> Bool -> Stmt (V Bool)
bool = var
-- | Boolean variable declaration and immediate assignment.
bool' :: Name -> E Bool -> Stmt (E Bool)
bool' name value = do
a <- bool name False
a <== value
return $ ref a
-- | Int variable declaration.
int :: Name -> Int -> Stmt (V Int)
int = var
-- | Int variable declaration and immediate assignment.
int' :: Name -> E Int -> Stmt (E Int)
int' name value = do
a <- int name 0
a <== value
return $ ref a
-- | Float variable declaration.
float :: Name -> Float -> Stmt (V Float)
float = var
-- | Float variable declaration and immediate assignment.
float' :: Name -> E Float -> Stmt (E Float)
float' name value = do
a <- float name 0
a <== value
return $ ref a
-- | Increments an E Int.
incr :: V Int -> Stmt ()
incr a = a <== ref a + 1
-- | Decrements an E Int.
decr :: V Int -> Stmt ()
decr a = a <== ref a - 1
-- | The Stmt monad holds variable declarations and statements.
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 that a condition is true.
assert :: Name -> E Bool -> Stmt ()
assert a b = do
path <- getPath
statement $ Assert (path ++ [a]) b
-- | Declare an assumption condition is true.
assume :: Name -> E Bool -> Stmt ()
assume a b = do
path <- getPath
statement $ Assume (path ++ [a]) b
-- | Conditional if-else.
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
-- | Conditional if without the else.
if_ :: Name -> E Bool -> Stmt () -> Stmt()
if_ name cond stmt = ifelse name cond stmt $ return ()
-- | Verify a program.
--
-- ^ verify pathToYices maxK program
verify :: FilePath -> Int -> Stmt () -> IO ()
verify yices maxK program = V.verify yices maxK $ snd $ evalStmt [] program
-- | Generate C code.
code :: Name -> Stmt () -> IO ()
code name program = C.code name $ snd $ evalStmt [name ++ "Variables"] program