'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 不进行任何前瞻,而是纯粹从左到右。您 可以 向您的解析器添加前瞻性,但这是您必须自己做的事情。
如果您为 a
和 b
:
打开调试,您可以更深入地了解正在发生的事情
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 的行为适合您。
我在 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 不进行任何前瞻,而是纯粹从左到右。您 可以 向您的解析器添加前瞻性,但这是您必须自己做的事情。
如果您为 a
和 b
:
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 的行为适合您。