optparse-Applicative: список парсинга

Я пытаюсь разобрать список пар с помощью optparse-Applicative. Разбор одной пары работает, но анализ произвольного количества пар с помощью комбинатора many не выполняется.

import           Options.Applicative

pairParser = (,) <$> argument str (metavar "s1")
                 <*> argument str (metavar "s2")

testParser p = getParseResult . execParserPure (prefs idm)
  (info (helper <*> p) fullDesc)

main = do
  print $ testParser pairParser ["one", "two"]
  print $ testParser (many pairParser) []
  print $ testParser (many pairParser) ["one", "two"]
  print $ testParser (many pairParser) ["one", "two", "three", "four"]

Выход:

Just ("one","two")   <- good
Just []              <- still good
Nothing              <- does not work
Nothing              <- also does not work

Любые идеи?


person gedenkt    schedule 20.07.2015    source источник


Ответы (2)


Отказ от ответственности: у меня нет опыта применения продвинутых приемов optparse-аппликативности, поэтому я могу упустить что-то очевидное. Читатели: укажите на это, если это так.

Ваша проблема в том, что many (в описании, описанном вручную) применяет синтаксический анализатор к каждому фрагменту ввода, причем фрагменты в этом случае состоят из отдельных аргументов, а затем собирает результаты. Итак, many pairParser применяет pairParser к ["one"], а затем к ["two"], и оба анализа завершаются неудачно. В этом случае вы можете заменить _ 6_ с функцией, которая соответствующим образом разбивает аргументы и соответствующим образом корректирует остальную часть программы, или (что, как я подозреваю, это более простой выбор) отказаться от pairParser и просто обработать проанализированные аргументы пост-обработкой, поскольку в:

pairArgs :: [a] -> [(a, a)]
pairArgs = noLeftover . foldr pairNext (Nothing, [])
    where
    noLeftover (m, ps) = case m of
        Nothing -> ps
        _       -> []
    pairNext x (m, ps) = case m of
        Just y  -> (Nothing, (x, y) : ps)
        Nothing -> (Just x, ps)

manyPairsParser :: Parser [(String, String)]
manyPairsParser = pairArgs <$> many (argument str (metavar "s1 s2.."))
GHCi> testParser manyPairsParser []
Just []
GHCi> testParser manyPairsParser ["foo"]
Just []
GHCi> testParser manyPairsParser ["foo","bar"]
Just [("foo","bar")]
GHCi> testParser manyPairsParser ["foo","bar","baz"]
Just []
GHCi> testParser manyPairsParser ["foo","bar","baz","quux"]
Just [("foo","bar"),("baz","quux")]

(Обратите внимание, что в приведенной выше демонстрации я обрабатываю сбой, возвращая пустой список пар и учитывая, что нечетное количество аргументов должно привести к сбою. Вам нужно будет внести некоторые изменения, если вы хотите другое поведение.)

person duplode    schedule 20.07.2015
comment
Спасибо! Я считаю, что поведение many действительно сбивает с толку, поскольку я ожидал, что он будет работать больше как Parsec ... Существует ли синтаксический анализатор командной строки в стиле Parsec? - person gedenkt; 21.07.2015
comment
@gedenkt Не знаю. Такие библиотеки, как optparse-applicative, оптимизированы и специализированы для обработки более обычных вариантов использования с передачей аргументов и опций. Кстати, вы можете попробовать еще одну вещь (хотя я ее не тестировал) - это указать собственный формат для ваших пар, например {item1,item2} вместо item1 item2. Я считаю, что пример FluxCapacitor в readme предлагает способ достижения этого с optparse-applicative. - person duplode; 22.07.2015

Многие и некоторые были слишком нетерпеливы в старых версиях optparse и потребовали бы, чтобы вся конструкция была успешной после единственной опции, прежде чем разрешать дальнейшие опции.

Я изменил логику для многих, и поэтому они более ленивы в том, как они используют параметры, которых больше многих, и некоторых. Вы можете увидеть изменения и логику здесь.

person Huw    schedule 14.06.2018