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 +23/−9
- paripari.cabal +42/−11
- specialise-all.hs +199/−0
- src/Text/PariPari.hs +37/−47
- src/Text/PariPari/Acceptor.hs +0/−242
- src/Text/PariPari/Ascii.hs +0/−45
- src/Text/PariPari/Class.hs +0/−126
- src/Text/PariPari/Combinators.hs +0/−457
- src/Text/PariPari/Decode.hs +0/−90
- src/Text/PariPari/Internal/Acceptor.hs +274/−0
- src/Text/PariPari/Internal/CharCombinators.hs +266/−0
- src/Text/PariPari/Internal/Chunk.hs +280/−0
- src/Text/PariPari/Internal/Class.hs +142/−0
- src/Text/PariPari/Internal/ElementCombinators.hs +184/−0
- src/Text/PariPari/Internal/Reporter.hs +447/−0
- src/Text/PariPari/Internal/Run.hs +89/−0
- src/Text/PariPari/Internal/Tracer.hs +43/−0
- src/Text/PariPari/Reporter.hs +0/−421
- test/test.hs +492/−0
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+-}