summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/compiler/pycodegen.py73
-rw-r--r--Tools/compiler/compiler/pycodegen.py73
2 files changed, 84 insertions, 62 deletions
diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py
index a697b55..b3223c1 100644
--- a/Lib/compiler/pycodegen.py
+++ b/Lib/compiler/pycodegen.py
@@ -105,6 +105,22 @@ class CodeGenerator:
else:
self.emit(prefix + '_GLOBAL', name)
+ def set_lineno(self, node):
+ """Emit SET_LINENO if node has lineno attribute
+
+ Returns true if SET_LINENO was emitted.
+
+ There are no rules for when an AST node should have a lineno
+ attribute. The transformer and AST code need to be reviewed
+ and a consistent policy implemented and documented. Until
+ then, this method works around missing line numbers.
+ """
+ lineno = getattr(node, 'lineno', None)
+ if lineno is not None:
+ self.emit('SET_LINENO', lineno)
+ return 1
+ return 0
+
# The first few visitor methods handle nodes that generator new
# code objects
@@ -128,7 +144,7 @@ class CodeGenerator:
gen = FunctionCodeGenerator(node, self.filename, isLambda)
walk(node.code, gen)
gen.finish()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
for default in node.defaults:
self.visit(default)
self.emit('LOAD_CONST', gen.getCode())
@@ -138,7 +154,7 @@ class CodeGenerator:
gen = ClassCodeGenerator(node, self.filename)
walk(node.code, gen)
gen.finish()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('LOAD_CONST', node.name)
for base in node.bases:
self.visit(base)
@@ -158,8 +174,7 @@ class CodeGenerator:
numtests = len(node.tests)
for i in range(numtests):
test, suite = node.tests[i]
- if hasattr(test, 'lineno'):
- self.emit('SET_LINENO', test.lineno)
+ self.set_lineno(test)
self.visit(test)
## if i == numtests - 1 and not node.else_:
## nextTest = end
@@ -178,7 +193,7 @@ class CodeGenerator:
self.nextBlock(end)
def visitWhile(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
loop = self.newBlock()
else_ = self.newBlock()
@@ -189,7 +204,7 @@ class CodeGenerator:
self.nextBlock(loop)
self.loops.push(loop)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.test)
self.emit('JUMP_IF_FALSE', else_ or after)
@@ -212,12 +227,12 @@ class CodeGenerator:
after = self.newBlock()
self.loops.push(start)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('SETUP_LOOP', after)
self.visit(node.list)
self.visit(ast.Const(0))
self.nextBlock(start)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('FOR_LOOP', anchor)
self.visit(node.assign)
self.visit(node.body)
@@ -233,7 +248,7 @@ class CodeGenerator:
if not self.loops:
raise SyntaxError, "'break' outside loop (%s, %d)" % \
(self.filename, node.lineno)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('BREAK_LOOP')
def visitContinue(self, node):
@@ -241,7 +256,7 @@ class CodeGenerator:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno)
l = self.loops.top()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('JUMP_ABSOLUTE', l)
self.nextBlock()
@@ -291,7 +306,7 @@ class CodeGenerator:
# XXX would be interesting to implement this via a
# transformation of the AST before this stage
end = self.newBlock()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
# XXX __debug__ and AssertionError appear to be special cases
# -- they are always loaded as globals even if there are local
# names. I guess this is a sort of renaming op.
@@ -309,7 +324,7 @@ class CodeGenerator:
self.emit('POP_TOP')
def visitRaise(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
n = 0
if node.expr1:
self.visit(node.expr1)
@@ -329,7 +344,7 @@ class CodeGenerator:
lElse = self.newBlock()
else:
lElse = end
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('SETUP_EXCEPT', handlers)
self.visit(node.body)
self.emit('POP_BLOCK')
@@ -339,8 +354,7 @@ class CodeGenerator:
last = len(node.handlers) - 1
for i in range(len(node.handlers)):
expr, target, body = node.handlers[i]
- if hasattr(expr, 'lineno'):
- self.emit('SET_LINENO', expr.lineno)
+ self.set_lineno(expr)
if expr:
self.emit('DUP_TOP')
self.visit(expr)
@@ -368,7 +382,7 @@ class CodeGenerator:
def visitTryFinally(self, node):
final = self.newBlock()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('SETUP_FINALLY', final)
self.visit(node.body)
self.emit('POP_BLOCK')
@@ -402,16 +416,16 @@ class CodeGenerator:
self.loadName(node.name)
def visitPass(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
def visitImport(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
for name in node.names:
self.emit('IMPORT_NAME', name)
self.storeName(name)
def visitFrom(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('IMPORT_NAME', node.modname)
for name in node.names:
if name == '*':
@@ -426,7 +440,7 @@ class CodeGenerator:
# next five implement assignments
def visitAssign(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.expr)
dups = len(node.nodes) - 1
for i in range(len(node.nodes)):
@@ -477,8 +491,7 @@ class CodeGenerator:
def visitCallFunc(self, node):
pos = 0
kw = 0
- if hasattr(node, 'lineno'):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.node)
for arg in node.args:
self.visit(arg)
@@ -496,7 +509,7 @@ class CodeGenerator:
self.emit(opcode, kw << 8 | pos)
def visitPrint(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
for child in node.nodes:
self.visit(child)
self.emit('PRINT_ITEM')
@@ -506,7 +519,7 @@ class CodeGenerator:
self.emit('PRINT_NEWLINE')
def visitReturn(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.value)
self.emit('RETURN_VALUE')
@@ -637,15 +650,13 @@ class CodeGenerator:
self.emit('BUILD_SLICE', len(node.nodes))
def visitDict(self, node):
- # XXX is this a good general strategy? could it be done
- # separately from the general visitor
- lineno = getattr(node, 'lineno', None)
- if lineno:
- self.emit('SET_LINENO', lineno)
+ lineno = getattr(node, 'lineno', None)
+ if lineno:
+ set.emit('SET_LINENO', lineno)
self.emit('BUILD_MAP', 0)
for k, v in node.items:
lineno2 = getattr(node, 'lineno', None)
- if lineno != lineno2:
+ if lineno2 is not None and lineno != lineno2:
self.emit('SET_LINENO', lineno2)
lineno = lineno2
self.emit('DUP_TOP')
@@ -687,7 +698,7 @@ class FunctionCodeGenerator(CodeGenerator):
self.graph.setFlag(CO_VARARGS)
if func.kwargs:
self.graph.setFlag(CO_VARKEYWORDS)
- self.emit('SET_LINENO', func.lineno)
+ self.set_lineno(func)
if hasTupleArg:
self.generateArgUnpack(func.argnames)
diff --git a/Tools/compiler/compiler/pycodegen.py b/Tools/compiler/compiler/pycodegen.py
index a697b55..b3223c1 100644
--- a/Tools/compiler/compiler/pycodegen.py
+++ b/Tools/compiler/compiler/pycodegen.py
@@ -105,6 +105,22 @@ class CodeGenerator:
else:
self.emit(prefix + '_GLOBAL', name)
+ def set_lineno(self, node):
+ """Emit SET_LINENO if node has lineno attribute
+
+ Returns true if SET_LINENO was emitted.
+
+ There are no rules for when an AST node should have a lineno
+ attribute. The transformer and AST code need to be reviewed
+ and a consistent policy implemented and documented. Until
+ then, this method works around missing line numbers.
+ """
+ lineno = getattr(node, 'lineno', None)
+ if lineno is not None:
+ self.emit('SET_LINENO', lineno)
+ return 1
+ return 0
+
# The first few visitor methods handle nodes that generator new
# code objects
@@ -128,7 +144,7 @@ class CodeGenerator:
gen = FunctionCodeGenerator(node, self.filename, isLambda)
walk(node.code, gen)
gen.finish()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
for default in node.defaults:
self.visit(default)
self.emit('LOAD_CONST', gen.getCode())
@@ -138,7 +154,7 @@ class CodeGenerator:
gen = ClassCodeGenerator(node, self.filename)
walk(node.code, gen)
gen.finish()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('LOAD_CONST', node.name)
for base in node.bases:
self.visit(base)
@@ -158,8 +174,7 @@ class CodeGenerator:
numtests = len(node.tests)
for i in range(numtests):
test, suite = node.tests[i]
- if hasattr(test, 'lineno'):
- self.emit('SET_LINENO', test.lineno)
+ self.set_lineno(test)
self.visit(test)
## if i == numtests - 1 and not node.else_:
## nextTest = end
@@ -178,7 +193,7 @@ class CodeGenerator:
self.nextBlock(end)
def visitWhile(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
loop = self.newBlock()
else_ = self.newBlock()
@@ -189,7 +204,7 @@ class CodeGenerator:
self.nextBlock(loop)
self.loops.push(loop)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.test)
self.emit('JUMP_IF_FALSE', else_ or after)
@@ -212,12 +227,12 @@ class CodeGenerator:
after = self.newBlock()
self.loops.push(start)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('SETUP_LOOP', after)
self.visit(node.list)
self.visit(ast.Const(0))
self.nextBlock(start)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('FOR_LOOP', anchor)
self.visit(node.assign)
self.visit(node.body)
@@ -233,7 +248,7 @@ class CodeGenerator:
if not self.loops:
raise SyntaxError, "'break' outside loop (%s, %d)" % \
(self.filename, node.lineno)
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('BREAK_LOOP')
def visitContinue(self, node):
@@ -241,7 +256,7 @@ class CodeGenerator:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno)
l = self.loops.top()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('JUMP_ABSOLUTE', l)
self.nextBlock()
@@ -291,7 +306,7 @@ class CodeGenerator:
# XXX would be interesting to implement this via a
# transformation of the AST before this stage
end = self.newBlock()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
# XXX __debug__ and AssertionError appear to be special cases
# -- they are always loaded as globals even if there are local
# names. I guess this is a sort of renaming op.
@@ -309,7 +324,7 @@ class CodeGenerator:
self.emit('POP_TOP')
def visitRaise(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
n = 0
if node.expr1:
self.visit(node.expr1)
@@ -329,7 +344,7 @@ class CodeGenerator:
lElse = self.newBlock()
else:
lElse = end
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('SETUP_EXCEPT', handlers)
self.visit(node.body)
self.emit('POP_BLOCK')
@@ -339,8 +354,7 @@ class CodeGenerator:
last = len(node.handlers) - 1
for i in range(len(node.handlers)):
expr, target, body = node.handlers[i]
- if hasattr(expr, 'lineno'):
- self.emit('SET_LINENO', expr.lineno)
+ self.set_lineno(expr)
if expr:
self.emit('DUP_TOP')
self.visit(expr)
@@ -368,7 +382,7 @@ class CodeGenerator:
def visitTryFinally(self, node):
final = self.newBlock()
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('SETUP_FINALLY', final)
self.visit(node.body)
self.emit('POP_BLOCK')
@@ -402,16 +416,16 @@ class CodeGenerator:
self.loadName(node.name)
def visitPass(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
def visitImport(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
for name in node.names:
self.emit('IMPORT_NAME', name)
self.storeName(name)
def visitFrom(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.emit('IMPORT_NAME', node.modname)
for name in node.names:
if name == '*':
@@ -426,7 +440,7 @@ class CodeGenerator:
# next five implement assignments
def visitAssign(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.expr)
dups = len(node.nodes) - 1
for i in range(len(node.nodes)):
@@ -477,8 +491,7 @@ class CodeGenerator:
def visitCallFunc(self, node):
pos = 0
kw = 0
- if hasattr(node, 'lineno'):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.node)
for arg in node.args:
self.visit(arg)
@@ -496,7 +509,7 @@ class CodeGenerator:
self.emit(opcode, kw << 8 | pos)
def visitPrint(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
for child in node.nodes:
self.visit(child)
self.emit('PRINT_ITEM')
@@ -506,7 +519,7 @@ class CodeGenerator:
self.emit('PRINT_NEWLINE')
def visitReturn(self, node):
- self.emit('SET_LINENO', node.lineno)
+ self.set_lineno(node)
self.visit(node.value)
self.emit('RETURN_VALUE')
@@ -637,15 +650,13 @@ class CodeGenerator:
self.emit('BUILD_SLICE', len(node.nodes))
def visitDict(self, node):
- # XXX is this a good general strategy? could it be done
- # separately from the general visitor
- lineno = getattr(node, 'lineno', None)
- if lineno:
- self.emit('SET_LINENO', lineno)
+ lineno = getattr(node, 'lineno', None)
+ if lineno:
+ set.emit('SET_LINENO', lineno)
self.emit('BUILD_MAP', 0)
for k, v in node.items:
lineno2 = getattr(node, 'lineno', None)
- if lineno != lineno2:
+ if lineno2 is not None and lineno != lineno2:
self.emit('SET_LINENO', lineno2)
lineno = lineno2
self.emit('DUP_TOP')
@@ -687,7 +698,7 @@ class FunctionCodeGenerator(CodeGenerator):
self.graph.setFlag(CO_VARARGS)
if func.kwargs:
self.graph.setFlag(CO_VARKEYWORDS)
- self.emit('SET_LINENO', func.lineno)
+ self.set_lineno(func)
if hasTupleArg:
self.generateArgUnpack(func.argnames)