使attoparsec解析器递归

时间:2011-06-19 19:56:55

标签: parsing haskell idioms attoparsec

我一直在编写一个attoparsec解析器,并且已经遇到了一种模式,我希望将解析器转换为递归解析器(递归地将它们与monad bind>> =运算符组合在一起)。

所以我创建了一个函数来将解析器转换为递归解析器,如下所示:

recursiveParser :: (a -> A.Parser a) -> a -> A.Parser a
recursiveParser parser a = (parser a >>= recursiveParser parser) <|> return a

如果您有像

这样的递归数据类型,那么这很有用
data Expression = ConsExpr Expression Expression | EmptyExpr

parseRHS :: Expression -> Parser Expression
parseRHS e = ConsExpr e <$> parseFoo

parseExpression :: Parser Expression
parseExpression = parseLHS >>= recursiveParser parseRHS
  where parseLHS = parseRHS EmptyExpr

是否有更惯用的解决方案?几乎看起来recursiveParser应该是某种折叠...我在文档中也看到sepBy,但这种方法似乎更适合我的应用。

编辑:哦,实际上现在我认为它实际上应该与fix类似......不知道我是怎么忘记的。

EDIT2: Rotsor以他的替代方案为我的例子提出了一个很好的观点,但我担心我的AST实际上比这更复杂。它实际上看起来更像这样(虽然这仍然是简化的)

data Segment = Choice1 Expression
             | Choice2 Expression
data Expression = ConsExpr Segment Expression 
                | Token String
                | EmptyExpr

其中字符串a -> b位于右侧,c:d括号位于左侧,:绑定比->更紧密。

即。 a -> b评估为

(ConsExpr (Choice1 (Token "a")) (Token "b"))

c:d评估为

(ConsExpr (Choice2 (Token "d")) (Token "c"))

我想我可以使用foldl作为另一个foldr而另一个使用"a:b:c -> e:f -> :g:h ->"但是那里还有更多的复杂性。请注意,它以略微奇怪的方式递归,因此"-> a"实际上是有效字符串,但"b:"fix不是。最后fixParser :: (a -> A.Parser a) -> a -> A.Parser a fixParser parser a = (parser a >>= fixParser parser) <|> pure a 对我来说似乎更简单。我已经重命名了这样的递归方法:

{{1}}

感谢。

1 个答案:

答案 0 :(得分:4)

为什么不解析一个列表并将其折叠成您想要的任何内容? 也许我错过了一些东西,但这对我来说更自然:

consChain :: [Expression] -> Expression
consChain = foldl ConsExpr EmptyExpr

parseExpression :: Parser Expression
parseExpression = consChain <$> many1 parseFoo

而且它也更短。

正如您所看到的,consChain现在独立于解析,并且可以在其他地方使用。此外,如果您将结果折叠分开,则在某些情况下,稍微不直观的递归解析会简化为manymany1

您可能还想了解many的实施方式:

many :: (Alternative f) => f a -> f [a]
many v = many_v
    where many_v = some_v <|> pure []
          some_v = (:) <$> v <*> many_v

与您的recursiveParser

有许多共同之处
  • some_vparser a >>= recursiveParser parser
  • 类似
  • many_vrecursiveParser parser
  • 类似

你可能会问我为什么称你的递归解析器函数不直观。这是因为这种模式允许解析器参数影响解析行为(a -> A.Parser a,记得吗?),这可能很有用,但不是很明显(我还没有看到用例)。您的示例不使用此功能这一事实使其看起来多余。