diff options
author | Jeremy Hylton <jeremy@alum.mit.edu> | 2000-10-13 21:58:13 (GMT) |
---|---|---|
committer | Jeremy Hylton <jeremy@alum.mit.edu> | 2000-10-13 21:58:13 (GMT) |
commit | 9c048f9f6565d9a83890d0834f5f9b4be3a7cd8b (patch) | |
tree | 64a498541cb97457d65910054a83353d35246636 /Lib/compiler/transformer.py | |
parent | 5bad5a4be277920f10414cce238376275d40f32b (diff) | |
download | cpython-9c048f9f6565d9a83890d0834f5f9b4be3a7cd8b.zip cpython-9c048f9f6565d9a83890d0834f5f9b4be3a7cd8b.tar.gz cpython-9c048f9f6565d9a83890d0834f5f9b4be3a7cd8b.tar.bz2 |
Now supports entire Python 2.0 language and still supports Python
1.5.2. The compiler generates code for the version of the interpreter
it is run under.
ast.py:
Print and Printnl add dest attr for extended print
new node AugAssign for augmented assignments
new nodes ListComp, ListCompFor, and ListCompIf for list
comprehensions
pyassem.py:
add work around for string-Unicode comparison raising UnicodeError
on comparison of two objects in code object's const table
pycodegen.py:
define VERSION, the Python major version number
get magic number using imp.get_magic() instead of hard coding
implement list comprehensions, extended print, and augmented
assignment; augmented assignment uses Delegator classes (see
doc string)
fix import and tuple unpacking for 1.5.2
transformer.py:
various changes to support new 2.0 grammar and old 1.5 grammar
add debug_tree helper than converts and symbol and token numbers
to their names
Diffstat (limited to 'Lib/compiler/transformer.py')
-rw-r--r-- | Lib/compiler/transformer.py | 207 |
1 files changed, 159 insertions, 48 deletions
diff --git a/Lib/compiler/transformer.py b/Lib/compiler/transformer.py index c8a8518..a97798d 100644 --- a/Lib/compiler/transformer.py +++ b/Lib/compiler/transformer.py @@ -1,19 +1,3 @@ -# -# Copyright (C) 1997-1998 Greg Stein. All Rights Reserved. -# -# This module is provided under a BSD-ish license. See -# http://www.opensource.org/licenses/bsd-license.html -# and replace OWNER, ORGANIZATION, and YEAR as appropriate. -# -# -# Written by Greg Stein (gstein@lyra.org) -# and Bill Tutt (rassilon@lima.mudlib.org) -# February 1997. -# -# Support for ast.Node subclasses written and other revisions by -# Jeremy Hylton (jeremy@beopen.com) -# - """Parse tree transformation module. Transforms Python source code into an abstract syntax tree (AST) @@ -24,7 +8,21 @@ parse(buf) -> AST parseFile(path) -> AST """ +# Original version written by Greg Stein (gstein@lyra.org) +# and Bill Tutt (rassilon@lima.mudlib.org) +# February 1997. # +# Modifications and improvements for Python 2.0 by Jeremy Hylton and +# Mark Hammond + +# Portions of this file are: +# Copyright (C) 1997-1998 Greg Stein. All Rights Reserved. +# +# This module is provided under a BSD-ish license. See +# http://www.opensource.org/licenses/bsd-license.html +# and replace OWNER, ORGANIZATION, and YEAR as appropriate. + + # The output tree has the following nodes: # # Source Python line #'s appear at the end of each of all of these nodes @@ -49,9 +47,10 @@ parseFile(path) -> AST # tryexcept: trySuiteNode, [ (exprNode, assgnNode, suiteNode), ... ], elseNode # return: valueNode # const: value -# print: [ node1, ..., nodeN ] -# printnl: [ node1, ..., nodeN ] +# print: [ node1, ..., nodeN ] [, dest] +# printnl: [ node1, ..., nodeN ] [, dest] # discard: exprNode +# augassign: node, op, expr # assign: [ node1, ..., nodeN ], exprNode # ass_tuple: [ node1, ..., nodeN ] # ass_list: [ node1, ..., nodeN ] @@ -97,12 +96,12 @@ parseFile(path) -> AST import ast 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 token import string -import pprint - error = 'walker.error' from consts import CO_VARARGS, CO_VARKEYWORDS @@ -328,27 +327,44 @@ class Transformer: # def expr_stmt(self, nodelist): - # testlist ('=' testlist)* + # augassign testlist | testlist ('=' testlist)* exprNode = self.com_node(nodelist[-1]) if len(nodelist) == 1: return Node('discard', exprNode) - nodes = [ ] - for i in range(0, len(nodelist) - 2, 2): - nodes.append(self.com_assign(nodelist[i], OP_ASSIGN)) - n = Node('assign', nodes, exprNode) - n.lineno = nodelist[1][2] + if nodelist[1][0] == token.EQUAL: + nodes = [ ] + for i in range(0, len(nodelist) - 2, 2): + nodes.append(self.com_assign(nodelist[i], OP_ASSIGN)) + n = Node('assign', nodes, exprNode) + n.lineno = nodelist[1][2] + else: + lval = self.com_augassign(nodelist[0]) + op = self.com_augassign_op(nodelist[1]) + n = Node('augassign', lval, op[1], exprNode) + n.lineno = op[2] return n def print_stmt(self, nodelist): - # print: (test ',')* [test] + # print ([ test (',' test)* [','] ] | '>>' test [ (',' test)+ [','] ]) items = [ ] - for i in range(1, len(nodelist), 2): + if len(nodelist) == 1: + start = 1 + dest = None + elif nodelist[1][0] == token.RIGHTSHIFT: + assert len(nodelist) == 3 \ + or nodelist[3][0] == token.COMMA + dest = self.com_node(nodelist[2]) + start = 4 + else: + dest = None + start = 1 + for i in range(start, len(nodelist), 2): items.append(self.com_node(nodelist[i])) if nodelist[-1][0] == token.COMMA: - n = Node('print', items) + n = Node('print', items, dest) n.lineno = nodelist[0][2] return n - n = Node('printnl', items) + n = Node('printnl', items, dest) n.lineno = nodelist[0][2] return n @@ -405,17 +421,24 @@ class Transformer: # import_stmt: 'import' dotted_as_name (',' dotted_as_name)* | # from: 'from' dotted_name 'import' # ('*' | import_as_name (',' import_as_name)*) - names = [] - is_as = 0 if nodelist[0][1] == 'from': - for i in range(3, len(nodelist), 2): - names.append(self.com_import_as_name(nodelist[i][1])) + names = [] + if nodelist[3][0] == token.NAME: + for i in range(3, len(nodelist), 2): + names.append((nodelist[i][1], None)) + else: + for i in range(3, len(nodelist), 2): + names.append(self.com_import_as_name(nodelist[i][1])) n = Node('from', self.com_dotted_name(nodelist[1]), names) n.lineno = nodelist[0][2] return n - for i in range(1, len(nodelist), 2): - names.append(self.com_dotted_as_name(nodelist[i])) + if nodelist[1][0] == symbol.dotted_name: + names = [(self.com_dotted_name(nodelist[1][1:]), None)] + else: + names = [] + for i in range(1, len(nodelist), 2): + names.append(self.com_dotted_as_name(nodelist[i])) n = Node('import', names) n.lineno = nodelist[0][2] return n @@ -737,7 +760,7 @@ class Transformer: return Node('discard', Node('const', None)) if node[0] not in _legal_node_types: - raise error, 'illegal node passed to com_node: %s' % node[0] + raise error, 'illegal node passed to com_node: %s' % `node` # print "dispatch", self._dispatch[node[0]].__name__, node return self._dispatch[node[0]](node[1:]) @@ -818,11 +841,14 @@ class Transformer: def com_dotted_as_name(self, node): dot = self.com_dotted_name(node[1]) - if len(node) == 2: + if len(node) <= 2: return dot, None - assert node[2][1] == 'as' - assert node[3][0] == token.NAME - return dot, node[3][1] + if node[0] == symbol.dotted_name: + pass + else: + assert node[2][1] == 'as' + assert node[3][0] == token.NAME + return dot, node[3][1] def com_import_as_name(self, node): if node == '*': @@ -872,6 +898,20 @@ class Transformer: n.lineno = nodelist[0][2] return n + def com_augassign_op(self, node): + assert node[0] == symbol.augassign + return node[1] + + def com_augassign(self, node): + """Return node suitable for lvalue of augmented assignment + + Names, slices, and attributes are the only allowable nodes. + """ + l = self.com_node(node) + if l[0] in ('name', 'slice', 'subscript', 'getattr'): + return l + raise SyntaxError, "can't assign to %s" % l[0] + def com_assign(self, node, assigning): # return a node suitable for use as an "lvalue" # loop to avoid trivial recursion @@ -955,7 +995,6 @@ class Transformer: return Node(type, items) def com_stmt(self, node): - #pprint.pprint(node) result = self.com_node(node) try: result[0] @@ -976,17 +1015,71 @@ class Transformer: else: stmts.append(result) - def com_list_constructor(self, nodelist): - values = [ ] - for i in range(1, len(nodelist), 2): - values.append(self.com_node(nodelist[i])) - return Node('list', values) + if hasattr(symbol, 'list_for'): + def com_list_constructor(self, nodelist): + # listmaker: test ( list_for | (',' test)* [','] ) + values = [ ] + for i in range(1, len(nodelist)): + if nodelist[i][0] == symbol.list_for: + assert len(nodelist[i:]) == 1 + return self.com_list_comprehension(values[0], + nodelist[i]) + elif nodelist[i][0] == token.COMMA: + continue + values.append(self.com_node(nodelist[i])) + return Node('list', values) + + def com_list_comprehension(self, expr, node): + # list_iter: list_for | list_if + # list_for: 'for' exprlist 'in' testlist [list_iter] + # list_if: 'if' test [list_iter] + lineno = node[1][2] + fors = [] + while node: + if node[1][1] == 'for': + assignNode = self.com_assign(node[2], OP_ASSIGN) + listNode = self.com_node(node[4]) + newfor = Node('listcomp_for', assignNode, + listNode, []) + newfor.lineno = node[1][2] + fors.append(newfor) + if len(node) == 5: + node = None + else: + node = self.com_list_iter(node[5]) + elif node[1][1] == 'if': + test = self.com_node(node[2]) + newif = Node('listcomp_if', test) + newif.lineno = node[1][2] + newfor.ifs.append(newif) + if len(node) == 3: + node = None + else: + node = self.com_list_iter(node[3]) + else: + raise SyntaxError, \ + ("unexpected list comprehension element: %s %d" + % (node, lineno)) + n = Node('listcomp', expr, fors) + n.lineno = lineno + return n + + def com_list_iter(self, node): + assert node[0] == symbol.list_iter + return node[1] + else: + def com_list_constructor(self, nodelist): + values = [ ] + for i in range(1, len(nodelist), 2): + values.append(self.com_node(nodelist[i])) + return Node('list', values) def com_dictmaker(self, nodelist): # dictmaker: test ':' test (',' test ':' value)* [','] items = [ ] for i in range(1, len(nodelist), 4): - items.append((self.com_node(nodelist[i]), self.com_node(nodelist[i+2]))) + items.append((self.com_node(nodelist[i]), + self.com_node(nodelist[i+2]))) return Node('dict', items) def com_apply_trailer(self, primaryNode, nodelist): @@ -1250,3 +1343,21 @@ _assign_types = [ symbol.term, symbol.factor, ] + +import types +_names = {} +for k, v in symbol.sym_name.items(): + _names[k] = v +for k, v in token.tok_name.items(): + _names[k] = v + +def debug_tree(tree): + l = [] + for elt in tree: + if type(elt) == types.IntType: + l.append(_names.get(elt, elt)) + elif type(elt) == types.StringType: + l.append(elt) + else: + l.append(debug_tree(elt)) + return l |