3
expr = Ref('expr')
block = '{' + Repeat(expr) + '}'
expr = block | Token(re='[0-9]')
START = expr

这是使用lrparsingPython 模块的语法。该模块报告语法中没有冲突。

它无法解析{{0}}带有错误 的字符串lrparsing.ParseError: line 1 column 5: Got '}' when expecting __end_of_input__ while trying to match block in state 11

一步一步的栈状态是:

shift  '{'; ItemSet:5='{'
shift  '{'; ItemSet:5='{' ItemSet:5='{'
shift  /[0-9]/; ItemSet:4=/[0-9]/ ItemSet:5='{' ItemSet:5='{'
reduce '}'; ItemSet:4=/[0-9]/ -- ItemSet:7=expr ItemSet:5='{' ItemSet:5='{'
reduce '}'; ItemSet:7=expr -- ItemSet:9=expr ItemSet:5='{' ItemSet:5='{'
shift  '}'; ItemSet:11='}' ItemSet:9=expr ItemSet:5='{' ItemSet:5='{'

其中 afaik 意味着它正在移动{{0,然后在看到}减少0到 时expr,然后在没有先移动它的情况下} 再次减少,这让我的 bajeezus 感到困惑。

这是一个错误,还是我在做一些非常简单和愚蠢的事情?

如果这是我的语法,我将如何重构它来满足我永恒的热情和热情的欲望?如果这是一个错误,有人可以指导我使用一个与lrparsing最相似的语法的 python 模块吗

编辑:重构为:

blocks = Ref('blocks')
block = Ref('block')
expr = Ref('expr')
blocks = blocks + block | THIS*0 # THIS*0 is the idiomatic way of getting the empty string
block = '{' + expr + '}'
expr = blocks | Token(re='[0-9]')
START = expr

允许正确解析。我现在的问题是……为什么?我觉得lrparsing之前会向我抱怨任何解析问题......Repeat根本不像我期望的那样工作吗?

4

3 回答 3

4

解析作者在这里。正如 Serge 所说,这是一个错误,并已在 1.0.8 中修复。这只是因为 Serge 在 Source Forge 的跟踪器上报告了它——否则我不会知道。谢谢谢尔盖。

关于它的评论可能是一个错误,Repeat()暗示不理解 lrparsing 的作用。Lrparsing 是一个相当复杂的野兽。它可以让你以我希望 Python 程序员很自然的方式输入语法。然后它编译成 LR(1) 解析器生成器可以理解的东西,这是一系列产品。然后它从这些产生式中生成一个 LR(1) 解析表。最后,它将您的输入语言和解析表提供给 LR(1) 解析器以生成解析树。值得一提的是,错误出现在生成解析表的部分。

如果我看不到每个步骤会产生什么,那么调试这样一系列的转换对我来说几乎是不可能的。因此,lrparsing 具有repr_xxxx()显示每个步骤的输出的功能。第一个转换是解析你的语法。结果显示为repr_grammar()

<G> = START + __end_of_input__
START = expr
block = '{' + expr * () + '}'
expr = block | /[0-9]/

这看起来与问题中提出的原始语法非常相似。下一步是在产品中编译这些规则,这是 LR(1) 解析器生成器可以理解的。这些由以下人员打印repr_productions()

<G> = START __end_of_input__
START = expr
block = '{' '}'
block = '{' block.Sequence.Repeat '}'
block.Sequence.Repeat = expr
block.Sequence.Repeat = block.Sequence.Repeat expr
expr = block
expr = /[0-9]/

block.Sequence.Repeat是为了处理Repeat(). 这些作品对我来说就像是原始语法的忠实代表。

Lrparsing 不遗余力地隐藏它引入的非终结符,例如block.Sequence.Repeat. 例如,它们不会出现在输出解析树中。这意味着 lrparsing 用户无需关心它们 - 除了 2 种情况。这两种情况是错误恢复和试图理解解析引擎的日志输出。前者是一项最复杂的技术,大多数人不会尝试。但是这里的一些人着眼于后者,以尝试了解 lrparsing 在做什么。除非您可以看到 LR(1) 解析器试图识别的产品,否则该日志将没有多大意义。但如果你看过它们,你就会知道Repeat().

您还可以转储生成的 LR(1) 解析表。如果您真的想了解 LR(1) 解析器的工作原理,那么您应该尝试去了解它。除非你碰巧发现解析一个非常有趣的话题,否则我不推荐它。

于 2014-08-24T17:26:39.103 回答
2

我在浏览此页面时向 lrparsing 作者报告了您的问题 - 这确实是一个错误,并且已在版本 1.0.8 中修复。如果将来有用,可以在此处找到 lrparsing 错误跟踪器。

于 2014-08-22T22:32:29.143 回答
1

lrparsing 有一个错误;它没有正确考虑递归重复。

您的实际问题可以通过简单的递归来解决,就像您在扩展编辑中所做的那样,但不会那么混乱。

block = Ref('block')
block = '{' + block + '}' | Token(re='[0-9]')
START = block

另请注意,您的原始语法将允许输入,例如{{0{1}}}. (原因是可重复的部分可以扩展为简单的数字或expr再次。)考虑到您的第二种语法,您可能无论如何都不想这样做。

我确实使用pyparsing完成了一些工作,但语法有很大不同。类似的例子:

from pyparsing import Forward, Literal, nums, oneOf, Word

l = lambda c: Literal(c).suppress()
block = Forward()
block << (Word(nums, exact=1) ^ l('{') + block + l('}'))
print(block.parseString("{{0}}"))

输出:

['0']

希望有帮助。

于 2014-01-05T02:56:08.190 回答