6 from cStringIO
import StringIO
8 from compiler
import ast
, parse
, walk
, syntax
9 from compiler
import pyassem
, misc
, future
, symbols
10 from compiler
.consts
import SC_LOCAL
, SC_GLOBAL
, SC_FREE
, SC_CELL
11 from compiler
.consts
import (CO_VARARGS
, CO_VARKEYWORDS
, CO_NEWLOCALS
,
12 CO_NESTED
, CO_GENERATOR
, CO_FUTURE_DIVISION
,
13 CO_FUTURE_ABSIMPORT
, CO_FUTURE_WITH_STATEMENT
)
14 from compiler
.pyassem
import TupleArg
16 # XXX The version-specific code can go, since this code only works with 2.x.
17 # Do we have Python 1.x or Python 2.x?
19 VERSION
= sys
.version_info
[0]
20 except AttributeError:
23 callfunc_opcode_info
= {
24 # (Have *args, Have **args) : opcode
25 (0,0) : "CALL_FUNCTION",
26 (1,0) : "CALL_FUNCTION_VAR",
27 (0,1) : "CALL_FUNCTION_KW",
28 (1,1) : "CALL_FUNCTION_VAR_KW",
36 def compileFile(filename
, display
=0):
37 f
= open(filename
, 'U')
40 mod
= Module(buf
, filename
)
46 f
= open(filename
+ "c", "wb")
50 def compile(source
, filename
, mode
, flags
=None, dont_inherit
=None):
51 """Replacement for builtin compile() function"""
52 if flags
is not None or dont_inherit
is not None:
53 raise RuntimeError, "not implemented yet"
56 gen
= Interactive(source
, filename
)
58 gen
= Module(source
, filename
)
60 gen
= Expression(source
, filename
)
62 raise ValueError("compile() 3rd arg must be 'exec' or "
67 class AbstractCompileMode
:
69 mode
= None # defined by subclass
71 def __init__(self
, source
, filename
):
73 self
.filename
= filename
77 tree
= parse(self
.source
, self
.mode
)
78 misc
.set_filename(self
.filename
, tree
)
83 pass # implemented by subclass
88 class Expression(AbstractCompileMode
):
93 tree
= self
._get
_tree
()
94 gen
= ExpressionCodeGenerator(tree
)
95 self
.code
= gen
.getCode()
97 class Interactive(AbstractCompileMode
):
102 tree
= self
._get
_tree
()
103 gen
= InteractiveCodeGenerator(tree
)
104 self
.code
= gen
.getCode()
106 class Module(AbstractCompileMode
):
110 def compile(self
, display
=0):
111 tree
= self
._get
_tree
()
112 gen
= ModuleCodeGenerator(tree
)
115 print pprint
.pprint(tree
)
116 self
.code
= gen
.getCode()
119 f
.write(self
.getPycHeader())
120 marshal
.dump(self
.code
, f
)
122 MAGIC
= imp
.get_magic()
124 def getPycHeader(self
):
125 # compile.c uses marshal to write a long directly, with
126 # calling the interface that would also generate a 1-byte code
127 # to indicate the type of the value. simplest way to get the
128 # same effect is to call marshal and then skip the code.
129 mtime
= os
.path
.getmtime(self
.filename
)
130 mtime
= struct
.pack('<i', mtime
)
131 return self
.MAGIC
+ mtime
133 class LocalNameFinder
:
134 """Find local names in scope"""
135 def __init__(self
, names
=()):
136 self
.names
= misc
.Set()
137 self
.globals = misc
.Set()
141 # XXX list comprehensions and for loops
144 for elt
in self
.globals.elements():
145 if self
.names
.has_elt(elt
):
146 self
.names
.remove(elt
)
149 def visitDict(self
, node
):
152 def visitGlobal(self
, node
):
153 for name
in node
.names
:
154 self
.globals.add(name
)
156 def visitFunction(self
, node
):
157 self
.names
.add(node
.name
)
159 def visitLambda(self
, node
):
162 def visitImport(self
, node
):
163 for name
, alias
in node
.names
:
164 self
.names
.add(alias
or name
)
166 def visitFrom(self
, node
):
167 for name
, alias
in node
.names
:
168 self
.names
.add(alias
or name
)
170 def visitClass(self
, node
):
171 self
.names
.add(node
.name
)
173 def visitAssName(self
, node
):
174 self
.names
.add(node
.name
)
176 def is_constant_false(node
):
177 if isinstance(node
, ast
.Const
):
183 """Defines basic code generator for Python bytecode
185 This class is an abstract base class. Concrete subclasses must
186 define an __init__() that defines self.graph and then calls the
187 __init__() defined in this class.
189 The concrete class must also define the class attributes
190 NameFinder, FunctionGen, and ClassGen. These attributes can be
191 defined in the initClass() method, which is a hook for
192 initializing these methods after all the classes have been
196 optimized
= 0 # is namespace access optimized?
198 class_name
= None # provide default for instance variable
201 if self
.__initialized
is None:
203 self
.__class
__.__initialized
= 1
205 self
.locals = misc
.Stack()
206 self
.setups
= misc
.Stack()
207 self
.last_lineno
= None
208 self
._setupGraphDelegation
()
209 self
._div
_op
= "BINARY_DIVIDE"
211 # XXX set flags based on future features
212 futures
= self
.get_module().futures
213 for feature
in futures
:
214 if feature
== "division":
215 self
.graph
.setFlag(CO_FUTURE_DIVISION
)
216 self
._div
_op
= "BINARY_TRUE_DIVIDE"
217 elif feature
== "absolute_import":
218 self
.graph
.setFlag(CO_FUTURE_ABSIMPORT
)
219 elif feature
== "with_statement":
220 self
.graph
.setFlag(CO_FUTURE_WITH_STATEMENT
)
223 """This method is called once for each class"""
225 def checkClass(self
):
226 """Verify that class is constructed correctly"""
228 assert hasattr(self
, 'graph')
229 assert getattr(self
, 'NameFinder')
230 assert getattr(self
, 'FunctionGen')
231 assert getattr(self
, 'ClassGen')
232 except AssertionError, msg
:
233 intro
= "Bad class construction for %s" % self
.__class
__.__name
__
234 raise AssertionError, intro
236 def _setupGraphDelegation(self
):
237 self
.emit
= self
.graph
.emit
238 self
.newBlock
= self
.graph
.newBlock
239 self
.startBlock
= self
.graph
.startBlock
240 self
.nextBlock
= self
.graph
.nextBlock
241 self
.setDocstring
= self
.graph
.setDocstring
244 """Return a code object"""
245 return self
.graph
.getCode()
247 def mangle(self
, name
):
248 if self
.class_name
is not None:
249 return misc
.mangle(name
, self
.class_name
)
253 def parseSymbols(self
, tree
):
254 s
= symbols
.SymbolVisitor()
258 def get_module(self
):
259 raise RuntimeError, "should be implemented by subclasses"
261 # Next five methods handle name access
263 def isLocalName(self
, name
):
264 return self
.locals.top().has_elt(name
)
266 def storeName(self
, name
):
267 self
._nameOp
('STORE', name
)
269 def loadName(self
, name
):
270 self
._nameOp
('LOAD', name
)
272 def delName(self
, name
):
273 self
._nameOp
('DELETE', name
)
275 def _nameOp(self
, prefix
, name
):
276 name
= self
.mangle(name
)
277 scope
= self
.scope
.check_name(name
)
278 if scope
== SC_LOCAL
:
279 if not self
.optimized
:
280 self
.emit(prefix
+ '_NAME', name
)
282 self
.emit(prefix
+ '_FAST', name
)
283 elif scope
== SC_GLOBAL
:
284 if not self
.optimized
:
285 self
.emit(prefix
+ '_NAME', name
)
287 self
.emit(prefix
+ '_GLOBAL', name
)
288 elif scope
== SC_FREE
or scope
== SC_CELL
:
289 self
.emit(prefix
+ '_DEREF', name
)
291 raise RuntimeError, "unsupported scope for var %s: %d" % \
294 def _implicitNameOp(self
, prefix
, name
):
295 """Emit name ops for names generated implicitly by for loops
297 The interpreter generates names that start with a period or
298 dollar sign. The symbol table ignores these names because
299 they aren't present in the program text.
302 self
.emit(prefix
+ '_FAST', name
)
304 self
.emit(prefix
+ '_NAME', name
)
306 # The set_lineno() function and the explicit emit() calls for
307 # SET_LINENO below are only used to generate the line number table.
308 # As of Python 2.3, the interpreter does not have a SET_LINENO
309 # instruction. pyassem treats SET_LINENO opcodes as a special case.
311 def set_lineno(self
, node
, force
=False):
312 """Emit SET_LINENO if necessary.
314 The instruction is considered necessary if the node has a
315 lineno attribute and it is different than the last lineno
318 Returns true if SET_LINENO was emitted.
320 There are no rules for when an AST node should have a lineno
321 attribute. The transformer and AST code need to be reviewed
322 and a consistent policy implemented and documented. Until
323 then, this method works around missing line numbers.
325 lineno
= getattr(node
, 'lineno', None)
326 if lineno
is not None and (lineno
!= self
.last_lineno
328 self
.emit('SET_LINENO', lineno
)
329 self
.last_lineno
= lineno
333 # The first few visitor methods handle nodes that generator new
334 # code objects. They use class attributes to determine what
335 # specialized code generators to use.
337 NameFinder
= LocalNameFinder
341 def visitModule(self
, node
):
342 self
.scopes
= self
.parseSymbols(node
)
343 self
.scope
= self
.scopes
[node
]
344 self
.emit('SET_LINENO', 0)
346 self
.emit('LOAD_CONST', node
.doc
)
347 self
.storeName('__doc__')
348 lnf
= walk(node
.node
, self
.NameFinder(), verbose
=0)
349 self
.locals.push(lnf
.getLocals())
350 self
.visit(node
.node
)
351 self
.emit('LOAD_CONST', None)
352 self
.emit('RETURN_VALUE')
354 def visitExpression(self
, node
):
355 self
.set_lineno(node
)
356 self
.scopes
= self
.parseSymbols(node
)
357 self
.scope
= self
.scopes
[node
]
358 self
.visit(node
.node
)
359 self
.emit('RETURN_VALUE')
361 def visitFunction(self
, node
):
362 self
._visitFuncOrLambda
(node
, isLambda
=0)
364 self
.setDocstring(node
.doc
)
365 self
.storeName(node
.name
)
367 def visitLambda(self
, node
):
368 self
._visitFuncOrLambda
(node
, isLambda
=1)
370 def _visitFuncOrLambda(self
, node
, isLambda
=0):
371 if not isLambda
and node
.decorators
:
372 for decorator
in node
.decorators
.nodes
:
373 self
.visit(decorator
)
374 ndecorators
= len(node
.decorators
.nodes
)
378 gen
= self
.FunctionGen(node
, self
.scopes
, isLambda
,
379 self
.class_name
, self
.get_module())
382 self
.set_lineno(node
)
383 for default
in node
.defaults
:
385 self
._makeClosure
(gen
, len(node
.defaults
))
386 for i
in range(ndecorators
):
387 self
.emit('CALL_FUNCTION', 1)
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 self
._makeClosure
(gen
, 0)
400 self
.emit('CALL_FUNCTION', 0)
401 self
.emit('BUILD_CLASS')
402 self
.storeName(node
.name
)
404 # The rest are standard visitor methods
406 # The next few implement control-flow statements
408 def visitIf(self
, node
):
409 end
= self
.newBlock()
410 numtests
= len(node
.tests
)
411 for i
in range(numtests
):
412 test
, suite
= node
.tests
[i
]
413 if is_constant_false(test
):
414 # XXX will need to check generator stuff here
416 self
.set_lineno(test
)
418 nextTest
= self
.newBlock()
419 self
.emit('JUMP_IF_FALSE', nextTest
)
423 self
.emit('JUMP_FORWARD', end
)
424 self
.startBlock(nextTest
)
427 self
.visit(node
.else_
)
430 def visitWhile(self
, node
):
431 self
.set_lineno(node
)
433 loop
= self
.newBlock()
434 else_
= self
.newBlock()
436 after
= self
.newBlock()
437 self
.emit('SETUP_LOOP', after
)
440 self
.setups
.push((LOOP
, loop
))
442 self
.set_lineno(node
, force
=True)
443 self
.visit(node
.test
)
444 self
.emit('JUMP_IF_FALSE', else_
or after
)
448 self
.visit(node
.body
)
449 self
.emit('JUMP_ABSOLUTE', loop
)
451 self
.startBlock(else_
) # or just the POPs if not else clause
453 self
.emit('POP_BLOCK')
456 self
.visit(node
.else_
)
457 self
.nextBlock(after
)
459 def visitFor(self
, node
):
460 start
= self
.newBlock()
461 anchor
= self
.newBlock()
462 after
= self
.newBlock()
463 self
.setups
.push((LOOP
, start
))
465 self
.set_lineno(node
)
466 self
.emit('SETUP_LOOP', after
)
467 self
.visit(node
.list)
468 self
.emit('GET_ITER')
470 self
.nextBlock(start
)
471 self
.set_lineno(node
, force
=1)
472 self
.emit('FOR_ITER', anchor
)
473 self
.visit(node
.assign
)
474 self
.visit(node
.body
)
475 self
.emit('JUMP_ABSOLUTE', start
)
476 self
.nextBlock(anchor
)
477 self
.emit('POP_BLOCK')
480 self
.visit(node
.else_
)
481 self
.nextBlock(after
)
483 def visitBreak(self
, node
):
485 raise SyntaxError, "'break' outside loop (%s, %d)" % \
486 (node
.filename
, node
.lineno
)
487 self
.set_lineno(node
)
488 self
.emit('BREAK_LOOP')
490 def visitContinue(self
, node
):
492 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
493 (node
.filename
, node
.lineno
)
494 kind
, block
= self
.setups
.top()
496 self
.set_lineno(node
)
497 self
.emit('JUMP_ABSOLUTE', block
)
499 elif kind
== EXCEPT
or kind
== TRY_FINALLY
:
500 self
.set_lineno(node
)
501 # find the block that starts the loop
502 top
= len(self
.setups
)
505 kind
, loop_block
= self
.setups
[top
]
509 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
510 (node
.filename
, node
.lineno
)
511 self
.emit('CONTINUE_LOOP', loop_block
)
513 elif kind
== END_FINALLY
:
514 msg
= "'continue' not allowed inside 'finally' clause (%s, %d)"
515 raise SyntaxError, msg
% (node
.filename
, node
.lineno
)
517 def visitTest(self
, node
, jump
):
518 end
= self
.newBlock()
519 for child
in node
.nodes
[:-1]:
524 self
.visit(node
.nodes
[-1])
527 def visitAnd(self
, node
):
528 self
.visitTest(node
, 'JUMP_IF_FALSE')
530 def visitOr(self
, node
):
531 self
.visitTest(node
, 'JUMP_IF_TRUE')
533 def visitIfExp(self
, node
):
534 endblock
= self
.newBlock()
535 elseblock
= self
.newBlock()
536 self
.visit(node
.test
)
537 self
.emit('JUMP_IF_FALSE', elseblock
)
539 self
.visit(node
.then
)
540 self
.emit('JUMP_FORWARD', endblock
)
541 self
.nextBlock(elseblock
)
543 self
.visit(node
.else_
)
544 self
.nextBlock(endblock
)
546 def visitCompare(self
, node
):
547 self
.visit(node
.expr
)
548 cleanup
= self
.newBlock()
549 for op
, code
in node
.ops
[:-1]:
552 self
.emit('ROT_THREE')
553 self
.emit('COMPARE_OP', op
)
554 self
.emit('JUMP_IF_FALSE', cleanup
)
557 # now do the last comparison
559 op
, code
= node
.ops
[-1]
561 self
.emit('COMPARE_OP', op
)
562 if len(node
.ops
) > 1:
563 end
= self
.newBlock()
564 self
.emit('JUMP_FORWARD', end
)
565 self
.startBlock(cleanup
)
570 # list comprehensions
573 def visitListComp(self
, node
):
574 self
.set_lineno(node
)
576 tmpname
= "$list%d" % self
.__list
_count
577 self
.__list
_count
= self
.__list
_count
+ 1
578 self
.emit('BUILD_LIST', 0)
580 self
._implicitNameOp
('STORE', tmpname
)
583 for i
, for_
in zip(range(len(node
.quals
)), node
.quals
):
584 start
, anchor
= self
.visit(for_
)
588 cont
= self
.newBlock()
589 self
.visit(if_
, cont
)
590 stack
.insert(0, (start
, cont
, anchor
))
592 self
._implicitNameOp
('LOAD', tmpname
)
593 self
.visit(node
.expr
)
594 self
.emit('LIST_APPEND')
596 for start
, cont
, anchor
in stack
:
598 skip_one
= self
.newBlock()
599 self
.emit('JUMP_FORWARD', skip_one
)
600 self
.startBlock(cont
)
602 self
.nextBlock(skip_one
)
603 self
.emit('JUMP_ABSOLUTE', start
)
604 self
.startBlock(anchor
)
605 self
._implicitNameOp
('DELETE', tmpname
)
607 self
.__list
_count
= self
.__list
_count
- 1
609 def visitListCompFor(self
, node
):
610 start
= self
.newBlock()
611 anchor
= self
.newBlock()
613 self
.visit(node
.list)
614 self
.emit('GET_ITER')
615 self
.nextBlock(start
)
616 self
.set_lineno(node
, force
=True)
617 self
.emit('FOR_ITER', anchor
)
619 self
.visit(node
.assign
)
622 def visitListCompIf(self
, node
, branch
):
623 self
.set_lineno(node
, force
=True)
624 self
.visit(node
.test
)
625 self
.emit('JUMP_IF_FALSE', branch
)
629 def _makeClosure(self
, gen
, args
):
630 frees
= gen
.scope
.get_free_vars()
633 self
.emit('LOAD_CLOSURE', name
)
634 self
.emit('BUILD_TUPLE', len(frees
))
635 self
.emit('LOAD_CONST', gen
)
636 self
.emit('MAKE_CLOSURE', args
)
638 self
.emit('LOAD_CONST', gen
)
639 self
.emit('MAKE_FUNCTION', args
)
641 def visitGenExpr(self
, node
):
642 gen
= GenExprCodeGenerator(node
, self
.scopes
, self
.class_name
,
646 self
.set_lineno(node
)
647 self
._makeClosure
(gen
, 0)
648 # precomputation of outmost iterable
649 self
.visit(node
.code
.quals
[0].iter)
650 self
.emit('GET_ITER')
651 self
.emit('CALL_FUNCTION', 1)
653 def visitGenExprInner(self
, node
):
654 self
.set_lineno(node
)
658 for i
, for_
in zip(range(len(node
.quals
)), node
.quals
):
659 start
, anchor
, end
= self
.visit(for_
)
663 cont
= self
.newBlock()
664 self
.visit(if_
, cont
)
665 stack
.insert(0, (start
, cont
, anchor
, end
))
667 self
.visit(node
.expr
)
668 self
.emit('YIELD_VALUE')
671 for start
, cont
, anchor
, end
in stack
:
673 skip_one
= self
.newBlock()
674 self
.emit('JUMP_FORWARD', skip_one
)
675 self
.startBlock(cont
)
677 self
.nextBlock(skip_one
)
678 self
.emit('JUMP_ABSOLUTE', start
)
679 self
.startBlock(anchor
)
680 self
.emit('POP_BLOCK')
684 self
.emit('LOAD_CONST', None)
686 def visitGenExprFor(self
, node
):
687 start
= self
.newBlock()
688 anchor
= self
.newBlock()
689 end
= self
.newBlock()
691 self
.setups
.push((LOOP
, start
))
692 self
.emit('SETUP_LOOP', end
)
697 self
.visit(node
.iter)
698 self
.emit('GET_ITER')
700 self
.nextBlock(start
)
701 self
.set_lineno(node
, force
=True)
702 self
.emit('FOR_ITER', anchor
)
704 self
.visit(node
.assign
)
705 return start
, anchor
, end
707 def visitGenExprIf(self
, node
, branch
):
708 self
.set_lineno(node
, force
=True)
709 self
.visit(node
.test
)
710 self
.emit('JUMP_IF_FALSE', branch
)
716 def visitAssert(self
, node
):
717 # XXX would be interesting to implement this via a
718 # transformation of the AST before this stage
720 end
= self
.newBlock()
721 self
.set_lineno(node
)
722 # XXX AssertionError appears to be special case -- it is always
723 # loaded as a global even if there is a local name. I guess this
724 # is a sort of renaming op.
726 self
.visit(node
.test
)
727 self
.emit('JUMP_IF_TRUE', end
)
730 self
.emit('LOAD_GLOBAL', 'AssertionError')
732 self
.visit(node
.fail
)
733 self
.emit('RAISE_VARARGS', 2)
735 self
.emit('RAISE_VARARGS', 1)
739 def visitRaise(self
, node
):
740 self
.set_lineno(node
)
743 self
.visit(node
.expr1
)
746 self
.visit(node
.expr2
)
749 self
.visit(node
.expr3
)
751 self
.emit('RAISE_VARARGS', n
)
753 def visitTryExcept(self
, node
):
754 body
= self
.newBlock()
755 handlers
= self
.newBlock()
756 end
= self
.newBlock()
758 lElse
= self
.newBlock()
761 self
.set_lineno(node
)
762 self
.emit('SETUP_EXCEPT', handlers
)
764 self
.setups
.push((EXCEPT
, body
))
765 self
.visit(node
.body
)
766 self
.emit('POP_BLOCK')
768 self
.emit('JUMP_FORWARD', lElse
)
769 self
.startBlock(handlers
)
771 last
= len(node
.handlers
) - 1
772 for i
in range(len(node
.handlers
)):
773 expr
, target
, body
= node
.handlers
[i
]
774 self
.set_lineno(expr
)
778 self
.emit('COMPARE_OP', 'exception match')
779 next
= self
.newBlock()
780 self
.emit('JUMP_IF_FALSE', next
)
790 self
.emit('JUMP_FORWARD', end
)
797 self
.emit('END_FINALLY')
799 self
.nextBlock(lElse
)
800 self
.visit(node
.else_
)
803 def visitTryFinally(self
, node
):
804 body
= self
.newBlock()
805 final
= self
.newBlock()
806 self
.set_lineno(node
)
807 self
.emit('SETUP_FINALLY', final
)
809 self
.setups
.push((TRY_FINALLY
, body
))
810 self
.visit(node
.body
)
811 self
.emit('POP_BLOCK')
813 self
.emit('LOAD_CONST', None)
814 self
.nextBlock(final
)
815 self
.setups
.push((END_FINALLY
, final
))
816 self
.visit(node
.final
)
817 self
.emit('END_FINALLY')
822 def visitWith(self
, node
):
823 body
= self
.newBlock()
824 final
= self
.newBlock()
825 exitvar
= "$exit%d" % self
.__with
_count
826 valuevar
= "$value%d" % self
.__with
_count
827 self
.__with
_count
+= 1
828 self
.set_lineno(node
)
829 self
.visit(node
.expr
)
831 self
.emit('LOAD_ATTR', '__exit__')
832 self
._implicitNameOp
('STORE', exitvar
)
833 self
.emit('LOAD_ATTR', '__enter__')
834 self
.emit('CALL_FUNCTION', 0)
835 if node
.vars is None:
838 self
._implicitNameOp
('STORE', valuevar
)
839 self
.emit('SETUP_FINALLY', final
)
841 self
.setups
.push((TRY_FINALLY
, body
))
842 if node
.vars is not None:
843 self
._implicitNameOp
('LOAD', valuevar
)
844 self
._implicitNameOp
('DELETE', valuevar
)
845 self
.visit(node
.vars)
846 self
.visit(node
.body
)
847 self
.emit('POP_BLOCK')
849 self
.emit('LOAD_CONST', None)
850 self
.nextBlock(final
)
851 self
.setups
.push((END_FINALLY
, final
))
852 self
.emit('WITH_CLEANUP')
853 self
.emit('END_FINALLY')
855 self
.__with
_count
-= 1
859 def visitDiscard(self
, node
):
860 self
.set_lineno(node
)
861 self
.visit(node
.expr
)
864 def visitConst(self
, node
):
865 self
.emit('LOAD_CONST', node
.value
)
867 def visitKeyword(self
, node
):
868 self
.emit('LOAD_CONST', node
.name
)
869 self
.visit(node
.expr
)
871 def visitGlobal(self
, node
):
872 # no code to generate
875 def visitName(self
, node
):
876 self
.set_lineno(node
)
877 self
.loadName(node
.name
)
879 def visitPass(self
, node
):
880 self
.set_lineno(node
)
882 def visitImport(self
, node
):
883 self
.set_lineno(node
)
884 level
= 0 if self
.graph
.checkFlag(CO_FUTURE_ABSIMPORT
) else -1
885 for name
, alias
in node
.names
:
887 self
.emit('LOAD_CONST', level
)
888 self
.emit('LOAD_CONST', None)
889 self
.emit('IMPORT_NAME', name
)
890 mod
= name
.split(".")[0]
892 self
._resolveDots
(name
)
893 self
.storeName(alias
)
897 def visitFrom(self
, node
):
898 self
.set_lineno(node
)
900 if level
== 0 and not self
.graph
.checkFlag(CO_FUTURE_ABSIMPORT
):
902 fromlist
= map(lambda (name
, alias
): name
, node
.names
)
904 self
.emit('LOAD_CONST', level
)
905 self
.emit('LOAD_CONST', tuple(fromlist
))
906 self
.emit('IMPORT_NAME', node
.modname
)
907 for name
, alias
in node
.names
:
911 self
.emit('IMPORT_STAR')
912 # There can only be one name w/ from ... import *
913 assert len(node
.names
) == 1
916 self
.emit('IMPORT_FROM', name
)
917 self
._resolveDots
(name
)
918 self
.storeName(alias
or name
)
920 self
.emit('IMPORT_FROM', name
)
923 def _resolveDots(self
, name
):
924 elts
= name
.split(".")
928 self
.emit('LOAD_ATTR', elt
)
930 def visitGetattr(self
, node
):
931 self
.visit(node
.expr
)
932 self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
934 # next five implement assignments
936 def visitAssign(self
, node
):
937 self
.set_lineno(node
)
938 self
.visit(node
.expr
)
939 dups
= len(node
.nodes
) - 1
940 for i
in range(len(node
.nodes
)):
944 if isinstance(elt
, ast
.Node
):
947 def visitAssName(self
, node
):
948 if node
.flags
== 'OP_ASSIGN':
949 self
.storeName(node
.name
)
950 elif node
.flags
== 'OP_DELETE':
951 self
.set_lineno(node
)
952 self
.delName(node
.name
)
954 print "oops", node
.flags
956 def visitAssAttr(self
, node
):
957 self
.visit(node
.expr
)
958 if node
.flags
== 'OP_ASSIGN':
959 self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
960 elif node
.flags
== 'OP_DELETE':
961 self
.emit('DELETE_ATTR', self
.mangle(node
.attrname
))
963 print "warning: unexpected flags:", node
.flags
966 def _visitAssSequence(self
, node
, op
='UNPACK_SEQUENCE'):
967 if findOp(node
) != 'OP_DELETE':
968 self
.emit(op
, len(node
.nodes
))
969 for child
in node
.nodes
:
973 visitAssTuple
= _visitAssSequence
974 visitAssList
= _visitAssSequence
976 def visitAssTuple(self
, node
):
977 self
._visitAssSequence
(node
, 'UNPACK_TUPLE')
979 def visitAssList(self
, node
):
980 self
._visitAssSequence
(node
, 'UNPACK_LIST')
982 # augmented assignment
984 def visitAugAssign(self
, node
):
985 self
.set_lineno(node
)
986 aug_node
= wrap_aug(node
.node
)
987 self
.visit(aug_node
, "load")
988 self
.visit(node
.expr
)
989 self
.emit(self
._augmented
_opcode
[node
.op
])
990 self
.visit(aug_node
, "store")
992 _augmented_opcode
= {
993 '+=' : 'INPLACE_ADD',
994 '-=' : 'INPLACE_SUBTRACT',
995 '*=' : 'INPLACE_MULTIPLY',
996 '/=' : 'INPLACE_DIVIDE',
997 '//=': 'INPLACE_FLOOR_DIVIDE',
998 '%=' : 'INPLACE_MODULO',
999 '**=': 'INPLACE_POWER',
1000 '>>=': 'INPLACE_RSHIFT',
1001 '<<=': 'INPLACE_LSHIFT',
1002 '&=' : 'INPLACE_AND',
1003 '^=' : 'INPLACE_XOR',
1004 '|=' : 'INPLACE_OR',
1007 def visitAugName(self
, node
, mode
):
1009 self
.loadName(node
.name
)
1010 elif mode
== "store":
1011 self
.storeName(node
.name
)
1013 def visitAugGetattr(self
, node
, mode
):
1015 self
.visit(node
.expr
)
1016 self
.emit('DUP_TOP')
1017 self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
1018 elif mode
== "store":
1019 self
.emit('ROT_TWO')
1020 self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
1022 def visitAugSlice(self
, node
, mode
):
1024 self
.visitSlice(node
, 1)
1025 elif mode
== "store":
1032 self
.emit('ROT_TWO')
1034 self
.emit('ROT_FOUR')
1036 self
.emit('ROT_THREE')
1037 self
.emit('STORE_SLICE+%d' % slice)
1039 def visitAugSubscript(self
, node
, mode
):
1041 self
.visitSubscript(node
, 1)
1042 elif mode
== "store":
1043 self
.emit('ROT_THREE')
1044 self
.emit('STORE_SUBSCR')
1046 def visitExec(self
, node
):
1047 self
.visit(node
.expr
)
1048 if node
.locals is None:
1049 self
.emit('LOAD_CONST', None)
1051 self
.visit(node
.locals)
1052 if node
.globals is None:
1053 self
.emit('DUP_TOP')
1055 self
.visit(node
.globals)
1056 self
.emit('EXEC_STMT')
1058 def visitCallFunc(self
, node
):
1061 self
.set_lineno(node
)
1062 self
.visit(node
.node
)
1063 for arg
in node
.args
:
1065 if isinstance(arg
, ast
.Keyword
):
1069 if node
.star_args
is not None:
1070 self
.visit(node
.star_args
)
1071 if node
.dstar_args
is not None:
1072 self
.visit(node
.dstar_args
)
1073 have_star
= node
.star_args
is not None
1074 have_dstar
= node
.dstar_args
is not None
1075 opcode
= callfunc_opcode_info
[have_star
, have_dstar
]
1076 self
.emit(opcode
, kw
<< 8 | pos
)
1078 def visitPrint(self
, node
, newline
=0):
1079 self
.set_lineno(node
)
1081 self
.visit(node
.dest
)
1082 for child
in node
.nodes
:
1084 self
.emit('DUP_TOP')
1087 self
.emit('ROT_TWO')
1088 self
.emit('PRINT_ITEM_TO')
1090 self
.emit('PRINT_ITEM')
1091 if node
.dest
and not newline
:
1092 self
.emit('POP_TOP')
1094 def visitPrintnl(self
, node
):
1095 self
.visitPrint(node
, newline
=1)
1097 self
.emit('PRINT_NEWLINE_TO')
1099 self
.emit('PRINT_NEWLINE')
1101 def visitReturn(self
, node
):
1102 self
.set_lineno(node
)
1103 self
.visit(node
.value
)
1104 self
.emit('RETURN_VALUE')
1106 def visitYield(self
, node
):
1107 self
.set_lineno(node
)
1108 self
.visit(node
.value
)
1109 self
.emit('YIELD_VALUE')
1111 # slice and subscript stuff
1113 def visitSlice(self
, node
, aug_flag
=None):
1114 # aug_flag is used by visitAugSlice
1115 self
.visit(node
.expr
)
1118 self
.visit(node
.lower
)
1121 self
.visit(node
.upper
)
1125 self
.emit('DUP_TOP')
1127 self
.emit('DUP_TOPX', 3)
1129 self
.emit('DUP_TOPX', 2)
1130 if node
.flags
== 'OP_APPLY':
1131 self
.emit('SLICE+%d' % slice)
1132 elif node
.flags
== 'OP_ASSIGN':
1133 self
.emit('STORE_SLICE+%d' % slice)
1134 elif node
.flags
== 'OP_DELETE':
1135 self
.emit('DELETE_SLICE+%d' % slice)
1137 print "weird slice", node
.flags
1140 def visitSubscript(self
, node
, aug_flag
=None):
1141 self
.visit(node
.expr
)
1142 for sub
in node
.subs
:
1144 if len(node
.subs
) > 1:
1145 self
.emit('BUILD_TUPLE', len(node
.subs
))
1147 self
.emit('DUP_TOPX', 2)
1148 if node
.flags
== 'OP_APPLY':
1149 self
.emit('BINARY_SUBSCR')
1150 elif node
.flags
== 'OP_ASSIGN':
1151 self
.emit('STORE_SUBSCR')
1152 elif node
.flags
== 'OP_DELETE':
1153 self
.emit('DELETE_SUBSCR')
1157 def binaryOp(self
, node
, op
):
1158 self
.visit(node
.left
)
1159 self
.visit(node
.right
)
1162 def visitAdd(self
, node
):
1163 return self
.binaryOp(node
, 'BINARY_ADD')
1165 def visitSub(self
, node
):
1166 return self
.binaryOp(node
, 'BINARY_SUBTRACT')
1168 def visitMul(self
, node
):
1169 return self
.binaryOp(node
, 'BINARY_MULTIPLY')
1171 def visitDiv(self
, node
):
1172 return self
.binaryOp(node
, self
._div
_op
)
1174 def visitFloorDiv(self
, node
):
1175 return self
.binaryOp(node
, 'BINARY_FLOOR_DIVIDE')
1177 def visitMod(self
, node
):
1178 return self
.binaryOp(node
, 'BINARY_MODULO')
1180 def visitPower(self
, node
):
1181 return self
.binaryOp(node
, 'BINARY_POWER')
1183 def visitLeftShift(self
, node
):
1184 return self
.binaryOp(node
, 'BINARY_LSHIFT')
1186 def visitRightShift(self
, node
):
1187 return self
.binaryOp(node
, 'BINARY_RSHIFT')
1191 def unaryOp(self
, node
, op
):
1192 self
.visit(node
.expr
)
1195 def visitInvert(self
, node
):
1196 return self
.unaryOp(node
, 'UNARY_INVERT')
1198 def visitUnarySub(self
, node
):
1199 return self
.unaryOp(node
, 'UNARY_NEGATIVE')
1201 def visitUnaryAdd(self
, node
):
1202 return self
.unaryOp(node
, 'UNARY_POSITIVE')
1204 def visitUnaryInvert(self
, node
):
1205 return self
.unaryOp(node
, 'UNARY_INVERT')
1207 def visitNot(self
, node
):
1208 return self
.unaryOp(node
, 'UNARY_NOT')
1210 def visitBackquote(self
, node
):
1211 return self
.unaryOp(node
, 'UNARY_CONVERT')
1215 def bitOp(self
, nodes
, op
):
1216 self
.visit(nodes
[0])
1217 for node
in nodes
[1:]:
1221 def visitBitand(self
, node
):
1222 return self
.bitOp(node
.nodes
, 'BINARY_AND')
1224 def visitBitor(self
, node
):
1225 return self
.bitOp(node
.nodes
, 'BINARY_OR')
1227 def visitBitxor(self
, node
):
1228 return self
.bitOp(node
.nodes
, 'BINARY_XOR')
1230 # object constructors
1232 def visitEllipsis(self
, node
):
1233 self
.emit('LOAD_CONST', Ellipsis)
1235 def visitTuple(self
, node
):
1236 self
.set_lineno(node
)
1237 for elt
in node
.nodes
:
1239 self
.emit('BUILD_TUPLE', len(node
.nodes
))
1241 def visitList(self
, node
):
1242 self
.set_lineno(node
)
1243 for elt
in node
.nodes
:
1245 self
.emit('BUILD_LIST', len(node
.nodes
))
1247 def visitSliceobj(self
, node
):
1248 for child
in node
.nodes
:
1250 self
.emit('BUILD_SLICE', len(node
.nodes
))
1252 def visitDict(self
, node
):
1253 self
.set_lineno(node
)
1254 self
.emit('BUILD_MAP', 0)
1255 for k
, v
in node
.items
:
1256 self
.emit('DUP_TOP')
1259 self
.emit('ROT_THREE')
1260 self
.emit('STORE_SUBSCR')
1262 class NestedScopeMixin
:
1263 """Defines initClass() for nested scoping (Python 2.2-compatible)"""
1264 def initClass(self
):
1265 self
.__class
__.NameFinder
= LocalNameFinder
1266 self
.__class
__.FunctionGen
= FunctionCodeGenerator
1267 self
.__class
__.ClassGen
= ClassCodeGenerator
1269 class ModuleCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1270 __super_init
= CodeGenerator
.__init
__
1274 def __init__(self
, tree
):
1275 self
.graph
= pyassem
.PyFlowGraph("<module>", tree
.filename
)
1276 self
.futures
= future
.find_futures(tree
)
1280 def get_module(self
):
1283 class ExpressionCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1284 __super_init
= CodeGenerator
.__init
__
1289 def __init__(self
, tree
):
1290 self
.graph
= pyassem
.PyFlowGraph("<expression>", tree
.filename
)
1294 def get_module(self
):
1297 class InteractiveCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1299 __super_init
= CodeGenerator
.__init
__
1304 def __init__(self
, tree
):
1305 self
.graph
= pyassem
.PyFlowGraph("<interactive>", tree
.filename
)
1307 self
.set_lineno(tree
)
1309 self
.emit('RETURN_VALUE')
1311 def get_module(self
):
1314 def visitDiscard(self
, node
):
1315 # XXX Discard means it's an expression. Perhaps this is a bad
1317 self
.visit(node
.expr
)
1318 self
.emit('PRINT_EXPR')
1320 class AbstractFunctionCode
:
1324 def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
1325 self
.class_name
= class_name
1328 klass
= FunctionCodeGenerator
1329 name
= "<lambda.%d>" % klass
.lambdaCount
1330 klass
.lambdaCount
= klass
.lambdaCount
+ 1
1334 args
, hasTupleArg
= generateArgList(func
.argnames
)
1335 self
.graph
= pyassem
.PyFlowGraph(name
, func
.filename
, args
,
1337 self
.isLambda
= isLambda
1340 if not isLambda
and func
.doc
:
1341 self
.setDocstring(func
.doc
)
1343 lnf
= walk(func
.code
, self
.NameFinder(args
), verbose
=0)
1344 self
.locals.push(lnf
.getLocals())
1346 self
.graph
.setFlag(CO_VARARGS
)
1348 self
.graph
.setFlag(CO_VARKEYWORDS
)
1349 self
.set_lineno(func
)
1351 self
.generateArgUnpack(func
.argnames
)
1353 def get_module(self
):
1357 self
.graph
.startExitBlock()
1358 if not self
.isLambda
:
1359 self
.emit('LOAD_CONST', None)
1360 self
.emit('RETURN_VALUE')
1362 def generateArgUnpack(self
, args
):
1363 for i
in range(len(args
)):
1365 if isinstance(arg
, tuple):
1366 self
.emit('LOAD_FAST', '.%d' % (i
* 2))
1367 self
.unpackSequence(arg
)
1369 def unpackSequence(self
, tup
):
1371 self
.emit('UNPACK_SEQUENCE', len(tup
))
1373 self
.emit('UNPACK_TUPLE', len(tup
))
1375 if isinstance(elt
, tuple):
1376 self
.unpackSequence(elt
)
1378 self
._nameOp
('STORE', elt
)
1380 unpackTuple
= unpackSequence
1382 class FunctionCodeGenerator(NestedScopeMixin
, AbstractFunctionCode
,
1384 super_init
= CodeGenerator
.__init
__ # call be other init
1387 __super_init
= AbstractFunctionCode
.__init
__
1389 def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
1390 self
.scopes
= scopes
1391 self
.scope
= scopes
[func
]
1392 self
.__super
_init
(func
, scopes
, isLambda
, class_name
, mod
)
1393 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1394 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1395 if self
.scope
.generator
is not None:
1396 self
.graph
.setFlag(CO_GENERATOR
)
1398 class GenExprCodeGenerator(NestedScopeMixin
, AbstractFunctionCode
,
1400 super_init
= CodeGenerator
.__init
__ # call be other init
1403 __super_init
= AbstractFunctionCode
.__init
__
1405 def __init__(self
, gexp
, scopes
, class_name
, mod
):
1406 self
.scopes
= scopes
1407 self
.scope
= scopes
[gexp
]
1408 self
.__super
_init
(gexp
, scopes
, 1, class_name
, mod
)
1409 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1410 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1411 self
.graph
.setFlag(CO_GENERATOR
)
1413 class AbstractClassCode
:
1415 def __init__(self
, klass
, scopes
, module
):
1416 self
.class_name
= klass
.name
1417 self
.module
= module
1418 self
.graph
= pyassem
.PyFlowGraph(klass
.name
, klass
.filename
,
1419 optimized
=0, klass
=1)
1421 lnf
= walk(klass
.code
, self
.NameFinder(), verbose
=0)
1422 self
.locals.push(lnf
.getLocals())
1423 self
.graph
.setFlag(CO_NEWLOCALS
)
1425 self
.setDocstring(klass
.doc
)
1427 def get_module(self
):
1431 self
.graph
.startExitBlock()
1432 self
.emit('LOAD_LOCALS')
1433 self
.emit('RETURN_VALUE')
1435 class ClassCodeGenerator(NestedScopeMixin
, AbstractClassCode
, CodeGenerator
):
1436 super_init
= CodeGenerator
.__init
__
1439 __super_init
= AbstractClassCode
.__init
__
1441 def __init__(self
, klass
, scopes
, module
):
1442 self
.scopes
= scopes
1443 self
.scope
= scopes
[klass
]
1444 self
.__super
_init
(klass
, scopes
, module
)
1445 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1446 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1447 self
.set_lineno(klass
)
1448 self
.emit("LOAD_GLOBAL", "__name__")
1449 self
.storeName("__module__")
1451 self
.emit("LOAD_CONST", klass
.doc
)
1452 self
.storeName('__doc__')
1454 def generateArgList(arglist
):
1455 """Generate an arg list marking TupleArgs"""
1459 for i
in range(len(arglist
)):
1461 if isinstance(elt
, str):
1463 elif isinstance(elt
, tuple):
1464 args
.append(TupleArg(i
* 2, elt
))
1465 extra
.extend(misc
.flatten(elt
))
1468 raise ValueError, "unexpect argument type:", elt
1469 return args
+ extra
, count
1472 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
1474 walk(node
, v
, verbose
=0)
1480 def visitAssName(self
, node
):
1482 self
.op
= node
.flags
1483 elif self
.op
!= node
.flags
:
1484 raise ValueError, "mixed ops in stmt"
1485 visitAssAttr
= visitAssName
1486 visitSubscript
= visitAssName
1489 """Base class to support delegation for augmented assignment nodes
1491 To generator code for augmented assignments, we use the following
1492 wrapper classes. In visitAugAssign, the left-hand expression node
1493 is visited twice. The first time the visit uses the normal method
1494 for that node . The second time the visit uses a different method
1495 that generates the appropriate code to perform the assignment.
1496 These delegator classes wrap the original AST nodes in order to
1497 support the variant visit methods.
1499 def __init__(self
, obj
):
1502 def __getattr__(self
, attr
):
1503 return getattr(self
.obj
, attr
)
1505 class AugGetattr(Delegator
):
1508 class AugName(Delegator
):
1511 class AugSlice(Delegator
):
1514 class AugSubscript(Delegator
):
1518 ast
.Getattr
: AugGetattr
,
1520 ast
.Slice
: AugSlice
,
1521 ast
.Subscript
: AugSubscript
,
1525 return wrapper
[node
.__class
__](node
)
1527 if __name__
== "__main__":
1528 for file in sys
.argv
[1:]: