我有一个基本的命令add,它接受两种类型的参数:一个单词或一个标签。标签只是一个以+开头的单词。一个单词就是一个String。它可以包含至少一个参数(为此我使用some )。
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命令没有收到参数一样,但是我不知道该怎么做。
发布于 2020-07-31 02:16:28
parseDescAndTags目前是一个纯函数,所以它不可能导致解析失败。为了解决这个问题,我还应该注意到在此代码中:
Add <$> parseDescAndTags <$> partition isTag <$> some (argument str (metavar "DESC"))运算符<$>被声明为infixl 4,因此它是左关联的,因此您的表达式等同于:
((Add <$> parseDescAndTags) <$> partition isTag) <$> some (argument str (metavar "DESC"))您恰好在“函数阅读器”函数器(->) a中使用了<$>,它相当于组合(.)
Add . parseDescAndTags . partition isTag <$> some (argument str (metavar "DESC"))如果要使用ReadM,则需要使用eitherReader等函数来构造ReadM操作。但问题是,您需要使用它作为argument的第一个参数,而不是str读取器,这是错误的位置,因为some位于外部,并且您希望根据整个选项的累积结果失败解析。
不幸的是,这种上下文相关的解析并不是optparse-applicative的设计目标;它没有为解析器提供Monad实例。
目前,您的解析器允许标签和描述交错,如下所示(为了说明起见,假设使用isTag = (== ".") . take 1 ):
add some .tag1 description .tag2 text为描述生成"some description text",并将[".tag1", ".tag2"]作为标记。这是你想要的吗,或者你可以使用一种更简单的格式,比如要求所有的标签都放在末尾?
add some description text .tag1 .tag2如果是这样,结果很简单:用some解析至少一个非标签,然后用many解析任意数量的标签
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记录进行更复杂的验证。然后,如果要手动打印帮助文本,可以使用:
printHelp :: ParserPrefs -> ParserInfo a -> IO a
printHelp parserPrefs parserInfo = handleParseResult $ Failure
$ parserFailure parserPrefs parserInfo ShowHelpText memptyhttps://stackoverflow.com/questions/63177527
复制相似问题