5

我一直在编写一个 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

谢谢。

4

1 回答 1

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_v类似于parser a >>= recursiveParser parser
  • many_v类似于recursiveParser parser

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

于 2011-06-20T00:41:10.147 回答