ivory-eval (empty) → 0.1.0.3
raw patch · 5 files changed
+603/−0 lines, 5 filesdep +basedep +base-compatdep +containerssetup-changed
Dependencies added: base, base-compat, containers, ivory, ivory-eval, monadLib, tasty, tasty-hunit
Files
- LICENSE +30/−0
- Setup.hs +2/−0
- ivory-eval.cabal +46/−0
- src/Ivory/Eval.hs +471/−0
- test/Test.hs +54/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2014, Galois, Inc++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Galois, Inc nor the names of its contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ ivory-eval.cabal view
@@ -0,0 +1,46 @@+name: ivory-eval+version: 0.1.0.3+author: Galois, Inc.+copyright: 2014 Galois, Inc.+maintainer: leepike@galois.com+category: Testing+build-type: Simple+cabal-version: >= 1.10+synopsis: Simple concrete evaluator for Ivory programs+description: Warning! This module is experimental and its implementation may change dramatically.+homepage: http://smaccmpilot.org/languages/ivory-introduction.html+license: BSD3+license-file: LICENSE+source-repository head+ type: git+ location: https://github.com/GaloisInc/ivory+ tag: hackage-0103++library+ exposed-modules: Ivory.Eval+ build-depends: base >= 4.6 && < 5,+ base-compat,+ containers,+ monadLib,+ ivory+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall++test-suite test+ hs-source-dirs: test+ type: exitcode-stdio-1.0+ main-is: Test.hs+ ghc-options: -Wall++ build-depends: base >= 4.6 && < 5+ , base-compat+ , containers+ , tasty >= 0.10+ , tasty-hunit+ , monadLib+ , ivory+ , ivory-eval++ default-language: Haskell2010+
+ src/Ivory/Eval.hs view
@@ -0,0 +1,471 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RankNTypes #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}++-- | A simple interpreter for a subset of Ivory.+module Ivory.Eval+ ( runEval+ , runEvalStartingFrom+ , Error+ , Eval+ , EvalState(EvalState)+ , Value(..)+ , initState+ , openModule+ , evalAssert+ , evalBlock+ , evalCond+ , evalDeref+ , evalExpr+ , evalInit+ , evalLit+ , evalOp+ , evalRequires+ , evalStmt+ ) where++import Prelude ()+import qualified Prelude.Compat as Prelude+import Prelude.Compat hiding (negate, div, mod, not, and, or)+++import Control.Monad (unless,foldM,void)+import Data.Int+import qualified Data.Map as Map+import Data.Maybe+import qualified Data.Sequence as Seq+import Data.Word+import MonadLib+ (ExceptionM(..),runId,Id,ExceptionT,runExceptionT,sets_+ ,StateT,runStateT,StateM(..))+import Ivory.Language.Syntax.Concrete.Location+import qualified Ivory.Language.Array as I+import qualified Ivory.Language.Syntax as I++-- XXX: DEBUG+-- import Debug.Trace++type Error = String+type Eval a = StateT EvalState (ExceptionT Error Id) a++runEval :: Eval a -> Either Error a+runEval doThis = fmap fst (runEvalStartingFrom (initState Map.empty) doThis)++runEvalStartingFrom :: EvalState -> Eval a -> Either Error (a, EvalState)+runEvalStartingFrom st doThis = runId (runExceptionT (runStateT st doThis))++data EvalState = EvalState+ { store :: Map.Map I.Sym Value+ , loc :: SrcLoc+ , structs :: Map.Map I.Sym I.Struct+ } deriving Show++initState :: Map.Map I.Sym Value -> EvalState+initState st = EvalState st NoLoc Map.empty+ +-- | Run an action inside the scope of a given module.+openModule :: I.Module -> Eval a -> Eval a+openModule (I.Module {..}) doThis = do+ oldStrs <- fmap structs get+ sets_ (\s -> s { structs = Map.union (structs s) newStructs })+ res <- doThis+ sets_ (\s -> s { structs = oldStrs })+ return res+ where+ newStructs = Map.fromList+ [ (sym, struct)+ | struct@(I.Struct sym _) <- I.public modStructs ++ I.private modStructs+ ]++data Value+ = Sint8 Int8+ | Sint16 Int16+ | Sint32 Int32+ | Sint64 Int64+ | Uint8 Word8+ | Uint16 Word16+ | Uint32 Word32+ | Uint64 Word64+ | Float Float+ | Double Double+ | Char Char+ | String String+ | Bool Bool+ | Array (Seq.Seq Value)+ | Struct (Map.Map I.Sym Value)+ | Ref I.Sym+ deriving (Show, Eq, Ord)++eq :: Value -> Value -> Value+eq x y = Bool (x == y)++neq :: Value -> Value -> Value+neq x y = Bool (x /= y)++not :: Value -> Value+not (Bool x) = Bool (Prelude.not x)+not x = error $ "invalid operands to `not`: " ++ show x++and :: Value -> Value -> Value+and (Bool x) (Bool y) = Bool (x && y)+and x y = error $ "invalid operands to `and`: " ++ show (x,y)++or :: Value -> Value -> Value+or (Bool x) (Bool y) = Bool (x || y)+or x y = error $ "invalid operors to `or`: " ++ show (x,y)++ordOp :: (forall a. Ord a => a -> a -> Bool) -> Value -> Value -> Value+ordOp op (Sint8 x) (Sint8 y) = Bool (op x y)+ordOp op (Sint16 x) (Sint16 y) = Bool (op x y)+ordOp op (Sint32 x) (Sint32 y) = Bool (op x y)+ordOp op (Sint64 x) (Sint64 y) = Bool (op x y)+ordOp op (Uint8 x) (Uint8 y) = Bool (op x y)+ordOp op (Uint16 x) (Uint16 y) = Bool (op x y)+ordOp op (Uint32 x) (Uint32 y) = Bool (op x y)+ordOp op (Uint64 x) (Uint64 y) = Bool (op x y)+ordOp op (Float x) (Float y) = Bool (op x y)+ordOp op (Double x) (Double y) = Bool (op x y)+ordOp op (Char x) (Char y) = Bool (op x y)+ordOp _ x y = error $ "invalid operands to `ordOp`: " ++ show (x,y)++gt :: Value -> Value -> Value+gt = ordOp (>)++gte :: Value -> Value -> Value+gte = ordOp (>=)++lt :: Value -> Value -> Value+lt = ordOp (<)++lte :: Value -> Value -> Value+lte = ordOp (<=)++numUnOp :: (forall a. Num a => a -> a) -> Value -> Value+numUnOp op (Sint8 x) = Sint8 (op x)+numUnOp op (Sint16 x) = Sint16 (op x)+numUnOp op (Sint32 x) = Sint32 (op x)+numUnOp op (Sint64 x) = Sint64 (op x)+numUnOp op (Uint8 x) = Uint8 (op x)+numUnOp op (Uint16 x) = Uint16 (op x)+numUnOp op (Uint32 x) = Uint32 (op x)+numUnOp op (Uint64 x) = Uint64 (op x)+numUnOp op (Float x) = Float (op x)+numUnOp op (Double x) = Double (op x)+numUnOp _ x = error $ "invalid operands to `numUnOp`: " ++ show x++negate :: Value -> Value+negate = numUnOp Prelude.negate++numBinOp :: (forall a. Num a => a -> a -> a) -> Value -> Value -> Value+numBinOp op (Sint8 x) (Sint8 y) = Sint8 (op x y)+numBinOp op (Sint16 x) (Sint16 y) = Sint16 (op x y)+numBinOp op (Sint32 x) (Sint32 y) = Sint32 (op x y)+numBinOp op (Sint64 x) (Sint64 y) = Sint64 (op x y)+numBinOp op (Uint8 x) (Uint8 y) = Uint8 (op x y)+numBinOp op (Uint16 x) (Uint16 y) = Uint16 (op x y)+numBinOp op (Uint32 x) (Uint32 y) = Uint32 (op x y)+numBinOp op (Uint64 x) (Uint64 y) = Uint64 (op x y)+numBinOp op (Float x) (Float y) = Float (op x y)+numBinOp op (Double x) (Double y) = Double (op x y)+numBinOp _ x y = error $ "invalid operands to `numBinOp`: " ++ show (x,y)++add :: Value -> Value -> Value+add = numBinOp (+)++sub :: Value -> Value -> Value+sub = numBinOp (-)++mul :: Value -> Value -> Value+mul = numBinOp (*)++div :: Value -> Value -> Value+(Sint8 x) `div` (Sint8 y) = Sint8 (x `Prelude.div` y)+(Sint16 x) `div` (Sint16 y) = Sint16 (x `Prelude.div` y)+(Sint32 x) `div` (Sint32 y) = Sint32 (x `Prelude.div` y)+(Sint64 x) `div` (Sint64 y) = Sint64 (x `Prelude.div` y)+(Uint8 x) `div` (Uint8 y) = Uint8 (x `Prelude.div` y)+(Uint16 x) `div` (Uint16 y) = Uint16 (x `Prelude.div` y)+(Uint32 x) `div` (Uint32 y) = Uint32 (x `Prelude.div` y)+(Uint64 x) `div` (Uint64 y) = Uint64 (x `Prelude.div` y)+(Float x) `div` (Float y) = Float (x / y)+(Double x) `div` (Double y) = Double (x / y)+x `div` y = error $ "invalid operands to `div`: " ++ show (x,y)++mod :: Value -> Value -> Value+(Sint8 x) `mod` (Sint8 y) = Sint8 (x `Prelude.mod` y)+(Sint16 x) `mod` (Sint16 y) = Sint16 (x `Prelude.mod` y)+(Sint32 x) `mod` (Sint32 y) = Sint32 (x `Prelude.mod` y)+(Sint64 x) `mod` (Sint64 y) = Sint64 (x `Prelude.mod` y)+(Uint8 x) `mod` (Uint8 y) = Uint8 (x `Prelude.mod` y)+(Uint16 x) `mod` (Uint16 y) = Uint16 (x `Prelude.mod` y)+(Uint32 x) `mod` (Uint32 y) = Uint32 (x `Prelude.mod` y)+(Uint64 x) `mod` (Uint64 y) = Uint64 (x `Prelude.mod` y)+-- (Float x) `mod` (Float y) = Float (x `Prelude.mod` y)+-- (Double x) `mod` (Double y) = Double (x `Prelude.mod` y)+x `mod` y = error $ "invalid operands to `mod`: " ++ show (x,y)++readStore :: I.Sym -> Eval Value+readStore sym = do+ st <- fmap store get+ case Map.lookup sym st of+ Nothing -> raise $ "Unbound variable: `" ++ sym ++ "'!"+ Just v -> return v++writeStore :: I.Sym -> Value -> Eval ()+writeStore sym val = sets_ (\s -> s { store = Map.insert sym val (store s) })++modifyStore :: I.Sym -> (Value -> Value) -> Eval ()+modifyStore sym f = sets_ (\s -> s { store = Map.update (Just . f) sym (store s) })++updateLoc :: SrcLoc -> Eval ()+updateLoc loc = sets_ (\ s -> s { loc = loc })++lookupStruct :: String -> Eval I.Struct+lookupStruct str = do+ structs <- fmap structs get+ case Map.lookup str structs of+ Nothing -> raise $ "Couldn't find struct: " ++ str+ Just str -> return str++----------------------------------------------------------------------+-- | Main Evaluator+----------------------------------------------------------------------+evalBlock :: I.Block -> Eval ()+evalBlock = mapM_ evalStmt++evalRequires :: [I.Require] -> Eval Bool+evalRequires = fmap Prelude.and . mapM (evalCond . I.getRequire)++evalCond :: I.Cond -> Eval Bool+evalCond cond = case cond of+ I.CondBool expr -> do+ val <- evalExpr I.TyBool expr+ case val of+ Bool True -> return True+ Bool False -> return False+ _ -> raise $ "evalCond: expected boolean, got: " ++ show val+ I.CondDeref ty expr var cond -> do+ evalStmt (I.Deref ty var expr)+ evalCond cond++evalStmt :: I.Stmt -> Eval ()+evalStmt stmt = case stmt of+ I.Comment (I.SourcePos loc)+ -> updateLoc loc+ I.Assert expr+ -> evalAssert expr+ I.CompilerAssert expr+ -> evalAssert expr+ I.IfTE cond true false+ -> do b <- evalExpr I.TyBool cond+ case b of+ Bool True -> evalBlock true+ Bool False -> evalBlock false+ _ -> raise $ "evalStmt: IfTE: expected true or false, got: " ++ show b+ I.Deref _ty var expr+ -> do val <- evalDeref _ty expr+ case val of+ Ref ref -> do+ val <- readStore ref+ writeStore (varSym var) val+ _ -> writeStore (varSym var) val+ I.Assign _ty var expr+ -> do val <- evalExpr _ty expr+ writeStore (varSym var) val+ I.Local ty var init+ -> do val <- evalInit ty init+ writeStore (varSym var) val+ I.AllocRef _ty var ref+ -> writeStore (varSym var) (Ref $ nameSym ref)+ I.Loop _ var expr incr body+ -> do val <- evalExpr I.ixRep expr+ writeStore (varSym var) val+ let (checkDone, stepFn, doneExpr) = case incr of+ I.IncrTo expr -> ((>=), (`add` Sint32 1), expr) -- XXX: don't hard-code ixrep+ I.DecrTo expr -> ((<=), (`sub` Sint32 1), expr)+ let step = modifyStore (varSym var) stepFn+ let done = do curVal <- readStore (varSym var)+ doneVal <- evalExpr I.ixRep doneExpr+ return (checkDone curVal doneVal)+ untilM done (evalBlock body >> step)+ I.Return (I.Typed _ty expr)+ -> void $ evalExpr _ty expr+ I.Store _ty (I.ExpAddrOfGlobal dst) expr+ -> do val <- evalExpr _ty expr+ writeStore dst val+ I.Store _ty (I.ExpVar dst) expr+ -> do val <- evalExpr _ty expr+ Ref ref <- readStore (varSym dst)+ writeStore ref val+ I.RefCopy _ty (I.ExpAddrOfGlobal dst) expr+ -> do val <- evalExpr _ty expr+ writeStore dst val+ _ -> error $ show stmt++evalDeref :: I.Type -> I.Expr -> Eval Value+evalDeref _ty expr = case expr of+ I.ExpSym sym -> readRef sym+ I.ExpVar var -> readRef (varSym var)+ I.ExpAddrOfGlobal sym -> readStore sym+ I.ExpIndex tarr arr tidx idx+ -> do Array arr <- evalDeref tarr arr+ Sint32 idx <- evalExpr tidx idx+ return (arr `Seq.index` fromIntegral idx)+ I.ExpLabel tstr str lab+ -> do Struct str <- evalDeref tstr str+ return (fromJust $ Map.lookup lab str)+ _ -> error $ show expr++readRef :: I.Sym -> Eval Value+readRef sym = do+ val <- readStore sym+ case val of+ Ref ref -> readStore ref+ _ -> raise $ "Expected Ref, got: " ++ show val++evalAssert :: I.Expr -> Eval ()+evalAssert asrt = do+ b <- evalExpr I.TyBool asrt+ case b of+ Bool True -> return ()+ _ -> raise $ "Assertion failed: " ++ show (b,asrt)++evalExpr :: I.Type -> I.Expr -> Eval Value+evalExpr ty expr = case expr of+ I.ExpSym sym -> readStore sym+ I.ExpVar var -> readStore (varSym var)+ I.ExpLit lit -> evalLit ty lit+ I.ExpOp op exprs+ -> do let opTy = case op of+ I.ExpEq t -> t+ I.ExpNeq t -> t+ I.ExpGt _ t -> t+ I.ExpLt _ t -> t+ I.ExpIsNan t -> t+ I.ExpIsInf t -> t+ _ -> ty+ vals <- mapM (evalExpr opTy) exprs+ evalOp op vals+ I.ExpSafeCast fromTy expr+ -> do val <- evalExpr fromTy expr+ return (cast ty fromTy val)+ I.ExpToIx expr max+ -> fmap (`mod` Sint32 (fromInteger max)) (evalExpr I.ixRep expr)+ _ -> error $ show expr++cast :: I.Type -> I.Type -> Value -> Value+cast toTy _fromTy val = mkVal toTy integer+ where+ integer = case val of+ Sint8 i -> toInteger i+ Sint16 i -> toInteger i+ Sint32 i -> toInteger i+ Sint64 i -> toInteger i+ Uint8 i -> toInteger i+ Uint16 i -> toInteger i+ Uint32 i -> toInteger i+ Uint64 i -> toInteger i+ _ -> error $ "Expected number, got: " ++ show val++evalLit :: I.Type -> I.Literal -> Eval Value+evalLit ty lit = case lit of+ I.LitInteger i -> return (mkVal ty i)+ I.LitFloat c -> return (Float c)+ I.LitDouble c -> return (Double c)+ I.LitChar c -> return (Char c)+ I.LitBool b -> return (Bool b)+ I.LitString s -> return (String s)+ _ -> raise $ "evalLit: can't handle: " ++ show lit++mkVal :: I.Type -> Integer -> Value+mkVal ty = case ty of+ I.TyInt I.Int8 -> Sint8 . fromInteger+ I.TyInt I.Int16 -> Sint16 . fromInteger+ I.TyInt I.Int32 -> Sint32 . fromInteger+ I.TyInt I.Int64 -> Sint64 . fromInteger+ I.TyWord I.Word8 -> Uint8 . fromInteger+ I.TyWord I.Word16 -> Uint16 . fromInteger+ I.TyWord I.Word32 -> Uint32 . fromInteger+ I.TyWord I.Word64 -> Uint64 . fromInteger+ I.TyIndex _ -> Sint32 . fromInteger -- XXX: don't hard-code index rep+ I.TyFloat -> Float . fromInteger+ I.TyDouble -> Double . fromInteger+ I.TyBool -> Bool . toEnum . fromInteger+ I.TyChar -> Char . toEnum . fromInteger+ _ -> error $ "mkVal: " ++ show ty++evalOp :: I.ExpOp -> [Value] -> Eval Value+evalOp (I.ExpEq _) [x, y] = return (x `eq` y)+evalOp (I.ExpNeq _) [x, y] = return (x `neq` y)+evalOp (I.ExpGt True _) [x, y] = return (x `gte` y)+evalOp (I.ExpGt False _) [x, y] = return (x `gt` y)+evalOp (I.ExpLt True _) [x, y] = return (x `lte` y)+evalOp (I.ExpLt False _) [x, y] = return (x `lt` y)+evalOp I.ExpAdd [x, y] = return (x `add` y)+evalOp I.ExpSub [x, y] = return (x `sub` y)+evalOp I.ExpMul [x, y] = return (x `mul` y)+evalOp I.ExpDiv [x, y] = return (x `div` y)+evalOp I.ExpMod [x, y] = return (x `mod` y)+evalOp I.ExpNegate [x] = return (negate x)+evalOp I.ExpNot [x] = return (not x)+evalOp I.ExpAnd [x, y] = return (x `and` y)+evalOp I.ExpOr [x, y] = return (x `or` y)+evalOp I.ExpCond [cond, true, false] =+ case cond of+ Bool True -> return true+ Bool False -> return false+ _ -> raise $ "evalOp: ExpCond: expected true or false, got: " ++ show cond+evalOp op args = raise $ "evalOp: can't handle: " ++ show (op, args)++evalInit :: I.Type -> I.Init -> Eval Value+evalInit ty init = case init of+ I.InitZero+ -> case ty of+ I.TyArr _ _ -> evalInit ty (I.InitArray [])+ I.TyStruct _ -> evalInit ty (I.InitStruct [])+ _ -> return (mkVal ty 0)+ I.InitExpr ty expr+ -> evalExpr ty expr+ I.InitArray inits+ -> case ty of+ I.TyArr len ty+ -> Array . Seq.fromList+ <$> mapM (evalInit ty) (take len $ inits ++ repeat I.InitZero)+ _ -> raise $ "evalInit: InitArray: unexpected type: " ++ show ty+ I.InitStruct inits+ -> case ty of+ I.TyStruct str -> do+ I.Struct _ fields <- lookupStruct str+ zstr <- foldM (\ str (I.Typed ty fld) -> do+ val <- evalInit ty I.InitZero+ return (Map.insert fld val str))+ Map.empty fields+ str <- foldM (\ str (fld, init) -> do+ val <- evalInit (lookupTyped fld fields) init+ return (Map.insert fld val str))+ zstr inits+ return $ Struct str+ _ -> raise $ "evalInit: InitStruct: unexpected type: " ++ show ty++lookupTyped :: (Show a, Eq a) => a -> [I.Typed a] -> I.Type+lookupTyped a [] = error $ "lookupTyped: couldn't find: " ++ show a+lookupTyped a (I.Typed t x : xs)+ | a == x = t+ | otherwise = lookupTyped a xs++varSym :: I.Var -> I.Sym+varSym (I.VarName sym) = sym+varSym (I.VarInternal sym) = sym+varSym (I.VarLitName sym) = sym++nameSym :: I.Name -> I.Sym+nameSym (I.NameSym sym) = sym+nameSym (I.NameVar var) = varSym var++----------------------------------------------------------------------+-- | Utilities+----------------------------------------------------------------------+untilM :: Monad m => m Bool -> m () -> m ()+untilM done doThis = do+ b <- done+ unless b (doThis >> untilM done doThis)
+ test/Test.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Main where++import Prelude ()+import Prelude.Compat++import Ivory.Language+import Ivory.Language.Monad++import Ivory.Eval++import qualified Data.Map as Map+import Test.Tasty+import Test.Tasty.HUnit++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [ sumTest, assertTest ]+++--------------------------------------------------------------------------------+-- test modules++sumTest :: TestTree+sumTest = testCase "sum" $ do+ let Right (_, EvalState st _ _)+ = runEvalStartingFrom (initState mempty)+ $ evalBlock $ blockStmts $ snd $ runIvory $ do+ r <- local (izero :: Init ('Stored Sint32))+ 11 `times` \(i :: Ix 12) -> do -- sum [ 10 .. 0 ]+ v <- deref r+ store r (v + safeCast i)+ assertEqual "wrong state!"+ (Map.fromList [ ("local0", Sint32 55), ("deref3", Sint32 54)+ , ("ix2", Sint32 0), ("ref1", Ref "local0")]) + st++assertTest :: TestTree+assertTest = testCase "assert" $ do+ let r = runEvalStartingFrom (initState mempty)+ $ evalBlock $ blockStmts $ snd $ runIvory $+ Ivory.Language.assert ((0 :: Sint32) <? 1)+ assertBool (show r) (case r of+ Left _ -> False+ Right _ -> True)