expr = Ref('expr')
block = '{' + Repeat(expr) + '}'
expr = block | Token(re='[0-9]')
START = expr
这是使用Python的lrparsing
模块的语法。该模块报告语法中没有冲突。
无法使用错误解析字符串{{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混淆了我。
这是一个错误,还是我在做一些无限简单和愚蠢的事情?
如果这是我的语法,我将如何重构它以满足我永恒的热情和热情的欲望?如果它是一个错误,有人可以指引我使用语法最相似的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
根本没有按照我预期的方式工作吗?
答案 0 :(得分:4)
Lrparsing作者在这里。正如塞尔说这是一个错误,并在1.0.8修复。这只发生是因为Serge在Source Forge上报告了它,但是追踪者 - 否则我不会知道。谢谢Serge。
关于它的评论可能是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。这些作品看起来像是对我原始语法的忠实表达。
Lrparsing摒弃了它引入的非终结者block.Sequence.Repeat
。例如,它们不会出现在输出解析树中。这意味着没有必要让lrparsing用户关心它们 - 除了2个案例。这2个案例是错误恢复并试图理解解析引擎的日志输出。前者是一种最不会尝试的复杂技术。但有些人在这里看了后者,试图了解lrparsing正在做什么。除非你能看到LR(1)解析器试图识别的产品,否则日志不会有多大意义。但如果你看过它们,你就会知道Repeat()
中没有错误。
您也可以转储生成的LR(1)解析表。如果您 确实 想要了解LR(1)解析器的工作原理,那就是您应该尝试解决的问题。除非您碰巧找到解析一个非常有趣的话题,否则我不推荐它。
答案 1 :(得分:2)
我在浏览此页面时向lrparsing作者报告了您的问题 - 这确实是一个错误,并且已在版本1.0.8中修复。如果它在将来有用,可以找到lrparsing bug跟踪器here。
答案 2 :(得分:1)
lrparsing有一个bug;它没有正确考虑递归重复。
您的实际问题可以通过简单的递归来解决,就像您在扩展编辑中所做的那样,虽然杂乱少了。
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']
希望有所帮助。