cleveland-0.2.0: src/Hedgehog/Gen/Michelson/Untyped.hs
-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA
module Hedgehog.Gen.Michelson.Untyped
( genInternalByteString
, genVar
, genTyVar
, genExpandedOp
, genExtInstrAbstract
, genPrintComment
, genStackRef
, genTestAssert
, genStackTypePattern
, genInstrAbstract
, genContract
, genContract'
, genEntriesOrder
, genValue
, genValue'
, genElt
, genParameterType
, genType
, genEpName
, genAnnotation
, genT
) where
import Prelude hiding (EQ, GT, LT)
import Data.Text qualified as T
import Hedgehog (MonadGen(GenBase), Range)
import Hedgehog.Gen qualified as Gen
import Hedgehog.Range qualified as Range
import Morley.Michelson.Untyped
import Hedgehog.Gen.Michelson (genErrorSrcPos, genMText)
import Hedgehog.Range.Defaults
genInternalByteString :: MonadGen m => Range Length -> m InternalByteString
genInternalByteString rangeLen = InternalByteString <$> Gen.bytes (unLength <$> rangeLen)
genVar :: MonadGen m => m Var
genVar = Var <$> genSmallText
genTyVar :: (MonadGen m, GenBase m ~ Identity) => m TyVar
genTyVar = Gen.choice [VarID <$> genVar, TyCon <$> genType]
genExpandedOp :: (MonadGen m, GenBase m ~ Identity) => m ExpandedOp
genExpandedOp = Gen.recursive Gen.choice
-- non-recursive constructors
[
-- NB: When used together, genExpandedOp & genInstrAbstract are mutually recursive.
-- So we use a generator of InstrAbstract that does not contain any ops as an escape hatch.
PrimEx <$> Gen.choice instrAbstractWithoutOp
]
-- recursive constructors
[ PrimEx <$> genInstrAbstract genExpandedOp
, SeqEx <$> genSmallList genExpandedOp
, Gen.subtermM genExpandedOp $ \expandedOp -> WithSrcEx <$> genErrorSrcPos <*> pure expandedOp
]
genExtInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (ExtInstrAbstract op)
genExtInstrAbstract genOp = Gen.choice
[ STACKTYPE <$> genStackTypePattern
, UTEST_ASSERT <$> genTestAssert genOp
, UPRINT <$> genPrintComment def
, UCOMMENT <$> genSmallText
]
genPrintComment :: MonadGen m => Range TinyLength -> m PrintComment
genPrintComment rangeLen = PrintComment <$> Gen.list (unTinyLength <$> rangeLen)
(Gen.either genSmallText (genStackRef def))
genStackRef :: MonadGen m => Range StackRef -> m StackRef
genStackRef range = StackRef <$> Gen.integral (unStackRef <$> range)
where unStackRef (StackRef x) = x
genTestAssert :: MonadGen m => m op -> m (TestAssert op)
genTestAssert genOp = TestAssert <$> genSmallText <*> genPrintComment def <*> genSmallList genOp
genStackTypePattern :: (MonadGen m, GenBase m ~ Identity) => m StackTypePattern
genStackTypePattern = Gen.recursive Gen.choice
[ pure StkEmpty, pure StkRest ]
[ Gen.subtermM genStackTypePattern $ \stp -> StkCons <$> genTyVar <*> pure stp ]
genInstrAbstract :: (MonadGen m, GenBase m ~ Identity) => m op -> m (InstrAbstract op)
genInstrAbstract genOp =
Gen.choice $ instrAbstractWithOp genOp <> instrAbstractWithoutOp
instrAbstractWithOp :: (MonadGen m, GenBase m ~ Identity) => m op -> [m (InstrAbstract op)]
instrAbstractWithOp genOp =
[ EXT <$> genExtInstrAbstract genOp
, PUSH <$> genAnnotation <*> genType <*> genValue' genOp
, IF_NONE <$> genSmallList genOp <*> genSmallList genOp
, IF_LEFT <$> genSmallList genOp <*> genSmallList genOp
, IF_CONS <$> genSmallList genOp <*> genSmallList genOp
, MAP <$> genAnnotation <*> genSmallList genOp
, ITER <$> genSmallList genOp
, IF <$> genSmallList genOp <*> genSmallList genOp
, LOOP <$> genSmallList genOp
, LOOP_LEFT <$> genSmallList genOp
, LAMBDA <$> genAnnotation <*> genType <*> genType <*> genSmallList genOp
, DIP <$> genSmallList genOp
, DIPN <$> Gen.word Range.linearBounded <*> genSmallList genOp
, CREATE_CONTRACT <$> genAnnotation <*> genAnnotation <*> genContract' genOp
]
instrAbstractWithoutOp :: (MonadGen m, GenBase m ~ Identity) => [m (InstrAbstract op)]
instrAbstractWithoutOp =
[ DROPN <$> Gen.word Range.linearBounded
, pure DROP
, DUP <$> genAnnotation
, DUPN <$> genAnnotation <*> Gen.word Range.linearBounded
, pure SWAP
, DIG <$> Gen.word Range.linearBounded
, DUG <$> Gen.word Range.linearBounded
, SOME <$> genAnnotation <*> genAnnotation
, NONE <$> genAnnotation <*> genAnnotation <*> genType
, UNIT <$> genAnnotation <*> genAnnotation
, PAIR <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation
, PAIRN <$> genAnnotation <*> Gen.word Range.linearBounded
, UNPAIRN <$> Gen.word Range.linearBounded
, CAR <$> genAnnotation <*> genAnnotation
, CDR <$> genAnnotation <*> genAnnotation
, LEFT <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation <*> genType
, RIGHT <$> genAnnotation <*> genAnnotation <*> genAnnotation <*> genAnnotation <*> genType
, NIL <$> genAnnotation <*> genAnnotation <*> genType
, CONS <$> genAnnotation
, SIZE <$> genAnnotation
, EMPTY_SET <$> genAnnotation <*> genAnnotation <*> genType
, EMPTY_MAP <$> genAnnotation <*> genAnnotation <*> genType <*> genType
, EMPTY_BIG_MAP <$> genAnnotation <*> genAnnotation <*> genType <*> genType
, MEM <$> genAnnotation
, GET <$> genAnnotation
, GETN <$> genAnnotation <*> Gen.word Range.linearBounded
, UPDATE <$> genAnnotation
, EXEC <$> genAnnotation
, APPLY <$> genAnnotation
, pure FAILWITH
, CAST <$> genAnnotation <*> genType
, RENAME <$> genAnnotation
, PACK <$> genAnnotation
, UNPACK <$> genAnnotation <*> genAnnotation <*> genType
, CONCAT <$> genAnnotation
, SLICE <$> genAnnotation
, ISNAT <$> genAnnotation
, ADD <$> genAnnotation
, SUB <$> genAnnotation
, SUB_MUTEZ <$> genAnnotation
, MUL <$> genAnnotation
, EDIV <$> genAnnotation
, ABS <$> genAnnotation
, NEG <$> genAnnotation
, LSL <$> genAnnotation
, LSR <$> genAnnotation
, OR <$> genAnnotation
, AND <$> genAnnotation
, XOR <$> genAnnotation
, NOT <$> genAnnotation
, COMPARE <$> genAnnotation
, EQ <$> genAnnotation
, NEQ <$> genAnnotation
, LT <$> genAnnotation
, GT <$> genAnnotation
, LE <$> genAnnotation
, GE <$> genAnnotation
, INT <$> genAnnotation
, SELF <$> genAnnotation <*> genAnnotation
, CONTRACT <$> genAnnotation <*> genAnnotation <*> genType
, TRANSFER_TOKENS <$> genAnnotation
, SET_DELEGATE <$> genAnnotation
, IMPLICIT_ACCOUNT <$> genAnnotation
, NOW <$> genAnnotation
, AMOUNT <$> genAnnotation
, BALANCE <$> genAnnotation
, CHECK_SIGNATURE <$> genAnnotation
, SHA256 <$> genAnnotation
, SHA512 <$> genAnnotation
, BLAKE2B <$> genAnnotation
, SHA3 <$> genAnnotation
, KECCAK <$> genAnnotation
, HASH_KEY <$> genAnnotation
, SOURCE <$> genAnnotation
, SENDER <$> genAnnotation
, ADDRESS <$> genAnnotation
, CHAIN_ID <$> genAnnotation
, LEVEL <$> genAnnotation
, SELF_ADDRESS <$> genAnnotation
]
genContract :: (MonadGen m, GenBase m ~ Identity) => m Contract
genContract = genContract' genExpandedOp
genContract' :: (MonadGen m, GenBase m ~ Identity) => m op -> m (Contract' op)
genContract' genOp = Contract
<$> genParameterType
<*> genType
<*> genSmallList genOp
<*> genEntriesOrder
<*> Gen.list (Range.exponential 0 2) (genSomeView genOp)
genEntriesOrder :: (MonadGen m) => m EntriesOrder
genEntriesOrder = Gen.enumBounded
genViewName :: (MonadGen m, GenBase m ~ Identity) => m ViewName
genViewName = unsafe . mkViewName <$> Gen.text
(Range.linear 0 viewNameMaxLength)
(Gen.filter isValidViewNameChar Gen.ascii)
genSomeView :: (MonadGen m, GenBase m ~ Identity) => m op -> m (View' op)
genSomeView genOp = View
<$> genViewName
<*> genType
<*> genType
<*> genSmallList genOp
genValue :: (MonadGen m, GenBase m ~ Identity) => m Value
genValue = genValue' genExpandedOp
genValueInt :: MonadGen m => Range ValueInt -> m (Value' op)
genValueInt range = ValueInt <$> Gen.integral (unValueInt <$> range)
genValueString :: MonadGen m => Range Length -> m (Value' op)
genValueString = fmap ValueString . genMText
genValueBytes :: MonadGen m => Range Length -> m (Value' op)
genValueBytes = fmap ValueBytes . genInternalByteString
genValue' :: MonadGen m => m op -> m (Value' op)
genValue' genOp = Gen.recursive Gen.choice
-- non-recursive constructors
[ genValueInt def
, genValueString def
, genValueBytes def
, pure ValueUnit
, pure ValueTrue
, pure ValueFalse
, pure ValueNone
, pure ValueNil
]
-- recursive constructors
[ Gen.subterm2 (genValue' genOp) (genValue' genOp) ValuePair
, Gen.subterm (genValue' genOp) ValueLeft
, Gen.subterm (genValue' genOp) ValueRight
, Gen.subterm (genValue' genOp) ValueSome
, ValueSeq <$> genSmallNonEmpty (genValue' genOp)
, ValueMap <$> genSmallNonEmpty (genElt genOp)
, ValueLambda <$> genSmallNonEmpty genOp
]
genElt :: MonadGen m => m op -> m (Elt op)
genElt genOp = Elt <$> genValue' genOp <*> genValue' genOp
genParameterType :: (MonadGen m, GenBase m ~ Identity) => m ParameterType
genParameterType = ParameterType <$> genType <*> genAnnotation
-- | Generate a 'Ty'.
genType :: (MonadGen m, GenBase m ~ Identity) => m Ty
genType = Ty <$> genT <*> genAnnotation
genComparableType :: (MonadGen m, GenBase m ~ Identity) => m Ty
genComparableType = Ty <$> genComparableT <*> genAnnotation
genEpName :: (MonadGen m, GenBase m ~ Identity) => m EpName
genEpName = Gen.mapMaybe (rightToMaybe . epNameFromRefAnn) genAnnotation
genAnnotation :: forall m a. (MonadGen m, GenBase m ~ Identity) => m (Annotation a)
genAnnotation = Gen.mapMaybe (rightToMaybe . mkAnnotation) genAnnotationText
where
genAnnotationText :: m Text
genAnnotationText = Gen.frequency
[ (1, pure "")
, (17, T.cons <$> annStart <*> Gen.text (Range.linear 0 100) annBodyChar)
, (1, pure specialFieldAnn)
, (1, Gen.element specialVarAnns)
]
annStart :: m Char
annStart = Gen.choice [ pure '_', Gen.alphaNum ]
annBodyChar :: m Char
annBodyChar = Gen.choice [ annStart, Gen.element (".%@" :: String) ]
-- | Generate a 'T'.
genT :: (MonadGen m, GenBase m ~ Identity) => m T
genT =
Gen.recursive Gen.choice
-- non-recursive constructors
[ pure TKey
, pure TUnit
, pure TSignature
, pure TChainId
, pure TOperation
, pure TInt
, pure TNat
, pure TString
, pure TBytes
, pure TMutez
, pure TBool
, pure TKeyHash
, pure TTimestamp
, pure TAddress
, pure TChest
, pure TChestKey
]
-- recursive constructors
[ TOption <$> genType
, TList <$> genType
, TSet <$> genComparableType
, TContract <$> genType
, TPair <$> genAnnotation <*> genAnnotation <*> genAnnotation
<*> genAnnotation <*> genType <*> genType
, TOr <$> genAnnotation <*> genAnnotation <*> genType <*> genType
, TLambda <$> genType <*> genType
, TMap <$> genComparableType <*> genType
, TBigMap <$> genComparableType <*> genType
]
-- We don't need to pass @mustBeValid@ here because comparable types are always valid.
genComparableT :: (MonadGen m, GenBase m ~ Identity) => m T
genComparableT =
Gen.recursive Gen.choice
[ pure TInt
, pure TNat
, pure TString
, pure TBytes
, pure TMutez
, pure TBool
, pure TKeyHash
, pure TTimestamp
, pure TAddress
]
[ TPair <$> genAnnotation <*> genAnnotation <*> genAnnotation
<*> genAnnotation <*> genComparableType <*> genComparableType
]
smallCollectionRange :: Range Int
smallCollectionRange = Range.linear 0 3
genSmallList :: MonadGen m => m a -> m [a]
genSmallList = Gen.list smallCollectionRange
genSmallNonEmpty :: MonadGen m => m a -> m (NonEmpty a)
genSmallNonEmpty = Gen.nonEmpty smallCollectionRange
genSmallText :: MonadGen m => m Text
genSmallText = Gen.text (Range.linear 0 10) Gen.unicodeAll