module Michelson.Parser.Helpers
  ( mkParser
  , sepEndBy1
  , sepBy2
  , parseDef
  , positive
  , mparens
  ) where

import Data.Default (Default(..))
import qualified Data.List.NonEmpty as NE
import qualified Text.Megaparsec as P
import Text.Megaparsec.Char.Lexer (decimal)

import Michelson.Parser.Lexer (parens, word')
import Michelson.Parser.Types (Parser)
import Util.Positive

sepEndBy1 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)
sepEndBy1 = fmap NE.fromList ... P.sepEndBy1

-- | @endBy2 p sep@ parses two or more occurrences of @p@, separated by @sep@.
sepBy2 :: MonadPlus m => m a -> m sep -> m (NonEmpty a)
sepBy2 parser sep = do
  e <- parser
  void sep
  es <- P.sepBy1 parser sep
  return $ e :| es

-- | Make a parser from a string
mkParser :: (a -> Text) -> a -> Parser a
mkParser f a = P.try $ word' (f a) a

-- | Apply given parser and return default value if it fails.
parseDef :: Default a => Parser a -> Parser a
parseDef a = P.try a <|> pure def

-- | Parse a positive number.
positive :: Parser Positive
positive = do
  n :: Integer <- decimal
  mkPositive n
    & either (fail . toString) pure

-- | Parse expression which can be wrapped in parentheses.
mparens :: Parser a -> Parser a
mparens p = p <|> parens p