packages feed

morley-1.17.0: src/Morley/Michelson/Parser/Macro.hs

-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

-- | Parsing of built-in Michelson macros.

module Morley.Michelson.Parser.Macro
  ( macro
  -- * These are handled separately to have better error messages
  , duupMac
  , pairMac
  , ifCmpMac
  , mapCadrMac
  , cadrMac
  , carnMac
  , cdrnMac
  ) where

import Prelude hiding (note, try)

import Text.Megaparsec (label, notFollowedBy, skipMany, try)
import Text.Megaparsec.Char (string)
import Text.Megaparsec.Char.Lexer (decimal)
import Unsafe qualified (fromIntegral)

import Morley.Michelson.Macro
  (CadrStruct(..), Macro(..), PairStruct(..), ParsedOp(..), UnpairStruct(..))
import Morley.Michelson.Macro qualified as Macro
import Morley.Michelson.Parser.Annotations
import Morley.Michelson.Parser.Instr
import Morley.Michelson.Parser.Lexer
import Morley.Michelson.Parser.Types (Parser)
import Morley.Michelson.Untyped (noAnn)

macro :: Parser ParsedOp -> Parser Macro
macro opParser = label "macro"
   $ setCadrMac
  <|> (string "CMP" >> return CMP <*> cmpOp <*> noteDef)
  <|> word "IF_SOME" IF_SOME <*> ops <*> ops
  <|> word "IF_RIGHT" IF_RIGHT <*> ops <*> ops
  <|> word "FAIL" FAIL
  <|> (string "ASSERT_CMP" >> return ASSERT_CMP <*> cmpOp)
  <|> word "ASSERT_NONE" ASSERT_NONE
  <|> word "ASSERT_SOME" ASSERT_SOME
  <|> word "ASSERT_LEFT" ASSERT_LEFT
  <|> word "ASSERT_RIGHT" ASSERT_RIGHT
  <|> (string "ASSERT_" >> return ASSERTX <*> cmpOp)
  <|> word "ASSERT" ASSERT
  <|> do string "DI"; n <- num "I"; symbol1 "P"; DIIP (n + 1) <$> ops
  <|> unpairMac
  where
   ops = ops' opParser
   num str = Unsafe.fromIntegral @Int @Word . length <$> some (string str)

duupMac :: Parser Macro
duupMac = do string "DU"; n <- num "U"; symbol1 "P"; DUUP (n + 1) <$> noteDef
  where
    num str = Unsafe.fromIntegral @Int @Word . length <$> some (string str)

pairMacInner :: Parser PairStruct
pairMacInner = do
  string "P"
  l <- (string "A" $> F noAnn) <|> pairMacInner
  r <- (string "I" $> F noAnn) <|> pairMacInner
  return $ P l r

pairMac :: Parser Macro
pairMac = do
  a <- pairMacInner
  symbol1 "R"
  (tn, vn, fns) <- permute3Def noteDef note (some note)
  let ps = Macro.mapPairLeaves fns a
  return $ PAPAIR ps tn vn

unpairMacInner :: Parser UnpairStruct
unpairMacInner = do
  string "P"
  l <- (string "A" $> UF) <|> unpairMacInner
  r <- (string "I" $> UF) <|> unpairMacInner
  return $ UP l r

unpairMac :: Parser Macro
unpairMac = do
  string "UN"
  a <- unpairMacInner
  symbol1 "R"
  skipMany $ (void noteF) <|> (void noteV)
  return $ UNPAPAIR a

cadrMac :: Parser Macro
cadrMac = lexeme $ do
  string "C"
  a <- some $ try $ cadrInner <* notFollowedBy (string "R")
  b <- cadrInner
  symbol1 "R"
  (vn, fn) <- notesVF
  return $ CADR (a ++ pure b) vn fn

cadrInner :: Parser CadrStruct
cadrInner = (string "A" $> A) <|> (string "D" $> D)

carnMac :: Parser Macro
carnMac = symbol1 "CAR" *> (CARN <$> noteDef <*> lexeme decimal)

cdrnMac :: Parser Macro
cdrnMac = symbol1 "CDR" *> (CDRN <$> noteDef <*> lexeme decimal)

{-# ANN module ("HLint: ignore Reduce duplication" :: Text) #-}
setCadrMac :: Parser Macro
setCadrMac = do
  string "SET_C"
  a <- some cadrInner
  symbol1 "R"
  (v, f) <- notesVF
  return $ SET_CADR a v f

mapCadrMac :: Parser ParsedOp -> Parser Macro
mapCadrMac opParser = do
  string "MAP_C"
  a <- some cadrInner
  symbol1 "R"
  (v, f) <- notesVF
  MAP_CADR a v f <$> ops' opParser

ifCmpMac :: Parser ParsedOp -> Parser Macro
ifCmpMac opParser = string "IFCMP" >> return
  IFCMP <*> cmpOp <*> noteDef <*> ops' opParser <*> ops' opParser