module Language.Boogie.Interpreter (
executeProgram,
Value (..),
Environment (..),
emptyEnv,
lookupFunction,
lookupProcedure,
modifyTypeContext,
setV,
setAll,
Execution,
SafeExecution,
execSafely,
execUnsafely,
FailureSource (..),
InternalCode,
StackFrame (..),
StackTrace,
RuntimeFailure (..),
FailureKind (..),
failureKind,
eval,
exec,
execProcedure,
collectDefinitions,
valueDoc,
varsDoc,
functionsDoc,
runtimeFailureDoc
) where
import Language.Boogie.AST
import Language.Boogie.Util
import Language.Boogie.Intervals
import Language.Boogie.Position
import Language.Boogie.Tokens (nonIdChar)
import Language.Boogie.PrettyPrinter
import Language.Boogie.TypeChecker
import Language.Boogie.NormalForm
import Language.Boogie.BasicBlocks
import Data.List
import Data.Map (Map, (!))
import qualified Data.Map as M
import Control.Monad.Error hiding (join)
import Control.Applicative hiding (empty)
import Control.Monad.State hiding (join)
import Text.PrettyPrint
executeProgram :: Program -> Context -> Id -> Either RuntimeFailure Environment
executeProgram p tc entryPoint = finalEnvironment
where
initEnvironment = emptyEnv { envTypeContext = tc }
finalEnvironment = case runState (runErrorT programExecution) initEnvironment of
(Left err, _) -> Left err
(_, env) -> Right env
programExecution = do
execUnsafely $ collectDefinitions p
execCall [] entryPoint [] noPos
data Value = IntValue Integer |
BoolValue Bool |
MapValue (Map [Value] Value) |
CustomValue Integer
deriving (Eq, Ord)
defaultValue :: Type -> Value
defaultValue BoolType = BoolValue False
defaultValue IntType = IntValue 0
defaultValue (MapType _ _ _) = MapValue M.empty
defaultValue (Instance _ _) = CustomValue 0
valueDoc :: Value -> Doc
valueDoc (IntValue n) = integer n
valueDoc (BoolValue False) = text "false"
valueDoc (BoolValue True) = text "true"
valueDoc (MapValue m) = brackets (commaSep (map itemDoc (M.toList m)))
where itemDoc (keys, v) = commaSep (map valueDoc keys) <+> text "->" <+> valueDoc v
valueDoc (CustomValue n) = text "custom_" <> integer n
instance Show Value where
show v = show (valueDoc v)
data Environment = Environment
{
envLocals :: Map Id Value,
envGlobals :: Map Id Value,
envOld :: Map Id Value,
envConstants :: Map Id Expression,
envFunctions :: Map Id [FDef],
envProcedures :: Map Id [PDef],
envTypeContext :: Context
}
emptyEnv = Environment
{
envLocals = M.empty,
envGlobals = M.empty,
envOld = M.empty,
envConstants = M.empty,
envFunctions = M.empty,
envProcedures = M.empty,
envTypeContext = emptyContext
}
lookupFunction id env = case M.lookup id (envFunctions env) of
Nothing -> []
Just defs -> defs
lookupProcedure id env = case M.lookup id (envProcedures env) of
Nothing -> []
Just defs -> defs
setGlobal id val env = env { envGlobals = M.insert id val (envGlobals env) }
setLocal id val env = env { envLocals = M.insert id val (envLocals env) }
addConstantDef id def env = env { envConstants = M.insert id def (envConstants env) }
addFunctionDefs id defs env = env { envFunctions = M.insert id (lookupFunction id env ++ defs) (envFunctions env) }
addProcedureDef id def env = env { envProcedures = M.insert id (def : (lookupProcedure id env)) (envProcedures env) }
modifyTypeContext f env = env { envTypeContext = f (envTypeContext env) }
varsDoc :: Map Id Value -> Doc
varsDoc vars = vsep $ map varDoc (M.toList vars)
where varDoc (id, val) = text id <+> text "=" <+> valueDoc val
functionsDoc :: Map Id [FDef] -> Doc
functionsDoc funcs = vsep $ map funcDoc (M.toList funcs)
where
funcDoc (id, defs) = vsep $ map (funcsDefDoc id) defs
funcsDefDoc id (FDef formals guard body) = exprDoc guard <+> text "->" <+>
text id <> parens (commaSep (map text formals)) <+> text "=" <+> exprDoc body
type Execution a = ErrorT RuntimeFailure (State Environment) a
type SafeExecution a = State Environment a
execUnsafely :: SafeExecution a -> Execution a
execUnsafely computation = ErrorT (Right <$> computation)
execSafely :: Execution a -> (RuntimeFailure -> SafeExecution a) -> SafeExecution a
execSafely computation handler = do
eres <- runErrorT computation
either handler return eres
class Monad m => Finalizer m where
finally :: m a -> m () -> m a
instance (Monad m) => Finalizer (StateT s m) where
finally main cleanup = do
res <- main
cleanup
return res
instance (Error e, Monad m) => Finalizer (ErrorT e m) where
finally main cleanup = do
res <- main `catchError` (\err -> cleanup >> throwError err)
cleanup
return res
setV id val = do
tc <- gets envTypeContext
if M.member id (localScope tc)
then modify $ setLocal id val
else modify $ setGlobal id val
setAll ids vals = zipWithM_ setV ids vals
old :: Execution a -> Execution a
old execution = do
env <- get
put env { envGlobals = envOld env }
res <- execution
put env
return res
saveOld :: Execution (Map Id Value)
saveOld = do
env <- get
put env { envOld = envGlobals env }
return $ envOld env
restoreOld :: Map Id Value -> Execution ()
restoreOld olds = do
env <- get
put env { envOld = olds }
executeLocally :: (MonadState Environment m, Finalizer m) => (Context -> Context) -> [Id] -> [Value] -> m a -> m a
executeLocally localTC formals actuals computation = do
oldEnv <- get
modify $ modifyTypeContext localTC
setAll formals actuals
computation `finally` unwind oldEnv
where
unwind oldEnv = do
env <- get
put env { envTypeContext = envTypeContext oldEnv, envLocals = envLocals oldEnv }
generateValue :: Type -> (Value -> Execution ()) -> (Execution ()) -> Execution Value
generateValue t set guard = let newValue = defaultValue t in
do
set newValue
guard
return newValue
data FailureSource =
SpecViolation SpecClause |
DivisionByZero |
UnsupportedConstruct String |
InfiniteDomain Id Interval |
NoImplementation Id |
InternalFailure InternalCode
deriving Eq
data StackFrame = StackFrame {
callPos :: SourcePos,
callName :: Id
} deriving Eq
type StackTrace = [StackFrame]
data RuntimeFailure = RuntimeFailure {
rtfSource :: FailureSource,
rtfPos :: SourcePos,
rtfEnv :: Environment,
rtfTrace :: StackTrace
}
throwRuntimeFailure source pos = do
env <- get
throwError (RuntimeFailure source pos env [])
addStackFrame frame (RuntimeFailure source pos env trace) = throwError (RuntimeFailure source pos env (frame : trace))
data FailureKind = Error |
Unreachable |
Nonexecutable
deriving Eq
failureKind :: RuntimeFailure -> FailureKind
failureKind err = case rtfSource err of
SpecViolation (SpecClause _ True _) -> Unreachable
SpecViolation (SpecClause _ False _) -> Error
DivisionByZero -> Error
_ -> Nonexecutable
instance Error RuntimeFailure where
noMsg = RuntimeFailure (UnsupportedConstruct "unknown") noPos emptyEnv []
strMsg s = RuntimeFailure (UnsupportedConstruct s) noPos emptyEnv []
runtimeFailureDoc err = failureSourceDoc (rtfSource err) <+> posDoc (rtfPos err) $+$
text "with" <+> varsDoc revelantVars $+$
vsep (map stackFrameDoc (reverse (rtfTrace err)))
where
failureSourceDoc (SpecViolation (SpecClause specType isFree e)) = text (clauseName specType isFree) <+> doubleQuotes (exprDoc e) <+> defPosition specType e <+> text "violated"
failureSourceDoc (DivisionByZero) = text "Division by zero"
failureSourceDoc (InfiniteDomain var int) = text "Variable" <+> text var <+> text "quantified over an infinite domain" <+> text (show int)
failureSourceDoc (NoImplementation name) = text "Procedure" <+> text name <+> text "with no implementation called"
failureSourceDoc (UnsupportedConstruct s) = text "Unsupported construct" <+> text s
clauseName Inline isFree = if isFree then "Assumption" else "Assertion"
clauseName Precondition isFree = if isFree then "Free precondition" else "Precondition"
clauseName Postcondition isFree = if isFree then "Free postcondition" else "Postcondition"
clauseName LoopInvariant isFree = if isFree then "Free loop invariant" else "Loop invariant"
clauseName Where True = "Where clause"
defPosition Inline _ = empty
defPosition LoopInvariant _ = empty
defPosition _ e = text "defined" <+> posDoc (position e)
revelantVars = let env = rtfEnv err
in M.filterWithKey (\k _ -> isRelevant k) (envLocals env `M.union` envGlobals env)
isRelevant k = case rtfSource err of
SpecViolation (SpecClause _ _ expr) -> k `elem` freeVars expr
_ -> False
stackFrameDoc f = text "in call to" <+> text (callName f) <+> posDoc (callPos f)
posDoc pos
| pos == noPos = text "from the environment"
| otherwise = text "at" <+> text (sourceName pos) <+> text "line" <+> int (sourceLine pos)
instance Show RuntimeFailure where
show err = show (runtimeFailureDoc err)
data InternalCode = NotLinear
deriving Eq
throwInternalFailure code = throwRuntimeFailure (InternalFailure code) noPos
unOp :: UnOp -> Value -> Value
unOp Neg (IntValue n) = IntValue (n)
unOp Not (BoolValue b) = BoolValue (not b)
binOpLazy :: BinOp -> Value -> Maybe Value
binOpLazy And (BoolValue False) = Just $ BoolValue False
binOpLazy Or (BoolValue True) = Just $ BoolValue True
binOpLazy Implies (BoolValue False) = Just $ BoolValue True
binOpLazy Explies (BoolValue True) = Just $ BoolValue True
binOpLazy _ _ = Nothing
binOp :: SourcePos -> BinOp -> Value -> Value -> Execution Value
binOp pos Plus (IntValue n1) (IntValue n2) = return $ IntValue (n1 + n2)
binOp pos Minus (IntValue n1) (IntValue n2) = return $ IntValue (n1 n2)
binOp pos Times (IntValue n1) (IntValue n2) = return $ IntValue (n1 * n2)
binOp pos Div (IntValue n1) (IntValue n2) = if n2 == 0
then throwRuntimeFailure DivisionByZero pos
else return $ IntValue (fst (n1 `euclidean` n2))
binOp pos Mod (IntValue n1) (IntValue n2) = if n2 == 0
then throwRuntimeFailure DivisionByZero pos
else return $ IntValue (snd (n1 `euclidean` n2))
binOp pos Leq (IntValue n1) (IntValue n2) = return $ BoolValue (n1 <= n2)
binOp pos Ls (IntValue n1) (IntValue n2) = return $ BoolValue (n1 < n2)
binOp pos Geq (IntValue n1) (IntValue n2) = return $ BoolValue (n1 >= n2)
binOp pos Gt (IntValue n1) (IntValue n2) = return $ BoolValue (n1 > n2)
binOp pos And (BoolValue b1) (BoolValue b2) = return $ BoolValue (b1 && b2)
binOp pos Or (BoolValue b1) (BoolValue b2) = return $ BoolValue (b1 || b2)
binOp pos Implies (BoolValue b1) (BoolValue b2) = return $ BoolValue (b1 <= b2)
binOp pos Explies (BoolValue b1) (BoolValue b2) = return $ BoolValue (b1 >= b2)
binOp pos Equiv (BoolValue b1) (BoolValue b2) = return $ BoolValue (b1 == b2)
binOp pos Eq v1 v2 = return $ BoolValue (v1 == v2)
binOp pos Neq v1 v2 = return $ BoolValue (v1 /= v2)
binOp pos Lc v1 v2 = throwRuntimeFailure (UnsupportedConstruct "orders") pos
euclidean :: Integer -> Integer -> (Integer, Integer)
a `euclidean` b =
case a `quotRem` b of
(q, r) | r >= 0 -> (q, r)
| b > 0 -> (q 1, r + b)
| otherwise -> (q + 1, r b)
eval :: Expression -> Execution Value
eval expr = case node expr of
TT -> return $ BoolValue True
FF -> return $ BoolValue False
Numeral n -> return $ IntValue n
Var id -> evalVar id (position expr)
Application id args -> evalApplication id args (position expr) Nothing
MapSelection m args -> evalMapSelection m args (position expr)
MapUpdate m args new -> evalMapUpdate m args new
Old e -> old $ eval e
IfExpr cond e1 e2 -> evalIf cond e1 e2
Coercion e t -> evalCoercion e t
UnaryExpression op e -> unOp op <$> eval e
BinaryExpression op e1 e2 -> evalBinary op e1 e2
Quantified Lambda _ _ _ -> throwRuntimeFailure (UnsupportedConstruct "lambda expressions") (position expr)
Quantified Forall tv vars e -> vnot <$> evalExists tv vars (enot e) (position expr)
where vnot (BoolValue b) = BoolValue (not b)
Quantified Exists tv vars e -> evalExists tv vars e (position expr)
evalVar id pos = do
tc <- gets envTypeContext
case M.lookup id (localScope tc) of
Just t -> lookup envLocals setLocal t
Nothing -> case M.lookup id (ctxGlobals tc) of
Just t -> lookup envGlobals setGlobal t
Nothing -> case M.lookup id (ctxConstants tc) of
Just t -> do
constants <- gets envConstants
case M.lookup id constants of
Just e -> eval e
Nothing -> return $ defaultValue t
Nothing -> (error . show) (text "encountered unknown identifier during execution:" <+> text id)
where
lookup getter setter t = do
vars <- gets getter
case M.lookup id vars of
Just val -> return val
Nothing -> generateValue t (modify . setter id) (checkWhere id pos)
evalApplication name args pos mRetType = do
defs <- gets (lookupFunction name)
evalDefs defs
where
evalDefs :: [FDef] -> Execution Value
evalDefs [] = defaultValue . returnType <$> gets envTypeContext
evalDefs (FDef formals guard body : defs) = do
argsV <- mapM eval args
applicable <- evalLocally formals argsV guard `catchError` addStackFrame frame
case applicable of
BoolValue True -> evalLocally formals argsV body `catchError` addStackFrame frame
BoolValue False -> evalDefs defs
evalLocally formals actuals expr = do
sig <- funSig name <$> gets envTypeContext
executeLocally (enterFunction sig formals args mRetType) formals actuals (eval expr)
returnType tc = case mRetType of
Nothing -> exprType tc (gen $ Application name args)
Just t -> t
frame = StackFrame pos name
evalMapSelection m args pos = do
tc <- gets envTypeContext
let rangeType = exprType tc (gen $ MapSelection m args)
mV <- eval m
argsV <- mapM eval args
case mV of
MapValue map -> case M.lookup argsV map of
Nothing ->
case mapVariable tc (node m) of
Nothing -> return $ defaultValue rangeType
Just v -> generateValue rangeType (\_ -> return ()) (checkWhere v pos)
Just v -> return v
where
mapVariable tc (Var v) = if M.member v (allVars tc)
then Just v
else Nothing
mapVariable tc (MapUpdate m _ _) = mapVariable tc (node m)
mapVariable tc _ = Nothing
evalMapUpdate m args new = do
mV <- eval m
argsV <- mapM eval args
newV <- eval new
case mV of
MapValue map -> return $ MapValue (M.insert argsV newV map)
evalIf cond e1 e2 = do
v <- eval cond
case v of
BoolValue True -> eval e1
BoolValue False -> eval e2
evalCoercion (Pos pos (Application f args)) t = do
c <- gets envTypeContext
let t' = resolve c t
evalApplication f args pos (Just t')
evalCoercion e _ = eval e
evalBinary op e1 e2 = do
left <- eval e1
case binOpLazy op left of
Just result -> return result
Nothing -> do
right <- eval e2
binOp (position e1) op left right
type Domain = [Value]
evalExists :: [Id] -> [IdType] -> Expression -> SourcePos -> Execution Value
evalExists tv vars e pos = do
tc <- gets envTypeContext
case node $ normalize tc (attachPos pos $ Quantified Exists tv vars e) of
Quantified Exists tv' vars' e' -> evalExists' tv' vars' e'
evalExists' :: [Id] -> [IdType] -> Expression -> Execution Value
evalExists' tv vars e = do
results <- executeLocally (enterQuantified tv vars) [] [] evalWithDomains
return $ BoolValue (any isTrue results)
where
evalWithDomains = do
doms <- domains e varNames
evalForEach varNames doms
evalForEach :: [Id] -> [Domain] -> Execution [Value]
evalForEach [] [] = replicate 1 <$> eval e
evalForEach (var : vars) (dom : doms) = concat <$> forM dom (fixOne vars doms var)
fixOne :: [Id] -> [Domain] -> Id -> Value -> Execution [Value]
fixOne vars doms var val = do
setV var val
evalForEach vars doms
isTrue (BoolValue b) = b
varNames = map fst vars
exec :: Statement -> Execution ()
exec stmt = case node stmt of
Predicate specClause -> execPredicate specClause (position stmt)
Havoc ids -> execHavoc ids (position stmt)
Assign lhss rhss -> execAssign lhss rhss
Call lhss name args -> execCall lhss name args (position stmt)
CallForall name args -> return ()
execPredicate specClause pos = do
b <- eval $ specExpr specClause
case b of
BoolValue True -> return ()
BoolValue False -> throwRuntimeFailure (SpecViolation specClause) pos
execHavoc ids pos = do
tc <- gets envTypeContext
mapM_ (havoc tc) ids
where
havoc tc id = generateValue (exprType tc . gen . Var $ id) (setV id) (checkWhere id pos)
execAssign lhss rhss = do
rVals <- mapM eval rhss'
setAll lhss' rVals
where
lhss' = map fst (zipWith simplifyLeft lhss rhss)
rhss' = map snd (zipWith simplifyLeft lhss rhss)
simplifyLeft (id, []) rhs = (id, rhs)
simplifyLeft (id, argss) rhs = (id, mapUpdate (gen $ Var id) argss rhs)
mapUpdate e [args] rhs = gen $ MapUpdate e args rhs
mapUpdate e (args1 : argss) rhs = gen $ MapUpdate e args1 (mapUpdate (gen $ MapSelection e args1) argss rhs)
execCall lhss name args pos = do
tc <- gets envTypeContext
defs <- gets (lookupProcedure name)
case defs of
[] -> throwRuntimeFailure (NoImplementation name) pos
def : _ -> do
let lhssExpr = map (attachPos (ctxPos tc) . Var) lhss
retsV <- execProcedure (procSig name tc) def args lhssExpr `catchError` addStackFrame frame
setAll lhss retsV
where
frame = StackFrame pos name
execBlock :: Map Id [Statement] -> Id -> Execution SourcePos
execBlock blocks label = let
block = blocks ! label
statements = init block
in do
mapM exec statements
case last block of
Pos pos Return -> return pos
Pos _ (Goto lbs) -> tryOneOf blocks lbs
tryOneOf :: Map Id [Statement] -> [Id] -> Execution SourcePos
tryOneOf blocks (l : lbs) = execBlock blocks l `catchError` retry
where
retry err
| failureKind err == Unreachable && not (null lbs) = tryOneOf blocks lbs
| otherwise = throwError err
execProcedure :: PSig -> PDef -> [Expression] -> [Expression] -> Execution [Value]
execProcedure sig def args lhss = let
ins = pdefIns def
outs = pdefOuts def
blocks = snd (pdefBody def)
exitPoint pos = if pos == noPos
then pdefPos def
else pos
execBody = do
checkPreconditions sig def
olds <- saveOld
pos <- exitPoint <$> execBlock blocks startLabel
checkPostonditions sig def pos
restoreOld olds
mapM (eval . attachPos (pdefPos def) . Var) outs
in do
argsV <- mapM eval args
executeLocally (enterProcedure sig def args lhss) ins argsV execBody
checkPreconditions sig def = mapM_ (exec . attachPos (pdefPos def) . Predicate . subst sig) (psigRequires sig)
where
subst sig (SpecClause t f e) = SpecClause t f (paramSubst sig def e)
checkPostonditions sig def exitPoint = mapM_ (exec . attachPos exitPoint . Predicate . subst sig) (psigEnsures sig)
where
subst sig (SpecClause t f e) = SpecClause t f (paramSubst sig def e)
checkWhere id pos = do
whereClauses <- ctxWhere <$> gets envTypeContext
case M.lookup id whereClauses of
Nothing -> return ()
Just w -> (exec . attachPos pos . Predicate . SpecClause Where True) w
collectDefinitions :: Program -> SafeExecution ()
collectDefinitions (Program decls) = mapM_ processDecl decls
where
processDecl (Pos _ (FunctionDecl name _ args _ (Just body))) = processFunctionBody name args body
processDecl (Pos pos (ProcedureDecl name _ args rets _ (Just body))) = processProcedureBody name pos (map noWhere args) (map noWhere rets) body
processDecl (Pos pos (ImplementationDecl name _ args rets bodies)) = mapM_ (processProcedureBody name pos args rets) bodies
processDecl (Pos _ (AxiomDecl expr)) = processAxiom expr
processDecl _ = return ()
processFunctionBody name args body = let
formals = map (formalName . fst) args
guard = gen TT
in
modify $ addFunctionDefs name [FDef formals guard body]
where
formalName Nothing = dummyFArg
formalName (Just n) = n
processProcedureBody name pos args rets body = do
sig <- procSig name <$> gets envTypeContext
modify $ addProcedureDef name (PDef argNames retNames (paramsRenamed sig) (flatten body) pos)
where
argNames = map fst args
retNames = map fst rets
flatten (locals, statements) = (concat locals, M.fromList (toBasicBlocks statements))
paramsRenamed sig = map itwId (psigParams sig) /= (argNames ++ retNames)
processAxiom expr = do
extractConstantDefs expr
extractFunctionDefs expr []
extractConstantDefs :: Expression -> SafeExecution ()
extractConstantDefs bExpr = case node bExpr of
BinaryExpression Eq (Pos _ (Var c)) rhs -> modify $ addConstantDef c rhs
_ -> return ()
extractFunctionDefs :: Expression -> [Expression] -> SafeExecution ()
extractFunctionDefs bExpr guards = extractFunctionDefs' (node bExpr) guards
extractFunctionDefs' (BinaryExpression Eq (Pos _ (Application f args)) rhs) outerGuards = do
c <- gets envTypeContext
if all (simple c) args && closedRhs c
then do
let (formals, guards) = unzip (extractArgs c)
let allGuards = concat guards ++ outerGuards
let guard = if null allGuards then gen TT else foldl1 (|&|) allGuards
modify $ addFunctionDefs f [FDef formals guard rhs]
else return ()
where
simple _ (Pos p (Var _)) = True
simple c e = null $ freeVars e `intersect` M.keys (ctxIns c)
closedRhs c = null $ (freeVars rhs \\ concatMap freeVars args) `intersect` M.keys (ctxIns c)
extractArgs c = zipWith (extractArg c) args [0..]
extractArg :: Context -> Expression -> Integer -> (String, [Expression])
extractArg c (Pos p e) i = let
x = freshArgName i
xExpr = attachPos p $ Var x
in
case e of
Var arg -> if arg `M.member` ctxIns c
then (arg, [])
else (x, [xExpr |=| Pos p e])
_ -> (x, [xExpr |=| Pos p e])
freshArgName i = f ++ (nonIdChar : show i)
extractFunctionDefs' (BinaryExpression Implies cond bExpr) outerGuards = extractFunctionDefs bExpr (cond : outerGuards)
extractFunctionDefs' (BinaryExpression And bExpr1 bExpr2) outerGuards = do
extractFunctionDefs bExpr1 outerGuards
extractFunctionDefs bExpr2 outerGuards
extractFunctionDefs' (Quantified Forall tv vars bExpr) outerGuards = executeLocally (enterQuantified tv vars) [] [] (extractFunctionDefs bExpr outerGuards)
extractFunctionDefs' _ _ = return ()
type Constraints = Map Id Interval
domains :: Expression -> [Id] -> Execution [Domain]
domains boolExpr vars = do
initC <- foldM initConstraints M.empty vars
finalC <- inferConstraints boolExpr initC
forM vars (domain finalC)
where
initConstraints c var = do
tc <- gets envTypeContext
case M.lookup var (allVars tc) of
Just BoolType -> return c
Just IntType -> return $ M.insert var top c
_ -> throwRuntimeFailure (UnsupportedConstruct "quantification over a map or user-defined type") (position boolExpr)
domain c var = do
tc <- gets envTypeContext
case M.lookup var (allVars tc) of
Just BoolType -> return $ map BoolValue [True, False]
Just IntType -> do
case c ! var of
int | isBottom int -> return []
Interval (Finite l) (Finite u) -> return $ map IntValue [l..u]
int -> throwRuntimeFailure (InfiniteDomain var int) (position boolExpr)
inferConstraints :: Expression -> Constraints -> Execution Constraints
inferConstraints boolExpr constraints = do
constraints' <- foldM refineVar constraints (M.keys constraints)
if bot `elem` M.elems constraints'
then return $ M.map (const bot) constraints'
else if constraints == constraints'
then return constraints'
else inferConstraints boolExpr constraints'
where
refineVar :: Constraints -> Id -> Execution Constraints
refineVar c id = do
int <- inferInterval boolExpr c id
return $ M.insert id (meet (c ! id) int) c
inferInterval :: Expression -> Constraints -> Id -> Execution Interval
inferInterval boolExpr constraints x = (case node boolExpr of
FF -> return bot
BinaryExpression And be1 be2 -> liftM2 meet (inferInterval be1 constraints x) (inferInterval be2 constraints x)
BinaryExpression Or be1 be2 -> liftM2 join (inferInterval be1 constraints x) (inferInterval be2 constraints x)
BinaryExpression Eq ae1 ae2 -> do
(a, b) <- toLinearForm (ae1 |-| ae2) constraints x
if 0 <: a && 0 <: b
then return top
else return $ b // a
BinaryExpression Leq ae1 ae2 -> do
(a, b) <- toLinearForm (ae1 |-| ae2) constraints x
if isBottom a || isBottom b
then return bot
else if 0 <: a && not (isBottom (meet b nonPositives))
then return top
else return $ join (lessEqual (b // meet a positives)) (greaterEqual (b // meet a negatives))
BinaryExpression Ls ae1 ae2 -> inferInterval (ae1 |<=| (ae2 |-| num 1)) constraints x
BinaryExpression Geq ae1 ae2 -> inferInterval (ae2 |<=| ae1) constraints x
BinaryExpression Gt ae1 ae2 -> inferInterval (ae2 |<=| (ae1 |-| num 1)) constraints x
_ -> return top
) `catchError` handleNotLinear
where
lessEqual int | isBottom int = bot
| otherwise = Interval NegInf (upper int)
greaterEqual int | isBottom int = bot
| otherwise = Interval (lower int) Inf
handleNotLinear err = case rtfSource err of
InternalFailure NotLinear -> return top
_ -> throwError err
type LinearForm = (Interval, Interval)
toLinearForm :: Expression -> Constraints -> Id -> Execution LinearForm
toLinearForm aExpr constraints x = case node aExpr of
Numeral n -> return (0, fromInteger n)
Var y -> if x == y
then return (1, 0)
else case M.lookup y constraints of
Just int -> return (0, int)
Nothing -> const aExpr
Application name args -> if null $ M.keys constraints `intersect` freeVars aExpr
then const aExpr
else throwInternalFailure NotLinear
MapSelection m args -> if null $ M.keys constraints `intersect` freeVars aExpr
then const aExpr
else throwInternalFailure NotLinear
Old e -> old $ toLinearForm e constraints x
UnaryExpression Neg e -> do
(a, b) <- toLinearForm e constraints x
return (a, b)
BinaryExpression op e1 e2 -> do
left <- toLinearForm e1 constraints x
right <- toLinearForm e2 constraints x
combineBinOp op left right
where
const e = do
v <- eval e
case v of
IntValue n -> return (0, fromInteger n)
combineBinOp Plus (a1, b1) (a2, b2) = return (a1 + a2, b1 + b2)
combineBinOp Minus (a1, b1) (a2, b2) = return (a1 a2, b1 b2)
combineBinOp Times (a, b) (0, k) = return (k * a, k * b)
combineBinOp Times (0, k) (a, b) = return (k * a, k * b)
combineBinOp _ _ _ = throwInternalFailure NotLinear