packages feed

morley-1.5.0: src/Michelson/Untyped/Type.hs

-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ

-- | Michelson types represented in untyped model.

module Michelson.Untyped.Type
  ( Type (..)
  , T (..)
  , ParameterType (..)
  , toption
  , tpair
  , tor
  , tyint
  , tynat
  , tyunit
  , tybool
  , typair
  , tyor
  , tyImplicitAccountParam
  , isAtomicType
  , isKey
  , isSignature
  , isComparable
  , isMutez
  , isKeyHash
  , isBool
  , isString
  , isInteger
  , isTimestamp
  , isNat
  , isInt
  , isBytes
  , renderType
  , unwrapT
  ) where

import Data.Aeson.TH (deriveJSON)
import Data.Data (Data(..))
import Fmt (Buildable(build))
import Language.Haskell.TH.Lift (deriveLift)
import Prelude hiding ((<$>))
import Text.PrettyPrint.Leijen.Text (Doc, align, softbreak, (<$>), (<+>))

import Michelson.Printer.Util
  (Prettier(..), RenderContext, RenderDoc(..), addParens, buildRenderDoc, doesntNeedParens,
  needsParens, wrapInParens)
import Michelson.Untyped.Annotation
  (AnnotationSet, FieldAnn, RootAnn, TypeAnn, convAnn, emptyAnnSet, fullAnnSet, noAnn,
  singleAnnSet)
import Util.Aeson

-- Annotated type
data Type
  = Type ~T TypeAnn
  deriving stock (Eq, Show, Data, Generic)

unwrapT :: Type -> T
unwrapT (Type t _) = t

instance NFData Type

instance RenderDoc (Prettier Type) where
  renderDoc pn (Prettier w) = case w of
    (Type t ta) -> renderType t False pn (singleAnnSet ta)

instance RenderDoc Type where
  renderDoc pn (Type t ta) = renderType t True pn (singleAnnSet ta)

instance RenderDoc T where
  renderDoc pn t = renderType t True pn emptyAnnSet

-- | Since Babylon parameter type can have special root annotation.
data ParameterType = ParameterType Type RootAnn
  deriving stock (Eq, Show, Data, Generic)

instance NFData ParameterType

instance RenderDoc (Prettier ParameterType) where
  renderDoc pn (Prettier w) = case w of
    ParameterType (Type t ta) ra ->
      renderType t False pn (fullAnnSet [ta] [convAnn ra] [])

instance RenderDoc ParameterType where
  renderDoc pn (ParameterType (Type t ta) ra) =
    renderType t True pn (fullAnnSet [ta] [convAnn ra] [])

-- Ordering between different kinds of annotations is not significant,
-- but ordering among annotations of the same kind is. Annotations
-- of a same kind must be grouped together.
-- (prim @v :t %x arg1 arg2 ...)
-- these are equivalent
-- PAIR :t @my_pair %x %y
-- PAIR %x %y :t @my_pair
renderType
  :: T
  -> Bool
  -> RenderContext
  -> AnnotationSet
  -> Doc
renderType t forceSingleLine pn annSet =
  let annDoc = renderDoc doesntNeedParens annSet
      recRenderer t' annSet' = renderType t' forceSingleLine needsParens annSet'
      renderBranches d1 d2 =
        if forceSingleLine
        then (d1 <+> d2)
        else align $ softbreak <> (d1 <$> d2)
  in
  case t of
    TInt              -> wrapInParens pn $ "int" :| [annDoc]
    TNat              -> wrapInParens pn $ "nat" :| [annDoc]
    TString           -> wrapInParens pn $ "string" :| [annDoc]
    TMutez            -> wrapInParens pn $ "mutez" :| [annDoc]
    TBool             -> wrapInParens pn $ "bool" :| [annDoc]
    TKeyHash          -> wrapInParens pn $ "key_hash" :| [annDoc]
    TTimestamp        -> wrapInParens pn $ "timestamp" :| [annDoc]
    TBytes            -> wrapInParens pn $ "bytes" :| [annDoc]
    TAddress          -> wrapInParens pn $ "address" :| [annDoc]
    TKey              -> wrapInParens pn $ "key"  :| [annDoc]
    TUnit             -> wrapInParens pn $ "unit" :| [annDoc]
    TSignature        -> wrapInParens pn $ "signature" :| [annDoc]
    TChainId          -> wrapInParens pn $ "chain_id" :| [annDoc]
    TOperation        -> wrapInParens pn $ "operation" :| [annDoc]

    TOption (Type t1 ta1) ->
      addParens pn $
      "option" <+> annDoc <+> recRenderer t1 (singleAnnSet ta1)

    TList (Type t1 ta1)       ->
      addParens pn $
      "list" <+> annDoc <+> recRenderer t1 (singleAnnSet ta1)

    TSet (Type t1 ta1) ->
      addParens pn $
      "set" <+> annDoc <+> recRenderer t1 (singleAnnSet ta1)

    TContract (Type t1 ta1)   ->
      addParens pn $
      "contract" <+> annDoc <+> recRenderer t1 (singleAnnSet ta1)

    TPair fa1 fa2 (Type t1 ta1) (Type t2 ta2) ->
      addParens pn $
        "pair" <+> annDoc <+>
          renderBranches
            (recRenderer t1 $ fullAnnSet [ta1] [fa1] [])
            (recRenderer t2 $ fullAnnSet [ta2] [fa2] [])

    TOr fa1 fa2 (Type t1 ta1) (Type t2 ta2) ->
      addParens pn $
        "or" <+> annDoc <+>
          renderBranches
            (recRenderer t1 $ fullAnnSet [ta1] [fa1] [])
            (recRenderer t2 $ fullAnnSet [ta2] [fa2] [])

    TLambda (Type t1 ta1) (Type t2 ta2) ->
      addParens pn $
        "lambda" <+> annDoc <+>
          renderBranches
            (recRenderer t1 $ singleAnnSet ta1)
            (recRenderer t2 $ singleAnnSet ta2)

    TMap (Type t1 ta1) (Type t2 ta2) ->
      addParens pn $
        "map" <+> annDoc <+>
          renderBranches
            (recRenderer t1 $ singleAnnSet ta1)
            (recRenderer t2 $ singleAnnSet ta2)

    TBigMap (Type t1 ta1) (Type t2 ta2) ->
      addParens pn $
        "big_map" <+> annDoc <+>
          renderBranches
            (recRenderer t1 $ singleAnnSet ta1)
            (recRenderer t2 $ singleAnnSet ta2)

instance Buildable Type where
  build = buildRenderDoc

instance Buildable ParameterType where
  build = buildRenderDoc

-- Michelson Type
data T =
    TKey
  | TUnit
  | TSignature
  | TChainId
  | TOption Type
  | TList Type
  | TSet Type
  | TOperation
  | TContract Type
  | TPair FieldAnn FieldAnn Type Type
  | TOr FieldAnn FieldAnn Type Type
  | TLambda Type Type
  | TMap Type Type
  | TBigMap Type Type
  | TInt
  | TNat
  | TString
  | TBytes
  | TMutez
  | TBool
  | TKeyHash
  | TTimestamp
  | TAddress
  deriving stock (Eq, Show, Data, Generic)

instance Buildable T where
  build = buildRenderDoc

instance NFData T

toption :: Type -> T
toption t = TOption t

tpair :: Type -> Type -> T
tpair l r = TPair noAnn noAnn l r

tor :: Type -> Type -> T
tor l r = TOr noAnn noAnn l r

tyint :: Type
tyint = Type TInt noAnn

tynat :: Type
tynat = Type TNat noAnn

tyunit :: Type
tyunit = Type TUnit noAnn

tybool :: Type
tybool = Type TBool noAnn

typair :: Type -> Type -> Type
typair l r = Type (tpair l r) noAnn

tyor :: Type -> Type -> Type
tyor l r = Type (tor l r) noAnn

-- | For implicit account, which type its parameter seems to have
-- from outside.
tyImplicitAccountParam :: Type
tyImplicitAccountParam = Type TUnit noAnn

isAtomicType :: Type -> Bool
isAtomicType t@(Type _ tAnn) | tAnn == noAnn =
    isComparable t || isKey t || isUnit t || isSignature t || isOperation t
isAtomicType _ = False

isKey :: Type -> Bool
isKey (Type TKey _) = True
isKey _              = False

isUnit :: Type -> Bool
isUnit (Type TUnit _) = True
isUnit _               = False

isSignature :: Type -> Bool
isSignature (Type TSignature _) = True
isSignature _                    = False

isOperation :: Type -> Bool
isOperation (Type TOperation _) = True
isOperation _                    = False

isComparable :: Type -> Bool
isComparable (Type t _) = case t of
  TInt -> True
  TNat -> True
  TString -> True
  TBytes -> True
  TMutez -> True
  TBool -> True
  TKeyHash -> True
  TTimestamp -> True
  TAddress -> True
  _ -> False

isMutez :: Type -> Bool
isMutez (Type TMutez _) = True
isMutez _ = False

isTimestamp :: Type -> Bool
isTimestamp (Type TTimestamp _) = True
isTimestamp _ = False

isKeyHash :: Type -> Bool
isKeyHash (Type TKeyHash _) = True
isKeyHash _ = False

isBool  :: Type -> Bool
isBool (Type TBool _) = True
isBool _ = False

isString  :: Type -> Bool
isString (Type TString _) = True
isString _ = False

isInteger :: Type -> Bool
isInteger a = isNat a || isInt a || isMutez a || isTimestamp a

isNat  :: Type -> Bool
isNat (Type TNat _) = True
isNat _ = False

isInt  :: Type -> Bool
isInt (Type TInt _) = True
isInt _ = False

isBytes :: Type -> Bool
isBytes (Type TBytes _) = True
isBytes _ = False

----------------------------------------------------------------------------
-- TH derivations
----------------------------------------------------------------------------

deriveJSON morleyAesonOptions ''Type
deriveJSON morleyAesonOptions ''T
deriveJSON morleyAesonOptions ''ParameterType

deriveLift ''Type
deriveLift ''T
deriveLift ''ParameterType