9 from cStringIO
import StringIO
11 from compiler
import ast
, parse
, walk
, syntax
12 from compiler
import pyassem
, misc
, future
, symbols
13 from compiler
.consts
import SC_LOCAL
, SC_GLOBAL
, SC_FREE
, SC_CELL
14 from compiler
.consts
import CO_VARARGS
, CO_VARKEYWORDS
, CO_NEWLOCALS
,\
15 CO_NESTED
, CO_GENERATOR
, CO_GENERATOR_ALLOWED
, CO_FUTURE_DIVISION
16 from compiler
.pyassem
import TupleArg
18 # Do we have Python 1.x or Python 2.x?
20 VERSION
= sys
.version_info
[0]
21 except AttributeError:
24 callfunc_opcode_info
= {
25 # (Have *args, Have **args) : opcode
26 (0,0) : "CALL_FUNCTION",
27 (1,0) : "CALL_FUNCTION_VAR",
28 (0,1) : "CALL_FUNCTION_KW",
29 (1,1) : "CALL_FUNCTION_VAR_KW",
37 # XXX this doesn't seem to be used
38 class BlockStack(misc
.Stack
):
39 __super_init
= misc
.Stack
.__init
__
42 self
.__super
_init
(self
)
45 def compileFile(filename
, display
=0):
49 mod
= Module(buf
, filename
)
52 except SyntaxError, err
:
55 f
= open(filename
+ "c", "wb")
59 def compile(source
, filename
, mode
, flags
=None, dont_inherit
=None):
60 """Replacement for builtin compile() function"""
61 if flags
is not None or dont_inherit
is not None:
62 raise RuntimeError, "not implemented yet"
65 gen
= Interactive(source
, filename
)
67 gen
= Module(source
, filename
)
69 gen
= Expression(source
, filename
)
71 raise ValueError("compile() 3rd arg must be 'exec' or "
76 class AbstractCompileMode
:
78 mode
= None # defined by subclass
80 def __init__(self
, source
, filename
):
82 self
.filename
= filename
86 tree
= parse(self
.source
, self
.mode
)
87 misc
.set_filename(self
.filename
, tree
)
92 pass # implemented by subclass
97 class Expression(AbstractCompileMode
):
102 tree
= self
._get
_tree
()
103 gen
= ExpressionCodeGenerator(tree
)
104 self
.code
= gen
.getCode()
106 class Interactive(AbstractCompileMode
):
111 tree
= self
._get
_tree
()
112 gen
= InteractiveCodeGenerator(tree
)
113 self
.code
= gen
.getCode()
115 class Module(AbstractCompileMode
):
119 def compile(self
, display
=0):
120 tree
= self
._get
_tree
()
121 gen
= ModuleCodeGenerator(tree
)
124 print pprint
.pprint(tree
)
125 self
.code
= gen
.getCode()
128 f
.write(self
.getPycHeader())
129 marshal
.dump(self
.code
, f
)
131 MAGIC
= imp
.get_magic()
133 def getPycHeader(self
):
134 # compile.c uses marshal to write a long directly, with
135 # calling the interface that would also generate a 1-byte code
136 # to indicate the type of the value. simplest way to get the
137 # same effect is to call marshal and then skip the code.
138 mtime
= os
.stat(self
.filename
)[stat
.ST_MTIME
]
139 mtime
= struct
.pack('<i', mtime
)
140 return self
.MAGIC
+ mtime
142 class LocalNameFinder
:
143 """Find local names in scope"""
144 def __init__(self
, names
=()):
145 self
.names
= misc
.Set()
146 self
.globals = misc
.Set()
150 # XXX list comprehensions and for loops
153 for elt
in self
.globals.elements():
154 if self
.names
.has_elt(elt
):
155 self
.names
.remove(elt
)
158 def visitDict(self
, node
):
161 def visitGlobal(self
, node
):
162 for name
in node
.names
:
163 self
.globals.add(name
)
165 def visitFunction(self
, node
):
166 self
.names
.add(node
.name
)
168 def visitLambda(self
, node
):
171 def visitImport(self
, node
):
172 for name
, alias
in node
.names
:
173 self
.names
.add(alias
or name
)
175 def visitFrom(self
, node
):
176 for name
, alias
in node
.names
:
177 self
.names
.add(alias
or name
)
179 def visitClass(self
, node
):
180 self
.names
.add(node
.name
)
182 def visitAssName(self
, node
):
183 self
.names
.add(node
.name
)
185 def is_constant_false(node
):
186 if isinstance(node
, ast
.Const
):
192 """Defines basic code generator for Python bytecode
194 This class is an abstract base class. Concrete subclasses must
195 define an __init__() that defines self.graph and then calls the
196 __init__() defined in this class.
198 The concrete class must also define the class attributes
199 NameFinder, FunctionGen, and ClassGen. These attributes can be
200 defined in the initClass() method, which is a hook for
201 initializing these methods after all the classes have been
205 optimized
= 0 # is namespace access optimized?
207 class_name
= None # provide default for instance variable
210 if self
.__initialized
is None:
212 self
.__class
__.__initialized
= 1
214 self
.locals = misc
.Stack()
215 self
.setups
= misc
.Stack()
218 self
.last_lineno
= None
219 self
._setupGraphDelegation
()
220 self
._div
_op
= "BINARY_DIVIDE"
222 # XXX set flags based on future features
223 futures
= self
.get_module().futures
224 for feature
in futures
:
225 if feature
== "division":
226 self
.graph
.setFlag(CO_FUTURE_DIVISION
)
227 self
._div
_op
= "BINARY_TRUE_DIVIDE"
228 elif feature
== "generators":
229 self
.graph
.setFlag(CO_GENERATOR_ALLOWED
)
232 """This method is called once for each class"""
234 def checkClass(self
):
235 """Verify that class is constructed correctly"""
237 assert hasattr(self
, 'graph')
238 assert getattr(self
, 'NameFinder')
239 assert getattr(self
, 'FunctionGen')
240 assert getattr(self
, 'ClassGen')
241 except AssertionError, msg
:
242 intro
= "Bad class construction for %s" % self
.__class
__.__name
__
243 raise AssertionError, intro
245 def _setupGraphDelegation(self
):
246 self
.emit
= self
.graph
.emit
247 self
.newBlock
= self
.graph
.newBlock
248 self
.startBlock
= self
.graph
.startBlock
249 self
.nextBlock
= self
.graph
.nextBlock
250 self
.setDocstring
= self
.graph
.setDocstring
253 """Return a code object"""
254 return self
.graph
.getCode()
256 def mangle(self
, name
):
257 if self
.class_name
is not None:
258 return misc
.mangle(name
, self
.class_name
)
262 def parseSymbols(self
, tree
):
263 s
= symbols
.SymbolVisitor()
267 def get_module(self
):
268 raise RuntimeError, "should be implemented by subclasses"
270 # Next five methods handle name access
272 def isLocalName(self
, name
):
273 return self
.locals.top().has_elt(name
)
275 def storeName(self
, name
):
276 self
._nameOp
('STORE', name
)
278 def loadName(self
, name
):
279 self
._nameOp
('LOAD', name
)
281 def delName(self
, name
):
282 self
._nameOp
('DELETE', name
)
284 def _nameOp(self
, prefix
, name
):
285 name
= self
.mangle(name
)
286 scope
= self
.scope
.check_name(name
)
287 if scope
== SC_LOCAL
:
288 if not self
.optimized
:
289 self
.emit(prefix
+ '_NAME', name
)
291 self
.emit(prefix
+ '_FAST', name
)
292 elif scope
== SC_GLOBAL
:
293 if not self
.optimized
:
294 self
.emit(prefix
+ '_NAME', name
)
296 self
.emit(prefix
+ '_GLOBAL', name
)
297 elif scope
== SC_FREE
or scope
== SC_CELL
:
298 self
.emit(prefix
+ '_DEREF', name
)
300 raise RuntimeError, "unsupported scope for var %s: %d" % \
303 def _implicitNameOp(self
, prefix
, name
):
304 """Emit name ops for names generated implicitly by for loops
306 The interpreter generates names that start with a period or
307 dollar sign. The symbol table ignores these names because
308 they aren't present in the program text.
311 self
.emit(prefix
+ '_FAST', name
)
313 self
.emit(prefix
+ '_NAME', name
)
315 def set_lineno(self
, node
, force
=0):
316 """Emit SET_LINENO if node has lineno attribute and it is
317 different than the last lineno emitted.
319 Returns true if SET_LINENO was emitted.
321 There are no rules for when an AST node should have a lineno
322 attribute. The transformer and AST code need to be reviewed
323 and a consistent policy implemented and documented. Until
324 then, this method works around missing line numbers.
326 lineno
= getattr(node
, 'lineno', None)
327 if lineno
is not None and (lineno
!= self
.last_lineno
329 self
.emit('SET_LINENO', lineno
)
330 self
.last_lineno
= lineno
334 # The first few visitor methods handle nodes that generator new
335 # code objects. They use class attributes to determine what
336 # specialized code generators to use.
338 NameFinder
= LocalNameFinder
342 def visitModule(self
, node
):
343 self
.scopes
= self
.parseSymbols(node
)
344 self
.scope
= self
.scopes
[node
]
345 self
.emit('SET_LINENO', 0)
347 self
.emit('LOAD_CONST', node
.doc
)
348 self
.storeName('__doc__')
349 lnf
= walk(node
.node
, self
.NameFinder(), verbose
=0)
350 self
.locals.push(lnf
.getLocals())
351 self
.visit(node
.node
)
352 self
.emit('LOAD_CONST', None)
353 self
.emit('RETURN_VALUE')
355 def visitExpression(self
, node
):
356 self
.set_lineno(node
)
357 self
.scopes
= self
.parseSymbols(node
)
358 self
.scope
= self
.scopes
[node
]
359 self
.visit(node
.node
)
360 self
.emit('RETURN_VALUE')
362 def visitFunction(self
, node
):
363 self
._visitFuncOrLambda
(node
, isLambda
=0)
365 self
.setDocstring(node
.doc
)
366 self
.storeName(node
.name
)
368 def visitLambda(self
, node
):
369 self
._visitFuncOrLambda
(node
, isLambda
=1)
371 def _visitFuncOrLambda(self
, node
, isLambda
=0):
372 gen
= self
.FunctionGen(node
, self
.scopes
, isLambda
,
373 self
.class_name
, self
.get_module())
376 self
.set_lineno(node
)
377 for default
in node
.defaults
:
379 frees
= gen
.scope
.get_free_vars()
382 self
.emit('LOAD_CLOSURE', name
)
383 self
.emit('LOAD_CONST', gen
)
384 self
.emit('MAKE_CLOSURE', len(node
.defaults
))
386 self
.emit('LOAD_CONST', gen
)
387 self
.emit('MAKE_FUNCTION', len(node
.defaults
))
389 def visitClass(self
, node
):
390 gen
= self
.ClassGen(node
, self
.scopes
,
394 self
.set_lineno(node
)
395 self
.emit('LOAD_CONST', node
.name
)
396 for base
in node
.bases
:
398 self
.emit('BUILD_TUPLE', len(node
.bases
))
399 frees
= gen
.scope
.get_free_vars()
401 self
.emit('LOAD_CLOSURE', name
)
402 self
.emit('LOAD_CONST', gen
)
404 self
.emit('MAKE_CLOSURE', 0)
406 self
.emit('MAKE_FUNCTION', 0)
407 self
.emit('CALL_FUNCTION', 0)
408 self
.emit('BUILD_CLASS')
409 self
.storeName(node
.name
)
411 # The rest are standard visitor methods
413 # The next few implement control-flow statements
415 def visitIf(self
, node
):
416 end
= self
.newBlock()
417 numtests
= len(node
.tests
)
418 for i
in range(numtests
):
419 test
, suite
= node
.tests
[i
]
420 if is_constant_false(test
):
421 # XXX will need to check generator stuff here
423 self
.set_lineno(test
)
425 nextTest
= self
.newBlock()
426 self
.emit('JUMP_IF_FALSE', nextTest
)
430 self
.emit('JUMP_FORWARD', end
)
431 self
.startBlock(nextTest
)
434 self
.visit(node
.else_
)
437 def visitWhile(self
, node
):
438 self
.set_lineno(node
)
440 loop
= self
.newBlock()
441 else_
= self
.newBlock()
443 after
= self
.newBlock()
444 self
.emit('SETUP_LOOP', after
)
447 self
.setups
.push((LOOP
, loop
))
449 self
.set_lineno(node
, force
=1)
450 self
.visit(node
.test
)
451 self
.emit('JUMP_IF_FALSE', else_
or after
)
455 self
.visit(node
.body
)
456 self
.emit('JUMP_ABSOLUTE', loop
)
458 self
.startBlock(else_
) # or just the POPs if not else clause
460 self
.emit('POP_BLOCK')
463 self
.visit(node
.else_
)
464 self
.nextBlock(after
)
466 def visitFor(self
, node
):
467 start
= self
.newBlock()
468 anchor
= self
.newBlock()
469 after
= self
.newBlock()
470 self
.setups
.push((LOOP
, start
))
472 self
.set_lineno(node
)
473 self
.emit('SETUP_LOOP', after
)
474 self
.visit(node
.list)
475 self
.emit('GET_ITER')
477 self
.nextBlock(start
)
478 self
.set_lineno(node
, force
=1)
479 self
.emit('FOR_ITER', anchor
)
480 self
.visit(node
.assign
)
481 self
.visit(node
.body
)
482 self
.emit('JUMP_ABSOLUTE', start
)
483 self
.nextBlock(anchor
)
484 self
.emit('POP_BLOCK')
487 self
.visit(node
.else_
)
488 self
.nextBlock(after
)
490 def visitBreak(self
, node
):
492 raise SyntaxError, "'break' outside loop (%s, %d)" % \
493 (node
.filename
, node
.lineno
)
494 self
.set_lineno(node
)
495 self
.emit('BREAK_LOOP')
497 def visitContinue(self
, node
):
499 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
500 (node
.filename
, node
.lineno
)
501 kind
, block
= self
.setups
.top()
503 self
.set_lineno(node
)
504 self
.emit('JUMP_ABSOLUTE', block
)
506 elif kind
== EXCEPT
or kind
== TRY_FINALLY
:
507 self
.set_lineno(node
)
508 # find the block that starts the loop
509 top
= len(self
.setups
)
512 kind
, loop_block
= self
.setups
[top
]
516 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
517 (node
.filename
, node
.lineno
)
518 self
.emit('CONTINUE_LOOP', loop_block
)
520 elif kind
== END_FINALLY
:
521 msg
= "'continue' not allowed inside 'finally' clause (%s, %d)"
522 raise SyntaxError, msg
% (node
.filename
, node
.lineno
)
524 def visitTest(self
, node
, jump
):
525 end
= self
.newBlock()
526 for child
in node
.nodes
[:-1]:
531 self
.visit(node
.nodes
[-1])
534 def visitAnd(self
, node
):
535 self
.visitTest(node
, 'JUMP_IF_FALSE')
537 def visitOr(self
, node
):
538 self
.visitTest(node
, 'JUMP_IF_TRUE')
540 def visitCompare(self
, node
):
541 self
.visit(node
.expr
)
542 cleanup
= self
.newBlock()
543 for op
, code
in node
.ops
[:-1]:
546 self
.emit('ROT_THREE')
547 self
.emit('COMPARE_OP', op
)
548 self
.emit('JUMP_IF_FALSE', cleanup
)
551 # now do the last comparison
553 op
, code
= node
.ops
[-1]
555 self
.emit('COMPARE_OP', op
)
556 if len(node
.ops
) > 1:
557 end
= self
.newBlock()
558 self
.emit('JUMP_FORWARD', end
)
559 self
.startBlock(cleanup
)
564 # list comprehensions
567 def visitListComp(self
, node
):
568 self
.set_lineno(node
)
570 append
= "$append%d" % self
.__list
_count
571 self
.__list
_count
= self
.__list
_count
+ 1
572 self
.emit('BUILD_LIST', 0)
574 self
.emit('LOAD_ATTR', 'append')
575 self
._implicitNameOp
('STORE', append
)
578 for i
, for_
in zip(range(len(node
.quals
)), node
.quals
):
579 start
, anchor
= self
.visit(for_
)
583 cont
= self
.newBlock()
584 self
.visit(if_
, cont
)
585 stack
.insert(0, (start
, cont
, anchor
))
587 self
._implicitNameOp
('LOAD', append
)
588 self
.visit(node
.expr
)
589 self
.emit('CALL_FUNCTION', 1)
592 for start
, cont
, anchor
in stack
:
594 skip_one
= self
.newBlock()
595 self
.emit('JUMP_FORWARD', skip_one
)
596 self
.startBlock(cont
)
598 self
.nextBlock(skip_one
)
599 self
.emit('JUMP_ABSOLUTE', start
)
600 self
.startBlock(anchor
)
601 self
._implicitNameOp
('DELETE', append
)
603 self
.__list
_count
= self
.__list
_count
- 1
605 def visitListCompFor(self
, node
):
606 start
= self
.newBlock()
607 anchor
= self
.newBlock()
609 self
.visit(node
.list)
610 self
.emit('GET_ITER')
611 self
.nextBlock(start
)
612 self
.emit('SET_LINENO', node
.lineno
)
613 self
.emit('FOR_ITER', anchor
)
615 self
.visit(node
.assign
)
618 def visitListCompIf(self
, node
, branch
):
619 self
.set_lineno(node
, force
=1)
620 self
.visit(node
.test
)
621 self
.emit('JUMP_IF_FALSE', branch
)
627 def visitAssert(self
, node
):
628 # XXX would be interesting to implement this via a
629 # transformation of the AST before this stage
630 end
= self
.newBlock()
631 self
.set_lineno(node
)
632 # XXX __debug__ and AssertionError appear to be special cases
633 # -- they are always loaded as globals even if there are local
634 # names. I guess this is a sort of renaming op.
635 self
.emit('LOAD_GLOBAL', '__debug__')
636 self
.emit('JUMP_IF_FALSE', end
)
639 self
.visit(node
.test
)
640 self
.emit('JUMP_IF_TRUE', end
)
643 self
.emit('LOAD_GLOBAL', 'AssertionError')
645 self
.visit(node
.fail
)
646 self
.emit('RAISE_VARARGS', 2)
648 self
.emit('RAISE_VARARGS', 1)
652 def visitRaise(self
, node
):
653 self
.set_lineno(node
)
656 self
.visit(node
.expr1
)
659 self
.visit(node
.expr2
)
662 self
.visit(node
.expr3
)
664 self
.emit('RAISE_VARARGS', n
)
666 def visitTryExcept(self
, node
):
667 body
= self
.newBlock()
668 handlers
= self
.newBlock()
669 end
= self
.newBlock()
671 lElse
= self
.newBlock()
674 self
.set_lineno(node
)
675 self
.emit('SETUP_EXCEPT', handlers
)
677 self
.setups
.push((EXCEPT
, body
))
678 self
.visit(node
.body
)
679 self
.emit('POP_BLOCK')
681 self
.emit('JUMP_FORWARD', lElse
)
682 self
.startBlock(handlers
)
684 last
= len(node
.handlers
) - 1
685 for i
in range(len(node
.handlers
)):
686 expr
, target
, body
= node
.handlers
[i
]
687 self
.set_lineno(expr
)
691 self
.emit('COMPARE_OP', 'exception match')
692 next
= self
.newBlock()
693 self
.emit('JUMP_IF_FALSE', next
)
703 self
.emit('JUMP_FORWARD', end
)
710 self
.emit('END_FINALLY')
712 self
.nextBlock(lElse
)
713 self
.visit(node
.else_
)
716 def visitTryFinally(self
, node
):
717 body
= self
.newBlock()
718 final
= self
.newBlock()
719 self
.set_lineno(node
)
720 self
.emit('SETUP_FINALLY', final
)
722 self
.setups
.push((TRY_FINALLY
, body
))
723 self
.visit(node
.body
)
724 self
.emit('POP_BLOCK')
726 self
.emit('LOAD_CONST', None)
727 self
.nextBlock(final
)
728 self
.setups
.push((END_FINALLY
, final
))
729 self
.visit(node
.final
)
730 self
.emit('END_FINALLY')
735 def visitDiscard(self
, node
):
736 self
.set_lineno(node
)
737 self
.visit(node
.expr
)
740 def visitConst(self
, node
):
741 self
.emit('LOAD_CONST', node
.value
)
743 def visitKeyword(self
, node
):
744 self
.emit('LOAD_CONST', node
.name
)
745 self
.visit(node
.expr
)
747 def visitGlobal(self
, node
):
748 # no code to generate
751 def visitName(self
, node
):
752 self
.set_lineno(node
)
753 self
.loadName(node
.name
)
755 def visitPass(self
, node
):
756 self
.set_lineno(node
)
758 def visitImport(self
, node
):
759 self
.set_lineno(node
)
760 for name
, alias
in node
.names
:
762 self
.emit('LOAD_CONST', None)
763 self
.emit('IMPORT_NAME', name
)
764 mod
= string
.split(name
, ".")[0]
765 self
.storeName(alias
or mod
)
767 def visitFrom(self
, node
):
768 self
.set_lineno(node
)
769 fromlist
= map(lambda (name
, alias
): name
, node
.names
)
771 self
.emit('LOAD_CONST', tuple(fromlist
))
772 self
.emit('IMPORT_NAME', node
.modname
)
773 for name
, alias
in node
.names
:
777 self
.emit('IMPORT_STAR')
778 # There can only be one name w/ from ... import *
779 assert len(node
.names
) == 1
782 self
.emit('IMPORT_FROM', name
)
783 self
._resolveDots
(name
)
784 self
.storeName(alias
or name
)
786 self
.emit('IMPORT_FROM', name
)
789 def _resolveDots(self
, name
):
790 elts
= string
.split(name
, ".")
794 self
.emit('LOAD_ATTR', elt
)
796 def visitGetattr(self
, node
):
797 self
.visit(node
.expr
)
798 self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
800 # next five implement assignments
802 def visitAssign(self
, node
):
803 self
.set_lineno(node
)
804 self
.visit(node
.expr
)
805 dups
= len(node
.nodes
) - 1
806 for i
in range(len(node
.nodes
)):
810 if isinstance(elt
, ast
.Node
):
813 def visitAssName(self
, node
):
814 if node
.flags
== 'OP_ASSIGN':
815 self
.storeName(node
.name
)
816 elif node
.flags
== 'OP_DELETE':
817 self
.set_lineno(node
)
818 self
.delName(node
.name
)
820 print "oops", node
.flags
822 def visitAssAttr(self
, node
):
823 self
.visit(node
.expr
)
824 if node
.flags
== 'OP_ASSIGN':
825 self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
826 elif node
.flags
== 'OP_DELETE':
827 self
.emit('DELETE_ATTR', self
.mangle(node
.attrname
))
829 print "warning: unexpected flags:", node
.flags
832 def _visitAssSequence(self
, node
, op
='UNPACK_SEQUENCE'):
833 if findOp(node
) != 'OP_DELETE':
834 self
.emit(op
, len(node
.nodes
))
835 for child
in node
.nodes
:
839 visitAssTuple
= _visitAssSequence
840 visitAssList
= _visitAssSequence
842 def visitAssTuple(self
, node
):
843 self
._visitAssSequence
(node
, 'UNPACK_TUPLE')
845 def visitAssList(self
, node
):
846 self
._visitAssSequence
(node
, 'UNPACK_LIST')
848 # augmented assignment
850 def visitAugAssign(self
, node
):
851 self
.set_lineno(node
)
852 aug_node
= wrap_aug(node
.node
)
853 self
.visit(aug_node
, "load")
854 self
.visit(node
.expr
)
855 self
.emit(self
._augmented
_opcode
[node
.op
])
856 self
.visit(aug_node
, "store")
858 _augmented_opcode
= {
859 '+=' : 'INPLACE_ADD',
860 '-=' : 'INPLACE_SUBTRACT',
861 '*=' : 'INPLACE_MULTIPLY',
862 '/=' : 'INPLACE_DIVIDE',
863 '//=': 'INPLACE_FLOOR_DIVIDE',
864 '%=' : 'INPLACE_MODULO',
865 '**=': 'INPLACE_POWER',
866 '>>=': 'INPLACE_RSHIFT',
867 '<<=': 'INPLACE_LSHIFT',
868 '&=' : 'INPLACE_AND',
869 '^=' : 'INPLACE_XOR',
873 def visitAugName(self
, node
, mode
):
875 self
.loadName(node
.name
)
876 elif mode
== "store":
877 self
.storeName(node
.name
)
879 def visitAugGetattr(self
, node
, mode
):
881 self
.visit(node
.expr
)
883 self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
884 elif mode
== "store":
886 self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
888 def visitAugSlice(self
, node
, mode
):
890 self
.visitSlice(node
, 1)
891 elif mode
== "store":
900 self
.emit('ROT_FOUR')
902 self
.emit('ROT_THREE')
903 self
.emit('STORE_SLICE+%d' % slice)
905 def visitAugSubscript(self
, node
, mode
):
906 if len(node
.subs
) > 1:
907 raise SyntaxError, "augmented assignment to tuple is not possible"
909 self
.visitSubscript(node
, 1)
910 elif mode
== "store":
911 self
.emit('ROT_THREE')
912 self
.emit('STORE_SUBSCR')
914 def visitExec(self
, node
):
915 self
.visit(node
.expr
)
916 if node
.locals is None:
917 self
.emit('LOAD_CONST', None)
919 self
.visit(node
.locals)
920 if node
.globals is None:
923 self
.visit(node
.globals)
924 self
.emit('EXEC_STMT')
926 def visitCallFunc(self
, node
):
929 self
.set_lineno(node
)
930 self
.visit(node
.node
)
931 for arg
in node
.args
:
933 if isinstance(arg
, ast
.Keyword
):
937 if node
.star_args
is not None:
938 self
.visit(node
.star_args
)
939 if node
.dstar_args
is not None:
940 self
.visit(node
.dstar_args
)
941 have_star
= node
.star_args
is not None
942 have_dstar
= node
.dstar_args
is not None
943 opcode
= callfunc_opcode_info
[have_star
, have_dstar
]
944 self
.emit(opcode
, kw
<< 8 | pos
)
946 def visitPrint(self
, node
, newline
=0):
947 self
.set_lineno(node
)
949 self
.visit(node
.dest
)
950 for child
in node
.nodes
:
956 self
.emit('PRINT_ITEM_TO')
958 self
.emit('PRINT_ITEM')
959 if node
.dest
and not newline
:
962 def visitPrintnl(self
, node
):
963 self
.visitPrint(node
, newline
=1)
965 self
.emit('PRINT_NEWLINE_TO')
967 self
.emit('PRINT_NEWLINE')
969 def visitReturn(self
, node
):
970 self
.set_lineno(node
)
971 self
.visit(node
.value
)
972 self
.emit('RETURN_VALUE')
974 def visitYield(self
, node
):
975 self
.set_lineno(node
)
976 self
.visit(node
.value
)
977 self
.emit('YIELD_STMT')
979 # slice and subscript stuff
981 def visitSlice(self
, node
, aug_flag
=None):
982 # aug_flag is used by visitAugSlice
983 self
.visit(node
.expr
)
986 self
.visit(node
.lower
)
989 self
.visit(node
.upper
)
995 self
.emit('DUP_TOPX', 3)
997 self
.emit('DUP_TOPX', 2)
998 if node
.flags
== 'OP_APPLY':
999 self
.emit('SLICE+%d' % slice)
1000 elif node
.flags
== 'OP_ASSIGN':
1001 self
.emit('STORE_SLICE+%d' % slice)
1002 elif node
.flags
== 'OP_DELETE':
1003 self
.emit('DELETE_SLICE+%d' % slice)
1005 print "weird slice", node
.flags
1008 def visitSubscript(self
, node
, aug_flag
=None):
1009 self
.visit(node
.expr
)
1010 for sub
in node
.subs
:
1013 self
.emit('DUP_TOPX', 2)
1014 if len(node
.subs
) > 1:
1015 self
.emit('BUILD_TUPLE', len(node
.subs
))
1016 if node
.flags
== 'OP_APPLY':
1017 self
.emit('BINARY_SUBSCR')
1018 elif node
.flags
== 'OP_ASSIGN':
1019 self
.emit('STORE_SUBSCR')
1020 elif node
.flags
== 'OP_DELETE':
1021 self
.emit('DELETE_SUBSCR')
1025 def binaryOp(self
, node
, op
):
1026 self
.visit(node
.left
)
1027 self
.visit(node
.right
)
1030 def visitAdd(self
, node
):
1031 return self
.binaryOp(node
, 'BINARY_ADD')
1033 def visitSub(self
, node
):
1034 return self
.binaryOp(node
, 'BINARY_SUBTRACT')
1036 def visitMul(self
, node
):
1037 return self
.binaryOp(node
, 'BINARY_MULTIPLY')
1039 def visitDiv(self
, node
):
1040 return self
.binaryOp(node
, self
._div
_op
)
1042 def visitFloorDiv(self
, node
):
1043 return self
.binaryOp(node
, 'BINARY_FLOOR_DIVIDE')
1045 def visitMod(self
, node
):
1046 return self
.binaryOp(node
, 'BINARY_MODULO')
1048 def visitPower(self
, node
):
1049 return self
.binaryOp(node
, 'BINARY_POWER')
1051 def visitLeftShift(self
, node
):
1052 return self
.binaryOp(node
, 'BINARY_LSHIFT')
1054 def visitRightShift(self
, node
):
1055 return self
.binaryOp(node
, 'BINARY_RSHIFT')
1059 def unaryOp(self
, node
, op
):
1060 self
.visit(node
.expr
)
1063 def visitInvert(self
, node
):
1064 return self
.unaryOp(node
, 'UNARY_INVERT')
1066 def visitUnarySub(self
, node
):
1067 return self
.unaryOp(node
, 'UNARY_NEGATIVE')
1069 def visitUnaryAdd(self
, node
):
1070 return self
.unaryOp(node
, 'UNARY_POSITIVE')
1072 def visitUnaryInvert(self
, node
):
1073 return self
.unaryOp(node
, 'UNARY_INVERT')
1075 def visitNot(self
, node
):
1076 return self
.unaryOp(node
, 'UNARY_NOT')
1078 def visitBackquote(self
, node
):
1079 return self
.unaryOp(node
, 'UNARY_CONVERT')
1083 def bitOp(self
, nodes
, op
):
1084 self
.visit(nodes
[0])
1085 for node
in nodes
[1:]:
1089 def visitBitand(self
, node
):
1090 return self
.bitOp(node
.nodes
, 'BINARY_AND')
1092 def visitBitor(self
, node
):
1093 return self
.bitOp(node
.nodes
, 'BINARY_OR')
1095 def visitBitxor(self
, node
):
1096 return self
.bitOp(node
.nodes
, 'BINARY_XOR')
1098 # object constructors
1100 def visitEllipsis(self
, node
):
1101 self
.emit('LOAD_CONST', Ellipsis)
1103 def visitTuple(self
, node
):
1104 self
.set_lineno(node
)
1105 for elt
in node
.nodes
:
1107 self
.emit('BUILD_TUPLE', len(node
.nodes
))
1109 def visitList(self
, node
):
1110 self
.set_lineno(node
)
1111 for elt
in node
.nodes
:
1113 self
.emit('BUILD_LIST', len(node
.nodes
))
1115 def visitSliceobj(self
, node
):
1116 for child
in node
.nodes
:
1118 self
.emit('BUILD_SLICE', len(node
.nodes
))
1120 def visitDict(self
, node
):
1121 lineno
= getattr(node
, 'lineno', None)
1123 self
.emit('SET_LINENO', lineno
)
1124 self
.emit('BUILD_MAP', 0)
1125 for k
, v
in node
.items
:
1126 lineno2
= getattr(node
, 'lineno', None)
1127 if lineno2
is not None and lineno
!= lineno2
:
1128 self
.emit('SET_LINENO', lineno2
)
1130 self
.emit('DUP_TOP')
1132 self
.emit('ROT_TWO')
1134 self
.emit('STORE_SUBSCR')
1136 class NestedScopeMixin
:
1137 """Defines initClass() for nested scoping (Python 2.2-compatible)"""
1138 def initClass(self
):
1139 self
.__class
__.NameFinder
= LocalNameFinder
1140 self
.__class
__.FunctionGen
= FunctionCodeGenerator
1141 self
.__class
__.ClassGen
= ClassCodeGenerator
1143 class ModuleCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1144 __super_init
= CodeGenerator
.__init
__
1148 def __init__(self
, tree
):
1149 self
.graph
= pyassem
.PyFlowGraph("<module>", tree
.filename
)
1150 self
.futures
= future
.find_futures(tree
)
1154 def get_module(self
):
1157 class ExpressionCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1158 __super_init
= CodeGenerator
.__init
__
1163 def __init__(self
, tree
):
1164 self
.graph
= pyassem
.PyFlowGraph("<expression>", tree
.filename
)
1168 def get_module(self
):
1171 class InteractiveCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1173 __super_init
= CodeGenerator
.__init
__
1178 def __init__(self
, tree
):
1179 self
.graph
= pyassem
.PyFlowGraph("<interactive>", tree
.filename
)
1181 self
.set_lineno(tree
)
1183 self
.emit('RETURN_VALUE')
1185 def get_module(self
):
1188 def visitDiscard(self
, node
):
1189 # XXX Discard means it's an expression. Perhaps this is a bad
1191 self
.visit(node
.expr
)
1192 self
.emit('PRINT_EXPR')
1194 class AbstractFunctionCode
:
1198 def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
1199 self
.class_name
= class_name
1202 klass
= FunctionCodeGenerator
1203 name
= "<lambda.%d>" % klass
.lambdaCount
1204 klass
.lambdaCount
= klass
.lambdaCount
+ 1
1207 args
, hasTupleArg
= generateArgList(func
.argnames
)
1208 self
.graph
= pyassem
.PyFlowGraph(name
, func
.filename
, args
,
1210 self
.isLambda
= isLambda
1213 if not isLambda
and func
.doc
:
1214 self
.setDocstring(func
.doc
)
1216 lnf
= walk(func
.code
, self
.NameFinder(args
), verbose
=0)
1217 self
.locals.push(lnf
.getLocals())
1219 self
.graph
.setFlag(CO_VARARGS
)
1221 self
.graph
.setFlag(CO_VARKEYWORDS
)
1222 self
.set_lineno(func
)
1224 self
.generateArgUnpack(func
.argnames
)
1226 def get_module(self
):
1230 self
.graph
.startExitBlock()
1231 if not self
.isLambda
:
1232 self
.emit('LOAD_CONST', None)
1233 self
.emit('RETURN_VALUE')
1235 def generateArgUnpack(self
, args
):
1236 for i
in range(len(args
)):
1238 if type(arg
) == types
.TupleType
:
1239 self
.emit('LOAD_FAST', '.%d' % (i
* 2))
1240 self
.unpackSequence(arg
)
1242 def unpackSequence(self
, tup
):
1244 self
.emit('UNPACK_SEQUENCE', len(tup
))
1246 self
.emit('UNPACK_TUPLE', len(tup
))
1248 if type(elt
) == types
.TupleType
:
1249 self
.unpackSequence(elt
)
1251 self
._nameOp
('STORE', elt
)
1253 unpackTuple
= unpackSequence
1255 class FunctionCodeGenerator(NestedScopeMixin
, AbstractFunctionCode
,
1257 super_init
= CodeGenerator
.__init
__ # call be other init
1260 __super_init
= AbstractFunctionCode
.__init
__
1262 def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
1263 self
.scopes
= scopes
1264 self
.scope
= scopes
[func
]
1265 self
.__super
_init
(func
, scopes
, isLambda
, class_name
, mod
)
1266 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1267 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1268 if self
.graph
.checkFlag(CO_GENERATOR_ALLOWED
):
1269 if self
.scope
.generator
is not None:
1270 self
.graph
.setFlag(CO_GENERATOR
)
1272 class AbstractClassCode
:
1274 def __init__(self
, klass
, scopes
, module
):
1275 self
.class_name
= klass
.name
1276 self
.module
= module
1277 self
.graph
= pyassem
.PyFlowGraph(klass
.name
, klass
.filename
,
1278 optimized
=0, klass
=1)
1280 lnf
= walk(klass
.code
, self
.NameFinder(), verbose
=0)
1281 self
.locals.push(lnf
.getLocals())
1282 self
.graph
.setFlag(CO_NEWLOCALS
)
1284 self
.setDocstring(klass
.doc
)
1286 def get_module(self
):
1290 self
.graph
.startExitBlock()
1291 self
.emit('LOAD_LOCALS')
1292 self
.emit('RETURN_VALUE')
1294 class ClassCodeGenerator(NestedScopeMixin
, AbstractClassCode
, CodeGenerator
):
1295 super_init
= CodeGenerator
.__init
__
1298 __super_init
= AbstractClassCode
.__init
__
1300 def __init__(self
, klass
, scopes
, module
):
1301 self
.scopes
= scopes
1302 self
.scope
= scopes
[klass
]
1303 self
.__super
_init
(klass
, scopes
, module
)
1304 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1305 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1306 self
.set_lineno(klass
)
1308 self
.emit("LOAD_CONST", klass
.doc
)
1309 self
.storeName("__doc__")
1311 def generateArgList(arglist
):
1312 """Generate an arg list marking TupleArgs"""
1316 for i
in range(len(arglist
)):
1318 if type(elt
) == types
.StringType
:
1320 elif type(elt
) == types
.TupleType
:
1321 args
.append(TupleArg(i
* 2, elt
))
1322 extra
.extend(misc
.flatten(elt
))
1325 raise ValueError, "unexpect argument type:", elt
1326 return args
+ extra
, count
1329 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
1331 walk(node
, v
, verbose
=0)
1337 def visitAssName(self
, node
):
1339 self
.op
= node
.flags
1340 elif self
.op
!= node
.flags
:
1341 raise ValueError, "mixed ops in stmt"
1342 visitAssAttr
= visitAssName
1343 visitSubscript
= visitAssName
1346 """Base class to support delegation for augmented assignment nodes
1348 To generator code for augmented assignments, we use the following
1349 wrapper classes. In visitAugAssign, the left-hand expression node
1350 is visited twice. The first time the visit uses the normal method
1351 for that node . The second time the visit uses a different method
1352 that generates the appropriate code to perform the assignment.
1353 These delegator classes wrap the original AST nodes in order to
1354 support the variant visit methods.
1356 def __init__(self
, obj
):
1359 def __getattr__(self
, attr
):
1360 return getattr(self
.obj
, attr
)
1362 class AugGetattr(Delegator
):
1365 class AugName(Delegator
):
1368 class AugSlice(Delegator
):
1371 class AugSubscript(Delegator
):
1375 ast
.Getattr
: AugGetattr
,
1377 ast
.Slice
: AugSlice
,
1378 ast
.Subscript
: AugSubscript
,
1382 return wrapper
[node
.__class
__](node
)
1384 if __name__
== "__main__":
1387 for file in sys
.argv
[1:]: