-- |
--
-- Module:      Language.Egison.Parser.Pattern.Prim.Parse
-- Description: Parser monad
-- Stability:   experimental
--
-- This module defines a parser monad 'Parse'.

module Language.Egison.Parser.Pattern.Prim.Parse
  ( Parse
  , runParse
  )
where

import           Control.Applicative            ( Alternative )
import           Control.Monad.Except           ( MonadError(..) )
import           Control.Monad.Reader           ( ReaderT
                                                , MonadReader(..)
                                                , runReaderT
                                                )
import           Control.Monad.Fail             ( MonadFail )
import           Control.Monad                  ( MonadPlus )

import           Text.Megaparsec                ( Parsec
                                                , MonadParsec
                                                )
import qualified Text.Megaparsec               as Parsec
                                                ( Stream
                                                , parse
                                                , eof
                                                , getSourcePos
                                                )

import           Language.Egison.Parser.Pattern.Prim.Source
                                                ( Source )
import           Language.Egison.Parser.Pattern.Prim.Location
                                                ( Locate(..)
                                                , fromSourcePos
                                                )
import           Language.Egison.Parser.Pattern.Prim.ParseMode
                                                ( ParseMode )
import           Language.Egison.Parser.Pattern.Prim.Error
                                                ( Errors
                                                , CustomError
                                                , fromParseErrorBundle
                                                )


-- | Parser monad.
newtype Parse n v e s a = Parse { unParse :: ReaderT (ParseMode n v e s) (Parsec (CustomError s) s) a }
  deriving newtype (Functor, Applicative, Alternative, Monad, MonadFail, MonadPlus)
  deriving newtype (MonadReader (ParseMode n v e s))
  deriving newtype (MonadParsec (CustomError s) s)

instance Parsec.Stream s => Locate (Parse n v e s) where
  getPosition = fromSourcePos <$> Parsec.getSourcePos


-- | Run 'Parse' monad and produce a parse result.
runParse
  :: (Source s, MonadError (Errors s) m)
  => Parse n v e s a
  -> ParseMode n v e s
  -> FilePath
  -> s
  -> m a
runParse parse mode filename content =
  case Parsec.parse parsec filename content of
    Left  bundle -> throwError $ fromParseErrorBundle bundle
    Right e      -> pure e
 where
  parsec = runReaderT (unParse file) mode
  file   = parse <* Parsec.eof