-- SPDX-FileCopyrightText: 2021 Oxhead Alpha -- SPDX-License-Identifier: LicenseRef-MIT-OA -- | Module that provides type classes for converting to and from low-level -- Micheline representation. module Morley.Micheline.Class ( ToExpression (..) , FromExpError (..) , FromExpressionError , FromExp (..) , FromExpression , fromExpression ) where import Control.Lens ((<>~)) import Data.Bits (Bits) import Data.Singletons (SingI(..), demote, fromSing) import Fmt (Buildable(..), fillSepF, indentF, nameF, pretty, punctuateF, unlinesF, (++|), (|++)) import Morley.Micheline.Expression import Morley.Michelson.Text (mkMText, unMText) import Morley.Michelson.TypeCheck (TcError, TypeCheckOptions(..), typeCheckingWith) import Morley.Michelson.TypeCheck.Instr (typeCheckValue) import Morley.Michelson.Typed (Contract, ForbidOp, Instr, LambdaCode'(..), Notes(..), T(..), Value, Value'(..), fromUType, mkUType, rfAnyInstr, toUType) import Morley.Michelson.Typed.Convert (convertContractOptimized, instrToOpsOptimized, untypeValueOptimized) import Morley.Michelson.Untyped qualified as Untyped import Morley.Michelson.Untyped.Annotation (AnnotationSet(..), FieldAnn, FieldTag, RootAnn, TypeAnn, TypeTag, VarAnn, VarTag, annsCount, firstAnn, noAnn, secondAnn) import Morley.Michelson.Untyped.Contract (ContractBlock(..), ContractBlockError, orderContractBlock) import Morley.Michelson.Untyped.Instr (ExpandedInstr, ExpandedOp(..), InstrAbstract(..)) import Morley.Michelson.Untyped.Type (Ty(..)) import Morley.Michelson.Untyped.View import Morley.Util.MismatchError -- ToExpression ---------------------------------------------------------------------------- -- | Type class that provides an ability to convert -- something to Micheline Expression. class ToExpression a where toExpression :: a -> Expression instance ForbidOp t => ToExpression (Value t) where toExpression = toExpression . untypeValueOptimized instance ToExpression Untyped.Value where toExpression = \case Untyped.ValueInt v -> expressionInt v Untyped.ValueString s -> expressionString $ unMText s Untyped.ValueBytes (Untyped.InternalByteString bs) -> expressionBytes bs Untyped.ValueUnit -> expressionPrim' Prim_Unit [] [] Untyped.ValueTrue -> expressionPrim' Prim_True [] [] Untyped.ValueFalse -> expressionPrim' Prim_False [] [] Untyped.ValuePair l r -> expressionPrim' Prim_Pair [toExpression l, toExpression r] [] Untyped.ValueLeft v -> expressionPrim' Prim_Left [toExpression v] [] Untyped.ValueRight v -> expressionPrim' Prim_Right [toExpression v] [] Untyped.ValueSome v -> expressionPrim' Prim_Some [toExpression v] [] Untyped.ValueNone -> expressionPrim' Prim_None [] [] Untyped.ValueNil -> expressionSeq [] Untyped.ValueSeq vs -> toExpression vs Untyped.ValueMap elts -> toExpression $ eltToExpr <$> elts Untyped.ValueLambda ops -> toExpression ops Untyped.ValueLamRec ops -> expressionPrim' Prim_Lambda_rec [toExpression ops] [] where eltToExpr :: Untyped.Elt [] ExpandedOp -> Expression eltToExpr (Untyped.Elt l r) = expressionPrim' Prim_Elt [toExpression l, toExpression r] [] instance ToExpression (Instr inp out) where toExpression = toExpression . instrToOpsOptimized instance ToExpression T where toExpression = toExpression . toUType instance ToExpression (Notes t) where toExpression = toExpression . mkUType instance ToExpression Untyped.T where toExpression = \case Untyped.TKey -> expressionPrim' Prim_key [] [] Untyped.TUnit -> expressionPrim' Prim_unit [] [] Untyped.TSignature -> expressionPrim' Prim_signature [] [] Untyped.TChainId -> expressionPrim' Prim_chain_id [] [] Untyped.TOption arg -> expressionPrim' Prim_option [toExpression arg] [] Untyped.TList arg -> expressionPrim' Prim_list [toExpression arg] [] Untyped.TSet arg -> expressionPrim' Prim_set [toExpression arg] [] Untyped.TOperation -> expressionPrim' Prim_operation [] [] Untyped.TContract arg -> expressionPrim' Prim_contract [toExpression arg] [] Untyped.TTicket arg -> expressionPrim' Prim_ticket [toExpression arg] [] t@Untyped.TPair{} -> expressionPrim' Prim_pair (rightCombedPairToList t) [] Untyped.TOr fa1 fa2 l r -> let exprL = addTrimmedAnns (toExpression l) [] [fa1] [] exprR = addTrimmedAnns (toExpression r) [] [fa2] [] in expressionPrim' Prim_or [exprL, exprR] [] Untyped.TLambda inp out -> expressionPrim' Prim_lambda [toExpression inp, toExpression out] [] Untyped.TMap k v -> expressionPrim' Prim_map [toExpression k, toExpression v] [] Untyped.TBigMap k v -> expressionPrim' Prim_big_map [toExpression k, toExpression v] [] Untyped.TInt -> expressionPrim' Prim_int [] [] Untyped.TNat -> expressionPrim' Prim_nat [] [] Untyped.TString -> expressionPrim' Prim_string [] [] Untyped.TBytes -> expressionPrim' Prim_bytes [] [] Untyped.TMutez -> expressionPrim' Prim_mutez [] [] Untyped.TBool -> expressionPrim' Prim_bool [] [] Untyped.TKeyHash -> expressionPrim' Prim_key_hash [] [] Untyped.TBls12381Fr -> expressionPrim' Prim_bls12_381_fr [] [] Untyped.TBls12381G1 -> expressionPrim' Prim_bls12_381_g1 [] [] Untyped.TBls12381G2 -> expressionPrim' Prim_bls12_381_g2 [] [] Untyped.TTimestamp -> expressionPrim' Prim_timestamp [] [] Untyped.TAddress -> expressionPrim' Prim_address [] [] Untyped.TChest -> expressionPrim' Prim_chest [] [] Untyped.TChestKey -> expressionPrim' Prim_chest_key [] [] Untyped.TNever -> expressionPrim' Prim_never [] [] Untyped.TSaplingState n -> expressionPrim' Prim_sapling_state [integralToExpr n] [] Untyped.TSaplingTransaction n -> expressionPrim' Prim_sapling_transaction [integralToExpr n] [] where addAnns :: Expression -> [Annotation] -> Expression addAnns e anns = e & _ExpressionPrim . mpaAnnotsL <>~ anns rightCombedPairToList :: Untyped.T -> [Expression] rightCombedPairToList t = let go ty (fa, va) | Ty (Untyped.TPair faL faR vaL vaR l r) ta <- ty , ta == noAnn, fa == noAnn, va == noAnn = toExpression l `addAnns` mkAnns [] [faL] [vaL] : go r (faR, vaR) | otherwise = one $ toExpression ty `addAnns` mkAnns [] [fa] [va] in go (Ty t noAnn) (noAnn, noAnn) instance ToExpression Ty where toExpression (Ty t ta) = addTrimmedAnns (toExpression t) [ta] [] [] instance (ToExpression a) => ToExpression [a] where toExpression xs = expressionSeq $ toExpression <$> xs instance (ToExpression a) => ToExpression (NonEmpty a) where toExpression = toExpression . toList instance ToExpression Expression where toExpression = id instance ToExpression ExpandedOp where toExpression = \case PrimEx instr -> toExpression instr SeqEx s -> expressionSeq $ toExpression <$> s WithSrcEx _ op -> toExpression op instance ToExpression ViewName where toExpression (ViewName s) = expressionString s -- Note: On adding new expressions here, you must also add the symmetric (reading) case -- to FromExp x (InstrAbstract [] op) as well. instance ToExpression ExpandedInstr where toExpression = \case PUSH va ty v -> expressionPrim' Prim_PUSH [toExpression ty, toExpression v] $ mkAnns [] [] [va] DROP -> expressionPrim' Prim_DROP [] [] DROPN n -> expressionPrim' Prim_DROP [integralToExpr n] [] DUP va -> expressionPrim' Prim_DUP [] $ mkAnns [] [] [va] DUPN va n -> expressionPrim' Prim_DUP [integralToExpr n] $ mkAnns [] [] [va] SWAP -> expressionPrim' Prim_SWAP [] [] DIG n -> expressionPrim' Prim_DIG [integralToExpr n] [] DUG n -> expressionPrim' Prim_DUG [integralToExpr n] [] SOME ta va -> expressionPrim' Prim_SOME [] $ mkAnns [ta] [] [va] NONE ta va ty -> expressionPrim' Prim_NONE [toExpression ty] $ mkAnns [ta] [] [va] UNIT ta va -> expressionPrim' Prim_UNIT [] $ mkAnns [ta] [] [va] IF_NONE ops1 ops2 -> expressionPrim' Prim_IF_NONE [toExpression ops1, toExpression ops2] [] PAIR ta va fa1 fa2 -> expressionPrim' Prim_PAIR [] $ mkAnns [ta] [fa1, fa2] [va] UNPAIR va1 va2 fa1 fa2 -> expressionPrim' Prim_UNPAIR [] $ mkAnns [] [fa1, fa2] [va1, va2] PAIRN va n -> expressionPrim' Prim_PAIR [integralToExpr n] $ mkAnns [] [] [va] UNPAIRN n -> expressionPrim' Prim_UNPAIR [integralToExpr n] [] CAR va fa -> expressionPrim' Prim_CAR [] $ mkAnns [] [fa] [va] CDR va fa -> expressionPrim' Prim_CDR [] $ mkAnns [] [fa] [va] LEFT ta va fa1 fa2 ty -> expressionPrim' Prim_LEFT [toExpression ty] $ mkAnns [ta] [fa1, fa2] [va] RIGHT ta va fa1 fa2 ty -> expressionPrim' Prim_RIGHT [toExpression ty] $ mkAnns [ta] [fa1, fa2] [va] IF_LEFT ops1 ops2 -> expressionPrim' Prim_IF_LEFT [toExpression ops1, toExpression ops2] [] NIL ta va ty -> expressionPrim' Prim_NIL [toExpression ty] $ mkAnns [ta] [] [va] CONS va -> expressionPrim' Prim_CONS [] $ mkAnns [] [] [va] IF_CONS ops1 ops2 -> expressionPrim' Prim_IF_CONS [toExpression ops1, toExpression ops2] [] SIZE va -> expressionPrim' Prim_SIZE [] $ mkAnns [] [] [va] EMPTY_SET ta va ty -> expressionPrim' Prim_EMPTY_SET [toExpression ty] $ mkAnns [ta] [] [va] EMPTY_MAP ta va kty vty -> expressionPrim' Prim_EMPTY_MAP [toExpression kty, toExpression vty] $ mkAnns [ta] [] [va] EMPTY_BIG_MAP ta va kty vty -> expressionPrim' Prim_EMPTY_BIG_MAP [toExpression kty, toExpression vty] $ mkAnns [ta] [] [va] MAP va ops -> expressionPrim' Prim_MAP [toExpression ops] $ mkAnns [] [] [va] ITER ops -> expressionPrim' Prim_ITER [toExpression ops] [] MEM va -> expressionPrim' Prim_MEM [] $ mkAnns [] [] [va] GET va -> expressionPrim' Prim_GET [] $ mkAnns [] [] [va] GETN va n -> expressionPrim' Prim_GET [integralToExpr n] $ mkAnns [] [] [va] UPDATE va -> expressionPrim' Prim_UPDATE [] $ mkAnns [] [] [va] UPDATEN va n -> expressionPrim' Prim_UPDATE [integralToExpr n] $ mkAnns [] [] [va] GET_AND_UPDATE va -> expressionPrim' Prim_GET_AND_UPDATE [] $ mkAnns [] [] [va] IF ops1 ops2 -> expressionPrim' Prim_IF [toExpression ops1, toExpression ops2] [] LOOP ops -> expressionPrim' Prim_LOOP [toExpression ops] [] LOOP_LEFT ops -> expressionPrim' Prim_LOOP_LEFT [toExpression ops] [] LAMBDA va tyin tyout ops -> expressionPrim' Prim_LAMBDA [ toExpression tyin , toExpression tyout , toExpression ops ] $ mkAnns [] [] [va] LAMBDA_REC va tyin tyout ops -> expressionPrim' Prim_LAMBDA_REC [ toExpression tyin , toExpression tyout , toExpression ops ] $ mkAnns [] [] [va] EXEC va -> expressionPrim' Prim_EXEC [] $ mkAnns [] [] [va] APPLY va -> expressionPrim' Prim_APPLY [] $ mkAnns [] [] [va] DIP ops -> expressionPrim' Prim_DIP [toExpression ops] [] DIPN n ops -> expressionPrim' Prim_DIP [integralToExpr n, toExpression ops] [] FAILWITH -> expressionPrim' Prim_FAILWITH [] [] CAST va ty -> expressionPrim' Prim_CAST [toExpression ty] $ mkAnns [] [] [va] RENAME va -> expressionPrim' Prim_RENAME [] $ mkAnns [] [] [va] PACK va -> expressionPrim' Prim_PACK [] $ mkAnns [] [] [va] UNPACK ta va ty -> expressionPrim' Prim_UNPACK [toExpression ty] $ mkAnns [ta] [] [va] CONCAT va -> expressionPrim' Prim_CONCAT [] $ mkAnns [] [] [va] SLICE va -> expressionPrim' Prim_SLICE [] $ mkAnns [] [] [va] ISNAT va -> expressionPrim' Prim_ISNAT [] $ mkAnns [] [] [va] ADD va -> expressionPrim' Prim_ADD [] $ mkAnns [] [] [va] SUB va -> expressionPrim' Prim_SUB [] $ mkAnns [] [] [va] SUB_MUTEZ va -> expressionPrim' Prim_SUB_MUTEZ [] $ mkAnns [] [] [va] MUL va -> expressionPrim' Prim_MUL [] $ mkAnns [] [] [va] EDIV va -> expressionPrim' Prim_EDIV [] $ mkAnns [] [] [va] ABS va -> expressionPrim' Prim_ABS [] $ mkAnns [] [] [va] NEG va -> expressionPrim' Prim_NEG [] $ mkAnns [] [] [va] LSL va -> expressionPrim' Prim_LSL [] $ mkAnns [] [] [va] LSR va -> expressionPrim' Prim_LSR [] $ mkAnns [] [] [va] OR va -> expressionPrim' Prim_OR [] $ mkAnns [] [] [va] AND va -> expressionPrim' Prim_AND [] $ mkAnns [] [] [va] XOR va -> expressionPrim' Prim_XOR [] $ mkAnns [] [] [va] NOT va -> expressionPrim' Prim_NOT [] $ mkAnns [] [] [va] COMPARE va -> expressionPrim' Prim_COMPARE [] $ mkAnns [] [] [va] Untyped.EQ va -> expressionPrim' Prim_EQ [] $ mkAnns [] [] [va] NEQ va -> expressionPrim' Prim_NEQ [] $ mkAnns [] [] [va] Untyped.LT va -> expressionPrim' Prim_LT [] $ mkAnns [] [] [va] Untyped.GT va -> expressionPrim' Prim_GT [] $ mkAnns [] [] [va] LE va -> expressionPrim' Prim_LE [] $ mkAnns [] [] [va] GE va -> expressionPrim' Prim_GE [] $ mkAnns [] [] [va] INT va -> expressionPrim' Prim_INT [] $ mkAnns [] [] [va] NAT va -> expressionPrim' Prim_NAT [] $ mkAnns [] [] [va] BYTES va -> expressionPrim' Prim_BYTES [] $ mkAnns [] [] [va] VIEW va n t -> expressionPrim' Prim_VIEW [toExpression n, toExpression t] $ mkAnns [] [] [va] SELF va fa -> expressionPrim' Prim_SELF [] $ mkAnns [] [fa] [va] CONTRACT va fa ty -> expressionPrim' Prim_CONTRACT [toExpression ty] $ mkAnns [] [fa] [va] TRANSFER_TOKENS va -> expressionPrim' Prim_TRANSFER_TOKENS [] $ mkAnns [] [] [va] SET_DELEGATE va -> expressionPrim' Prim_SET_DELEGATE [] $ mkAnns [] [] [va] CREATE_CONTRACT va1 va2 c -> expressionPrim' Prim_CREATE_CONTRACT [toExpression c] $ mkAnns [] [] [va1, va2] IMPLICIT_ACCOUNT va -> expressionPrim' Prim_IMPLICIT_ACCOUNT [] $ mkAnns [] [] [va] NOW va -> expressionPrim' Prim_NOW [] $ mkAnns [] [] [va] AMOUNT va -> expressionPrim' Prim_AMOUNT [] $ mkAnns [] [] [va] BALANCE va -> expressionPrim' Prim_BALANCE [] $ mkAnns [] [] [va] VOTING_POWER va -> expressionPrim' Prim_VOTING_POWER [] $ mkAnns [] [] [va] TOTAL_VOTING_POWER va -> expressionPrim' Prim_TOTAL_VOTING_POWER [] $ mkAnns [] [] [va] CHECK_SIGNATURE va -> expressionPrim' Prim_CHECK_SIGNATURE [] $ mkAnns [] [] [va] SHA256 va -> expressionPrim' Prim_SHA256 [] $ mkAnns [] [] [va] SHA512 va -> expressionPrim' Prim_SHA512 [] $ mkAnns [] [] [va] BLAKE2B va -> expressionPrim' Prim_BLAKE2B [] $ mkAnns [] [] [va] SHA3 va -> expressionPrim' Prim_SHA3 [] $ mkAnns [] [] [va] KECCAK va -> expressionPrim' Prim_KECCAK [] $ mkAnns [] [] [va] HASH_KEY va -> expressionPrim' Prim_HASH_KEY [] $ mkAnns [] [] [va] PAIRING_CHECK va -> expressionPrim' Prim_PAIRING_CHECK [] $ mkAnns [] [] [va] SOURCE va -> expressionPrim' Prim_SOURCE [] $ mkAnns [] [] [va] SENDER va -> expressionPrim' Prim_SENDER [] $ mkAnns [] [] [va] ADDRESS va -> expressionPrim' Prim_ADDRESS [] $ mkAnns [] [] [va] CHAIN_ID va -> expressionPrim' Prim_CHAIN_ID [] $ mkAnns [] [] [va] LEVEL va -> expressionPrim' Prim_LEVEL [] $ mkAnns [] [] [va] SELF_ADDRESS va -> expressionPrim' Prim_SELF_ADDRESS [] $ mkAnns [] [] [va] TICKET va -> expressionPrim' Prim_TICKET [] $ mkAnns [] [] [va] TICKET_DEPRECATED va -> expressionPrim' Prim_TICKET_DEPRECATED [] $ mkAnns [] [] [va] READ_TICKET va -> expressionPrim' Prim_READ_TICKET [] $ mkAnns [] [] [va] SPLIT_TICKET va -> expressionPrim' Prim_SPLIT_TICKET [] $ mkAnns [] [] [va] JOIN_TICKETS va -> expressionPrim' Prim_JOIN_TICKETS [] $ mkAnns [] [] [va] OPEN_CHEST va -> expressionPrim' Prim_OPEN_CHEST [] $ mkAnns [] [] [va] NEVER -> expressionPrim' Prim_NEVER [] [] EXT _ -> expressionSeq [] SAPLING_EMPTY_STATE va n -> expressionPrim' Prim_SAPLING_EMPTY_STATE [integralToExpr n] $ mkAnns [] [] [va] SAPLING_VERIFY_UPDATE va -> expressionPrim' Prim_SAPLING_VERIFY_UPDATE [] $ mkAnns [] [] [va] MIN_BLOCK_TIME va -> expressionPrim' Prim_MIN_BLOCK_TIME [] $ mkAnnsFromAny va EMIT va tag ty -> expressionPrim' Prim_EMIT (toExpression <$> maybeToList ty) $ mkAnns [] [tag] [va] instance ToExpression Untyped.Contract where toExpression contract = expressionSeq $ Untyped.mapEntriesOrdered contract (\(Untyped.ParameterType ty rootAnn) -> expressionPrim' Prim_parameter [insertRootAnn (toExpression ty) rootAnn] []) (\storage -> expressionPrim' Prim_storage [toExpression storage] []) (\code -> expressionPrim' Prim_code [toExpression code] []) (\Untyped.View{..} -> expressionPrim' Prim_view [toExpression viewName, toExpression viewArgument, toExpression viewReturn, toExpression viewCode] [] ) instance ToExpression (Contract cp st) where toExpression = toExpression . convertContractOptimized -- FromExpression ---------------------------------------------------------------------------- -- | Errors that can happen when we convert an 'Exp' to our -- data type. data FromExpError x = FromExpError (Exp x) FromExpErrorReason deriving stock instance Show (Exp x) => Show (FromExpError x) deriving stock instance Eq (Exp x) => Eq (FromExpError x) data FromExpErrorReason = FEERMTextDecodingFailure Text | FEERTcError T TcError | FEERNotEnoughArguments Word Int | FEERUnexpectedPrim (Maybe MichelinePrimitive) MichelinePrimitive | FEERUnexpectedPrimClass MichelinePrimitiveTag MichelinePrimitiveTag | FEERArgumentCountMismatch (NonEmpty Word) Int | FEERUnexpectedAnnotations | FEERExpectedPrim (NonEmpty MichelinePrimitiveTag) | FEERExpectedSeq | FEERUnsupported | FEERDeprecated | FEERTooManyAnns (Maybe Int, Maybe Int, Maybe Int) (Int, Int, Int) | FEEROutOfBounds | FEERExpectedNumber | FEERExpectedString | FEERViewNameError BadViewNameError | FEERBadContractBlocks (NonEmpty ContractBlockError) deriving stock (Show, Eq) instance Buildable FromExpErrorReason where build = \case FEERMTextDecodingFailure msg -> nameF "Text decoding failure" $ build msg FEERTcError ty err -> unlinesF [ nameF "Failed to typecheck expression as a value of type" $ build ty , mempty , nameF "Typechecker error" $ build err ] FEERNotEnoughArguments minArgs numArgs -> "Expected at least" ++| minArgs |++ "arguments, but got" ++| numArgs |++ "" FEERUnexpectedPrim expected got -> nameF "Unexpected primitive" $ case expected of Nothing -> build got Just e -> build MkMismatchError { meExpected = e, meActual = got } FEERUnexpectedPrimClass expected got -> nameF "Unexpected primitive class" $ build $ MkMismatchError { meExpected = expected, meActual = got } FEERArgumentCountMismatch expected got -> fillSepF $ "Expected exactly" : punctuateF "," " or" expected <> [ "arguments, but got" , build got ] FEERUnexpectedAnnotations -> "Unexpected annotations" FEERExpectedPrim prims -> fillSepF $ "Expected primitive" : punctuateF "," " or" prims FEERExpectedSeq -> "Expected sequence" FEERTooManyAnns (maxTas, maxFas, maxVas) (tasCnt, fasCnt, vasCnt) -> unlinesF [ nameF "Expected at most" $ unlinesF $ catMaybes [ (<> " type annotations") . build <$> maxTas , (<> " field annotations") . build <$> maxFas , (<> " variable annotations") . build <$> maxVas ] , nameF "but found" $ unlinesF $ catMaybes [ (<> " type annotations") . build <$> (maxTas $> tasCnt) , (<> " field annotations") . build <$> (maxFas $> fasCnt) , (<> " variable annotations") . build <$> (maxVas $> vasCnt) ] ] FEEROutOfBounds -> "Value is out of bounds" FEERBadContractBlocks cbes -> unlinesF $ build <$> cbes FEERUnsupported -> "Unsupported" FEERDeprecated -> "Deprecated primitive" FEERExpectedNumber -> "Expected number" FEERExpectedString -> "Expected string" FEERViewNameError err -> nameF "View name error" $ build err -- | Error in case of vanilla expression. type FromExpressionError = FromExpError RegularExp instance Buildable FromExpressionError where build (FromExpError expr err) = unlinesF [ "Failed to convert expression:" , indentF 2 $ build expr , "" , "Error:" , indentF 2 $ build err ] instance Exception FromExpressionError where displayException = pretty -- | Type class that provides the ability to convert -- something from a Micheline Expression. class FromExp x a where fromExp :: Exp x -> Either (FromExpError x) a type FromExpression = FromExp RegularExp -- | Parse vanilla expression to something. fromExpression :: FromExp RegularExp a => Expression -> Either FromExpressionError a fromExpression = fromExp instance (FromExp x Untyped.Value, SingI t) => FromExp x (Value t) where fromExp expr = case fromExp @_ @Untyped.Value expr of Right uv -> case typeCheck uv of Left tcErr -> Left $ FromExpError expr $ FEERTcError (demote @t) tcErr Right tv -> Right tv Left e -> Left e where typeCheck uv = typeCheckingWith (TypeCheckOptions False False) $ typeCheckValue uv instance FromExp x op => FromExp x (Untyped.Value' [] op) where fromExp e = case e of ExpInt _ v -> pure $ Untyped.ValueInt v ExpString _ s -> first (FromExpError e . FEERMTextDecodingFailure) (Untyped.ValueString <$> mkMText s) ExpBytes _ bs -> pure $ Untyped.ValueBytes $ Untyped.InternalByteString bs ExpPrim _ (MichelinePrimAp prim args _) -> withClassifiedPrim prim \case SMPTValue -> \case C_Prim_Unit -> withArgs Untyped.ValueUnit C_Prim_True -> withArgs Untyped.ValueTrue C_Prim_False -> withArgs Untyped.ValueFalse C_Prim_Pair -> case nonEmpty args >>= forbidSingletonList of Nothing -> Left $ FromExpError e $ FEERNotEnoughArguments 2 (length args) Just args' -> do tys <- mapM fromExp args' return $ foldr1 Untyped.ValuePair tys C_Prim_Left -> withArgs Untyped.ValueLeft C_Prim_Right -> withArgs Untyped.ValueRight C_Prim_Some -> withArgs Untyped.ValueSome C_Prim_None -> withArgs Untyped.ValueNone C_Prim_Lambda_rec -> withArgs Untyped.ValueLamRec C_Prim_Elt -> Left $ FromExpError e $ FEERUnexpectedPrim Nothing Prim_Elt s -> const $ Left $ FromExpError e $ FEERUnexpectedPrimClass MPTValue (fromSing s) where withArgs :: WithArgsRec x a r => a -> Either (FromExpError x) r withArgs = withArgsRec e args ExpSeq _ [] -> pure Untyped.ValueNil ExpSeq _ (h : t) -> case fromExp @x @op h of Right op -> do ops <- traverse (fromExp @x @op) t pure . Untyped.ValueLambda $ op : ops Left _ -> case exprToElt h of Right elt -> do elts <- traverse exprToElt t pure . Untyped.ValueMap $ elt :| elts Left _ -> case fromExp h of Left (FromExpError err _) -> Left $ FromExpError err $ FEERExpectedPrim (MPTValue :| [MPTInstr]) Right h' -> do t' <- traverse fromExp t pure . Untyped.ValueSeq $ h' :| t' ExpX _ -> Left $ FromExpError e $ FEERExpectedPrim (one MPTValue) where exprToElt :: Exp x -> Either (FromExpError x) (Untyped.Elt [] op) exprToElt ex = case ex of ExpPrim' _ Prim_Elt [l, r] [] -> do l' <- fromExp l r' <- fromExp r pure $ Untyped.Elt l' r' ExpPrim' _ Prim_Elt args' [] -> Left $ FromExpError ex $ FEERArgumentCountMismatch (one 2) (length args') ExpPrim' _ Prim_Elt _ _ -> Left $ FromExpError ex FEERUnexpectedAnnotations ExpPrim' _ prim _ _ -> Left $ FromExpError ex $ FEERUnexpectedPrim (Just Prim_Elt) prim _ -> Left $ FromExpError ex $ FEERExpectedPrim $ one MPTValue instance FromExp x a => FromExp x [a] where fromExp = \case ExpSeq _ exprs -> traverse fromExp exprs e -> Left $ FromExpError e FEERExpectedSeq instance FromExp RegularExp ExpandedOp where fromExp = \case ExpSeq _ s -> SeqEx <$> traverse fromExp s e -> PrimEx <$> fromExp e instance FromExp x Word where fromExp = integralFromExpr instance FromExp x Natural where fromExp = integralFromExpr -- | Used to improve type inference with 'WithArgsRec'. type family EndResult a where EndResult (_ -> b) = EndResult b EndResult b = b -- | Recursive typeclass that unpacks a list of constructor arguments and -- applies a function to them. class (EndResult a ~ r, CountArgs a r) => WithArgsRec x a r where -- | Given the 'Exp' itself (used for error reporting), its arguments, and a -- function constructing the result form the arguments, produce the result or -- an error if the argument count is mismatched. withArgsRec' :: FromExpError x -> [Exp x] -> a -> Either (FromExpError x) r instance (FromExp x a, WithArgsRec x b r) => WithArgsRec x (a -> b) r where withArgsRec' ee (e:es) f = fromExp @x e >>= withArgsRec' ee es . f withArgsRec' ee [] _ = Left ee instance (EndResult r ~ r) => WithArgsRec x r r where withArgsRec' _ [] x = pure x withArgsRec' ee _ _ = Left ee class CountArgs a r where countArgs :: Word instance CountArgs r r where countArgs = 0 instance CountArgs b r => CountArgs (a -> b) r where countArgs = succ $ countArgs @b @r withArgsRec :: forall x a r. WithArgsRec x a r => Exp x -> [Exp x] -> a -> Either (FromExpError x) r withArgsRec e args = withArgsRec' (FromExpError e $ FEERArgumentCountMismatch (one $ countArgs @a @r) (length args)) args -- Note: On adding new expressions here, you must also add the symmetric (writing) case -- to ToExpression as well. instance FromExp x op => FromExp x (InstrAbstract [] op) where fromExp e@(ExpPrim' _ prim args anns) = withClassifiedPrim prim \case SMPTInstr -> \case C_Prim_DROP -> nilAnns *> case args of [] -> pure DROP [n] -> DROPN <$> integralFromExpr n _ -> invalidNumArgs $ 0 :| [1] C_Prim_DUP -> annsCnt (0, 0, 1) *> case args of [n] -> DUPN va <$> integralFromExpr n [] -> pure $ DUP va _ -> invalidNumArgs $ 0 :| [1] C_Prim_SWAP -> withArgsAnns nil SWAP C_Prim_DIG -> withArgsAnns nil DIG C_Prim_DUG -> withArgsAnns nil DUG C_Prim_PUSH -> withArgsAnns (0, 0, 1) $ PUSH va C_Prim_SOME -> withArgsAnns (1, 0, 1) $ SOME ta va C_Prim_NONE -> withArgsAnns (1, 0, 1) $ NONE ta va C_Prim_UNIT -> withArgsAnns (1, 0, 1) $ UNIT ta va C_Prim_IF_NONE -> withArgsAnns nil IF_NONE C_Prim_PAIR -> case args of [] -> annsCnt (1, 2, 1) $> PAIR ta va fa1 fa2 [n] -> annsCnt (0, 0, 1) $> PAIRN va <*> integralFromExpr n _ -> invalidNumArgs $ 0 :| [1] C_Prim_UNPAIR -> case args of [] -> annsCnt (0, 2, 2) $> UNPAIR va1 va2 fa1 fa2 [n] -> nilAnns $> UNPAIRN <*> integralFromExpr n _ -> invalidNumArgs $ 0 :| [1] C_Prim_CAR -> withArgsAnns (0, 1, 1) $ CAR va fa C_Prim_CDR -> withArgsAnns (0, 1, 1) $ CDR va fa C_Prim_LEFT -> withArgsAnns (1, 2, 1) $ LEFT ta va fa1 fa2 C_Prim_RIGHT -> withArgsAnns (1, 2, 1) $ RIGHT ta va fa1 fa2 C_Prim_IF_LEFT -> withArgsAnns nil IF_LEFT C_Prim_NIL -> withArgsAnns (1, 0, 1) $ NIL ta va C_Prim_CONS -> mkInstrWithVarAnn CONS C_Prim_IF_CONS -> withArgsAnns nil IF_CONS C_Prim_SIZE -> mkInstrWithVarAnn SIZE C_Prim_EMPTY_SET -> withArgsAnns (1, 0, 1) $ EMPTY_SET ta va C_Prim_EMPTY_MAP -> withArgsAnns (1, 0, 1) $ EMPTY_MAP ta va C_Prim_EMPTY_BIG_MAP -> withArgsAnns (1, 0, 1) $ EMPTY_BIG_MAP ta va C_Prim_MAP -> withArgsAnns (0, 0, 1) $ MAP va C_Prim_ITER -> withArgsAnns nil ITER C_Prim_MEM -> mkInstrWithVarAnn MEM C_Prim_GET -> case args of [] -> mkInstrWithVarAnn GET [n] -> annsCnt (0, 0, 1) $> GETN va <*> integralFromExpr n _ -> invalidNumArgs $ 0 :| [1] C_Prim_UPDATE -> case args of [] -> mkInstrWithVarAnn UPDATE [n] -> annsCnt (0, 0, 1) $> UPDATEN va <*> integralFromExpr n _ -> invalidNumArgs $ 0 :| [1] C_Prim_GET_AND_UPDATE -> mkInstrWithVarAnn GET_AND_UPDATE C_Prim_IF -> withArgsAnns nil IF C_Prim_LOOP -> withArgsAnns nil LOOP C_Prim_LOOP_LEFT -> withArgsAnns nil LOOP_LEFT C_Prim_LAMBDA -> withArgsAnns (0, 0, 1) $ LAMBDA va C_Prim_LAMBDA_REC -> withArgsAnns (0, 0, 1) $ LAMBDA_REC va C_Prim_EXEC -> mkInstrWithVarAnn EXEC C_Prim_APPLY -> mkInstrWithVarAnn APPLY C_Prim_DIP -> nilAnns *> case args of [ops] -> DIP <$> fromExp ops [n, ops] -> DIPN <$> fromExp n <*> fromExp ops _ -> invalidNumArgs $ 1 :| [2] C_Prim_FAILWITH -> withArgsAnns nil FAILWITH C_Prim_CAST -> withArgsAnns (0, 0, 1) $ CAST va C_Prim_RENAME -> mkInstrWithVarAnn RENAME C_Prim_PACK -> mkInstrWithVarAnn PACK C_Prim_UNPACK -> withArgsAnns (1, 0, 1) $ UNPACK ta va C_Prim_CONCAT -> mkInstrWithVarAnn CONCAT C_Prim_SLICE -> mkInstrWithVarAnn SLICE C_Prim_ISNAT -> mkInstrWithVarAnn ISNAT C_Prim_ADD -> mkInstrWithVarAnn ADD C_Prim_SUB -> mkInstrWithVarAnn SUB C_Prim_SUB_MUTEZ -> mkInstrWithVarAnn SUB_MUTEZ C_Prim_MUL -> mkInstrWithVarAnn MUL C_Prim_EDIV -> mkInstrWithVarAnn EDIV C_Prim_ABS -> mkInstrWithVarAnn ABS C_Prim_NEG -> mkInstrWithVarAnn NEG C_Prim_LSL -> mkInstrWithVarAnn LSL C_Prim_LSR -> mkInstrWithVarAnn LSR C_Prim_OR -> mkInstrWithVarAnn OR C_Prim_AND -> mkInstrWithVarAnn AND C_Prim_XOR -> mkInstrWithVarAnn XOR C_Prim_NOT -> mkInstrWithVarAnn NOT C_Prim_COMPARE -> mkInstrWithVarAnn COMPARE C_Prim_EQ -> mkInstrWithVarAnn Untyped.EQ C_Prim_NEQ -> mkInstrWithVarAnn NEQ C_Prim_LT -> mkInstrWithVarAnn Untyped.LT C_Prim_GT -> mkInstrWithVarAnn Untyped.GT C_Prim_LE -> mkInstrWithVarAnn LE C_Prim_GE -> mkInstrWithVarAnn GE C_Prim_INT -> mkInstrWithVarAnn INT C_Prim_NAT -> mkInstrWithVarAnn NAT C_Prim_BYTES -> mkInstrWithVarAnn BYTES C_Prim_VIEW -> withArgsAnns (0, 0, 1) $ VIEW va C_Prim_SELF -> withArgsAnns (0, 1, 1) $ SELF va fa C_Prim_CONTRACT -> withArgsAnns (0, 1, 1) $ CONTRACT va fa C_Prim_TRANSFER_TOKENS -> mkInstrWithVarAnn TRANSFER_TOKENS C_Prim_SET_DELEGATE -> mkInstrWithVarAnn SET_DELEGATE C_Prim_CREATE_CONTRACT -> withArgsAnns (0, 0, 2) $ CREATE_CONTRACT va1 va2 C_Prim_IMPLICIT_ACCOUNT -> mkInstrWithVarAnn IMPLICIT_ACCOUNT C_Prim_NOW -> mkInstrWithVarAnn NOW C_Prim_AMOUNT -> mkInstrWithVarAnn AMOUNT C_Prim_BALANCE -> mkInstrWithVarAnn BALANCE C_Prim_VOTING_POWER -> mkInstrWithVarAnn VOTING_POWER C_Prim_TOTAL_VOTING_POWER -> mkInstrWithVarAnn TOTAL_VOTING_POWER C_Prim_CHECK_SIGNATURE -> mkInstrWithVarAnn CHECK_SIGNATURE C_Prim_SHA256 -> mkInstrWithVarAnn SHA256 C_Prim_SHA512 -> mkInstrWithVarAnn SHA512 C_Prim_BLAKE2B -> mkInstrWithVarAnn BLAKE2B C_Prim_SHA3 -> mkInstrWithVarAnn SHA3 C_Prim_KECCAK -> mkInstrWithVarAnn KECCAK C_Prim_HASH_KEY -> mkInstrWithVarAnn HASH_KEY C_Prim_PAIRING_CHECK -> mkInstrWithVarAnn PAIRING_CHECK C_Prim_SOURCE -> mkInstrWithVarAnn SOURCE C_Prim_SENDER -> mkInstrWithVarAnn SENDER C_Prim_ADDRESS -> mkInstrWithVarAnn ADDRESS C_Prim_CHAIN_ID -> mkInstrWithVarAnn CHAIN_ID C_Prim_LEVEL -> mkInstrWithVarAnn LEVEL C_Prim_SELF_ADDRESS -> mkInstrWithVarAnn SELF_ADDRESS C_Prim_NEVER -> withArgsAnns nil NEVER C_Prim_TICKET_DEPRECATED -> mkInstrWithVarAnn TICKET_DEPRECATED C_Prim_TICKET -> mkInstrWithVarAnn TICKET C_Prim_READ_TICKET -> mkInstrWithVarAnn READ_TICKET C_Prim_SPLIT_TICKET -> mkInstrWithVarAnn SPLIT_TICKET C_Prim_JOIN_TICKETS -> mkInstrWithVarAnn JOIN_TICKETS C_Prim_OPEN_CHEST -> mkInstrWithVarAnn OPEN_CHEST C_Prim_SAPLING_EMPTY_STATE -> withArgsAnns (0, 0, 1) $ SAPLING_EMPTY_STATE va C_Prim_SAPLING_VERIFY_UPDATE -> mkInstrWithVarAnn SAPLING_VERIFY_UPDATE C_Prim_MIN_BLOCK_TIME -> withArgsRec e args $ mkInstrWithAnyAnns MIN_BLOCK_TIME C_Prim_EMIT -> annsCnt (0, 1, 1) $> EMIT va fa <*> case args of [] -> pure Nothing [ty] -> Just <$> fromExp ty _ -> invalidNumArgs (0 :| [1]) s -> const $ Left $ FromExpError e $ FEERUnexpectedPrimClass MPTInstr (fromSing s) where annSet = toAnnSet anns va = firstAnn @VarTag annSet ta = firstAnn @TypeTag annSet fa = firstAnn @FieldTag annSet fa1 = fa fa2 = secondAnn @FieldTag annSet va1 = va va2 = secondAnn @VarTag annSet withArgsAnns :: WithArgsRec x a (InstrAbstract [] op) => (Int, Int, Int) -> a -> Either (FromExpError x) (InstrAbstract [] op) withArgsAnns n f = annsCnt n *> withArgsRec e args f invalidNumArgs :: NonEmpty Word -> Either (FromExpError x) b invalidNumArgs expected = Left $ FromExpError e $ FEERArgumentCountMismatch expected (length args) annsCnt = checkAnnsCount e annSet nil = (0, 0, 0) nilAnns = annsCnt nil mkInstrWithVarAnn :: WithArgsRec x a (InstrAbstract [] op) => (Untyped.Annotation VarTag -> a) -> Either (FromExpError x) (InstrAbstract [] op) mkInstrWithVarAnn ctor = withArgsAnns (0, 0, 1) $ ctor va mkInstrWithAnyAnns :: ([Untyped.AnyAnn] -> t) -> t mkInstrWithAnyAnns ctor = ctor $ anns <&> \case AnnotationType x -> Untyped.AnyAnnType x AnnotationField x -> Untyped.AnyAnnField x AnnotationVariable x -> Untyped.AnyAnnVar x fromExp e = Left $ FromExpError e $ FEERExpectedPrim (one MPTInstr) instance (FromExp x op) => FromExp x (Untyped.Contract' op) where fromExp blocks = case blocks of ExpSeq _ bs -> do bs' <- mapM exprToCB bs first (FromExpError blocks . FEERBadContractBlocks) $ orderContractBlock bs' expr -> Left $ FromExpError expr FEERExpectedSeq where exprToCB :: Exp x -> Either (FromExpError x) (ContractBlock op) exprToCB e@(ExpPrim' _ prim args anns) = withClassifiedPrim prim \case SMPTKeyword -> \case C_Prim_parameter -> mkCbParam e args anns C_Prim_storage -> mkCBStorage e args anns C_Prim_code -> mkCBCode e args anns C_Prim_view -> mkCBView e args anns s -> const $ Left $ FromExpError e $ FEERUnexpectedPrimClass MPTKeyword (fromSing s) exprToCB e = Left $ FromExpError e $ FEERExpectedPrim $ one MPTKeyword mkCbParam :: Exp x -> [Exp x] -> [Annotation] -> Either (FromExpError x) (ContractBlock op) mkCbParam e args anns = checkAnnsCount e (toAnnSet anns) (0, 0, 0) *> case args of [p] -> do let annSet = toAnnSet (p ^. _ExpPrim . _2 . mpaAnnotsL) annCnt = annsCount annSet let rootAnn = firstAnn @FieldTag annSet unless (secondAnn @FieldTag annSet == noAnn) $ Left $ FromExpError p $ FEERTooManyAnns (Nothing, Just 1, Nothing) annCnt p' <- fromExp @x @Ty (p & _ExpPrim . _2 . mpaAnnotsL %~ filter (not . isAnnotationField)) pure $ CBParam $ Untyped.ParameterType p' rootAnn _ -> Left $ FromExpError e $ FEERArgumentCountMismatch (one 1) (length args) mkCBStorage :: Exp x -> [Exp x] -> [Annotation] -> Either (FromExpError x) (ContractBlock op) mkCBStorage e args anns = checkAnnsCount e (toAnnSet anns) (0, 0, 0) *> withArgsRec e args CBStorage mkCBCode :: Exp x -> [Exp x] -> [Annotation] -> Either (FromExpError x) (ContractBlock op) mkCBCode e args anns = checkAnnsCount e (toAnnSet anns) (0, 0, 0) *> withArgsRec e args CBCode mkCBView :: Exp x -> [Exp x] -> [Annotation] -> Either (FromExpError x) (ContractBlock op) mkCBView e args anns = checkAnnsCount e (toAnnSet anns) (0, 0, 0) *> withArgsRec e args (CBView ... Untyped.View) instance FromExp x Untyped.T where fromExp e@(ExpPrim' _ prim args anns) = assertNoAnns *> withClassifiedPrim prim \case SMPTType -> \case C_Prim_key -> withArgs Untyped.TKey C_Prim_unit -> withArgs Untyped.TUnit C_Prim_signature -> withArgs Untyped.TSignature C_Prim_chain_id -> withArgs Untyped.TChainId C_Prim_option -> withArgs Untyped.TOption C_Prim_list -> withArgs Untyped.TList C_Prim_set -> withArgs Untyped.TSet C_Prim_operation -> withArgs Untyped.TOperation C_Prim_contract -> withArgs Untyped.TContract C_Prim_ticket -> withArgs Untyped.TTicket C_Prim_or | [arg1, arg2] <- args -> do let as1 = toAnnSet $ arg1 ^. _ExpPrim . _2 . mpaAnnotsL let as2 = toAnnSet $ arg2 ^. _ExpPrim . _2 . mpaAnnotsL checkAnnsCount e as1 (1, 1, 0) checkAnnsCount e as2 (1, 1, 0) let fa1 = firstAnn @FieldTag as1 let fa2 = firstAnn @FieldTag as2 l <- fromExp $ removeAnns arg1 isAnnotationField r <- fromExp $ removeAnns arg2 isAnnotationField pure $ Untyped.TOr fa1 fa2 l r | otherwise -> Left $ FromExpError e $ FEERArgumentCountMismatch (one 2) (length args) C_Prim_pair -> do args2 <- case nonEmpty args >>= forbidSingletonList of Nothing -> Left $ FromExpError e $ FEERNotEnoughArguments 2 (length args) Just as -> pure as -- Check and extract annotations info tyInfos <- forM args2 \arg -> do let as = toAnnSet $ arg ^. _ExpPrim . _2 . mpaAnnotsL checkAnnsCount e as (1, 1, 1) let fa = firstAnn @FieldTag as let va = firstAnn @VarTag as ty <- fromExp $ removeAnns arg (isAnnotationField || isAnnotationVariable) return (ty, fa, va) -- Make a right-comb pairs tree let combiner (ty1, fa1, va1) (ty2, fa2, va2) = ( Ty (Untyped.TPair fa1 fa2 va1 va2 ty1 ty2) noAnn , noAnn , noAnn ) let (Ty tRes _, _, _) = foldr1 combiner tyInfos return tRes C_Prim_lambda -> withArgs Untyped.TLambda C_Prim_map -> withArgs Untyped.TMap C_Prim_big_map -> withArgs Untyped.TBigMap C_Prim_int -> withArgs Untyped.TInt C_Prim_nat -> withArgs Untyped.TNat C_Prim_string -> withArgs Untyped.TString C_Prim_bytes -> withArgs Untyped.TBytes C_Prim_mutez -> withArgs Untyped.TMutez C_Prim_bool -> withArgs Untyped.TBool C_Prim_key_hash -> withArgs Untyped.TKeyHash C_Prim_bls12_381_fr -> withArgs Untyped.TBls12381Fr C_Prim_bls12_381_g1 -> withArgs Untyped.TBls12381G1 C_Prim_bls12_381_g2 -> withArgs Untyped.TBls12381G2 C_Prim_timestamp -> withArgs Untyped.TTimestamp C_Prim_address -> withArgs Untyped.TAddress C_Prim_chest -> withArgs Untyped.TChest C_Prim_chest_key -> withArgs Untyped.TChestKey C_Prim_tx_rollup_l2_address -> Left $ FromExpError e FEERUnsupported C_Prim_never -> withArgs Untyped.TNever C_Prim_sapling_state -> withArgs Untyped.TSaplingState C_Prim_sapling_transaction -> withArgs Untyped.TSaplingTransaction C_Prim_sapling_transaction_deprecated -> Left $ FromExpError e FEERDeprecated s -> const $ Left $ FromExpError e $ FEERUnexpectedPrimClass MPTType (fromSing s) where assertNoAnns = unless (null anns) $ Left $ FromExpError e FEERUnexpectedAnnotations withArgs :: WithArgsRec x a Untyped.T => a -> Either (FromExpError x) Untyped.T withArgs = withArgsRec e args removeAnns :: Exp x -> (Annotation -> Bool) -> Exp x removeAnns expr p = expr & _ExpPrim . _2 . mpaAnnotsL %~ filter (not . p) fromExp e = Left $ FromExpError e $ FEERExpectedPrim $ one MPTType instance FromExp x Ty where fromExp e = case e of ExpPrim' ex primName args anns -> do let annSet = toAnnSet anns annCnt = annsCount annSet let ta = firstAnn @TypeTag annSet when (secondAnn @TypeTag annSet /= noAnn) $ Left $ FromExpError e $ FEERTooManyAnns (Just 1, Nothing, Nothing) annCnt t <- fromExp @x @Untyped.T $ ExpPrim' ex primName args $ filter (not . isAnnotationType) anns pure $ Ty t ta _ -> Left $ FromExpError e $ FEERExpectedPrim (one MPTType) instance FromExp x T where fromExp = second fromUType . fromExp @x @Untyped.Ty -- Note: we should generalize this to work for any instruction, -- not just lambdas (i.e. instructions with one input and one output). instance (SingI inp, SingI out) => FromExp RegularExp (Instr '[inp] '[out]) where fromExp expr = fromExpression @(Value ('TLambda inp out)) expr >>= \case VLam (LambdaCode instr) -> pure $ rfAnyInstr instr VLam LambdaCodeRec{} -> Left $ FromExpError expr $ FEERUnexpectedPrim Nothing Prim_Lambda_rec instance FromExp x ViewName where fromExp e = case e of ExpString _ s -> first (FromExpError e . FEERViewNameError) $ mkViewName s _ -> Left $ FromExpError e FEERExpectedString ---------------------------------------------------------------------------- -- Helpers ---------------------------------------------------------------------------- -- | Adds annotations to the expression, after removing empty annotations -- at the end of each list. addTrimmedAnns :: Exp x -> [TypeAnn] -> [FieldAnn] -> [VarAnn] -> Exp x addTrimmedAnns e tas fas vas = e & _ExpPrim . _2 . mpaAnnotsL <>~ mkAnns tas fas vas -- | Inserts the root annotation into the contract parameter. insertRootAnn :: HasCallStack => Expression -> RootAnn -> Expression insertRootAnn expr rootAnn = case expr of ExpPrim () p -- The order of annotations is important iff there are -- multiple annotations of the same kind or there are -- other kinds of annotations in the list. -- Prepending root field annotation is okay because -- there can not be more than one root annotation. | rootAnn /= noAnn -> expressionPrim p { mpaAnnots = AnnotationField rootAnn : mpaAnnots p } | otherwise -> expr -- Currently this error can't happen because parameter type -- must be a Micheline primitive. If it ever changes, we -- would like to notice it ASAP and update this place. _ -> error $ "parameter is not a primitive: " <> pretty expr -- | Checks for a given expression that the number of annotations -- of each type in it doesn't exceed the specified threshold. checkAnnsCount :: Exp x -> AnnotationSet -> (Int, Int, Int) -> Either (FromExpError x) () checkAnnsCount e annSet maxCount@(maxTas, maxFas, maxVas) = do let actualCount = annsCount annSet unless (actualCount <= maxCount) $ Left $ FromExpError e $ FEERTooManyAnns (Just maxTas, Just maxFas, Just maxVas) actualCount forbidSingletonList :: NonEmpty a -> Maybe (NonEmpty a) forbidSingletonList = \case _ :| [] -> Nothing x -> Just x integralToExpr :: Integral i => i -> Expression integralToExpr = expressionInt . toInteger integralFromExpr :: (Integral i, Bits i) => Exp x -> Either (FromExpError x) i integralFromExpr e = case e of ExpInt _ v -> maybeToRight (FromExpError e FEEROutOfBounds) (fromIntegralMaybe @Integer v) _ -> Left $ FromExpError e FEERExpectedNumber