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 append
= "$append%d" % self
.__list
_count
577 self
.__list
_count
= self
.__list
_count
+ 1
578 self
.emit('BUILD_LIST', 0)
580 self
.emit('LOAD_ATTR', 'append')
581 self
._implicitNameOp
('STORE', append
)
584 for i
, for_
in zip(range(len(node
.quals
)), node
.quals
):
585 start
, anchor
= self
.visit(for_
)
589 cont
= self
.newBlock()
590 self
.visit(if_
, cont
)
591 stack
.insert(0, (start
, cont
, anchor
))
593 self
._implicitNameOp
('LOAD', append
)
594 self
.visit(node
.expr
)
595 self
.emit('CALL_FUNCTION', 1)
598 for start
, cont
, anchor
in stack
:
600 skip_one
= self
.newBlock()
601 self
.emit('JUMP_FORWARD', skip_one
)
602 self
.startBlock(cont
)
604 self
.nextBlock(skip_one
)
605 self
.emit('JUMP_ABSOLUTE', start
)
606 self
.startBlock(anchor
)
607 self
._implicitNameOp
('DELETE', append
)
609 self
.__list
_count
= self
.__list
_count
- 1
611 def visitListCompFor(self
, node
):
612 start
= self
.newBlock()
613 anchor
= self
.newBlock()
615 self
.visit(node
.list)
616 self
.emit('GET_ITER')
617 self
.nextBlock(start
)
618 self
.set_lineno(node
, force
=True)
619 self
.emit('FOR_ITER', anchor
)
621 self
.visit(node
.assign
)
624 def visitListCompIf(self
, node
, branch
):
625 self
.set_lineno(node
, force
=True)
626 self
.visit(node
.test
)
627 self
.emit('JUMP_IF_FALSE', branch
)
631 def _makeClosure(self
, gen
, args
):
632 frees
= gen
.scope
.get_free_vars()
635 self
.emit('LOAD_CLOSURE', name
)
636 self
.emit('BUILD_TUPLE', len(frees
))
637 self
.emit('LOAD_CONST', gen
)
638 self
.emit('MAKE_CLOSURE', args
)
640 self
.emit('LOAD_CONST', gen
)
641 self
.emit('MAKE_FUNCTION', args
)
643 def visitGenExpr(self
, node
):
644 gen
= GenExprCodeGenerator(node
, self
.scopes
, self
.class_name
,
648 self
.set_lineno(node
)
649 self
._makeClosure
(gen
, 0)
650 # precomputation of outmost iterable
651 self
.visit(node
.code
.quals
[0].iter)
652 self
.emit('GET_ITER')
653 self
.emit('CALL_FUNCTION', 1)
655 def visitGenExprInner(self
, node
):
656 self
.set_lineno(node
)
660 for i
, for_
in zip(range(len(node
.quals
)), node
.quals
):
661 start
, anchor
, end
= self
.visit(for_
)
665 cont
= self
.newBlock()
666 self
.visit(if_
, cont
)
667 stack
.insert(0, (start
, cont
, anchor
, end
))
669 self
.visit(node
.expr
)
670 self
.emit('YIELD_VALUE')
673 for start
, cont
, anchor
, end
in stack
:
675 skip_one
= self
.newBlock()
676 self
.emit('JUMP_FORWARD', skip_one
)
677 self
.startBlock(cont
)
679 self
.nextBlock(skip_one
)
680 self
.emit('JUMP_ABSOLUTE', start
)
681 self
.startBlock(anchor
)
682 self
.emit('POP_BLOCK')
686 self
.emit('LOAD_CONST', None)
688 def visitGenExprFor(self
, node
):
689 start
= self
.newBlock()
690 anchor
= self
.newBlock()
691 end
= self
.newBlock()
693 self
.setups
.push((LOOP
, start
))
694 self
.emit('SETUP_LOOP', end
)
699 self
.visit(node
.iter)
700 self
.emit('GET_ITER')
702 self
.nextBlock(start
)
703 self
.set_lineno(node
, force
=True)
704 self
.emit('FOR_ITER', anchor
)
706 self
.visit(node
.assign
)
707 return start
, anchor
, end
709 def visitGenExprIf(self
, node
, branch
):
710 self
.set_lineno(node
, force
=True)
711 self
.visit(node
.test
)
712 self
.emit('JUMP_IF_FALSE', branch
)
718 def visitAssert(self
, node
):
719 # XXX would be interesting to implement this via a
720 # transformation of the AST before this stage
722 end
= self
.newBlock()
723 self
.set_lineno(node
)
724 # XXX AssertionError appears to be special case -- it is always
725 # loaded as a global even if there is a local name. I guess this
726 # is a sort of renaming op.
728 self
.visit(node
.test
)
729 self
.emit('JUMP_IF_TRUE', end
)
732 self
.emit('LOAD_GLOBAL', 'AssertionError')
734 self
.visit(node
.fail
)
735 self
.emit('RAISE_VARARGS', 2)
737 self
.emit('RAISE_VARARGS', 1)
741 def visitRaise(self
, node
):
742 self
.set_lineno(node
)
745 self
.visit(node
.expr1
)
748 self
.visit(node
.expr2
)
751 self
.visit(node
.expr3
)
753 self
.emit('RAISE_VARARGS', n
)
755 def visitTryExcept(self
, node
):
756 body
= self
.newBlock()
757 handlers
= self
.newBlock()
758 end
= self
.newBlock()
760 lElse
= self
.newBlock()
763 self
.set_lineno(node
)
764 self
.emit('SETUP_EXCEPT', handlers
)
766 self
.setups
.push((EXCEPT
, body
))
767 self
.visit(node
.body
)
768 self
.emit('POP_BLOCK')
770 self
.emit('JUMP_FORWARD', lElse
)
771 self
.startBlock(handlers
)
773 last
= len(node
.handlers
) - 1
774 for i
in range(len(node
.handlers
)):
775 expr
, target
, body
= node
.handlers
[i
]
776 self
.set_lineno(expr
)
780 self
.emit('COMPARE_OP', 'exception match')
781 next
= self
.newBlock()
782 self
.emit('JUMP_IF_FALSE', next
)
792 self
.emit('JUMP_FORWARD', end
)
799 self
.emit('END_FINALLY')
801 self
.nextBlock(lElse
)
802 self
.visit(node
.else_
)
805 def visitTryFinally(self
, node
):
806 body
= self
.newBlock()
807 final
= self
.newBlock()
808 self
.set_lineno(node
)
809 self
.emit('SETUP_FINALLY', final
)
811 self
.setups
.push((TRY_FINALLY
, body
))
812 self
.visit(node
.body
)
813 self
.emit('POP_BLOCK')
815 self
.emit('LOAD_CONST', None)
816 self
.nextBlock(final
)
817 self
.setups
.push((END_FINALLY
, final
))
818 self
.visit(node
.final
)
819 self
.emit('END_FINALLY')
824 def visitWith(self
, node
):
825 body
= self
.newBlock()
826 final
= self
.newBlock()
827 exitvar
= "$exit%d" % self
.__with
_count
828 valuevar
= "$value%d" % self
.__with
_count
829 self
.__with
_count
+= 1
830 self
.set_lineno(node
)
831 self
.visit(node
.expr
)
833 self
.emit('LOAD_ATTR', '__exit__')
834 self
._implicitNameOp
('STORE', exitvar
)
835 self
.emit('LOAD_ATTR', '__enter__')
836 self
.emit('CALL_FUNCTION', 0)
837 if node
.vars is None:
840 self
._implicitNameOp
('STORE', valuevar
)
841 self
.emit('SETUP_FINALLY', final
)
843 self
.setups
.push((TRY_FINALLY
, body
))
844 if node
.vars is not None:
845 self
._implicitNameOp
('LOAD', valuevar
)
846 self
._implicitNameOp
('DELETE', valuevar
)
847 self
.visit(node
.vars)
848 self
.visit(node
.body
)
849 self
.emit('POP_BLOCK')
851 self
.emit('LOAD_CONST', None)
852 self
.nextBlock(final
)
853 self
.setups
.push((END_FINALLY
, final
))
854 self
.emit('WITH_CLEANUP')
855 self
.emit('END_FINALLY')
857 self
.__with
_count
-= 1
861 def visitDiscard(self
, node
):
862 self
.set_lineno(node
)
863 self
.visit(node
.expr
)
866 def visitConst(self
, node
):
867 self
.emit('LOAD_CONST', node
.value
)
869 def visitKeyword(self
, node
):
870 self
.emit('LOAD_CONST', node
.name
)
871 self
.visit(node
.expr
)
873 def visitGlobal(self
, node
):
874 # no code to generate
877 def visitName(self
, node
):
878 self
.set_lineno(node
)
879 self
.loadName(node
.name
)
881 def visitPass(self
, node
):
882 self
.set_lineno(node
)
884 def visitImport(self
, node
):
885 self
.set_lineno(node
)
886 level
= 0 if self
.graph
.checkFlag(CO_FUTURE_ABSIMPORT
) else -1
887 for name
, alias
in node
.names
:
889 self
.emit('LOAD_CONST', level
)
890 self
.emit('LOAD_CONST', None)
891 self
.emit('IMPORT_NAME', name
)
892 mod
= name
.split(".")[0]
894 self
._resolveDots
(name
)
895 self
.storeName(alias
)
899 def visitFrom(self
, node
):
900 self
.set_lineno(node
)
902 if level
== 0 and not self
.graph
.checkFlag(CO_FUTURE_ABSIMPORT
):
904 fromlist
= map(lambda (name
, alias
): name
, node
.names
)
906 self
.emit('LOAD_CONST', level
)
907 self
.emit('LOAD_CONST', tuple(fromlist
))
908 self
.emit('IMPORT_NAME', node
.modname
)
909 for name
, alias
in node
.names
:
913 self
.emit('IMPORT_STAR')
914 # There can only be one name w/ from ... import *
915 assert len(node
.names
) == 1
918 self
.emit('IMPORT_FROM', name
)
919 self
._resolveDots
(name
)
920 self
.storeName(alias
or name
)
922 self
.emit('IMPORT_FROM', name
)
925 def _resolveDots(self
, name
):
926 elts
= name
.split(".")
930 self
.emit('LOAD_ATTR', elt
)
932 def visitGetattr(self
, node
):
933 self
.visit(node
.expr
)
934 self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
936 # next five implement assignments
938 def visitAssign(self
, node
):
939 self
.set_lineno(node
)
940 self
.visit(node
.expr
)
941 dups
= len(node
.nodes
) - 1
942 for i
in range(len(node
.nodes
)):
946 if isinstance(elt
, ast
.Node
):
949 def visitAssName(self
, node
):
950 if node
.flags
== 'OP_ASSIGN':
951 self
.storeName(node
.name
)
952 elif node
.flags
== 'OP_DELETE':
953 self
.set_lineno(node
)
954 self
.delName(node
.name
)
956 print "oops", node
.flags
958 def visitAssAttr(self
, node
):
959 self
.visit(node
.expr
)
960 if node
.flags
== 'OP_ASSIGN':
961 self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
962 elif node
.flags
== 'OP_DELETE':
963 self
.emit('DELETE_ATTR', self
.mangle(node
.attrname
))
965 print "warning: unexpected flags:", node
.flags
968 def _visitAssSequence(self
, node
, op
='UNPACK_SEQUENCE'):
969 if findOp(node
) != 'OP_DELETE':
970 self
.emit(op
, len(node
.nodes
))
971 for child
in node
.nodes
:
975 visitAssTuple
= _visitAssSequence
976 visitAssList
= _visitAssSequence
978 def visitAssTuple(self
, node
):
979 self
._visitAssSequence
(node
, 'UNPACK_TUPLE')
981 def visitAssList(self
, node
):
982 self
._visitAssSequence
(node
, 'UNPACK_LIST')
984 # augmented assignment
986 def visitAugAssign(self
, node
):
987 self
.set_lineno(node
)
988 aug_node
= wrap_aug(node
.node
)
989 self
.visit(aug_node
, "load")
990 self
.visit(node
.expr
)
991 self
.emit(self
._augmented
_opcode
[node
.op
])
992 self
.visit(aug_node
, "store")
994 _augmented_opcode
= {
995 '+=' : 'INPLACE_ADD',
996 '-=' : 'INPLACE_SUBTRACT',
997 '*=' : 'INPLACE_MULTIPLY',
998 '/=' : 'INPLACE_DIVIDE',
999 '//=': 'INPLACE_FLOOR_DIVIDE',
1000 '%=' : 'INPLACE_MODULO',
1001 '**=': 'INPLACE_POWER',
1002 '>>=': 'INPLACE_RSHIFT',
1003 '<<=': 'INPLACE_LSHIFT',
1004 '&=' : 'INPLACE_AND',
1005 '^=' : 'INPLACE_XOR',
1006 '|=' : 'INPLACE_OR',
1009 def visitAugName(self
, node
, mode
):
1011 self
.loadName(node
.name
)
1012 elif mode
== "store":
1013 self
.storeName(node
.name
)
1015 def visitAugGetattr(self
, node
, mode
):
1017 self
.visit(node
.expr
)
1018 self
.emit('DUP_TOP')
1019 self
.emit('LOAD_ATTR', self
.mangle(node
.attrname
))
1020 elif mode
== "store":
1021 self
.emit('ROT_TWO')
1022 self
.emit('STORE_ATTR', self
.mangle(node
.attrname
))
1024 def visitAugSlice(self
, node
, mode
):
1026 self
.visitSlice(node
, 1)
1027 elif mode
== "store":
1034 self
.emit('ROT_TWO')
1036 self
.emit('ROT_FOUR')
1038 self
.emit('ROT_THREE')
1039 self
.emit('STORE_SLICE+%d' % slice)
1041 def visitAugSubscript(self
, node
, mode
):
1043 self
.visitSubscript(node
, 1)
1044 elif mode
== "store":
1045 self
.emit('ROT_THREE')
1046 self
.emit('STORE_SUBSCR')
1048 def visitExec(self
, node
):
1049 self
.visit(node
.expr
)
1050 if node
.locals is None:
1051 self
.emit('LOAD_CONST', None)
1053 self
.visit(node
.locals)
1054 if node
.globals is None:
1055 self
.emit('DUP_TOP')
1057 self
.visit(node
.globals)
1058 self
.emit('EXEC_STMT')
1060 def visitCallFunc(self
, node
):
1063 self
.set_lineno(node
)
1064 self
.visit(node
.node
)
1065 for arg
in node
.args
:
1067 if isinstance(arg
, ast
.Keyword
):
1071 if node
.star_args
is not None:
1072 self
.visit(node
.star_args
)
1073 if node
.dstar_args
is not None:
1074 self
.visit(node
.dstar_args
)
1075 have_star
= node
.star_args
is not None
1076 have_dstar
= node
.dstar_args
is not None
1077 opcode
= callfunc_opcode_info
[have_star
, have_dstar
]
1078 self
.emit(opcode
, kw
<< 8 | pos
)
1080 def visitPrint(self
, node
, newline
=0):
1081 self
.set_lineno(node
)
1083 self
.visit(node
.dest
)
1084 for child
in node
.nodes
:
1086 self
.emit('DUP_TOP')
1089 self
.emit('ROT_TWO')
1090 self
.emit('PRINT_ITEM_TO')
1092 self
.emit('PRINT_ITEM')
1093 if node
.dest
and not newline
:
1094 self
.emit('POP_TOP')
1096 def visitPrintnl(self
, node
):
1097 self
.visitPrint(node
, newline
=1)
1099 self
.emit('PRINT_NEWLINE_TO')
1101 self
.emit('PRINT_NEWLINE')
1103 def visitReturn(self
, node
):
1104 self
.set_lineno(node
)
1105 self
.visit(node
.value
)
1106 self
.emit('RETURN_VALUE')
1108 def visitYield(self
, node
):
1109 self
.set_lineno(node
)
1110 self
.visit(node
.value
)
1111 self
.emit('YIELD_VALUE')
1113 # slice and subscript stuff
1115 def visitSlice(self
, node
, aug_flag
=None):
1116 # aug_flag is used by visitAugSlice
1117 self
.visit(node
.expr
)
1120 self
.visit(node
.lower
)
1123 self
.visit(node
.upper
)
1127 self
.emit('DUP_TOP')
1129 self
.emit('DUP_TOPX', 3)
1131 self
.emit('DUP_TOPX', 2)
1132 if node
.flags
== 'OP_APPLY':
1133 self
.emit('SLICE+%d' % slice)
1134 elif node
.flags
== 'OP_ASSIGN':
1135 self
.emit('STORE_SLICE+%d' % slice)
1136 elif node
.flags
== 'OP_DELETE':
1137 self
.emit('DELETE_SLICE+%d' % slice)
1139 print "weird slice", node
.flags
1142 def visitSubscript(self
, node
, aug_flag
=None):
1143 self
.visit(node
.expr
)
1144 for sub
in node
.subs
:
1146 if len(node
.subs
) > 1:
1147 self
.emit('BUILD_TUPLE', len(node
.subs
))
1149 self
.emit('DUP_TOPX', 2)
1150 if node
.flags
== 'OP_APPLY':
1151 self
.emit('BINARY_SUBSCR')
1152 elif node
.flags
== 'OP_ASSIGN':
1153 self
.emit('STORE_SUBSCR')
1154 elif node
.flags
== 'OP_DELETE':
1155 self
.emit('DELETE_SUBSCR')
1159 def binaryOp(self
, node
, op
):
1160 self
.visit(node
.left
)
1161 self
.visit(node
.right
)
1164 def visitAdd(self
, node
):
1165 return self
.binaryOp(node
, 'BINARY_ADD')
1167 def visitSub(self
, node
):
1168 return self
.binaryOp(node
, 'BINARY_SUBTRACT')
1170 def visitMul(self
, node
):
1171 return self
.binaryOp(node
, 'BINARY_MULTIPLY')
1173 def visitDiv(self
, node
):
1174 return self
.binaryOp(node
, self
._div
_op
)
1176 def visitFloorDiv(self
, node
):
1177 return self
.binaryOp(node
, 'BINARY_FLOOR_DIVIDE')
1179 def visitMod(self
, node
):
1180 return self
.binaryOp(node
, 'BINARY_MODULO')
1182 def visitPower(self
, node
):
1183 return self
.binaryOp(node
, 'BINARY_POWER')
1185 def visitLeftShift(self
, node
):
1186 return self
.binaryOp(node
, 'BINARY_LSHIFT')
1188 def visitRightShift(self
, node
):
1189 return self
.binaryOp(node
, 'BINARY_RSHIFT')
1193 def unaryOp(self
, node
, op
):
1194 self
.visit(node
.expr
)
1197 def visitInvert(self
, node
):
1198 return self
.unaryOp(node
, 'UNARY_INVERT')
1200 def visitUnarySub(self
, node
):
1201 return self
.unaryOp(node
, 'UNARY_NEGATIVE')
1203 def visitUnaryAdd(self
, node
):
1204 return self
.unaryOp(node
, 'UNARY_POSITIVE')
1206 def visitUnaryInvert(self
, node
):
1207 return self
.unaryOp(node
, 'UNARY_INVERT')
1209 def visitNot(self
, node
):
1210 return self
.unaryOp(node
, 'UNARY_NOT')
1212 def visitBackquote(self
, node
):
1213 return self
.unaryOp(node
, 'UNARY_CONVERT')
1217 def bitOp(self
, nodes
, op
):
1218 self
.visit(nodes
[0])
1219 for node
in nodes
[1:]:
1223 def visitBitand(self
, node
):
1224 return self
.bitOp(node
.nodes
, 'BINARY_AND')
1226 def visitBitor(self
, node
):
1227 return self
.bitOp(node
.nodes
, 'BINARY_OR')
1229 def visitBitxor(self
, node
):
1230 return self
.bitOp(node
.nodes
, 'BINARY_XOR')
1232 # object constructors
1234 def visitEllipsis(self
, node
):
1235 self
.emit('LOAD_CONST', Ellipsis)
1237 def visitTuple(self
, node
):
1238 self
.set_lineno(node
)
1239 for elt
in node
.nodes
:
1241 self
.emit('BUILD_TUPLE', len(node
.nodes
))
1243 def visitList(self
, node
):
1244 self
.set_lineno(node
)
1245 for elt
in node
.nodes
:
1247 self
.emit('BUILD_LIST', len(node
.nodes
))
1249 def visitSliceobj(self
, node
):
1250 for child
in node
.nodes
:
1252 self
.emit('BUILD_SLICE', len(node
.nodes
))
1254 def visitDict(self
, node
):
1255 self
.set_lineno(node
)
1256 self
.emit('BUILD_MAP', 0)
1257 for k
, v
in node
.items
:
1258 self
.emit('DUP_TOP')
1261 self
.emit('ROT_THREE')
1262 self
.emit('STORE_SUBSCR')
1264 class NestedScopeMixin
:
1265 """Defines initClass() for nested scoping (Python 2.2-compatible)"""
1266 def initClass(self
):
1267 self
.__class
__.NameFinder
= LocalNameFinder
1268 self
.__class
__.FunctionGen
= FunctionCodeGenerator
1269 self
.__class
__.ClassGen
= ClassCodeGenerator
1271 class ModuleCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1272 __super_init
= CodeGenerator
.__init
__
1276 def __init__(self
, tree
):
1277 self
.graph
= pyassem
.PyFlowGraph("<module>", tree
.filename
)
1278 self
.futures
= future
.find_futures(tree
)
1282 def get_module(self
):
1285 class ExpressionCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1286 __super_init
= CodeGenerator
.__init
__
1291 def __init__(self
, tree
):
1292 self
.graph
= pyassem
.PyFlowGraph("<expression>", tree
.filename
)
1296 def get_module(self
):
1299 class InteractiveCodeGenerator(NestedScopeMixin
, CodeGenerator
):
1301 __super_init
= CodeGenerator
.__init
__
1306 def __init__(self
, tree
):
1307 self
.graph
= pyassem
.PyFlowGraph("<interactive>", tree
.filename
)
1309 self
.set_lineno(tree
)
1311 self
.emit('RETURN_VALUE')
1313 def get_module(self
):
1316 def visitDiscard(self
, node
):
1317 # XXX Discard means it's an expression. Perhaps this is a bad
1319 self
.visit(node
.expr
)
1320 self
.emit('PRINT_EXPR')
1322 class AbstractFunctionCode
:
1326 def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
1327 self
.class_name
= class_name
1330 klass
= FunctionCodeGenerator
1331 name
= "<lambda.%d>" % klass
.lambdaCount
1332 klass
.lambdaCount
= klass
.lambdaCount
+ 1
1336 args
, hasTupleArg
= generateArgList(func
.argnames
)
1337 self
.graph
= pyassem
.PyFlowGraph(name
, func
.filename
, args
,
1339 self
.isLambda
= isLambda
1342 if not isLambda
and func
.doc
:
1343 self
.setDocstring(func
.doc
)
1345 lnf
= walk(func
.code
, self
.NameFinder(args
), verbose
=0)
1346 self
.locals.push(lnf
.getLocals())
1348 self
.graph
.setFlag(CO_VARARGS
)
1350 self
.graph
.setFlag(CO_VARKEYWORDS
)
1351 self
.set_lineno(func
)
1353 self
.generateArgUnpack(func
.argnames
)
1355 def get_module(self
):
1359 self
.graph
.startExitBlock()
1360 if not self
.isLambda
:
1361 self
.emit('LOAD_CONST', None)
1362 self
.emit('RETURN_VALUE')
1364 def generateArgUnpack(self
, args
):
1365 for i
in range(len(args
)):
1367 if isinstance(arg
, tuple):
1368 self
.emit('LOAD_FAST', '.%d' % (i
* 2))
1369 self
.unpackSequence(arg
)
1371 def unpackSequence(self
, tup
):
1373 self
.emit('UNPACK_SEQUENCE', len(tup
))
1375 self
.emit('UNPACK_TUPLE', len(tup
))
1377 if isinstance(elt
, tuple):
1378 self
.unpackSequence(elt
)
1380 self
._nameOp
('STORE', elt
)
1382 unpackTuple
= unpackSequence
1384 class FunctionCodeGenerator(NestedScopeMixin
, AbstractFunctionCode
,
1386 super_init
= CodeGenerator
.__init
__ # call be other init
1389 __super_init
= AbstractFunctionCode
.__init
__
1391 def __init__(self
, func
, scopes
, isLambda
, class_name
, mod
):
1392 self
.scopes
= scopes
1393 self
.scope
= scopes
[func
]
1394 self
.__super
_init
(func
, scopes
, isLambda
, class_name
, mod
)
1395 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1396 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1397 if self
.scope
.generator
is not None:
1398 self
.graph
.setFlag(CO_GENERATOR
)
1400 class GenExprCodeGenerator(NestedScopeMixin
, AbstractFunctionCode
,
1402 super_init
= CodeGenerator
.__init
__ # call be other init
1405 __super_init
= AbstractFunctionCode
.__init
__
1407 def __init__(self
, gexp
, scopes
, class_name
, mod
):
1408 self
.scopes
= scopes
1409 self
.scope
= scopes
[gexp
]
1410 self
.__super
_init
(gexp
, scopes
, 1, class_name
, mod
)
1411 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1412 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1413 self
.graph
.setFlag(CO_GENERATOR
)
1415 class AbstractClassCode
:
1417 def __init__(self
, klass
, scopes
, module
):
1418 self
.class_name
= klass
.name
1419 self
.module
= module
1420 self
.graph
= pyassem
.PyFlowGraph(klass
.name
, klass
.filename
,
1421 optimized
=0, klass
=1)
1423 lnf
= walk(klass
.code
, self
.NameFinder(), verbose
=0)
1424 self
.locals.push(lnf
.getLocals())
1425 self
.graph
.setFlag(CO_NEWLOCALS
)
1427 self
.setDocstring(klass
.doc
)
1429 def get_module(self
):
1433 self
.graph
.startExitBlock()
1434 self
.emit('LOAD_LOCALS')
1435 self
.emit('RETURN_VALUE')
1437 class ClassCodeGenerator(NestedScopeMixin
, AbstractClassCode
, CodeGenerator
):
1438 super_init
= CodeGenerator
.__init
__
1441 __super_init
= AbstractClassCode
.__init
__
1443 def __init__(self
, klass
, scopes
, module
):
1444 self
.scopes
= scopes
1445 self
.scope
= scopes
[klass
]
1446 self
.__super
_init
(klass
, scopes
, module
)
1447 self
.graph
.setFreeVars(self
.scope
.get_free_vars())
1448 self
.graph
.setCellVars(self
.scope
.get_cell_vars())
1449 self
.set_lineno(klass
)
1450 self
.emit("LOAD_GLOBAL", "__name__")
1451 self
.storeName("__module__")
1453 self
.emit("LOAD_CONST", klass
.doc
)
1454 self
.storeName('__doc__')
1456 def generateArgList(arglist
):
1457 """Generate an arg list marking TupleArgs"""
1461 for i
in range(len(arglist
)):
1463 if isinstance(elt
, str):
1465 elif isinstance(elt
, tuple):
1466 args
.append(TupleArg(i
* 2, elt
))
1467 extra
.extend(misc
.flatten(elt
))
1470 raise ValueError, "unexpect argument type:", elt
1471 return args
+ extra
, count
1474 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
1476 walk(node
, v
, verbose
=0)
1482 def visitAssName(self
, node
):
1484 self
.op
= node
.flags
1485 elif self
.op
!= node
.flags
:
1486 raise ValueError, "mixed ops in stmt"
1487 visitAssAttr
= visitAssName
1488 visitSubscript
= visitAssName
1491 """Base class to support delegation for augmented assignment nodes
1493 To generator code for augmented assignments, we use the following
1494 wrapper classes. In visitAugAssign, the left-hand expression node
1495 is visited twice. The first time the visit uses the normal method
1496 for that node . The second time the visit uses a different method
1497 that generates the appropriate code to perform the assignment.
1498 These delegator classes wrap the original AST nodes in order to
1499 support the variant visit methods.
1501 def __init__(self
, obj
):
1504 def __getattr__(self
, attr
):
1505 return getattr(self
.obj
, attr
)
1507 class AugGetattr(Delegator
):
1510 class AugName(Delegator
):
1513 class AugSlice(Delegator
):
1516 class AugSubscript(Delegator
):
1520 ast
.Getattr
: AugGetattr
,
1522 ast
.Slice
: AugSlice
,
1523 ast
.Subscript
: AugSubscript
,
1527 return wrapper
[node
.__class
__](node
)
1529 if __name__
== "__main__":
1530 for file in sys
.argv
[1:]: