packages feed

paripari 0.1.0.0 → 0.2.0.0

raw patch · 19 files changed

+2518/−1448 lines, 19 filesdep +tastydep +tasty-hunitnew-component:exe:paripari-examplenew-component:exe:paripari-specialise-allPVP ok

version bump matches the API change (PVP)

Dependencies added: tasty, tasty-hunit

API changes (from Hackage documentation)

- Text.PariPari: runParser :: Parser a -> FilePath -> ByteString -> Either Report a
- Text.PariPari: runParserWithOptions :: ReportOptions -> Parser a -> FilePath -> ByteString -> Either Report a
- Text.PariPari: runSeqParser :: Parser a -> FilePath -> ByteString -> Either Report a
- Text.PariPari: runSeqParserWithOptions :: ReportOptions -> Parser a -> FilePath -> ByteString -> Either Report a
- Text.PariPari.Acceptor: data Acceptor a
- Text.PariPari.Acceptor: instance Control.Monad.Fail.MonadFail Text.PariPari.Acceptor.Acceptor
- Text.PariPari.Acceptor: instance GHC.Base.Alternative Text.PariPari.Acceptor.Acceptor
- Text.PariPari.Acceptor: instance GHC.Base.Applicative Text.PariPari.Acceptor.Acceptor
- Text.PariPari.Acceptor: instance GHC.Base.Functor Text.PariPari.Acceptor.Acceptor
- Text.PariPari.Acceptor: instance GHC.Base.Monad Text.PariPari.Acceptor.Acceptor
- Text.PariPari.Acceptor: instance GHC.Base.MonadPlus Text.PariPari.Acceptor.Acceptor
- Text.PariPari.Acceptor: instance GHC.Base.Monoid a => GHC.Base.Monoid (Text.PariPari.Acceptor.Acceptor a)
- Text.PariPari.Acceptor: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Text.PariPari.Acceptor.Acceptor a)
- Text.PariPari.Acceptor: instance Text.PariPari.Class.MonadParser Text.PariPari.Acceptor.Acceptor
- Text.PariPari.Acceptor: runAcceptor :: Acceptor a -> FilePath -> ByteString -> Either Error a
- Text.PariPari.Ascii: asc_0 :: Word8
- Text.PariPari.Ascii: asc_9 :: Word8
- Text.PariPari.Ascii: asc_A :: Word8
- Text.PariPari.Ascii: asc_E :: Word8
- Text.PariPari.Ascii: asc_P :: Word8
- Text.PariPari.Ascii: asc_a :: Word8
- Text.PariPari.Ascii: asc_e :: Word8
- Text.PariPari.Ascii: asc_minus :: Word8
- Text.PariPari.Ascii: asc_newline :: Word8
- Text.PariPari.Ascii: asc_p :: Word8
- Text.PariPari.Ascii: asc_plus :: Word8
- Text.PariPari.Ascii: asc_point :: Word8
- Text.PariPari.Ascii: byteS :: Word8 -> ShowS
- Text.PariPari.Ascii: bytesS :: ByteString -> ShowS
- Text.PariPari.Ascii: showByte :: Word8 -> String
- Text.PariPari.Ascii: showBytes :: ByteString -> String
- Text.PariPari.Ascii: unsafeAsciiToChar :: Word8 -> Char
- Text.PariPari.Class: (<|>) :: Alternative f => f a -> f a -> f a
- Text.PariPari.Class: ECombinator :: String -> Error
- Text.PariPari.Class: EEmpty :: Error
- Text.PariPari.Class: EExpected :: [String] -> Error
- Text.PariPari.Class: EExpectedEnd :: Error
- Text.PariPari.Class: EFail :: String -> Error
- Text.PariPari.Class: EIndentNotAligned :: !Int -> !Int -> Error
- Text.PariPari.Class: EIndentOverLine :: !Int -> !Int -> Error
- Text.PariPari.Class: EInvalidUtf8 :: Error
- Text.PariPari.Class: ENotEnoughIndent :: !Int -> !Int -> Error
- Text.PariPari.Class: EUnexpected :: String -> Error
- Text.PariPari.Class: Pos :: !Int -> !Int -> Pos
- Text.PariPari.Class: [_posColumn] :: Pos -> !Int
- Text.PariPari.Class: [_posLine] :: Pos -> !Int
- Text.PariPari.Class: asBytes :: MonadParser p => p () -> p ByteString
- Text.PariPari.Class: byte :: MonadParser p => Word8 -> p Word8
- Text.PariPari.Class: byteSatisfy :: MonadParser p => (Word8 -> Bool) -> p Word8
- Text.PariPari.Class: bytes :: MonadParser p => ByteString -> p ByteString
- Text.PariPari.Class: char :: MonadParser p => Char -> p Char
- Text.PariPari.Class: class Applicative f => Alternative (f :: * -> *)
- Text.PariPari.Class: class (MonadFail p, MonadPlus p) => MonadParser p
- Text.PariPari.Class: class (Alternative m, Monad m) => MonadPlus (m :: * -> *)
- Text.PariPari.Class: commit :: MonadParser p => p a -> p a
- Text.PariPari.Class: data ByteString
- Text.PariPari.Class: data Error
- Text.PariPari.Class: data Pos
- Text.PariPari.Class: data Word8
- Text.PariPari.Class: empty :: Alternative f => f a
- Text.PariPari.Class: eof :: MonadParser p => p ()
- Text.PariPari.Class: failWith :: MonadParser p => Error -> p a
- Text.PariPari.Class: getFile :: MonadParser p => p FilePath
- Text.PariPari.Class: getPos :: MonadParser p => p Pos
- Text.PariPari.Class: getRefPos :: MonadParser p => p Pos
- Text.PariPari.Class: hidden :: MonadParser p => p a -> p a
- Text.PariPari.Class: instance GHC.Classes.Eq Text.PariPari.Class.Error
- Text.PariPari.Class: instance GHC.Classes.Eq Text.PariPari.Class.Pos
- Text.PariPari.Class: instance GHC.Classes.Ord Text.PariPari.Class.Error
- Text.PariPari.Class: instance GHC.Generics.Generic Text.PariPari.Class.Error
- Text.PariPari.Class: instance GHC.Generics.Generic Text.PariPari.Class.Pos
- Text.PariPari.Class: instance GHC.Show.Show Text.PariPari.Class.Error
- Text.PariPari.Class: instance GHC.Show.Show Text.PariPari.Class.Pos
- Text.PariPari.Class: label :: MonadParser p => String -> p a -> p a
- Text.PariPari.Class: lookAhead :: MonadParser p => p a -> p a
- Text.PariPari.Class: notFollowedBy :: (MonadParser p, Show a) => p a -> p ()
- Text.PariPari.Class: satisfy :: MonadParser p => (Char -> Bool) -> p Char
- Text.PariPari.Class: showError :: Error -> String
- Text.PariPari.Class: type Parser a = (forall p. MonadParser p => p a)
- Text.PariPari.Class: withRefPos :: MonadParser p => p a -> p a
- Text.PariPari.Combinators: (:|) :: a -> [a] -> NonEmpty a
- Text.PariPari.Combinators: (<?>) :: MonadParser p => p a -> String -> p a
- Text.PariPari.Combinators: (<|>) :: Alternative f => f a -> f a -> f a
- Text.PariPari.Combinators: align :: Parser ()
- Text.PariPari.Combinators: alphaNumChar :: Parser Char
- Text.PariPari.Combinators: anyByte :: Parser Word8
- Text.PariPari.Combinators: anyChar :: Parser Char
- Text.PariPari.Combinators: asString :: MonadParser p => p () -> p Text
- Text.PariPari.Combinators: asciiByte :: Parser Word8
- Text.PariPari.Combinators: asciiChar :: Int -> Parser Char
- Text.PariPari.Combinators: between :: Applicative m => m open -> m close -> m a -> m a
- Text.PariPari.Combinators: categoryChar :: GeneralCategory -> Parser Char
- Text.PariPari.Combinators: char' :: Char -> Parser Char
- Text.PariPari.Combinators: choice :: (Foldable f, Alternative m) => f m a -> m a
- Text.PariPari.Combinators: count :: Monad m => Int -> m a -> m [a]
- Text.PariPari.Combinators: count' :: MonadPlus m => Int -> Int -> m a -> m [a]
- Text.PariPari.Combinators: data NonEmpty a
- Text.PariPari.Combinators: data Text
- Text.PariPari.Combinators: decimal :: Num a => Parser a
- Text.PariPari.Combinators: digit :: Int -> Parser Word
- Text.PariPari.Combinators: digitByte :: Int -> Parser Word8
- Text.PariPari.Combinators: digitChar :: Int -> Parser Char
- Text.PariPari.Combinators: eitherP :: Alternative m => m a -> m b -> m Either a b
- Text.PariPari.Combinators: empty :: Alternative f => f a
- Text.PariPari.Combinators: endBy :: MonadPlus m => m a -> m sep -> m [a]
- Text.PariPari.Combinators: endBy1 :: MonadPlus m => m a -> m sep -> m NonEmpty a
- Text.PariPari.Combinators: fractionDec :: (Num a, MonadParser p) => p digitSep -> p (a, Int, a)
- Text.PariPari.Combinators: fractionHex :: (Num a, MonadParser p) => p digitSep -> p (a, Int, a)
- Text.PariPari.Combinators: getColumn :: Parser Int
- Text.PariPari.Combinators: getLine :: Parser Int
- Text.PariPari.Combinators: getRefColumn :: Parser Int
- Text.PariPari.Combinators: getRefLine :: Parser Int
- Text.PariPari.Combinators: hexadecimal :: Num a => Parser a
- Text.PariPari.Combinators: indented :: Parser ()
- Text.PariPari.Combinators: infix 0 <?>
- Text.PariPari.Combinators: infixl 3 <|>
- Text.PariPari.Combinators: integer :: (Num a, MonadParser p) => p sep -> Int -> p a
- Text.PariPari.Combinators: integer' :: (Num a, MonadParser p) => p sep -> Int -> p (a, Int)
- Text.PariPari.Combinators: letterChar :: Parser Char
- Text.PariPari.Combinators: line :: Parser ()
- Text.PariPari.Combinators: linefold :: Parser ()
- Text.PariPari.Combinators: lowerChar :: Parser Char
- Text.PariPari.Combinators: many :: MonadPlus m => m a -> m [a]
- Text.PariPari.Combinators: manyTill :: MonadPlus m => m a -> m end -> m [a]
- Text.PariPari.Combinators: notByte :: Word8 -> Parser Word8
- Text.PariPari.Combinators: notChar :: Char -> Parser Char
- Text.PariPari.Combinators: octal :: Num a => Parser a
- Text.PariPari.Combinators: option :: Alternative m => a -> m a -> m a
- Text.PariPari.Combinators: optional :: Alternative f => f a -> f Maybe a
- Text.PariPari.Combinators: punctuationChar :: Parser Char
- Text.PariPari.Combinators: sepBy :: MonadPlus m => m a -> m sep -> m [a]
- Text.PariPari.Combinators: sepBy1 :: MonadPlus m => m a -> m sep -> m NonEmpty a
- Text.PariPari.Combinators: sepEndBy :: MonadPlus m => m a -> m sep -> m [a]
- Text.PariPari.Combinators: sepEndBy1 :: MonadPlus m => m a -> m sep -> m NonEmpty a
- Text.PariPari.Combinators: signed :: (Num a, MonadParser p) => p a -> p a
- Text.PariPari.Combinators: skipBytes :: Int -> Parser ()
- Text.PariPari.Combinators: skipBytesWhile :: (Word8 -> Bool) -> Parser ()
- Text.PariPari.Combinators: skipBytesWhile1 :: (Word8 -> Bool) -> Parser ()
- Text.PariPari.Combinators: skipChars :: Int -> Parser ()
- Text.PariPari.Combinators: skipCharsWhile :: (Char -> Bool) -> Parser ()
- Text.PariPari.Combinators: skipCharsWhile1 :: (Char -> Bool) -> Parser ()
- Text.PariPari.Combinators: skipCount :: Monad m => Int -> m a -> m ()
- Text.PariPari.Combinators: skipMany :: MonadPlus m => m a -> m ()
- Text.PariPari.Combinators: skipManyTill :: MonadPlus m => m a -> m end -> m end
- Text.PariPari.Combinators: skipSome :: MonadPlus m => m a -> m ()
- Text.PariPari.Combinators: skipSomeTill :: MonadPlus m => m a -> m end -> m end
- Text.PariPari.Combinators: some :: MonadPlus m => m a -> m NonEmpty a
- Text.PariPari.Combinators: someTill :: MonadPlus m => m a -> m end -> m NonEmpty a
- Text.PariPari.Combinators: spaceChar :: Parser Char
- Text.PariPari.Combinators: string :: Text -> Parser Text
- Text.PariPari.Combinators: string' :: Text -> Parser Text
- Text.PariPari.Combinators: symbolChar :: Parser Char
- Text.PariPari.Combinators: takeBytes :: Int -> Parser ByteString
- Text.PariPari.Combinators: takeBytesWhile :: (Word8 -> Bool) -> Parser ByteString
- Text.PariPari.Combinators: takeBytesWhile1 :: (Word8 -> Bool) -> Parser ByteString
- Text.PariPari.Combinators: takeChars :: Int -> Parser Text
- Text.PariPari.Combinators: takeCharsWhile :: (Char -> Bool) -> Parser Text
- Text.PariPari.Combinators: takeCharsWhile1 :: (Char -> Bool) -> Parser Text
- Text.PariPari.Combinators: upperChar :: Parser Char
- Text.PariPari.Combinators: void :: Functor f => f a -> f ()
- Text.PariPari.Combinators: withPos :: MonadParser p => p a -> p (Pos, a)
- Text.PariPari.Combinators: withRefPos :: MonadParser p => p a -> p a
- Text.PariPari.Combinators: withSpan :: MonadParser p => p a -> p (Span, a)
- Text.PariPari.Decode: byteAt :: ForeignPtr Word8 -> Int -> Word8
- Text.PariPari.Decode: bytesEqual :: ForeignPtr Word8 -> Int -> ForeignPtr Word8 -> Int -> Int -> Bool
- Text.PariPari.Decode: utf8Decode :: ForeignPtr Word8 -> Int -> (Char, Int)
- Text.PariPari.Decode: utf8DecodeFixed :: Int -> ForeignPtr Word8 -> Int -> Char
- Text.PariPari.Decode: utf8Width :: Char -> Int
- Text.PariPari.Reporter: Report :: !FilePath -> !Int -> !Int -> [ErrorContext] -> Report
- Text.PariPari.Reporter: ReportOptions :: !Int -> !Int -> !Int -> ReportOptions
- Text.PariPari.Reporter: [_optMaxContexts] :: ReportOptions -> !Int
- Text.PariPari.Reporter: [_optMaxErrorsPerContext] :: ReportOptions -> !Int
- Text.PariPari.Reporter: [_optMaxLabelsPerContext] :: ReportOptions -> !Int
- Text.PariPari.Reporter: [_reportCol] :: Report -> !Int
- Text.PariPari.Reporter: [_reportErrors] :: Report -> [ErrorContext]
- Text.PariPari.Reporter: [_reportFile] :: Report -> !FilePath
- Text.PariPari.Reporter: [_reportLine] :: Report -> !Int
- Text.PariPari.Reporter: data Report
- Text.PariPari.Reporter: data ReportOptions
- Text.PariPari.Reporter: data Reporter a
- Text.PariPari.Reporter: data Tracer a
- Text.PariPari.Reporter: defaultReportOptions :: ReportOptions
- Text.PariPari.Reporter: instance Control.Monad.Fail.MonadFail Text.PariPari.Reporter.Reporter
- Text.PariPari.Reporter: instance Control.Monad.Fail.MonadFail Text.PariPari.Reporter.Tracer
- Text.PariPari.Reporter: instance GHC.Base.Alternative Text.PariPari.Reporter.Reporter
- Text.PariPari.Reporter: instance GHC.Base.Alternative Text.PariPari.Reporter.Tracer
- Text.PariPari.Reporter: instance GHC.Base.Applicative Text.PariPari.Reporter.Reporter
- Text.PariPari.Reporter: instance GHC.Base.Applicative Text.PariPari.Reporter.Tracer
- Text.PariPari.Reporter: instance GHC.Base.Functor Text.PariPari.Reporter.Reporter
- Text.PariPari.Reporter: instance GHC.Base.Functor Text.PariPari.Reporter.Tracer
- Text.PariPari.Reporter: instance GHC.Base.Monad Text.PariPari.Reporter.Reporter
- Text.PariPari.Reporter: instance GHC.Base.Monad Text.PariPari.Reporter.Tracer
- Text.PariPari.Reporter: instance GHC.Base.MonadPlus Text.PariPari.Reporter.Reporter
- Text.PariPari.Reporter: instance GHC.Base.MonadPlus Text.PariPari.Reporter.Tracer
- Text.PariPari.Reporter: instance GHC.Base.Monoid a => GHC.Base.Monoid (Text.PariPari.Reporter.Reporter a)
- Text.PariPari.Reporter: instance GHC.Base.Monoid a => GHC.Base.Monoid (Text.PariPari.Reporter.Tracer a)
- Text.PariPari.Reporter: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Text.PariPari.Reporter.Reporter a)
- Text.PariPari.Reporter: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (Text.PariPari.Reporter.Tracer a)
- Text.PariPari.Reporter: instance GHC.Classes.Eq Text.PariPari.Reporter.Report
- Text.PariPari.Reporter: instance GHC.Classes.Eq Text.PariPari.Reporter.ReportOptions
- Text.PariPari.Reporter: instance GHC.Generics.Generic Text.PariPari.Reporter.Report
- Text.PariPari.Reporter: instance GHC.Generics.Generic Text.PariPari.Reporter.ReportOptions
- Text.PariPari.Reporter: instance GHC.Show.Show Text.PariPari.Reporter.Report
- Text.PariPari.Reporter: instance GHC.Show.Show Text.PariPari.Reporter.ReportOptions
- Text.PariPari.Reporter: instance Text.PariPari.Class.MonadParser Text.PariPari.Reporter.Reporter
- Text.PariPari.Reporter: instance Text.PariPari.Class.MonadParser Text.PariPari.Reporter.Tracer
- Text.PariPari.Reporter: runReporter :: Reporter a -> FilePath -> ByteString -> Either Report a
- Text.PariPari.Reporter: runReporterWithOptions :: ReportOptions -> Reporter a -> FilePath -> ByteString -> Either Report a
- Text.PariPari.Reporter: runTracer :: Tracer a -> FilePath -> ByteString -> Either Report a
- Text.PariPari.Reporter: showErrors :: [ErrorContext] -> String
- Text.PariPari.Reporter: showReport :: Report -> String
- Text.PariPari.Reporter: type ErrorContext = ([Error], [String])
+ Text.PariPari: ECombinator :: String -> Error
+ Text.PariPari: EExpected :: [String] -> Error
+ Text.PariPari: EFail :: String -> Error
+ Text.PariPari: EIndentNotAligned :: !Int -> !Int -> Error
+ Text.PariPari: EIndentOverLine :: !Int -> !Int -> Error
+ Text.PariPari: EInvalidUtf8 :: Error
+ Text.PariPari: ENotEnoughIndent :: !Int -> !Int -> Error
+ Text.PariPari: EUnexpected :: String -> Error
+ Text.PariPari: Pos :: !Int -> !Int -> Pos
+ Text.PariPari: [_posColumn] :: Pos -> !Int
+ Text.PariPari: [_posLine] :: Pos -> !Int
+ Text.PariPari: asChunk :: ChunkParser k p => p () -> p k
+ Text.PariPari: asciiByte :: CharParser k p => Word8 -> p Word8
+ Text.PariPari: asciiSatisfy :: CharParser k p => (Word8 -> Bool) -> p Word8
+ Text.PariPari: char :: CharParser k p => Char -> p Char
+ Text.PariPari: chunk :: ChunkParser k p => k -> p k
+ Text.PariPari: class Chunk k => CharChunk k
+ Text.PariPari: class (ChunkParser k p, CharChunk k) => CharParser k p | p -> k
+ Text.PariPari: class (Ord (Element k), Ord k) => Chunk k where {
+ Text.PariPari: class (MonadFail p, MonadPlus p, Chunk k) => ChunkParser k p | p -> k
+ Text.PariPari: commit :: ChunkParser k p => p a -> p a
+ Text.PariPari: data Acceptor k a
+ Text.PariPari: data Error
+ Text.PariPari: data Pos
+ Text.PariPari: data Reporter k a
+ Text.PariPari: data Tracer k a
+ Text.PariPari: element :: ChunkParser k p => Element k -> p (Element k)
+ Text.PariPari: elementSatisfy :: ChunkParser k p => (Element k -> Bool) -> p (Element k)
+ Text.PariPari: eof :: ChunkParser k p => p ()
+ Text.PariPari: failWith :: ChunkParser k p => Error -> p a
+ Text.PariPari: getFile :: ChunkParser k p => p FilePath
+ Text.PariPari: getPos :: ChunkParser k p => p Pos
+ Text.PariPari: getRefPos :: ChunkParser k p => p Pos
+ Text.PariPari: hidden :: ChunkParser k p => p a -> p a
+ Text.PariPari: label :: ChunkParser k p => String -> p a -> p a
+ Text.PariPari: lookAhead :: ChunkParser k p => p a -> p a
+ Text.PariPari: notFollowedBy :: (ChunkParser k p, Show a) => p a -> p ()
+ Text.PariPari: runAcceptor :: Chunk k => Acceptor k a -> FilePath -> k -> Either Error a
+ Text.PariPari: runCharParser :: CharChunk k => (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runCharParserWithOptions :: CharChunk k => ReportOptions -> (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runChunkParser :: CharChunk k => (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runChunkParserWithOptions :: Chunk k => ReportOptions -> (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runReporter :: Chunk k => Reporter k a -> FilePath -> k -> Either Report a
+ Text.PariPari: runReporterWithOptions :: Chunk k => ReportOptions -> Reporter k a -> FilePath -> k -> Either Report a
+ Text.PariPari: runSeqCharParser :: CharChunk k => (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runSeqCharParserWithOptions :: CharChunk k => ReportOptions -> (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runSeqChunkParser :: Chunk k => (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runSeqChunkParserWithOptions :: Chunk k => ReportOptions -> (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari: runTracer :: Chunk k => Tracer k a -> FilePath -> k -> Either Report a
+ Text.PariPari: satisfy :: CharParser k p => (Char -> Bool) -> p Char
+ Text.PariPari: showChunk :: Chunk k => k -> String
+ Text.PariPari: showElement :: Chunk k => Element k -> String
+ Text.PariPari: showError :: Error -> String
+ Text.PariPari: showErrors :: [ErrorContext] -> String
+ Text.PariPari: showReport :: Report -> String
+ Text.PariPari: type family Element k;
+ Text.PariPari: withRefPos :: ChunkParser k p => p a -> p a
+ Text.PariPari: }
+ Text.PariPari.Internal.Acceptor: Acceptor :: forall b. Env k -> State -> (a -> State -> b) -> (Error -> b) -> b -> Acceptor k a
+ Text.PariPari.Internal.Acceptor: Env :: !(Buffer k) -> !Int -> !FilePath -> !Int -> !Int -> Env k
+ Text.PariPari.Internal.Acceptor: State :: !Int -> !Int -> !Int -> State
+ Text.PariPari.Internal.Acceptor: [_envBuf] :: Env k -> !(Buffer k)
+ Text.PariPari.Internal.Acceptor: [_envEnd] :: Env k -> !Int
+ Text.PariPari.Internal.Acceptor: [_envFile] :: Env k -> !FilePath
+ Text.PariPari.Internal.Acceptor: [_envRefCol] :: Env k -> !Int
+ Text.PariPari.Internal.Acceptor: [_envRefLine] :: Env k -> !Int
+ Text.PariPari.Internal.Acceptor: [_stCol] :: State -> !Int
+ Text.PariPari.Internal.Acceptor: [_stLine] :: State -> !Int
+ Text.PariPari.Internal.Acceptor: [_stOff] :: State -> !Int
+ Text.PariPari.Internal.Acceptor: [unAcceptor] :: Acceptor k a -> forall b. Env k -> State -> (a -> State -> b) -> (Error -> b) -> b
+ Text.PariPari.Internal.Acceptor: data Env k
+ Text.PariPari.Internal.Acceptor: data State
+ Text.PariPari.Internal.Acceptor: get :: (Env k -> State -> a) -> Acceptor k a
+ Text.PariPari.Internal.Acceptor: instance (Text.PariPari.Internal.Chunk.Chunk k, GHC.Base.Monoid a) => GHC.Base.Monoid (Text.PariPari.Internal.Acceptor.Acceptor k a)
+ Text.PariPari.Internal.Acceptor: instance (Text.PariPari.Internal.Chunk.Chunk k, GHC.Base.Semigroup a) => GHC.Base.Semigroup (Text.PariPari.Internal.Acceptor.Acceptor k a)
+ Text.PariPari.Internal.Acceptor: instance GHC.Base.Functor (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: instance Text.PariPari.Internal.Chunk.CharChunk k => Text.PariPari.Internal.Class.CharParser k (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: instance Text.PariPari.Internal.Chunk.Chunk k => Control.Monad.Fail.MonadFail (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Alternative (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Applicative (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Monad (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.MonadPlus (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: instance Text.PariPari.Internal.Chunk.Chunk k => Text.PariPari.Internal.Class.ChunkParser k (Text.PariPari.Internal.Acceptor.Acceptor k)
+ Text.PariPari.Internal.Acceptor: local :: (State -> Env k -> Env k) -> Acceptor k a -> Acceptor k a
+ Text.PariPari.Internal.Acceptor: newtype Acceptor k a
+ Text.PariPari.Internal.Acceptor: runAcceptor :: Chunk k => Acceptor k a -> FilePath -> k -> Either Error a
+ Text.PariPari.Internal.CharCombinators: alphaNumChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: anyChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: asciiChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: categoryChar :: CharParser k p => GeneralCategory -> p Char
+ Text.PariPari.Internal.CharCombinators: char' :: CharParser k p => Char -> p Char
+ Text.PariPari.Internal.CharCombinators: decimal :: Num a => CharP k a
+ Text.PariPari.Internal.CharCombinators: digit :: CharParser k p => Int -> p Word
+ Text.PariPari.Internal.CharCombinators: digitByte :: CharParser k p => Int -> p Word8
+ Text.PariPari.Internal.CharCombinators: digitChar :: CharParser k p => Int -> p Char
+ Text.PariPari.Internal.CharCombinators: fractionDec :: (Num a, CharParser k p) => p digitSep -> p (a, Int, a)
+ Text.PariPari.Internal.CharCombinators: fractionHex :: (Num a, CharParser k p) => p digitSep -> p (a, Int, a)
+ Text.PariPari.Internal.CharCombinators: hexadecimal :: Num a => CharP k a
+ Text.PariPari.Internal.CharCombinators: integer :: (Num a, CharParser k p) => p sep -> Int -> p a
+ Text.PariPari.Internal.CharCombinators: integer' :: (Num a, CharParser k p) => p sep -> Int -> p (a, Int)
+ Text.PariPari.Internal.CharCombinators: letterChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: lowerChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: notChar :: CharParser k p => Char -> p Char
+ Text.PariPari.Internal.CharCombinators: octal :: Num a => CharP k a
+ Text.PariPari.Internal.CharCombinators: punctuationChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: signed :: (Num a, CharParser k p) => p a -> p a
+ Text.PariPari.Internal.CharCombinators: skipChars :: CharParser k p => Int -> p ()
+ Text.PariPari.Internal.CharCombinators: skipCharsWhile :: CharParser k p => (Char -> Bool) -> p ()
+ Text.PariPari.Internal.CharCombinators: skipCharsWhile1 :: CharParser k p => (Char -> Bool) -> p ()
+ Text.PariPari.Internal.CharCombinators: spaceChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: string :: CharParser k p => Text -> p Text
+ Text.PariPari.Internal.CharCombinators: symbolChar :: CharP k Char
+ Text.PariPari.Internal.CharCombinators: takeChars :: CharParser k p => Int -> p k
+ Text.PariPari.Internal.CharCombinators: takeCharsWhile :: CharParser k p => (Char -> Bool) -> p k
+ Text.PariPari.Internal.CharCombinators: takeCharsWhile1 :: CharParser k p => (Char -> Bool) -> p k
+ Text.PariPari.Internal.CharCombinators: upperChar :: CharP k Char
+ Text.PariPari.Internal.Chunk: Pos :: !Int -> !Int -> Pos
+ Text.PariPari.Internal.Chunk: [_posColumn] :: Pos -> !Int
+ Text.PariPari.Internal.Chunk: [_posLine] :: Pos -> !Int
+ Text.PariPari.Internal.Chunk: asc_0 :: Word8
+ Text.PariPari.Internal.Chunk: asc_9 :: Word8
+ Text.PariPari.Internal.Chunk: asc_A :: Word8
+ Text.PariPari.Internal.Chunk: asc_E :: Word8
+ Text.PariPari.Internal.Chunk: asc_P :: Word8
+ Text.PariPari.Internal.Chunk: asc_a :: Word8
+ Text.PariPari.Internal.Chunk: asc_e :: Word8
+ Text.PariPari.Internal.Chunk: asc_minus :: Word8
+ Text.PariPari.Internal.Chunk: asc_newline :: Word8
+ Text.PariPari.Internal.Chunk: asc_p :: Word8
+ Text.PariPari.Internal.Chunk: asc_plus :: Word8
+ Text.PariPari.Internal.Chunk: asc_point :: Word8
+ Text.PariPari.Internal.Chunk: byteAt :: CharChunk k => Buffer k -> Int -> Word8
+ Text.PariPari.Internal.Chunk: charAt :: CharChunk k => Buffer k -> Int -> (Char, Int)
+ Text.PariPari.Internal.Chunk: charAtFixed :: CharChunk k => Int -> Buffer k -> Int -> Char
+ Text.PariPari.Internal.Chunk: charWidth :: CharChunk k => Char -> Int
+ Text.PariPari.Internal.Chunk: chunkEqual :: Chunk k => Buffer k -> Int -> k -> Bool
+ Text.PariPari.Internal.Chunk: chunkWidth :: Chunk k => k -> Int
+ Text.PariPari.Internal.Chunk: class Chunk k => CharChunk k
+ Text.PariPari.Internal.Chunk: class (Ord (Element k), Ord k) => Chunk k where {
+ Text.PariPari.Internal.Chunk: data Pos
+ Text.PariPari.Internal.Chunk: elementAt :: Chunk k => Buffer k -> Int -> (Element k, Int)
+ Text.PariPari.Internal.Chunk: elementPos :: Chunk k => Element k -> Pos -> Pos
+ Text.PariPari.Internal.Chunk: instance GHC.Classes.Eq Text.PariPari.Internal.Chunk.Pos
+ Text.PariPari.Internal.Chunk: instance GHC.Generics.Generic Text.PariPari.Internal.Chunk.Pos
+ Text.PariPari.Internal.Chunk: instance GHC.Show.Show Text.PariPari.Internal.Chunk.Pos
+ Text.PariPari.Internal.Chunk: instance Text.PariPari.Internal.Chunk.CharChunk Data.ByteString.Internal.ByteString
+ Text.PariPari.Internal.Chunk: instance Text.PariPari.Internal.Chunk.CharChunk Data.Text.Internal.Text
+ Text.PariPari.Internal.Chunk: instance Text.PariPari.Internal.Chunk.Chunk Data.ByteString.Internal.ByteString
+ Text.PariPari.Internal.Chunk: instance Text.PariPari.Internal.Chunk.Chunk Data.Text.Internal.Text
+ Text.PariPari.Internal.Chunk: packChunk :: Chunk k => Buffer k -> Int -> Int -> k
+ Text.PariPari.Internal.Chunk: showByte :: Word8 -> String
+ Text.PariPari.Internal.Chunk: showByteString :: ByteString -> String
+ Text.PariPari.Internal.Chunk: showChunk :: Chunk k => k -> String
+ Text.PariPari.Internal.Chunk: showElement :: Chunk k => Element k -> String
+ Text.PariPari.Internal.Chunk: textToChunk :: CharChunk k => Text -> k
+ Text.PariPari.Internal.Chunk: type family Buffer k;
+ Text.PariPari.Internal.Chunk: unpackChunk :: Chunk k => k -> (Buffer k, Int, Int)
+ Text.PariPari.Internal.Chunk: unsafeAsciiToChar :: Word8 -> Char
+ Text.PariPari.Internal.Chunk: }
+ Text.PariPari.Internal.Class: (<|>) :: Alternative f => f a -> f a -> f a
+ Text.PariPari.Internal.Class: ECombinator :: String -> Error
+ Text.PariPari.Internal.Class: EExpected :: [String] -> Error
+ Text.PariPari.Internal.Class: EFail :: String -> Error
+ Text.PariPari.Internal.Class: EIndentNotAligned :: !Int -> !Int -> Error
+ Text.PariPari.Internal.Class: EIndentOverLine :: !Int -> !Int -> Error
+ Text.PariPari.Internal.Class: EInvalidUtf8 :: Error
+ Text.PariPari.Internal.Class: ENotEnoughIndent :: !Int -> !Int -> Error
+ Text.PariPari.Internal.Class: EUnexpected :: String -> Error
+ Text.PariPari.Internal.Class: Pos :: !Int -> !Int -> Pos
+ Text.PariPari.Internal.Class: [_posColumn] :: Pos -> !Int
+ Text.PariPari.Internal.Class: [_posLine] :: Pos -> !Int
+ Text.PariPari.Internal.Class: asChunk :: ChunkParser k p => p () -> p k
+ Text.PariPari.Internal.Class: asciiByte :: CharParser k p => Word8 -> p Word8
+ Text.PariPari.Internal.Class: asciiSatisfy :: CharParser k p => (Word8 -> Bool) -> p Word8
+ Text.PariPari.Internal.Class: char :: CharParser k p => Char -> p Char
+ Text.PariPari.Internal.Class: chunk :: ChunkParser k p => k -> p k
+ Text.PariPari.Internal.Class: class Applicative f => Alternative (f :: * -> *)
+ Text.PariPari.Internal.Class: class (ChunkParser k p, CharChunk k) => CharParser k p | p -> k
+ Text.PariPari.Internal.Class: class (MonadFail p, MonadPlus p, Chunk k) => ChunkParser k p | p -> k
+ Text.PariPari.Internal.Class: class (Alternative m, Monad m) => MonadPlus (m :: * -> *)
+ Text.PariPari.Internal.Class: commit :: ChunkParser k p => p a -> p a
+ Text.PariPari.Internal.Class: data Error
+ Text.PariPari.Internal.Class: data Pos
+ Text.PariPari.Internal.Class: element :: ChunkParser k p => Element k -> p (Element k)
+ Text.PariPari.Internal.Class: elementSatisfy :: ChunkParser k p => (Element k -> Bool) -> p (Element k)
+ Text.PariPari.Internal.Class: empty :: Alternative f => f a
+ Text.PariPari.Internal.Class: eof :: ChunkParser k p => p ()
+ Text.PariPari.Internal.Class: expectedEnd :: Error
+ Text.PariPari.Internal.Class: failWith :: ChunkParser k p => Error -> p a
+ Text.PariPari.Internal.Class: getFile :: ChunkParser k p => p FilePath
+ Text.PariPari.Internal.Class: getPos :: ChunkParser k p => p Pos
+ Text.PariPari.Internal.Class: getRefPos :: ChunkParser k p => p Pos
+ Text.PariPari.Internal.Class: hidden :: ChunkParser k p => p a -> p a
+ Text.PariPari.Internal.Class: instance GHC.Classes.Eq Text.PariPari.Internal.Class.Error
+ Text.PariPari.Internal.Class: instance GHC.Classes.Ord Text.PariPari.Internal.Class.Error
+ Text.PariPari.Internal.Class: instance GHC.Generics.Generic Text.PariPari.Internal.Class.Error
+ Text.PariPari.Internal.Class: instance GHC.Show.Show Text.PariPari.Internal.Class.Error
+ Text.PariPari.Internal.Class: label :: ChunkParser k p => String -> p a -> p a
+ Text.PariPari.Internal.Class: lookAhead :: ChunkParser k p => p a -> p a
+ Text.PariPari.Internal.Class: notFollowedBy :: (ChunkParser k p, Show a) => p a -> p ()
+ Text.PariPari.Internal.Class: satisfy :: CharParser k p => (Char -> Bool) -> p Char
+ Text.PariPari.Internal.Class: showError :: Error -> String
+ Text.PariPari.Internal.Class: unexpectedEnd :: Error
+ Text.PariPari.Internal.Class: withRefPos :: ChunkParser k p => p a -> p a
+ Text.PariPari.Internal.ElementCombinators: (<?>) :: ChunkParser k p => p a -> String -> p a
+ Text.PariPari.Internal.ElementCombinators: (<|>) :: Alternative f => f a -> f a -> f a
+ Text.PariPari.Internal.ElementCombinators: align :: ChunkP k ()
+ Text.PariPari.Internal.ElementCombinators: anyElement :: ChunkP k (Element k)
+ Text.PariPari.Internal.ElementCombinators: between :: Applicative m => m open -> m close -> m a -> m a
+ Text.PariPari.Internal.ElementCombinators: choice :: (Foldable f, Alternative m) => f m a -> m a
+ Text.PariPari.Internal.ElementCombinators: count :: Monad m => Int -> m a -> m [a]
+ Text.PariPari.Internal.ElementCombinators: count' :: MonadPlus m => Int -> Int -> m a -> m [a]
+ Text.PariPari.Internal.ElementCombinators: eitherP :: Alternative m => m a -> m b -> m Either a b
+ Text.PariPari.Internal.ElementCombinators: empty :: Alternative f => f a
+ Text.PariPari.Internal.ElementCombinators: endBy :: MonadPlus m => m a -> m sep -> m [a]
+ Text.PariPari.Internal.ElementCombinators: endBy1 :: MonadPlus m => m a -> m sep -> m NonEmpty a
+ Text.PariPari.Internal.ElementCombinators: getColumn :: ChunkP k Int
+ Text.PariPari.Internal.ElementCombinators: getLine :: ChunkP k Int
+ Text.PariPari.Internal.ElementCombinators: getRefColumn :: ChunkP k Int
+ Text.PariPari.Internal.ElementCombinators: getRefLine :: ChunkP k Int
+ Text.PariPari.Internal.ElementCombinators: indented :: ChunkP k ()
+ Text.PariPari.Internal.ElementCombinators: infix 0 <?>
+ Text.PariPari.Internal.ElementCombinators: infixl 3 <|>
+ Text.PariPari.Internal.ElementCombinators: line :: ChunkP k ()
+ Text.PariPari.Internal.ElementCombinators: linefold :: ChunkP k ()
+ Text.PariPari.Internal.ElementCombinators: many :: MonadPlus m => m a -> m [a]
+ Text.PariPari.Internal.ElementCombinators: manyTill :: MonadPlus m => m a -> m end -> m [a]
+ Text.PariPari.Internal.ElementCombinators: notElement :: forall k p. ChunkParser k p => Element k -> p (Element k)
+ Text.PariPari.Internal.ElementCombinators: option :: Alternative m => a -> m a -> m a
+ Text.PariPari.Internal.ElementCombinators: optional :: Alternative f => f a -> f Maybe a
+ Text.PariPari.Internal.ElementCombinators: sepBy :: MonadPlus m => m a -> m sep -> m [a]
+ Text.PariPari.Internal.ElementCombinators: sepBy1 :: MonadPlus m => m a -> m sep -> m NonEmpty a
+ Text.PariPari.Internal.ElementCombinators: sepEndBy :: MonadPlus m => m a -> m sep -> m [a]
+ Text.PariPari.Internal.ElementCombinators: sepEndBy1 :: MonadPlus m => m a -> m sep -> m NonEmpty a
+ Text.PariPari.Internal.ElementCombinators: skipCount :: Monad m => Int -> m a -> m ()
+ Text.PariPari.Internal.ElementCombinators: skipElements :: ChunkParser k p => Int -> p ()
+ Text.PariPari.Internal.ElementCombinators: skipElementsWhile :: ChunkParser k p => (Element k -> Bool) -> p ()
+ Text.PariPari.Internal.ElementCombinators: skipElementsWhile1 :: ChunkParser k p => (Element k -> Bool) -> p ()
+ Text.PariPari.Internal.ElementCombinators: skipMany :: MonadPlus m => m a -> m ()
+ Text.PariPari.Internal.ElementCombinators: skipManyTill :: MonadPlus m => m a -> m end -> m end
+ Text.PariPari.Internal.ElementCombinators: skipSome :: MonadPlus m => m a -> m ()
+ Text.PariPari.Internal.ElementCombinators: skipSomeTill :: MonadPlus m => m a -> m end -> m end
+ Text.PariPari.Internal.ElementCombinators: some :: MonadPlus m => m a -> m NonEmpty a
+ Text.PariPari.Internal.ElementCombinators: someTill :: MonadPlus m => m a -> m end -> m NonEmpty a
+ Text.PariPari.Internal.ElementCombinators: takeElements :: ChunkParser k p => Int -> p k
+ Text.PariPari.Internal.ElementCombinators: takeElementsWhile :: ChunkParser k p => (Element k -> Bool) -> p k
+ Text.PariPari.Internal.ElementCombinators: takeElementsWhile1 :: ChunkParser k p => (Element k -> Bool) -> p k
+ Text.PariPari.Internal.ElementCombinators: void :: Functor f => f a -> f ()
+ Text.PariPari.Internal.ElementCombinators: withPos :: ChunkParser k p => p a -> p (Pos, a)
+ Text.PariPari.Internal.ElementCombinators: withRefPos :: ChunkParser k p => p a -> p a
+ Text.PariPari.Internal.ElementCombinators: withSpan :: ChunkParser k p => p a -> p (Span, a)
+ Text.PariPari.Internal.Reporter: Env :: !(Buffer k) -> !Int -> !FilePath -> !ReportOptions -> !Bool -> !Int -> [String] -> !Int -> !Int -> Env k
+ Text.PariPari.Internal.Reporter: ErrorContext :: [Error] -> [String] -> ErrorContext
+ Text.PariPari.Internal.Reporter: Report :: !FilePath -> !Int -> !Int -> [ErrorContext] -> Report
+ Text.PariPari.Internal.Reporter: ReportOptions :: !Int -> !Int -> !Int -> ReportOptions
+ Text.PariPari.Internal.Reporter: Reporter :: forall b. Env k -> State -> (a -> State -> b) -> (State -> b) -> b -> Reporter k a
+ Text.PariPari.Internal.Reporter: State :: !Int -> !Int -> !Int -> !Int -> !Int -> !Int -> !Int -> [ErrorContext] -> State
+ Text.PariPari.Internal.Reporter: [_ecContext] :: ErrorContext -> [String]
+ Text.PariPari.Internal.Reporter: [_ecErrors] :: ErrorContext -> [Error]
+ Text.PariPari.Internal.Reporter: [_envBuf] :: Env k -> !(Buffer k)
+ Text.PariPari.Internal.Reporter: [_envCommit] :: Env k -> !Int
+ Text.PariPari.Internal.Reporter: [_envContext] :: Env k -> [String]
+ Text.PariPari.Internal.Reporter: [_envEnd] :: Env k -> !Int
+ Text.PariPari.Internal.Reporter: [_envFile] :: Env k -> !FilePath
+ Text.PariPari.Internal.Reporter: [_envHidden] :: Env k -> !Bool
+ Text.PariPari.Internal.Reporter: [_envOptions] :: Env k -> !ReportOptions
+ Text.PariPari.Internal.Reporter: [_envRefCol] :: Env k -> !Int
+ Text.PariPari.Internal.Reporter: [_envRefLine] :: Env k -> !Int
+ Text.PariPari.Internal.Reporter: [_optMaxContexts] :: ReportOptions -> !Int
+ Text.PariPari.Internal.Reporter: [_optMaxErrorsPerContext] :: ReportOptions -> !Int
+ Text.PariPari.Internal.Reporter: [_optMaxLabelsPerContext] :: ReportOptions -> !Int
+ Text.PariPari.Internal.Reporter: [_reportCol] :: Report -> !Int
+ Text.PariPari.Internal.Reporter: [_reportErrors] :: Report -> [ErrorContext]
+ Text.PariPari.Internal.Reporter: [_reportFile] :: Report -> !FilePath
+ Text.PariPari.Internal.Reporter: [_reportLine] :: Report -> !Int
+ Text.PariPari.Internal.Reporter: [_stCol] :: State -> !Int
+ Text.PariPari.Internal.Reporter: [_stErrCol] :: State -> !Int
+ Text.PariPari.Internal.Reporter: [_stErrCommit] :: State -> !Int
+ Text.PariPari.Internal.Reporter: [_stErrLine] :: State -> !Int
+ Text.PariPari.Internal.Reporter: [_stErrOff] :: State -> !Int
+ Text.PariPari.Internal.Reporter: [_stErrors] :: State -> [ErrorContext]
+ Text.PariPari.Internal.Reporter: [_stLine] :: State -> !Int
+ Text.PariPari.Internal.Reporter: [_stOff] :: State -> !Int
+ Text.PariPari.Internal.Reporter: [unReporter] :: Reporter k a -> forall b. Env k -> State -> (a -> State -> b) -> (State -> b) -> b
+ Text.PariPari.Internal.Reporter: data Env k
+ Text.PariPari.Internal.Reporter: data ErrorContext
+ Text.PariPari.Internal.Reporter: data Report
+ Text.PariPari.Internal.Reporter: data ReportOptions
+ Text.PariPari.Internal.Reporter: data State
+ Text.PariPari.Internal.Reporter: defaultReportOptions :: ReportOptions
+ Text.PariPari.Internal.Reporter: get :: (Env k -> State -> a) -> Reporter k a
+ Text.PariPari.Internal.Reporter: instance (Text.PariPari.Internal.Chunk.Chunk k, GHC.Base.Monoid a) => GHC.Base.Monoid (Text.PariPari.Internal.Reporter.Reporter k a)
+ Text.PariPari.Internal.Reporter: instance (Text.PariPari.Internal.Chunk.Chunk k, GHC.Base.Semigroup a) => GHC.Base.Semigroup (Text.PariPari.Internal.Reporter.Reporter k a)
+ Text.PariPari.Internal.Reporter: instance GHC.Classes.Eq Text.PariPari.Internal.Reporter.ErrorContext
+ Text.PariPari.Internal.Reporter: instance GHC.Classes.Eq Text.PariPari.Internal.Reporter.Report
+ Text.PariPari.Internal.Reporter: instance GHC.Classes.Eq Text.PariPari.Internal.Reporter.ReportOptions
+ Text.PariPari.Internal.Reporter: instance GHC.Generics.Generic Text.PariPari.Internal.Reporter.ErrorContext
+ Text.PariPari.Internal.Reporter: instance GHC.Generics.Generic Text.PariPari.Internal.Reporter.Report
+ Text.PariPari.Internal.Reporter: instance GHC.Generics.Generic Text.PariPari.Internal.Reporter.ReportOptions
+ Text.PariPari.Internal.Reporter: instance GHC.Show.Show Text.PariPari.Internal.Reporter.ErrorContext
+ Text.PariPari.Internal.Reporter: instance GHC.Show.Show Text.PariPari.Internal.Reporter.Report
+ Text.PariPari.Internal.Reporter: instance GHC.Show.Show Text.PariPari.Internal.Reporter.ReportOptions
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.CharChunk k => Text.PariPari.Internal.Class.CharParser k (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.Chunk k => Control.Monad.Fail.MonadFail (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Alternative (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Applicative (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Functor (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Monad (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.MonadPlus (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: instance Text.PariPari.Internal.Chunk.Chunk k => Text.PariPari.Internal.Class.ChunkParser k (Text.PariPari.Internal.Reporter.Reporter k)
+ Text.PariPari.Internal.Reporter: local :: (State -> Env k -> Env k) -> Reporter k a -> Reporter k a
+ Text.PariPari.Internal.Reporter: mergeErrorState :: Env k -> State -> State -> State
+ Text.PariPari.Internal.Reporter: newtype Reporter k a
+ Text.PariPari.Internal.Reporter: raiseError :: Env k -> State -> (State -> b) -> Error -> b
+ Text.PariPari.Internal.Reporter: runReporter :: Chunk k => Reporter k a -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Reporter: runReporterWithOptions :: Chunk k => ReportOptions -> Reporter k a -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Reporter: showErrors :: [ErrorContext] -> String
+ Text.PariPari.Internal.Reporter: showReport :: Report -> String
+ Text.PariPari.Internal.Run: runCharParser :: CharChunk k => (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Run: runCharParserWithOptions :: CharChunk k => ReportOptions -> (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Run: runChunkParser :: CharChunk k => (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Run: runChunkParserWithOptions :: Chunk k => ReportOptions -> (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Run: runSeqCharParser :: CharChunk k => (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Run: runSeqCharParserWithOptions :: CharChunk k => ReportOptions -> (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Run: runSeqChunkParser :: Chunk k => (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Run: runSeqChunkParserWithOptions :: Chunk k => ReportOptions -> (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a
+ Text.PariPari.Internal.Tracer: Tracer :: Reporter k a -> Tracer k a
+ Text.PariPari.Internal.Tracer: [unTracer] :: Tracer k a -> Reporter k a
+ Text.PariPari.Internal.Tracer: instance (Text.PariPari.Internal.Chunk.Chunk k, GHC.Base.Monoid a) => GHC.Base.Monoid (Text.PariPari.Internal.Tracer.Tracer k a)
+ Text.PariPari.Internal.Tracer: instance (Text.PariPari.Internal.Chunk.Chunk k, GHC.Base.Semigroup a) => GHC.Base.Semigroup (Text.PariPari.Internal.Tracer.Tracer k a)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.CharChunk k => Text.PariPari.Internal.Class.CharParser k (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.CharChunk k => Text.PariPari.Internal.Class.ChunkParser k (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.Chunk k => Control.Monad.Fail.MonadFail (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Alternative (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Applicative (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Functor (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.Monad (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: instance Text.PariPari.Internal.Chunk.Chunk k => GHC.Base.MonadPlus (Text.PariPari.Internal.Tracer.Tracer k)
+ Text.PariPari.Internal.Tracer: newtype Tracer k a
+ Text.PariPari.Internal.Tracer: runTracer :: Chunk k => Tracer k a -> FilePath -> k -> Either Report a

Files

example.hs view
@@ -1,12 +1,26 @@ +{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}+ import System.Environment (getArgs) import Text.PariPari import qualified Data.ByteString as B +type StringType    = B.ByteString+type ParserMonad p = CharParser StringType p+type Parser a      = (forall p. ParserMonad p => p a)++-- {-# SPECIALISE_ALL ParserMonad p = p ~ Acceptor StringType #-}+-- {-# SPECIALISE_ALL ParserMonad p = p ~ Reporter StringType #-}+-- {-# SPECIALISE_ALL Parser = Acceptor StringType #-}+-- {-# SPECIALISE_ALL Parser = Reporter StringType #-}+ data Value-  = Object ![(Text, Value)]+  = Object ![(StringType, Value)]   | Array  ![Value]-  | String !Text+  | String !StringType   | Number !Integer !Integer   | Bool   !Bool   | Null@@ -18,7 +32,7 @@ object :: Parser Value object = Object <$> (char '{' *> space *> sepBy pair (space *> char ',' *> space) <* space <* char '}') <?> "object" -pair :: Parser (Text, Value)+pair :: Parser (StringType, Value) pair = (,) <$> (text <* space) <*> (char ':' *> space *> value)  array :: Parser Value@@ -34,7 +48,7 @@     <|> (Null       <$ string "null")     <|> number -text :: Parser Text+text :: Parser StringType text = char '"' *> takeCharsWhile (/= '"') <* char '"' <?> "text"  number :: Parser Value@@ -52,9 +66,9 @@   case args of     [file] -> do       b <- B.readFile file-      case runParser json file b of-        Left x  -> do-          putStrLn $ showReport x+      case runCharParser json file b of+        Left report -> do+          putStrLn $ showReport report           print $ runTracer json file b-        Right x -> print x-    _ -> error "Usage: example test.json"+        Right val -> print val+    _ -> error "Usage: paripari-example test.json"
paripari.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 35567a9d1dd99ccd046b88ea5fd4cf74ce38d5bb58560bb7f1b3450ff1a1fbbe+-- hash: 2b6dd0a88b444dee67e31d36c8744b234a1ef0a86434d9caacc7196eba7ac3f9  name:           paripari-version:        0.1.0.0+version:        0.2.0.0 synopsis:       Fast-path parser combinators with fallback for error reporting description:    PariPari offers two parsing strategies. There is a fast Acceptor and a slower Reporter which are evaluated in parallel. If the Acceptor fails, the Reporter returns a report about the parsing errors. Unlike Parsec and like Attoparsec, the parser combinators backtrack by default. category:       Text@@ -28,17 +28,18 @@ library   exposed-modules:       Text.PariPari-      Text.PariPari.Acceptor-      Text.PariPari.Ascii-      Text.PariPari.Class-      Text.PariPari.Combinators-      Text.PariPari.Decode-      Text.PariPari.Reporter+      Text.PariPari.Internal.Acceptor+      Text.PariPari.Internal.CharCombinators+      Text.PariPari.Internal.Chunk+      Text.PariPari.Internal.Class+      Text.PariPari.Internal.ElementCombinators+      Text.PariPari.Internal.Reporter+      Text.PariPari.Internal.Run+      Text.PariPari.Internal.Tracer   other-modules:       Paths_paripari   hs-source-dirs:       src-  default-extensions: BangPatterns DeriveGeneric GeneralizedNewtypeDeriving MultiWayIf NamedFieldPuns OverloadedStrings Rank2Types   ghc-options: -Wall -Widentities -Wmonomorphism-restriction -Wincomplete-uni-patterns -Wincomplete-record-updates -Wtabs -fprint-potential-instances   build-depends:       base >=4.8 && <5@@ -47,16 +48,46 @@     , text >=0.11 && <1.3   default-language: Haskell2010 -executable example+executable paripari-example   main-is: example.hs   other-modules:       Paths_paripari-  default-extensions: BangPatterns DeriveGeneric GeneralizedNewtypeDeriving MultiWayIf NamedFieldPuns OverloadedStrings Rank2Types   ghc-options: -Wall -Widentities -Wmonomorphism-restriction -Wincomplete-uni-patterns -Wincomplete-record-updates -Wtabs -fprint-potential-instances   build-depends:       base >=4.8 && <5     , bytestring >=0.10 && <0.11     , paripari     , parser-combinators >=1.0 && <1.1+    , text >=0.11 && <1.3+  default-language: Haskell2010++executable paripari-specialise-all+  main-is: specialise-all.hs+  other-modules:+      Paths_paripari+  ghc-options: -Wall -Widentities -Wmonomorphism-restriction -Wincomplete-uni-patterns -Wincomplete-record-updates -Wtabs -fprint-potential-instances+  build-depends:+      base >=4.8 && <5+    , bytestring >=0.10 && <0.11+    , paripari+    , parser-combinators >=1.0 && <1.1+    , text >=0.11 && <1.3+  default-language: Haskell2010++test-suite test+  type: exitcode-stdio-1.0+  main-is: test.hs+  other-modules:+      Paths_paripari+  hs-source-dirs:+      test+  ghc-options: -Wall -Widentities -Wmonomorphism-restriction -Wincomplete-uni-patterns -Wincomplete-record-updates -Wtabs -fprint-potential-instances+  build-depends:+      base >=4.8 && <5+    , bytestring >=0.10 && <0.11+    , paripari+    , parser-combinators >=1.0 && <1.1+    , tasty+    , tasty-hunit     , text >=0.11 && <1.3   default-language: Haskell2010
+ specialise-all.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}++import System.Environment (getArgs)+import Text.PariPari+import qualified Data.Char as C+import qualified Data.List.NonEmpty as NE+import qualified Data.Text as T+import qualified Data.Text.IO as T++type StringType    = T.Text+type ParserMonad p = CharParser StringType p+type Parser a      = (forall p. ParserMonad p => p a)++data Type+  = TypeName   !StringType+  | TypeVar    !StringType+  | TypeApp    !Type !(NE.NonEmpty Type)+  | TypeEq     !Type !Type+  | TypeConstr !Type !Type+  | TypeLam    !Type !Type+  | TypeTuple  ![Type]+  | TypeList   !Type+  deriving (Show, Eq)++data SourceLine+  = SpecialiseAll !Type !Type+  | TypeDecl      !(NE.NonEmpty StringType) !Type+  | OtherLine     !StringType+  deriving (Show)++source :: Parser [SourceLine]+source = sepBy sourceLine (char '\n') <* eof++sourceLine :: Parser SourceLine+sourceLine = specialiseAll <|> typeDecl <|> otherLine++otherLine :: Parser SourceLine+otherLine = OtherLine <$> takeCharsWhile (/= '\n')++specialiseAll :: Parser SourceLine+specialiseAll = SpecialiseAll+  <$> (symbol "{-#" *> symbol "SPECIALISE_ALL" *> type_)+  <*> (symbol "=" *> type_ <* symbol "#-}")++identifierAtom :: ParserMonad p => (Char -> Bool) -> p ()+identifierAtom f = satisfy f *> skipCharsWhile (\c -> C.isAlphaNum c || c == '_' || c == '\'')++name :: Parser StringType+name = asChunk (sepEndBy (identifierAtom C.isUpper) (char '.') *>+                identifierAtom C.isLower) <* space++typeName :: Parser StringType+typeName = asChunk (void $ sepBy1 (identifierAtom C.isUpper) (char '.')) <* space++symbol :: ParserMonad p => StringType -> p StringType+symbol s = string s <* space++typeTuple :: Parser Type+typeTuple = do+  ts <- between (symbol "(") (symbol ")") (sepBy type_ (symbol ","))+  pure $ case ts of+           []  -> TypeName "()"+           [t] -> t+           _   -> TypeTuple ts++typeAtom :: Parser Type+typeAtom =+  TypeName <$> typeName+  <|> TypeVar <$> name+  <|> TypeList <$> between (symbol "[") (symbol "]") type_+  <|> typeTuple++typeApp :: Parser Type+typeApp = do+  t <- typeAtom+  option t $ TypeApp t <$> some typeAtom++type_ :: Parser Type+type_ = do+  t <- typeApp+  TypeEq t <$> (symbol "~" *> type_)+    <|> TypeLam t <$> (symbol "->" *> type_)+    <|> TypeConstr t <$> (symbol "=>" *> type_)+    <|> pure t++typeDecl :: Parser SourceLine+typeDecl = TypeDecl <$> sepBy1 name (symbol ",") <*> (symbol "::" *> type_)++space :: Parser ()+space = skipCharsWhile (== ' ')++showType :: Type -> StringType+showType (TypeName t)  = t+showType (TypeVar t)   = t+showType (TypeApp t a) = "(" <> showType t <> " " <> T.intercalate " " (NE.toList $ fmap showType a) <> ")"+showType (TypeEq a b)  = "(" <> showType a <> " ~ " <> showType b <> ")"+showType (TypeLam a b)  = "(" <> showType a <> " -> " <> showType b <> ")"+showType (TypeConstr a b)  = "(" <> showType a <> " => " <> showType b <> ")"+showType (TypeList t) = "[" <> showType t <> "]"+showType (TypeTuple t) = "(" <> T.intercalate ", " (fmap showType t) <> ")"++showSource :: SourceLine -> StringType+showSource (SpecialiseAll from to) = "-- SPECIALISE_ALL " <> showType from <> " = " <> showType to+showSource (TypeDecl      names t) = T.intercalate "," (NE.toList names) <> " :: " <> showType t+showSource (OtherLine     str)     = str++substitute :: [(StringType, Type)] -> Type -> Type+substitute vars t@(TypeVar v)+  | Just t' <- lookup v vars = t'+  | otherwise = t+substitute _    t@TypeName{} = t+substitute vars (TypeApp t a) = TypeApp (substitute vars t) (fmap (substitute vars) a)+substitute vars (TypeEq a b) = TypeEq (substitute vars a) (substitute vars b)+substitute vars (TypeConstr a b) = TypeConstr (substitute vars a) (substitute vars b)+substitute vars (TypeLam a b) = TypeLam (substitute vars a) (substitute vars b)+substitute vars (TypeTuple t) = TypeTuple (fmap (substitute vars) t)+substitute vars (TypeList t) = TypeList (substitute vars t)++unify :: Type -> Type -> Maybe [(StringType, Type)]+unify (TypeVar v) t =+  Just [(v, t)]+unify (TypeName t1) (TypeName t2)+  | t1 == t2 = Just []+unify (TypeApp t1 a1) (TypeApp t2 a2) | length a1 == length a2 = do+  t <- unify t1 t2+  a <- foldMap (uncurry unify) $ NE.zip a1 a2+  pure $ t <> a+unify (TypeEq a1 b1) (TypeEq a2 b2) = do+  a <- unify a1 a2+  b <- unify b1 b2+  pure $ a <> b+unify (TypeConstr a1 b1) (TypeConstr a2 b2) = do+  a <- unify a1 a2+  b <- unify b1 b2+  pure $ a <> b+unify (TypeLam a1 b1) (TypeLam a2 b2) = do+  a <- unify a1 a2+  b <- unify b1 b2+  pure $ a <> b+unify (TypeTuple t1) (TypeTuple t2) | length t1 == length t2 =+  foldMap (uncurry unify) $ zip t1 t2+unify (TypeList t1) (TypeList t2) =+  unify t1 t2+unify _ _ = Nothing++simplify :: Type -> Type+simplify t@TypeVar{} = t+simplify t@TypeName{} = t+simplify (TypeApp t a) = TypeApp (simplify t) (fmap simplify a)+simplify (TypeEq a b) = TypeEq (simplify a) (simplify b)+simplify (TypeLam a b) = TypeLam (simplify a) (simplify b)+simplify (TypeTuple t) = TypeTuple (fmap simplify t)+simplify (TypeList t) = TypeList (simplify t)+simplify (TypeConstr (TypeEq (TypeVar v) t) t') = substitute [(v, t)] t'+simplify (TypeConstr c t) = TypeConstr (TypeTuple otherConstraints) (substitute constraintVars t)+  where eqConstraints = [e | e@(TypeEq TypeVar{} _) <- constraints]+        constraintVars = [(v,x) | TypeEq (TypeVar v) x <- eqConstraints]+        otherConstraints = filter (\x -> all (/= x) eqConstraints) constraints+        constraints = case c of+          TypeTuple xs -> xs+          x -> [x]++specialiseType :: Type -> Type -> Type -> Type+specialiseType from to typ+  | Just vars <- unify from typ = substitute vars to+  | otherwise =+      case typ of+        TypeName{}     -> typ+        TypeVar{}      -> typ+        TypeApp t a    -> TypeApp (specialiseType from to t) (fmap (specialiseType from to) a)+        TypeEq a b     -> TypeEq (specialiseType from to a) (specialiseType from to b)+        TypeConstr a b -> TypeConstr (specialiseType from to a) (specialiseType from to b)+        TypeLam a b    -> TypeLam (specialiseType from to a) (specialiseType from to b)+        TypeTuple t    -> TypeTuple (fmap (specialiseType from to) t)+        TypeList t     -> TypeList (specialiseType from to t)++specialise :: [(Type, Type)] -> (NE.NonEmpty StringType, Type) -> [StringType]+specialise specs (names, typ) = concatMap go specs+  where go (from, to)+          | typ' <- specialiseType from to typ, typ /= typ' =+              ["{-# SPECIALISE " <> n <> " :: " <> showType (simplify typ') <> " #-}" | n <- NE.toList names]+          | otherwise = []++main :: IO ()+main = do+  args <- getArgs+  case args of+    [src, _, dst] -> do+      code <- T.readFile src+      case runCharParser source src code of+        Left report -> putStrLn $ showReport report+        Right ls -> do+          let specialisers = [(from, to) | SpecialiseAll from to <- ls]+              specialisedTypeDecls = concatMap (specialise specialisers) [(n, t) | TypeDecl n t <- ls]+          T.writeFile dst $ T.intercalate "\n" $ map showSource ls <> specialisedTypeDecls+    _ -> error "Usage: paripari-specialise-all src _ dst"
src/Text/PariPari.hs view
@@ -1,53 +1,43 @@ module Text.PariPari (-  module Text.PariPari.Class-  , module Text.PariPari.Combinators-  , module Text.PariPari.Acceptor-  , module Text.PariPari.Reporter-  , runParser-  , runSeqParser-  , runParserWithOptions-  , runSeqParserWithOptions-) where+  C.ChunkParser(..)+  , C.CharParser(..)+  , C.Error(..)+  , C.showError -import Text.PariPari.Acceptor-import Text.PariPari.Class-import Text.PariPari.Combinators-import Text.PariPari.Reporter-import GHC.Conc (par)+  , K.Chunk(Element, showElement, showChunk)+  , K.CharChunk+  , K.Pos(..) --- | Run fast 'Acceptor' and slower 'Reporter' on the given 'ByteString' **in parallel**.--- The 'FilePath' is used for error reporting.--- When the acceptor does not return successfully, the result from the reporter--- is awaited.-runParser :: Parser a -> FilePath -> ByteString -> Either Report a-runParser = runParserWithOptions defaultReportOptions-{-# INLINE runParser #-}--- Inline to force the specializer to kick in+  , U.runCharParser+  , U.runSeqCharParser+  , U.runCharParserWithOptions+  , U.runSeqCharParserWithOptions+  , U.runChunkParser+  , U.runSeqChunkParser+  , U.runChunkParserWithOptions+  , U.runSeqChunkParserWithOptions --- | Run fast 'Acceptor' and slower 'Reporter' on the given 'ByteString' **sequentially**.--- The 'FilePath' is used for error reporting.--- When the acceptor does not return successfully, the result from the reporter--- is awaited.-runSeqParser :: Parser a -> FilePath -> ByteString -> Either Report a-runSeqParser = runSeqParserWithOptions defaultReportOptions-{-# INLINE runSeqParser #-}+  , A.Acceptor+  , A.runAcceptor --- | Run parsers **in parallel** with additional 'ReportOptions'.-runParserWithOptions :: ReportOptions -> Parser a -> FilePath -> ByteString -> Either Report a-runParserWithOptions o p f b =-  let a = runAcceptor p f b-      r = runReporterWithOptions o p f b-  in case r `par` a of-       Left _  -> r-       Right x -> Right x-{-# INLINE runParserWithOptions #-}+  , R.Reporter+  , R.runReporter+  , R.showReport+  , R.showErrors+  , R.runReporterWithOptions --- | Run parsers **sequentially** with additional 'ReportOptions'.-runSeqParserWithOptions :: ReportOptions -> Parser a -> FilePath -> ByteString -> Either Report a-runSeqParserWithOptions o p f b =-  let a = runAcceptor p f b-      r = runReporterWithOptions o p f b-  in case a of-       Left _  -> r-       Right x -> Right x-{-# INLINE runSeqParserWithOptions #-}+  , T.Tracer+  , T.runTracer++  , module Text.PariPari.Internal.ElementCombinators+  , module Text.PariPari.Internal.CharCombinators+) where++import Text.PariPari.Internal.CharCombinators+import Text.PariPari.Internal.ElementCombinators+import qualified Text.PariPari.Internal.Acceptor as A+import qualified Text.PariPari.Internal.Chunk as K+import qualified Text.PariPari.Internal.Class as C+import qualified Text.PariPari.Internal.Reporter as R+import qualified Text.PariPari.Internal.Run as U+import qualified Text.PariPari.Internal.Tracer as T
− src/Text/PariPari/Acceptor.hs
@@ -1,242 +0,0 @@-module Text.PariPari.Acceptor (-  Acceptor-  , runAcceptor-) where--import Control.Monad (void)-import Text.PariPari.Ascii-import Text.PariPari.Class-import Text.PariPari.Decode-import Foreign.ForeignPtr (ForeignPtr)-import qualified Control.Monad.Fail as Fail-import qualified Data.ByteString.Internal as B--data Env = Env-  { _envSrc     :: !(ForeignPtr Word8)-  , _envEnd     :: !Int-  , _envFile    :: !FilePath-  , _envRefLine :: !Int-  , _envRefCol  :: !Int-  }--data State = State-  { _stOff     :: !Int-  , _stLine    :: !Int-  , _stCol     :: !Int-  }---- | Parser which is optimized for fast parsing. Error reporting--- is minimal.-newtype Acceptor a = Acceptor-  { unAcceptor :: forall b. Env -> State-               -> (a     -> State -> b)-               -> (Error -> b)-               -> b-  }--instance Semigroup a => Semigroup (Acceptor a) where-  p1 <> p2 = (<>) <$> p1 <*> p2-  {-# INLINE (<>) #-}--instance Monoid a => Monoid (Acceptor a) where-  mempty = pure mempty-  {-# INLINE mempty #-}--instance Functor Acceptor where-  fmap f p = Acceptor $ \env st ok err ->-    unAcceptor p env st (ok . f) err-  {-# INLINE fmap #-}--instance Applicative Acceptor where-  pure x = Acceptor $ \_ st ok _ -> ok x st-  {-# INLINE pure #-}--  f <*> a = Acceptor $ \env st ok err ->-    let ok1 f' s =-          let ok2 a' s' = ok (f' a') s'-          in unAcceptor a env s ok2 err-    in unAcceptor f env st ok1 err-  {-# INLINE (<*>) #-}--  p1 *> p2 = do-    void p1-    p2-  {-# INLINE (*>) #-}--  p1 <* p2 = do-    x <- p1-    void p2-    pure x-  {-# INLINE (<*) #-}--instance Alternative Acceptor where-  empty = Acceptor $ \_ _ _ err -> err EEmpty-  {-# INLINE empty #-}--  p1 <|> p2 = Acceptor $ \env st ok err ->-    let err' _ = unAcceptor p2 env st ok err-    in unAcceptor p1 env st ok err'-  {-# INLINE (<|>) #-}--instance MonadPlus Acceptor--instance Monad Acceptor where-  p >>= f = Acceptor $ \env st ok err ->-    let ok' x s = unAcceptor (f x) env s ok err-    in unAcceptor p env st ok' err-  {-# INLINE (>>=) #-}--  fail msg = Fail.fail msg-  {-# INLINE fail #-}--instance Fail.MonadFail Acceptor where-  fail msg = failWith $ EFail msg-  {-# INLINE fail #-}--instance MonadParser Acceptor where-  getPos = get $ \_ st -> Pos (_stLine st) (_stCol st)-  {-# INLINE getPos #-}--  getFile = get $ \env _ -> _envFile env-  {-# INLINE getFile #-}--  getRefPos = get $ \env _ -> Pos (_envRefLine env) (_envRefCol env)-  {-# INLINE getRefPos #-}--  withRefPos p = local (\st env -> env { _envRefLine = _stLine st, _envRefCol = _stCol st }) p-  {-# INLINE withRefPos #-}--  notFollowedBy p = Acceptor $ \env st ok err ->-    let ok' _ _ = err $ ECombinator "notFollowedBy"-        err' _ = ok () st-    in unAcceptor p env st ok' err'-  {-# INLINE notFollowedBy #-}--  lookAhead p = Acceptor $ \env st ok err ->-    let ok' x _ = ok x st-    in unAcceptor p env st ok' err-  {-# INLINE lookAhead #-}--  failWith e = Acceptor $ \_ _ _ err -> err e-  {-# INLINE failWith #-}--  eof = Acceptor $ \env st ok err ->-    if _stOff st >= _envEnd env then-      ok () st-    else-      err EExpectedEnd-  {-# INLINE eof #-}--  label _ p = p-  {-# INLINE label #-}--  hidden p = p-  {-# INLINE hidden #-}--  commit p = p-  {-# INLINE commit #-}--  byte b = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->-    if | _stOff >= _envEnd env -> err EEmpty-       | b == byteAt (_envSrc env) _stOff ->-           ok b st-           { _stOff =_stOff + 1-           , _stLine = if b == asc_newline then _stLine + 1 else _stLine-           , _stCol = if b == asc_newline then 1 else _stCol + 1-           }-       | otherwise ->-           err $ ECombinator "byte"-  {-# INLINE byte #-}--  byteSatisfy f = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->-    let b = byteAt (_envSrc env) _stOff-    in if | _stOff >= _envEnd env -> err EEmpty-          | f b ->-              ok b st-              { _stOff =_stOff + 1-              , _stLine = if b == asc_newline then _stLine + 1 else _stLine-              , _stCol = if b == asc_newline then 1 else _stCol + 1-              }-          | otherwise ->-              err $ ECombinator "byteSatisfy"-  {-# INLINE byteSatisfy #-}--  bytes b@(B.PS p i n) = Acceptor $ \env st@State{_stOff,_stCol} ok err ->-    if n + _stOff <= _envEnd env &&-       bytesEqual (_envSrc env) _stOff p i n then-      ok b st { _stOff = _stOff + n, _stCol = _stCol + n }-    else-      err $ ECombinator "bytes"-  {-# INLINE bytes #-}--  asBytes p = do-    begin <- get (const _stOff)-    p-    end <- get (const _stOff)-    src <- get (\env _ -> _envSrc env)-    pure $ B.PS src begin (end - begin)-  {-# INLINE asBytes #-}--  satisfy f = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->-    let (c, w) = utf8Decode (_envSrc env) _stOff-    in if | c /= '\0' ->-            if f c then-              ok c st-              { _stOff =_stOff + w-              , _stLine = if c == '\n' then _stLine + 1 else _stLine-              , _stCol = if c == '\n' then 1 else _stCol + 1-              }-            else-              err $ ECombinator "satisfy"-          | c == '\0' && _stOff >= _envEnd env -> err EEmpty-          | otherwise -> err $ ECombinator "satisfy"-  {-# INLINE satisfy #-}--  -- By inling this combinator, GHC should figure out the `utf8Width`-  -- of the character resulting in an optimized decoder.-  char c =-    let w = utf8Width c-    in Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->-      if utf8DecodeFixed w (_envSrc env) _stOff == c then-        ok c st-        { _stOff =_stOff + w-        , _stLine = if c == '\n' then _stLine + 1 else _stLine-        , _stCol = if c == '\n' then 1 else _stCol + 1-        }-      else-        err $ ECombinator "char"-  {-# INLINE char #-}---- | Reader monad, get something from the environment-get :: (Env -> State -> a) -> Acceptor a-get f = Acceptor $ \env st ok _ -> ok (f env st) st-{-# INLINE get #-}---- | Reader monad, modify environment locally-local :: (State -> Env -> Env) -> Acceptor a -> Acceptor a-local f p = Acceptor $ \env st ok err ->-  unAcceptor p (f st env) st ok err-{-# INLINE local #-}---- | Run 'Acceptor' on the given 'ByteString', returning either--- a simple 'Error' or, if successful, the result.-runAcceptor :: Acceptor a -> FilePath -> ByteString -> Either Error a-runAcceptor p f t =-  let b = t <> "\0\0\0"-  in unAcceptor p (initialEnv f b) (initialState b) (\x _ -> Right x) Left--initialEnv :: FilePath -> ByteString -> Env-initialEnv _envFile (B.PS _envSrc off len) = Env-  { _envSrc-  , _envFile-  , _envEnd = off + len - 3-  , _envRefLine = 0-  , _envRefCol = 0-  }--initialState :: ByteString -> State-initialState (B.PS _ _stOff _) = State-  { _stOff-  , _stLine = 1-  , _stCol = 1-  }
− src/Text/PariPari/Ascii.hs
@@ -1,45 +0,0 @@-module Text.PariPari.Ascii (-  module Text.PariPari.Ascii-) where--import Data.ByteString (ByteString)-import Data.Foldable (foldl')-import Data.Word (Word8)-import GHC.Base (unsafeChr)-import GHC.Show (showLitChar)-import Numeric (showHex)-import qualified Data.ByteString as B--asc_0, asc_9, asc_A, asc_E, asc_P, asc_a, asc_e, asc_p,-  asc_minus, asc_plus, asc_point, asc_newline :: Word8-asc_0 = 48-asc_9 = 57-asc_A = 65-asc_E = 69-asc_P = 80-asc_a = 97-asc_e = 101-asc_p = 112-asc_minus = 45-asc_plus = 43-asc_point = 46-asc_newline = 10--unsafeAsciiToChar :: Word8 -> Char-unsafeAsciiToChar = unsafeChr . fromIntegral-{-# INLINE unsafeAsciiToChar #-}--byteS :: Word8 -> ShowS-byteS b-  | b < 128 = showLitChar $ unsafeAsciiToChar b-  | otherwise = ("\\x" <>) . showHex b--bytesS :: ByteString -> ShowS-bytesS b | B.length b == 1 = byteS $ B.head b-         | otherwise = foldl' ((. byteS) . (.)) id $ B.unpack b--showByte :: Word8 -> String-showByte b = ('\'':) . byteS b . ('\'':) $ ""--showBytes :: ByteString -> String-showBytes b = ('"':) . bytesS b . ('"':) $ ""
− src/Text/PariPari/Class.hs
@@ -1,126 +0,0 @@-module Text.PariPari.Class (-  MonadParser(..)-  , Parser-  , Alternative(..)-  , MonadPlus-  , Pos(..)-  , Error(..)-  , ByteString-  , Word8-  , showError-) where--import Control.Applicative (Alternative(empty, (<|>)))-import Control.Monad (MonadPlus(..))-import Control.Monad.Fail (MonadFail(..))-import Data.ByteString (ByteString)-import Data.List (intercalate)-import Data.Word (Word8)-import GHC.Generics (Generic)---- | Line and column position starting at (1,1)-data Pos = Pos-  { _posLine   :: !Int-  , _posColumn :: !Int-  } deriving (Eq, Show, Generic)---- | Parsing errors-data Error-  = EEmpty-  | EInvalidUtf8-  | EExpectedEnd-  | EExpected         [String]-  | EUnexpected       String-  | EFail             String-  | ECombinator       String-  | EIndentNotAligned !Int !Int-  | EIndentOverLine   !Int !Int-  | ENotEnoughIndent  !Int !Int-  deriving (Eq, Ord, Show, Generic)---- | Parser shortcut-type Parser a = (forall p. MonadParser p => p a)---- | Parser class, which specifies the necessary--- primitives for parsing. All other parser combinators--- rely on these primitives.-class (MonadFail p, MonadPlus p) => MonadParser p where-  -- | Get file name associated with current parser-  getFile :: p FilePath--  -- | Get current position of the parser-  getPos :: p Pos--  -- | Get reference position used for indentation-sensitive parsing-  getRefPos :: p Pos--  -- | Update reference position with current position-  withRefPos :: p a -> p a--  -- | Parser which succeeds when the given parser fails-  notFollowedBy :: Show a => p a -> p ()--  -- | Look ahead and return result of the given parser-  -- The current position stays the same.-  lookAhead :: p a -> p a--  -- | Parser failure with detailled 'Error'-  failWith :: Error -> p a--  -- | Parser which succeeds at the end of file-  eof :: p ()--  -- | Annotate the given parser with a label-  -- used for error reporting-  label :: String -> p a -> p a--  -- | Hide errors occurring within the given parser-  -- from the error report. Based on the given-  -- labels an 'Error' is constructed instead.-  hidden :: p a -> p a--  -- | Commit to the given branch, increasing-  -- the priority of the errors within this branch-  -- in contrast to other branches.-  ---  -- This is basically the opposite of the `try`-  -- combinator provided by other parser combinator-  -- libraries, which decreases the error priority-  -- within the given branch (and usually also influences backtracking).-  ---  -- __Note__: `commit` only applies to the reported-  -- errors, it has no effect on the backtracking behavior-  -- of the parser.-  commit :: p a -> p a--  -- | Parse a single byte-  byte :: Word8 -> p Word8--  -- | Parse a single UTF-8 character-  char :: Char -> p Char--  -- | Parse a single character with the given predicate-  satisfy :: (Char -> Bool) -> p Char--  -- | Parse a single byte with the given predicate-  byteSatisfy :: (Word8 -> Bool) -> p Word8--  -- | Parse a string of bytes-  bytes :: ByteString -> p ByteString--  -- | Run the given parser and return the-  -- result as bytes-  asBytes :: p () -> p ByteString---- | Pretty string representation of 'Error'-showError :: Error -> String-showError EEmpty                   = "No error"-showError EInvalidUtf8             = "Invalid UTF-8 character found"-showError EExpectedEnd             = "Expected end of file"-showError (EExpected tokens)       = "Expected " <> intercalate ", " tokens-showError (EUnexpected token)      = "Unexpected " <> token-showError (EFail msg)              = msg-showError (ECombinator name)       = "Combinator " <> name <> " failed"-showError (EIndentNotAligned rc c) = "Invalid alignment, expected column " <> show rc <> " expected, got " <> show c-showError (EIndentOverLine   rl l) = "Indentation over line, expected line " <> show rl <> ", got " <> show l-showError (ENotEnoughIndent  rc c) = "Not enough indentation, expected column " <> show rc <> ", got " <> show c
− src/Text/PariPari/Combinators.hs
@@ -1,457 +0,0 @@-module Text.PariPari.Combinators (-  -- * Basics-  Text-  , void-  , (<|>)-  , empty-  , optional--  -- * Control.Monad.Combinators.NonEmpty-  , NonEmpty(..)-  , ON.some-  , ON.endBy1-  , ON.someTill-  , ON.sepBy1-  , ON.sepEndBy1--  -- * Control.Monad.Combinators-  , O.many -- dont use Applicative version for efficiency-  , O.between-  , O.choice-  , O.count-  , O.count'-  , O.eitherP-  , O.endBy-  , O.manyTill-  , O.option-  , O.sepBy-  , O.sepEndBy-  , O.skipMany-  , O.skipSome-  , O.skipCount-  , O.skipManyTill-  , O.skipSomeTill--  -- * PariPari-  , (<?>)-  , getLine-  , getColumn-  , withPos-  , withSpan-  , getRefColumn-  , getRefLine-  , withRefPos-  , align-  , indented-  , line-  , linefold-  , notByte-  , anyByte-  , digitByte-  , asciiByte-  , integer-  , integer'-  , decimal-  , octal-  , hexadecimal-  , digit-  , signed-  , fractionHex-  , fractionDec-  , char'-  , notChar-  , anyChar-  , alphaNumChar-  , digitChar-  , letterChar-  , lowerChar-  , upperChar-  , symbolChar-  , categoryChar-  , punctuationChar-  , spaceChar-  , asciiChar-  , string-  , string'-  , asString-  , takeBytes-  , skipChars-  , skipBytes-  , takeChars-  , skipCharsWhile-  , takeCharsWhile-  , skipBytesWhile-  , takeBytesWhile-  , skipBytesWhile1-  , takeBytesWhile1-  , skipCharsWhile1-  , takeCharsWhile1-) where--import Control.Applicative ((<|>), empty, optional)-import Control.Monad (when)-import Control.Monad.Combinators (option, skipCount, skipMany)-import Data.List.NonEmpty (NonEmpty(..))-import Text.PariPari.Ascii-import Text.PariPari.Class-import Data.Text (Text)-import Data.Functor (void)-import Prelude hiding (getLine)-import qualified Control.Monad.Combinators as O-import qualified Control.Monad.Combinators.NonEmpty as ON-import qualified Data.Char as C-import qualified Data.Text.Encoding as T-import qualified Data.Text as T--infix 0 <?>---- | Infix alias for 'label'-(<?>) :: MonadParser p => p a -> String -> p a-(<?>) = flip label-{-# INLINE (<?>) #-}---- | Get line number of the reference position-getRefLine :: Parser Int-getRefLine = _posLine <$> getRefPos-{-# INLINE getRefLine #-}---- | Get column number of the reference position-getRefColumn :: Parser Int-getRefColumn = _posColumn <$> getRefPos-{-# INLINE getRefColumn #-}---- | Get current line number-getLine :: Parser Int-getLine = _posLine <$> getPos-{-# INLINE getLine #-}---- | Get current column-getColumn :: Parser Int-getColumn = _posColumn <$> getPos-{-# INLINE getColumn #-}---- | Decorate the parser result with the current position-withPos :: MonadParser p => p a -> p (Pos, a)-withPos p = do-  pos <- getPos-  ret <- p-  pure (pos, ret)-{-# INLINE withPos #-}--type Span = (Pos, Pos)---- | Decoreate the parser result with the position span-withSpan :: MonadParser p => p a -> p (Span, a)-withSpan p = do-  begin <- getPos-  ret <- p-  end <- getPos-  pure ((begin, end), ret)-{-# INLINE withSpan #-}---- | Parser succeeds on the same line as the reference line-line :: Parser ()-line = do-  l <- getLine-  rl <- getRefLine-  when (l /= rl) $ failWith $ EIndentOverLine rl l-{-# INLINE line #-}---- | Parser succeeds on the same column as the reference column-align :: Parser ()-align = do-  c <- getColumn-  rc <- getRefColumn-  when (c /= rc) $ failWith $ EIndentNotAligned rc c-{-# INLINE align #-}---- | Parser succeeds for columns greater than the current reference column-indented :: Parser ()-indented = do-  c <- getColumn-  rc <- getRefColumn-  when (c <= rc) $ failWith $ ENotEnoughIndent rc c-{-# INLINE indented #-}---- | Parser succeeds either on the reference line or--- for columns greater than the current reference column-linefold :: Parser ()-linefold = line <|> indented-{-# INLINE linefold #-}---- | Parser a single byte different from the given one-notByte :: Word8 -> Parser Word8-notByte b = byteSatisfy (/= b) <?> "not " <> showByte b-{-# INLINE notByte #-}---- | Parse an arbitrary byte-anyByte :: Parser Word8-anyByte = byteSatisfy (const True)-{-# INLINE anyByte #-}---- | Parse a byte of the ASCII charset (< 128)-asciiByte :: Parser Word8-asciiByte = byteSatisfy (< 128)-{-# INLINE asciiByte #-}---- | Parse a digit byte for the given base.--- Bases 2 to 36 are supported.-digitByte :: Int -> Parser Word8-digitByte base = byteSatisfy (isDigit base)-{-# INLINE digitByte #-}---- | Parse an integer of the given base.--- Returns the integer and the number of digits.--- Bases 2 to 36 are supported.--- Digits can be separated by separator, e.g. `optional (char '_')`.-integer' :: (Num a, MonadParser p) => p sep -> Int -> p (a, Int)-integer' sep base = label (integerLabel base) $ do-  d <- digit base-  accum 1 $ fromIntegral d-  where accum !i !n = next i n <|> pure (n, i)-        next !i !n = do-          void $ sep-          d <- digit base-          accum (i + 1) $ n * fromIntegral base + fromIntegral d-{-# INLINE integer' #-}---- | Parse an integer of the given base.--- Bases 2 to 36 are supported.--- Digits can be separated by separator, e.g. `optional (char '_')`.-integer :: (Num a, MonadParser p) => p sep -> Int -> p a-integer sep base = label (integerLabel base) $ do-  d <- digit base-  accum $ fromIntegral d-  where accum !n = next n <|> pure n-        next !n = do-          void $ sep-          d <- digit base-          accum $ n * fromIntegral base + fromIntegral d-{-# INLINE integer #-}--integerLabel :: Int -> String-integerLabel 2  = "binary integer"-integerLabel 8  = "octal integer"-integerLabel 10 = "decimal integer"-integerLabel 16 = "hexadecimal integer"-integerLabel b  = "integer of base " <> show b--decimal :: Num a => Parser a-decimal = integer (pure ()) 10-{-# INLINE decimal #-}--octal :: Num a => Parser a-octal = integer (pure ()) 8-{-# INLINE octal #-}--hexadecimal :: Num a => Parser a-hexadecimal = integer (pure ()) 16-{-# INLINE hexadecimal #-}--digitToInt :: Int -> Word8 -> Word-digitToInt base b-  | n <- (fromIntegral b :: Word) - fromIntegral asc_0, base <= 10 || n <= 9  = n-  | n <- (fromIntegral b :: Word) - fromIntegral asc_A, n               <= 26 = n + 10-  | n <- (fromIntegral b :: Word) - fromIntegral asc_a                        = n + 10-{-# INLINE digitToInt #-}---- | Parse a single digit of the given base and return its value.--- Bases 2 to 36 are supported.-digit :: Int -> Parser Word-digit base = digitToInt base <$> byteSatisfy (isDigit base)-{-# INLINE digit #-}--isDigit :: Int -> Word8 -> Bool-isDigit base b-  | base >= 2 && base <= 10 = b >= asc_0 && b <= asc_0 + fromIntegral base - 1-  | base <= 36 = (b >= asc_0 && b <= asc_9)-                 || ((fromIntegral b :: Word) - fromIntegral asc_A) < fromIntegral (base - 10)-                 || ((fromIntegral b :: Word) - fromIntegral asc_a) < fromIntegral (base - 10)-  |otherwise = error "Text.PariPari.Combinators.isDigit: Bases 2 to 36 are supported"-{-# INLINE isDigit #-}---- | Parse a number with a plus or minus sign.-signed :: (Num a, MonadParser p) => p a -> p a-signed p = ($) <$> ((id <$ byte asc_plus) <|> (negate <$ byte asc_minus) <|> pure id) <*> p-{-# INLINE signed #-}---- | Parse a fraction of arbitrary exponent base and coefficient base.--- 'fractionDec' and 'fractionHex' should be used instead probably.-fraction :: (Num a, MonadParser p) => p expSep -> Int -> Int -> p digitSep -> p (a, Int, a)-fraction expSep expBase coeffBasePow digitSep = do-  let coeffBase = expBase ^ coeffBasePow-  coeff <- integer digitSep coeffBase-  void $ optional $ byte asc_point-  (frac, fracLen) <- option (0, 0) $ integer' digitSep coeffBase-  expVal <- option 0 $ expSep *> signed (integer digitSep 10)-  pure (coeff * fromIntegral coeffBase ^ fracLen + frac,-        expBase,-        expVal - fromIntegral (fracLen * coeffBasePow))-{-# INLINE fraction #-}---- | Parse a decimal fraction, returning (coefficient, 10, exponent),--- corresponding to coefficient * 10^exponent.--- Digits can be separated by separator, e.g. `optional (char '_')`.-fractionDec :: (Num a, MonadParser p) => p digitSep -> p (a, Int, a)-fractionDec sep = fraction (byteSatisfy (\b -> b == asc_E || b == asc_e)) 10 1 sep <?> "fraction"-{-# INLINE fractionDec #-}---- | Parse a hexadecimal fraction, returning (coefficient, 2, exponent),--- corresponding to coefficient * 2^exponent.--- Digits can be separated by separator, e.g. `optional (char '_')`.-fractionHex :: (Num a, MonadParser p) => p digitSep -> p (a, Int, a)-fractionHex sep = fraction (byteSatisfy (\b -> b == asc_P || b == asc_p)) 2 4 sep <?> "hexadecimal fraction"-{-# INLINE fractionHex #-}---- | Parse a case-insensitive character-char' :: Char -> Parser Char-char' x =-  let l = C.toLower x-      u = C.toUpper x-  in satisfy (\c -> c == l || c == u)-{-# INLINE char' #-}---- | Parse a character different from the given one.-notChar :: Char -> Parser Char-notChar c = satisfy (/= c)-{-# INLINE notChar #-}---- | Parse an arbitrary character.-anyChar :: Parser Char-anyChar = satisfy (const True)-{-# INLINE anyChar #-}---- | Parse an alphanumeric character, including Unicode.-alphaNumChar :: Parser Char-alphaNumChar = satisfy C.isAlphaNum <?> "alphanumeric character"-{-# INLINE alphaNumChar #-}---- | Parse a letter character, including Unicode.-letterChar :: Parser Char-letterChar = satisfy C.isLetter <?> "letter"-{-# INLINE letterChar #-}---- | Parse a lowercase letter, including Unicode.-lowerChar :: Parser Char-lowerChar = satisfy C.isLower <?> "lowercase letter"-{-# INLINE lowerChar #-}---- | Parse a uppercase letter, including Unicode.-upperChar :: Parser Char-upperChar = satisfy C.isUpper <?> "uppercase letter"-{-# INLINE upperChar #-}---- | Parse a space character, including Unicode.-spaceChar :: Parser Char-spaceChar = satisfy C.isSpace <?> "space"-{-# INLINE spaceChar #-}---- | Parse a symbol character, including Unicode.-symbolChar :: Parser Char-symbolChar = satisfy C.isSymbol <?> "symbol"-{-# INLINE symbolChar #-}---- | Parse a punctuation character, including Unicode.-punctuationChar :: Parser Char-punctuationChar = satisfy C.isPunctuation <?> "punctuation"-{-# INLINE punctuationChar #-}---- | Parse a digit character of the given base.--- Bases 2 to 36 are supported.-digitChar :: Int -> Parser Char-digitChar base = unsafeAsciiToChar <$> digitByte base-{-# INLINE digitChar #-}---- | Parse a character beloning to the ASCII charset (< 128)-asciiChar :: Int -> Parser Char-asciiChar base = unsafeAsciiToChar <$> digitByte base-{-# INLINE asciiChar #-}---- | Parse a character belonging to the given Unicode category-categoryChar :: C.GeneralCategory -> Parser Char-categoryChar cat = satisfy ((== cat) . C.generalCategory) <?> untitle (show cat)-{-# INLINE categoryChar #-}---- | Parse a text string-string :: Text -> Parser Text-string t = t <$ bytes (T.encodeUtf8 t)-{-# INLINE string #-}--string' :: Text -> Parser Text-string' s = asString (go s) <?> "case-insensitive \"" <> T.unpack (T.toLower s) <> "\""-  where go t-          | T.null t  = pure ()-          | otherwise = char' (T.head t) *> go (T.tail t)-{-# INLINE string' #-}---- | Run the given parser but return the result as a 'Text' string-asString :: MonadParser p => p () -> p Text-asString p = T.decodeUtf8 <$> asBytes p-{-# INLINE asString #-}---- | Take the next n bytes and advance the position by n bytes-takeBytes :: Int -> Parser ByteString-takeBytes n = asBytes (skipBytes n) <?> show n <> " bytes"-{-# INLINE takeBytes #-}---- | Skip the next n bytes-skipBytes :: Int -> Parser ()-skipBytes n = skipCount n anyByte-{-# INLINE skipBytes #-}---- | Skip the next n characters-skipChars :: Int -> Parser ()-skipChars n = skipCount n anyChar-{-# INLINE skipChars #-}---- | Take the next n characters and advance the position by n characters-takeChars :: Int -> Parser Text-takeChars n = asString (skipChars n) <?> "string of length " <> show n-{-# INLINE takeChars #-}---- | Skip char while predicate is true-skipCharsWhile :: (Char -> Bool) -> Parser ()-skipCharsWhile f = skipMany (satisfy f)-{-# INLINE skipCharsWhile #-}---- | Take chars while predicate is true-takeCharsWhile :: (Char -> Bool) -> Parser Text-takeCharsWhile f = asString (skipCharsWhile f)-{-# INLINE takeCharsWhile #-}---- | Skip bytes while predicate is true-skipBytesWhile :: (Word8 -> Bool) -> Parser ()-skipBytesWhile f = skipMany (byteSatisfy f)-{-# INLINE skipBytesWhile #-}---- | Takes bytes while predicate is true-takeBytesWhile :: (Word8 -> Bool) -> Parser ByteString-takeBytesWhile f = asBytes (skipBytesWhile f)-{-# INLINE takeBytesWhile #-}---- | Skip at least one byte while predicate is true-skipBytesWhile1 :: (Word8 -> Bool) -> Parser ()-skipBytesWhile1 f = byteSatisfy f *> skipBytesWhile f-{-# INLINE skipBytesWhile1 #-}---- | Take at least one byte while predicate is true-takeBytesWhile1 :: (Word8 -> Bool) -> Parser ByteString-takeBytesWhile1 f = asBytes (skipBytesWhile1 f)-{-# INLINE takeBytesWhile1 #-}---- | Skip at least one byte while predicate is true-skipCharsWhile1 :: (Char -> Bool) -> Parser ()-skipCharsWhile1 f = satisfy f *> skipCharsWhile f-{-# INLINE skipCharsWhile1 #-}---- | Take at least one byte while predicate is true-takeCharsWhile1 :: (Char -> Bool) -> Parser Text-takeCharsWhile1 f = asString (skipCharsWhile1 f)-{-# INLINE takeCharsWhile1 #-}--untitle :: String -> String-untitle []     = []-untitle (x:xs) = C.toLower x : go xs-  where go [] = ""-        go (y:ys) | C.isUpper y = ' ' : C.toLower y : untitle ys-                  | otherwise   = y : ys
− src/Text/PariPari/Decode.hs
@@ -1,90 +0,0 @@-module Text.PariPari.Decode (-  bytesEqual-  , byteAt-  , utf8Decode-  , utf8DecodeFixed-  , utf8Width-) where--import Data.Word (Word8)-import Data.Bits (unsafeShiftL, (.|.), (.&.))-import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)-import Foreign.Ptr (plusPtr)-import Foreign.Storable (peekByteOff)-import GHC.Base (unsafeChr)-import qualified Data.ByteString.Internal as B--bytesEqual :: ForeignPtr Word8 -> Int -> ForeignPtr Word8 -> Int -> Int -> Bool-bytesEqual p1 i1 p2 i2 n =-  B.accursedUnutterablePerformIO $-  withForeignPtr p1 $ \q1 ->-  withForeignPtr p2 $ \q2 ->-  (== 0) <$> B.memcmp (q1 `plusPtr` i1) (q2 `plusPtr` i2) n-{-# INLINE bytesEqual #-}--byteAt :: ForeignPtr Word8 -> Int -> Word8-byteAt p i = B.accursedUnutterablePerformIO $-  withForeignPtr p $ \q -> peekByteOff q i-{-# INLINE byteAt #-}--at :: ForeignPtr Word8 -> Int -> Int-at p i = fromIntegral $ byteAt p i-{-# INLINE at #-}---- | Decode UTF-8 character at the given offset relative to the pointer-utf8Decode :: ForeignPtr Word8 -> Int -> (Char, Int)-utf8Decode p i-  | a1 <- at p i,-    a1 <= 0x7F =-    (unsafeChr a1, 1)-  | a1 <- at p i, a2 <- at p (i + 1),-    (a1 .&. 0xE0) == 0xC0,-    (a2 .&. 0xC0) == 0x80 =-    (unsafeChr (((a1 .&. 31) `unsafeShiftL` 6)-                .|. (a2 .&. 0x3F)), 2)-  | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2),-    (a1 .&. 0xF0) == 0xE0,-    (a2 .&. 0xC0) == 0x80,-    (a3 .&. 0xC0) == 0x80 =-    (unsafeChr (((a1 .&. 15) `unsafeShiftL` 12)-                 .|. ((a2 .&. 0x3F) `unsafeShiftL` 6)-                 .|. (a3 .&. 0x3F)), 3)-  | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2), a4 <- at p (i + 3),-    (a1 .&. 0xF8) == 0xF0,-    (a2 .&. 0xC0) == 0x80,-    (a3 .&. 0xC0) == 0x80,-    (a4 .&. 0xC0) == 0x80 =-    (unsafeChr (((a1 .&. 7) `unsafeShiftL` 18)-                 .|. ((a2 .&. 0x3F) `unsafeShiftL` 12)-                 .|. ((a3 .&. 0x3F) `unsafeShiftL` 6)-                 .|. (a4 .&. 0x3F)), 4)-  | otherwise = ('\0', 0)-{-# INLINE utf8Decode #-}---- | Decode UTF-8 character with known width at the given offset relative to the pointer-utf8DecodeFixed :: Int -> ForeignPtr Word8 -> Int -> Char-utf8DecodeFixed w p i = unsafeChr $-  case w of-    1 -> at p i-    2 | a1 <- at p i, a2 <- at p (i + 1) ->-        ((a1 .&. 31) `unsafeShiftL` 6)-        .|. (a2 .&. 0x3F)-    3 | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2) ->-        ((a1 .&. 15) `unsafeShiftL` 12)-        .|. ((a2 .&. 0x3F) `unsafeShiftL` 6)-        .|. (a3 .&. 0x3F)-    4 | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2), a4 <- at p (i + 3) ->-        ((a1 .&. 7) `unsafeShiftL` 18)-        .|. ((a2 .&. 0x3F) `unsafeShiftL` 12)-        .|. ((a3 .&. 0x3F) `unsafeShiftL` 6)-        .|. (a4 .&. 0x3F)-    _ -> 0-{-# INLINE utf8DecodeFixed #-}---- | Bytes width of an UTF-8 character-utf8Width :: Char -> Int-utf8Width c | c <= unsafeChr 0x7F = 1-            | c <= unsafeChr 0x7FF = 2-            | c <= unsafeChr 0xFFFF = 3-            | otherwise = 4-{-# INLINE utf8Width #-}
+ src/Text/PariPari/Internal/Acceptor.hs view
@@ -0,0 +1,274 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+module Text.PariPari.Internal.Acceptor (+  Acceptor(..)+  , Env(..)+  , State(..)+  , get+  , local+  , runAcceptor+) where++import Control.Monad (void)+import Text.PariPari.Internal.Class+import Text.PariPari.Internal.Chunk+import qualified Control.Monad.Fail as Fail++data Env k = Env+  { _envBuf     :: !(Buffer k)+  , _envEnd     :: !Int+  , _envFile    :: !FilePath+  , _envRefLine :: !Int+  , _envRefCol  :: !Int+  }++data State = State+  { _stOff     :: !Int+  , _stLine    :: !Int+  , _stCol     :: !Int+  }++-- | Parser which is optimised for fast parsing. Error reporting+-- is minimal.+newtype Acceptor k a = Acceptor+  { unAcceptor :: forall b. Env k -> State+               -> (a     -> State -> b)+               -> (Error -> b)+               -> b+  }++instance (Chunk k, Semigroup a) => Semigroup (Acceptor k a) where+  p1 <> p2 = (<>) <$> p1 <*> p2+  {-# INLINE (<>) #-}++instance (Chunk k, Monoid a) => Monoid (Acceptor k a) where+  mempty = pure mempty+  {-# INLINE mempty #-}++instance Functor (Acceptor k) where+  fmap f p = Acceptor $ \env st ok err ->+    unAcceptor p env st (ok . f) err+  {-# INLINE fmap #-}++instance Chunk k => Applicative (Acceptor k) where+  pure x = Acceptor $ \_ st ok _ -> ok x st+  {-# INLINE pure #-}++  f <*> a = Acceptor $ \env st ok err ->+    let ok1 f' s =+          let ok2 a' s' = ok (f' a') s'+          in unAcceptor a env s ok2 err+    in unAcceptor f env st ok1 err+  {-# INLINE (<*>) #-}++  p1 *> p2 = do+    void p1+    p2+  {-# INLINE (*>) #-}++  p1 <* p2 = do+    x <- p1+    void p2+    pure x+  {-# INLINE (<*) #-}++instance Chunk k => Alternative (Acceptor k) where+  empty = Acceptor $ \_ _ _ err -> err $ ECombinator "empty"+  {-# INLINE empty #-}++  p1 <|> p2 = Acceptor $ \env st ok err ->+    let err' _ = unAcceptor p2 env st ok err+    in unAcceptor p1 env st ok err'+  {-# INLINE (<|>) #-}++instance Chunk k => MonadPlus (Acceptor k)++instance Chunk k => Monad (Acceptor k) where+  p >>= f = Acceptor $ \env st ok err ->+    let ok' x s = unAcceptor (f x) env s ok err+    in unAcceptor p env st ok' err+  {-# INLINE (>>=) #-}++  fail msg = Fail.fail msg+  {-# INLINE fail #-}++instance Chunk k => Fail.MonadFail (Acceptor k) where+  fail msg = failWith $ EFail msg+  {-# INLINE fail #-}++instance Chunk k => ChunkParser k (Acceptor k) where+  getPos = get $ \_ st -> Pos (_stLine st) (_stCol st)+  {-# INLINE getPos #-}++  getFile = get $ \env _ -> _envFile env+  {-# INLINE getFile #-}++  getRefPos = get $ \env _ -> Pos (_envRefLine env) (_envRefCol env)+  {-# INLINE getRefPos #-}++  withRefPos p = local (\st env -> env { _envRefLine = _stLine st, _envRefCol = _stCol st }) p+  {-# INLINE withRefPos #-}++  notFollowedBy p = Acceptor $ \env st ok err ->+    let ok' _ _ = err $ ECombinator "notFollowedBy"+        err' _ = ok () st+    in unAcceptor p env st ok' err'+  {-# INLINE notFollowedBy #-}++  lookAhead p = Acceptor $ \env st ok err ->+    let ok' x _ = ok x st+    in unAcceptor p env st ok' err+  {-# INLINE lookAhead #-}++  failWith e = Acceptor $ \_ _ _ err -> err e+  {-# INLINE failWith #-}++  eof = Acceptor $ \env st ok err ->+    if _stOff st >= _envEnd env then+      ok () st+    else+      err expectedEnd+  {-# INLINE eof #-}++  label _ p = p+  {-# INLINE label #-}++  hidden p = p+  {-# INLINE hidden #-}++  commit p = p+  {-# INLINE commit #-}++  element e = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    if | _stOff < _envEnd env,+         (e', w) <- elementAt @k (_envBuf env) _stOff,+         e == e',+         pos <- elementPos @k e (Pos _stLine _stCol) ->+           ok e st { _stOff = _stOff + w, _stLine = _posLine pos, _stCol = _posColumn pos }+       | otherwise ->+           err $ ECombinator "element"+  {-# INLINE element #-}++  elementSatisfy f = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    if | _stOff < _envEnd env,+         (e, w) <- elementAt @k (_envBuf env) _stOff,+         f e,+         pos <- elementPos @k e (Pos _stLine _stCol) ->+           ok e st { _stOff = _stOff + w, _stLine = _posLine pos, _stCol = _posColumn pos }+       | otherwise ->+           err $ ECombinator "elementSatisfy"+  {-# INLINE elementSatisfy #-}++  chunk k = Acceptor $ \env st@State{_stOff,_stCol} ok err ->+    let n = chunkWidth @k k+    in if n + _stOff <= _envEnd env &&+          chunkEqual @k (_envBuf env) _stOff k then+         ok k st { _stOff = _stOff + n, _stCol = _stCol + n }+       else+         err $ ECombinator "chunk"+  {-# INLINE chunk #-}++  asChunk p = do+    begin <- get (const _stOff)+    p+    end <- get (const _stOff)+    src <- get (\env _ -> _envBuf env)+    pure $ packChunk src begin (end - begin)+  {-# INLINE asChunk #-}++instance CharChunk k => CharParser k (Acceptor k) where+  satisfy f = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    if | (c, w) <- charAt @k (_envBuf env) _stOff,+         c /= '\0',+         f c ->+           ok c st+           { _stOff = _stOff + w+           , _stLine = if c == '\n' then _stLine + 1 else _stLine+           , _stCol = if c == '\n' then 1 else _stCol + 1+           }+       | otherwise ->+           err $ ECombinator "satisfy"+  {-# INLINE satisfy #-}++  -- By inling this combinator, GHC should figure out the `charWidth`+  -- of the character resulting in an optimised decoder.+  char '\0' = error "Character '\\0' cannot be parsed because it is used as sentinel"+  char c+    | w <- charWidth @k c =+        Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->+        if charAtFixed @k w (_envBuf env) _stOff == c then+          ok c st+          { _stOff = _stOff + w+          , _stLine = if c == '\n' then _stLine + 1 else _stLine+          , _stCol = if c == '\n' then 1 else _stCol + 1+          }+        else+          err $ ECombinator "char"+  {-# INLINE char #-}++  asciiSatisfy f = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    if | b <- byteAt @k (_envBuf env) _stOff,+         b /= 0,+         b < 128,+         f b ->+           ok b st+           { _stOff = _stOff + 1+           , _stLine = if b == asc_newline then _stLine + 1 else _stLine+           , _stCol = if b == asc_newline then 1 else _stCol + 1+           }+       | otherwise ->+           err $ ECombinator "asciiSatisfy"+  {-# INLINE asciiSatisfy #-}++  asciiByte 0 = error "Character '\\0' cannot be parsed because it is used as sentinel"+  asciiByte b+    | b >= 128 = error "Not an ASCII character"+    | otherwise = Acceptor $ \env st@State{_stOff, _stLine, _stCol} ok err ->+        if byteAt @k (_envBuf env) _stOff == b then+          ok b st+          { _stOff = _stOff + 1+          , _stLine = if b == asc_newline then _stLine + 1 else _stLine+          , _stCol = if b == asc_newline then 1 else _stCol + 1+          }+        else+          err $ ECombinator "asciiByte"+  {-# INLINE asciiByte #-}++-- | Reader monad, get something from the environment+get :: (Env k -> State -> a) -> Acceptor k a+get f = Acceptor $ \env st ok _ -> ok (f env st) st+{-# INLINE get #-}++-- | Reader monad, modify environment locally+local :: (State -> Env k -> Env k) -> Acceptor k a -> Acceptor k a+local f p = Acceptor $ \env st ok err ->+  unAcceptor p (f st env) st ok err+{-# INLINE local #-}++-- | Run 'Acceptor' on the given chunk, returning either+-- a simple 'Error' or, if successful, the result.+runAcceptor :: Chunk k => Acceptor k a -> FilePath -> k -> Either Error a+runAcceptor p f k =+  let (b, off, len) = unpackChunk k+  in unAcceptor p (initialEnv f b (off + len)) (initialState off) (\x _ -> Right x) Left++initialEnv :: FilePath -> Buffer k -> Int -> Env k+initialEnv _envFile _envBuf _envEnd = Env+  { _envBuf+  , _envFile+  , _envEnd+  , _envRefLine = 1+  , _envRefCol = 1+  }++initialState :: Int -> State+initialState _stOff = State+  { _stOff+  , _stLine = 1+  , _stCol = 1+  }
+ src/Text/PariPari/Internal/CharCombinators.hs view
@@ -0,0 +1,266 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE Rank2Types #-}+module Text.PariPari.Internal.CharCombinators (+  digitByte+  , integer+  , integer'+  , decimal+  , octal+  , hexadecimal+  , digit+  , signed+  , fractionHex+  , fractionDec+  , char'+  , notChar+  , anyChar+  , alphaNumChar+  , digitChar+  , letterChar+  , lowerChar+  , upperChar+  , symbolChar+  , categoryChar+  , punctuationChar+  , spaceChar+  , asciiChar+  , skipChars+  , takeChars+  , skipCharsWhile+  , takeCharsWhile+  , skipCharsWhile1+  , takeCharsWhile1+  , string+) where++import Control.Applicative ((<|>), optional)+import Control.Monad.Combinators (option, skipCount, skipMany)+import Text.PariPari.Internal.Chunk+import Text.PariPari.Internal.Class+import Text.PariPari.Internal.ElementCombinators ((<?>))+import Data.Text (Text)+import Data.Functor (void)+import Data.Word (Word8)+import qualified Data.Char as C++type CharP k a  = (forall p. CharParser k p => p a)++-- | Parse a digit byte for the given base.+-- Bases 2 to 36 are supported.+digitByte :: CharParser k p => Int -> p Word8+digitByte base = asciiSatisfy (isDigit base)+{-# INLINE digitByte #-}++isDigit :: Int -> Word8 -> Bool+isDigit base b+  | base >= 2 && base <= 10 = b >= asc_0 && b <= asc_0 + fromIntegral base - 1+  | base <= 36 = (b >= asc_0 && b <= asc_9)+                 || ((fromIntegral b :: Word) - fromIntegral asc_A) < fromIntegral (base - 10)+                 || ((fromIntegral b :: Word) - fromIntegral asc_a) < fromIntegral (base - 10)+  |otherwise = error "Text.PariPari.Internal.Combinators.isDigit: Bases 2 to 36 are supported"+{-# INLINE isDigit #-}++digitToInt :: Int -> Word8 -> Word+digitToInt base b+  | n <- (fromIntegral b :: Word) - fromIntegral asc_0, base <= 10 || n <= 9  = n+  | n <- (fromIntegral b :: Word) - fromIntegral asc_A, n               <= 26 = n + 10+  | n <- (fromIntegral b :: Word) - fromIntegral asc_a                        = n + 10+{-# INLINE digitToInt #-}++-- | Parse a single digit of the given base and return its value.+-- Bases 2 to 36 are supported.+digit :: CharParser k p => Int -> p Word+digit base = digitToInt base <$> asciiSatisfy (isDigit base)+{-# INLINE digit #-}++-- | Parse an integer of the given base.+-- Returns the integer and the number of digits.+-- Bases 2 to 36 are supported.+-- Digits can be separated by separator, e.g. `optional (char '_')`.+integer' :: (Num a, CharParser k p) => p sep -> Int -> p (a, Int)+integer' sep base = label (integerLabel base) $ do+  d <- digit base+  accum 1 $ fromIntegral d+  where accum !i !n = next i n <|> pure (n, i)+        next !i !n = do+          void $ sep+          d <- digit base+          accum (i + 1) $ n * fromIntegral base + fromIntegral d+{-# INLINE integer' #-}++-- | Parse an integer of the given base.+-- Bases 2 to 36 are supported.+-- Digits can be separated by separator, e.g. `optional (char '_')`.+integer :: (Num a, CharParser k p) => p sep -> Int -> p a+integer sep base = label (integerLabel base) $ do+  d <- digit base+  accum $ fromIntegral d+  where accum !n = next n <|> pure n+        next !n = do+          void $ sep+          d <- digit base+          accum $ n * fromIntegral base + fromIntegral d+{-# INLINE integer #-}++integerLabel :: Int -> String+integerLabel 2  = "binary integer"+integerLabel 8  = "octal integer"+integerLabel 10 = "decimal integer"+integerLabel 16 = "hexadecimal integer"+integerLabel b  = "integer of base " <> show b++decimal :: Num a => CharP k a+decimal = integer (pure ()) 10+{-# INLINE decimal #-}++octal :: Num a => CharP k a+octal = integer (pure ()) 8+{-# INLINE octal #-}++hexadecimal :: Num a => CharP k a+hexadecimal = integer (pure ()) 16+{-# INLINE hexadecimal #-}++-- | Parse a number with a plus or minus sign.+signed :: (Num a, CharParser k p) => p a -> p a+signed p = ($) <$> ((id <$ asciiByte asc_plus) <|> (negate <$ asciiByte asc_minus) <|> pure id) <*> p+{-# INLINE signed #-}++-- | Parse a fraction of arbitrary exponent base and coefficient base.+-- 'fractionDec' and 'fractionHex' should be used instead probably.+fraction :: (Num a, CharParser k p) => p expSep -> Int -> Int -> p digitSep -> p (a, Int, a)+fraction expSep expBase coeffBasePow digitSep = do+  let coeffBase = expBase ^ coeffBasePow+  coeff <- integer digitSep coeffBase+  void $ optional $ asciiByte asc_point+  (frac, fracLen) <- option (0, 0) $ integer' digitSep coeffBase+  expVal <- option 0 $ expSep *> signed (integer digitSep 10)+  pure (coeff * fromIntegral coeffBase ^ fracLen + frac,+        expBase,+        expVal - fromIntegral (fracLen * coeffBasePow))+{-# INLINE fraction #-}++-- | Parse a decimal fraction, returning (coefficient, 10, exponent),+-- corresponding to coefficient * 10^exponent.+-- Digits can be separated by separator, e.g. `optional (char '_')`.+fractionDec :: (Num a, CharParser k p) => p digitSep -> p (a, Int, a)+fractionDec sep = fraction (asciiSatisfy (\b -> b == asc_E || b == asc_e)) 10 1 sep <?> "fraction"+{-# INLINE fractionDec #-}++-- | Parse a hexadecimal fraction, returning (coefficient, 2, exponent),+-- corresponding to coefficient * 2^exponent.+-- Digits can be separated by separator, e.g. `optional (char '_')`.+fractionHex :: (Num a, CharParser k p) => p digitSep -> p (a, Int, a)+fractionHex sep = fraction (asciiSatisfy (\b -> b == asc_P || b == asc_p)) 2 4 sep <?> "hexadecimal fraction"+{-# INLINE fractionHex #-}++-- | Parse a case-insensitive character+char' :: CharParser k p => Char -> p Char+char' x =+  let l = C.toLower x+      u = C.toUpper x+  in satisfy (\c -> c == l || c == u)+{-# INLINE char' #-}++-- | Parse a character different from the given one.+notChar :: CharParser k p => Char -> p Char+notChar c = satisfy (/= c)+{-# INLINE notChar #-}++-- | Parse an arbitrary character.+anyChar :: CharP k Char+anyChar = satisfy (const True)+{-# INLINE anyChar #-}++-- | Parse an alphanumeric character, including Unicode.+alphaNumChar :: CharP k Char+alphaNumChar = satisfy C.isAlphaNum <?> "alphanumeric character"+{-# INLINE alphaNumChar #-}++-- | Parse a letter character, including Unicode.+letterChar :: CharP k Char+letterChar = satisfy C.isLetter <?> "letter"+{-# INLINE letterChar #-}++-- | Parse a lowercase letter, including Unicode.+lowerChar :: CharP k Char+lowerChar = satisfy C.isLower <?> "lowercase letter"+{-# INLINE lowerChar #-}++-- | Parse a uppercase letter, including Unicode.+upperChar :: CharP k Char+upperChar = satisfy C.isUpper <?> "uppercase letter"+{-# INLINE upperChar #-}++-- | Parse a space character, including Unicode.+spaceChar :: CharP k Char+spaceChar = satisfy C.isSpace <?> "space"+{-# INLINE spaceChar #-}++-- | Parse a symbol character, including Unicode.+symbolChar :: CharP k Char+symbolChar = satisfy C.isSymbol <?> "symbol"+{-# INLINE symbolChar #-}++-- | Parse a punctuation character, including Unicode.+punctuationChar :: CharP k Char+punctuationChar = satisfy C.isPunctuation <?> "punctuation"+{-# INLINE punctuationChar #-}++-- | Parse a digit character of the given base.+-- Bases 2 to 36 are supported.+digitChar :: CharParser k p => Int -> p Char+digitChar base = unsafeAsciiToChar <$> digitByte base+{-# INLINE digitChar #-}++-- | Parse a character beloning to the ASCII charset (< 128)+asciiChar :: CharP k Char+asciiChar = unsafeAsciiToChar <$> asciiSatisfy (const True)+{-# INLINE asciiChar #-}++-- | Parse a character belonging to the given Unicode category+categoryChar :: CharParser k p => C.GeneralCategory -> p Char+categoryChar cat = satisfy ((== cat) . C.generalCategory) <?> untitle (show cat)+{-# INLINE categoryChar #-}++untitle :: String -> String+untitle []     = []+untitle (x:xs) = C.toLower x : go xs+  where go [] = ""+        go (y:ys) | C.isUpper y = ' ' : C.toLower y : untitle ys+                  | otherwise   = y : ys++-- | Skip the next n characters+skipChars :: CharParser k p => Int -> p ()+skipChars n = skipCount n anyChar+{-# INLINE skipChars #-}++-- | Skip char while predicate is true+skipCharsWhile :: CharParser k p => (Char -> Bool) -> p ()+skipCharsWhile f = skipMany (satisfy f)+{-# INLINE skipCharsWhile #-}++-- | Skip at least one char while predicate is true+skipCharsWhile1 :: CharParser k p => (Char -> Bool) -> p ()+skipCharsWhile1 f = satisfy f *> skipCharsWhile f+{-# INLINE skipCharsWhile1 #-}++-- | Take the next n characters and advance the position by n characters+takeChars :: CharParser k p => Int -> p k+takeChars n = asChunk (skipChars n) <?> "string of length " <> show n+{-# INLINE takeChars #-}++-- | Take chars while predicate is true+takeCharsWhile :: CharParser k p => (Char -> Bool) -> p k+takeCharsWhile f = asChunk (skipCharsWhile f)+{-# INLINE takeCharsWhile #-}++-- | Take at least one byte while predicate is true+takeCharsWhile1 :: CharParser k p => (Char -> Bool) -> p k+takeCharsWhile1 f = asChunk (skipCharsWhile1 f)+{-# INLINE takeCharsWhile1 #-}++-- | Parse a string+string :: CharParser k p => Text -> p Text+string t = t <$ chunk (textToChunk t)+{-# INLINE string #-}
+ src/Text/PariPari/Internal/Chunk.hs view
@@ -0,0 +1,280 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+module Text.PariPari.Internal.Chunk (+  Chunk(..)+  , CharChunk(..)+  , Pos(..)+  , showByte+  , showByteString+  , unsafeAsciiToChar+  , asc_0, asc_9, asc_A, asc_E, asc_P, asc_a, asc_e, asc_p,+    asc_minus, asc_plus, asc_point, asc_newline+) where++import Data.Bits (unsafeShiftL, (.|.), (.&.))+import Data.ByteString (ByteString)+import Data.Foldable (foldl')+import Data.String (fromString)+import Data.Text (Text)+import Data.Word (Word8)+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)+import Foreign.Ptr (plusPtr)+import Foreign.Storable (peekByteOff)+import GHC.Base (unsafeChr)+import GHC.Generics (Generic)+import GHC.Show (showLitChar)+import Numeric (showHex)+import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as B+import qualified Data.Text.Array as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Internal as T++-- | Line and column position starting at (1,1)+data Pos = Pos+  { _posLine   :: !Int+  , _posColumn :: !Int+  } deriving (Eq, Show, Generic)++class (Ord (Element k), Ord k) => Chunk k where+  type Element  k+  type Buffer   k+  elementAt :: Buffer k -> Int -> (Element k, Int)+  elementPos :: Element k -> Pos -> Pos+  chunkWidth :: k -> Int+  chunkEqual :: Buffer k -> Int -> k -> Bool+  packChunk :: Buffer k -> Int -> Int -> k+  unpackChunk :: k -> (Buffer k, Int, Int)+  showElement :: Element k -> String+  showChunk :: k -> String++class Chunk k => CharChunk k where+  byteAt :: Buffer k -> Int -> Word8+  charAt :: Buffer k -> Int -> (Char, Int)+  charAtFixed :: Int -> Buffer k -> Int -> Char+  charWidth :: Char -> Int+  textToChunk :: Text -> k++instance Chunk ByteString where+  type Element  ByteString = Word8+  type Buffer   ByteString = ForeignPtr Word8++  elementAt b i = (ptrByteAt b i, 1)+  {-# INLINE elementAt #-}++  elementPos e (Pos l c) =+    Pos (if e == asc_newline then l + 1 else l)+        (if e == asc_newline then 1 else c + 1)+  {-# INLINE elementPos #-}++  chunkWidth (B.PS _ _ n) = n+  {-# INLINE chunkWidth #-}++  chunkEqual b i (B.PS p j n) = ptrBytesEqual b i p j n+  {-# INLINE chunkEqual #-}++  packChunk b i n = B.PS b i n+  {-# INLINE packChunk #-}++  unpackChunk k =+    let (B.PS b i n) = k <> fromString "\0\0\0" -- sentinel+    in (b, i, n - 3)+  {-# INLINE unpackChunk #-}++  showElement = showByte+  showChunk = showByteString++instance CharChunk ByteString where+  byteAt = ptrByteAt+  {-# INLINE byteAt #-}++  charAt = ptrDecodeUtf8+  {-# INLINE charAt #-}++  charWidth = charWidthUtf8+  {-# INLINE charWidth #-}++  charAtFixed = ptrDecodeFixedUtf8+  {-# INLINE charAtFixed #-}++  textToChunk t = T.encodeUtf8 t+  {-# INLINE textToChunk #-}++instance Chunk Text where+  type Element  Text = Char+  type Buffer   Text = T.Array++  elementAt b i = arrayCharAt 2 b i+  {-# INLINE elementAt #-}++  elementPos e (Pos l c) =+    Pos (if e == '\n' then l + 1 else l)+        (if e == '\n' then 1 else c + 1)+  {-# INLINE elementPos #-}++  chunkWidth (T.Text _ _ n) = n+  {-# INLINE chunkWidth #-}++  chunkEqual b i (T.Text a j n) = T.equal b i a j n+  {-# INLINE chunkEqual #-}++  packChunk b i n = T.Text b i n+  {-# INLINE packChunk #-}++  unpackChunk k =+    let (T.Text b i n) = k <> fromString "\0" -- sentinel+    in (b, i, n - 1)+  {-# INLINE unpackChunk #-}++  showElement = show+  showChunk = show++instance CharChunk Text where+  byteAt = arrayByteAt+  {-# INLINE byteAt #-}++  charAt = arrayCharAt 2+  {-# INLINE charAt #-}++  charWidth = charWidthUtf16+  {-# INLINE charWidth #-}++  charAtFixed n b i = fst $ arrayCharAt n b i+  {-# INLINE charAtFixed #-}++  textToChunk t = t+  {-# INLINE textToChunk #-}++arrayByteAt :: T.Array -> Int -> Word8+arrayByteAt a i+  | c <- T.unsafeIndex a i, c <= 0xFF = fromIntegral c+  | otherwise = 0+{-# INLINE arrayByteAt #-}++arrayCharAt :: Int -> T.Array -> Int -> (Char, Int)+arrayCharAt 1 a i+  | c <- T.unsafeIndex a i, c < 0xD800 || c > 0xDFFF = (unsafeChr $ fromIntegral c, 1)+  | otherwise = ('\0', 0)+arrayCharAt _ a i+  | hi <- T.unsafeIndex a i, lo <- T.unsafeIndex a (i + 1) =+      if hi < 0xD800 || hi > 0xDFFF then+        (unsafeChr $ fromIntegral hi, 1)+      else+        (unsafeChr $ 0x10000 + ((fromIntegral $ hi - 0xD800) `unsafeShiftL` 10) + (fromIntegral lo - 0xDC00), 2)+{-# INLINE arrayCharAt #-}++ptrBytesEqual :: ForeignPtr Word8 -> Int -> ForeignPtr Word8 -> Int -> Int -> Bool+ptrBytesEqual p1 i1 p2 i2 n =+  B.accursedUnutterablePerformIO $+  withForeignPtr p1 $ \q1 ->+  withForeignPtr p2 $ \q2 ->+  (== 0) <$> B.memcmp (q1 `plusPtr` i1) (q2 `plusPtr` i2) n+{-# INLINE ptrBytesEqual #-}++ptrByteAt :: ForeignPtr Word8 -> Int -> Word8+ptrByteAt p i = B.accursedUnutterablePerformIO $+  withForeignPtr p $ \q -> peekByteOff q i+{-# INLINE ptrByteAt #-}++at :: ForeignPtr Word8 -> Int -> Int+at p i = fromIntegral $ ptrByteAt p i+{-# INLINE at #-}++-- | Decode UTF-8 character at the given offset relative to the pointer+ptrDecodeUtf8 :: ForeignPtr Word8 -> Int -> (Char, Int)+ptrDecodeUtf8 p i+  | a1 <- at p i,+    a1 <= 0x7F =+    (unsafeChr a1, 1)+  | a1 <- at p i, a2 <- at p (i + 1),+    (a1 .&. 0xE0) == 0xC0,+    (a2 .&. 0xC0) == 0x80 =+    (unsafeChr (((a1 .&. 31) `unsafeShiftL` 6)+                .|. (a2 .&. 0x3F)), 2)+  | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2),+    (a1 .&. 0xF0) == 0xE0,+    (a2 .&. 0xC0) == 0x80,+    (a3 .&. 0xC0) == 0x80 =+    (unsafeChr (((a1 .&. 15) `unsafeShiftL` 12)+                 .|. ((a2 .&. 0x3F) `unsafeShiftL` 6)+                 .|. (a3 .&. 0x3F)), 3)+  | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2), a4 <- at p (i + 3),+    (a1 .&. 0xF8) == 0xF0,+    (a2 .&. 0xC0) == 0x80,+    (a3 .&. 0xC0) == 0x80,+    (a4 .&. 0xC0) == 0x80 =+    (unsafeChr (((a1 .&. 7) `unsafeShiftL` 18)+                 .|. ((a2 .&. 0x3F) `unsafeShiftL` 12)+                 .|. ((a3 .&. 0x3F) `unsafeShiftL` 6)+                 .|. (a4 .&. 0x3F)), 4)+  | otherwise = ('\0', 0)+{-# INLINE ptrDecodeUtf8 #-}++-- | Decode UTF-8 character with fixed width at the given offset relative to the pointer+ptrDecodeFixedUtf8 :: Int -> ForeignPtr Word8 -> Int -> Char+ptrDecodeFixedUtf8 w p i = unsafeChr $+  case w of+    1 -> at p i+    2 | a1 <- at p i, a2 <- at p (i + 1) ->+        ((a1 .&. 31) `unsafeShiftL` 6)+        .|. (a2 .&. 0x3F)+    3 | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2) ->+        ((a1 .&. 15) `unsafeShiftL` 12)+        .|. ((a2 .&. 0x3F) `unsafeShiftL` 6)+        .|. (a3 .&. 0x3F)+    4 | a1 <- at p i, a2 <- at p (i + 1), a3 <- at p (i + 2), a4 <- at p (i + 3) ->+        ((a1 .&. 7) `unsafeShiftL` 18)+        .|. ((a2 .&. 0x3F) `unsafeShiftL` 12)+        .|. ((a3 .&. 0x3F) `unsafeShiftL` 6)+        .|. (a4 .&. 0x3F)+    _ -> 0+{-# INLINE ptrDecodeFixedUtf8 #-}++charWidthUtf16 :: Char -> Int+charWidthUtf16 c | c <= unsafeChr 0xFFFF = 1+                 | otherwise = 2+{-# INLINE charWidthUtf16 #-}++-- | Bytes width of an UTF-8 character+charWidthUtf8 :: Char -> Int+charWidthUtf8 c | c <= unsafeChr 0x7F = 1+                | c <= unsafeChr 0x7FF = 2+                | c <= unsafeChr 0xFFFF = 3+                | otherwise = 4+{-# INLINE charWidthUtf8 #-}++asc_0, asc_9, asc_A, asc_E, asc_P, asc_a, asc_e, asc_p,+  asc_minus, asc_plus, asc_point, asc_newline :: Word8+asc_0 = 48+asc_9 = 57+asc_A = 65+asc_E = 69+asc_P = 80+asc_a = 97+asc_e = 101+asc_p = 112+asc_minus = 45+asc_plus = 43+asc_point = 46+asc_newline = 10++unsafeAsciiToChar :: Word8 -> Char+unsafeAsciiToChar = unsafeChr . fromIntegral+{-# INLINE unsafeAsciiToChar #-}++byteS :: Word8 -> ShowS+byteS b+  | b < 128 = showLitChar $ unsafeAsciiToChar b+  | otherwise = ("\\x" <>) . showHex b++bytesS :: ByteString -> ShowS+bytesS b | B.length b == 1 = byteS $ B.head b+         | otherwise = foldl' ((. byteS) . (.)) id $ B.unpack b++showByte :: Word8 -> String+showByte b = ('\'':) . byteS b . ('\'':) $ ""++showByteString :: ByteString -> String+showByteString b = ('"':) . bytesS b . ('"':) $ ""
+ src/Text/PariPari/Internal/Class.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FunctionalDependencies #-}+module Text.PariPari.Internal.Class (+  ChunkParser(..)+  , CharParser(..)+  , Alternative(..)+  , MonadPlus+  , Pos(..)+  , Error(..)+  , showError+  , expectedEnd+  , unexpectedEnd+) where++import Control.Applicative (Alternative(empty, (<|>)))+import Control.Monad (MonadPlus(..))+import Control.Monad.Fail (MonadFail(..))+import Data.List (intercalate)+import Data.Word (Word8)+import GHC.Generics (Generic)+import Text.PariPari.Internal.Chunk++-- | Parsing errors+data Error+  = EInvalidUtf8+  | EExpected         [String]+  | EUnexpected       String+  | EFail             String+  | ECombinator       String+  | EIndentNotAligned !Int !Int+  | EIndentOverLine   !Int !Int+  | ENotEnoughIndent  !Int !Int+  deriving (Eq, Ord, Show, Generic)++-- | Parser class, which specifies the necessary+-- primitives for parsing. All other parser combinators+-- rely on these primitives.+class (MonadFail p, MonadPlus p, Chunk k) => ChunkParser k p | p -> k where+  -- | Get file name associated with current parser+  getFile :: p FilePath++  -- | Get current position of the parser+  getPos :: p Pos++  -- | Get reference position used for indentation-sensitive parsing+  getRefPos :: p Pos++  -- | Update reference position with current position+  withRefPos :: p a -> p a++  -- | Parser which succeeds when the given parser fails+  notFollowedBy :: Show a => p a -> p ()++  -- | Look ahead and return result of the given parser+  -- The current position stays the same.+  lookAhead :: p a -> p a++  -- | Parser failure with detailled 'Error'+  failWith :: Error -> p a++  -- | Parser which succeeds at the end of file+  eof :: p ()++  -- | Annotate the given parser with a label+  -- used for error reporting+  label :: String -> p a -> p a++  -- | Hide errors occurring within the given parser+  -- from the error report. Based on the given+  -- labels an 'Error' is constructed instead.+  hidden :: p a -> p a++  -- | Commit to the given branch, increasing+  -- the priority of the errors within this branch+  -- in contrast to other branches.+  --+  -- This is basically the opposite of the `try`+  -- combinator provided by other parser combinator+  -- libraries, which decreases the error priority+  -- within the given branch (and usually also influences backtracking).+  --+  -- __Note__: `commit` only applies to the reported+  -- errors, it has no effect on the backtracking behavior+  -- of the parser.+  commit :: p a -> p a++  -- | Parse a single element+  element :: Element k -> p (Element k)++  -- | Parse a single byte with the given predicate+  elementSatisfy :: (Element k -> Bool) -> p (Element k)++  -- | Parse a chunk of elements. The chunk must not+  -- contain multiple lines, otherwise the position information+  -- will be invalid.+  chunk :: k -> p k++  -- | Run the given parser and return the+  -- result as buffer+  asChunk :: p () -> p k++class (ChunkParser k p, CharChunk k) => CharParser k p | p -> k where+  -- | Parse a single character+  --+  -- __Note__: The character '\0' cannot be parsed using this combinator+  -- since it is used as decoding sentinel. Use 'element' instead.+  char :: Char -> p Char++  -- | Parse a single character with the given predicate+  --+  -- __Note__: The character '\0' cannot be parsed using this combinator+  -- since it is used as decoding sentinel. Use 'elementSatisfy' instead.+  satisfy :: (Char -> Bool) -> p Char++  -- | Parse a single character within the ASCII charset+  --+  -- __Note__: The character '\0' cannot be parsed using this combinator+  -- since it is used as decoding sentinel. Use 'element' instead.+  asciiByte :: Word8 -> p Word8++  -- | Parse a single character within the ASCII charset with the given predicate+  --+  -- __Note__: The character '\0' cannot be parsed using this combinator+  -- since it is used as decoding sentinel. Use 'elementSatisfy' instead.+  asciiSatisfy :: (Word8 -> Bool) -> p Word8++-- | Pretty string representation of 'Error'+showError :: Error -> String+showError EInvalidUtf8             = "Invalid UTF-8 character found"+showError (EExpected tokens)       = "Expected " <> intercalate ", " tokens+showError (EUnexpected token)      = "Unexpected " <> token+showError (EFail msg)              = msg+showError (ECombinator name)       = "Combinator " <> name <> " failed"+showError (EIndentNotAligned rc c) = "Invalid alignment, expected column " <> show rc <> " expected, got " <> show c+showError (EIndentOverLine   rl l) = "Indentation over line, expected line " <> show rl <> ", got " <> show l+showError (ENotEnoughIndent  rc c) = "Not enough indentation, expected column " <> show rc <> ", got " <> show c++expectedEnd :: Error+expectedEnd = EExpected ["end of file"]++unexpectedEnd :: Error+unexpectedEnd = EUnexpected "end of file"
+ src/Text/PariPari/Internal/ElementCombinators.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+module Text.PariPari.Internal.ElementCombinators (+  -- * Basic combinators+  void+  , (<|>)+  , empty++  -- * Control.Monad.Combinators.NonEmpty+  , ON.some+  , ON.endBy1+  , ON.someTill+  , ON.sepBy1+  , ON.sepEndBy1++  -- * Control.Monad.Combinators+  , O.optional -- dont use Applicative version for efficiency+  , O.many -- dont use Applicative version for efficiency+  , O.between+  , O.choice+  , O.count+  , O.count'+  , O.eitherP+  , O.endBy+  , O.manyTill+  , O.option+  , O.sepBy+  , O.sepEndBy+  , O.skipMany+  , O.skipSome+  , O.skipCount+  , O.skipManyTill+  , O.skipSomeTill++  -- * PariPari+  , (<?>)+  , getLine+  , getColumn+  , withPos+  , withSpan+  , getRefColumn+  , getRefLine+  , withRefPos+  , align+  , indented+  , line+  , linefold+  , notElement+  , anyElement+  , takeElements+  , skipElements+  , skipElementsWhile+  , takeElementsWhile+  , skipElementsWhile1+  , takeElementsWhile1+) where++import Control.Applicative ((<|>), empty)+import Control.Monad (when)+import Control.Monad.Combinators (skipCount, skipMany)+import Data.Functor (void)+import Prelude hiding (getLine)+import Text.PariPari.Internal.Class+import Text.PariPari.Internal.Chunk+import qualified Control.Monad.Combinators as O+import qualified Control.Monad.Combinators.NonEmpty as ON++type ChunkP k a = (forall p. ChunkParser k p => p a)++-- | Infix alias for 'label'+(<?>) :: ChunkParser k p => p a -> String -> p a+(<?>) = flip label+{-# INLINE (<?>) #-}+infix 0 <?>++-- | Get line number of the reference position+getRefLine :: ChunkP k Int+getRefLine = _posLine <$> getRefPos+{-# INLINE getRefLine #-}++-- | Get column number of the reference position+getRefColumn :: ChunkP k Int+getRefColumn = _posColumn <$> getRefPos+{-# INLINE getRefColumn #-}++-- | Get current line number+getLine :: ChunkP k Int+getLine = _posLine <$> getPos+{-# INLINE getLine #-}++-- | Get current column+getColumn :: ChunkP k Int+getColumn = _posColumn <$> getPos+{-# INLINE getColumn #-}++-- | Decorate the parser result with the current position+withPos :: ChunkParser k p => p a -> p (Pos, a)+withPos p = do+  pos <- getPos+  ret <- p+  pure (pos, ret)+{-# INLINE withPos #-}++type Span = (Pos, Pos)++-- | Decoreate the parser result with the position span+withSpan :: ChunkParser k p => p a -> p (Span, a)+withSpan p = do+  begin <- getPos+  ret <- p+  end <- getPos+  pure ((begin, end), ret)+{-# INLINE withSpan #-}++-- | Parser succeeds on the same line as the reference line+line :: ChunkP k ()+line = do+  l <- getLine+  rl <- getRefLine+  when (l /= rl) $ failWith $ EIndentOverLine rl l+{-# INLINE line #-}++-- | Parser succeeds on the same column as the reference column+align :: ChunkP k ()+align = do+  c <- getColumn+  rc <- getRefColumn+  when (c /= rc) $ failWith $ EIndentNotAligned rc c+{-# INLINE align #-}++-- | Parser succeeds for columns greater than the current reference column+indented :: ChunkP k ()+indented = do+  c <- getColumn+  rc <- getRefColumn+  when (c <= rc) $ failWith $ ENotEnoughIndent rc c+{-# INLINE indented #-}++-- | Parser succeeds either on the reference line or+-- for columns greater than the current reference column+linefold :: ChunkP k ()+linefold = line <|> indented+{-# INLINE linefold #-}++-- | Parser a single byte different from the given one+notElement :: forall k p. ChunkParser k p => Element k -> p (Element k)+notElement e = elementSatisfy @k (/= e) <?> "not " <> showElement @k e+{-# INLINE notElement #-}++-- | Parse an arbitrary byte+anyElement :: ChunkP k (Element k)+anyElement = elementSatisfy (const True)+{-# INLINE anyElement #-}++-- | Skip the next n elements+skipElements :: ChunkParser k p => Int -> p ()+skipElements n = skipCount n anyElement+{-# INLINE skipElements #-}++-- | Take the next n elements and advance the position by n+takeElements :: ChunkParser k p => Int -> p k+takeElements n = asChunk (skipElements n) <?> show n <> " elements"+{-# INLINE takeElements #-}++-- | Skip elements while predicate is true+skipElementsWhile :: ChunkParser k p => (Element k -> Bool) -> p ()+skipElementsWhile f = skipMany (elementSatisfy f)+{-# INLINE skipElementsWhile #-}++-- | Takes elements while predicate is true+takeElementsWhile :: ChunkParser k p => (Element k -> Bool) -> p k+takeElementsWhile f = asChunk (skipElementsWhile f)+{-# INLINE takeElementsWhile #-}++-- | Skip at least one element while predicate is true+skipElementsWhile1 :: ChunkParser k p => (Element k -> Bool) -> p ()+skipElementsWhile1 f = elementSatisfy f *> skipElementsWhile f+{-# INLINE skipElementsWhile1 #-}++-- | Take at least one element while predicate is true+takeElementsWhile1 :: ChunkParser k p => (Element k -> Bool) -> p k+takeElementsWhile1 f = asChunk (skipElementsWhile1 f)+{-# INLINE takeElementsWhile1 #-}
+ src/Text/PariPari/Internal/Reporter.hs view
@@ -0,0 +1,447 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+module Text.PariPari.Internal.Reporter (+  Reporter(..)+  , Env(..)+  , State(..)+  , local+  , get+  , raiseError+  , mergeErrorState+  , Report(..)+  , runReporter+  , runReporterWithOptions+  , ErrorContext(..)+  , showReport+  , showErrors+  , ReportOptions(..)+  , defaultReportOptions+) where++import Control.Monad (void)+import Data.Function (on)+import Data.List (intercalate, sort, group, sortOn)+import Data.List.NonEmpty (NonEmpty(..))+import GHC.Generics (Generic)+import Text.PariPari.Internal.Chunk+import Text.PariPari.Internal.Class+import qualified Control.Monad.Fail as Fail+import qualified Data.List.NonEmpty as NE++data ErrorContext = ErrorContext+  { _ecErrors  :: [Error]+  , _ecContext :: [String]+  } deriving (Eq, Show, Generic)++data ReportOptions = ReportOptions+  { _optMaxContexts         :: !Int+  , _optMaxErrorsPerContext :: !Int+  , _optMaxLabelsPerContext :: !Int+  } deriving (Eq, Show, Generic)++data Report = Report+  { _reportFile   :: !FilePath+  , _reportLine   :: !Int+  , _reportCol    :: !Int+  , _reportErrors :: [ErrorContext]+  } deriving (Eq, Show, Generic)++data Env k = Env+  { _envBuf     :: !(Buffer k)+  , _envEnd     :: !Int+  , _envFile    :: !FilePath+  , _envOptions :: !ReportOptions+  , _envHidden  :: !Bool+  , _envCommit  :: !Int+  , _envContext :: [String]+  , _envRefLine :: !Int+  , _envRefCol  :: !Int+  }++data State = State+  { _stOff       :: !Int+  , _stLine      :: !Int+  , _stCol       :: !Int+  , _stErrOff    :: !Int+  , _stErrLine   :: !Int+  , _stErrCol    :: !Int+  , _stErrCommit :: !Int+  , _stErrors    :: [ErrorContext]+  }++-- | Parser which is optimised for good error reports.+-- Performance is secondary, since the 'Reporter' is used+-- as a fallback to the 'Acceptor'.+newtype Reporter k a = Reporter+  { unReporter :: forall b. Env k -> State+               -> (a     -> State -> b)+               -> (State -> b)+               -> b+  }++instance (Chunk k, Semigroup a) => Semigroup (Reporter k a) where+  p1 <> p2 = (<>) <$> p1 <*> p2+  {-# INLINE (<>) #-}++instance (Chunk k, Monoid a) => Monoid (Reporter k a) where+  mempty = pure mempty+  {-# INLINE mempty #-}++instance Chunk k => Functor (Reporter k) where+  fmap f p = Reporter $ \env st ok err ->+    unReporter p env st (ok . f) err+  {-# INLINE fmap #-}++instance Chunk k => Applicative (Reporter k) where+  pure x = Reporter $ \_ st ok _ -> ok x st+  {-# INLINE pure #-}++  f <*> a = Reporter $ \env st ok err ->+    let ok1 f' s =+          let ok2 a' s' = ok (f' a') s'+          in unReporter a env s ok2 err+    in unReporter f env st ok1 err+  {-# INLINE (<*>) #-}++  p1 *> p2 = do+    void p1+    p2+  {-# INLINE (*>) #-}++  p1 <* p2 = do+    x <- p1+    void p2+    pure x+  {-# INLINE (<*) #-}++instance Chunk k => Alternative (Reporter k) where+  empty = Reporter $ \_ st _ err -> err st+  {-# INLINE empty #-}++  p1 <|> p2 = Reporter $ \env st ok err ->+    let err' s = unReporter p2 env (mergeErrorState env st s) ok err+    in unReporter p1 env st ok err'+  {-# INLINE (<|>) #-}++instance Chunk k => MonadPlus (Reporter k)++instance Chunk k => Monad (Reporter k) where+  p >>= f = Reporter $ \env st ok err ->+    let ok' x s = unReporter (f x) env s ok err+    in unReporter p env st ok' err+  {-# INLINE (>>=) #-}++  fail msg = Fail.fail msg+  {-# INLINE fail #-}++instance Chunk k => Fail.MonadFail (Reporter k) where+  fail msg = failWith $ EFail msg+  {-# INLINE fail #-}++instance Chunk k => ChunkParser k (Reporter k) where+  getPos = get $ \_ st -> Pos (_stLine st) (_stCol st)+  {-# INLINE getPos #-}++  getFile = get $ \env _ -> _envFile env+  {-# INLINE getFile #-}++  getRefPos = get $ \env _ -> Pos (_envRefLine env) (_envRefCol env)+  {-# INLINE getRefPos #-}++  withRefPos p = local (\st env -> env { _envRefLine = _stLine st, _envRefCol = _stCol st }) p+  {-# INLINE withRefPos #-}++  label l p = local (const $ addLabel l) p+  {-# INLINE label #-}++  hidden p = local (const $ \env -> env { _envHidden = True }) p+  {-# INLINE hidden #-}++  commit p = local (const $ \env -> env { _envCommit = _envCommit env + 1 }) p+  {-# INLINE commit #-}++  notFollowedBy p = Reporter $ \env st ok err ->+    let ok' x _ = raiseError env st err $ EUnexpected $ show x+        err' _ = ok () st+    in unReporter p env st ok' err'+  {-# INLINE notFollowedBy #-}++  lookAhead p = Reporter $ \env st ok err ->+    let ok' x _ = ok x st+    in unReporter p env st ok' err+  {-# INLINE lookAhead #-}++  failWith e = Reporter $ \env st _ err -> raiseError env st err e+  {-# INLINE failWith #-}++  eof = Reporter $ \env st ok err ->+    if _stOff st >= _envEnd env then+      ok () st+    else+      raiseError env st err expectedEnd+  {-# INLINE eof #-}++  element e = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    if | _stOff < _envEnd env,+         (e', w) <- elementAt @k (_envBuf env) _stOff,+         e == e',+         pos <- elementPos @k e (Pos _stLine _stCol) ->+           ok e st { _stOff =_stOff + w, _stLine = _posLine pos, _stCol = _posColumn pos }+       | otherwise ->+           raiseError env st err $ EExpected [showElement @k e]+  {-# INLINE element #-}++  elementSatisfy f = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    let (e, w) = elementAt @k (_envBuf env) _stOff+    in if | _stOff >= _envEnd env ->+              raiseError env st err unexpectedEnd+          | _stOff < _envEnd env,+            f e,+            pos <- elementPos @k e (Pos _stLine _stCol) ->+              ok e st { _stOff =_stOff + w, _stLine = _posLine pos, _stCol = _posColumn pos }+          | otherwise ->+              raiseError env st err $ EUnexpected $ showElement @k e+  {-# INLINE elementSatisfy #-}++  chunk k = Reporter $ \env st@State{_stOff,_stCol} ok err ->+    let n = chunkWidth @k k+    in if n + _stOff <= _envEnd env &&+          chunkEqual @k (_envBuf env) _stOff k then+         ok k st { _stOff = _stOff + n, _stCol = _stCol + n }+       else+         raiseError env st err $ EExpected [showChunk @k k]+  {-# INLINE chunk #-}++  asChunk p = do+    begin <- get (const _stOff)+    p+    end <- get (const _stOff)+    src <- get (\env _ -> _envBuf env)+    pure $ packChunk src begin (end - begin)+  {-# INLINE asChunk #-}++instance CharChunk k => CharParser k (Reporter k) where+  satisfy f = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    if | (c, w) <- charAt @k (_envBuf env) _stOff,+         c /= '\0' ->+           if f c then+             ok c st+             { _stOff = _stOff + w+             , _stLine = if c == '\n' then _stLine + 1 else _stLine+             , _stCol = if c == '\n' then 1 else _stCol + 1+             }+           else+             raiseError env st err $ EUnexpected $ show c+       | _stOff >= _envEnd env ->+           raiseError env st err unexpectedEnd+       | otherwise ->+           raiseError env st err EInvalidUtf8+  {-# INLINE satisfy #-}++  -- By inling this combinator, GHC should figure out the `charWidth`+  -- of the character resulting in an optimised decoder.+  char '\0' = error "Character '\\0' cannot be parsed because it is used as sentinel"+  char c+    | w <- charWidth @k c =+        Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->+        if charAtFixed @k w (_envBuf env) _stOff == c then+          ok c st+          { _stOff = _stOff + w+          , _stLine = if c == '\n' then _stLine + 1 else _stLine+          , _stCol = if c == '\n' then 1 else _stCol + 1+          }+        else+          raiseError env st err $ EExpected [show c]+  {-# INLINE char #-}++  asciiSatisfy f = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->+    let b = byteAt @k (_envBuf env) _stOff+    in if | b /= 0,+            b < 128,+            f b ->+              ok b st+              { _stOff = _stOff + 1+              , _stLine = if b == asc_newline then _stLine + 1 else _stLine+              , _stCol = if b == asc_newline then 1 else _stCol + 1+              }+          | _stOff >= _envEnd env ->+              raiseError env st err unexpectedEnd+          | otherwise ->+              raiseError env st err $ EUnexpected $ showByte b+  {-# INLINE asciiSatisfy #-}++  asciiByte 0 = error "Character '\\0' cannot be parsed because it is used as sentinel"+  asciiByte b+    | b >= 128 = error "Not an ASCII character"+    | otherwise = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->+        if byteAt @k (_envBuf env) _stOff == b then+          ok b st+          { _stOff = _stOff + 1+          , _stLine = if b == asc_newline then _stLine + 1 else _stLine+          , _stCol = if b == asc_newline then 1 else _stCol + 1+          }+        else+          raiseError env st err $ EExpected [showByte b]+  {-# INLINE asciiByte #-}++raiseError :: Env k -> State -> (State -> b) -> Error -> b+raiseError env st err e = err $ addError env st e+{-# INLINE raiseError #-}++-- | Reader monad, modify environment locally+local :: (State -> Env k -> Env k) -> Reporter k a -> Reporter k a+local f p = Reporter $ \env st ok err ->+  unReporter p (f st env) st ok err+{-# INLINE local #-}++-- | Reader monad, get something from the environment+get :: (Env k -> State -> a) -> Reporter k a+get f = Reporter $ \env st ok _ -> ok (f env st) st+{-# INLINE get #-}++addLabel :: String -> Env k -> Env k+addLabel l env = case _envContext env of+  (l':_) | l == l' -> env+  ls               -> env { _envContext = take (_optMaxLabelsPerContext._envOptions $ env) $ l : ls }+{-# INLINE addLabel #-}++-- | Add parser error to the list of errors+-- which are kept in the parser state.+-- Errors of lower priority and at an earlier position.+-- Furthermore the error is merged with existing errors if possible.+addError :: Env k -> State -> Error -> State+addError env st e+  | _stOff st > _stErrOff st || _envCommit env > _stErrCommit st,+    Just e' <- mkError env e =+      st { _stErrors    = [e']+         , _stErrOff    = _stOff st+         , _stErrLine   = _stLine st+         , _stErrCol    = _stCol st+         , _stErrCommit = _envCommit env+         }+  | _stOff st == _stErrOff st && _envCommit env == _stErrCommit st,+    Just e' <- mkError env e =+      st { _stErrors = shrinkErrors env $ e' : _stErrors st }+  | otherwise = st+{-# INLINE addError #-}++mkError :: Env k -> Error -> Maybe ErrorContext+mkError env e+  | _envHidden env, (l:ls) <- _envContext env = Just $ ErrorContext [EExpected [l]] ls+  | _envHidden env = Nothing+  | otherwise = Just $ ErrorContext [e] $ _envContext env+{-# INLINE mkError #-}++-- | Merge errors of two states, used when backtracking+mergeErrorState :: Env k -> State -> State -> State+mergeErrorState env s s'+  | _stErrOff s' > _stErrOff s || _stErrCommit s' > _stErrCommit s =+      s { _stErrors    = _stErrors s'+        , _stErrOff    = _stErrOff s'+        , _stErrLine   = _stErrLine s'+        , _stErrCol    = _stErrCol s'+        , _stErrCommit = _stErrCommit s'+        }+  | _stErrOff s' == _stErrOff s && _stErrCommit s' == _stErrCommit s =+      s { _stErrors = shrinkErrors env $ _stErrors s' <> _stErrors s }+  | otherwise = s+{-# INLINE mergeErrorState #-}++groupOn :: Eq e => (a -> e) -> [a] -> [NonEmpty a]+groupOn f = NE.groupBy ((==) `on` f)++shrinkErrors :: Env k -> [ErrorContext] -> [ErrorContext]+shrinkErrors env = take (_optMaxContexts._envOptions $ env) . map (mergeErrorContexts env) . groupOn _ecContext . sortOn _ecContext++-- | Shrink error context by deleting duplicates+-- and merging errors if possible.+mergeErrorContexts :: Env k -> NonEmpty ErrorContext -> ErrorContext+mergeErrorContexts env es@(ErrorContext{_ecContext}:| _) = ErrorContext+  { _ecErrors = take (_optMaxErrorsPerContext._envOptions $ env) $ nubSort $ mergeEExpected $ concatMap _ecErrors $ NE.toList es+  , _ecContext = _ecContext+  }++mergeEExpected :: [Error] -> [Error]+mergeEExpected es = [EExpected $ nubSort expects | not (null expects)] <> filter (null . asEExpected) es+  where expects = concatMap asEExpected es++nubSort :: Ord a => [a] -> [a]+nubSort = map head . group . sort++asEExpected :: Error -> [String]+asEExpected (EExpected s) = s+asEExpected _ = []++defaultReportOptions :: ReportOptions+defaultReportOptions = ReportOptions+  { _optMaxContexts         = 20+  , _optMaxErrorsPerContext = 20+  , _optMaxLabelsPerContext = 5+  }++-- | Run 'Reporter' with additional 'ReportOptions'.+runReporterWithOptions :: Chunk k => ReportOptions -> Reporter k a -> FilePath -> k -> Either Report a+runReporterWithOptions o p f k =+  let (b, off, len) = unpackChunk k+  in unReporter p (initialEnv o f b (off + len)) (initialState off) (\x _ -> Right x) (Left . getReport f)++-- | Run 'Reporter' on the given 'ByteString', returning either+-- an error 'Report' or, if successful, the result.+runReporter :: Chunk k => Reporter k a -> FilePath -> k -> Either Report a+runReporter = runReporterWithOptions defaultReportOptions++getReport :: FilePath -> State -> Report+getReport f s = Report f (_stErrLine s) (_stErrCol s) (_stErrors s)++initialEnv :: ReportOptions -> FilePath -> Buffer k -> Int -> Env k+initialEnv _envOptions _envFile _envBuf _envEnd = Env+  { _envFile+  , _envBuf+  , _envOptions+  , _envEnd+  , _envContext = []+  , _envHidden  = False+  , _envCommit  = 0+  , _envRefLine = 1+  , _envRefCol  = 1+  }++initialState :: Int -> State+initialState _stOff = State+  { _stOff+  , _stLine      = 1+  , _stCol       = 1+  , _stErrOff    = 0+  , _stErrLine   = 0+  , _stErrCol    = 0+  , _stErrCommit = 0+  , _stErrors    = []+  }++-- | Pretty string representation of 'Report'.+showReport :: Report -> String+showReport r =+  "Parser errors at " <> _reportFile r+  <> ", line " <> show (_reportLine r)+  <> ", column " <> show (_reportCol r)+  <> "\n\n" <> showErrors (_reportErrors r)++-- | Pretty string representation of '[ErrorContext]'.+showErrors :: [ErrorContext] -> String+showErrors [] = "No errors"+showErrors es = intercalate "\n" $ map showErrorContext es++showErrorContext :: ErrorContext -> String+showErrorContext ec =+  intercalate ", " (map showError $ _ecErrors ec)+  <> showContext (_ecContext ec) <> "."++showContext :: [String] -> String+showContext [] = ""+showContext xs = " in context of " <> intercalate ", " xs
+ src/Text/PariPari/Internal/Run.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE Rank2Types #-}+module Text.PariPari.Internal.Run (+  runCharParser+  , runSeqCharParser+  , runCharParserWithOptions+  , runSeqCharParserWithOptions+  , runChunkParser+  , runSeqChunkParser+  , runChunkParserWithOptions+  , runSeqChunkParserWithOptions+) where++import Text.PariPari.Internal.Acceptor+import Text.PariPari.Internal.Class+import Text.PariPari.Internal.Chunk+import Text.PariPari.Internal.Reporter+import GHC.Conc (par)++-- | Run fast 'Acceptor' and slower 'Reporter' on the given 'ByteString' **in parallel**.+-- The 'FilePath' is used for error reporting.+-- When the acceptor does not return successfully, the result from the reporter+-- is awaited.+runCharParser :: CharChunk k => (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a+runCharParser = runCharParserWithOptions defaultReportOptions+{-# INLINE runCharParser #-}++-- | Run fast 'Acceptor' and slower 'Reporter' on the given 'ByteString' **sequentially**.+-- The 'FilePath' is used for error reporting.+-- When the acceptor does not return successfully, the result from the reporter+-- is awaited.+runSeqCharParser :: CharChunk k => (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a+runSeqCharParser = runSeqCharParserWithOptions defaultReportOptions+{-# INLINE runSeqCharParser #-}++-- | Run parsers **in parallel** with additional 'ReportOptions'.+runCharParserWithOptions :: CharChunk k => ReportOptions -> (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a+runCharParserWithOptions o p f b =+  let a = runAcceptor p f b+      r = runReporterWithOptions o p f b+  in case r `par` a of+       Left _  -> r+       Right x -> Right x+{-# INLINE runCharParserWithOptions #-}++-- | Run parsers **sequentially** with additional 'ReportOptions'.+runSeqCharParserWithOptions :: CharChunk k => ReportOptions -> (forall p. CharParser k p => p a) -> FilePath -> k -> Either Report a+runSeqCharParserWithOptions o p f b =+  let a = runAcceptor p f b+      r = runReporterWithOptions o p f b+  in case a of+       Left _  -> r+       Right x -> Right x+{-# INLINE runSeqCharParserWithOptions #-}++-- | Run fast 'Acceptor' and slower 'Reporter' on the given 'ByteString' **in parallel**.+-- The 'FilePath' is used for error reporting.+-- When the acceptor does not return successfully, the result from the reporter+-- is awaited.+runChunkParser :: CharChunk k => (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a+runChunkParser = runCharParserWithOptions defaultReportOptions+{-# INLINE runChunkParser #-}++-- | Run fast 'Acceptor' and slower 'Reporter' on the given 'ByteString' **sequentially**.+-- The 'FilePath' is used for error reporting.+-- When the acceptor does not return successfully, the result from the reporter+-- is awaited.+runSeqChunkParser :: Chunk k => (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a+runSeqChunkParser = runSeqChunkParserWithOptions defaultReportOptions+{-# INLINE runSeqChunkParser #-}++-- | Run parsers **in parallel** with additional 'ReportOptions'.+runChunkParserWithOptions :: Chunk k => ReportOptions -> (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a+runChunkParserWithOptions o p f b =+  let a = runAcceptor p f b+      r = runReporterWithOptions o p f b+  in case r `par` a of+       Left _  -> r+       Right x -> Right x+{-# INLINE runChunkParserWithOptions #-}++-- | Run parsers **sequentially** with additional 'ReportOptions'.+runSeqChunkParserWithOptions :: Chunk k => ReportOptions -> (forall p. ChunkParser k p => p a) -> FilePath -> k -> Either Report a+runSeqChunkParserWithOptions o p f b =+  let a = runAcceptor p f b+      r = runReporterWithOptions o p f b+  in case a of+       Left _  -> r+       Right x -> Right x+{-# INLINE runSeqChunkParserWithOptions #-}
+ src/Text/PariPari/Internal/Tracer.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+module Text.PariPari.Internal.Tracer (+  Tracer(..)+  , runTracer+) where++import Debug.Trace (trace)+import Text.PariPari.Internal.Class+import Text.PariPari.Internal.Chunk+import Text.PariPari.Internal.Reporter+import qualified Control.Monad.Fail as Fail++-- | Parser which prints trace messages, when backtracking occurs.+newtype Tracer k a = Tracer { unTracer :: Reporter k a }+  deriving (Semigroup, Monoid, Functor, Applicative, MonadPlus, Monad, Fail.MonadFail)++deriving instance CharChunk k => ChunkParser k (Tracer k)+deriving instance CharChunk k => CharParser k (Tracer k)++instance Chunk k => Alternative (Tracer k) where+  empty = Tracer empty++  p1 <|> p2 = Tracer $ Reporter $ \env st ok err ->+    let err' s =+          let width = _stOff s -_stOff st+              next  = unReporter (unTracer p2) env (mergeErrorState env st s) ok err+          in if width > 1 then+               trace ("Backtracking " <> show width <> " bytes at line " <> show (_stLine s)+                       <> ", column " <> show (_stCol s) <> ", context " <> show (_envContext env) <> ": "+                       <> showChunk (packChunk @k (_envBuf env) (_stOff st) width)) next+             else+               next+    in unReporter (unTracer p1) env st ok err'++-- | Run 'Tracer' on the given 'ByteString', returning either+-- an error 'Report' or, if successful, the result.+runTracer :: Chunk k => Tracer k a -> FilePath -> k -> Either Report a+runTracer = runReporter . unTracer
− src/Text/PariPari/Reporter.hs
@@ -1,421 +0,0 @@-module Text.PariPari.Reporter (-  Reporter-  , Report(..)-  , runReporter-  , runReporterWithOptions-  , ErrorContext-  , showReport-  , showErrors-  , ReportOptions(..)-  , defaultReportOptions-  , Tracer-  , runTracer-) where--import Control.Monad (void)-import Data.Function (on)-import Data.List (intercalate, sort, group, sortOn)-import Data.List.NonEmpty (NonEmpty(..))-import Debug.Trace (trace)-import Foreign.ForeignPtr (ForeignPtr)-import GHC.Generics (Generic)-import Text.PariPari.Ascii-import Text.PariPari.Class-import Text.PariPari.Decode-import qualified Control.Monad.Fail as Fail-import qualified Data.ByteString.Internal as B-import qualified Data.List.NonEmpty as NE--type ErrorContext = ([Error], [String])--data ReportOptions = ReportOptions-  { _optMaxContexts         :: !Int-  , _optMaxErrorsPerContext :: !Int-  , _optMaxLabelsPerContext :: !Int-  } deriving (Eq, Show, Generic)--data Report = Report-  { _reportFile   :: !FilePath-  , _reportLine   :: !Int-  , _reportCol    :: !Int-  , _reportErrors :: [ErrorContext]-  } deriving (Eq, Show, Generic)--data Env = Env-  { _envSrc     :: !(ForeignPtr Word8)-  , _envEnd     :: !Int-  , _envFile    :: !FilePath-  , _envOptions :: !ReportOptions-  , _envHidden  :: !Bool-  , _envCommit  :: !Int-  , _envContext :: [String]-  , _envRefLine :: !Int-  , _envRefCol  :: !Int-  }--data State = State-  { _stOff       :: !Int-  , _stLine      :: !Int-  , _stCol       :: !Int-  , _stErrOff    :: !Int-  , _stErrLine   :: !Int-  , _stErrCol    :: !Int-  , _stErrCommit :: !Int-  , _stErrors    :: [ErrorContext]-  }---- | Parser which is optimized for good error reports.--- Performance is secondary, since the 'Reporter' is used--- as a fallback to the 'Acceptor'.-newtype Reporter a = Reporter-  { unReporter :: forall b. Env -> State-               -> (a     -> State -> b)-               -> (State -> b)-               -> b-  }--instance Semigroup a => Semigroup (Reporter a) where-  p1 <> p2 = (<>) <$> p1 <*> p2-  {-# INLINE (<>) #-}--instance Monoid a => Monoid (Reporter a) where-  mempty = pure mempty-  {-# INLINE mempty #-}--instance Functor Reporter where-  fmap f p = Reporter $ \env st ok err ->-    unReporter p env st (ok . f) err-  {-# INLINE fmap #-}--instance Applicative Reporter where-  pure x = Reporter $ \_ st ok _ -> ok x st-  {-# INLINE pure #-}--  f <*> a = Reporter $ \env st ok err ->-    let ok1 f' s =-          let ok2 a' s' = ok (f' a') s'-          in unReporter a env s ok2 err-    in unReporter f env st ok1 err-  {-# INLINE (<*>) #-}--  p1 *> p2 = do-    void p1-    p2-  {-# INLINE (*>) #-}--  p1 <* p2 = do-    x <- p1-    void p2-    pure x-  {-# INLINE (<*) #-}--instance Alternative Reporter where-  empty = Reporter $ \_ st _ err -> err st-  {-# INLINE empty #-}--  p1 <|> p2 = Reporter $ \env st ok err ->-    let err' s = unReporter p2 env (mergeStateErrors env st s) ok err-    in unReporter p1 env st ok err'-  {-# INLINE (<|>) #-}--instance MonadPlus Reporter--instance Monad Reporter where-  p >>= f = Reporter $ \env st ok err ->-    let ok' x s = unReporter (f x) env s ok err-    in unReporter p env st ok' err-  {-# INLINE (>>=) #-}--  fail msg = Fail.fail msg-  {-# INLINE fail #-}--instance Fail.MonadFail Reporter where-  fail msg = failWith $ EFail msg-  {-# INLINE fail #-}--instance MonadParser Reporter where-  getPos = get $ \_ st -> Pos (_stLine st) (_stCol st)-  {-# INLINE getPos #-}--  getFile = get $ \env _ -> _envFile env-  {-# INLINE getFile #-}--  getRefPos = get $ \env _ -> Pos (_envRefLine env) (_envRefCol env)-  {-# INLINE getRefPos #-}--  withRefPos p = local (\st env -> env { _envRefLine = _stLine st, _envRefCol = _stCol st }) p-  {-# INLINE withRefPos #-}--  label l p = local (const $ addLabel l) p-  {-# INLINE label #-}--  hidden p = local (const $ \env -> env { _envHidden = True }) p-  {-# INLINE hidden #-}--  commit p = local (const $ \env -> env { _envCommit = _envCommit env + 1 }) p-  {-# INLINE commit #-}--  notFollowedBy p = Reporter $ \env st ok err ->-    let ok' x _ = raiseError env st err $ EUnexpected $ show x-        err' _ = ok () st-    in unReporter p env st ok' err'-  {-# INLINE notFollowedBy #-}--  lookAhead p = Reporter $ \env st ok err ->-    let ok' x _ = ok x st-    in unReporter p env st ok' err-  {-# INLINE lookAhead #-}--  failWith e = Reporter $ \env st _ err -> raiseError env st err e-  {-# INLINE failWith #-}--  eof = Reporter $ \env st ok err ->-    if _stOff st >= _envEnd env then-      ok () st-    else-      raiseError env st err EExpectedEnd-  {-# INLINE eof #-}--  byte b = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->-    if | _stOff >= _envEnd env -> err st-       | b == byteAt (_envSrc env) _stOff ->-           ok b st-           { _stOff =_stOff + 1-           , _stLine = if b == asc_newline then _stLine + 1 else _stLine-           , _stCol = if b == asc_newline then 1 else _stCol + 1-           }-       | otherwise ->-           raiseError env st err $ EExpected [showByte b]-  {-# INLINE byte #-}--  byteSatisfy f = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->-    let b = byteAt (_envSrc env) _stOff-    in if | _stOff >= _envEnd env -> err st-          | f b ->-              ok b st-              { _stOff =_stOff + 1-              , _stLine = if b == asc_newline then _stLine + 1 else _stLine-              , _stCol = if b == asc_newline then 1 else _stCol + 1-              }-          | otherwise ->-              raiseError env st err $ EUnexpected $ showByte b-  {-# INLINE byteSatisfy #-}--  bytes b@(B.PS p i n) = Reporter $ \env st@State{_stOff,_stCol} ok err ->-    if n + _stOff <= _envEnd env &&-       bytesEqual (_envSrc env) _stOff p i n then-      ok b st { _stOff = _stOff + n, _stCol = _stCol + n }-    else-      raiseError env st err $ EExpected [showBytes b]-  {-# INLINE bytes #-}--  asBytes p = do-    begin <- get (const _stOff)-    p-    end <- get (const _stOff)-    src <- get (\env _ -> _envSrc env)-    pure $ B.PS src begin (end - begin)-  {-# INLINE asBytes #-}--  satisfy f = Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->-    let (c, w) = utf8Decode (_envSrc env) _stOff-    in if | c /= '\0' ->-            if f c then-              ok c st-              { _stOff =_stOff + w-              , _stLine = if c == '\n' then _stLine + 1 else _stLine-              , _stCol = if c == '\n' then 1 else _stCol + 1-              }-            else-              raiseError env st err $ EUnexpected $ show c-          | c == '\0' && _stOff >= _envEnd env -> err st-          | otherwise -> raiseError env st err EInvalidUtf8-  {-# INLINE satisfy #-}--  char c =-    let w = utf8Width c-    in Reporter $ \env st@State{_stOff, _stLine, _stCol} ok err ->-      if utf8DecodeFixed w (_envSrc env) _stOff == c then-        ok c st-        { _stOff =_stOff + w-        , _stLine = if c == '\n' then _stLine + 1 else _stLine-        , _stCol = if c == '\n' then 1 else _stCol + 1-        }-      else-        raiseError env st err $ EExpected [show c]-  {-# INLINE char #-}--raiseError :: Env -> State -> (State -> b) -> Error -> b-raiseError env st err e = err $ addError env st e-{-# INLINE raiseError #-}---- | Reader monad, modify environment locally-local :: (State -> Env -> Env) -> Reporter a -> Reporter a-local f p = Reporter $ \env st ok err ->-  unReporter p (f st env) st ok err-{-# INLINE local #-}---- | Reader monad, get something from the environment-get :: (Env -> State -> a) -> Reporter a-get f = Reporter $ \env st ok _ -> ok (f env st) st-{-# INLINE get #-}--addLabel :: String -> Env -> Env-addLabel l env = case _envContext env of-  (l':_) | l == l' -> env-  ls               -> env { _envContext = take (_optMaxLabelsPerContext._envOptions $ env) $ l : ls }-{-# INLINE addLabel #-}---- | Add parser error to the list of errors--- which are kept in the parser state.--- Errors of lower priority and at an earlier position.--- Furthermore the error is merged with existing errors if possible.-addError :: Env -> State -> Error -> State-addError env st e-  | _stOff st > _stErrOff st || _envCommit env > _stErrCommit st,-    Just e' <- mkError env e =-      st { _stErrors    = [e']-         , _stErrOff    = _stOff st-         , _stErrLine   = _stLine st-         , _stErrCol    = _stCol st-         , _stErrCommit = _envCommit env-         }-  | _stOff st == _stErrOff st && _envCommit env == _stErrCommit st,-    Just e' <- mkError env e =-      st { _stErrors = shrinkErrors env $ e' : _stErrors st }-  | otherwise = st-{-# INLINE addError #-}--mkError :: Env -> Error -> Maybe ErrorContext-mkError env e-  | _envHidden env, (l:ls) <- _envContext env = Just $ ([EExpected [l]], ls)-  | _envHidden env = Nothing-  | otherwise = Just $ ([e], _envContext env)-{-# INLINE mkError #-}---- | Merge errors of two states, used when backtracking-mergeStateErrors :: Env -> State -> State -> State-mergeStateErrors env s s'-  | _stErrOff s' > _stErrOff s || _stErrCommit s' > _stErrCommit s =-      s { _stErrors    = _stErrors s'-        , _stErrOff    = _stErrOff s'-        , _stErrLine   = _stErrLine s'-        , _stErrCol    = _stErrCol s'-        , _stErrCommit = _stErrCommit s'-        }-  | _stErrOff s' == _stErrOff s && _stErrCommit s' == _stErrCommit s =-      s { _stErrors = shrinkErrors env $ _stErrors s' <> _stErrors s }-  | otherwise = s-{-# INLINE mergeStateErrors #-}--groupOn :: Eq e => (a -> e) -> [a] -> [NonEmpty a]-groupOn f = NE.groupBy ((==) `on` f)--shrinkErrors :: Env -> [ErrorContext] -> [ErrorContext]-shrinkErrors env = take (_optMaxContexts._envOptions $ env) . map (mergeErrorContexts env) . groupOn snd . sortOn snd---- | Shrink error context by deleting duplicates--- and merging errors if possible.-mergeErrorContexts :: Env -> NonEmpty ErrorContext -> ErrorContext-mergeErrorContexts env es@((_, ctx):| _) = (take (_optMaxErrorsPerContext._envOptions $ env) $ nubSort $ mergeEExpected $ concatMap fst $ NE.toList es, ctx)--mergeEExpected :: [Error] -> [Error]-mergeEExpected es = [EExpected $ nubSort expects | not (null expects)] <> filter (null . asEExpected) es-  where expects = concatMap asEExpected es--nubSort :: Ord a => [a] -> [a]-nubSort = map head . group . sort--asEExpected :: Error -> [String]-asEExpected (EExpected s) = s-asEExpected _ = []---- | Run 'Reporter' with additional 'ReportOptions'.-runReporterWithOptions :: ReportOptions -> Reporter a -> FilePath -> ByteString -> Either Report a-runReporterWithOptions o p f t =-  let b = t <> "\0\0\0"-  in unReporter p (initialEnv o f b) (initialState b) (\x _ -> Right x) (Left . getReport f)---- | Run 'Reporter' on the given 'ByteString', returning either--- an error 'Report' or, if successful, the result.-runReporter :: Reporter a -> FilePath -> ByteString -> Either Report a-runReporter = runReporterWithOptions defaultReportOptions--getReport :: FilePath -> State -> Report-getReport f s = Report f (_stErrLine s) (_stErrCol s) (_stErrors s)--initialEnv :: ReportOptions -> FilePath -> ByteString -> Env-initialEnv _envOptions _envFile (B.PS _envSrc off len) = Env-  { _envFile-  , _envSrc-  , _envOptions-  , _envEnd     = off + len - 3-  , _envContext = []-  , _envHidden  = False-  , _envCommit  = 0-  , _envRefLine = 0-  , _envRefCol  = 0-  }--defaultReportOptions :: ReportOptions-defaultReportOptions = ReportOptions-  { _optMaxContexts         = 20-  , _optMaxErrorsPerContext = 20-  , _optMaxLabelsPerContext = 5-  }--initialState :: ByteString -> State-initialState (B.PS _ _stOff _) = State-  { _stOff-  , _stLine      = 1-  , _stCol       = 1-  , _stErrOff    = 0-  , _stErrLine   = 0-  , _stErrCol    = 0-  , _stErrCommit = 0-  , _stErrors    = []-  }---- | Pretty string representation of 'Report'.-showReport :: Report -> String-showReport r =-  "Parser errors at " <> _reportFile r-  <> ", line " <> show (_reportLine r)-  <> ", column " <> show (_reportCol r)-  <> "\n\n" <> showErrors (_reportErrors r)---- | Pretty string representation of '[ErrorContext]'.-showErrors :: [ErrorContext] -> String-showErrors [] = "No errors"-showErrors es = intercalate "\n" $ map showErrorContext es--showErrorContext :: ErrorContext -> String-showErrorContext (e, c) = intercalate ", " (map showError e) <> showContext c <> "."--showContext :: [String] -> String-showContext [] = ""-showContext xs = " in context of " <> intercalate ", " xs---- | Parser which prints trace messages, when backtracking occurs.-newtype Tracer a = Tracer { unTracer :: Reporter a }-  deriving (Semigroup, Monoid, Functor, Applicative, MonadPlus, Monad, Fail.MonadFail, MonadParser)--instance Alternative Tracer where-  empty = Tracer empty--  p1 <|> p2 = Tracer $ Reporter $ \env st ok err ->-    let err' s =-          let width = _stOff s -_stOff st-              next  = unReporter (unTracer p2) env (mergeStateErrors env st s) ok err-          in if width > 1 then-               trace ("Back tracking " <> show width <> " bytes at line " <> show (_stLine s)-                       <> ", column " <> show (_stCol s) <> ", context " <> show (_envContext env) <> ": "-                       <> showBytes (B.PS (_envSrc env) (_stOff st) width)) next-             else-               next-    in unReporter (unTracer p1) env st ok err'---- | Run 'Tracer' on the given 'ByteString', returning either--- an error 'Report' or, if successful, the result.-runTracer :: Tracer a -> FilePath -> ByteString -> Either Report a-runTracer = runReporter . unTracer
+ test/test.hs view
@@ -0,0 +1,492 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE FlexibleContexts #-}+module Main (main) where++import Data.ByteString (ByteString)+import Data.Either (isLeft)+import Data.Text (Text)+import GHC.Stack (HasCallStack)+import Prelude hiding (getLine)+import Test.Tasty+import Test.Tasty.HUnit+import Text.PariPari+import Text.PariPari.Internal.Chunk (textToChunk, asc_a, asc_0, asc_9)+import qualified Data.Char as C+import qualified Data.List.NonEmpty as NE++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests"+  [ testGroup "Chunk"+    [ testGroup "Acceptor" $ chunkTests runAcceptor+    , testGroup "Reporter" $ chunkTests runAcceptor+    ]++  , testGroup "Char"+    [ testGroup "Acceptor"+      [ testGroup "Text"       $ charTests @Text       runAcceptor+      , testGroup "ByteString" $ charTests @ByteString runAcceptor+      ]++    , testGroup "Reporter"+      [ testGroup "Text"       $ charTests @Text       runReporter+      , testGroup "ByteString" $ charTests @ByteString runReporter+      ]+    ]+  ]++charTests :: forall k p e. (CharParser k p, CharChunk k, Eq e, Show e)+          => (forall a. p a -> FilePath -> k -> Either e a) -> [TestTree]+charTests run =+  [ testGroup "CharParser" $+    [ testCase "satisfy" $ do+        ok (satisfy (== 'a')) "abc" 'a'+        ok (satisfy (== 'a') <* eof) "a" 'a'+        err (satisfy (== 'b')) "abc"+        err (satisfy (== 'a')) ""++        -- because of sentinel+        err (satisfy (== '\0')) "\0"+        err (satisfy (== '\0')) ""++    , testCase "char" $ do+        ok (char 'a') "abc" 'a'+        ok (char 'a' <* eof) "a" 'a'+        err (char 'b') "abc"+        err (char 'a') ""++    , testCase "asciiSatisfy" $ do+        ok (asciiSatisfy (== asc_a)) "abc" asc_a+        ok (asciiSatisfy (== asc_a) <* eof) "a" asc_a+        err (asciiSatisfy (== asc_0)) "abc"+        err (asciiSatisfy (== asc_0)) ""++        -- because of sentinel+        err (asciiSatisfy (== 0)) "\0"+        err (asciiSatisfy (== 0)) ""++    , testCase "asciiByte" $ do+        ok (asciiByte asc_a) "abc" asc_a+        ok (asciiByte asc_a <* eof) "a" asc_a+        ok (asciiByte 127 <* eof) "\x7F" 127+        err (asciiByte asc_0) "abc"+        err (asciiByte asc_0) ""+    ]++  , testGroup "Char Combinators"+    [ testCase "string" $ do+        ok (string "ab") "abc" "ab"+        err (string "bc") "abc"+        err (string "ab") ""++    , testCase "anyChar" $ do+        ok anyChar "abc" 'a'+        ok (anyChar <* eof) "a" 'a'+        err anyChar ""++    , testCase "notChar" $ do+        ok (notChar 'b') "abc" 'a'+        ok (notChar 'b' <* eof) "a" 'a'+        err (notChar 'a') "a"+        err (notChar 'a') ""++    , testCase "char'" $ do+        ok (char' 'a') "a" 'a'+        ok (char' 'a' <* eof) "a" 'a'+        ok (char' 'a') "A" 'A'+        ok (char' 'A') "a" 'a'+        ok (char' 'A') "A" 'A'+        ok (char' '9') "9" '9'+        err (char' 'a') "b"+        err (char' 'a') ""++    , testCase "alphaNumChar" $ do+        ok (alphaNumChar <* eof) "a" 'a'+        ok alphaNumChar "9" '9'+        err alphaNumChar "_"+        err alphaNumChar ""++    , testCase "digitChar" $ do+        ok (digitChar 10 <* eof) "9" '9'+        ok (digitChar 36) "z" 'z'+        err (digitChar 2) "2"+        err (digitChar 2) ""++    , testCase "letterChar" $ do+        ok (letterChar <* eof) "a" 'a'+        err letterChar "9"+        err letterChar "_"+        err letterChar ""++    , testCase "lowerChar" $ do+        ok (lowerChar <* eof) "a" 'a'+        err lowerChar "A"+        err lowerChar "9"+        err lowerChar "_"+        err lowerChar ""++    , testCase "upperChar" $ do+        ok (upperChar <* eof) "A" 'A'+        err upperChar "a"+        err upperChar "9"+        err upperChar "_"+        err upperChar ""++    , testCase "symbolChar" $ do+        ok (symbolChar <* eof) "+" '+'+        err symbolChar "a"+        err symbolChar "."+        err symbolChar ""++    , testCase "punctuationChar" $ do+        ok (punctuationChar <* eof) "." '.'+        err punctuationChar "+"+        err punctuationChar "a"+        err punctuationChar ""++    , testCase "spaceChar" $ do+        ok (spaceChar <* eof) " " ' '+        ok (spaceChar <* eof) "\n" '\n'+        ok (spaceChar <* eof) "\r" '\r'+        ok (spaceChar <* eof) "\t" '\t'+        err spaceChar "a"+        err spaceChar ""++    , testCase "asciiChar" $ do+        ok (asciiChar <* eof) "a" 'a'+        ok (asciiChar <* eof) "\x7F" '\x7F'+        err asciiChar "\x80"+        err asciiChar ""++    , testCase "categoryChar" $ do+        ok (categoryChar C.UppercaseLetter <* eof) "A" 'A'+        err (categoryChar C.UppercaseLetter) "a"+        err (categoryChar C.UppercaseLetter) ""++    , testCase "digitByte" $ do+        ok (digitByte 2 <* eof) "0" asc_0+        ok (digitByte 10 <* eof) "9" asc_9+        err (digitByte 10) "\0"+        err (digitByte 10) ""+    ]++  , testGroup "Integer Combinators"+    [ testCase "decimal" $ do+        ok @Integer (decimal <* eof) "0123" 123+        ok @Integer (decimal <* eof) "1234567890" 1234567890+        ok @Integer (decimal <* string "abc" <* eof) "123abc" 123+        err @Integer decimal "abc"+        err @Integer decimal "-1"+        err @Integer decimal ""++    , testCase "octal" $ do+        ok @Integer (octal <* eof) "0123" 0o123+        ok @Integer (octal <* eof) "12345670" 0o12345670+        ok @Integer (octal <* string "abc" <* eof) "123abc" 0o123+        err @Integer octal "8abc"+        err @Integer octal "-1"+        err @Integer octal ""++    , testCase "hexadecimal" $ do+        ok @Integer (hexadecimal <* eof) "0123" 0x123+        ok @Integer (hexadecimal <* eof) "123456789aBcDeF0" 0x123456789ABCDEF0+        ok @Integer (hexadecimal <* string "xyz" <* eof) "123xyz" 0x123+        err @Integer hexadecimal "gabc"+        err @Integer hexadecimal "-1"+        err @Integer hexadecimal ""++    , testCase "integer" $ do+        err @Integer (integer (pure ()) 10) "abc"+        ok @Integer (integer (char '_') 10) "1_2_3" 123+        ok @Integer (integer (char '_') 10 <* char '_') "1_2_3_" 123+        err @Integer (integer (char '_') 10) "_1_2_3"+        ok @Integer (integer (optional $ char '_') 10) "123_456_789" 123456789+        err @Integer (integer (pure ()) 10) "-1"+        err @Integer (integer (pure ()) 10) ""++        ok @Integer (integer (pure ()) 2) "101" 5+        ok @Integer (integer (pure ()) 7) "321" 162+        ok @Integer (integer (pure ()) 36) "XyZ" 44027++    , testCase "integer'" $ do+        err @(Integer, Int) (integer' (pure ()) 10) "abc"+        ok @(Integer, Int) (integer' (char '_') 10) "1_2_3" (123, 3)+        ok @(Integer, Int) (integer' (char '_') 10 <* char '_') "1_2_3_" (123, 3)+        err @(Integer, Int) (integer' (char '_') 10) "_1_2_3"+        ok @(Integer, Int) (integer' (optional $ char '_') 10) "123_456_789" (123456789, 9)+        err @(Integer, Int) (integer' (pure ()) 10) "-1"+        err @(Integer, Int) (integer' (pure ()) 10) ""++        ok @(Integer, Int) (integer' (pure ()) 2) "101" (5, 3)+        ok @(Integer, Int) (integer' (pure ()) 7) "321" (162, 3)+        ok @(Integer, Int) (integer' (pure ()) 36) "XyZ" (44027, 3)++    , testCase "signed" $ do+        ok @Integer (signed decimal) "-123" (-123)+        ok @Integer (signed decimal) "+123" 123+        err @Integer (signed decimal) "- 123"+        err @Integer (signed decimal) "+ 123"+        err @Integer (signed decimal) ""++    , testCase "digit" $ do+        ok (digit 2) "1" 1+        ok (digit 10) "7" 7+        ok (digit 36) "Z" 35+        err (digit 10) "a"+        err (digit 10) ""+    ]+  ]+  where+  ok :: (Eq a, Show a, HasCallStack) => p a -> Text -> a -> Assertion+  ok p i o = run p "filename" (textToChunk @k i) @?= Right o+  err :: (Eq a, Show a, HasCallStack) => p a -> Text -> Assertion+  err p i = assertBool "err" $ isLeft $ run p "filename" (textToChunk @k i)++chunkTests :: forall p e. (ChunkParser Text p, Eq e, Show e)+          => (forall a. p a -> FilePath -> Text -> Either e a) -> [TestTree]+chunkTests run =+  [ testGroup "ChunkParser"+    [ testCase "getFile" $ do+        ok getFile "" "filename"++    , testCase "getPos" $ do+        ok getPos "" (Pos 1 1)+        ok (element 'a' *> getPos) "abc" (Pos 1 2)+        ok (element 'a' *> element '\n' *> getPos) "a\nb" (Pos 2 1)+        ok (chunk "a\n" *> getPos) "a\nb" (Pos 1 3) -- chunk must not contain newlines!+        ok (element 'a' *> element '\n' *> element 'b' *> getPos) "a\nb" (Pos 2 2)++    , testCase "getRefPos" $ do+        ok getRefPos "" (Pos 1 1)+        ok (element 'a' *> getRefPos) "abc" (Pos 1 1)+        ok (element 'a' *> element '\n' *> withRefPos getRefPos) "a\nb" (Pos 2 1)+        ok (element 'a' *> element '\n' *> element 'b' *> withRefPos getRefPos) "a\nb" (Pos 2 2)+        ok (element 'a' *> element '\n' *> withRefPos (element 'b' *> getRefPos)) "a\nb" (Pos 2 1)++    , testCase "notFollowedBy" $ do+        ok (element 'a' <* notFollowedBy (element 'c')) "abc" 'a'+        err (element 'a' <* notFollowedBy (element 'b')) "abc"+        ok (element 'a' *> notFollowedBy (chunk "bd") *> element 'b') "abc" 'b'+        err (element 'a' *> notFollowedBy (chunk "bc")) "abc"+        ok (element 'a' *> notFollowedBy (element 'c') *> getPos) "abc" (Pos 1 2)++    , testCase "lookAhead" $ do+        ok (lookAhead (element 'a')) "abc" 'a'+        ok (lookAhead (element 'a') *> getPos) "abc" (Pos 1 1)+        ok (lookAhead (chunk "ab") *> getPos) "abc" (Pos 1 1)+        err (lookAhead (element 'b')) "abc"+        err (lookAhead (chunk "bd")) "abc"+        err (lookAhead (element 'a')) ""++    , testCase "failWith" $+        err (failWith (ECombinator "empty") :: p ()) "abc"++    , testCase "eof" $ do+        ok eof "" ()+        ok (chunk "abc" *> eof) "abc" ()+        err eof "abc"+        err eof "\0"+        err (chunk "ab" *> eof) "abc"++    , testCase "label" $ do+        ok (label "blub" $ element 'a') "abc" 'a'+        err (label "blub" $ element 'b') "abc"+        ok (element 'a' <?> "blub") "abc" 'a'++    , testCase "hidden" $ do+        ok (hidden $ element 'a') "abc" 'a'+        err (hidden $ element 'b') "abc"++    , testCase "commit" $ do+        ok (commit $ element 'a') "abc" 'a'+        err (commit $ element 'b') "abc"++    , testCase "elementSatisfy" $ do+        ok (elementSatisfy (== 'a')) "abc" 'a'+        ok (elementSatisfy (== 'a') <* eof) "a" 'a'+        err (elementSatisfy (== 'b')) "abc"+        err (elementSatisfy (== 'a')) ""++        -- sentinel+        ok (elementSatisfy (== '\0')) "\0" '\0'+        ok (elementSatisfy (== '\0') <* eof) "\0" '\0'+        err (elementSatisfy (== '\0')) ""++    , testCase "element" $ do+        ok (element 'a') "abc" 'a'+        ok (element 'a' <* eof) "a" 'a'+        err (element 'b') "abc"+        err (element 'a') ""++        -- sentinel+        ok (element '\0') "\0" '\0'+        ok (element '\0' <* eof) "\0" '\0'+        err (element '\0') ""++    , testCase "chunk" $ do+        ok (chunk "ab") "abc" "ab"+        err (chunk "bc") "abc"+        err (chunk "ab") ""++    , testCase "asChunk" $ do+        ok (asChunk (void $ chunk "ab")) "abc" "ab"+        ok (asChunk (void $ anyElement *> anyElement)) "abc" "ab"+        ok (asChunk (skipCount 2 anyElement)) "abc" "ab"+        err (asChunk (void $ chunk "bc")) "abc"+        err (asChunk (void $ chunk "ab")) ""+    ]++  , testGroup "Position Combinators"+    [ testCase "getLine" $ do+        ok getLine "" 1+        ok (element 'a' *> getLine) "abc" 1+        ok (element 'a' *> element '\n' *> getLine) "a\nb" 2+        ok (element 'a' *> element '\n' *> element 'b' *> getLine) "a\nb" 2++    , testCase "getRefLine" $ do+        ok getRefLine "" 1+        ok (element 'a' *> getRefLine) "abc" 1+        ok (element 'a' *> element '\n' *> withRefPos getRefLine) "a\nb" 2+        ok (element 'a' *> element '\n' *> element 'b' *> withRefPos getRefLine) "a\nb" 2+        ok (element 'a' *> element '\n' *> withRefPos (element 'b' *> getRefLine)) "a\nb" 2++    , testCase "getColumn" $ do+        ok getColumn "" 1+        ok (element 'a' *> getColumn) "abc" 2+        ok (element 'a' *> element '\n' *> getColumn) "a\nb" 1+        ok (element 'a' *> element '\n' *> element 'b' *> getColumn) "a\nb" 2++    , testCase "getRefColumn" $ do+        ok getRefColumn "" 1+        ok (element 'a' *> getRefColumn) "abc" 1+        ok (element 'a' *> element '\n' *> withRefPos getRefColumn) "a\nb" 1+        ok (element 'a' *> element '\n' *> element 'b' *> withRefPos getRefColumn) "a\nb" 2+        ok (element 'a' *> element '\n' *> withRefPos (element 'b' *> getRefColumn)) "a\nb" 1++    , testCase "withPos" $ do+        ok (withPos $ element 'a') "abc" (Pos 1 1, 'a')+        ok (element 'a' *> withPos (element 'b')) "abc" (Pos 1 2, 'b')+        ok (element 'a' *> element '\n' *> withPos (element 'b')) "a\nb" (Pos 2 1, 'b')++    , testCase "withSpan" $ do+        ok (withSpan $ chunk "ab") "abc" ((Pos 1 1, Pos 1 3), "ab")+        ok (element 'a' *> withSpan (chunk "bcd")) "abcde" ((Pos 1 2, Pos 1 5), "bcd")+        ok (element 'a' *> element '\n' *> withSpan (chunk "bcd")) "a\nbcde" ((Pos 2 1, Pos 2 4), "bcd")+    ]++  , testGroup "Element Combinators"+    [ testCase "anyElement" $ do+        ok anyElement "abc" 'a'+        err anyElement ""++    , testCase "notElement" $ do+        ok (notElement 'b') "abc" 'a'+        err (notElement 'a') "a"+        err (notElement 'a') ""+    ]++  , testGroup "MonadFail"+    [ testCase "fail" $ do+        err (failWith (ECombinator "empty") :: p ()) "abc"+    ]++  , testGroup "Alternative"+    [ testCase "(<|>)" $ do+        ok (element 'b' <|> element 'a' <|> element 'c') "abc" 'a'+        ok (chunk "abd" <|> chunk "abc" <|> chunk "abe") "abcdef" "abc"++    , testCase "empty" $ do+        err (empty :: p ()) "abc"+        err (empty :: p ()) ""+    ]++  , testGroup "Basic Combinators"+    [ testCase "optional" $ do+        ok (optional (element 'a')) "abc" (Just 'a')+        ok (optional (element 'b')) "abc" Nothing+        ok (optional (element 'a')) "" Nothing++    , testCase "some" $ do+        ok (some (element 'a')) "abc" (NE.fromList "a")+        ok (some (element 'a')) "aabc" (NE.fromList "aa")+        err (some (element 'b')) "abc"+        err (some (element 'a')) ""++    , testCase "many" $ do+        ok (many (element 'a')) "abc" "a"+        ok (many (element 'a')) "aabc" "aa"+        ok (many (element 'b')) "abc" ""+        ok (many (element 'a')) "" ""+    ]+  ]+  where+  ok :: (Eq a, Show a, HasCallStack) => p a -> Text -> a -> Assertion+  ok p i o = run p "filename" i @?= Right o+  err :: (Eq a, Show a, HasCallStack) => p a -> Text -> Assertion+  err p i = assertBool "err" $ isLeft $ run p "filename" i+++{-++Semigroup+Monoid+Functor+Applicative+Monad+MonadPlus+Alternative:++TODO:++Additional combinators:+endBy1+someTill+sepBy1+sepEndBy1+between+choice+count+count'+eitherP+endBy+manyTill+option+sepBy+sepEndBy+skipMany+skipSome+skipCount+skipManyTill+skipSomeTill++Identation:+align+indented+line+linefold++Fraction:+fractionHex+fractionDec++Char Combinators:+skipChars+takeChars+skipCharsWhile+takeCharsWhile+skipCharsWhile1+takeCharsWhile1++Element Combinators:+takeElements+skipElements+skipElementsWhile+takeElementsWhile+skipElementsWhile1+takeElementsWhile1+-}