compiler.transformer: correct lineno attribute when possible

SF patch #1015989

The basic idea of this patch is to compute lineno attributes for all AST nodes.  The actual
implementation lead to a lot of restructing and code cleanup.

The generated AST nodes now have an optional lineno argument to constructor.  Remove the
top-level asList(), since it didn't seem to serve any purpose.  Add an __iter__ to ast nodes.
Use isinstance() instead of explicit type tests.

Change transformer to use the new lineno attribute, which replaces three lines of code with one.
Use universal newlines so that we can get rid of special-case code for line endings.  Use
lookup_node() in a few more frequently called, but simple com_xxx methods().  Change string
exception to class exception.
This commit is contained in:
Jeremy Hylton 2004-09-07 15:28:01 +00:00
parent 2ad68e69b9
commit 566d934745
6 changed files with 407 additions and 447 deletions

File diff suppressed because it is too large Load diff

View file

@ -14,7 +14,10 @@ parseFile(path) -> AST
# #
# Modifications and improvements for Python 2.0 by Jeremy Hylton and # Modifications and improvements for Python 2.0 by Jeremy Hylton and
# Mark Hammond # Mark Hammond
#
# Some fixes to try to have correct line number on almost all nodes
# (except Module, Discard and Stmt) added by Sylvain Thenault
#
# Portions of this file are: # Portions of this file are:
# Copyright (C) 1997-1998 Greg Stein. All Rights Reserved. # Copyright (C) 1997-1998 Greg Stein. All Rights Reserved.
# #
@ -22,21 +25,20 @@ parseFile(path) -> AST
# http://www.opensource.org/licenses/bsd-license.html # http://www.opensource.org/licenses/bsd-license.html
# and replace OWNER, ORGANIZATION, and YEAR as appropriate. # and replace OWNER, ORGANIZATION, and YEAR as appropriate.
from ast import * from compiler.ast import *
import parser import parser
# Care must be taken to use only symbols and tokens defined in Python
# 1.5.2 for code branches executed in 1.5.2
import symbol import symbol
import token import token
import sys import sys
error = 'walker.error' class WalkerError(StandardError):
pass
from consts import CO_VARARGS, CO_VARKEYWORDS from consts import CO_VARARGS, CO_VARKEYWORDS
from consts import OP_ASSIGN, OP_DELETE, OP_APPLY from consts import OP_ASSIGN, OP_DELETE, OP_APPLY
def parseFile(path): def parseFile(path):
f = open(path) f = open(path, "U")
# XXX The parser API tolerates files without a trailing newline, # XXX The parser API tolerates files without a trailing newline,
# but not strings without a trailing newline. Always add an extra # but not strings without a trailing newline. Always add an extra
# newline to the file contents, since we're going through the string # newline to the file contents, since we're going through the string
@ -68,6 +70,16 @@ def asList(nodes):
l.append(item) l.append(item)
return l return l
def extractLineNo(ast):
if not isinstance(ast[1], tuple):
# get a terminal node
return ast[2]
for child in ast[1:]:
if isinstance(child, tuple):
lineno = extractLineNo(child)
if lineno is not None:
return lineno
def Node(*args): def Node(*args):
kind = args[0] kind = args[0]
if nodes.has_key(kind): if nodes.has_key(kind):
@ -77,7 +89,7 @@ def Node(*args):
print nodes[kind], len(args), args print nodes[kind], len(args), args
raise raise
else: else:
raise error, "Can't find appropriate Node type: %s" % str(args) raise WalkerEror, "Can't find appropriate Node type: %s" % str(args)
#return apply(ast.Node, args) #return apply(ast.Node, args)
class Transformer: class Transformer:
@ -108,15 +120,12 @@ class Transformer:
def transform(self, tree): def transform(self, tree):
"""Transform an AST into a modified parse tree.""" """Transform an AST into a modified parse tree."""
if type(tree) != type(()) and type(tree) != type([]): if not (isinstance(tree, tuple) or isinstance(tree, list)):
tree = parser.ast2tuple(tree, line_info=1) tree = parser.ast2tuple(tree, line_info=1)
return self.compile_node(tree) return self.compile_node(tree)
def parsesuite(self, text): def parsesuite(self, text):
"""Return a modified parse tree for the given suite text.""" """Return a modified parse tree for the given suite text."""
# Hack for handling non-native line endings on non-DOS like OSs.
# this can go now we have universal newlines?
text = text.replace('\x0d', '')
return self.transform(parser.suite(text)) return self.transform(parser.suite(text))
def parseexpr(self, text): def parseexpr(self, text):
@ -156,7 +165,7 @@ class Transformer:
if n == symbol.classdef: if n == symbol.classdef:
return self.classdef(node[1:]) return self.classdef(node[1:])
raise error, ('unexpected node type', n) raise WalkerEror, ('unexpected node type', n)
def single_input(self, node): def single_input(self, node):
### do we want to do anything about being "interactive" ? ### do we want to do anything about being "interactive" ?
@ -254,9 +263,8 @@ class Transformer:
assert isinstance(code, Stmt) assert isinstance(code, Stmt)
assert isinstance(code.nodes[0], Discard) assert isinstance(code.nodes[0], Discard)
del code.nodes[0] del code.nodes[0]
n = Function(decorators, name, names, defaults, flags, doc, code) return Function(decorators, name, names, defaults, flags, doc, code,
n.lineno = lineno lineno=lineno)
return n
def lambdef(self, nodelist): def lambdef(self, nodelist):
# lambdef: 'lambda' [varargslist] ':' test # lambdef: 'lambda' [varargslist] ':' test
@ -269,9 +277,7 @@ class Transformer:
# code for lambda # code for lambda
code = self.com_node(nodelist[-1]) code = self.com_node(nodelist[-1])
n = Lambda(names, defaults, flags, code) return Lambda(names, defaults, flags, code, lineno=nodelist[1][2])
n.lineno = nodelist[1][2]
return n
def classdef(self, nodelist): def classdef(self, nodelist):
# classdef: 'class' NAME ['(' testlist ')'] ':' suite # classdef: 'class' NAME ['(' testlist ')'] ':' suite
@ -291,9 +297,7 @@ class Transformer:
assert isinstance(code.nodes[0], Discard) assert isinstance(code.nodes[0], Discard)
del code.nodes[0] del code.nodes[0]
n = Class(name, bases, doc, code) return Class(name, bases, doc, code, lineno=nodelist[1][2])
n.lineno = nodelist[1][2]
return n
def stmt(self, nodelist): def stmt(self, nodelist):
return self.com_stmt(nodelist[0]) return self.com_stmt(nodelist[0])
@ -310,31 +314,31 @@ class Transformer:
return Stmt(stmts) return Stmt(stmts)
def parameters(self, nodelist): def parameters(self, nodelist):
raise error raise WalkerEror
def varargslist(self, nodelist): def varargslist(self, nodelist):
raise error raise WalkerEror
def fpdef(self, nodelist): def fpdef(self, nodelist):
raise error raise WalkerEror
def fplist(self, nodelist): def fplist(self, nodelist):
raise error raise WalkerEror
def dotted_name(self, nodelist): def dotted_name(self, nodelist):
raise error raise WalkerEror
def comp_op(self, nodelist): def comp_op(self, nodelist):
raise error raise WalkerEror
def trailer(self, nodelist): def trailer(self, nodelist):
raise error raise WalkerEror
def sliceop(self, nodelist): def sliceop(self, nodelist):
raise error raise WalkerEror
def argument(self, nodelist): def argument(self, nodelist):
raise error raise WalkerEror
# -------------------------------------------------------------- # --------------------------------------------------------------
# #
@ -346,21 +350,17 @@ class Transformer:
en = nodelist[-1] en = nodelist[-1]
exprNode = self.lookup_node(en)(en[1:]) exprNode = self.lookup_node(en)(en[1:])
if len(nodelist) == 1: if len(nodelist) == 1:
n = Discard(exprNode) return Discard(exprNode, lineno=exprNode.lineno)
n.lineno = exprNode.lineno
return n
if nodelist[1][0] == token.EQUAL: if nodelist[1][0] == token.EQUAL:
nodesl = [] nodesl = []
for i in range(0, len(nodelist) - 2, 2): for i in range(0, len(nodelist) - 2, 2):
nodesl.append(self.com_assign(nodelist[i], OP_ASSIGN)) nodesl.append(self.com_assign(nodelist[i], OP_ASSIGN))
n = Assign(nodesl, exprNode) return Assign(nodesl, exprNode, lineno=nodelist[1][2])
n.lineno = nodelist[1][2]
else: else:
lval = self.com_augassign(nodelist[0]) lval = self.com_augassign(nodelist[0])
op = self.com_augassign_op(nodelist[1]) op = self.com_augassign_op(nodelist[1])
n = AugAssign(lval, op[1], exprNode) return AugAssign(lval, op[1], exprNode, lineno=op[2])
n.lineno = op[2] raise WalkerError, "can't get here"
return n
def print_stmt(self, nodelist): def print_stmt(self, nodelist):
# print ([ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ]) # print ([ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ])
@ -379,45 +379,29 @@ class Transformer:
for i in range(start, len(nodelist), 2): for i in range(start, len(nodelist), 2):
items.append(self.com_node(nodelist[i])) items.append(self.com_node(nodelist[i]))
if nodelist[-1][0] == token.COMMA: if nodelist[-1][0] == token.COMMA:
n = Print(items, dest) return Print(items, dest, lineno=nodelist[0][2])
n.lineno = nodelist[0][2] return Printnl(items, dest, lineno=nodelist[0][2])
return n
n = Printnl(items, dest)
n.lineno = nodelist[0][2]
return n
def del_stmt(self, nodelist): def del_stmt(self, nodelist):
return self.com_assign(nodelist[1], OP_DELETE) return self.com_assign(nodelist[1], OP_DELETE)
def pass_stmt(self, nodelist): def pass_stmt(self, nodelist):
n = Pass() return Pass(lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def break_stmt(self, nodelist): def break_stmt(self, nodelist):
n = Break() return Break(lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def continue_stmt(self, nodelist): def continue_stmt(self, nodelist):
n = Continue() return Continue(lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def return_stmt(self, nodelist): def return_stmt(self, nodelist):
# return: [testlist] # return: [testlist]
if len(nodelist) < 2: if len(nodelist) < 2:
n = Return(Const(None)) return Return(Const(None), lineno=nodelist[0][2])
n.lineno = nodelist[0][2] return Return(self.com_node(nodelist[1]), lineno=nodelist[0][2])
return n
n = Return(self.com_node(nodelist[1]))
n.lineno = nodelist[0][2]
return n
def yield_stmt(self, nodelist): def yield_stmt(self, nodelist):
n = Yield(self.com_node(nodelist[1])) return Yield(self.com_node(nodelist[1]), lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def raise_stmt(self, nodelist): def raise_stmt(self, nodelist):
# raise: [test [',' test [',' test]]] # raise: [test [',' test [',' test]]]
@ -433,9 +417,7 @@ class Transformer:
expr1 = self.com_node(nodelist[1]) expr1 = self.com_node(nodelist[1])
else: else:
expr1 = None expr1 = None
n = Raise(expr1, expr2, expr3) return Raise(expr1, expr2, expr3, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def import_stmt(self, nodelist): def import_stmt(self, nodelist):
# import_stmt: import_name | import_from # import_stmt: import_name | import_from
@ -444,9 +426,8 @@ class Transformer:
def import_name(self, nodelist): def import_name(self, nodelist):
# import_name: 'import' dotted_as_names # import_name: 'import' dotted_as_names
n = Import(self.com_dotted_as_names(nodelist[1])) return Import(self.com_dotted_as_names(nodelist[1]),
n.lineno = nodelist[0][2] lineno=nodelist[0][2])
return n
def import_from(self, nodelist): def import_from(self, nodelist):
# import_from: 'from' dotted_name 'import' ('*' | # import_from: 'from' dotted_name 'import' ('*' |
@ -456,21 +437,19 @@ class Transformer:
assert nodelist[2][1] == 'import' assert nodelist[2][1] == 'import'
fromname = self.com_dotted_name(nodelist[1]) fromname = self.com_dotted_name(nodelist[1])
if nodelist[3][0] == token.STAR: if nodelist[3][0] == token.STAR:
n = From(fromname, [('*', None)]) # TODO(jhylton): where is the lineno?
return From(fromname, [('*', None)])
else: else:
node = nodelist[3 + (nodelist[3][0] == token.LPAR)] node = nodelist[3 + (nodelist[3][0] == token.LPAR)]
n = From(fromname, self.com_import_as_names(node)) return From(fromname, self.com_import_as_names(node),
n.lineno = nodelist[0][2] lineno=nodelist[0][2])
return n
def global_stmt(self, nodelist): def global_stmt(self, nodelist):
# global: NAME (',' NAME)* # global: NAME (',' NAME)*
names = [] names = []
for i in range(1, len(nodelist), 2): for i in range(1, len(nodelist), 2):
names.append(nodelist[i][1]) names.append(nodelist[i][1])
n = Global(names) return Global(names, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def exec_stmt(self, nodelist): def exec_stmt(self, nodelist):
# exec_stmt: 'exec' expr ['in' expr [',' expr]] # exec_stmt: 'exec' expr ['in' expr [',' expr]]
@ -484,9 +463,7 @@ class Transformer:
else: else:
expr2 = expr3 = None expr2 = expr3 = None
n = Exec(expr1, expr2, expr3) return Exec(expr1, expr2, expr3, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def assert_stmt(self, nodelist): def assert_stmt(self, nodelist):
# 'assert': test, [',' test] # 'assert': test, [',' test]
@ -495,9 +472,7 @@ class Transformer:
expr2 = self.com_node(nodelist[3]) expr2 = self.com_node(nodelist[3])
else: else:
expr2 = None expr2 = None
n = Assert(expr1, expr2) return Assert(expr1, expr2, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def if_stmt(self, nodelist): def if_stmt(self, nodelist):
# if: test ':' suite ('elif' test ':' suite)* ['else' ':' suite] # if: test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
@ -512,9 +487,7 @@ class Transformer:
## elseNode.lineno = nodelist[-1][1][2] ## elseNode.lineno = nodelist[-1][1][2]
else: else:
elseNode = None elseNode = None
n = If(tests, elseNode) return If(tests, elseNode, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def while_stmt(self, nodelist): def while_stmt(self, nodelist):
# 'while' test ':' suite ['else' ':' suite] # 'while' test ':' suite ['else' ':' suite]
@ -527,9 +500,7 @@ class Transformer:
else: else:
elseNode = None elseNode = None
n = While(testNode, bodyNode, elseNode) return While(testNode, bodyNode, elseNode, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def for_stmt(self, nodelist): def for_stmt(self, nodelist):
# 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
@ -543,9 +514,8 @@ class Transformer:
else: else:
elseNode = None elseNode = None
n = For(assignNode, listNode, bodyNode, elseNode) return For(assignNode, listNode, bodyNode, elseNode,
n.lineno = nodelist[0][2] lineno=nodelist[0][2])
return n
def try_stmt(self, nodelist): def try_stmt(self, nodelist):
# 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] # 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
@ -601,9 +571,7 @@ class Transformer:
# 'not' not_test | comparison # 'not' not_test | comparison
result = self.com_node(nodelist[-1]) result = self.com_node(nodelist[-1])
if len(nodelist) == 2: if len(nodelist) == 2:
n = Not(result) return Not(result, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
return result return result
def comparison(self, nodelist): def comparison(self, nodelist):
@ -637,9 +605,7 @@ class Transformer:
# the two have very different semantics and results (note that the # the two have very different semantics and results (note that the
# latter form is always true) # latter form is always true)
n = Compare(node, results) return Compare(node, results, lineno=lineno)
n.lineno = lineno
return n
def expr(self, nodelist): def expr(self, nodelist):
# xor_expr ('|' xor_expr)* # xor_expr ('|' xor_expr)*
@ -659,11 +625,9 @@ class Transformer:
for i in range(2, len(nodelist), 2): for i in range(2, len(nodelist), 2):
right = self.com_node(nodelist[i]) right = self.com_node(nodelist[i])
if nodelist[i-1][0] == token.LEFTSHIFT: if nodelist[i-1][0] == token.LEFTSHIFT:
node = LeftShift([node, right]) node = LeftShift([node, right], lineno=nodelist[1][2])
node.lineno = nodelist[1][2]
elif nodelist[i-1][0] == token.RIGHTSHIFT: elif nodelist[i-1][0] == token.RIGHTSHIFT:
node = RightShift([node, right]) node = RightShift([node, right], lineno=nodelist[1][2])
node.lineno = nodelist[1][2]
else: else:
raise ValueError, "unexpected token: %s" % nodelist[i-1][0] raise ValueError, "unexpected token: %s" % nodelist[i-1][0]
return node return node
@ -673,11 +637,9 @@ class Transformer:
for i in range(2, len(nodelist), 2): for i in range(2, len(nodelist), 2):
right = self.com_node(nodelist[i]) right = self.com_node(nodelist[i])
if nodelist[i-1][0] == token.PLUS: if nodelist[i-1][0] == token.PLUS:
node = Add([node, right]) node = Add([node, right], lineno=nodelist[1][2])
node.lineno = nodelist[1][2]
elif nodelist[i-1][0] == token.MINUS: elif nodelist[i-1][0] == token.MINUS:
node = Sub([node, right]) node = Sub([node, right], lineno=nodelist[1][2])
node.lineno = nodelist[1][2]
else: else:
raise ValueError, "unexpected token: %s" % nodelist[i-1][0] raise ValueError, "unexpected token: %s" % nodelist[i-1][0]
return node return node
@ -703,17 +665,14 @@ class Transformer:
def factor(self, nodelist): def factor(self, nodelist):
elt = nodelist[0] elt = nodelist[0]
t = elt[0] t = elt[0]
node = self.com_node(nodelist[-1]) node = self.lookup_node(nodelist[-1])(nodelist[-1][1:])
# need to handle (unary op)constant here... # need to handle (unary op)constant here...
if t == token.PLUS: if t == token.PLUS:
node = UnaryAdd(node) return UnaryAdd(node, lineno=elt[2])
node.lineno = elt[2]
elif t == token.MINUS: elif t == token.MINUS:
node = UnarySub(node) return UnarySub(node, lineno=elt[2])
node.lineno = elt[2]
elif t == token.TILDE: elif t == token.TILDE:
node = Invert(node) node = Invert(node, lineno=elt[2])
node.lineno = elt[2]
return node return node
def power(self, nodelist): def power(self, nodelist):
@ -722,31 +681,26 @@ class Transformer:
for i in range(1, len(nodelist)): for i in range(1, len(nodelist)):
elt = nodelist[i] elt = nodelist[i]
if elt[0] == token.DOUBLESTAR: if elt[0] == token.DOUBLESTAR:
n = Power([node, self.com_node(nodelist[i+1])]) return Power([node, self.com_node(nodelist[i+1])],
n.lineno = elt[2] lineno=elt[2])
return n
node = self.com_apply_trailer(node, elt) node = self.com_apply_trailer(node, elt)
return node return node
def atom(self, nodelist): def atom(self, nodelist):
n = self._atom_dispatch[nodelist[0][0]](nodelist) return self._atom_dispatch[nodelist[0][0]](nodelist)
n.lineno = nodelist[0][2] n.lineno = nodelist[0][2]
return n return n
def atom_lpar(self, nodelist): def atom_lpar(self, nodelist):
if nodelist[1][0] == token.RPAR: if nodelist[1][0] == token.RPAR:
n = Tuple(()) return Tuple(())
n.lineno = nodelist[0][2]
return n
return self.com_node(nodelist[1]) return self.com_node(nodelist[1])
def atom_lsqb(self, nodelist): def atom_lsqb(self, nodelist):
if nodelist[1][0] == token.RSQB: if nodelist[1][0] == token.RSQB:
n = List(()) return List(())
n.lineno = nodelist[0][2]
return n
return self.com_list_constructor(nodelist[1]) return self.com_list_constructor(nodelist[1])
def atom_lbrace(self, nodelist): def atom_lbrace(self, nodelist):
@ -755,16 +709,12 @@ class Transformer:
return self.com_dictmaker(nodelist[1]) return self.com_dictmaker(nodelist[1])
def atom_backquote(self, nodelist): def atom_backquote(self, nodelist):
n = Backquote(self.com_node(nodelist[1])) return Backquote(self.com_node(nodelist[1]))
n.lineno = nodelist[0][2]
return n
def atom_number(self, nodelist): def atom_number(self, nodelist):
### need to verify this matches compile.c ### need to verify this matches compile.c
k = eval(nodelist[0][1]) k = eval(nodelist[0][1])
n = Const(k) return Const(k, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def decode_literal(self, lit): def decode_literal(self, lit):
if self.encoding: if self.encoding:
@ -781,15 +731,10 @@ class Transformer:
k = '' k = ''
for node in nodelist: for node in nodelist:
k += self.decode_literal(node[1]) k += self.decode_literal(node[1])
n = Const(k) return Const(k, lineno=nodelist[0][2])
n.lineno = nodelist[0][2]
return n
def atom_name(self, nodelist): def atom_name(self, nodelist):
### any processing to do? return Name(nodelist[0][1], lineno=nodelist[0][2])
n = Name(nodelist[0][1])
n.lineno = nodelist[0][2]
return n
# -------------------------------------------------------------- # --------------------------------------------------------------
# #
@ -807,6 +752,8 @@ class Transformer:
def lookup_node(self, node): def lookup_node(self, node):
return self._dispatch[node[0]] return self._dispatch[node[0]]
_callers = {}
def com_node(self, node): def com_node(self, node):
# Note: compile.c has handling in com_node for del_stmt, pass_stmt, # Note: compile.c has handling in com_node for del_stmt, pass_stmt,
# break_stmt, stmt, small_stmt, flow_stmt, simple_stmt, # break_stmt, stmt, small_stmt, flow_stmt, simple_stmt,
@ -865,6 +812,7 @@ class Transformer:
i = i + 2 i = i + 2
elif len(defaults): elif len(defaults):
# Treat "(a=1, b)" as "(a=1, b=None)" # Treat "(a=1, b)" as "(a=1, b=None)"
print nodelist[i]
defaults.append(Const(None)) defaults.append(Const(None))
i = i + 1 i = i + 1
@ -938,10 +886,9 @@ class Transformer:
def com_try_finally(self, nodelist): def com_try_finally(self, nodelist):
# try_fin_stmt: "try" ":" suite "finally" ":" suite # try_fin_stmt: "try" ":" suite "finally" ":" suite
n = TryFinally(self.com_node(nodelist[2]), return TryFinally(self.com_node(nodelist[2]),
self.com_node(nodelist[5])) self.com_node(nodelist[5]),
n.lineno = nodelist[0][2] lineno=nodelist[0][2])
return n
def com_try_except(self, nodelist): def com_try_except(self, nodelist):
# try_except: 'try' ':' suite (except_clause ':' suite)* ['else' suite] # try_except: 'try' ':' suite (except_clause ':' suite)* ['else' suite]
@ -965,9 +912,8 @@ class Transformer:
if node[0] == token.NAME: if node[0] == token.NAME:
elseNode = self.com_node(nodelist[i+2]) elseNode = self.com_node(nodelist[i+2])
n = TryExcept(self.com_node(nodelist[2]), clauses, elseNode) return TryExcept(self.com_node(nodelist[2]), clauses, elseNode,
n.lineno = nodelist[0][2] lineno=nodelist[0][2])
return n
def com_augassign_op(self, node): def com_augassign_op(self, node):
assert node[0] == symbol.augassign assert node[0] == symbol.augassign
@ -1031,7 +977,7 @@ class Transformer:
assigns = [] assigns = []
for i in range(1, len(node), 2): for i in range(1, len(node), 2):
assigns.append(self.com_assign(node[i], assigning)) assigns.append(self.com_assign(node[i], assigning))
return AssTuple(assigns) return AssTuple(assigns, lineno=extractLineNo(node))
def com_assign_list(self, node, assigning): def com_assign_list(self, node, assigning):
assigns = [] assigns = []
@ -1041,12 +987,10 @@ class Transformer:
raise SyntaxError, "can't assign to list comprehension" raise SyntaxError, "can't assign to list comprehension"
assert node[i + 1][0] == token.COMMA, node[i + 1] assert node[i + 1][0] == token.COMMA, node[i + 1]
assigns.append(self.com_assign(node[i], assigning)) assigns.append(self.com_assign(node[i], assigning))
return AssList(assigns) return AssList(assigns, lineno=extractLineNo(node))
def com_assign_name(self, node, assigning): def com_assign_name(self, node, assigning):
n = AssName(node[1], assigning) return AssName(node[1], assigning, lineno=node[2])
n.lineno = node[2]
return n
def com_assign_trailer(self, primary, node, assigning): def com_assign_trailer(self, primary, node, assigning):
t = node[1][0] t = node[1][0]
@ -1059,7 +1003,7 @@ class Transformer:
raise SyntaxError, "unknown trailer type: %s" % t raise SyntaxError, "unknown trailer type: %s" % t
def com_assign_attr(self, primary, node, assigning): def com_assign_attr(self, primary, node, assigning):
return AssAttr(primary, node[1], assigning) return AssAttr(primary, node[1], assigning, lineno=node[-1])
def com_binary(self, constructor, nodelist): def com_binary(self, constructor, nodelist):
"Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])." "Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])."
@ -1071,7 +1015,7 @@ class Transformer:
for i in range(0, l, 2): for i in range(0, l, 2):
n = nodelist[i] n = nodelist[i]
items.append(self.lookup_node(n)(n[1:])) items.append(self.lookup_node(n)(n[1:]))
return constructor(items) return constructor(items, lineno=extractLineNo(nodelist))
def com_stmt(self, node): def com_stmt(self, node):
result = self.lookup_node(node)(node[1:]) result = self.lookup_node(node)(node[1:])
@ -1081,7 +1025,7 @@ class Transformer:
return Stmt([result]) return Stmt([result])
def com_append_stmt(self, stmts, node): def com_append_stmt(self, stmts, node):
result = self.com_node(node) result = self.lookup_node(node)(node[1:])
assert result is not None assert result is not None
if isinstance(result, Stmt): if isinstance(result, Stmt):
stmts.extend(result.nodes) stmts.extend(result.nodes)
@ -1100,7 +1044,7 @@ class Transformer:
elif nodelist[i][0] == token.COMMA: elif nodelist[i][0] == token.COMMA:
continue continue
values.append(self.com_node(nodelist[i])) values.append(self.com_node(nodelist[i]))
return List(values) return List(values, lineno=values[0].lineno)
def com_list_comprehension(self, expr, node): def com_list_comprehension(self, expr, node):
# list_iter: list_for | list_if # list_iter: list_for | list_if
@ -1125,8 +1069,7 @@ class Transformer:
node = self.com_list_iter(node[5]) node = self.com_list_iter(node[5])
elif t == 'if': elif t == 'if':
test = self.com_node(node[2]) test = self.com_node(node[2])
newif = ListCompIf(test) newif = ListCompIf(test, lineno=node[1][2])
newif.lineno = node[1][2]
newfor.ifs.append(newif) newfor.ifs.append(newif)
if len(node) == 3: if len(node) == 3:
node = None node = None
@ -1136,9 +1079,7 @@ class Transformer:
raise SyntaxError, \ raise SyntaxError, \
("unexpected list comprehension element: %s %d" ("unexpected list comprehension element: %s %d"
% (node, lineno)) % (node, lineno))
n = ListComp(expr, fors) return ListComp(expr, fors, lineno=lineno)
n.lineno = lineno
return n
def com_list_iter(self, node): def com_list_iter(self, node):
assert node[0] == symbol.list_iter assert node[0] == symbol.list_iter
@ -1163,8 +1104,8 @@ class Transformer:
if t == 'for': if t == 'for':
assignNode = self.com_assign(node[2], OP_ASSIGN) assignNode = self.com_assign(node[2], OP_ASSIGN)
genNode = self.com_node(node[4]) genNode = self.com_node(node[4])
newfor = GenExprFor(assignNode, genNode, []) newfor = GenExprFor(assignNode, genNode, [],
newfor.lineno = node[1][2] lineno=node[1][2])
fors.append(newfor) fors.append(newfor)
if (len(node)) == 5: if (len(node)) == 5:
node = None node = None
@ -1172,8 +1113,7 @@ class Transformer:
node = self.com_gen_iter(node[5]) node = self.com_gen_iter(node[5])
elif t == 'if': elif t == 'if':
test = self.com_node(node[2]) test = self.com_node(node[2])
newif = GenExprIf(test) newif = GenExprIf(test, lineno=node[1][2])
newif.lineno = node[1][2]
newfor.ifs.append(newif) newfor.ifs.append(newif)
if len(node) == 3: if len(node) == 3:
node = None node = None
@ -1184,9 +1124,7 @@ class Transformer:
("unexpected generator expression element: %s %d" ("unexpected generator expression element: %s %d"
% (node, lineno)) % (node, lineno))
fors[0].is_outmost = True fors[0].is_outmost = True
n = GenExpr(GenExprInner(expr, fors)) return GenExpr(GenExprInner(expr, fors), lineno=lineno)
n.lineno = lineno
return n
def com_gen_iter(self, node): def com_gen_iter(self, node):
assert node[0] == symbol.gen_iter assert node[0] == symbol.gen_iter
@ -1214,13 +1152,11 @@ class Transformer:
def com_select_member(self, primaryNode, nodelist): def com_select_member(self, primaryNode, nodelist):
if nodelist[0] != token.NAME: if nodelist[0] != token.NAME:
raise SyntaxError, "member must be a name" raise SyntaxError, "member must be a name"
n = Getattr(primaryNode, nodelist[1]) return Getattr(primaryNode, nodelist[1], lineno=nodelist[2])
n.lineno = nodelist[2]
return n
def com_call_function(self, primaryNode, nodelist): def com_call_function(self, primaryNode, nodelist):
if nodelist[0] == token.RPAR: if nodelist[0] == token.RPAR:
return CallFunc(primaryNode, []) return CallFunc(primaryNode, [], lineno=extractLineNo(nodelist))
args = [] args = []
kw = 0 kw = 0
len_nodelist = len(nodelist) len_nodelist = len(nodelist)
@ -1253,8 +1189,8 @@ class Transformer:
dstar_node = self.com_node(ch) dstar_node = self.com_node(ch)
else: else:
raise SyntaxError, 'unknown node type: %s' % tok raise SyntaxError, 'unknown node type: %s' % tok
return CallFunc(primaryNode, args, star_node, dstar_node,
return CallFunc(primaryNode, args, star_node, dstar_node) lineno=extractLineNo(nodelist))
def com_argument(self, nodelist, kw): def com_argument(self, nodelist, kw):
if len(nodelist) == 3 and nodelist[2][0] == symbol.gen_for: if len(nodelist) == 3 and nodelist[2][0] == symbol.gen_for:
@ -1270,8 +1206,7 @@ class Transformer:
n = n[1] n = n[1]
if n[0] != token.NAME: if n[0] != token.NAME:
raise SyntaxError, "keyword can't be an expression (%s)"%n[0] raise SyntaxError, "keyword can't be an expression (%s)"%n[0]
node = Keyword(n[1], result) node = Keyword(n[1], result, lineno=n[2])
node.lineno = n[2]
return 1, node return 1, node
def com_subscriptlist(self, primary, nodelist, assigning): def com_subscriptlist(self, primary, nodelist, assigning):
@ -1291,8 +1226,8 @@ class Transformer:
subscripts = [] subscripts = []
for i in range(1, len(nodelist), 2): for i in range(1, len(nodelist), 2):
subscripts.append(self.com_subscript(nodelist[i])) subscripts.append(self.com_subscript(nodelist[i]))
return Subscript(primary, assigning, subscripts,
return Subscript(primary, assigning, subscripts) lineno=extractLineNo(nodelist))
def com_subscript(self, node): def com_subscript(self, node):
# slice_item: expression | proper_slice | ellipsis # slice_item: expression | proper_slice | ellipsis
@ -1338,8 +1273,7 @@ class Transformer:
items.append(Const(None)) items.append(Const(None))
else: else:
items.append(self.com_node(ch[2])) items.append(self.com_node(ch[2]))
return Sliceobj(items, lineno=extractLineNo(node))
return Sliceobj(items)
def com_slice(self, primary, node, assigning): def com_slice(self, primary, node, assigning):
# short_slice: [lower_bound] ":" [upper_bound] # short_slice: [lower_bound] ":" [upper_bound]
@ -1352,7 +1286,8 @@ class Transformer:
elif len(node) == 4: elif len(node) == 4:
lower = self.com_node(node[1]) lower = self.com_node(node[1])
upper = self.com_node(node[3]) upper = self.com_node(node[3])
return Slice(primary, assigning, lower, upper) return Slice(primary, assigning, lower, upper,
lineno=extractLineNo(node))
def get_docstring(self, node, n=None): def get_docstring(self, node, n=None):
if n is None: if n is None:

View file

@ -33,6 +33,65 @@ class CompilerTest(unittest.TestCase):
else: else:
compiler.compile(buf, basename, "exec") compiler.compile(buf, basename, "exec")
def testLineNo(self):
# Test that all nodes except Module have a correct lineno attribute.
filename = __file__
if filename.endswith(".pyc") or filename.endswith(".pyo"):
filename = filename[:-1]
tree = compiler.parseFile(filename)
self.check_lineno(tree)
def check_lineno(self, node):
try:
self._check_lineno(node)
except AssertionError:
print node.__class__, node.lineno
raise
def _check_lineno(self, node):
if not node.__class__ in NOLINENO:
self.assert_(isinstance(node.lineno, int),
"lineno=%s on %s" % (node.lineno, node.__class__))
self.assert_(node.lineno > 0,
"lineno=%s on %s" % (node.lineno, node.__class__))
for child in node.getChildNodes():
self.check_lineno(child)
NOLINENO = (compiler.ast.Module, compiler.ast.Stmt, compiler.ast.Discard)
###############################################################################
# code below is just used to trigger some possible errors, for the benefit of
# testLineNo
###############################################################################
class Toto:
"""docstring"""
pass
a, b = 2, 3
[c, d] = 5, 6
l = [(x, y) for x, y in zip(range(5), range(5,10))]
l[0]
l[3:4]
if l:
pass
else:
a, b = b, a
try:
print yo
except:
yo = 3
else:
yo += 3
try:
a += b
finally:
b = 0
###############################################################################
def test_main(): def test_main():
global TEST_ALL global TEST_ALL
TEST_ALL = test.test_support.is_resource_enabled("compiler") TEST_ALL = test.test_support.is_resource_enabled("compiler")

View file

@ -29,9 +29,7 @@ class Tests(unittest.TestCase):
assert vals['b'] == 2 assert vals['b'] == 2
def test_main(): def test_main():
test_support.run_unittest( test_support.run_unittest(Tests)
Tests
)
if __name__ == "__main__": if __name__ == "__main__":
test_main() test_main()

View file

@ -563,6 +563,7 @@ Michael Stone
Ken Stox Ken Stox
Daniel Stutzbach Daniel Stutzbach
Paul Swartz Paul Swartz
Thenault Sylvain
Geoff Talvola Geoff Talvola
William Tanksley William Tanksley
Christian Tanzer Christian Tanzer

View file

@ -94,7 +94,6 @@ class NodeInfo:
def gen_source(self): def gen_source(self):
buf = StringIO() buf = StringIO()
print >> buf, "class %s(Node):" % self.name print >> buf, "class %s(Node):" % self.name
print >> buf, ' nodes["%s"] = "%s"' % (self.name.lower(), self.name)
self._gen_init(buf) self._gen_init(buf)
print >> buf print >> buf
self._gen_getChildren(buf) self._gen_getChildren(buf)
@ -106,12 +105,14 @@ class NodeInfo:
return buf.read() return buf.read()
def _gen_init(self, buf): def _gen_init(self, buf):
print >> buf, " def __init__(self, %s):" % self.args if self.args:
print >> buf, " def __init__(self, %s, lineno=None):" % self.args
else:
print >> buf, " def __init__(self, lineno=None):"
if self.argnames: if self.argnames:
for name in self.argnames: for name in self.argnames:
print >> buf, " self.%s = %s" % (name, name) print >> buf, " self.%s = %s" % (name, name)
else: print >> buf, " self.lineno = lineno"
print >> buf, " pass"
if self.init: if self.init:
print >> buf, "".join([" " + line for line in self.init]) print >> buf, "".join([" " + line for line in self.init])
@ -128,15 +129,18 @@ class NodeInfo:
else: else:
print >> buf, " return %s" % clist print >> buf, " return %s" % clist
else: else:
print >> buf, " children = []" if len(self.argnames) == 1:
template = " children.%s(%sself.%s%s)" print >> buf, " return tuple(flatten(self.%s))" % self.argnames[0]
for name in self.argnames: else:
if self.argprops[name] == P_NESTED: print >> buf, " children = []"
print >> buf, template % ("extend", "flatten(", template = " children.%s(%sself.%s%s)"
name, ")") for name in self.argnames:
else: if self.argprops[name] == P_NESTED:
print >> buf, template % ("append", "", name, "") print >> buf, template % ("extend", "flatten(",
print >> buf, " return tuple(children)" name, ")")
else:
print >> buf, template % ("append", "", name, "")
print >> buf, " return tuple(children)"
def _gen_getChildNodes(self, buf): def _gen_getChildNodes(self, buf):
print >> buf, " def getChildNodes(self):" print >> buf, " def getChildNodes(self):"
@ -158,7 +162,7 @@ class NodeInfo:
template = " nodelist.%s(%sself.%s%s)" template = " nodelist.%s(%sself.%s%s)"
for name in self.argnames: for name in self.argnames:
if self.argprops[name] == P_NONE: if self.argprops[name] == P_NONE:
tmp = (" if self.%s is not None:" tmp = (" if self.%s is not None:\n"
" nodelist.append(self.%s)") " nodelist.append(self.%s)")
print >> buf, tmp % (name, name) print >> buf, tmp % (name, name)
elif self.argprops[name] == P_NESTED: elif self.argprops[name] == P_NESTED:
@ -226,16 +230,15 @@ if __name__ == "__main__":
### PROLOGUE ### PROLOGUE
"""Python abstract syntax node definitions """Python abstract syntax node definitions
This file is automatically generated. This file is automatically generated by Tools/compiler/astgen.py
""" """
from types import TupleType, ListType
from consts import CO_VARARGS, CO_VARKEYWORDS from consts import CO_VARARGS, CO_VARKEYWORDS
def flatten(list): def flatten(list):
l = [] l = []
for elt in list: for elt in list:
t = type(elt) t = type(elt)
if t is TupleType or t is ListType: if t is tuple or t is list:
for elt2 in flatten(elt): for elt2 in flatten(elt):
l.append(elt2) l.append(elt2)
else: else:
@ -245,29 +248,17 @@ def flatten(list):
def flatten_nodes(list): def flatten_nodes(list):
return [n for n in flatten(list) if isinstance(n, Node)] return [n for n in flatten(list) if isinstance(n, Node)]
def asList(nodearg):
l = []
for item in nodearg:
if hasattr(item, "asList"):
l.append(item.asList())
else:
t = type(item)
if t is TupleType or t is ListType:
l.append(tuple(asList(item)))
else:
l.append(item)
return l
nodes = {} nodes = {}
class Node: # an abstract base class class Node:
lineno = None # provide a lineno for nodes that don't have one """Abstract base class for ast nodes."""
def getType(self):
pass # implemented by subclass
def getChildren(self): def getChildren(self):
pass # implemented by subclasses pass # implemented by subclasses
def asList(self): def __iter__(self):
return tuple(asList(self.getChildren())) for n in self.getChildren():
yield n
def asList(self): # for backwards compatibility
return self.getChildren()
def getChildNodes(self): def getChildNodes(self):
pass # implemented by subclasses pass # implemented by subclasses
@ -290,6 +281,6 @@ class Expression(Node):
return "Expression(%s)" % (repr(self.node)) return "Expression(%s)" % (repr(self.node))
### EPILOGUE ### EPILOGUE
klasses = globals() for name, obj in globals().items():
for k in nodes.keys(): if isinstance(obj, type) and issubclass(obj, Node):
nodes[k] = klasses[nodes[k]] nodes[name.lower()] = obj