module Text.Megaparsec.Error
( Message (..)
, isUnexpected
, isExpected
, isMessage
, messageString
, badMessage
, ParseError
, errorPos
, errorMessages
, errorIsUnknown
, newErrorMessage
, newErrorMessages
, newErrorUnknown
, addErrorMessage
, addErrorMessages
, setErrorMessage
, setErrorPos
, mergeError
, showMessages )
where
import Control.Exception (Exception)
import Data.Foldable (find, concat)
import Data.List (intercalate)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (fromMaybe, fromJust)
import Data.Semigroup (Semigroup((<>)))
import Data.Typeable (Typeable)
import Prelude hiding (concat)
import qualified Data.List.NonEmpty as NE
import Text.Megaparsec.Pos
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative ((<$>))
import Data.Foldable (foldMap)
import Data.Monoid (Monoid(..))
#endif
data Message
= Unexpected !String
| Expected !String
| Message !String
deriving (Show, Eq, Ord)
isUnexpected :: Message -> Bool
isUnexpected (Unexpected _) = True
isUnexpected _ = False
isExpected :: Message -> Bool
isExpected (Expected _) = True
isExpected _ = False
isMessage :: Message -> Bool
isMessage (Message _) = True
isMessage _ = False
messageString :: Message -> String
messageString (Unexpected s) = s
messageString (Expected s) = s
messageString (Message s) = s
badMessage :: Message -> Bool
badMessage = null . messageString
data ParseError = ParseError
{
errorPos :: !SourcePos
, errorMessages :: [Message] }
deriving (Eq, Typeable)
instance Show ParseError where
show e = show (errorPos e) ++ ":\n" ++ showMessages (errorMessages e)
instance Monoid ParseError where
mempty = newErrorUnknown (initialPos "")
mappend = (<>)
instance Semigroup ParseError where
(<>) = mergeError
instance Exception ParseError
errorIsUnknown :: ParseError -> Bool
errorIsUnknown (ParseError _ ms) = null ms
newErrorMessage :: Message -> SourcePos -> ParseError
newErrorMessage m = newErrorMessages [m]
newErrorMessages :: [Message] -> SourcePos -> ParseError
newErrorMessages ms pos = addErrorMessages ms $ newErrorUnknown pos
newErrorUnknown :: SourcePos -> ParseError
newErrorUnknown pos = ParseError pos []
addErrorMessage :: Message -> ParseError -> ParseError
addErrorMessage m (ParseError pos ms) =
ParseError pos $ if badMessage m then ms else pre ++ [m] ++ post
where pre = filter (< m) ms
post = filter (> m) ms
addErrorMessages :: [Message] -> ParseError -> ParseError
addErrorMessages ms err = foldr addErrorMessage err ms
setErrorMessage :: Message -> ParseError -> ParseError
setErrorMessage m (ParseError pos ms) =
if badMessage m then err else addErrorMessage m err
where err = ParseError pos (filter (not . f) ms)
f = fromJust $ find ($ m) [isUnexpected, isExpected, isMessage]
setErrorPos :: SourcePos -> ParseError -> ParseError
setErrorPos pos (ParseError _ ms) = ParseError pos ms
mergeError :: ParseError -> ParseError -> ParseError
mergeError e1@(ParseError pos1 _) e2@(ParseError pos2 ms2) =
case pos1 `compare` pos2 of
LT -> e2
EQ -> addErrorMessages ms2 e1
GT -> e1
showMessages :: [Message] -> String
showMessages [] = "unknown parse error"
showMessages ms = tail $ foldMap (fromMaybe "") (zipWith f ns rs)
where (unexpected, ms') = span isUnexpected ms
(expected, messages) = span isExpected ms'
f prefix m = (prefix ++) <$> m
ns = ["\nunexpected ","\nexpecting ","\n"]
rs = (renderMsgs orList <$> [unexpected, expected]) ++
[renderMsgs (concat . NE.intersperse "\n") messages]
renderMsgs
:: (NonEmpty String -> String)
-> [Message]
-> Maybe String
renderMsgs f ms = f . fmap messageString <$> NE.nonEmpty ms
orList :: NonEmpty String -> String
orList (x:|[]) = x
orList (x:|[y]) = x ++ " or " ++ y
orList xs = intercalate ", " (NE.init xs) ++ ", or " ++ NE.last xs