{-# LANGUAGE BangPatterns         #-}
{-# LANGUAGE DeriveDataTypeable   #-}
{-# LANGUAGE DeriveFunctor        #-}
{-# LANGUAGE DeriveGeneric        #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE LambdaCase           #-}
{-# LANGUAGE RecordWildCards      #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE StandaloneDeriving   #-}
{-# LANGUAGE UndecidableInstances #-}
module Text.Megaparsec.Error
  ( 
    ErrorItem (..)
  , ErrorFancy (..)
  , ParseError (..)
  , mapParseError
  , errorOffset
  , setErrorOffset
  , ParseErrorBundle (..)
  , attachSourcePos
    
  , ShowErrorComponent (..)
  , errorBundlePretty
  , parseErrorPretty
  , parseErrorTextPretty )
where
import Control.DeepSeq
import Control.Exception
import Control.Monad.State.Strict
import Data.Data (Data)
import Data.List (intercalate)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (isNothing)
import Data.Proxy
import Data.Set (Set)
import Data.Typeable (Typeable)
import Data.Void
import GHC.Generics
import Text.Megaparsec.Pos
import Text.Megaparsec.State
import Text.Megaparsec.Stream
import qualified Data.List.NonEmpty as NE
import qualified Data.Set           as E
data ErrorItem t
  = Tokens (NonEmpty t)      
  | Label (NonEmpty Char)    
  | EndOfInput               
  deriving (Show, Read, Eq, Ord, Data, Typeable, Generic, Functor)
instance NFData t => NFData (ErrorItem t)
data ErrorFancy e
  = ErrorFail String
    
  | ErrorIndentation Ordering Pos Pos
    
    
    
  | ErrorCustom e
    
  deriving (Show, Read, Eq, Ord, Data, Typeable, Generic, Functor)
instance NFData a => NFData (ErrorFancy a) where
  rnf (ErrorFail str) = rnf str
  rnf (ErrorIndentation ord ref act) = ord `seq` rnf ref `seq` rnf act
  rnf (ErrorCustom a) = rnf a
data ParseError s e
  = TrivialError Int (Maybe (ErrorItem (Token s))) (Set (ErrorItem (Token s)))
    
    
    
    
    
  | FancyError Int (Set (ErrorFancy e))
    
    
    
  deriving (Typeable, Generic)
deriving instance ( Show (Token s)
                  , Show e
                  ) => Show (ParseError s e)
deriving instance ( Eq (Token s)
                  , Eq e
                  ) => Eq (ParseError s e)
deriving instance ( Data s
                  , Data (Token s)
                  , Ord (Token s)
                  , Data e
                  , Ord e
                  ) => Data (ParseError s e)
instance ( NFData (Token s)
         , NFData e
         ) => NFData (ParseError s e)
instance (Stream s, Ord e) => Semigroup (ParseError s e) where
  (<>) = mergeError
  {-# INLINE (<>) #-}
instance (Stream s, Ord e) => Monoid (ParseError s e) where
  mempty  = TrivialError 0 Nothing E.empty
  mappend = (<>)
  {-# INLINE mappend #-}
instance ( Show s
         , Show (Token s)
         , Show e
         , ShowErrorComponent e
         , Stream s
         , Typeable s
         , Typeable e )
  => Exception (ParseError s e) where
  displayException = parseErrorPretty
mapParseError :: Ord e'
  => (e -> e')
  -> ParseError s e
  -> ParseError s e'
mapParseError _ (TrivialError o u p) = TrivialError o u p
mapParseError f (FancyError o x) = FancyError o (E.map (fmap f) x)
errorOffset :: ParseError s e -> Int
errorOffset (TrivialError o _ _) = o
errorOffset (FancyError   o _)   = o
setErrorOffset :: Int -> ParseError s e -> ParseError s e
setErrorOffset o (TrivialError _ u p) = TrivialError o u p
setErrorOffset o (FancyError _ x) = FancyError o x
mergeError :: (Stream s, Ord e)
  => ParseError s e
  -> ParseError s e
  -> ParseError s e
mergeError e1 e2 =
  case errorOffset e1 `compare` errorOffset e2 of
    LT -> e2
    EQ ->
      case (e1, e2) of
        (TrivialError s1 u1 p1, TrivialError _ u2 p2) ->
          TrivialError s1 (n u1 u2) (E.union p1 p2)
        (FancyError {}, TrivialError {}) -> e1
        (TrivialError {}, FancyError {}) -> e2
        (FancyError s1 x1, FancyError _ x2) ->
          FancyError s1 (E.union x1 x2)
    GT -> e1
  where
    
    
    
    
    
    
    
    
    n Nothing  Nothing = Nothing
    n (Just x) Nothing = Just x
    n Nothing (Just y) = Just y
    n (Just x) (Just y) = Just (max x y)
{-# INLINE mergeError #-}
data ParseErrorBundle s e = ParseErrorBundle
  { bundleErrors :: NonEmpty (ParseError s e)
    
  , bundlePosState :: PosState s
    
  } deriving (Generic)
deriving instance ( Show s
                  , Show (Token s)
                  , Show e
                  ) => Show (ParseErrorBundle s e)
deriving instance ( Eq s
                  , Eq (Token s)
                  , Eq e
                  ) => Eq (ParseErrorBundle s e)
deriving instance ( Typeable s
                  , Typeable (Token s)
                  , Typeable e
                  ) => Typeable (ParseErrorBundle s e)
deriving instance ( Data s
                  , Data (Token s)
                  , Ord (Token s)
                  , Data e
                  , Ord e
                  ) => Data (ParseErrorBundle s e)
instance ( NFData s
         , NFData (Token s)
         , NFData e
         ) => NFData (ParseErrorBundle s e)
instance ( Show s
         , Show (Token s)
         , Show e
         , ShowErrorComponent e
         , Stream s
         , Typeable s
         , Typeable e
         ) => Exception (ParseErrorBundle s e) where
  displayException = errorBundlePretty
attachSourcePos
  :: (Traversable t, Stream s)
  => (a -> Int) 
  -> t a               
  -> PosState s        
  -> (t (a, SourcePos), PosState s) 
                                    
attachSourcePos projectOffset xs = runState (traverse f xs)
  where
    f a = do
      pst <- get
      let pst' = reachOffsetNoLine (projectOffset a) pst
      put pst'
      return (a, pstateSourcePos pst')
{-# INLINEABLE attachSourcePos #-}
class Ord a => ShowErrorComponent a where
  
  showErrorComponent :: a -> String
  
  
  
  
  errorComponentLen :: a -> Int
  errorComponentLen _ = 1
instance ShowErrorComponent Void where
  showErrorComponent = absurd
errorBundlePretty
  :: forall s e. ( Stream s
                 , ShowErrorComponent e
                 )
  => ParseErrorBundle s e 
  -> String               
errorBundlePretty ParseErrorBundle {..} =
  let (r, _) = foldl f (id, bundlePosState) bundleErrors
  in drop 1 (r "")
  where
    f :: (ShowS, PosState s)
      -> ParseError s e
      -> (ShowS, PosState s)
    f (o, !pst) e = (o . (outChunk ++), pst')
      where
        (sline, pst') = reachOffset (errorOffset e) pst
        epos = pstateSourcePos pst'
        outChunk =
          "\n" <> sourcePosPretty epos <> ":\n" <>
          padding <> "|\n" <>
          lineNumber <> " | " <> sline <> "\n" <>
          padding <> "| " <> rpadding <> pointer <> "\n" <>
          parseErrorTextPretty e
        lineNumber = (show . unPos . sourceLine) epos
        padding = replicate (length lineNumber + 1) ' '
        rpadding =
          if pointerLen > 0
            then replicate rpshift ' '
            else ""
        rpshift = unPos (sourceColumn epos) - 1
        pointer = replicate pointerLen '^'
        pointerLen =
          if rpshift + elen > slineLen
            then slineLen - rpshift + 1
            else elen
        slineLen = length sline
        pxy = Proxy :: Proxy s
        elen =
          case e of
            TrivialError _ Nothing _ -> 1
            TrivialError _ (Just x) _ -> errorItemLength pxy x
            FancyError _ xs ->
              E.foldl' (\a b -> max a (errorFancyLength b)) 1 xs
parseErrorPretty
  :: (Stream s, ShowErrorComponent e)
  => ParseError s e    
  -> String            
parseErrorPretty e =
  "offset=" <> show (errorOffset e) <> ":\n" <> parseErrorTextPretty e
parseErrorTextPretty
  :: forall s e. (Stream s, ShowErrorComponent e)
  => ParseError s e    
  -> String            
parseErrorTextPretty (TrivialError _ us ps) =
  if isNothing us && E.null ps
    then "unknown parse error\n"
    else messageItemsPretty "unexpected " (showErrorItem pxy `E.map` maybe E.empty E.singleton us) <>
         messageItemsPretty "expecting "  (showErrorItem pxy `E.map` ps)
  where
    pxy = Proxy :: Proxy s
parseErrorTextPretty (FancyError _ xs) =
  if E.null xs
    then "unknown fancy parse error\n"
    else unlines (showErrorFancy <$> E.toAscList xs)
showErrorItem :: Stream s => Proxy s -> ErrorItem (Token s) -> String
showErrorItem pxy = \case
    Tokens   ts -> showTokens pxy ts
    Label label -> NE.toList label
    EndOfInput  -> "end of input"
errorItemLength :: Stream s => Proxy s -> ErrorItem (Token s) -> Int
errorItemLength pxy = \case
  Tokens ts -> tokensLength pxy ts
  _         -> 1
showErrorFancy :: ShowErrorComponent e => ErrorFancy e -> String
showErrorFancy = \case
  ErrorFail msg -> msg
  ErrorIndentation ord ref actual ->
    "incorrect indentation (got " <> show (unPos actual) <>
    ", should be " <> p <> show (unPos ref) <> ")"
    where
      p = case ord of
            LT -> "less than "
            EQ -> "equal to "
            GT -> "greater than "
  ErrorCustom a -> showErrorComponent a
errorFancyLength :: ShowErrorComponent e => ErrorFancy e -> Int
errorFancyLength = \case
  ErrorCustom a -> errorComponentLen a
  _             -> 1
messageItemsPretty
  :: String            
  -> Set String        
  -> String            
messageItemsPretty prefix ts
  | E.null ts = ""
  | otherwise =
    prefix <> (orList . NE.fromList . E.toAscList) ts <> "\n"
orList :: NonEmpty String -> String
orList (x:|[])  = x
orList (x:|[y]) = x <> " or " <> y
orList xs       = intercalate ", " (NE.init xs) <> ", or " <> NE.last xs