Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 18 additions & 64 deletions plutus-core/plutus-core/src/PlutusCore/Parser/Builtin.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,80 +6,34 @@ module PlutusCore.Parser.Builtin where

import Data.Text qualified as T
import Data.Text.Internal.Read (hexDigitToInt)
import PlutusPrelude
import Text.Megaparsec hiding (ParseError, State, parse, some)
import PlutusPrelude (Word8)
import Text.Megaparsec (MonadParsec (takeWhileP), choice, many, manyTill)
import Text.Megaparsec.Char (char, hexDigitChar)
import Text.Megaparsec.Char.Lexer qualified as Lex hiding (hexadecimal)

import Data.ByteString (pack)
import Data.Map (Map, empty, insert, lookup)
import PlutusCore.Default
import PlutusCore.Parser.ParserCommon (Parser, lexeme, symbol, whitespace)
import PlutusCore.Parser.ParserCommon (Parser, isIdentifierChar, lexeme, symbol, whitespace)
import PlutusCore.Parser.Type (defaultUniType)
import PlutusCore.Pretty (display)
import Prelude hiding (lookup)

mkCachedBuiltin :: [DefaultFun] -> Map T.Text DefaultFun -> Map T.Text DefaultFun
mkCachedBuiltin (hdFns:tlFns) builtinMap =
mkCachedBuiltin tlFns (insert (display hdFns) hdFns builtinMap)
mkCachedBuiltin [] builtinMap = builtinMap
Comment on lines +22 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpicking (i.e. not important at all): instead of writing the recursion manually you define mkCachedBuiltin as Map.fromList . map (\fun -> (display fun, fun)) or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah HLint told me to do that too but I thought it's harder to read. Maybe not. I can change that. I was gonna open a follow up PR for the error anyway.


-- | The list of parsable default functions and their pretty print correspondence.
builtinFnList :: [(DefaultFun, T.Text)]
builtinFnList =
[ (AddInteger,"addInteger")
, (SubtractInteger,"subtractInteger")
, (MultiplyInteger,"multiplyInteger")
, (DivideInteger,"divideInteger")
, (QuotientInteger,"quotientInteger")
, (RemainderInteger,"remainderInteger")
, (ModInteger,"modInteger")
, (EqualsInteger,"equalsInteger")
, (LessThanInteger,"lessThanInteger")
, (LessThanEqualsInteger,"lessThanEqualsInteger")
, (AppendByteString,"appendByteString")
, (ConsByteString,"consByteString")
, (SliceByteString,"sliceByteString")
, (LengthOfByteString,"lengthOfByteString")
, (IndexByteString,"indexByteString")
, (EqualsByteString,"equalsByteString")
, (LessThanByteString,"lessThanByteString")
, (LessThanEqualsByteString,"lessThanEqualsByteString")
, (Sha2_256,"sha2_256")
, (Sha3_256,"sha3_256")
, (Blake2b_256,"blake2b_256")
, (VerifySignature,"verifySignature")
, (AppendString,"appendString")
, (EqualsString,"equalsString")
, (EncodeUtf8,"encodeUtf8")
, (DecodeUtf8,"decodeUtf8")
, (IfThenElse,"ifThenElse")
, (ChooseUnit,"chooseUnit")
, (Trace,"trace")
, (FstPair,"fstPair")
, (SndPair,"sndPair")
, (ChooseList,"chooseList")
, (MkCons,"mkCons")
, (HeadList,"headList")
, (TailList,"tailList")
, (NullList,"nullList")
, (ChooseData,"chooseData")
, (ConstrData,"constrData")
, (MapData,"mapData")
, (ListData,"listData")
, (IData,"iData")
, (BData,"bData")
, (UnConstrData,"unConstrData")
, (UnMapData,"unMapData")
, (UnListData,"unListData")
, (UnIData,"unIData")
, (UnBData,"unBData")
, (EqualsData,"equalsData")
, (SerialiseData,"serialiseData")
, (MkPairData,"mkPairData")
, (MkNilData,"mkNilData")
, (MkNilPairData,"mkNilPairData")
]
cachedBuiltin :: Map T.Text DefaultFun
cachedBuiltin = mkCachedBuiltin [minBound .. maxBound] empty

-- | Parser for builtin functions. Atm the parser can only parse `DefaultFun`.
builtinFunction :: Parser DefaultFun
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalize to an arbitrary fun (implementing Pretty, Bounded etc)?

Copy link
Contributor Author

@thealmarty thealmarty Apr 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parser can only parse terms in the DefaultUni and is a DefaultFun...the old parser also did that. (Now a parsable term is Term Name DefaultUni DefaultFun SourcePos). I can still parse it more generically but it probably still won't work for other funs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we decided to leave the current parser monomorphic for now. I think we can leave making it generic over universes for a future chunk of work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did add a comment reminding people that only DefaultFun will work. Will the error be easier to read this way? Or maybe restricting the parser to DefaultFun will show better errors I think? If so I'll drop the last commit and make it parse to DefaultFun only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see you generalized it anyway. That seems fine!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can leave making it generic over universes for a future chunk of work.

I'm not suggesting to make it generic over universes. It's harder and we have only one universe anyway. However generalizing over sets of built-in functions is fairly trivial and we do have multiple of them. I don't really care, just proposing.

builtinFunction =
choice $
map
(try . (\(fn, text) -> fn <$ symbol text))
builtinFnList
builtinFunction = lexeme $ do
txt <- takeWhileP (Just "identifier") isIdentifierChar
case lookup txt cachedBuiltin of
Nothing -> error $ "Not a builtin" <> show cachedBuiltin <> show txt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't call error here! There's nothing exceptional about a bulitin not being recognized. The Parser monad allows you to throw an error in it. I think the right function is customFailure, but not sure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, yeah, the error to throw is UnknownBuiltinFunction from ParserError.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thank you! I was rushing to merge the rest so that the blocked PR can go through. I want to show the cachedBuiltin somehow, I think it's useful to see. I'll work on that today.

Just builtin -> pure builtin

signedInteger :: Parser Integer
signedInteger = Lex.signed whitespace (lexeme Lex.decimal)
Expand Down