morley-1.16.3: src/Morley/Michelson/Parser/Let.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 let blocks
module Morley.Michelson.Parser.Let
( letBlock
, mkLetMac
-- * For tests
, letInner
, letType
) where
import Prelude hiding (try)
import Data.Char qualified as Char
import Data.Map qualified as Map
import Data.Set qualified as Set
import Data.Type.Equality ((:~:)(Refl))
import Text.Megaparsec (choice, satisfy, try)
import Text.Megaparsec.Char (lowerChar, upperChar)
import Morley.Michelson.Let (LetType(..), LetValue(..))
import Morley.Michelson.Macro (LetMacro(..), ParsedOp(..))
import Morley.Michelson.Parser.Ext
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 (LetEnv(..), Parser, Parser', assertLetEnv, noLetEnv)
import Morley.Michelson.Parser.Value
import Morley.Michelson.Untyped (StackFn(..), Ty(..), mkAnnotation, noAnn)
-- | Element of a let block
data Let = LetM LetMacro | LetV LetValue | LetT LetType
-- | let block parser
letBlock :: forall le. Parser' le ParsedOp -> Parser le le
letBlock opParser = do
symbol "let"
symbol "{"
Refl <- assertLetEnv
ls <- local (const noLetEnv) (letInner opParser)
symbol "}"
semicolon
return ls
-- | Incrementally build the let environment
letInner :: Parser' LetEnv ParsedOp -> Parser' LetEnv LetEnv
letInner opParser = do
env <- ask
l <- lets opParser
semicolon
local (addLet l) (letInner opParser) <|> return (addLet l env)
-- | Add a Let to the environment in the correct place
addLet :: Let -> LetEnv -> LetEnv
addLet l (LetEnv lms lvs lts) = case l of
LetM lm -> LetEnv (Map.insert (lmName lm) lm lms) lvs lts
LetV lv -> LetEnv lms (Map.insert (lvName lv) lv lvs) lts
LetT lt -> LetEnv lms lvs (Map.insert (ltName lt) lt lts)
lets :: Parser' LetEnv ParsedOp -> Parser' LetEnv Let
lets opParser = choice
[ (LetM <$> letMacro opParser)
, (LetV <$> letValue opParser)
, (LetT <$> letType)
]
-- | Build a let name parser from a leading character parser
letName :: Parser' LetEnv Char -> Parser' LetEnv Text
letName p = lexeme $ do
v <- p
let validChar x = Char.isAscii x && (Char.isAlphaNum x || x == '\'' || x == '_')
vs <- many (satisfy validChar)
return $ toText (v:vs)
letMacro :: Parser' LetEnv ParsedOp -> Parser' LetEnv LetMacro
letMacro opParser = lexeme $ do
n <- try $ do
n <- letName lowerChar
symbol "::"
return n
s <- stackFn
symbol "="
o <- ops' opParser
return $ LetMacro n s o
letType :: Parser' LetEnv LetType
letType = lexeme $ do
n <- try $ do
symbol "type"
n <- letName upperChar <|> letName lowerChar
symbol "="
return n
t@(Ty t' a) <- type_
if a == noAnn
then case mkAnnotation n of
Right an -> return $ LetType n (Ty t' an)
Left err -> fail $ toString err
else return $ LetType n t
letValue :: Parser' LetEnv ParsedOp -> Parser' LetEnv LetValue
letValue opParser = lexeme $ do
n <- try $ do
n <- letName upperChar
symbol "::"
return n
t <- type_
symbol "="
v <- value' opParser
return $ LetValue n t v
mkLetMac :: Map Text LetMacro -> Parser' LetEnv LetMacro
mkLetMac lms = choice $ mkParser lmName <$> (Map.elems lms)
stackFn :: Parser' LetEnv StackFn
stackFn = do
vs <- (optional (symbol "forall" >> some varID <* symbol "."))
a <- stackType
symbol "->"
b <- stackType
return $ StackFn (Set.fromList <$> vs) a b