我正在尝试使用Parsec包在Haskell中编写解析器。输入规则的一部分要求解析器匹配规则选项。在规则之外,可以匹配多个规则,但至少一个规则必须匹配,否则解析器将产生错误。
让我举个例子。假设我们有两个名为first
和more
的Parsec规则。有可能:
first
后跟more
; first
;或那more
。在任何情况下,first
或more
中至少有一个必须匹配。关于如何做到这一点的任何想法?我考虑过使用<|>
,但如果我理解正确,它只会匹配规则的一个(即第一个成功的那个)。
修改
澄清:如果first
和more
都匹配,则必须返回两者的结果。如果只有一个匹配,则另一个的返回值可以是Nothing
之类的空值,但不允许Nothing
和first
返回more
。< / p>
答案 0 :(得分:4)
假设事情应该是某种特定的顺序:
atLeastOne :: [Parser a] -> Parser [a]
atLeastOne ps = do
rs <- catMaybes <$> mapM optionMaybe ps
if null rs
then parserFail "At least one thing should be present"
else return rs
答案 1 :(得分:1)
真正天真的方式是这样的:
oneOrBoth first_ more_ = try both <|> first <|> more where
first = (:[]) <$> first_
more = (:[]) <$> more_
both = liftM2 (++) first more
这将生成一个长度为一个或两个的列表,并且在可能的情况下优先生成长度为2的列表。
答案 2 :(得分:0)
它不像其他答案一般,但这可以解决你的问题:
atLeastOne :: ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m (Maybe a, Maybe b)
atLeastOne p1 p2 = firstMatches <|> secondMatches <|> fail "Invalid input"
where
firstMatches = do
a <- p1
maybeB <- (p2 >>= Just <|> return Nothing)
return (Just a, maybeB)
secondMatches = do
b <- p2
return (Nothing, Just b)
用法:
atLeastOne first more
稍后编辑:
或更类型安全的版本:
data Choice3 a b c = Choice1Of3 a | Choice2Of3 b | Choice3Of3 c
atLeastOne :: ParsecT s u m a -> ParsecT s u m b -> ParsecT s u m (Choice1Of3 a b (a, b))
atLeastOne p1 p2 = firstMatches <|> secondMatches <|> fail "Invalid input"
where
firstMatches = do
a <- p1
(p2 >>= \b -> Choice3Of3 (a, b)) <|> Choice1Of3 a
secondMatches = do
b <- p2
return $ Choice2Of3 b