Fix a bug in the ``compiler`` package that caused invalid code to be

generated for generator expressions.
This commit is contained in:
Neil Schemenauer 2006-08-16 23:38:05 +00:00
parent 7ae354846f
commit 4c6b0d5bec
5 changed files with 26 additions and 9 deletions

View File

@ -583,11 +583,9 @@ class GenExpr(Node):
def __init__(self, code, lineno=None): def __init__(self, code, lineno=None):
self.code = code self.code = code
self.lineno = lineno self.lineno = lineno
self.argnames = ['[outmost-iterable]'] self.argnames = ['.0']
self.varargs = self.kwargs = None self.varargs = self.kwargs = None
def getChildren(self): def getChildren(self):
return self.code, return self.code,

View File

@ -658,18 +658,19 @@ class CodeGenerator:
stack = [] stack = []
for i, for_ in zip(range(len(node.quals)), node.quals): for i, for_ in zip(range(len(node.quals)), node.quals):
start, anchor = self.visit(for_) start, anchor, end = self.visit(for_)
cont = None cont = None
for if_ in for_.ifs: for if_ in for_.ifs:
if cont is None: if cont is None:
cont = self.newBlock() cont = self.newBlock()
self.visit(if_, cont) self.visit(if_, cont)
stack.insert(0, (start, cont, anchor)) stack.insert(0, (start, cont, anchor, end))
self.visit(node.expr) self.visit(node.expr)
self.emit('YIELD_VALUE') self.emit('YIELD_VALUE')
self.emit('POP_TOP')
for start, cont, anchor in stack: for start, cont, anchor, end in stack:
if cont: if cont:
skip_one = self.newBlock() skip_one = self.newBlock()
self.emit('JUMP_FORWARD', skip_one) self.emit('JUMP_FORWARD', skip_one)
@ -678,14 +679,22 @@ class CodeGenerator:
self.nextBlock(skip_one) self.nextBlock(skip_one)
self.emit('JUMP_ABSOLUTE', start) self.emit('JUMP_ABSOLUTE', start)
self.startBlock(anchor) self.startBlock(anchor)
self.emit('POP_BLOCK')
self.setups.pop()
self.startBlock(end)
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
def visitGenExprFor(self, node): def visitGenExprFor(self, node):
start = self.newBlock() start = self.newBlock()
anchor = self.newBlock() anchor = self.newBlock()
end = self.newBlock()
self.setups.push((LOOP, start))
self.emit('SETUP_LOOP', end)
if node.is_outmost: if node.is_outmost:
self.loadName('[outmost-iterable]') self.loadName('.0')
else: else:
self.visit(node.iter) self.visit(node.iter)
self.emit('GET_ITER') self.emit('GET_ITER')
@ -695,7 +704,7 @@ class CodeGenerator:
self.emit('FOR_ITER', anchor) self.emit('FOR_ITER', anchor)
self.nextBlock() self.nextBlock()
self.visit(node.assign) self.visit(node.assign)
return start, anchor return start, anchor, end
def visitGenExprIf(self, node, branch): def visitGenExprIf(self, node, branch):
self.set_lineno(node, force=True) self.set_lineno(node, force=True)

View File

@ -188,7 +188,7 @@ class GenExprScope(Scope):
i = self.__counter i = self.__counter
self.__counter += 1 self.__counter += 1
self.__super_init("generator expression<%d>"%i, module, klass) self.__super_init("generator expression<%d>"%i, module, klass)
self.add_param('[outmost-iterable]') self.add_param('.0')
def get_names(self): def get_names(self):
keys = Scope.get_names(self) keys = Scope.get_names(self)

View File

@ -116,6 +116,13 @@ class CompilerTest(unittest.TestCase):
exec c in dct exec c in dct
self.assertEquals(dct.get('result'), 3) self.assertEquals(dct.get('result'), 3)
def testGenExp(self):
c = compiler.compile('list((i,j) for i in range(3) if i < 3'
' for j in range(4) if j > 2)',
'<string>',
'eval')
self.assertEquals(eval(c), [(0, 3), (1, 3), (2, 3)])
NOLINENO = (compiler.ast.Module, compiler.ast.Stmt, compiler.ast.Discard) NOLINENO = (compiler.ast.Module, compiler.ast.Stmt, compiler.ast.Discard)

View File

@ -64,6 +64,9 @@ Core and builtins
Library Library
------- -------
- Fix a bug in the ``compiler`` package that caused invalid code to be
generated for generator expressions.
- The distutils version has been changed to 2.5.0. The change to - The distutils version has been changed to 2.5.0. The change to
keep it programmatically in sync with the Python version running keep it programmatically in sync with the Python version running
the code (introduced in 2.5b3) has been reverted. It will continue the code (introduced in 2.5b3) has been reverted. It will continue