packages feed

morley-1.15.0: src/Michelson/Parser/Type.hs

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

-- | Parsing of Michelson types.

module Michelson.Parser.Type
  ( type_
  , typeWithParen
  , field
  ) where

import Prelude hiding (note, some, try)

import Data.Default (Default, def)
import qualified Data.Map as Map
import Fmt (pretty)
import Text.Megaparsec (choice, customFailure, label, sepBy)

import Michelson.Let (LetType(..))
import Michelson.Parser.Annotations
import Michelson.Parser.Error
import Michelson.Parser.Helpers
import Michelson.Parser.Lexer
import Michelson.Parser.Types (Parser, letTypes)
import Michelson.Untyped
import Util.Generic

type_ :: Parser Ty
type_ = snd <$> typeInner (pure noAnn)

field :: Parser (FieldAnn, Ty)
field = typeInner note

t_operator :: Parser FieldAnn -> Parser (FieldAnn, Ty)
t_operator fp = do
  whole <- parens do
    optional do
      ty   <- field
      rest <- optional do
        isOr <- (symbol' "|" >> return True)
            <|> (symbol' "," >> return False)
        others <- field `sepBy` symbol' if isOr then "|" else ","
        return (isOr, others)
      return (ty, rest)

  (f, t) <- fieldType fp
  case whole of
    Just (ty, Just (isOr, tys)) -> do
      let (f', Ty ty' _) = mkGenericTree (mergeTwo isOr) (ty :| tys)
      f'' <- mergeAnnots f f'
      return (f'', Ty ty' t)
    Just (res, _) -> do
      return res
    Nothing -> do
      return (f, Ty TUnit t)
  where
    mergeTwo isOr _ (l, a) (r, b) =
      (noAnn, Ty ((if isOr then TOr l r else TPair l r noAnn noAnn) a b) noAnn)

    mergeAnnots l r
      | l == def  = return r
      | r == def  = return l
      | otherwise = customFailure ExcessFieldAnnotation

typeInner
  :: Parser FieldAnn -> Parser (FieldAnn, Ty)
typeInner fp = label "type" $ lexeme $ choice $ (\x -> x fp) <$>
  [ t_int, t_nat, t_string, t_bytes, t_mutez, t_bool
  , t_keyhash, t_timestamp, t_address
  , t_key, t_unit, t_never, t_signature, t_chain_id
  , t_bls12381fr, t_bls12381g1, t_bls12381g2
  , t_option, t_list, t_set
  , t_operation, t_contract, t_ticket, t_pair, t_or
  , t_lambda, t_map, t_big_map, t_view
  , t_void, t_letType
  , t_operator
  ]

----------------------------------------------------------------------------
-- Comparable types
----------------------------------------------------------------------------

typeWithParen :: Parser Ty
typeWithParen = mparens type_

----------------------------------------------------------------------------
-- Non-comparable types
----------------------------------------------------------------------------

mkType :: T -> (a, TypeAnn) -> (a, Ty)
mkType t (a, ta) = (a, Ty t ta)


t_int :: (Default a) => Parser a -> Parser (a, Ty)
t_int fp = word' "Int" (mkType TInt) <*> fieldType fp

t_nat :: (Default a) => Parser a -> Parser (a, Ty)
t_nat fp = word' "Nat" (mkType TNat) <*> fieldType fp

t_string :: (Default a) => Parser a -> Parser (a, Ty)
t_string fp = word' "String" (mkType TString) <*> fieldType fp

t_bytes :: (Default a) => Parser a -> Parser (a, Ty)
t_bytes fp = word' "Bytes" (mkType TBytes) <*> fieldType fp

t_mutez :: (Default a) => Parser a -> Parser (a, Ty)
t_mutez fp = word' "Mutez" (mkType TMutez) <*> fieldType fp

t_bool :: (Default a) => Parser a -> Parser (a, Ty)
t_bool fp = word' "Bool" (mkType TBool) <*> fieldType fp

t_keyhash :: (Default a) => Parser a -> Parser (a, Ty)
t_keyhash fp = ((word' "KeyHash" (mkType TKeyHash)) <|> (word "key_hash" (mkType TKeyHash))) <*> fieldType fp

t_timestamp :: (Default a) => Parser a -> Parser (a, Ty)
t_timestamp fp = word' "Timestamp" (mkType TTimestamp) <*> fieldType fp

t_address :: (Default a) => Parser a -> Parser (a, Ty)
t_address fp = word' "Address" (mkType TAddress) <*> fieldType fp

t_key :: (Default a) => Parser a -> Parser (a, Ty)
t_key fp = word' "Key" (mkType TKey) <*> fieldType fp

t_signature :: (Default a) => Parser a -> Parser (a, Ty)
t_signature fp = word' "Signature" (mkType TSignature) <*> fieldType fp

t_bls12381fr :: (Default a) => Parser a -> Parser (a, Ty)
t_bls12381fr fp = do
  symbol' "bls12_381_fr" <|> symbol' "Bls12381Fr"
  mkType TBls12381Fr <$> fieldType fp

t_bls12381g1 :: (Default a) => Parser a -> Parser (a, Ty)
t_bls12381g1 fp = do
  symbol' "bls12_381_g1" <|> symbol' "Bls12381G1"
  mkType TBls12381G1 <$> fieldType fp

t_bls12381g2 :: (Default a) => Parser a -> Parser (a, Ty)
t_bls12381g2 fp = do
  symbol' "bls12_381_g2" <|> symbol' "Bls12381G2"
  mkType TBls12381G2 <$> fieldType fp

t_chain_id :: (Default a) => Parser a -> Parser (a, Ty)
t_chain_id fp = do
  symbol' "ChainId" <|> symbol' "chain_id"
  mkType TChainId <$> fieldType fp

t_operation :: (Default a) => Parser a -> Parser (a, Ty)
t_operation fp = word' "Operation" (mkType TOperation) <*> fieldType fp

t_contract :: (Default a) => Parser a -> Parser (a, Ty)
t_contract fp = do
  symbol' "Contract"
  (f, t) <- fieldType fp
  a <- type_
  return (f, Ty (TContract a) t)

t_ticket :: (Default a) => Parser a -> Parser (a, Ty)
t_ticket fp = do
  symbol' "Ticket"
  (f, t) <- fieldType fp
  a <- type_
  return (f, Ty (TTicket a) t)

t_unit :: (Default a) => Parser a -> Parser (a, Ty)
t_unit fp = do
  symbol' "Unit" <|> symbol' "()"
  (f,t) <- fieldType fp
  return (f, Ty TUnit t)

t_never :: (Default a) => Parser a -> Parser (a, Ty)
t_never fp = do
  symbol' "Never" <|> symbol' "⊥"
  (f,t) <- fieldType fp
  return (f, Ty TNever t)

t_pair :: (Default a) => Parser a -> Parser (a, Ty)
t_pair fp = do
  symbol' "Pair"
  (fieldAnn, typeAnn) <- fieldType fp
  fields <- many field
  tPair <- go fields
  pure $ (fieldAnn, Ty tPair typeAnn)
  where
    go :: [(FieldAnn, Ty)] -> Parser T
    go = \case
      [] -> fail "The 'pair' type expects at least 2 type arguments, but 0 were given."
      [(_, t)] -> fail $ "The 'pair' type expects at least 2 type arguments, but only 1 was given: '" <> pretty t <> "'."
      [(fieldAnnL, typeL), (fieldAnnR, typeR)] ->
        pure $ TPair fieldAnnL fieldAnnR noAnn noAnn typeL typeR
      (fieldAnnL, typeL) : fields -> do
        rightCombedT <- go fields
        pure $ TPair fieldAnnL noAnn noAnn noAnn typeL (Ty rightCombedT noAnn)

t_or :: (Default a) => Parser a -> Parser (a, Ty)
t_or fp = do
  symbol' "Or"
  (f, t) <- fieldType fp
  (l, a) <- field
  (r, b) <- field
  return (f, Ty (TOr l r a b) t)

t_option :: (Default a) => Parser a -> Parser (a, Ty)
t_option fp = do
  symbol' "Option"
  (f, t) <- fieldType fp
  a <- mparens $ snd <$> typeInner (pure noAnn)
  return (f, Ty (TOption a) t)

t_lambda :: (Default a) => Parser a -> Parser (a, Ty)
t_lambda fp = core <|> slashLambda
  where
    core = do
      symbol' "Lambda"
      (f, t) <- fieldType fp
      a <- type_
      b <- type_
      return (f, Ty (TLambda a b) t)
    slashLambda = do
      symbol "\\"
      (f, t) <- fieldType fp
      a <- type_
      symbol "->"
      b <- type_
      return (f, Ty (TLambda a b) t)

-- Container types
t_list :: (Default a) => Parser a -> Parser (a, Ty)
t_list fp = core <|> bracketList
  where
    core = do
      symbol' "List"
      (f, t) <- fieldType fp
      a <- type_
      return (f, Ty (TList a) t)
    bracketList = do
      a <- brackets type_
      (f, t) <- fieldType fp
      return (f, Ty (TList a) t)

t_set :: (Default a) => Parser a -> Parser (a, Ty)
t_set fp = core <|> braceSet
  where
    core = do
      symbol' "Set"
      (f, t) <- fieldType fp
      a <- typeWithParen
      return (f, Ty (TSet a) t)
    braceSet = do
      a <- braces typeWithParen
      (f, t) <- fieldType fp
      return (f, Ty (TSet a) t)

t_map_like
  :: Default a
  => Parser a -> Parser (Ty, Ty, a, TypeAnn)
t_map_like fp = do
  (f, t) <- fieldType fp
  a <- typeWithParen
  b <- type_
  return (a, b, f, t)

t_map :: (Default a) => Parser a -> Parser (a, Ty)
t_map fp = do
  symbol' "Map"
  (a, b, f, t) <- t_map_like fp
  return (f, Ty (TMap a b) t)

t_big_map :: (Default a) => Parser a -> Parser (a, Ty)
t_big_map fp = do
  symbol' "BigMap" <|> symbol "big_map"
  (a, b, f, t) <- t_map_like fp
  return (f, Ty (TBigMap a b) t)

----------------------------------------------------------------------------
-- Non-standard types (Morley extensions)
----------------------------------------------------------------------------

t_view :: Default a => Parser a -> Parser (a, Ty)
t_view fp = do
  symbol' "View"
  a <- type_
  r <- type_
  (f, t) <- fieldType fp
  let c' = Ty (TContract r) noAnn
  return (f, Ty (TPair noAnn noAnn noAnn noAnn a c') t)

t_void :: Default a => Parser a -> Parser (a, Ty)
t_void fp = do
  symbol' "Void"
  a <- type_
  b <- type_
  (f, t) <- fieldType fp
  let c = Ty (TLambda b b) noAnn
  return (f, Ty (TPair noAnn noAnn noAnn noAnn a c) t)

t_letType :: Default fp => Parser fp -> Parser (fp, Ty)
t_letType fp = do
  lts <- asks letTypes
  lt <- ltSig <$> (mkLetType lts)
  f <- parseDef fp
  return (f, lt)

mkLetType :: Map Text LetType -> Parser LetType
mkLetType lts = choice $ mkParser ltName <$> (Map.elems lts)