{-# 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 qualified Data.List.NonEmpty as NE
import Data.Maybe (isNothing)
import Data.Proxy
import Data.Set (Set)
import qualified Data.Set as E
import Data.Typeable (Typeable)
import Data.Void
import GHC.Generics
import Text.Megaparsec.Pos
import Text.Megaparsec.State
import Text.Megaparsec.Stream
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,
    VisualStream 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,
    VisualStream s,
    TraversableStream s,
    Typeable s,
    Typeable e
  ) =>
  Exception (ParseErrorBundle s e)
  where
  displayException = errorBundlePretty
attachSourcePos ::
  (Traversable t, TraversableStream 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.
  ( VisualStream s,
    TraversableStream 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
        (msline, pst') = reachOffset (errorOffset e) pst
        epos = pstateSourcePos pst'
        outChunk =
          "\n" <> sourcePosPretty epos <> ":\n"
            <> offendingLine
            <> parseErrorTextPretty e
        offendingLine =
          case msline of
            Nothing -> ""
            Just sline ->
              let rpadding =
                    if pointerLen > 0
                      then replicate rpshift ' '
                      else ""
                  pointerLen =
                    if rpshift + elen > slineLen
                      then slineLen - rpshift + 1
                      else elen
                  pointer = replicate pointerLen '^'
                  lineNumber = (show . unPos . sourceLine) epos
                  padding = replicate (length lineNumber + 1) ' '
                  rpshift = unPos (sourceColumn epos) - 1
                  slineLen = length sline
               in padding <> "|\n" <> lineNumber <> " | " <> sline
                    <> "\n"
                    <> padding
                    <> "| "
                    <> rpadding
                    <> pointer
                    <> "\n"
        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 ::
  (VisualStream s, ShowErrorComponent e) =>
  
  ParseError s e ->
  
  String
parseErrorPretty e =
  "offset=" <> show (errorOffset e) <> ":\n" <> parseErrorTextPretty e
parseErrorTextPretty ::
  forall s e.
  (VisualStream 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 :: VisualStream 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 :: VisualStream 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