我在python3.6中编写了一个解析器;在产生bug的同时,我尽可能地简化了它:
def tokenize(expr):
for i in expr:
try:
yield int(i)
except ValueError:
yield i
def push_on_stream(obj, stream):
yield obj
yield from stream
class OpenBracket:
"just a token value, could have used Ellipsis"
pass
def parse_toks(tokstream):
result = []
leading_brak = False
for tok in tokstream:
if tok == OpenBracket:
leading_brak = True
elif tok == '(':
result.append(parse_toks(
push_on_stream(OpenBracket, tokstream)))
elif tok == ')':
if not leading_brak:
raise SyntaxError("Very bad ')'.")
break
else:
result.append(tok)
return sum(result)
def test(expr="12(34)21"):
tokens = tokenize(expr)
print( parse_toks(tokens) )
print(list(tokens))
test()
这个例子很简单;其效果应该是将字符串中的所有数字相加,包括括号中的数字。你知道吗
tokenize()函数生成令牌,parse_tok()函数解析令牌流。如果遇到一个开括号,它就会递归(将OpenBracket推到令牌流上),这样做的效果应该是将括号中的数字视为一个单独的表达式,对其进行解析并将结果添加到result堆栈中。你知道吗
当我解析代码时,例如在表达式“1(2)3”上,它立即在右括号后结束,返回3,实际上令牌流似乎已经结束。你知道吗
但是,当我使用pdb运行它,并在parse_tok中的循环内设置断点时,当它处理“)”时,我可以小心地进行操作,程序正确地返回6。你知道吗
我认为这个bug与push\u on\u stream()中的令牌流的屈服有关。你知道吗
这是口译员的错误吗?如果是这样,是否有一个好的解决方法?你知道吗
我为python-3.6编写了它,但我也在python-3.7上的另一台机器上测试了它,得到了相同的结果。你知道吗
问题就在你描述的地方:
一旦你点击了右括号,你就中止了paring循环,要么是异常,要么是显式中断。只需删除
break
。你希望这里有什么功能?你知道吗测试代码:
输出值:
你的
push_on_stream
没有按照你认为应该的方式工作。你知道吗请参阅,当回收
push_on_stream
生成器时,Python调用生成器上的close
,这会将GeneratorExit
抛出到生成器中,以确保任何finally
块和__exit__
方法都能运行。因为push_on_stream
在底层生成器上使用yield from
,如果push_on_stream
在yield from
中挂起,这将在底层tokenize
生成器中抛出一个GeneratorExit
。你知道吗这将立即终止令牌流。在pdb中,某些东西导致
push_on_stream
生成器不能被收集,从而阻止了这种效果。你知道吗假设
当
break
语句离开循环时,将引发一个GeneratorExit
异常,该异常通过生成器传播。pdb
修改了它的传播方式,这正是我希望它引入的那种微妙的bug,使它不会耗尽push_on_stream
正在yield
的生成器。你知道吗试验
如果我们将
push_on_stream
从:收件人:
那么,这将对它产生足够的影响,以保证在这两种情况下的正确行为。你知道吗
结果
修正了错误!你知道吗
解释
由user2357112's answer提供。基本上,
yield from
没有按您认为的方式工作;当生成器由于break
语句退出时,yield from
会导致您正在迭代的生成器将自己标记为已耗尽。(pdb
中断了这个过程,因为这是一个有点麻烦的问题。)这会导致解析器在第一个)
终止,因为当第一个break
语句运行时,底层迭代器会停止。你知道吗相关问题 更多 >
编程相关推荐