Add support for absolute/relative imports and if/else expressions:

- regenerate ast.py
 - add future flags for absolute-import and with-statement so they
   (hopefully) properly get set in code-object flags
 - try out if/else expressions in actual code for the hell of it.

Seems to generate the same kind of bytecode as the normal compiler.
This commit is contained in:
Thomas Wouters 2006-03-03 18:16:20 +00:00
parent 7e2ac2533e
commit fa0cf4f3ae
5 changed files with 82 additions and 19 deletions

View file

@ -524,19 +524,20 @@ class For(Node):
return "For(%s, %s, %s, %s)" % (repr(self.assign), repr(self.list), repr(self.body), repr(self.else_)) return "For(%s, %s, %s, %s)" % (repr(self.assign), repr(self.list), repr(self.body), repr(self.else_))
class From(Node): class From(Node):
def __init__(self, modname, names, lineno=None): def __init__(self, modname, names, level, lineno=None):
self.modname = modname self.modname = modname
self.names = names self.names = names
self.level = level
self.lineno = lineno self.lineno = lineno
def getChildren(self): def getChildren(self):
return self.modname, self.names return self.modname, self.names, self.level
def getChildNodes(self): def getChildNodes(self):
return () return ()
def __repr__(self): def __repr__(self):
return "From(%s, %s)" % (repr(self.modname), repr(self.names)) return "From(%s, %s, %s)" % (repr(self.modname), repr(self.names), repr(self.level))
class Function(Node): class Function(Node):
def __init__(self, decorators, name, argnames, defaults, flags, doc, code, lineno=None): def __init__(self, decorators, name, argnames, defaults, flags, doc, code, lineno=None):
@ -708,6 +709,22 @@ class If(Node):
def __repr__(self): def __repr__(self):
return "If(%s, %s)" % (repr(self.tests), repr(self.else_)) return "If(%s, %s)" % (repr(self.tests), repr(self.else_))
class IfExp(Node):
def __init__(self, test, then, else_, lineno=None):
self.test = test
self.then = then
self.else_ = else_
self.lineno = lineno
def getChildren(self):
return self.test, self.then, self.else_
def getChildNodes(self):
return self.test, self.then, self.else_
def __repr__(self):
return "IfExp(%s, %s, %s)" % (repr(self.test), repr(self.then), repr(self.else_))
class Import(Node): class Import(Node):
def __init__(self, names, lineno=None): def __init__(self, names, lineno=None):
self.names = names self.names = names

View file

@ -17,3 +17,5 @@ CO_NESTED = 0x0010
CO_GENERATOR = 0x0020 CO_GENERATOR = 0x0020
CO_GENERATOR_ALLOWED = 0x1000 CO_GENERATOR_ALLOWED = 0x1000
CO_FUTURE_DIVISION = 0x2000 CO_FUTURE_DIVISION = 0x2000
CO_FUTURE_ABSIMPORT = 0x4000
CO_FUTURE_WITH_STATEMENT = 0x8000

View file

@ -771,7 +771,7 @@ class StackDepthTracker:
'COMPARE_OP': -1, 'COMPARE_OP': -1,
'STORE_FAST': -1, 'STORE_FAST': -1,
'IMPORT_STAR': -1, 'IMPORT_STAR': -1,
'IMPORT_NAME': 0, 'IMPORT_NAME': -1,
'IMPORT_FROM': 1, 'IMPORT_FROM': 1,
'LOAD_ATTR': 0, # unlike other loads 'LOAD_ATTR': 0, # unlike other loads
# close enough... # close enough...

View file

@ -8,8 +8,9 @@ from cStringIO import StringIO
from compiler import ast, parse, walk, syntax from compiler import ast, parse, walk, syntax
from compiler import pyassem, misc, future, symbols from compiler import pyassem, misc, future, symbols
from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL
from compiler.consts import CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,\ from compiler.consts import (CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,
CO_NESTED, CO_GENERATOR, CO_GENERATOR_ALLOWED, CO_FUTURE_DIVISION CO_NESTED, CO_GENERATOR, CO_GENERATOR_ALLOWED, CO_FUTURE_DIVISION,
CO_FUTURE_ABSIMPORT, CO_FUTURE_WITH_STATEMENT)
from compiler.pyassem import TupleArg from compiler.pyassem import TupleArg
# XXX The version-specific code can go, since this code only works with 2.x. # XXX The version-specific code can go, since this code only works with 2.x.
@ -215,6 +216,10 @@ class CodeGenerator:
self._div_op = "BINARY_TRUE_DIVIDE" self._div_op = "BINARY_TRUE_DIVIDE"
elif feature == "generators": elif feature == "generators":
self.graph.setFlag(CO_GENERATOR_ALLOWED) self.graph.setFlag(CO_GENERATOR_ALLOWED)
elif feature == "absolute_import":
self.graph.setFlag(CO_FUTURE_ABSIMPORT)
elif feature == "with_statement":
self.graph.setFlag(CO_FUTURE_WITH_STATEMENT)
def initClass(self): def initClass(self):
"""This method is called once for each class""" """This method is called once for each class"""
@ -543,6 +548,19 @@ class CodeGenerator:
def visitOr(self, node): def visitOr(self, node):
self.visitTest(node, 'JUMP_IF_TRUE') self.visitTest(node, 'JUMP_IF_TRUE')
def visitIfExp(self, node):
endblock = self.newBlock()
elseblock = self.newBlock()
self.visit(node.test)
self.emit('JUMP_IF_FALSE', elseblock)
self.emit('POP_TOP')
self.visit(node.then)
self.emit('JUMP_FORWARD', endblock)
self.nextBlock(elseblock)
self.emit('POP_TOP')
self.visit(node.else_)
self.nextBlock(endblock)
def visitCompare(self, node): def visitCompare(self, node):
self.visit(node.expr) self.visit(node.expr)
cleanup = self.newBlock() cleanup = self.newBlock()
@ -875,8 +893,10 @@ class CodeGenerator:
def visitImport(self, node): def visitImport(self, node):
self.set_lineno(node) self.set_lineno(node)
level = 0 if "absolute_import" in self.futures else -1
for name, alias in node.names: for name, alias in node.names:
if VERSION > 1: if VERSION > 1:
self.emit('LOAD_CONST', level)
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.emit('IMPORT_NAME', name) self.emit('IMPORT_NAME', name)
mod = name.split(".")[0] mod = name.split(".")[0]
@ -888,8 +908,12 @@ class CodeGenerator:
def visitFrom(self, node): def visitFrom(self, node):
self.set_lineno(node) self.set_lineno(node)
level = node.level
if level == 0 and "absolute_import" not in self.futures:
level = -1
fromlist = map(lambda (name, alias): name, node.names) fromlist = map(lambda (name, alias): name, node.names)
if VERSION > 1: if VERSION > 1:
self.emit('LOAD_CONST', level)
self.emit('LOAD_CONST', tuple(fromlist)) self.emit('LOAD_CONST', tuple(fromlist))
self.emit('IMPORT_NAME', node.modname) self.emit('IMPORT_NAME', node.modname)
for name, alias in node.names: for name, alias in node.names:

View file

@ -441,18 +441,25 @@ class Transformer:
lineno=nodelist[0][2]) lineno=nodelist[0][2])
def import_from(self, nodelist): def import_from(self, nodelist):
# import_from: 'from' dotted_name 'import' ('*' | # import_from: 'from' ('.'* dotted_name | '.') 'import' ('*' |
# '(' import_as_names ')' | import_as_names) # '(' import_as_names ')' | import_as_names)
assert nodelist[0][1] == 'from' assert nodelist[0][1] == 'from'
assert nodelist[1][0] == symbol.dotted_name idx = 1
assert nodelist[2][1] == 'import' while nodelist[idx][1] == '.':
fromname = self.com_dotted_name(nodelist[1]) idx += 1
if nodelist[3][0] == token.STAR: level = idx - 1
return From(fromname, [('*', None)], if nodelist[idx][0] == symbol.dotted_name:
fromname = self.com_dotted_name(nodelist[idx])
idx += 1
else:
fromname = ""
assert nodelist[idx][1] == 'import'
if nodelist[idx + 1][0] == token.STAR:
return From(fromname, [('*', None)], level,
lineno=nodelist[0][2]) lineno=nodelist[0][2])
else: else:
node = nodelist[3 + (nodelist[3][0] == token.LPAR)] node = nodelist[idx + 1 + (nodelist[idx + 1][0] == token.LPAR)]
return From(fromname, self.com_import_as_names(node), return From(fromname, self.com_import_as_names(node), level,
lineno=nodelist[0][2]) lineno=nodelist[0][2])
def global_stmt(self, nodelist): def global_stmt(self, nodelist):
@ -575,12 +582,25 @@ class Transformer:
return self.testlist(nodelist) return self.testlist(nodelist)
def test(self, nodelist): def test(self, nodelist):
# or_test ['if' or_test 'else' test] | lambdef
if len(nodelist) == 1 and nodelist[0][0] == symbol.lambdef:
return self.lambdef(nodelist[0])
then = self.com_node(nodelist[0])
if len(nodelist) > 1:
assert len(nodelist) == 5
assert nodelist[1][1] == 'if'
assert nodelist[3][1] == 'else'
test = self.com_node(nodelist[2])
else_ = self.com_node(nodelist[4])
return IfExp(test, then, else_, lineno=nodelist[1][2])
return then
def or_test(self, nodelist):
# and_test ('or' and_test)* | lambdef # and_test ('or' and_test)* | lambdef
if len(nodelist) == 1 and nodelist[0][0] == symbol.lambdef: if len(nodelist) == 1 and nodelist[0][0] == symbol.lambdef:
return self.lambdef(nodelist[0]) return self.lambdef(nodelist[0])
return self.com_binary(Or, nodelist) return self.com_binary(Or, nodelist)
or_test = test old_test = or_test
old_test = test
def and_test(self, nodelist): def and_test(self, nodelist):
# not_test ('and' not_test)* # not_test ('and' not_test)*