optstream-0.1.1.0: Command line option parsing library with a twice applicative interface
Copyright(c) Dan Shved 2022
LicenseBSD-3
Maintainerdanshved@gmail.com
Stabilityexperimental
Safe HaskellSafe-Inferred
LanguageHaskell2010

Options.OptStream.Classes

Description

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

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.

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 parser x starts running and looking at the remainder of the stream. Once x has finished and produced a value of type a, the results of f and x are combined into a final value of type b.

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 over y). When (say) x has consumed the first token, y is terminated, and x gets to look at the rest of the stream. This implies that there is no backtracking. Once either x or y 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 (see orElse), so this module provides its own replacements with the same names: many, some, optional.

Minimal complete definition

(<#>), (<-#>), (<#->), (<-|>), (<|->), eof

Methods

(<#>) :: 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.

eof :: p () Source #

Skips all input tokens except EOF, then returns (). See also: orElse.

many :: p a -> p [a] Source #

Zero or more. This will run the given parser repeatedly until EOF is reached and gather results in a list.

some :: p a -> p [a] Source #

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.

between Source #

Arguments

:: Int

Lower bound (l).

-> Int

Upper bound (u).

-> p a 
-> p [a] 

Bounded number of items. between l u x will try running the parser x 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

Instances details
SelectiveParser RawParser Source # 
Instance details

Defined in Options.OptStream.Raw

SelectiveParser Parser Source # 
Instance details

Defined in Options.OptStream

Methods

(<#>) :: Parser (a -> b) -> Parser a -> Parser b Source #

(<-#>) :: Parser (a -> b) -> Parser a -> Parser b Source #

(<#->) :: Parser (a -> b) -> Parser a -> Parser b Source #

(<-|>) :: Parser a -> Parser a -> Parser a Source #

(<|->) :: Parser a -> Parser a -> Parser a Source #

eof :: Parser () Source #

many :: Parser a -> Parser [a] Source #

some :: Parser a -> Parser [a] Source #

optional :: Parser a -> Parser (Maybe a) Source #

between :: Int -> Int -> Parser a -> Parser [a] Source #

perm :: [Parser a] -> Parser [a] Source #

orElse :: SelectiveParser p => a -> p a Source #

Waits for EOF, then produces the given parse result. Intended usage is the <|> orElse idiom to provide a fallback alternative for a chain of parsers connected with <|>.

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 #

Convenience wrapper around some. Like applyMany but will insist on parsing at least one update of type a -> a. If no update items are parsed from the input then applySome will refuse to consume EOF.

(<#) :: 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 #

Like <#> but with the types of the arguments swappped. Note that a <##> f is not the same as f <#> a. In both <#> and <##> the left hand side parser looks at each input token before the right hand side parser.

(<-#) :: 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 #

Like <-#> but with the types of the arguments swappped. Note that a <-##> f is not the same as f <#-> a. In both cases the parser a will be gracefully terminated once f consumes an input. However, in a <-##> f it is a that gets the first look at each input token, whereas in f <#-> a it is f.

(<#-) :: 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 #

Like <#-> but with the types of the arguments swappped. Note that a <##-> f is not the same as f <-#> a. In both cases the parser f will be gracefully terminated once a consumes an input. However, in a <##-> f it is a that gets the first look at each input token, whereas in f <-#> a it is f.

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

Methods

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 -> Either String b) to a value of type 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

Instances details
FunctorFail RawParser Source # 
Instance details

Defined in Options.OptStream.Raw

Methods

fmapOrFail :: (a -> Either String b) -> RawParser a -> RawParser b Source #

FunctorFail RawFollower Source # 
Instance details

Defined in Options.OptStream.Raw

Methods

fmapOrFail :: (a -> Either String b) -> RawFollower a -> RawFollower b Source #

FunctorFail Parser Source # 
Instance details

Defined in Options.OptStream

Methods

fmapOrFail :: (a -> Either String b) -> Parser a -> Parser b Source #

FunctorFail Follower Source # 
Instance details

Defined in Options.OptStream

Methods

fmapOrFail :: (a -> Either String b) -> Follower a -> Follower b Source #

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.

Minimal complete definition

Nothing

Methods

failA :: String -> f a Source #

Unconditional failure.

Instances

Instances details
ApplicativeFail RawParser Source # 
Instance details

Defined in Options.OptStream.Raw

Methods

failA :: String -> RawParser a Source #

ApplicativeFail RawFollower Source # 
Instance details

Defined in Options.OptStream.Raw

ApplicativeFail Parser Source # 
Instance details

Defined in Options.OptStream

Methods

failA :: String -> Parser a Source #

ApplicativeFail Follower Source # 
Instance details

Defined in Options.OptStream

Methods

failA :: String -> Follower a Source #