optparse-applicative icon indicating copy to clipboard operation
optparse-applicative copied to clipboard

combining parsers with overlapping flags

Open aavogt opened this issue 11 years ago • 5 comments
trafficstars

Could optparse-applicative provide more help when combining parsers that have the same flag names. For example:

mapOptName  :: (OptName -> OptName) -> Parser a -> Parser a
mapOptName f (OptP x) = OptP (mapOption x)
  where
    mapOption :: Option a -> Option a
    mapOption (Option r p) = Option (mapReader r) p

    mapReader :: OptReader a -> OptReader a
    mapReader (OptReader n x y) = OptReader (map f n) x y
    mapReader (FlagReader n a) = FlagReader (map f n) a
    mapReader x = x
mapOptName f (MultP x y) = MultP (mapOptName f x) (mapOptName f y)
mapOptName f (AltP x y) = AltP (mapOptName f x) (mapOptName f y)
mapOptName f (BindP x y) = BindP (mapOptName f x) (mapOptName f . y)
mapOptName f x = x

addPrefix p (OptShort c) = OptLong (p ++ "-" ++ [c])
addPrefix p (OptLong c) = OptLong (p ++ "-" ++ c)

Lets me write something like:

step :: Parser Step
step = ...

steps :: Parser (Step, Step)
steps = (,) <$> mapOptName (addPrefix "step1") step
   <*> mapOptName (addPrefix "step2") step

steps accepts arguments like --step1-x --step2-x if step accepts -x. Ideally the prefix could be left out when there is no conflict, but I think BindP gets in the way of defining a renameOptNames :: ([OptName] -> [OptName]) -> Parser a -> Parser a that sees all OptName at once.

uu-options supports +blah -blah delimiters. See section 5.2 of their techreport

aavogt avatar May 29 '14 19:05 aavogt

I'm not sure what feature you are requesting here. Can you elaborate? See also #77.

pcapriotti avatar Aug 18 '14 13:08 pcapriotti

I want something(s) better than the mapOptName pasted above. Maybe these examples help:

import Options.Applicative; import Options.Applicative.Internal; import Options.Applicative.Types
import Data.Monoid

mapOptName  :: (OptName -> OptName) -> Parser a -> Parser a
addPrefix :: String -> OptName -> OptName
-- both are defined above

step1 = (,) <$> switch (long "step1specific") <*> switch (short 'x')
step2 = (,) <$> switch (long "step2specific") <*> switch (short 'x')


steps = (,) <$> mapOptName (addPrefix "step1") step1
    <*> mapOptName (addPrefix "step2") step2


run str = execParserPure (prefs mempty) (info steps mempty) str
{- ^

>>> run ["--step1-step1specific","--step2-step2specific","--step1-x","--step2-x"]
Success ((True,True),(True,True))

The above code works. I would like if it were possible to
make the following examples work without needing to make
substantial changes to the parsers.

>>> run ["--step1specific","--step2specific","--step2-x"]
Success ((True,False),(True,True))

>>> run ["-x"]
Success ((False,True),(False,True))

>>> run ["+step1","-x","-step1","--step2specific"]
Success ((False,True),(True,False))

-}

aavogt avatar Aug 18 '14 15:08 aavogt

I see. This is a sensible request, as the current semantics of sequencing of parsers with overlapping option names is a bit unsatisfactory (the second option cannot be parsed until the first one is).

I think the proper way to handle this is to add a namespace field to OptName, and then take it into account when parsing. Then we could make "+foo" and "-foo" set and unset the current namespace respectively.

I'm not sure I'll be able to implement this right away, but patches are (as always) welcome :)

pcapriotti avatar Aug 18 '14 16:08 pcapriotti

Hi,

I am also worried about current behavior. I have short name flag in a subcommand parser with same name as on level above. The flag from a subcommand parser is not available. Validation for top level flag is triggered and it terminates my program. I would like to see at least warning when short name flag is used and help produced by the library could remove short name for subcommand if it is already used and enforce user to use long version.

yaitskov avatar Sep 29 '21 14:09 yaitskov

So I'm going to guess you're in subparser inline mode. If you move the flag to after the definitions of the commands it should work for you, as the inlined parser will be searched first.

HuwCampbell avatar Oct 07 '21 05:10 HuwCampbell