0

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

让我举个例子。假设我们有两个名为first和的 Parsec 规则more。它可能是:

  • 输入匹配first后跟more;
  • 输入仅匹配first;或者那个
  • 输入仅匹配more

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

编辑:

澄清:如果两者都first匹配more,则必须返回两者的结果。如果只有一个匹配,另一个的返回值可以是一些空值,如Nothing,但不允许同时Nothing返回firstmore

4

3 回答 3

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
于 2013-01-21T12:43:32.150 回答
1

真正天真的方式是这样的:

oneOrBoth first_ more_ = try both <|> first <|> more where
    first = (:[]) <$> first_
    more  = (:[]) <$> more_
    both  = liftM2 (++) first more

这将生成长度为 1 或 2 的列表,并在可能的情况下优先生成长度为 2 的列表。

于 2013-01-21T16:39:37.667 回答
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
于 2013-01-21T12:51:59.833 回答