cleveland-0.1.0: morley-test/Test/Macro.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
module Test.Macro
( unit_PAPAIR
, unit_UNPAPAIR
, unit_CADR
, unit_SET_CADR
, unit_MAP_CADR
, unit_mapPairLeaves
, unit_expand
, unit_expandValue
, test_carnAndCdrnExpandToGetN
) where
import Hedgehog (forAll, property, (===))
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Gen.Michelson.Untyped as Gen.U
import qualified Hedgehog.Range as Range
import Test.Hspec (Expectation, shouldBe)
import Test.Tasty (TestTree)
import Test.Tasty.Hedgehog (testProperty)
import Morley.Michelson.ErrorPos (InstrCallStack(..), LetName(..), SrcPos, srcPos)
import Morley.Michelson.Macro
import Morley.Michelson.Untyped
(ExpandedOp(..), InstrAbstract(..), Value, Value'(..), noAnn, unsafeMkAnnotation)
import Test.Cleveland.Instances ()
defPos :: SrcPos
defPos = srcPos 1 1
defICS :: InstrCallStack
defICS = InstrCallStack [] defPos
-- TODO: it seems to me that these duplicated "where" blocks should be
-- replaced with some reasonable mini-EDSL - at least to facilitate tests
-- writing - and that would be a rather big refactoring.
-- Dunno how to deal with this duplication otherwise.
{-# ANN module ("HLint: ignore Reduce duplication" :: Text) #-}
unit_PAPAIR :: Expectation
unit_PAPAIR = do
expandPapair defICS pair n n `shouldBe` [primEx $ PAIR n n n n]
expandPapair defICS (P leaf pair) n n `shouldBe`
[primEx $ DIP (expandMacro defICS $ PAPAIR pair n n), primEx $ PAIR n n n n]
expandList [mac $ PAPAIR (P pair leaf) n n] `shouldBe`
[WithSrcEx defICS $ SeqEx [primEx $ PAIR n n n n, primEx $ PAIR n n n n]]
expandList [mac $ PAPAIR (P pair pair) n n] `shouldBe`
[WithSrcEx defICS $ SeqEx [primEx (PAIR n n n n),
primEx (DIP [primEx (PAIR n n n n)]),
primEx (PAIR n n n n)]]
where
mac = flip Mac defPos
primEx = PrimEx
n = noAnn
leaf = F n
pair = P leaf leaf
unit_UNPAPAIR :: Expectation
unit_UNPAPAIR = do
expandUnpapair defICS pair `shouldBe`
[primEx $ UNPAIR n n n n]
expandList [mac $ UNPAPAIR $ UP UF pair] `shouldBe`
[WithSrcEx defICS $ SeqEx
[ primEx (UNPAIR n n n n)
, primEx (DIP [primEx (UNPAIR n n n n)])
]
]
expandList [mac $ UNPAPAIR $ UP pair UF] `shouldBe`
[WithSrcEx defICS $ SeqEx
[ primEx (UNPAIR n n n n)
, primEx (UNPAIR n n n n)
]
]
expandList [mac $ UNPAPAIR $ UP pair pair] `shouldBe`
[WithSrcEx defICS $ SeqEx
[ primEx (UNPAIR n n n n)
, primEx (DIP [primEx $ UNPAIR n n n n])
, primEx (UNPAIR n n n n)
]
]
where
mac = flip Mac defPos
primEx = PrimEx
n = noAnn
pair = UP UF UF
unit_CADR :: Expectation
unit_CADR = do
expandCadr defICS ([A]) v f `shouldBe` [primEx $ CAR v f]
expandCadr defICS ([D]) v f `shouldBe` [primEx $ CDR v f]
expandCadr defICS (A:xs) v f `shouldBe` primEx (CAR n n) : expandMacro defICS (CADR xs v f)
expandCadr defICS (D:xs) v f `shouldBe` primEx (CDR n n) : expandMacro defICS (CADR xs v f)
where
primEx = PrimEx
v = "var"
f = "field"
n = noAnn
xs = [A, D]
unit_SET_CADR :: Expectation
unit_SET_CADR = do
expandSetCadr defICS [A] v f `shouldBe` primEx <$> [ DUP noAnn, CAR noAnn f, DROP
, CDR "%%" noAnn, SWAP, PAIR noAnn v f "@"]
expandSetCadr defICS [D] v f `shouldBe` primEx <$> [ DUP noAnn, CDR noAnn f, DROP
, CAR "%%" noAnn, PAIR noAnn v "@" f]
expandSetCadr defICS (A:xs) v f `shouldBe`
primEx <$> [DUP noAnn, DIP (primEx carN : expandMacro defICS (SET_CADR xs noAnn f)), cdrN, SWAP, pairN]
expandSetCadr defICS (D:xs) v f `shouldBe`
primEx <$> [DUP noAnn, DIP (primEx cdrN : expandMacro defICS (SET_CADR xs noAnn f)), carN, pairN]
where
primEx = PrimEx
v = "var"
f = "field"
xs = [A, D]
carN = CAR "%%" noAnn
cdrN = CDR "%%" noAnn
pairN = PAIR noAnn v "@" "@"
unit_MAP_CADR :: Expectation
unit_MAP_CADR = do
expandMapCadr defICS [A] v f ops `shouldBe`
primEx <$> [DUP noAnn, cdrN, DIP [primEx $ CAR noAnn f, SeqEx ops'], SWAP, PAIR noAnn v f "@"]
expandMapCadr defICS [D] v f ops `shouldBe`
concat [primEx <$> [DUP noAnn, CDR noAnn f], [SeqEx ops'], primEx <$> [SWAP, carN, PAIR noAnn v "@" f]]
expandMapCadr defICS (A:xs) v f ops `shouldBe`
primEx <$> [DUP noAnn, DIP (primEx carN : expandMacro defICS (MAP_CADR xs noAnn f ops)), cdrN, SWAP, pairN]
expandMapCadr defICS (D:xs) v f ops `shouldBe`
primEx <$> [DUP noAnn, DIP (primEx cdrN : expandMacro defICS (MAP_CADR xs noAnn f ops)), carN, pairN]
where
primEx = PrimEx
v = "var"
f = "field"
n = noAnn
xs = [A, D]
ops = [Prim (DUP n) defPos]
ops' = [WithSrcEx defICS $ PrimEx (DUP n)]
carN = CAR "%%" noAnn
cdrN = CDR "%%" noAnn
pairN = PAIR noAnn v "@" "@"
unit_mapPairLeaves :: Expectation
unit_mapPairLeaves = do
mapPairLeaves [f, f] pair `shouldBe` P (F f) (F f)
mapPairLeaves annotations (P pair (F n)) `shouldBe`
P (P (leaf "field1") (leaf "field2")) (leaf "field3")
mapPairLeaves annotations (P pair pair) `shouldBe`
P (P (leaf "field1") (leaf "field2")) (P (leaf "field3") (F n))
where
annotations = unsafeMkAnnotation <$> ["field1", "field2", "field3"]
n = noAnn
f = "field"
leaf f' = F (unsafeMkAnnotation f')
pair = P (F n) (F n)
unit_expand :: Expectation
unit_expand = do
expand [LetName "a"] diip `shouldBe` expandedDiip
expand [LetName "a"] (prim $ IF [diip] [diip]) `shouldBe` (primEx $ IF [expandedDiip] [expandedDiip])
expand [LetName "a"] (Seq [diip, diip] defPos) `shouldBe` (WithSrcEx aIcs $ SeqEx $ [expandedDiip, expandedDiip])
where
aIcs = InstrCallStack [LetName "a"] defPos
prim = flip Prim defPos
primEx = WithSrcEx aIcs . PrimEx
mac = flip Mac defPos
diip :: ParsedOp
diip = mac (DIIP 2 [prim SWAP])
expandedDiip :: ExpandedOp
expandedDiip = primEx (DIPN 2 [primEx SWAP])
unit_expandValue :: Expectation
unit_expandValue = do
expandValue parsedPair `shouldBe` expandedPair
expandValue parsedPapair `shouldBe` expandedPapair
expandValue parsedLambdaWithMac `shouldBe` expandedLambdaWithMac
where
mac = flip Mac defPos
primEx = PrimEx
parsedPair :: Value' ParsedOp
parsedPair = ValuePair (ValueInt 5) (ValueInt 5)
expandedPair :: Value
expandedPair = ValuePair (ValueInt 5) (ValueInt 5)
parsedPapair :: Value' ParsedOp
parsedPapair = ValuePair (ValuePair (ValueInt 5) (ValueInt 5)) (ValueInt 5)
expandedPapair :: Value
expandedPapair = ValuePair (ValuePair (ValueInt 5) (ValueInt 5)) (ValueInt 5)
parsedLambdaWithMac :: Value' ParsedOp
parsedLambdaWithMac = ValueLambda $
one (mac (PAPAIR (P (F noAnn) (P (F noAnn) (F noAnn))) noAnn noAnn))
expandedLambdaWithMac :: Value
expandedLambdaWithMac = ValueLambda . one $ WithSrcEx defICS $ SeqEx
[ primEx $ DIP [primEx $ PAIR noAnn noAnn noAnn noAnn]
, primEx $ PAIR noAnn noAnn noAnn noAnn
]
test_carnAndCdrnExpandToGetN :: [TestTree]
test_carnAndCdrnExpandToGetN =
[ testProperty "CAR k to GET 2k+1" $ property do
k <- forAll $ Gen.word $ Range.linear 0 100
n <- forAll $ Gen.U.genAnnotation
expand' (CARN n k) === primEx (GETN n $ 2 * k + 1)
, testProperty "CDR k to GET 2k" $ property do
k <- forAll $ Gen.word $ Range.linear 0 100
n <- forAll $ Gen.U.genAnnotation
expand' (CDRN n k) === primEx (GETN n $ 2 * k)
]
where
primEx = WithSrcEx defICS . SeqEx . one . PrimEx
mac = flip Mac defPos
expand' = expand [] . mac