lmapM f parser maps the monadic function f on the input of the parser.
Modulestreamly-core-0.2.2Haskell2010
Streamly.Internal.Data.Parser
- 8 types
- 93 values
- Packagestreamly-core-0.2.2
- Exports101
- LanguageHaskell2010
- LicenceBSD-3-Clause
- SourceType.hs
Setup
24 declarationsTo execute the code examples provided in this module in ghci, please run the following commands first.
:mimport Control.Applicative ((<|>))import Data.Bifunctor (second)import Data.Char (isSpace)import qualified Data.Foldable as Foldableimport qualified Data.Maybe as Maybe
import Streamly.Data.Fold (Fold)import Streamly.Data.Parser (Parser)
import qualified Streamly.Data.Fold as Foldimport qualified Streamly.Data.Parser as Parserimport qualified Streamly.Data.Stream as Stream
For APIs that have not been released yet.
import qualified Streamly.Internal.Data.Fold as Foldimport qualified Streamly.Internal.Data.Parser as Parser
rmapM f parser maps the monadic function f on the output of the parser.
rmap = fmapThe return type of a Parser step.
The parse operation feeds the input stream to the parser one element at a time, representing a parse Step. The parser may or may not consume the item and returns a result. If the result is Partial we can either extract the result or feed more input to the parser. If the result is Continue, we must feed more input in order to get a result. If the parser returns Done then the parser can no longer take any more input.
If the result is Continue, the parse operation retains the input in a
backtracking buffer, in case the parser may ask to backtrack in future.
Whenever a 'Partial n' result is returned we first backtrack by n elements
in the input and then release any remaining backtracking buffer. Similarly,
'Continue n' backtracks to n elements before the current position and
starts feeding the input from that point for future invocations of the
parser.
If parser is not yet done, we can use the extract operation on the state
of the parser to extract a result. If the parser has not yet yielded a
result, the operation fails with a ParseError exception. If the parser
yielded a Partial result in the past the last partial result is returned.
Therefore, if a parser yields a partial result once it cannot fail later on.
The parser can never backtrack beyond the position where the last partial result left it at. The parser must ensure that the backtrack position is always after that.
Pre-release
Constructors
Partial !Int !sPartial count state. The following hold on Partial result:extractonstatewould succeed and give a result.Input stream position is reset to
current position - count.All input before the new position is dropped. The parser can never backtrack beyond this position.
Continue !Int !sDone !Int !bDone with leftover input count and result.
Done count resultmeans the parser has finished, it will accept no more input, lastcountelements from the input are unused and the result of the parser is inresult.Error !StringParser failed without generating any output.
The parsing operation may backtrack to the beginning and try another alternative.
A parser is a fold that can fail and is represented as Parser step
initial extract. Before we drive a parser we call the initial action to
retrieve the initial state of the fold. The parser driver invokes step
with the state returned by the previous step and the next input element. It
results into a new state and a command to the driver represented by Step
type. The driver keeps invoking the step function until it stops or fails.
At any point of time the driver can call extract to inspect the result of
the fold. If the parser hits the end of input extract is called.
It may result in an error or an output value.
Pre-release
Instances6Monad, Functor, MonadFail, Applicative, Alternative, MonadIO
Monad m => Monad (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeREAD THE CAVEATS in concatMap before using this instance.
Example1 expression (>>=) = flip Parser.concatMapFunctor m => Functor (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeMap a function on the result i.e. on
binParser a m b.Monad m => MonadFail (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeExample1 expression fail = Parser.dieMonad m => Applicative (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeREAD THE CAVEATS in splitWith before using this instance.
Example3 expressions pure = Parser.fromPure(<*>) = Parser.splitWith id(*>) = Parser.split_
Monad m => Alternative (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeREAD THE CAVEATS in alt before using this instance.
Example4 expressions empty = Parser.die "empty"(<|>) = Parser.altmany = flip Parser.many Fold.toListsome = flip Parser.some Fold.toList
MonadIO m => MonadIO (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeExample1 expression liftIO = Parser.fromEffect . liftIO
A parser that always yields a pure value without consuming any input.
The type of a Parser's initial action.
Internal
Instances2Bifunctor, Functor
Include only those elements that pass a predicate.
Stream.parse (Parser.filter (> 5) (Parser.fromFold Fold.sum)) $ Stream.fromList [1..10]Right 40
This exception is used when a parser ultimately fails, the user of the parser is intimated via this exception.
Pre-release
Constructors
Instances2Show, Exception
Show ParseErrorDefined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeException ParseErrorDefined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.Type
A parser that always yields the result of an effectful action without consuming any input.
Sequential parser application. Apply two parsers sequentially to an input stream. The first parser runs and processes the input, the remaining input is then passed to the second parser. If both parsers succeed, their outputs are combined using the supplied function. If either parser fails, the operation fails.
This combinator delivers high performance by stream fusion but it comes with
some limitations. For those cases use the Applicative instance of
Streamly.Data.ParserK.ParserK.
CAVEAT 1: NO RECURSION. This function is strict in both arguments. As a result, if a parser is defined recursively using this, it may cause an infintie loop. The following example checks the strictness:
p = Parser.splitWith const (Parser.satisfy (> 0)) undefinedStream.parse p $ Stream.fromList [1]*** Exception: Prelude.undefined...
CAVEAT 2: QUADRATIC TIME COMPLEXITY. Static composition is fast due to stream fusion, but it works well only for limited (e.g. up to 8) compositions, use Streamly.Data.ParserK for larger compositions.
Below are some common idioms that can be expressed using splitWith:
span p f1 f2 = Parser.splitWith (,) (Parser.takeWhile p f1) (Parser.fromFold f2)spanBy eq f1 f2 = Parser.splitWith (,) (Parser.groupBy eq f1) (Parser.fromFold f2)
Pre-release
lmap f parser maps the function f on the input of the parser.
Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)Right 338350
lmap = Parser.lmapM returnA parser that always fails with an error message without consuming any input.
Sequential parser application ignoring the output of the first parser. Apply two parsers sequentially to an input stream. The input is provided to the first parser, when it is done the remaining input is provided to the second parser. The output of the parser is the output of the second parser. The operation fails if any of the parsers fail.
ALL THE CAVEATS IN splitWith APPLY HERE AS WELL.
This implementation is strict in the second argument, therefore, the following will fail:
Stream.parse (Parser.split_ (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]*** Exception: Prelude.undefined...
Pre-release
Map an extract function over the state of Step
Bimap discarding the count, and using the supplied count instead.
A parser that always fails with an effectful error message and without consuming any input.
Pre-release
See documentation of some.
Pre-release
See documentation of many.
Pre-release
Like splitMany, but inner fold emits an output at the end even if no input is received.
Internal
Sequential alternative. The input is first passed to the first parser, if it succeeds, the result is returned. However, if the first parser fails, the parser driver backtracks and tries the same input on the second (alternative) parser, returning the result if it succeeds.
This combinator delivers high performance by stream fusion but it comes with
some limitations. For those cases use the Alternative instance of
Streamly.Data.ParserK.ParserK.
CAVEAT 1: NO RECURSION. This function is strict in both arguments. As a result, if a parser is defined recursively using this, it may cause an infintie loop. The following example checks the strictness:
p = Parser.satisfy (> 0) `Parser.alt` undefinedStream.parse p $ Stream.fromList [1..10]*** Exception: Prelude.undefined
CAVEAT 2: QUADRATIC TIME COMPLEXITY. Static composition is fast due to stream fusion, but it works well only for limited (e.g. up to 8) compositions, use Streamly.Data.ParserK for larger compositions.
Time Complexity: O(n^2) where n is the number of compositions.
Pre-release
Types
4 declarationsA parser is a fold that can fail and is represented as Parser step
initial extract. Before we drive a parser we call the initial action to
retrieve the initial state of the fold. The parser driver invokes step
with the state returned by the previous step and the next input element. It
results into a new state and a command to the driver represented by Step
type. The driver keeps invoking the step function until it stops or fails.
At any point of time the driver can call extract to inspect the result of
the fold. If the parser hits the end of input extract is called.
It may result in an error or an output value.
Pre-release
Instances6Monad, Functor, MonadFail, Applicative, Alternative, MonadIO
Monad m => Monad (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeREAD THE CAVEATS in concatMap before using this instance.
Example1 expression (>>=) = flip Parser.concatMapFunctor m => Functor (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeMap a function on the result i.e. on
binParser a m b.Monad m => MonadFail (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeExample1 expression fail = Parser.dieMonad m => Applicative (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeREAD THE CAVEATS in splitWith before using this instance.
Example3 expressions pure = Parser.fromPure(<*>) = Parser.splitWith id(*>) = Parser.split_
Monad m => Alternative (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeREAD THE CAVEATS in alt before using this instance.
Example4 expressions empty = Parser.die "empty"(<|>) = Parser.altmany = flip Parser.many Fold.toListsome = flip Parser.some Fold.toList
MonadIO m => MonadIO (Parser a m)Defined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeExample1 expression liftIO = Parser.fromEffect . liftIO
This exception is used when a parser ultimately fails, the user of the parser is intimated via this exception.
Pre-release
Constructors
Instances2Show, Exception
Show ParseErrorDefined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.TypeException ParseErrorDefined in streamly-core-0.2.2 · Streamly.Internal.Data.Parser.Type
The return type of a Parser step.
The parse operation feeds the input stream to the parser one element at a time, representing a parse Step. The parser may or may not consume the item and returns a result. If the result is Partial we can either extract the result or feed more input to the parser. If the result is Continue, we must feed more input in order to get a result. If the parser returns Done then the parser can no longer take any more input.
If the result is Continue, the parse operation retains the input in a
backtracking buffer, in case the parser may ask to backtrack in future.
Whenever a 'Partial n' result is returned we first backtrack by n elements
in the input and then release any remaining backtracking buffer. Similarly,
'Continue n' backtracks to n elements before the current position and
starts feeding the input from that point for future invocations of the
parser.
If parser is not yet done, we can use the extract operation on the state
of the parser to extract a result. If the parser has not yet yielded a
result, the operation fails with a ParseError exception. If the parser
yielded a Partial result in the past the last partial result is returned.
Therefore, if a parser yields a partial result once it cannot fail later on.
The parser can never backtrack beyond the position where the last partial result left it at. The parser must ensure that the backtrack position is always after that.
Pre-release
Constructors
Partial !Int !sPartial count state. The following hold on Partial result:extractonstatewould succeed and give a result.Input stream position is reset to
current position - count.All input before the new position is dropped. The parser can never backtrack beyond this position.
Continue !Int !sDone !Int !bDone with leftover input count and result.
Done count resultmeans the parser has finished, it will accept no more input, lastcountelements from the input are unused and the result of the parser is inresult.Error !StringParser failed without generating any output.
The parsing operation may backtrack to the beginning and try another alternative.
The type of a Parser's initial action.
Internal
Instances2Bifunctor, Functor
Downgrade to Fold
1 declarationAccumulators
2 declarationsConvert a Maybe returning fold to an error returning parser. The first argument is the error message that the parser would return when the fold returns Nothing.
Pre-release
Map on input
1 declarationStateful scan on the input of a parser using a Fold.
Unimplemented
Element parsers
10 declarationsPeek the head element of a stream, without consuming it. Fails if it encounters end of input.
Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]Right (1,1)
peek = lookAhead (satisfy True)
Consume one element from the head of the stream. Fails if it encounters end of input.
one = Parser.satisfy $ const TrueMatch a specific element.
oneEq x = Parser.satisfy (== x)Match anything other than the supplied element.
oneNotEq x = Parser.satisfy (/= x)Match any one of the elements in the supplied list.
oneOf xs = Parser.satisfy (`Foldable.elem` xs)When performance matters a pattern matching predicate could be more efficient than a Foldable datatype:
let p x =
case x of
a -> True
e -> True
_ -> False
in satisfy p
GHC may use a binary search instead of linear search in the list. Alternatively, you can also use an array instead of list for storage and search.
See performance notes in oneOf.
noneOf xs = Parser.satisfy (`Foldable.notElem` xs)Succeeds if we are at the end of input, fails otherwise.
Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]Right (1,())
Returns the next element if it passes the predicate, fails otherwise.
Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]Right 1
toMaybe f x = if f x then Just x else Nothingsatisfy f = Parser.maybe (toMaybe f)
Map a Maybe returning function on the next element in the stream. The parser fails if the function returns Nothing otherwise returns the Just value.
toEither = Maybe.maybe (Left "maybe: predicate failed") Rightmaybe f = Parser.either (toEither . f)
maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)Pre-release
Sequence parsers (tokenizers)
1 declarationParsers chained in series, if one parser terminates the composition terminates. Currently we are using folds to collect the output of the parsers but we can use Parsers instead of folds to make the composition more powerful. For example, we can do:
takeEndByOrMax cond n p = takeEndBy cond (take n p) takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p) takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)
Run a parser without consuming the input.
By length
Grab a sequence of input elements without inspecting them
takeBetween m n takes a minimum of m and a maximum of n input
elements and folds them using the supplied fold.
Stops after n elements.
Fails if the stream ends before m elements could be taken.
Examples: -
>>> :{
takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)
where prsr = Parser.takeBetween low high Fold.toList
:}
takeBetween' 2 4 [1, 2, 3, 4, 5]Right [1,2,3,4]
takeBetween' 2 4 [1, 2]Right [1,2]
takeBetween' 2 4 [1]Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")
takeBetween' 0 0 [1, 2]Right []
takeBetween' 0 1 []Right []
takeBetween is the most general take operation, other take operations can
be defined in terms of takeBetween. For example:
take n = Parser.takeBetween 0 ntakeEQ n = Parser.takeBetween n ntakeGE n = Parser.takeBetween n maxBound
Pre-release
Stops after taking exactly n input elements.
Stops - after consuming
nelements.Fails - if the stream or the collecting fold ends before it can collect exactly
nelements.
Stream.parse (Parser.takeEQ 2 Fold.toList) $ Stream.fromList [1,0,1]Right [1,0]
Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]Left (ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3")
Take at least n input elements, but can collect more.
Stops - when the collecting fold stops.
Fails - if the stream or the collecting fold ends before producing
nelements.
Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]Left (ParseError "takeGE: Expecting at least 4 elements, input terminated on 3")
Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]Right [1,0,1,0,1]
Pre-release
Takes at-most n input elements.
Stops - when the collecting parser stops.
Fails - when the collecting parser fails.
Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]Right [1,2]
Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]Left (ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4")
Internal
Exact match
Match the input sequence with the supplied list and return it if successful.
listEq = Parser.listEqBy (==)Match the given sequence of elements using the given comparison function. Returns the original sequence if successful.
Definition:
listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xsExamples:
Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"Right "string"
Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"Left (ParseError "streamEqBy: mismtach occurred")
Like listEqBy but uses a stream instead of a list and does not return the stream.
Match if the input stream is a subsequence of the argument stream i.e. all the elements of the input stream occur, in order, in the argument stream. The elements do not have to occur consecutively. A sequence is considered a subsequence of itself.
By predicate
Collect stream elements until an element fails the predicate. The element on which the predicate fails is returned back to the input stream.
Stops - when the predicate fails or the collecting fold stops.
Fails - never.
Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]Right [0,0]
takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)We can implement a breakOn using takeWhile:
breakOn p = takeWhile (not p)
Like takeWhile but uses a Parser instead of a Fold to collect the input. The combinator stops when the condition fails or if the collecting parser stops.
Other interesting parsers can be implemented in terms of this parser:
takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)takeWhileBetween cond m n p = Parser.takeWhileP cond (Parser.takeBetween m n p)
Stops: when the condition fails or the collecting parser stops. Fails: when the collecting parser fails.
Pre-release
Like takeWhile but takes at least one element otherwise fails.
takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)Drain the input as long as the predicate succeeds, running the effects and discarding the results.
This is also called skipWhile in some parsing libraries.
dropWhile p = Parser.takeWhile p Fold.drainSeparated by elements
Separator could be in prefix postion (takeStartBy), or suffix
position (takeEndBy). See deintercalate, sepBy etc for infix
separator parsing, also see intersperseQuotedBy fold.
takeEndBy cond parser parses a token that ends by a separator chosen by
the supplied predicate. The separator is also taken with the token.
This can be combined with other parsers to implement other interesting parsers as follows:
takeEndByLE cond n p = Parser.takeEndBy cond (Parser.fromFold $ Fold.take n p)takeEndByBetween cond m n p = Parser.takeEndBy cond (Parser.takeBetween m n p)
takeEndBy = Parser.takeEndByEsc (const False)See also "Streamly.Data.Fold.takeEndBy". Unlike the fold, the collecting parser in the takeEndBy parser can decide whether to fail or not if the stream does not end with separator.
Pre-release
Like takeEndBy but the separator elements can be escaped using an escape char determined by the first predicate. The escape characters are removed.
pre-release
Parse a token that starts with an element chosen by the predicate. The parser fails if the input does not start with the selected element.
Stops - when the predicate succeeds in non-leading position.
Fails - when the predicate fails in the leading position.
splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)Examples: -
p = Parser.takeStartBy (== ',') Fold.toListleadingComma = Stream.parse p . Stream.fromListleadingComma "a,b"Left (ParseError "takeStartBy: missing frame start")...leadingComma ",,"Right ","leadingComma ",a,b"Right ",a"leadingComma ""Right ""
Pre-release
Like takeStartBy but drops the separator.
takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) NothingTake either the separator or the token. Separator is a Left value and token is Right value.
Unimplemented
Like splitOn but strips leading, trailing, and repeated separators.
Therefore, ".a..b." having . as the separator would be parsed as
["a","b"]. In other words, its like parsing words from whitespace
separated text.
Stops - when it finds a word separator after a non-word element
Fails - never.
wordBy = Parser.wordFramedBy (const False) (const False) (const False)S.wordsBy pred f = S.parseMany (PR.wordBy pred f)
Grouped by element comparison
Given an input stream [a,b,c,...] and a comparison function cmp, the
parser assigns the element a to the first group, then if a `cmp` b is
True b is also assigned to the same group. If a `cmp` c is True
then c is also assigned to the same group and so on. When the comparison
fails the parser is terminated. Each group is folded using the Fold f and
the result of the fold is the result of the parser.
Stops - when the comparison fails.
Fails - never.
:{ runGroupsBy eq = Stream.fold Fold.toList . Stream.parseMany (Parser.groupBy eq Fold.toList) . Stream.fromList:}
runGroupsBy (<) [][]
runGroupsBy (<) [1][Right [1]]
runGroupsBy (<) [3, 5, 4, 1, 2, 0][Right [3,5,4],Right [1,2],Right [0]]
Unlike groupBy this combinator performs a rolling comparison of two
successive elements in the input stream. Assuming the input stream
is [a,b,c,...] and the comparison function is cmp, the parser
first assigns the element a to the first group, then if a `cmp` b is
True b is also assigned to the same group. If b `cmp` c is True
then c is also assigned to the same group and so on. When the comparison
fails the parser is terminated. Each group is folded using the Fold f and
the result of the fold is the result of the parser.
Stops - when the comparison fails.
Fails - never.
:{ runGroupsByRolling eq = Stream.fold Fold.toList . Stream.parseMany (Parser.groupByRolling eq Fold.toList) . Stream.fromList:}
runGroupsByRolling (<) [][]
runGroupsByRolling (<) [1][Right [1]]
runGroupsByRolling (<) [3, 5, 4, 1, 2, 0][Right [3,5],Right [4],Right [1,2],Right [0]]
Pre-release
Like groupByRolling, but if the predicate is True then collects using the first fold as long as the predicate holds True, if the predicate is False collects using the second fold as long as it remains False. Returns Left for the first case and Right for the second case.
For example, if we want to detect sorted sequences in a stream, both ascending and descending cases we can use 'groupByRollingEither (<=) Fold.toList Fold.toList'.
Pre-release
Framed by elements
Also see intersperseQuotedBy fold.
Framed by a one or more ocurrences of a separator around a word like
spaces or quotes. No nesting.
wordFramedBy Like wordBy but treats anything inside a pair of quotes as a single word, the quotes can be escaped by an escape character. Recursive quotes are possible if quote begin and end characters are different, quotes must be balanced. Outermost quotes are stripped.
braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toListStream.parse braces $ Stream.fromList "{ab} cd"Right "ab"Stream.parse braces $ Stream.fromList "{ab}{cd}"Right "abcd"Stream.parse braces $ Stream.fromList "a{b} cd"Right "ab"Stream.parse braces $ Stream.fromList "a{{b}} cd"Right "a{b}"
quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toListStream.parse quotes $ Stream.fromList "\"a\"\"b\""Right "ab"
wordWithQuotes Quote and bracket aware word splitting with escaping. Like wordBy but word separators within specified quotes or brackets are ignored. Quotes and escape characters can be processed. If the end quote is different from the start quote it is called a bracket. The following quoting rules apply:
In an unquoted string a character may be preceded by an escape character. The escape character is removed and the character following it is treated literally with no special meaning e.g. e.g. h e l l o is a single word, n is same as n.
Any part of the word can be placed within quotes. Inside quotes all characters are treated literally with no special meaning. Quoting character itself cannot be used within quotes unless escape processing within quotes is applied to allow it.
Optionally escape processing for quoted part can be specified. Escape character has no special meaning inside quotes unless it is followed by a character that has a escape translation specified, in that case the escape character is removed, and the specified translation is applied to the character following it. This can be used to escape the quoting character itself within quotes.
There can be multiple quoting characters, when a quote starts, all other quoting characters within that quote lose any special meaning until the quote is closed.
A starting quote char without an ending char generates a parse error. An ending bracket char without a corresponding bracket begin is ignored.
Brackets can be nested.
We should note that unquoted and quoted escape processing are different. In unquoted part escape character is always removed. In quoted part it is removed only if followed by a special meaning character. This is consistent with how shell performs escape processing.
wordKeepQuotes wordWithQuotes without processing the quotes and escape function supplied to escape the quote char within a quote. Can be used to parse words keeping the quotes and escapes intact.
wordKeepQuotes = Parser.wordWithQuotes True (\_ _ -> Nothing)wordProcessQuotes wordWithQuotes with quote processing applied and escape function supplied to escape the quote char within a quote. Can be ysed to parse words and processing the quoting and escaping at the same time.
wordProcessQuotes = Parser.wordWithQuotes False (\_ _ -> Nothing)takeFramedBy_ isBegin isEnd fold parses a token framed by a begin and an
end predicate.
takeFramedBy_ = Parser.takeFramedByEsc_ (const False)takeFramedByEsc_ isEsc isBegin isEnd fold parses a token framed using a
begin and end predicate, and an escape character. The frame begin and end
characters lose their special meaning if preceded by the escape character.
Nested frames are allowed if begin and end markers are different, nested frames must be balanced unless escaped, nested frame markers are emitted as it is.
For example,
p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toListStream.parse p $ Stream.fromList "{hello}"Right "hello"Stream.parse p $ Stream.fromList "{hello {world}}"Right "hello {world}"Stream.parse p $ Stream.fromList "{hello \\{world}"Right "hello {world"Stream.parse p $ Stream.fromList "{hello {world}"Left (ParseError "takeFramedByEsc_: missing frame end")
Pre-release
blockWithQuotes Parse a block enclosed within open, close brackets. Block contents may be quoted, brackets inside quotes are ignored. Quoting characters can be used within quotes if escaped. A block can have a nested block inside it.
Quote begin and end chars are the same. Block brackets and quote chars must not overlap. Block start and end brackets must be different for nesting blocks within blocks.
p = Parser.blockWithQuotes (== '\\') (== '"') '{' '}' Fold.toListStream.parse p $ Stream.fromList "{msg: \"hello world\"}"Right "msg: \"hello world\""
Spanning
span p f1 f2 composes folds f1 and f2 such that f1 consumes the
input as long as the predicate p is True. f2 consumes the rest of the
input.
> let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs
> span_ (< 1) 1,2,3
> span_ (< 2) 1,2,3
> span_ (< 4) 1,2,3
Pre-release
Break the input stream into two groups, the first group takes the input as long as the predicate applied to the first element of the stream and next input element holds True, the second group takes the rest of the input.
Pre-release
Like spanBy but applies the predicate in a rolling fashion i.e. predicate is applied to the previous and the next input elements.
Pre-release
Binary Combinators
0 declarationsN-ary Combinators
0 declarationsSequential Collection
sequence f p collects sequential parses of parsers in a
serial stream p using the fold f. Fails if the input ends or any
of the parsers fail.
Pre-release
Sequential Repetition
count n f p collects exactly n sequential parses of parser p using
the fold f. Fails if the input ends or the parser fails before n
results are collected.
count n = Parser.countBetween n ncount n p f = Parser.manyP p (Parser.takeEQ n f)
Unimplemented
countBetween m n f p collects between m and n sequential parses of
parser p using the fold f. Stop after collecting n results. Fails if
the input ends or the parser fails before m results are collected.
countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)Unimplemented
Collect zero or more parses. Apply the supplied parser repeatedly on the input stream and push the parse results to a downstream fold.
Stops: when the downstream fold stops or the parser fails. Fails: never, produces zero or more results.
many = Parser.countBetween 0 maxBoundCompare with many.
Collect one or more parses. Apply the supplied parser repeatedly on the input stream and push the parse results to a downstream fold.
Stops: when the downstream fold stops or the parser fails. Fails: if it stops without producing a single result.
some p f = Parser.manyP p (Parser.takeGE 1 f)some = Parser.countBetween 1 maxBound
Compare with some.
Interleaved Repetition
Apply two parsers alternately to an input stream. The input stream is considered an interleaving of two patterns. The two parsers represent the two patterns. Parsing starts at the first parser and stops at the first parser. It can be used to parse a infix style pattern e.g. p1 p2 p1 . Empty input or single parse of the first parser is accepted.
p1 = Parser.takeWhile1 (not . (== '+')) Fold.toListp2 = Parser.satisfy (== '+')p = Parser.deintercalate p1 p2 Fold.toListStream.parse p $ Stream.fromList ""Right []Stream.parse p $ Stream.fromList "1"Right [Left "1"]Stream.parse p $ Stream.fromList "1+"Right [Left "1"]Stream.parse p $ Stream.fromList "1+2+3"Right [Left "1",Right '+',Left "2",Right '+',Left "3"]
Apply two parsers alternately to an input stream. The input stream is considered an interleaving of two patterns. The two parsers represent the two patterns. Parsing starts at the first parser and stops at the first parser. It can be used to parse a infix style pattern e.g. p1 p2 p1 . Empty input or single parse of the first parser is accepted.
p1 = Parser.takeWhile1 (not . (== '+')) Fold.toListp2 = Parser.satisfy (== '+')p = Parser.deintercalate1 p1 p2 Fold.toListStream.parse p $ Stream.fromList ""Left (ParseError "takeWhile1: end of input")Stream.parse p $ Stream.fromList "1"Right [Left "1"]Stream.parse p $ Stream.fromList "1+"Right [Left "1"]Stream.parse p $ Stream.fromList "1+2+3"Right [Left "1",Right '+',Left "2",Right '+',Left "3"]
Like deintercalate but the entire input must satisfy the pattern otherwise the parser fails. This is many times faster than deintercalate.
p1 = Parser.takeWhile1 (not . (== '+')) Fold.toListp2 = Parser.satisfy (== '+')p = Parser.deintercalateAll p1 p2 Fold.toListStream.parse p $ Stream.fromList ""Right []Stream.parse p $ Stream.fromList "1"Right [Left "1"]Stream.parse p $ Stream.fromList "1+"Left (ParseError "takeWhile1: end of input")Stream.parse p $ Stream.fromList "1+2+3"Right [Left "1",Right '+',Left "2",Right '+',Left "3"]
Special cases
TODO: traditional implmentations of these may be of limited use. For
example, consider parsing lines separated by \r\n. The main parser
will have to detect and exclude the sequence \r\n anyway so that we
can apply the "sep" parser.
We can instead implement these as special cases of deintercalate.
, endBy
, sepEndBy
, beginBy
, sepBeginBy
, sepAroundBy
Like sepBy but requires at least one successful parse.
Definition:
sepBy1 p1 p2 f = Parser.deintercalate1 p1 p2 (Fold.catLefts f)Examples:
p1 = Parser.takeWhile1 (not . (== '+')) Fold.toListp2 = Parser.satisfy (== '+')p = Parser.sepBy1 p1 p2 Fold.toListStream.parse p $ Stream.fromList ""Left (ParseError "takeWhile1: end of input")Stream.parse p $ Stream.fromList "1"Right ["1"]Stream.parse p $ Stream.fromList "1+"Right ["1"]Stream.parse p $ Stream.fromList "1+2+3"Right ["1","2","3"]
Apply two parsers alternately to an input stream. Parsing starts at the first parser and stops at the first parser. The output of the first parser is emiited and the output of the second parser is discarded. It can be used to parse a infix style pattern e.g. p1 p2 p1 . Empty input or single parse of the first parser is accepted.
Definitions:
sepBy p1 p2 f = Parser.deintercalate p1 p2 (Fold.catLefts f)sepBy p1 p2 f = Parser.sepBy1 p1 p2 f <|> Parser.fromEffect (Fold.extractM f)
Examples:
p1 = Parser.takeWhile1 (not . (== '+')) Fold.toListp2 = Parser.satisfy (== '+')p = Parser.sepBy p1 p2 Fold.toListStream.parse p $ Stream.fromList ""Right []Stream.parse p $ Stream.fromList "1"Right ["1"]Stream.parse p $ Stream.fromList "1+"Right ["1"]Stream.parse p $ Stream.fromList "1+2+3"Right ["1","2","3"]
Non-backtracking version of sepBy. Several times faster.
manyTill chunking test f tries the parser test on the input, if test
fails it backtracks and tries chunking, after chunking succeeds test is
tried again and so on. The parser stops when test succeeds. The output of
test is discarded and the output of chunking is accumulated by the
supplied fold. The parser fails if chunking fails.
Stops when the fold f stops.
manyThen f collect recover repeats the parser collect on the input and
collects the output in the supplied fold. If the the parser collect fails,
parser recover is run until it stops and then we start repeating the
parser collect again. The parser fails if the recovery parser fails.
For example, this can be used to find a key frame in a video stream after an error.
Unimplemented
Interleaved collection
Round robin
Priority based
Apply a collection of parsers to an input stream in a round robin fashion. Each parser is applied until it stops and then we repeat starting with the the first parser again.
Unimplemented
Collection of Alternatives
Unimplemented
, shortestN
, longestN
, fastestN -- first N successful in time
, choiceN -- first N successful in position
, choice -- first successful in position
Repeated Alternatives
Keep trying a parser up to a maximum of n failures. When the parser
fails the input consumed till now is dropped and the new instance is tried
on the fresh input.
Unimplemented
Like retryMaxTotal but aborts after n successive failures.
Unimplemented
Keep trying a parser until it succeeds. When the parser fails the input consumed till now is dropped and the new instance is tried on the fresh input.
Unimplemented
Zipping Input
Zip the input of a fold with a stream.
Pre-release
Pair each element of a fold input with its index, starting from index 0.
Pre-release
makeIndexFilter indexer filter predicate generates a fold filtering
function using a fold indexing function that attaches an index to each input
element and a filtering function that filters using @(index, element) ->
Bool) as predicate.
For example:
filterWithIndex = makeIndexFilter indexed filter
filterWithAbsTime = makeIndexFilter timestamped filter
filterWithRelTime = makeIndexFilter timeIndexed filter
Pre-release
sampleFromthen offset stride samples the element at offset index and
then every element at strides of stride.
Pre-release