covenant-1.3.0: test/json-conformance/Main.hs
{-# LANGUAGE OverloadedLists #-}
module Main (main) where
import Control.Monad (void)
import Covenant.ASG
( ASG,
ASGBuilder,
CovenantError,
Id,
Ref (AnArg, AnId),
app',
arg,
baseFunctorOf,
builtin2,
cata,
ctor,
ctor',
dtype,
err,
lam,
lazyLam,
lit,
match,
runASGBuilder,
)
import Covenant.Constant
( AConstant (AString, AnInteger),
)
import Covenant.DeBruijn (DeBruijn (S, Z))
import Covenant.Index (ix0, ix1)
import Covenant.JSON (deserializeAndValidate_)
import Covenant.Prim (TwoArgFunc (AddInteger, EqualsInteger, SubtractInteger))
import Covenant.Test
( conformanceDatatypes1,
conformanceDatatypes2,
unsafeMkDatatypeInfos,
)
import Covenant.Type
( AbstractTy,
BuiltinFlatT (BoolT, IntegerT, StringT),
CompT (Comp0, Comp1),
CompTBody (ReturnT, (:--:>)),
ValT (BuiltinFlat, Datatype),
tyvar,
)
import Data.Either (isRight)
import Data.Vector qualified as Vector
import Data.Wedge (Wedge (There))
import Test.Tasty (defaultMain, testGroup)
import Test.Tasty.HUnit (assertBool, testCase)
main :: IO ()
main =
defaultMain . testGroup "Conformance" $
[ testCase "conformance1_asg" (assertBool "case 1 compiles to asg" $ isRight conformance_body1),
testCase "conformance2_asg" (assertBool "case 2 compiles to asg" $ isRight conformance_body2),
testCase "deserialize_1" (void $ deserializeAndValidate_ "./test/json-conformance/conformance_case_1.json"),
testCase "deserialize_2" (void $ deserializeAndValidate_ "./test/json-conformance/conformance_case_2.json")
]
{- Case 1:
Datatypes:
data Maybe a = Nothing | Just a
data Result e a = Exception e | OK a
data Pair a b = Pair a b
data List a = Nil | Cons a (List a)
Body:
f :: Maybe (Pair Integer Integer) -> List Integer -> Result String Integer
f = \inpMPair inpList ->
match inpMPair
(Exception "Input is Nothing")
(\pair ->
match
(\a b ->
let listSum = cata (\listF -> match listF
(0)
(\x r -> x + r)
)
in OK (a + b + listSum)
)
)
-}
(#+) :: Ref -> Ref -> ASGBuilder Ref
x #+ y = do
plus <- builtin2 AddInteger
AnId <$> app' plus [x, y]
(#-) :: Ref -> Ref -> ASGBuilder Ref
x #- y = do
minus <- builtin2 SubtractInteger
AnId <$> app' minus [x, y]
(#==) :: Ref -> Ref -> ASGBuilder Ref
x #== y = do
equals <- builtin2 EqualsInteger
AnId <$> app' equals [x, y]
conformance_body1 :: Either CovenantError ASG
conformance_body1 =
runASGBuilder
(unsafeMkDatatypeInfos conformanceDatatypes1)
conformance_body1_builder
conformance_body1_builder :: ASGBuilder Id
conformance_body1_builder = lam topLevelTy body
where
{- arg1: Maybe (Pair Integer Integer)
arg2: List Integer
-}
body :: ASGBuilder Ref
body = do
maybeIntPair <- AnArg <$> arg Z ix0
nothingHandler' <- nothingHandler
justHandler' <- justHandler
AnId <$> match maybeIntPair [AnId nothingHandler', AnId justHandler']
nothingHandler :: ASGBuilder Id
nothingHandler = lazyLam nothingHandlerT $ do
errMsg <- AnId <$> lit (AString "Input is nothing")
AnId <$> ctor "Result" "Exception" (Vector.singleton errMsg) [There (BuiltinFlat IntegerT)]
where
nothingHandlerT :: CompT AbstractTy
nothingHandlerT = Comp0 $ ReturnT resultT
justHandler :: ASGBuilder Id
justHandler = lazyLam justHandlerT $ do
intPair <- AnArg <$> arg Z ix0
pairHandler' <- pairHandler
AnId <$> match intPair [AnId pairHandler']
where
justHandlerT :: CompT AbstractTy
justHandlerT = Comp0 $ intPairT :--:> ReturnT resultT
pairHandler :: ASGBuilder Id
pairHandler = lazyLam pairHandlerT $ do
int1 <- AnArg <$> arg Z ix0
int2 <- AnArg <$> arg Z ix1
summedArgs <- int1 #+ int2
tlListInt <- AnArg <$> arg (S (S Z)) ix1
summedList <- AnId <$> sumList tlListInt
finalResult <- summedArgs #+ summedList
AnId <$> ctor "Result" "OK" [finalResult] [There (BuiltinFlat StringT)]
where
pairHandlerT :: CompT AbstractTy
pairHandlerT = Comp0 $ intT :--:> intT :--:> ReturnT resultT
sumList :: Ref -> ASGBuilder Id
sumList listToSum = do
listF <- baseFunctorOf "List"
let cataTy = Comp0 $ Datatype listF [intT, intT] :--:> ReturnT intT
nilHandler <- AnId <$> lit (AnInteger 0)
consHandler <- lazyLam (Comp0 $ intT :--:> intT :--:> ReturnT intT) $ do
x <- AnArg <$> arg Z ix0
y <- AnArg <$> arg Z ix1
x #+ y
cata cataTy [nilHandler, AnId consHandler] listToSum
{-
sumList :: Ref -> ASGBuilder Id
sumList listToSum = do
sumListF' <- AnId <$> sumListF
cata sumListF' listToSum
sumListF :: ASGBuilder Id
sumListF = lazyLam (Comp0 $ listFIntT :--:> ReturnT intT) $ do
listFInt <- AnArg <$> arg Z ix0
nilHandler <- lazyLam (Comp0 . ReturnT $ intT) (AnId <$> lit (AnInteger 0))
consHandler <- lazyLam (Comp0 $ intT :--:> intT :--:> ReturnT intT) $ do
x <- AnArg <$> arg Z ix0
y <- AnArg <$> arg Z ix1
x #+ y
AnId <$> match listFInt (AnId <$> [nilHandler, consHandler])
where
listFIntT :: ValT AbstractTy
listFIntT = dtype "#List" [intT, intT]
-}
intT :: ValT AbstractTy
intT = BuiltinFlat IntegerT
stringT :: ValT AbstractTy
stringT = BuiltinFlat StringT
intPairT :: ValT AbstractTy
intPairT = dtype "Pair" [intT, intT]
maybeIntPairT :: ValT AbstractTy
maybeIntPairT =
dtype
"Maybe"
[intPairT]
listIntT :: ValT AbstractTy
listIntT = dtype "List" [intT]
resultT :: ValT AbstractTy
resultT = dtype "Result" [stringT, intT]
topLevelTy :: CompT AbstractTy
topLevelTy = Comp0 $ maybeIntPairT :--:> listIntT :--:> ReturnT resultT
{- Case 2:
opaque data Foo = Foo
data Void
data Maybe a = Nothing | Just a
data Pair a b = Pair a b
f :: Maybe (Pair Integer Foo) -> Maybe Boolean
f mabPairIntFoo =
let g :: forall a. Integer -> a -> Integer
g n _x = n + n
in match mabPairIntFoo
(error "Input is nothing")
(\pairIntFoo ->
match pairIntFoo (\n foo ->
let doubled = g n foo
zero = doubled - doubled
in Just (zero == 0)
)
)
-}
conformance_body2 :: Either CovenantError ASG
conformance_body2 =
runASGBuilder
(unsafeMkDatatypeInfos conformanceDatatypes2)
conformance_body2_builder
conformance_body2_builder :: ASGBuilder Id
conformance_body2_builder = lam topLevelTy body
where
body :: ASGBuilder Ref
body = do
maybeIntFooPair <- AnArg <$> arg Z ix0
g' <- g
nothingHandler' <- nothingHandler
justHandler' <- justHandler g'
AnId <$> match maybeIntFooPair [AnId nothingHandler', AnId justHandler']
nothingHandler :: ASGBuilder Id
nothingHandler = lazyLam nothingHandlerT (AnId <$> err)
where
nothingHandlerT :: CompT AbstractTy
nothingHandlerT = Comp0 $ ReturnT maybeBoolT
justHandler :: Id -> ASGBuilder Id
justHandler gx = lazyLam justHandlerTy $ do
intFooPair <- AnArg <$> arg Z ix0
pairHandler' <- pairHandler gx
AnId <$> match intFooPair [AnId pairHandler']
where
justHandlerTy :: CompT AbstractTy
justHandlerTy = Comp0 $ pairIntFooT :--:> ReturnT maybeBoolT
pairHandler :: Id -> ASGBuilder Id
pairHandler gx = lazyLam pairHandlerTy $ do
intArg <- AnArg <$> arg Z ix0
fooArg <- AnArg <$> arg Z ix1
doubled <- AnId <$> app' gx [intArg, fooArg]
zero <- doubled #- doubled
zeroIs0 <- zero #== zero
AnId <$> ctor' "Maybe" "Just" [zeroIs0]
where
pairHandlerTy :: CompT AbstractTy
pairHandlerTy = Comp0 $ integerT :--:> fooT :--:> ReturnT maybeBoolT
g :: ASGBuilder Id
g = lam gTy $ do
intArg <- AnArg <$> arg Z ix0
intArg #+ intArg
where
gTy :: CompT AbstractTy
gTy = Comp1 $ integerT :--:> tyvar Z ix0 :--:> ReturnT integerT
topLevelTy :: CompT AbstractTy
topLevelTy = Comp0 $ maybePairIntFooT :--:> ReturnT maybeBoolT
integerT :: forall a. ValT a
integerT = BuiltinFlat IntegerT
boolT :: forall a. ValT a
boolT = BuiltinFlat BoolT
fooT :: ValT AbstractTy
fooT = dtype "Foo" []
pairIntFooT :: ValT AbstractTy
pairIntFooT = dtype "Pair" [integerT, fooT]
maybePairIntFooT :: ValT AbstractTy
maybePairIntFooT = dtype "Maybe" [pairIntFooT]
maybeBoolT :: ValT AbstractTy
maybeBoolT = dtype "Maybe" [boolT]