morley-1.16.3: src/Morley/Michelson/Parser/Macro.hs
-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA
-- TODO [#712]: Remove this next major release
{-# OPTIONS_GHC -Wno-deprecations #-}
-- | 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 (customFailure, label, notFollowedBy, skipMany, try)
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.Error
import Morley.Michelson.Parser.Helpers
import Morley.Michelson.Parser.Instr
import Morley.Michelson.Parser.Lexer
import Morley.Michelson.Parser.Type
import Morley.Michelson.Parser.Types (Parser, Parser', assertLetEnv)
import Morley.Michelson.Untyped (T(..), Ty(..), noAnn)
import Morley.Util.Positive
macro :: Parser' le ParsedOp -> Parser le Macro
macro opParser = label "macro"
$ setCadrMac
<|> (word' "CASE" CASE <* assertLetEnv) <*> someNE ops
<|> (symbol1' "TAG" <* assertLetEnv) *> tagMac
<|> (symbol1' "ACCESS" <* assertLetEnv) *> accessMac
<|> (symbol1' "SET" <* assertLetEnv) *> setMac
<|> (word' "CONSTRUCT" CONSTRUCT <* assertLetEnv) <*> someNE ops
<|> (word' "VIEW_" VIEW_ <* assertLetEnv) <*> ops
<|> (word' "VOID" VOID <* assertLetEnv) <*> ops
<|> (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 le 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 le PairStruct
pairMacInner = do
string' "P"
l <- (string' "A" $> F noAnn) <|> pairMacInner
r <- (string' "I" $> F noAnn) <|> pairMacInner
return $ P l r
pairMac :: Parser le 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 le UnpairStruct
unpairMacInner = do
string' "P"
l <- (string' "A" $> UF) <|> unpairMacInner
r <- (string' "I" $> UF) <|> unpairMacInner
return $ UP l r
unpairMac :: Parser le Macro
unpairMac = do
string' "UN"
a <- unpairMacInner
symbol1' "R"
skipMany $ (void noteF) <|> (void noteV)
return $ UNPAPAIR a
cadrMac :: Parser le 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 le CadrStruct
cadrInner = (string' "A" $> A) <|> (string' "D" $> D)
carnMac :: Parser le Macro
carnMac = symbol1' "CAR" *> (CARN <$> noteDef <*> lexeme decimal)
cdrnMac :: Parser le Macro
cdrnMac = symbol1' "CDR" *> (CDRN <$> noteDef <*> lexeme decimal)
{-# ANN module ("HLint: ignore Reduce duplication" :: Text) #-}
setCadrMac :: Parser le Macro
setCadrMac = do
string' "SET_C"
a <- some cadrInner
symbol1' "R"
(v, f) <- notesVF
return $ SET_CADR a v f
mapCadrMac :: Parser le ParsedOp -> Parser le Macro
mapCadrMac opParser = do
string' "MAP_C"
a <- some cadrInner
symbol1' "R"
(v, f) <- notesVF
MAP_CADR a v f <$> ops' opParser
ifCmpMac :: Parser le ParsedOp -> Parser le Macro
ifCmpMac opParser = string' "IFCMP" >> return
IFCMP <*> cmpOp <*> noteDef <*> ops' opParser <*> ops' opParser
tagMac :: Parser le Macro
tagMac = do
idx <- decimal
spaces
ty <- type_
let utys = unrollUnion ty []
when (Unsafe.fromIntegral @Natural @Int idx >= length utys) $
customFailure $ WrongTagArgs idx (lengthNE utys)
return $ TAG idx utys
where
unrollUnion ty =
case ty of
Ty (TOr _ _ l r) _ -> unrollUnion l . toList . unrollUnion r
_ -> (ty :|)
accessMac :: Parser le Macro
accessMac = do
idx <- decimal
spaces
size <- positive
when (idx >= unPositive size) $
customFailure $ WrongAccessArgs idx size
return $ ACCESS idx size
setMac :: Parser le Macro
setMac = do
idx <- decimal
spaces
size <- positive
when (idx >= unPositive size) $
customFailure $ WrongSetArgs idx size
return $ SET idx size