2008-01-13 20:53:15 +03:00
|
|
|
|
-- |
|
2015-08-01 19:24:45 +03:00
|
|
|
|
-- Module : Text.Megaparsec.Error
|
|
|
|
|
-- Copyright : © 2015 Megaparsec contributors
|
2015-07-30 19:20:37 +03:00
|
|
|
|
-- © 2007 Paolo Martini
|
|
|
|
|
-- © 1999–2001 Daan Leijen
|
2015-07-28 16:32:19 +03:00
|
|
|
|
-- 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-08-01 19:24:45 +03:00
|
|
|
|
module Text.Megaparsec.Error
|
2015-07-28 16:32:19 +03:00
|
|
|
|
( 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
|
|
|
|
|
, newErrorMessage
|
|
|
|
|
, newErrorUnknown
|
|
|
|
|
, addErrorMessage
|
2015-08-08 12:48:20 +03:00
|
|
|
|
, setErrorMessage
|
2015-07-28 16:32:19 +03:00
|
|
|
|
, setErrorPos
|
2015-08-03 20:44:40 +03:00
|
|
|
|
, mergeError
|
|
|
|
|
, showMessages )
|
2015-07-28 16:32:19 +03:00
|
|
|
|
where
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-08-11 00:19:16 +03:00
|
|
|
|
import Data.List (intercalate)
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-08-01 19:24:45 +03:00
|
|
|
|
import Text.Megaparsec.Pos
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2008-01-20 09:15:04 +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:
|
2008-01-20 09:15:04 +03:00
|
|
|
|
--
|
|
|
|
|
-- * A 'SysUnExpect' message is automatically generated by the
|
2008-01-22 04:35:30 +03:00
|
|
|
|
-- 'Text.Parsec.Combinator.satisfy' combinator. The argument is the
|
2008-01-20 09:15:04 +03:00
|
|
|
|
-- 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.
|
2008-01-20 09:15:04 +03:00
|
|
|
|
--
|
2008-01-22 04:35:30 +03:00
|
|
|
|
-- * A 'Expect' message is generated by the 'Text.Parsec.Prim.<?>'
|
2008-01-20 09:15:04 +03:00
|
|
|
|
-- 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
|
2015-08-03 20:44:40 +03:00
|
|
|
|
deriving Show
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
|
|
|
|
instance Enum Message where
|
|
|
|
|
fromEnum (SysUnExpect _) = 0
|
|
|
|
|
fromEnum (UnExpect _) = 1
|
|
|
|
|
fromEnum (Expect _) = 2
|
|
|
|
|
fromEnum (Message _) = 3
|
2015-08-03 20:44:40 +03:00
|
|
|
|
toEnum _ = error "Text.Megaparsec.Error: toEnum is undefined for Message"
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
|
|
|
|
instance Eq Message where
|
2015-08-08 12:48:20 +03:00
|
|
|
|
m1 == m2 =
|
|
|
|
|
fromEnum m1 == fromEnum m2 && messageString m1 == messageString m2
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
|
|
|
|
instance Ord Message where
|
2015-08-08 12:48:20 +03:00
|
|
|
|
compare m1 m2 =
|
|
|
|
|
case compare (fromEnum m1) (fromEnum m2) of
|
|
|
|
|
LT -> LT
|
|
|
|
|
EQ -> compare (messageString m1) (messageString m2)
|
|
|
|
|
GT -> GT
|
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-20 09:15:04 +03:00
|
|
|
|
|
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
|
|
|
|
|
|
2008-01-20 09:15:04 +03:00
|
|
|
|
-- | 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
|
|
|
|
|
2015-08-03 20:44:40 +03:00
|
|
|
|
data ParseError = ParseError
|
|
|
|
|
{ -- | Extract the source position from the parse error.
|
|
|
|
|
errorPos :: !SourcePos
|
|
|
|
|
-- | Extract the list of error messages from the parse error.
|
|
|
|
|
, errorMessages :: [Message] }
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-07-29 11:38:32 +03:00
|
|
|
|
instance Show ParseError where
|
2015-08-03 20:44:40 +03:00
|
|
|
|
show e = show (errorPos e) ++ ":\n" ++ showMessages (errorMessages e)
|
2015-07-29 11:38:32 +03:00
|
|
|
|
|
|
|
|
|
instance Eq ParseError where
|
2015-08-08 12:48:20 +03:00
|
|
|
|
l == r =
|
|
|
|
|
errorPos l == errorPos r && errorMessages l == errorMessages r
|
2015-07-29 11:38:32 +03:00
|
|
|
|
|
2015-07-30 21:36:54 +03:00
|
|
|
|
-- | Test whether given @ParseError@ has associated collection of error
|
|
|
|
|
-- messages. Return @True@ if it has none and @False@ otherwise.
|
|
|
|
|
|
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
|
|
|
|
|
2015-07-30 21:36:54 +03:00
|
|
|
|
-- | @newErrorUnknown pos@ creates @ParseError@ without any associated
|
|
|
|
|
-- message but with specified position @pos@.
|
|
|
|
|
|
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
|
|
|
|
|
2015-07-30 21:36:54 +03:00
|
|
|
|
-- | @newErrorMessage m pos@ creates @ParseError@ with message @m@ and
|
|
|
|
|
-- associated position @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
|
|
|
|
|
2015-07-30 21:36:54 +03:00
|
|
|
|
-- | @addErrorMessage m err@ returns @ParseError@ @err@ with message @m@
|
2015-08-03 20:44:40 +03:00
|
|
|
|
-- added. This function makes sure that list of messages is always ordered
|
|
|
|
|
-- and doesn't contain duplicates.
|
2015-07-30 21:36:54 +03:00
|
|
|
|
|
2008-01-13 20:53:15 +03:00
|
|
|
|
addErrorMessage :: Message -> ParseError -> ParseError
|
2015-08-03 20:44:40 +03:00
|
|
|
|
addErrorMessage m (ParseError pos ms) = ParseError pos (pre ++ [m] ++ post)
|
|
|
|
|
where pre = filter (< m) ms
|
|
|
|
|
post = filter (> m) ms
|
2008-01-13 20:53:15 +03:00
|
|
|
|
|
2015-08-08 12:48:20 +03:00
|
|
|
|
-- | @setErrorMessage m err@ returns @err@ with message @m@ added. This
|
2015-08-08 12:53:40 +03:00
|
|
|
|
-- function also deletes all existing error messages that were created with
|
|
|
|
|
-- the same constructor as @m@.
|
2015-08-08 12:48:20 +03:00
|
|
|
|
|
|
|
|
|
setErrorMessage :: Message -> ParseError -> ParseError
|
2015-08-08 21:38:30 +03:00
|
|
|
|
setErrorMessage m (ParseError pos ms) = addErrorMessage m (ParseError pos xs)
|
2015-08-08 12:48:20 +03:00
|
|
|
|
where xs = filter ((/= fromEnum m) . fromEnum) ms
|
|
|
|
|
|
2015-07-30 21:36:54 +03:00
|
|
|
|
-- | @setErrorPos pos err@ returns @ParseError@ identical to @err@, but with
|
|
|
|
|
-- position @pos@.
|
|
|
|
|
|
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
|
|
|
|
|
2015-07-30 21:36:54 +03:00
|
|
|
|
-- | Merge two error data structures into one joining their collections of
|
2015-08-03 20:44:40 +03:00
|
|
|
|
-- messages and preferring shortest match.
|
2015-07-30 21:36:54 +03:00
|
|
|
|
|
2008-01-13 20:53:15 +03:00
|
|
|
|
mergeError :: ParseError -> ParseError -> ParseError
|
2015-08-03 20:44:40 +03:00
|
|
|
|
mergeError e1@(ParseError pos1 ms1) e2@(ParseError pos2 ms2) =
|
|
|
|
|
case pos1 `compare` pos2 of
|
|
|
|
|
LT -> e1
|
2015-08-11 00:19:16 +03:00
|
|
|
|
EQ -> foldr addErrorMessage (ParseError pos1 ms1) ms2
|
2015-08-03 20:44:40 +03:00
|
|
|
|
GT -> e2
|
|
|
|
|
|
|
|
|
|
-- | @showMessages ms@ transforms list of error messages @ms@ into
|
2015-07-30 21:36:54 +03:00
|
|
|
|
-- their textual representation.
|
|
|
|
|
|
2015-08-03 20:44:40 +03:00
|
|
|
|
showMessages :: [Message] -> String
|
|
|
|
|
showMessages [] = "unknown parse error"
|
|
|
|
|
showMessages ms =
|
|
|
|
|
intercalate "\n" $ clean [sysUnExpect', unExpect', expect', msgs']
|
2008-01-13 20:53:15 +03:00
|
|
|
|
where
|
2015-08-08 12:48:20 +03:00
|
|
|
|
(sysUnExpect, ms1) = span ((== 0) . fromEnum) ms
|
|
|
|
|
(unExpect, ms2) = span ((== 1) . fromEnum) ms1
|
|
|
|
|
(expect, messages) = span ((== 2) . fromEnum) ms2
|
2015-07-29 11:38:32 +03:00
|
|
|
|
|
2015-08-03 20:44:40 +03:00
|
|
|
|
sysUnExpect'
|
2015-07-29 11:38:32 +03:00
|
|
|
|
| not (null unExpect) || null sysUnExpect = ""
|
2015-08-08 12:48:20 +03:00
|
|
|
|
| otherwise = showMany "unexpected " (emptyToEnd <$> sysUnExpect)
|
2015-08-03 20:44:40 +03:00
|
|
|
|
unExpect' = showMany "unexpected " unExpect
|
|
|
|
|
expect' = showMany "expecting " expect
|
|
|
|
|
msgs' = showMany "" messages
|
2015-07-29 11:38:32 +03:00
|
|
|
|
|
2015-08-08 12:48:20 +03:00
|
|
|
|
emptyToEnd (SysUnExpect x) =
|
|
|
|
|
SysUnExpect $ if null x then "end of input" else x
|
|
|
|
|
emptyToEnd x = x
|
|
|
|
|
|
2015-07-29 11:38:32 +03:00
|
|
|
|
showMany pre msgs =
|
|
|
|
|
case clean (messageString <$> msgs) of
|
|
|
|
|
[] -> ""
|
|
|
|
|
xs | null pre -> commasOr xs
|
2015-08-03 20:44:40 +03:00
|
|
|
|
| otherwise -> pre ++ commasOr xs
|
2015-07-29 11:38:32 +03:00
|
|
|
|
|
|
|
|
|
commasOr [] = ""
|
|
|
|
|
commasOr [x] = x
|
2015-08-03 20:44:40 +03:00
|
|
|
|
commasOr xs = intercalate ", " (init xs) ++ " or " ++ last xs
|
2015-07-29 11:38:32 +03:00
|
|
|
|
|
2015-08-03 20:44:40 +03:00
|
|
|
|
clean = filter (not . null)
|