megaparsec/Text/MegaParsec/Error.hs

174 lines
5.7 KiB
Haskell
Raw Normal View History

2008-01-13 20:53:15 +03:00
-- |
2015-07-28 16:32:19 +03:00
-- Module : Text.MegaParsec.Error
-- Copyright : © 19992001 Daan Leijen, © 2007 Paolo Martini, © 2015 MegaParsec contributors
-- License : BSD3
--
-- Maintainer : Mark Karpov <markkarpov@opmbx.org>
2015-07-29 11:38:32 +03:00
-- Stability : experimental
2008-01-13 20:53:15 +03:00
-- Portability : portable
2015-07-28 16:32:19 +03:00
--
-- Parse errors.
2008-01-13 20:53:15 +03:00
2015-07-28 16:32:19 +03:00
module Text.MegaParsec.Error
( Message (SysUnExpect, UnExpect, Expect, Message)
2008-01-13 20:53:15 +03:00
, messageString
2015-07-28 16:32:19 +03:00
, ParseError
, errorPos
, errorMessages
, errorIsUnknown
2008-01-13 20:53:15 +03:00
, showErrorMessages
2015-07-28 16:32:19 +03:00
, newErrorMessage
, newErrorUnknown
, addErrorMessage
, setErrorPos
, setErrorMessage
, mergeError )
where
2008-01-13 20:53:15 +03:00
2015-07-29 11:38:32 +03:00
import Data.List (nub, sort, intercalate)
2008-01-13 20:53:15 +03:00
2015-07-28 16:32:19 +03:00
import Text.MegaParsec.Pos
2008-01-13 20:53:15 +03:00
-- | This abstract data type represents parse error messages. There are
-- four kinds of messages:
--
2015-07-29 11:38:32 +03:00
-- > data Message = SysUnExpect String
-- > | UnExpect String
-- > | Expect String
-- > | Message String
2015-07-28 16:32:19 +03:00
--
2015-07-29 11:38:32 +03:00
-- The fine distinction between different kinds of parse errors allows the
-- system to generate quite good error messages for the user. It also allows
-- error messages that are formatted in different languages. Each kind of
-- message is generated by different combinators:
--
-- * A 'SysUnExpect' message is automatically generated by the
-- 'Text.Parsec.Combinator.satisfy' combinator. The argument is the
-- unexpected input.
--
2015-07-29 11:38:32 +03:00
-- * A 'UnExpect' message is generated by the
-- 'Text.Parsec.Prim.unexpected' combinator. The argument describes
-- the unexpected item.
--
-- * A 'Expect' message is generated by the 'Text.Parsec.Prim.<?>'
-- combinator. The argument describes the expected item.
--
2015-07-29 11:38:32 +03:00
-- * A 'Message' message is generated by the 'fail' combinator. The
-- argument is some general parser message.
2008-01-13 20:53:15 +03:00
data Message = SysUnExpect !String -- @ library generated unexpect
| UnExpect !String -- @ unexpected something
| Expect !String -- @ expecting something
| Message !String -- @ raw message
instance Enum Message where
fromEnum (SysUnExpect _) = 0
fromEnum (UnExpect _) = 1
fromEnum (Expect _) = 2
fromEnum (Message _) = 3
toEnum _ = error "toEnum is undefined for Message"
instance Eq Message where
m1 == m2 = fromEnum m1 == fromEnum m2
instance Ord Message where
2015-07-29 11:38:32 +03:00
compare m1 m2 = compare (fromEnum m1) (fromEnum m2)
2008-01-13 20:53:15 +03:00
2015-07-28 16:32:19 +03:00
-- | Extract the message string from an error message
2008-01-13 20:53:15 +03:00
messageString :: Message -> String
messageString (SysUnExpect s) = s
messageString (UnExpect s) = s
messageString (Expect s) = s
messageString (Message s) = s
-- | The abstract data type @ParseError@ represents parse errors. It
2015-07-29 11:38:32 +03:00
-- provides the source position ('SourcePos') of the error and a list of
-- error messages ('Message'). A @ParseError@ can be returned by the
-- function 'Text.Parsec.Prim.parse'. @ParseError@ is an instance of the
-- 'Show' and 'Eq' classes.
2008-01-13 20:53:15 +03:00
data ParseError = ParseError !SourcePos [Message]
2015-07-29 11:38:32 +03:00
instance Show ParseError where
show e = show (errorPos e) ++ ":" ++ showErrorMessages (errorMessages e)
instance Eq ParseError where
l == r = errorPos l == errorPos r && mStrs l == mStrs r
where mStrs = fmap messageString . errorMessages
-- | Extract the source position from the parse error.
2008-01-13 20:53:15 +03:00
errorPos :: ParseError -> SourcePos
2015-07-29 11:38:32 +03:00
errorPos (ParseError pos _) = pos
2008-01-13 20:53:15 +03:00
2015-07-29 11:38:32 +03:00
-- | Extract the list of error messages from the parse error.
2008-01-13 20:53:15 +03:00
errorMessages :: ParseError -> [Message]
2015-07-29 11:38:32 +03:00
errorMessages (ParseError _ ms) = sort ms
2008-01-13 20:53:15 +03:00
errorIsUnknown :: ParseError -> Bool
2015-07-29 11:38:32 +03:00
errorIsUnknown (ParseError _ ms) = null ms
2008-01-13 20:53:15 +03:00
2015-07-29 11:38:32 +03:00
-- Creation of parse errors
2008-01-13 20:53:15 +03:00
newErrorUnknown :: SourcePos -> ParseError
2015-07-29 11:38:32 +03:00
newErrorUnknown pos = ParseError pos []
2008-01-13 20:53:15 +03:00
newErrorMessage :: Message -> SourcePos -> ParseError
2015-07-29 11:38:32 +03:00
newErrorMessage m pos = ParseError pos [m]
2008-01-13 20:53:15 +03:00
addErrorMessage :: Message -> ParseError -> ParseError
2015-07-29 11:38:32 +03:00
addErrorMessage m (ParseError pos ms) = ParseError pos (m:ms)
2008-01-13 20:53:15 +03:00
setErrorPos :: SourcePos -> ParseError -> ParseError
2015-07-29 11:38:32 +03:00
setErrorPos pos (ParseError _ ms) = ParseError pos ms
2008-01-13 20:53:15 +03:00
setErrorMessage :: Message -> ParseError -> ParseError
2015-07-29 11:38:32 +03:00
setErrorMessage m (ParseError pos ms) = ParseError pos (m : filter (m /=) ms)
2008-01-13 20:53:15 +03:00
mergeError :: ParseError -> ParseError -> ParseError
2015-07-29 11:38:32 +03:00
mergeError e1@(ParseError pos1 ms1) e2@(ParseError pos2 ms2)
-- prefer meaningful errors
2015-07-29 11:38:32 +03:00
| null ms2 && not (null ms1) = e1
| null ms1 && not (null ms2) = e2
| otherwise
2015-07-29 11:38:32 +03:00
= case pos1 `compare` pos2 of
-- select the longest match
EQ -> ParseError pos1 (ms1 ++ ms2)
GT -> e1
LT -> e2
showErrorMessages :: [Message] -> String
showErrorMessages ms
| null ms = "unknown parse error"
| otherwise = concatMap ("\n" ++) $ clean
[showSysUnExpect, showUnExpect, showExpect, showMessages]
2008-01-13 20:53:15 +03:00
where
2015-07-29 11:38:32 +03:00
(sysUnExpect, ms1) = span (SysUnExpect "" ==) ms
(unExpect, ms2) = span (UnExpect "" ==) ms1
(expect, messages) = span (Expect "" ==) ms2
firstMsg = messageString (head sysUnExpect)
showExpect = showMany "expecting" expect
showUnExpect = showMany "unexpected" unExpect
showSysUnExpect
| not (null unExpect) || null sysUnExpect = ""
| null firstMsg = "unexpected end of input"
| otherwise = "unexpected " ++ firstMsg
showMessages = showMany "" messages
showMany pre msgs =
case clean (messageString <$> msgs) of
[] -> ""
xs | null pre -> commasOr xs
| otherwise -> pre ++ " " ++ commasOr xs
commasOr [] = ""
commasOr [x] = x
commasOr xs = commaSep (init xs) ++ " or " ++ last xs
commaSep = intercalate ", " . clean
clean = nub . filter (not . null)