from cStringIO
import StringIO
from compiler
import ast
, parse
, walk
, syntax
from compiler
import pyassem
, misc
, future
, symbols
from compiler
.consts
import SC_LOCAL
, SC_GLOBAL
, SC_FREE
, SC_CELL
from compiler
.consts
import CO_VARARGS
, CO_VARKEYWORDS
, CO_NEWLOCALS
,\
CO_NESTED
, CO_GENERATOR
, CO_GENERATOR_ALLOWED
, CO_FUTURE_DIVISION
from compiler
.pyassem
import TupleArg
# XXX The version-specific code can go, since this code only works with 2.x.
# Do we have Python 1.x or Python 2.x?
VERSION
= sys
.version_info
[0]
# (Have *args, Have **args) : opcode
(1,0) : "CALL_FUNCTION_VAR",
(0,1) : "CALL_FUNCTION_KW",
(1,1) : "CALL_FUNCTION_VAR_KW",
def compileFile(filename
, display
=0):
mod
= Module(buf
, filename
)
f
= open(filename
+ "c", "wb")
def compile(source
, filename
, mode
, flags
=None, dont_inherit
=None):
"""Replacement for builtin compile() function"""
if flags
is not None or dont_inherit
is not None:
raise RuntimeError, "not implemented yet"
gen
= Interactive(source
, filename
)
gen
= Module(source
, filename
)
gen
= Expression(source
, filename
)
raise ValueError("compile() 3rd arg must be 'exec' or "
class AbstractCompileMode
:
mode
= None # defined by subclass
def __init__(self
, source
, filename
):
tree
= parse(self
.source
, self
.mode
)
misc
.set_filename(self
.filename
, tree
)
pass # implemented by subclass
class Expression(AbstractCompileMode
):
gen
= ExpressionCodeGenerator(tree
)
self
.code
= gen
.getCode()
class Interactive(AbstractCompileMode
):
gen
= InteractiveCodeGenerator(tree
)
self
.code
= gen
.getCode()
class Module(AbstractCompileMode
):
def compile(self
, display
=0):
gen
= ModuleCodeGenerator(tree
)
print pprint
.pprint(tree
)
self
.code
= gen
.getCode()
f
.write(self
.getPycHeader())
marshal
.dump(self
.code
, f
)
# compile.c uses marshal to write a long directly, with
# calling the interface that would also generate a 1-byte code
# to indicate the type of the value. simplest way to get the
# same effect is to call marshal and then skip the code.
mtime
= os
.path
.getmtime(self
.filename
)
mtime
= struct
.pack('<i', mtime
)
return self
.MAGIC
+ mtime
"""Find local names in scope"""
def __init__(self
, names
=()):
self
.globals = misc
.Set()
# XXX list comprehensions and for loops
for elt
in self
.globals.elements():
if self
.names
.has_elt(elt
):
def visitDict(self
, node
):
def visitGlobal(self
, node
):
def visitFunction(self
, node
):
self
.names
.add(node
.name
)
def visitLambda(self
, node
):
def visitImport(self
, node
):
for name
, alias
in node
.names
:
self
.names
.add(alias
or name
)
def visitFrom(self
, node
):
for name
, alias
in node
.names
:
self
.names
.add(alias
or name
)
def visitClass(self
, node
):
self
.names
.add(node
.name
)
def visitAssName(self
, node
):
self
.names
.add(node
.name
)
def is_constant_false(node
):
if isinstance(node
, ast
.Const
):
"""Defines basic code generator for Python bytecode
This class is an abstract base class. Concrete subclasses must
define an __init__() that defines self.graph and then calls the
__init__() defined in this class.
The concrete class must also define the class attributes
NameFinder, FunctionGen, and ClassGen. These attributes can be
defined in the initClass() method, which is a hook for
initializing these methods after all the classes have been
optimized
= 0 # is namespace access optimized?
class_name
= None # provide default for instance variable
if self
.__initialized
is None:
self
.__class
__.__initialized
= 1
self
.locals = misc
.Stack()
self
.setups
= misc
.Stack()
self
._setupGraphDelegation
()
self
._div
_op
= "BINARY_DIVIDE"
# XXX set flags based on future features
futures
= self
.get_module().futures
if feature
== "division":
self
.graph
.setFlag(CO_FUTURE_DIVISION
)
self
._div
_op
= "BINARY_TRUE_DIVIDE"
elif feature
== "generators":
self
.graph
.setFlag(CO_GENERATOR_ALLOWED
)
"""This method is called once for each class"""
"""Verify that class is constructed correctly"""
assert hasattr(self
, 'graph')
assert getattr(self
, 'NameFinder')
assert getattr(self
, 'FunctionGen')
assert getattr(self
, 'ClassGen')
except AssertionError, msg
:
intro
= "Bad class construction for %s" % self
.__class
__.__name
__
raise AssertionError, intro
def _setupGraphDelegation(self
):
self
.emit
= self
.graph
.emit
self
.newBlock
= self
.graph
.newBlock
self
.startBlock
= self
.graph
.startBlock
self
.nextBlock
= self
.graph
.nextBlock
self
.setDocstring
= self
.graph
.setDocstring
"""Return a code object"""
return self
.graph
.getCode()
if self
.class_name
is not None:
return misc
.mangle(name
, self
.class_name
)
def parseSymbols(self
, tree
):
s
= symbols
.SymbolVisitor()
raise RuntimeError, "should be implemented by subclasses"
# Next five methods handle name access
def isLocalName(self
, name
):
return self
.locals.top().has_elt(name
)
def storeName(self
, name
):
self
._nameOp
('STORE', name
)
def loadName(self
, name
):
self
._nameOp
('LOAD', name
)
self
._nameOp
('DELETE', name
)
def _nameOp(self
, prefix
, name
):
scope
= self
.scope
.check_name(name
)
self
.emit(prefix
+ '_NAME', name
)
self
.emit(prefix
+ '_FAST', name
)
self
.emit(prefix
+ '_NAME', name
)
self
.emit(prefix
+ '_GLOBAL', name
)
elif scope
== SC_FREE
or scope
== SC_CELL
:
self
.emit(prefix
+ '_DEREF', name
)
raise RuntimeError, "unsupported scope for var %s: %d" % \
def _implicitNameOp(self
, prefix
, name
):
"""Emit name ops for names generated implicitly by for loops
The interpreter generates names that start with a period or
dollar sign. The symbol table ignores these names because
they aren't present in the program text.
self
.emit(prefix
+ '_FAST', name
)
self
.emit(prefix
+ '_NAME', name
)
# The set_lineno() function and the explicit emit() calls for
# SET_LINENO below are only used to generate the line number table.
# As of Python 2.3, the interpreter does not have a SET_LINENO
# instruction. pyassem treats SET_LINENO opcodes as a special case.
def set_lineno(self
, node
, force
=False):
"""Emit SET_LINENO if necessary.
The instruction is considered necessary if the node has a
lineno attribute and it is different than the last lineno
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 and (lineno
!= self
.last_lineno
self
.emit('SET_LINENO', lineno
)
self
.last_lineno
= lineno
# The first few visitor methods handle nodes that generator new
# code objects. They use class attributes to determine what
# specialized code generators to use.
NameFinder
= LocalNameFinder
def visitModule(self
, node
):
self
.scopes
= self
.parseSymbols(node
)
self
.scope
= self
.scopes
[node
]
self
.emit('SET_LINENO', 0)
self
.emit('LOAD_CONST', node
.doc
)
self
.storeName('__doc__')
lnf
= walk(node
.node
, self
.NameFinder(), verbose
=0)
self
.locals.push(lnf
.getLocals())
self
.emit('LOAD_CONST', None)
self
.emit('RETURN_VALUE')
def visitExpression(self
, node
):
self
.scopes
= self
.parseSymbols(node
)
self
.scope
= self
.scopes
[node
]
self
.emit('RETURN_VALUE')
def visitFunction(self
, node
):
self
._visitFuncOrLambda
(node
, isLambda
=0)
self
.setDocstring(node
.doc
)
self
.storeName(node
.name
)
def visitLambda(self
, node
):
self
._visitFuncOrLambda
(node
, isLambda
=1)
def _visitFuncOrLambda(self
, node
, isLambda
=0):
if not isLambda
and node
.decorators
:
for decorator
in node
.decorators
.nodes
:
ndecorators
= len(node
.decorators
.nodes
)
gen
= self
.FunctionGen(node
, self
.scopes
, isLambda
,
self
.class_name
, self
.get_module())
for default
in node
.defaults
:
frees
= gen
.scope
.get_free_vars()
self
.emit('LOAD_CLOSURE', name
)
self
.emit('LOAD_CONST', gen
)
self
.emit('MAKE_CLOSURE', len(node
.defaults
))
self
.emit('LOAD_CONST', gen
)
self
.emit('MAKE_FUNCTION', len(node
.defaults
))
for i
in range(ndecorators
):
self
.emit('CALL_FUNCTION', 1)
def visitClass(self
, node
):
gen
= self
.ClassGen(node
, self
.scopes
,
self
.emit('LOAD_CONST', node
.name
)
self
.emit('BUILD_TUPLE', len(node
.bases
))
frees
= gen
.scope
.get_free_vars()
self
.emit('LOAD_CLOSURE', name
)
self
.emit('LOAD_CONST', gen
)
self
.emit('MAKE_CLOSURE', 0)
self
.emit('MAKE_FUNCTION', 0)
self
.emit('CALL_FUNCTION', 0)
self
.storeName(node
.name
)
# The rest are standard visitor methods
# The next few implement control-flow statements
numtests
= len(node
.tests
)
for i
in range(numtests
):
test
, suite
= node
.tests
[i
]
if is_constant_false(test
):
# XXX will need to check generator stuff here
nextTest
= self
.newBlock()
self
.emit('JUMP_IF_FALSE', nextTest
)
self
.emit('JUMP_FORWARD', end
)
self
.startBlock(nextTest
)
def visitWhile(self
, node
):
self
.emit('SETUP_LOOP', after
)
self
.setups
.push((LOOP
, loop
))
self
.set_lineno(node
, force
=True)
self
.emit('JUMP_IF_FALSE', else_
or after
)
self
.emit('JUMP_ABSOLUTE', loop
)
self
.startBlock(else_
) # or just the POPs if not else clause
def visitFor(self
, node
):
self
.setups
.push((LOOP
, start
))
self
.emit('SETUP_LOOP', after
)
self
.set_lineno(node
, force
=1)
self
.emit('FOR_ITER', anchor
)
self
.emit('JUMP_ABSOLUTE', start
)
def visitBreak(self
, node
):
raise SyntaxError, "'break' outside loop (%s, %d)" % \
(node
.filename
, node
.lineno
)
def visitContinue(self
, node
):
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(node
.filename
, node
.lineno
)
kind
, block
= self
.setups
.top()
self
.emit('JUMP_ABSOLUTE', block
)
elif kind
== EXCEPT
or kind
== TRY_FINALLY
:
# find the block that starts the loop
kind
, loop_block
= self
.setups
[top
]
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(node
.filename
, node
.lineno
)
self
.emit('CONTINUE_LOOP', loop_block
)
elif kind
== END_FINALLY
:
msg
= "'continue' not allowed inside 'finally' clause (%s, %d)"
raise SyntaxError, msg
% (node
.filename
, node
.lineno
)
def visitTest(self
, node
, jump
):
for child
in node
.nodes
[:-1]:
self
.visit(node
.nodes
[-1])
def visitAnd(self
, node
):
self
.visitTest(node
, 'JUMP_IF_FALSE')
self
.visitTest(node
, 'JUMP_IF_TRUE')
def visitCompare(self
, node
):
cleanup
= self
.newBlock()
for op
, code
in node
.ops
[:-1]:
self
.emit('COMPARE_OP', op
)
self
.emit('JUMP_IF_FALSE', cleanup
)
# now do the last comparison
self
.emit('COMPARE_OP', op
)
self
.emit('JUMP_FORWARD', end
)
def visitListComp(self
, node
):
append
= "$append%d" % self
.__list
_count
self
.__list
_count
= self
.__list
_count
+ 1
self
.emit('BUILD_LIST', 0)
self
.emit('LOAD_ATTR', 'append')
self
._implicitNameOp
('STORE', append
)
for i
, for_
in zip(range(len(node
.quals
)), node
.quals
):
start
, anchor
= self
.visit(for_
)
stack
.insert(0, (start
, cont
, anchor
))
self
._implicitNameOp
('LOAD', append
)
self
.emit('CALL_FUNCTION', 1)
for start
, cont
, anchor
in stack
:
skip_one
= self
.newBlock()
self
.emit('JUMP_FORWARD', skip_one
)
self
.emit('JUMP_ABSOLUTE', start
)
self
._implicitNameOp
('DELETE', append
)
self
.__list
_count
= self
.__list
_count
- 1
def visitListCompFor(self
, node
):
self
.set_lineno(node
, force
=True)
self
.emit('FOR_ITER', anchor
)
def visitListCompIf(self
, node
, branch
):
self
.set_lineno(node
, force
=True)
self
.emit('JUMP_IF_FALSE', branch
)
def visitGenExpr(self
, node
):
gen
= GenExprCodeGenerator(node
, self
.scopes
, self
.class_name
,
frees
= gen
.scope
.get_free_vars()
self
.emit('LOAD_CLOSURE', name
)
self
.emit('LOAD_CONST', gen
)
self
.emit('MAKE_CLOSURE', 0)
self
.emit('LOAD_CONST', gen
)
self
.emit('MAKE_FUNCTION', 0)
# precomputation of outmost iterable
self
.visit(node
.code
.quals
[0].iter)
self
.emit('CALL_FUNCTION', 1)
def visitGenExprInner(self
, node
):
for i
, for_
in zip(range(len(node
.quals
)), node
.quals
):
start
, anchor
= self
.visit(for_
)
stack
.insert(0, (start
, cont
, anchor
))
for start
, cont
, anchor
in stack
:
skip_one
= self
.newBlock()
self
.emit('JUMP_FORWARD', skip_one
)
self
.emit('JUMP_ABSOLUTE', start
)
self
.emit('LOAD_CONST', None)
def visitGenExprFor(self
, node
):
self
.loadName('[outmost-iterable]')
self
.set_lineno(node
, force
=True)
self
.emit('FOR_ITER', anchor
)
def visitGenExprIf(self
, node
, branch
):
self
.set_lineno(node
, force
=True)
self
.emit('JUMP_IF_FALSE', branch
)
def visitAssert(self
, node
):
# XXX would be interesting to implement this via a
# transformation of the AST before this stage
# XXX AssertionError appears to be special case -- it is always
# loaded as a global even if there is a local name. I guess this
# is a sort of renaming op.
self
.emit('JUMP_IF_TRUE', end
)
self
.emit('LOAD_GLOBAL', 'AssertionError')
self
.emit('RAISE_VARARGS', 2)
self
.emit('RAISE_VARARGS', 1)
def visitRaise(self
, node
):
self
.emit('RAISE_VARARGS', n
)
def visitTryExcept(self
, node
):
handlers
= self
.newBlock()
self
.emit('SETUP_EXCEPT', handlers
)
self
.setups
.push((EXCEPT
, body
))
self
.emit('JUMP_FORWARD', lElse
)
self
.startBlock(handlers
)
last
= len(node
.handlers
) - 1
for i
in range(len(node
.handlers
)):
expr
, target
, body
= node
.handlers
[i
]
self
.emit('COMPARE_OP', 'exception match')
self
.emit('JUMP_IF_FALSE', next
)
self
.emit('JUMP_FORWARD', end
)
def visitTryFinally(self
, node
):
self
.emit('SETUP_FINALLY', final
)
self
.setups
.push((TRY_FINALLY
, body
))
self
.emit('LOAD_CONST', None)
self
.setups
.push((END_FINALLY
, final
))
def visitDiscard(self
, node
):
def visitConst(self
, node
):
self
.emit('LOAD_CONST', node
.value
)
def visitKeyword(self
, node
):
self
.emit('LOAD_CONST', node
.name
)
def visitGlobal(self
, node
):
def visitName(self
, node
):
def visitPass(self
, node
):
def visitImport(self
, node
):
for name
, alias
in node
.names
:
self
.emit('LOAD_CONST', None)
self
.emit('IMPORT_NAME', name
)
def visitFrom(self
, node
):
fromlist
= map(lambda (name
, alias
): name
, node
.names
)
self
.emit('LOAD_CONST', tuple(fromlist
))
self
.emit('IMPORT_NAME', node
.modname
)
for name
, alias
in node
.names
:
# There can only be one name w/ from ... import *
assert len(node
.names
) == 1
self
.emit('IMPORT_FROM', name
)
self
.storeName(alias
or name
)
self
.emit('IMPORT_FROM', name
)
def _resolveDots(self
, name
):
self
.emit('LOAD_ATTR', elt
)
def visitGetattr(self
, node
):
self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
# next five implement assignments
def visitAssign(self
, node
):
dups
= len(node
.nodes
) - 1
for i
in range(len(node
.nodes
)):
if isinstance(elt
, ast
.Node
):
def visitAssName(self
, node
):
if node
.flags
== 'OP_ASSIGN':
self
.storeName(node
.name
)
elif node
.flags
== 'OP_DELETE':
def visitAssAttr(self
, node
):
if node
.flags
== 'OP_ASSIGN':
self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
elif node
.flags
== 'OP_DELETE':
self
.emit('DELETE_ATTR', self
.mangle(node
.attrname
))
print "warning: unexpected flags:", node
.flags
def _visitAssSequence(self
, node
, op
='UNPACK_SEQUENCE'):
if findOp(node
) != 'OP_DELETE':
self
.emit(op
, len(node
.nodes
))
visitAssTuple
= _visitAssSequence
visitAssList
= _visitAssSequence
def visitAssTuple(self
, node
):
self
._visitAssSequence
(node
, 'UNPACK_TUPLE')
def visitAssList(self
, node
):
self
._visitAssSequence
(node
, 'UNPACK_LIST')
def visitAugAssign(self
, node
):
aug_node
= wrap_aug(node
.node
)
self
.visit(aug_node
, "load")
self
.emit(self
._augmented
_opcode
[node
.op
])
self
.visit(aug_node
, "store")
'-=' : 'INPLACE_SUBTRACT',
'*=' : 'INPLACE_MULTIPLY',
'//=': 'INPLACE_FLOOR_DIVIDE',
def visitAugName(self
, node
, mode
):
self
.storeName(node
.name
)
def visitAugGetattr(self
, node
, mode
):
self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
def visitAugSlice(self
, node
, mode
):
self
.emit('STORE_SLICE+%d' % slice)
def visitAugSubscript(self
, node
, mode
):
raise SyntaxError, "augmented assignment to tuple is not possible"
self
.visitSubscript(node
, 1)
self
.emit('STORE_SUBSCR')
def visitExec(self
, node
):
self
.emit('LOAD_CONST', None)
def visitCallFunc(self
, node
):
if isinstance(arg
, ast
.Keyword
):
if node
.star_args
is not None:
self
.visit(node
.star_args
)
if node
.dstar_args
is not None:
self
.visit(node
.dstar_args
)
have_star
= node
.star_args
is not None
have_dstar
= node
.dstar_args
is not None
opcode
= callfunc_opcode_info
[have_star
, have_dstar
]
self
.emit(opcode
, kw
<< 8 | pos
)
def visitPrint(self
, node
, newline
=0):
self
.emit('PRINT_ITEM_TO')
if node
.dest
and not newline
:
def visitPrintnl(self
, node
):
self
.visitPrint(node
, newline
=1)
self
.emit('PRINT_NEWLINE_TO')
self
.emit('PRINT_NEWLINE')
def visitReturn(self
, node
):
self
.emit('RETURN_VALUE')
def visitYield(self
, node
):
# slice and subscript stuff
def visitSlice(self
, node
, aug_flag
=None):
# aug_flag is used by visitAugSlice
if node
.flags
== 'OP_APPLY':
self
.emit('SLICE+%d' % slice)
elif node
.flags
== 'OP_ASSIGN':
self
.emit('STORE_SLICE+%d' % slice)
elif node
.flags
== 'OP_DELETE':
self
.emit('DELETE_SLICE+%d' % slice)
print "weird slice", node
.flags
def visitSubscript(self
, node
, aug_flag
=None):
self
.emit('BUILD_TUPLE', len(node
.subs
))
if node
.flags
== 'OP_APPLY':
self
.emit('BINARY_SUBSCR')
elif node
.flags
== 'OP_ASSIGN':
self
.emit('STORE_SUBSCR')
elif node
.flags
== 'OP_DELETE':
self
.emit('DELETE_SUBSCR')
def binaryOp(self
, node
, op
):
def visitAdd(self
, node
):
return self
.binaryOp(node
, 'BINARY_ADD')
def visitSub(self
, node
):
return self
.binaryOp(node
, 'BINARY_SUBTRACT')
def visitMul(self
, node
):
return self
.binaryOp(node
, 'BINARY_MULTIPLY')
def visitDiv(self
, node
):
return self
.binaryOp(node
, self
._div
_op
)
def visitFloorDiv(self
, node
):
return self
.binaryOp(node
, 'BINARY_FLOOR_DIVIDE')
def visitMod(self
, node
):
return self
.binaryOp(node
, 'BINARY_MODULO')
def visitPower(self
, node
):
return self
.binaryOp(node
, 'BINARY_POWER')
def visitLeftShift(self
, node
):
return self
.binaryOp(node
, 'BINARY_LSHIFT')
def visitRightShift(self
, node
):
return self
.binaryOp(node
, 'BINARY_RSHIFT')
def unaryOp(self
, node
, op
):
def visitInvert(self
, node
):
return self
.unaryOp(node
, 'UNARY_INVERT')
def visitUnarySub(self
, node
):
return self
.unaryOp(node
, 'UNARY_NEGATIVE')
def visitUnaryAdd(self
, node
):
return self
.unaryOp(node
, 'UNARY_POSITIVE')
def visitUnaryInvert(self
, node
):
return self
.unaryOp(node
, 'UNARY_INVERT')
def visitNot(self
, node
):
return self
.unaryOp(node
, 'UNARY_NOT')
def visitBackquote(self
, node
):
return self
.unaryOp(node
, 'UNARY_CONVERT')
def bitOp(self
, nodes
, op
):
def visitBitand(self
, node
):
return self
.bitOp(node
.nodes
, 'BINARY_AND')
def visitBitor(self
, node
):
return self
.bitOp(node
.nodes
, 'BINARY_OR')
def visitBitxor(self
, node
):
return self
.bitOp(node
.nodes
, 'BINARY_XOR')
def visitEllipsis(self
, node
):
self
.emit('LOAD_CONST', Ellipsis)
def visitTuple(self
, node
):
self
.emit('BUILD_TUPLE', len(node
.nodes
))
def visitList(self
, node
):
self
.emit('BUILD_LIST', len(node
.nodes
))
def visitSliceobj(self
, node
):
self
.emit('BUILD_SLICE', len(node
.nodes
))
def visitDict(self
, node
):
self
.emit('BUILD_MAP', 0)
self
.emit('STORE_SUBSCR')
"""Defines initClass() for nested scoping (Python 2.2-compatible)"""
self
.__class
__.NameFinder
= LocalNameFinder
self
.__class
__.FunctionGen
= FunctionCodeGenerator
self
.__class
__.ClassGen
= ClassCodeGenerator
class ModuleCodeGenerator(NestedScopeMixin
, CodeGenerator
):
__super_init
= CodeGenerator
.__init
__
def __init__(self
, tree
):
self
.graph
= pyassem
.PyFlowGraph("<module>", tree
.filename
)
self
.futures
= future
.find_futures(tree
)
class ExpressionCodeGenerator(NestedScopeMixin
, CodeGenerator
):
__super_init
= CodeGenerator
.__init
__
def __init__(self
, tree
):
self
.graph
= pyassem
.PyFlowGraph("<expression>", tree
.filename
)
class InteractiveCodeGenerator(NestedScopeMixin
, CodeGenerator
):
__super_init
= CodeGenerator
.__init
__
def __init__(self
, tree
):
self
.graph
= pyassem
.PyFlowGraph("<interactive>", tree
.filename
)
self
.emit('RETURN_VALUE')
def visitDiscard(self
, node
):
# XXX Discard means it's an expression. Perhaps this is a bad
class AbstractFunctionCode
:
def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
self
.class_name
= class_name
klass
= FunctionCodeGenerator
name
= "<lambda.%d>" % klass
.lambdaCount
klass
.lambdaCount
= klass
.lambdaCount
+ 1
args
, hasTupleArg
= generateArgList(func
.argnames
)
self
.graph
= pyassem
.PyFlowGraph(name
, func
.filename
, args
,
if not isLambda
and func
.doc
:
self
.setDocstring(func
.doc
)
lnf
= walk(func
.code
, self
.NameFinder(args
), verbose
=0)
self
.locals.push(lnf
.getLocals())
self
.graph
.setFlag(CO_VARARGS
)
self
.graph
.setFlag(CO_VARKEYWORDS
)
self
.generateArgUnpack(func
.argnames
)
self
.graph
.startExitBlock()
self
.emit('LOAD_CONST', None)
self
.emit('RETURN_VALUE')
def generateArgUnpack(self
, args
):
for i
in range(len(args
)):
if type(arg
) == types
.TupleType
:
self
.emit('LOAD_FAST', '.%d' % (i
* 2))
def unpackSequence(self
, tup
):
self
.emit('UNPACK_SEQUENCE', len(tup
))
self
.emit('UNPACK_TUPLE', len(tup
))
if type(elt
) == types
.TupleType
:
self
._nameOp
('STORE', elt
)
unpackTuple
= unpackSequence
class FunctionCodeGenerator(NestedScopeMixin
, AbstractFunctionCode
,
super_init
= CodeGenerator
.__init
__ # call be other init
__super_init
= AbstractFunctionCode
.__init
__
def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
self
.scope
= scopes
[func
]
self
.__super
_init
(func
, scopes
, isLambda
, class_name
, mod
)
self
.graph
.setFreeVars(self
.scope
.get_free_vars())
self
.graph
.setCellVars(self
.scope
.get_cell_vars())
if self
.scope
.generator
is not None:
self
.graph
.setFlag(CO_GENERATOR
)
class GenExprCodeGenerator(NestedScopeMixin
, AbstractFunctionCode
,
super_init
= CodeGenerator
.__init
__ # call be other init
__super_init
= AbstractFunctionCode
.__init
__
def __init__(self
, gexp
, scopes
, class_name
, mod
):
self
.scope
= scopes
[gexp
]
self
.__super
_init
(gexp
, scopes
, 1, class_name
, mod
)
self
.graph
.setFreeVars(self
.scope
.get_free_vars())
self
.graph
.setCellVars(self
.scope
.get_cell_vars())
self
.graph
.setFlag(CO_GENERATOR
)
def __init__(self
, klass
, scopes
, module
):
self
.class_name
= klass
.name
self
.graph
= pyassem
.PyFlowGraph(klass
.name
, klass
.filename
,
lnf
= walk(klass
.code
, self
.NameFinder(), verbose
=0)
self
.locals.push(lnf
.getLocals())
self
.graph
.setFlag(CO_NEWLOCALS
)
self
.setDocstring(klass
.doc
)
self
.graph
.startExitBlock()
self
.emit('RETURN_VALUE')
class ClassCodeGenerator(NestedScopeMixin
, AbstractClassCode
, CodeGenerator
):
super_init
= CodeGenerator
.__init
__
__super_init
= AbstractClassCode
.__init
__
def __init__(self
, klass
, scopes
, module
):
self
.scope
= scopes
[klass
]
self
.__super
_init
(klass
, scopes
, module
)
self
.graph
.setFreeVars(self
.scope
.get_free_vars())
self
.graph
.setCellVars(self
.scope
.get_cell_vars())
self
.emit("LOAD_GLOBAL", "__name__")
self
.storeName("__module__")
self
.emit("LOAD_CONST", klass
.doc
)
self
.storeName('__doc__')
def generateArgList(arglist
):
"""Generate an arg list marking TupleArgs"""
for i
in range(len(arglist
)):
if type(elt
) == types
.StringType
:
elif type(elt
) == types
.TupleType
:
args
.append(TupleArg(i
* 2, elt
))
extra
.extend(misc
.flatten(elt
))
raise ValueError, "unexpect argument type:", elt
return args
+ extra
, count
"""Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
def visitAssName(self
, node
):
elif self
.op
!= node
.flags
:
raise ValueError, "mixed ops in stmt"
visitAssAttr
= visitAssName
visitSubscript
= visitAssName
"""Base class to support delegation for augmented assignment nodes
To generator code for augmented assignments, we use the following
wrapper classes. In visitAugAssign, the left-hand expression node
is visited twice. The first time the visit uses the normal method
for that node . The second time the visit uses a different method
that generates the appropriate code to perform the assignment.
These delegator classes wrap the original AST nodes in order to
support the variant visit methods.
def __getattr__(self
, attr
):
return getattr(self
.obj
, attr
)
class AugGetattr(Delegator
):
class AugName(Delegator
):
class AugSlice(Delegator
):
class AugSubscript(Delegator
):
ast
.Subscript
: AugSubscript
,
return wrapper
[node
.__class
__](node
)
if __name__
== "__main__":
for file in sys
.argv
[1:]: