首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Optparse-applicative:连续解析(ReadM)

Optparse-applicative:连续解析(ReadM)
EN

Stack Overflow用户
提问于 2020-07-31 00:33:00
回答 1查看 101关注 0票数 1

我有一个基本的命令add,它接受两种类型的参数:一个单词或一个标签。标签只是一个以+开头的单词。一个单词就是一个String。它可以包含至少一个参数(为此我使用some )。

代码语言:javascript
复制
data Arg = Add AddOpts

data AddOpts = AddOpts
  { desc :: String,
    tags :: [String]
  }
  deriving (Show)

addCommand :: Mod CommandFields Arg
addCommand = command "add" (info parser infoMod)
  where
    infoMod = progDesc "Add a new task"
    parser = Add <$> parseDescAndTags <$> partition isTag <$> some (argument str (metavar "DESC"))
    parseDescAndTags (_, []) = FAIL HERE
    parseDescAndTags (tags, desc) = AddOpts (unwords desc) (map tail tags)

我想添加另一个规则:add命令应该至少接收一个单词(但不能有0个或更多标签)。为此,我需要在第一次解析单词列表后进行检查。如果它是空的,我希望失败,就好像add命令没有收到参数一样,但是我不知道该怎么做。

EN

回答 1

Stack Overflow用户

发布于 2020-07-31 02:16:28

parseDescAndTags目前是一个纯函数,所以它不可能导致解析失败。为了解决这个问题,我还应该注意到在此代码中:

代码语言:javascript
复制
Add <$> parseDescAndTags <$> partition isTag <$> some (argument str (metavar "DESC"))

运算符<$>被声明为infixl 4,因此它是左关联的,因此您的表达式等同于:

代码语言:javascript
复制
((Add <$> parseDescAndTags) <$> partition isTag) <$> some (argument str (metavar "DESC"))

您恰好在“函数阅读器”函数器(->) a中使用了<$>,它相当于组合(.)

代码语言:javascript
复制
Add . parseDescAndTags . partition isTag <$> some (argument str (metavar "DESC"))

如果要使用ReadM,则需要使用eitherReader等函数来构造ReadM操作。但问题是,您需要使用它作为argument的第一个参数,而不是str读取器,这是错误的位置,因为some位于外部,并且您希望根据整个选项的累积结果失败解析。

不幸的是,这种上下文相关的解析并不是optparse-applicative的设计目标;它没有为解析器提供Monad实例。

目前,您的解析器允许标签和描述交错,如下所示(为了说明起见,假设使用isTag = (== ".") . take 1 ):

代码语言:javascript
复制
add some .tag1 description .tag2 text

为描述生成"some description text",并将[".tag1", ".tag2"]作为标记。这是你想要的吗,或者你可以使用一种更简单的格式,比如要求所有的标签都放在末尾?

代码语言:javascript
复制
add some description text .tag1 .tag2

如果是这样,结果很简单:用some解析至少一个非标签,然后用many解析任意数量的标签

代码语言:javascript
复制
addCommand :: Mod CommandFields Arg
addCommand = command "add" (info parser infoMod)
  where
    infoMod = progDesc "Add a new task"
    parser = Add <$> addOpts
    addOpts = AddOpts
      <$> (unwords <$> some (argument nonTag (metavar "DESC")))
      <*> many (argument tag (metavar "TAG"))

    nonTag = eitherReader
      $ \ str -> if isTag str
        then Left ("unexpected tag: '" <> str <> "'")
        else Right str

    tag = eitherReader
      $ \ str -> if isTag str
        then Right $ drop 1 str
        else Left ("not a tag: '" <> str <> "'")

作为另一种选择,您可以使用optparse-applicative解析命令行选项,但是在运行解析器之后对options记录进行更复杂的验证。然后,如果要手动打印帮助文本,可以使用:

代码语言:javascript
复制
printHelp :: ParserPrefs -> ParserInfo a -> IO a
printHelp parserPrefs parserInfo = handleParseResult $ Failure
  $ parserFailure parserPrefs parserInfo ShowHelpText mempty
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63177527

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档