我喜欢这个图书馆的简朴。遗憾的是,我还没有弄清楚如何创建递归定义:
考虑这个最小的人为例子:

import parsec as psc
digit = psc.regex("[0-9]")
number = lambda: digit ^ (digit + number())
_ = number().parse("42")

正如所料,number()的计算是无限递归的:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
  File "<stdin>", line 1, in <lambda>
  File "<stdin>", line 1, in <lambda>
  [Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded

我知道在这个具体的例子中,可以通过将第3行改为
number = lambda: psc.many1(digit)

但我不确定一般情况下都有可能。
是否可以将数字的解析递归为
<number> ::= <digit><number> | <digit>

最佳答案

(更新)免责声明:正如我们在评论中发现的,这只适用于3.3以上的parsec.py版本(截至2018年8月),这是PyPI上可用的最新版本,因此现在您必须从GitHub手动安装开发版本才能使此解决方案工作。
更新2:parsec.py v3.4终于发布并修复了该问题。
在这种情况下,我发现“手动扩展”parsec提供的原语并编写一个自己的“低级”解析器(即一个接受textindex参数的解析器,而不是一个由parsec原语构建的解析器)通常是有帮助的,只是看看发生了什么。通过从parsec源代码中获取try_choice^)的定义并手动插入表达式1的组成部分,我们可以编写一个解析器,我认为它可以满足您的需要:

import parsec as psc
digit = psc.regex("[0-9]")

def number():
  @psc.Parser
  def number_parser(text, index):
    res = (digit + number())(text, index)
    return res if res.status else digit(text, index)
  return number_parser

_ = number().parse("42423")
print(_)

输出:
('4', ('2', ('4', ('2', '3'))))

当然,这是因为“parser returning function”number()只能在实际的解析器中有条件地调用,而不是(无条件地)在其自身中调用。
一种更简单的方法是完全不使用“parser returning function”,而直接编写解析器本身:
@psc.Parser
def number(text, index):
  return ((digit + number) ^ digit)(text, index)

用法从number().parse("42423")变为number.parse("42423"),但它的作用是一样的。
最后,parsec.py中是否有一些功能允许您在不使用显式textindex参数的情况下执行此操作,从而使解析器完全由其他parsec.py原语构建?结果发现parsec.generate很适合这个法案!从它的in-source documentation
构造解析器的最强大的方法是使用
装饰工。generate从一个
应该产生分析器。这些解析器依次应用
结果使用.send()协议发送回生成器。这个
生成器应返回结果或另一个解析器,即
相当于应用它并返回其结果。
您可以找到示例用法here,从中我们可以清楚地看到:
@psc.generate
def number():
  r = yield (digit + number) ^ digit
  return r

这是因为除了它所做的所有花哨的生成器魔术之外,generatedecorator返回一个函数,该函数本身用@parsec.Parser修饰(参见上面链接的源代码),因此得到的完全修饰的number函数将与第二个解决方案中的函数具有相同的类型。因此,我们可以以与digit等相同的方式使用它,而不必调用它或任何东西,这就是我们在yield表达式中所做的。
用法又是number.parse("42423"),它返回的结果与其他两个解决方案相同。
也许有更好的解决办法,但这是我能想到的。
我不得不把顺序从digit ^ (digit + number())颠倒到(digit + number()) ^ digit,因为第一个只返回第一个数字,然后认为自己完成了。我希望这样可以吗?

10-08 12:47