如何在Parsec中形成一个OR选项,可以匹配许多选项,但必须至少匹配一个选项?

时间:2013-01-21 12:18:24

标签: haskell parsec

我正在尝试使用Parsec包在Haskell中编写解析器。输入规则的一部分要求解析器匹配规则选项。在规则之外,可以匹配多个规则,但至少一个规则必须匹配,否则解析器将产生错误。

让我举个例子。假设我们有两个名为firstmore的Parsec规则。有可能:

  • 输入与first后跟more;
  • 相匹配
  • 输入仅匹配first;或那
  • 输入仅匹配more

在任何情况下,firstmore中至少有一个必须匹配。关于如何做到这一点的任何想法?我考虑过使用<|>,但如果我理解正确,它只会匹配规则的一个(即第一个成功的那个)。

修改

澄清:如果firstmore都匹配,则必须返回两者的结果。如果只有一个匹配,则另一个的返回值可以是Nothing之类的空值,但不允许Nothingfirst返回more。< / p>

3 个答案:

答案 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