packages feed

zuramaru-0.1.0.0: src/Maru/Type/Parser.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE TemplateHaskell #-}

-- | The type for Maru.Parser
module Maru.Type.Parser
  ( ParseLog (..)
  , ParseLogs
  , MaruParser
  , runMaruParser
  , tell'
  , ParseErrorResult
  ) where

import Control.Applicative (Alternative)
import Control.Monad (MonadPlus)
import Control.Monad.Fail (MonadFail)
import Control.Monad.State.Class (MonadState, modify)
import Control.Monad.State.Lazy (State, runState)
import Data.Sequence (Seq, (|>))
import Data.String (IsString)
import Data.Text (Text)
import Maru.Type.SExpr
import Text.Megaparsec (ParsecT, ParseError, Dec, runParserT)
import Text.Megaparsec.Prim (MonadParsec)
import qualified Data.Text as T
import qualified GHC.Exts as L

-- | The parse result of the failure
type ParseErrorResult = ParseError MaruToken Dec

-- | A log message of the parsing, for debug
newtype ParseLog = ParseLog { unParseLog :: Text }
  deriving (IsString, Monoid)

instance Show ParseLog where
  show = show . T.unpack . unParseLog

type ParseLogs = Seq ParseLog

-- |
-- A parser for the code of maru
--
-- NOTE:
-- This `MonadState` instance is instead of `MonadWriter`,
-- because `ParsecT` is not the `MonadWriter` instance.
newtype MaruParser a = MaruParser { unMaruParser :: ParsecT Dec Text (State ParseLogs) a }
  deriving ( Functor, Applicative, Monad
           , Alternative, MonadPlus
           , MonadState ParseLogs
           , MonadParsec Dec Text
           , MonadFail
           )

-- |
-- Run parser and extract result and logs.
runMaruParser :: MaruParser a -> Text -> (Either ParseErrorResult a, [ParseLog])
runMaruParser parser source =
  let bareness = unMaruParser parser
      (result, logs) = flip runState [] $ runParserT bareness "" source
  in (result, L.toList logs)

-- | Append a log to head of _parseLogs in the parsing
tell' :: ParseLog -> MaruParser ()
tell' log = modify (|> log)