optparse-applicative Backtracking

2019-02-25 04:03发布

问题:

I'm trying to use the optparse-applicative library in an program which should perform a different action depending on the number of arguments.

For example, the argument parsing for a program which calculates perimeters:

module TestOpts where

import Options.Applicative

type Length = Double

data PerimeterCommand
    = GeneralQuadranglePerimeter Length Length Length Length
    | RectanglePerimeter Length Length

parsePerimeterCommand :: Parser PerimeterCommand
parsePerimeterCommand = parseQuadPerimeter <|> parseRectPerimeter

parseQuadPerimeter = GeneralQuadranglePerimeter <$>
                     parseLength "SIDE1" <*>
                     parseLength "SIDE2" <*>
                     parseLength "SIDE3" <*>
                     parseLength "SIDE4"

parseRectPerimeter = RectanglePerimeter <$>
                     parseLength "WIDTH" <*> parseLength "HEIGHT"

parseLength name = argument auto (metavar name)

Only the first argument to <|> will ever successfully parse. I think some kind of argument backtracking is required, similar to Parsec's try combinator.

Any ideas on how to parse alternative sets of arguments, when the first alternative may consume some arguments of the next alternative?

回答1:

Please note: this answer was written by the optparse-applicative author, Paolo Capriotti.

You can't do this with optparse-applicative directly. The main feature of optparse-applicative is that options can be parsed in any order. If you want to work mainly with arguments (which are positional), you are better off having two levels of parsers: use many argument in optparse-applicative, then pass the resulting array to a normal parser (say using Parsec). If you only have positional arguments, then optparse-applicative won't buy you very much, and you could just parse the arguments manually with Parsec.