'ZeroOrMore' 的明显 pyparsing 错误

apparent pyparsing bug with 'ZeroOrMore'

我在 mac 上使用 python 3.6.5 的 pyparsing。以下代码在第二次解析时崩溃:

from pyparsing import *

a = Word(alphas) + Literal(';')
b = Word(alphas) + Optional(Literal(';'))
bad_parser = ZeroOrMore(a) + b

b.parseString('hello;')
print("no problems yet...")
bad_parser.parseString('hello;')
print("this will not print because we're dead")

这是符合逻辑的行为吗?或者这是一个错误?


编辑:这是完整的控制台输出:

no problems yet...
Traceback (most recent call last):
  File "test.py", line 9, in <module>
    bad_parser.parseString('hello;')
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py", line 1632, in parseString
    raise exc
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py", line 1622, in parseString
    loc, tokens = self._parse( instring, 0 )
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py", line 1379, in _parseNoCache
    loc,tokens = self.parseImpl( instring, preloc, doActions )
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py", line 3395, in parseImpl
    loc, exprtokens = e._parse( instring, loc, doActions )
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py", line 1379, in _parseNoCache
    loc,tokens = self.parseImpl( instring, preloc, doActions )
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyparsing.py", line 2689, in parseImpl
    raise ParseException(instring, loc, self.errmsg, self)
pyparsing.ParseException: Expected W:(ABCD...) (at char 6), (line:1, col:7)

这是预期的行为。 Pyparsing 不进行任何前瞻,而是纯粹从左到右。您 可以 向您的解析器添加前瞻性,但这是您必须自己做的事情。

如果您为 ab:

打开调试,您可以更深入地了解正在发生的事情
a.setName('a').setDebug()
b.setName('b').setDebug()

这会告诉你每一个pyparsing要匹配表达式的地方,然后如果匹配失败或成功,如果成功,匹配的标记:

Match a at loc 0(1,1)
Matched a -> ['hello', ';']
Match a at loc 6(1,7)
Exception raised:Expected W:(ABCD...) (at char 6), (line:1, col:7)
Match b at loc 6(1,7)
Exception raised:Expected W:(ABCD...) (at char 6), (line:1, col:7)

由于 a 匹配完整的输入字符串,因此符合 "zero or more" 的条件。然后 pyparsing 继续匹配 b,但是由于单词和分号已经被读取,所以没有更多的要解析。由于 b 不是可选的,pyparsing 会引发无法找到它的异常。即使你要解析 "hello; hello; hello;",所有的字符串和 semis 都会被 ZeroOrMore,没有尾随 b 需要解析。

试试这个:

not_so_bad_parser = ZeroOrMore(a + ~StringEnd()) + b

通过声明你只想读取不在字符串末尾的a表达式,那么解析"hello;"将不会匹配a,所以继续b,然后匹配。

这是一个非常普遍的问题,我将 stopOn 关键字添加到 ZeroOrMore 和 OneOrMore class 构造函数中,以避免需要添加明显的 ~(意思是 NotAny) .起初我认为这可能有效:

even_less_bad_parser = ZeroOrMore(a, stopOn=b) + b

但是,由于 b 也匹配为 a,这将有效地 永远不会 匹配任何 a,并且可能会离开不匹配的文本。只有在字符串末尾时,我们才需要在 b 处停止:

even_less_bad_parser = ZeroOrMore(a, stopOn=b + StringEnd()) + b

我不确定这是否会真正满足您的 "less bad"-ness 概念,但这就是为什么 pyparsing 的行为适合您。