Copyright | (c) Dan Shved 2022 |
---|---|
License | BSD-3 |
Maintainer | danshved@gmail.com |
Stability | experimental |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
This module contains the SelectiveParser
typeclass. Selective parsers can be
composed in many ways, notably using sequential application <*>
as well as
parallel application <#>
, which makes them twice applicative. See the
typeclass documentation for more details.
Normally you shouldn't need to import this module as it is re-exported by Options.OptStream.
Synopsis
- class Alternative p => SelectiveParser p where
- orElse :: SelectiveParser p => a -> p a
- applyMany :: SelectiveParser p => a -> p (a -> a) -> p a
- applySome :: SelectiveParser p => a -> p (a -> a) -> p a
- (<#) :: SelectiveParser p => p a -> p b -> p a
- (#>) :: SelectiveParser p => p a -> p b -> p b
- (<##>) :: SelectiveParser p => p a -> p (a -> b) -> p b
- (<-#) :: SelectiveParser p => p a -> p b -> p a
- (-#>) :: SelectiveParser p => p a -> p b -> p b
- (<-##>) :: SelectiveParser p => p a -> p (a -> b) -> p b
- (<#-) :: SelectiveParser p => p a -> p b -> p a
- (#->) :: SelectiveParser p => p a -> p b -> p b
- (<##->) :: SelectiveParser p => p a -> p (a -> b) -> p b
- class Functor f => FunctorFail f where
- fmapOrFail :: (a -> Either String b) -> f a -> f b
- fmapOrFailM :: MonadFail f => (a -> Either String b) -> f a -> f b
- (<$?>) :: FunctorFail f => (a -> Either String b) -> f a -> f b
- (<&?>) :: FunctorFail f => f a -> (a -> Either String b) -> f b
- class (Applicative f, FunctorFail f) => ApplicativeFail f where
Twice applicative parsers
class Alternative p => SelectiveParser p where Source #
A type p
is a SelectiveParser
if it offers functions <#>
, <-#>
,
<#->
, <-|>
, <|->
, eof
, standard Applicative
functions pure
and
<*>
, and Alternative
functions empty
and <|>
.
A selective parser handles a stream of tokens of some kind. In case of
Parser
and RawParser
, there are two
kinds of tokens: command line arguments and short flags (which are
characters inside command line arguments starting with one -
). Common to
all selective parsers is also a special EOF token that is received at the
end of the stream.
A selective parser looks at each token, including EOF, and makes one of these decisions:
- Skip the token, in which case the token may be consumed by a different parser. Hence the name selective parser: a parser only handles part of the stream and skips the rest.
Consume the token, in which case there are generally two subchoices:
- Finish the parse (and return a value of type
a
, or maybe an error). - Continue the parse, i.e. keep looking at more tokens. Not an option when handling EOF.
- Finish the parse (and return a value of type
Applicative
The meaning of Applicative
functions for selective parsers:
pure
a- A parser that finishes immediately before looking at any tokens
and produces the value
a
. f
<*>
x- Sequential application. The left hand side parser
f
runs first until it finishes and produces a value of type(a -> b)
. Then the right hand side parserx
starts running and looking at the remainder of the stream. Oncex
has finished and produced a value of typea
, the results off
andx
are combined into a final value of typeb
.
Alternative
empty
- A parser that skips all tokens, including EOF, and never finishes.
x
<|>
y- Alternative. The combined parser looks at each token and
determines if either of the subparsers wants to consume it (with
x
having priority overy
). When (say)x
has consumed the first token,y
is terminated, andx
gets to look at the rest of the stream. This implies that there is no backtracking. Once eitherx
ory
has cosnumed a token, the decision has been made and cannot be reversed. many
,some
,optional
- These functions from Control.Applicative
use
pure
as a fallback alternative. This doesn't work for selective parsers (seeorElse
), so this module provides its own replacements with the same names:many
,some
,optional
.
(<#>) :: p (a -> b) -> p a -> p b infixl 4 Source #
Parallel application. This runs two parsers in parallel. For each input
token, the left hand side (LHS) parser looks at it first. If the LHS
parser skips the token, the right hand side (RHS) parser gets a chance to
look at it too. Once both the LHS and RHS parsers have finished and
produced results of type (a -> b)
and a
respectively, the compound
parser also finishes and produces a combined result of type b
.
Together with the sequential application <*>
this operation makes
selective parsers "twice applicative".
(<-#>) :: p (a -> b) -> p a -> p b infixl 4 Source #
Left-interruptive application. This starts out in the same way as <#>
:
each token is presented to the left hand side (LHS) parser first, then to
the right hand side (RHS) parser if the LHS one has skipped it. However,
as soon as the RHS parser has consumed a token, the LHS parser is
terminated.
The LHS parser is terminated gracefully (it looks to the LHS parser as if it received EOF). The LHS result is stored, and the RHS parser continues to run alone. When the RHS parser finishes, the LHS and RHS results are combined.
In command line parsing, this is used by beforeDashes
.
The LHS in this case is the main options parser for an application, and
the RHS matches "--"
. If the user passes "--"
on the command line, it
looks like EOF to the main options parser.
Note: This operator is "natural". The actual inputs consumed by the LHS
parser always precede the ones consumed by the RHS in the stream. In this
sense <-#>
is similar to <*>
, which has the same property. The
difference between them is that in <*>
the RHS parser is "patient": it
doesn't start looking at inputs until the LHS parser has finished.
Contrast this with <-#>
, where the RHS is "impatient": the RHS starts
looking at inputs a.s.a.p. and has the ability to terminate the LHS.
(<#->) :: p (a -> b) -> p a -> p b infixl 4 Source #
Right-interruptive application. Similar to <-#>
but with the roles
reversed: once the left hand side (LHS) parser consumes a token, the right
hand side (RHS) is terminated as if by reaching EOF.
Note however that both in <-#>
and <#->
the LHS parser looks at each
token first, and the RHS parser gets to look at it only if the LHS one has
skipped it.
Note: this operator is "unnatural". The actual inputs consumed by the LHS parser will always come after the ones consumed by the RHS in the stream.
(<-|>) :: p a -> p a -> p a infixl 3 Source #
Left-interruptive alternative. This starts off running two parsers in parallel. For each input token, the left hand side (LHS) parser looks at it first. If the LHS parser skips a token, the right hand side (RHS) parser gets a chance to look at it.
If the LHS parser finishes before the RHS one consumes any tokens, the LHS parser "wins": its result becomes the result of the compound parser.
If, on the other hand, the RHS parser consumes a token before the LHS parser has finished, the LHS parser is terminated. The state of the LHS parser is discarded, all the tokens it has consumed are lost. The RHS parser takes over and gets to finish the parse.
In command line parsing, this is used by withHelp
to
implement the --help
flag. In this case the LHS is the main options
parser for an application, and the RHS matches "--help"
. If the user
passes "--help"
anywhere on the command line, the normal argument
parsing gets interrupted and help information is returned instead.
(<|->) :: p a -> p a -> p a infixl 3 Source #
Right-interruptive alternative. Similar to <-|>
but with the roles
reversed: if the left hand side (LHS) parser consumes a token before the
right hand side (RHS) one has finished, the RHS parser is terminated.
Note however that both in <-|>
and <|->
the LHS parser looks at each
token first, and the RHS parser gets to look at it only if the LHS one has
skipped it.
Skips all input tokens except EOF, then returns ()
. See also:
orElse
.
Zero or more. This will run the given parser repeatedly until EOF is reached and gather results in a list.
One or more. This will run the given parser repeatedly until EOF is reached and gather results in a list. Will refuse to consume EOF unless at least one item has been parsed.
optional :: p a -> p (Maybe a) Source #
Zero or one. This will try running the given parser, but will return
Nothing
if the parser never consumes any tokens.
Bounded number of items.
will try running the parser
between
l u xx
at least l
times in sequence, and fail if EOF is reached before l
items have been parsed.
Will finish the parse when either EOF is reached or u
items have been
parsed.
perm :: [p a] -> p [a] Source #
Permutation. Will try to parse one item by each of the given parsers, in any order.
Just like <|>
this doesn't offer any back-tracking. All the parsers will
get a chance to look at each input token one after another. When one of
the parsers consumes a token, that parser will run until it finishes the
parse. Then the remaining parsers will continue the process. When all the
parsers have produced a value the compound parser will finish and produce
a list of results. The returned items will be in the same order as they
appear in the stream.
In general, this is less convenient than simply combining all your parsers
with <#>
. However, you can use perm
if you care about the order in
which the items appear in the stream.
Instances
orElse :: SelectiveParser p => a -> p a Source #
Waits for EOF, then produces the given parse result. Intended usage is the
idiom to provide a fallback alternative for a chain of
parsers connected with <|>
orElse
<|>
.
p <|> orElse x
will run the parser p
, and if p
consumes any token (normal or EOF), then
p
will finish the parse. If p
doesn't consume any tokens at all and
never finishes then the alternative parser will produce the value x
upon
receiving EOF.
Note that orElse
has the same type as pure
: a -> p a
. They produce the
same parse result. However, unlike orElse
pure
finishes the parse
immediately, without waiting for EOF. This makes pure
unsuitable as a
fallback alternative most of the time.
applyMany :: SelectiveParser p => a -> p (a -> a) -> p a Source #
Convenience wrapper around many
. Will start with a given value of type
a
, and then will parse zero or more "updates" of type a -> a
. Updates
will be applied to the original value left-to-right until EOF is reached, at
which point the final updated value will be produced.
applySome :: SelectiveParser p => a -> p (a -> a) -> p a Source #
(<#) :: SelectiveParser p => p a -> p b -> p a infixl 4 Source #
Like <#>
but ignores the value produced by the right hand side parser.
(#>) :: SelectiveParser p => p a -> p b -> p b infixl 4 Source #
Like <#>
but ignores the value produced by the left hand side parser.
(<##>) :: SelectiveParser p => p a -> p (a -> b) -> p b infixl 4 Source #
(<-#) :: SelectiveParser p => p a -> p b -> p a infixl 4 Source #
Like <-#>
but ignores the value produced by the right hand side parser.
(-#>) :: SelectiveParser p => p a -> p b -> p b infixl 4 Source #
Like <-#>
but ignores the value produced by the left hand side parser.
(<-##>) :: SelectiveParser p => p a -> p (a -> b) -> p b infixl 4 Source #
(<#-) :: SelectiveParser p => p a -> p b -> p a infixl 4 Source #
Like <#->
but ignores the value produced by the right hand side parser.
(#->) :: SelectiveParser p => p a -> p b -> p b infixl 4 Source #
Like <#->
but ignores the value produced by the left hand side parser.
(<##->) :: SelectiveParser p => p a -> p (a -> b) -> p b infixl 4 Source #
Functors and Applicatives with failure
class Functor f => FunctorFail f where Source #
A functor with failure. Instances are expected to satisfy the laws:
Coordinated with fmap
:
fmapOrFail (Right . f) = fmap f
Composition:
fmapOrFail f . fmapOrFail g = fmapOrFail (g >=> f)
Additionally, if f
is a MonadFail
, it is expected that failure is the
same as the monadic fail
:
fmapOrFail Left (return err) = fail err
fmapOrFail :: (a -> Either String b) -> f a -> f b Source #
Like fmap
but with a possibility for failure. This applies a function
of type (a ->
to a value of type Either
String
b)f a
. If the
function returns a Left
, this represents failure, with the String
being a message describing the nature of the failure. In this case the
resulting value of type f b
should encapsulate this failure.
In command line parsing this is used by functions like
paramRead
to produce an unrecoverable failure when a
parameter cannot be parsed e.g. as an integer.
Instances
FunctorFail RawParser Source # | |
Defined in Options.OptStream.Raw | |
FunctorFail RawFollower Source # | |
Defined in Options.OptStream.Raw fmapOrFail :: (a -> Either String b) -> RawFollower a -> RawFollower b Source # | |
FunctorFail Parser Source # | |
Defined in Options.OptStream | |
FunctorFail Follower Source # | |
Defined in Options.OptStream |
fmapOrFailM :: MonadFail f => (a -> Either String b) -> f a -> f b Source #
Like liftM
but with a possibility for failure. Suitable as
a drop-in implementation of fmapOrFail
for monads.
(<$?>) :: FunctorFail f => (a -> Either String b) -> f a -> f b infixl 4 Source #
Operator form of fmapOrFail
. Provided for convenience.
(<&?>) :: FunctorFail f => f a -> (a -> Either String b) -> f b infixl 1 Source #
A version <$?>
with the arguments flipped. Provided for convenience.
class (Applicative f, FunctorFail f) => ApplicativeFail f where Source #
An Applicative
that is also a FunctorFail
. Instances are expected to
satisfy the law:
failA err = fmapOrFail Left (pure err)
This is also the default implementation of failA
.
Nothing