Patch by Jeremy Katz (SF #1609407)
[python.git] / Lib / compiler / pycodegen.py
blob8cf7d772daccd8d3e514c429b5276c98a314efec
1 import imp
2 import os
3 import marshal
4 import struct
5 import sys
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?
18 try:
19 VERSION = sys.version_info[0]
20 except AttributeError:
21 VERSION = 1
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",
31 LOOP = 1
32 EXCEPT = 2
33 TRY_FINALLY = 3
34 END_FINALLY = 4
36 def compileFile(filename, display=0):
37 f = open(filename, 'U')
38 buf = f.read()
39 f.close()
40 mod = Module(buf, filename)
41 try:
42 mod.compile(display)
43 except SyntaxError:
44 raise
45 else:
46 f = open(filename + "c", "wb")
47 mod.dump(f)
48 f.close()
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"
55 if mode == "single":
56 gen = Interactive(source, filename)
57 elif mode == "exec":
58 gen = Module(source, filename)
59 elif mode == "eval":
60 gen = Expression(source, filename)
61 else:
62 raise ValueError("compile() 3rd arg must be 'exec' or "
63 "'eval' or 'single'")
64 gen.compile()
65 return gen.code
67 class AbstractCompileMode:
69 mode = None # defined by subclass
71 def __init__(self, source, filename):
72 self.source = source
73 self.filename = filename
74 self.code = None
76 def _get_tree(self):
77 tree = parse(self.source, self.mode)
78 misc.set_filename(self.filename, tree)
79 syntax.check(tree)
80 return tree
82 def compile(self):
83 pass # implemented by subclass
85 def getCode(self):
86 return self.code
88 class Expression(AbstractCompileMode):
90 mode = "eval"
92 def compile(self):
93 tree = self._get_tree()
94 gen = ExpressionCodeGenerator(tree)
95 self.code = gen.getCode()
97 class Interactive(AbstractCompileMode):
99 mode = "single"
101 def compile(self):
102 tree = self._get_tree()
103 gen = InteractiveCodeGenerator(tree)
104 self.code = gen.getCode()
106 class Module(AbstractCompileMode):
108 mode = "exec"
110 def compile(self, display=0):
111 tree = self._get_tree()
112 gen = ModuleCodeGenerator(tree)
113 if display:
114 import pprint
115 print pprint.pprint(tree)
116 self.code = gen.getCode()
118 def dump(self, f):
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()
138 for name in names:
139 self.names.add(name)
141 # XXX list comprehensions and for loops
143 def getLocals(self):
144 for elt in self.globals.elements():
145 if self.names.has_elt(elt):
146 self.names.remove(elt)
147 return self.names
149 def visitDict(self, node):
150 pass
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):
160 pass
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):
178 if not node.value:
179 return 1
180 return 0
182 class CodeGenerator:
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
193 defined.
196 optimized = 0 # is namespace access optimized?
197 __initialized = None
198 class_name = None # provide default for instance variable
200 def __init__(self):
201 if self.__initialized is None:
202 self.initClass()
203 self.__class__.__initialized = 1
204 self.checkClass()
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)
222 def initClass(self):
223 """This method is called once for each class"""
225 def checkClass(self):
226 """Verify that class is constructed correctly"""
227 try:
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
243 def getCode(self):
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)
250 else:
251 return name
253 def parseSymbols(self, tree):
254 s = symbols.SymbolVisitor()
255 walk(tree, s)
256 return s.scopes
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)
281 else:
282 self.emit(prefix + '_FAST', name)
283 elif scope == SC_GLOBAL:
284 if not self.optimized:
285 self.emit(prefix + '_NAME', name)
286 else:
287 self.emit(prefix + '_GLOBAL', name)
288 elif scope == SC_FREE or scope == SC_CELL:
289 self.emit(prefix + '_DEREF', name)
290 else:
291 raise RuntimeError, "unsupported scope for var %s: %d" % \
292 (name, scope)
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.
301 if self.optimized:
302 self.emit(prefix + '_FAST', name)
303 else:
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
316 emitted.
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
327 or force):
328 self.emit('SET_LINENO', lineno)
329 self.last_lineno = lineno
330 return True
331 return False
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
338 FunctionGen = None
339 ClassGen = None
341 def visitModule(self, node):
342 self.scopes = self.parseSymbols(node)
343 self.scope = self.scopes[node]
344 self.emit('SET_LINENO', 0)
345 if node.doc:
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)
363 if node.doc:
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)
375 else:
376 ndecorators = 0
378 gen = self.FunctionGen(node, self.scopes, isLambda,
379 self.class_name, self.get_module())
380 walk(node.code, gen)
381 gen.finish()
382 self.set_lineno(node)
383 for default in node.defaults:
384 self.visit(default)
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,
391 self.get_module())
392 walk(node.code, gen)
393 gen.finish()
394 self.set_lineno(node)
395 self.emit('LOAD_CONST', node.name)
396 for base in node.bases:
397 self.visit(base)
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
415 continue
416 self.set_lineno(test)
417 self.visit(test)
418 nextTest = self.newBlock()
419 self.emit('JUMP_IF_FALSE', nextTest)
420 self.nextBlock()
421 self.emit('POP_TOP')
422 self.visit(suite)
423 self.emit('JUMP_FORWARD', end)
424 self.startBlock(nextTest)
425 self.emit('POP_TOP')
426 if node.else_:
427 self.visit(node.else_)
428 self.nextBlock(end)
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)
439 self.nextBlock(loop)
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)
446 self.nextBlock()
447 self.emit('POP_TOP')
448 self.visit(node.body)
449 self.emit('JUMP_ABSOLUTE', loop)
451 self.startBlock(else_) # or just the POPs if not else clause
452 self.emit('POP_TOP')
453 self.emit('POP_BLOCK')
454 self.setups.pop()
455 if node.else_:
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')
478 self.setups.pop()
479 if node.else_:
480 self.visit(node.else_)
481 self.nextBlock(after)
483 def visitBreak(self, node):
484 if not self.setups:
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):
491 if not self.setups:
492 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
493 (node.filename, node.lineno)
494 kind, block = self.setups.top()
495 if kind == LOOP:
496 self.set_lineno(node)
497 self.emit('JUMP_ABSOLUTE', block)
498 self.nextBlock()
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)
503 while top > 0:
504 top = top - 1
505 kind, loop_block = self.setups[top]
506 if kind == LOOP:
507 break
508 if kind != LOOP:
509 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
510 (node.filename, node.lineno)
511 self.emit('CONTINUE_LOOP', loop_block)
512 self.nextBlock()
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]:
520 self.visit(child)
521 self.emit(jump, end)
522 self.nextBlock()
523 self.emit('POP_TOP')
524 self.visit(node.nodes[-1])
525 self.nextBlock(end)
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)
538 self.emit('POP_TOP')
539 self.visit(node.then)
540 self.emit('JUMP_FORWARD', endblock)
541 self.nextBlock(elseblock)
542 self.emit('POP_TOP')
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]:
550 self.visit(code)
551 self.emit('DUP_TOP')
552 self.emit('ROT_THREE')
553 self.emit('COMPARE_OP', op)
554 self.emit('JUMP_IF_FALSE', cleanup)
555 self.nextBlock()
556 self.emit('POP_TOP')
557 # now do the last comparison
558 if node.ops:
559 op, code = node.ops[-1]
560 self.visit(code)
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)
566 self.emit('ROT_TWO')
567 self.emit('POP_TOP')
568 self.nextBlock(end)
570 # list comprehensions
571 __list_count = 0
573 def visitListComp(self, node):
574 self.set_lineno(node)
575 # setup list
576 tmpname = "$list%d" % self.__list_count
577 self.__list_count = self.__list_count + 1
578 self.emit('BUILD_LIST', 0)
579 self.emit('DUP_TOP')
580 self._implicitNameOp('STORE', tmpname)
582 stack = []
583 for i, for_ in zip(range(len(node.quals)), node.quals):
584 start, anchor = self.visit(for_)
585 cont = None
586 for if_ in for_.ifs:
587 if cont is None:
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:
597 if cont:
598 skip_one = self.newBlock()
599 self.emit('JUMP_FORWARD', skip_one)
600 self.startBlock(cont)
601 self.emit('POP_TOP')
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)
618 self.nextBlock()
619 self.visit(node.assign)
620 return start, anchor
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)
626 self.newBlock()
627 self.emit('POP_TOP')
629 def _makeClosure(self, gen, args):
630 frees = gen.scope.get_free_vars()
631 if frees:
632 for name in frees:
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)
637 else:
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,
643 self.get_module())
644 walk(node.code, gen)
645 gen.finish()
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)
655 # setup list
657 stack = []
658 for i, for_ in zip(range(len(node.quals)), node.quals):
659 start, anchor, end = self.visit(for_)
660 cont = None
661 for if_ in for_.ifs:
662 if cont is None:
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')
669 self.emit('POP_TOP')
671 for start, cont, anchor, end in stack:
672 if cont:
673 skip_one = self.newBlock()
674 self.emit('JUMP_FORWARD', skip_one)
675 self.startBlock(cont)
676 self.emit('POP_TOP')
677 self.nextBlock(skip_one)
678 self.emit('JUMP_ABSOLUTE', start)
679 self.startBlock(anchor)
680 self.emit('POP_BLOCK')
681 self.setups.pop()
682 self.startBlock(end)
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)
694 if node.is_outmost:
695 self.loadName('.0')
696 else:
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)
703 self.nextBlock()
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)
711 self.newBlock()
712 self.emit('POP_TOP')
714 # exception related
716 def visitAssert(self, node):
717 # XXX would be interesting to implement this via a
718 # transformation of the AST before this stage
719 if __debug__:
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.
725 self.nextBlock()
726 self.visit(node.test)
727 self.emit('JUMP_IF_TRUE', end)
728 self.nextBlock()
729 self.emit('POP_TOP')
730 self.emit('LOAD_GLOBAL', 'AssertionError')
731 if node.fail:
732 self.visit(node.fail)
733 self.emit('RAISE_VARARGS', 2)
734 else:
735 self.emit('RAISE_VARARGS', 1)
736 self.nextBlock(end)
737 self.emit('POP_TOP')
739 def visitRaise(self, node):
740 self.set_lineno(node)
741 n = 0
742 if node.expr1:
743 self.visit(node.expr1)
744 n = n + 1
745 if node.expr2:
746 self.visit(node.expr2)
747 n = n + 1
748 if node.expr3:
749 self.visit(node.expr3)
750 n = n + 1
751 self.emit('RAISE_VARARGS', n)
753 def visitTryExcept(self, node):
754 body = self.newBlock()
755 handlers = self.newBlock()
756 end = self.newBlock()
757 if node.else_:
758 lElse = self.newBlock()
759 else:
760 lElse = end
761 self.set_lineno(node)
762 self.emit('SETUP_EXCEPT', handlers)
763 self.nextBlock(body)
764 self.setups.push((EXCEPT, body))
765 self.visit(node.body)
766 self.emit('POP_BLOCK')
767 self.setups.pop()
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)
775 if expr:
776 self.emit('DUP_TOP')
777 self.visit(expr)
778 self.emit('COMPARE_OP', 'exception match')
779 next = self.newBlock()
780 self.emit('JUMP_IF_FALSE', next)
781 self.nextBlock()
782 self.emit('POP_TOP')
783 self.emit('POP_TOP')
784 if target:
785 self.visit(target)
786 else:
787 self.emit('POP_TOP')
788 self.emit('POP_TOP')
789 self.visit(body)
790 self.emit('JUMP_FORWARD', end)
791 if expr:
792 self.nextBlock(next)
793 else:
794 self.nextBlock()
795 if expr: # XXX
796 self.emit('POP_TOP')
797 self.emit('END_FINALLY')
798 if node.else_:
799 self.nextBlock(lElse)
800 self.visit(node.else_)
801 self.nextBlock(end)
803 def visitTryFinally(self, node):
804 body = self.newBlock()
805 final = self.newBlock()
806 self.set_lineno(node)
807 self.emit('SETUP_FINALLY', final)
808 self.nextBlock(body)
809 self.setups.push((TRY_FINALLY, body))
810 self.visit(node.body)
811 self.emit('POP_BLOCK')
812 self.setups.pop()
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')
818 self.setups.pop()
820 __with_count = 0
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)
830 self.emit('DUP_TOP')
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:
836 self.emit('POP_TOP')
837 else:
838 self._implicitNameOp('STORE', valuevar)
839 self.emit('SETUP_FINALLY', final)
840 self.nextBlock(body)
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')
848 self.setups.pop()
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')
854 self.setups.pop()
855 self.__with_count -= 1
857 # misc
859 def visitDiscard(self, node):
860 self.set_lineno(node)
861 self.visit(node.expr)
862 self.emit('POP_TOP')
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
873 pass
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:
886 if VERSION > 1:
887 self.emit('LOAD_CONST', level)
888 self.emit('LOAD_CONST', None)
889 self.emit('IMPORT_NAME', name)
890 mod = name.split(".")[0]
891 if alias:
892 self._resolveDots(name)
893 self.storeName(alias)
894 else:
895 self.storeName(mod)
897 def visitFrom(self, node):
898 self.set_lineno(node)
899 level = node.level
900 if level == 0 and not self.graph.checkFlag(CO_FUTURE_ABSIMPORT):
901 level = -1
902 fromlist = map(lambda (name, alias): name, node.names)
903 if VERSION > 1:
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:
908 if VERSION > 1:
909 if name == '*':
910 self.namespace = 0
911 self.emit('IMPORT_STAR')
912 # There can only be one name w/ from ... import *
913 assert len(node.names) == 1
914 return
915 else:
916 self.emit('IMPORT_FROM', name)
917 self._resolveDots(name)
918 self.storeName(alias or name)
919 else:
920 self.emit('IMPORT_FROM', name)
921 self.emit('POP_TOP')
923 def _resolveDots(self, name):
924 elts = name.split(".")
925 if len(elts) == 1:
926 return
927 for elt in elts[1:]:
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)):
941 elt = node.nodes[i]
942 if i < dups:
943 self.emit('DUP_TOP')
944 if isinstance(elt, ast.Node):
945 self.visit(elt)
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)
953 else:
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))
962 else:
963 print "warning: unexpected flags:", node.flags
964 print node
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:
970 self.visit(child)
972 if VERSION > 1:
973 visitAssTuple = _visitAssSequence
974 visitAssList = _visitAssSequence
975 else:
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):
1008 if mode == "load":
1009 self.loadName(node.name)
1010 elif mode == "store":
1011 self.storeName(node.name)
1013 def visitAugGetattr(self, node, mode):
1014 if mode == "load":
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):
1023 if mode == "load":
1024 self.visitSlice(node, 1)
1025 elif mode == "store":
1026 slice = 0
1027 if node.lower:
1028 slice = slice | 1
1029 if node.upper:
1030 slice = slice | 2
1031 if slice == 0:
1032 self.emit('ROT_TWO')
1033 elif slice == 3:
1034 self.emit('ROT_FOUR')
1035 else:
1036 self.emit('ROT_THREE')
1037 self.emit('STORE_SLICE+%d' % slice)
1039 def visitAugSubscript(self, node, mode):
1040 if mode == "load":
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)
1050 else:
1051 self.visit(node.locals)
1052 if node.globals is None:
1053 self.emit('DUP_TOP')
1054 else:
1055 self.visit(node.globals)
1056 self.emit('EXEC_STMT')
1058 def visitCallFunc(self, node):
1059 pos = 0
1060 kw = 0
1061 self.set_lineno(node)
1062 self.visit(node.node)
1063 for arg in node.args:
1064 self.visit(arg)
1065 if isinstance(arg, ast.Keyword):
1066 kw = kw + 1
1067 else:
1068 pos = pos + 1
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)
1080 if node.dest:
1081 self.visit(node.dest)
1082 for child in node.nodes:
1083 if node.dest:
1084 self.emit('DUP_TOP')
1085 self.visit(child)
1086 if node.dest:
1087 self.emit('ROT_TWO')
1088 self.emit('PRINT_ITEM_TO')
1089 else:
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)
1096 if node.dest:
1097 self.emit('PRINT_NEWLINE_TO')
1098 else:
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)
1116 slice = 0
1117 if node.lower:
1118 self.visit(node.lower)
1119 slice = slice | 1
1120 if node.upper:
1121 self.visit(node.upper)
1122 slice = slice | 2
1123 if aug_flag:
1124 if slice == 0:
1125 self.emit('DUP_TOP')
1126 elif slice == 3:
1127 self.emit('DUP_TOPX', 3)
1128 else:
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)
1136 else:
1137 print "weird slice", node.flags
1138 raise
1140 def visitSubscript(self, node, aug_flag=None):
1141 self.visit(node.expr)
1142 for sub in node.subs:
1143 self.visit(sub)
1144 if len(node.subs) > 1:
1145 self.emit('BUILD_TUPLE', len(node.subs))
1146 if aug_flag:
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')
1155 # binary ops
1157 def binaryOp(self, node, op):
1158 self.visit(node.left)
1159 self.visit(node.right)
1160 self.emit(op)
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')
1189 # unary ops
1191 def unaryOp(self, node, op):
1192 self.visit(node.expr)
1193 self.emit(op)
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')
1213 # bit ops
1215 def bitOp(self, nodes, op):
1216 self.visit(nodes[0])
1217 for node in nodes[1:]:
1218 self.visit(node)
1219 self.emit(op)
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:
1238 self.visit(elt)
1239 self.emit('BUILD_TUPLE', len(node.nodes))
1241 def visitList(self, node):
1242 self.set_lineno(node)
1243 for elt in node.nodes:
1244 self.visit(elt)
1245 self.emit('BUILD_LIST', len(node.nodes))
1247 def visitSliceobj(self, node):
1248 for child in node.nodes:
1249 self.visit(child)
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')
1257 self.visit(k)
1258 self.visit(v)
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__
1272 scopes = None
1274 def __init__(self, tree):
1275 self.graph = pyassem.PyFlowGraph("<module>", tree.filename)
1276 self.futures = future.find_futures(tree)
1277 self.__super_init()
1278 walk(tree, self)
1280 def get_module(self):
1281 return self
1283 class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator):
1284 __super_init = CodeGenerator.__init__
1286 scopes = None
1287 futures = ()
1289 def __init__(self, tree):
1290 self.graph = pyassem.PyFlowGraph("<expression>", tree.filename)
1291 self.__super_init()
1292 walk(tree, self)
1294 def get_module(self):
1295 return self
1297 class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator):
1299 __super_init = CodeGenerator.__init__
1301 scopes = None
1302 futures = ()
1304 def __init__(self, tree):
1305 self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename)
1306 self.__super_init()
1307 self.set_lineno(tree)
1308 walk(tree, self)
1309 self.emit('RETURN_VALUE')
1311 def get_module(self):
1312 return self
1314 def visitDiscard(self, node):
1315 # XXX Discard means it's an expression. Perhaps this is a bad
1316 # name.
1317 self.visit(node.expr)
1318 self.emit('PRINT_EXPR')
1320 class AbstractFunctionCode:
1321 optimized = 1
1322 lambdaCount = 0
1324 def __init__(self, func, scopes, isLambda, class_name, mod):
1325 self.class_name = class_name
1326 self.module = mod
1327 if isLambda:
1328 klass = FunctionCodeGenerator
1329 name = "<lambda.%d>" % klass.lambdaCount
1330 klass.lambdaCount = klass.lambdaCount + 1
1331 else:
1332 name = func.name
1334 args, hasTupleArg = generateArgList(func.argnames)
1335 self.graph = pyassem.PyFlowGraph(name, func.filename, args,
1336 optimized=1)
1337 self.isLambda = isLambda
1338 self.super_init()
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())
1345 if func.varargs:
1346 self.graph.setFlag(CO_VARARGS)
1347 if func.kwargs:
1348 self.graph.setFlag(CO_VARKEYWORDS)
1349 self.set_lineno(func)
1350 if hasTupleArg:
1351 self.generateArgUnpack(func.argnames)
1353 def get_module(self):
1354 return self.module
1356 def finish(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)):
1364 arg = args[i]
1365 if isinstance(arg, tuple):
1366 self.emit('LOAD_FAST', '.%d' % (i * 2))
1367 self.unpackSequence(arg)
1369 def unpackSequence(self, tup):
1370 if VERSION > 1:
1371 self.emit('UNPACK_SEQUENCE', len(tup))
1372 else:
1373 self.emit('UNPACK_TUPLE', len(tup))
1374 for elt in tup:
1375 if isinstance(elt, tuple):
1376 self.unpackSequence(elt)
1377 else:
1378 self._nameOp('STORE', elt)
1380 unpackTuple = unpackSequence
1382 class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
1383 CodeGenerator):
1384 super_init = CodeGenerator.__init__ # call be other init
1385 scopes = None
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,
1399 CodeGenerator):
1400 super_init = CodeGenerator.__init__ # call be other init
1401 scopes = None
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)
1420 self.super_init()
1421 lnf = walk(klass.code, self.NameFinder(), verbose=0)
1422 self.locals.push(lnf.getLocals())
1423 self.graph.setFlag(CO_NEWLOCALS)
1424 if klass.doc:
1425 self.setDocstring(klass.doc)
1427 def get_module(self):
1428 return self.module
1430 def finish(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__
1437 scopes = None
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__")
1450 if klass.doc:
1451 self.emit("LOAD_CONST", klass.doc)
1452 self.storeName('__doc__')
1454 def generateArgList(arglist):
1455 """Generate an arg list marking TupleArgs"""
1456 args = []
1457 extra = []
1458 count = 0
1459 for i in range(len(arglist)):
1460 elt = arglist[i]
1461 if isinstance(elt, str):
1462 args.append(elt)
1463 elif isinstance(elt, tuple):
1464 args.append(TupleArg(i * 2, elt))
1465 extra.extend(misc.flatten(elt))
1466 count = count + 1
1467 else:
1468 raise ValueError, "unexpect argument type:", elt
1469 return args + extra, count
1471 def findOp(node):
1472 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
1473 v = OpFinder()
1474 walk(node, v, verbose=0)
1475 return v.op
1477 class OpFinder:
1478 def __init__(self):
1479 self.op = None
1480 def visitAssName(self, node):
1481 if self.op is None:
1482 self.op = node.flags
1483 elif self.op != node.flags:
1484 raise ValueError, "mixed ops in stmt"
1485 visitAssAttr = visitAssName
1486 visitSubscript = visitAssName
1488 class Delegator:
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):
1500 self.obj = obj
1502 def __getattr__(self, attr):
1503 return getattr(self.obj, attr)
1505 class AugGetattr(Delegator):
1506 pass
1508 class AugName(Delegator):
1509 pass
1511 class AugSlice(Delegator):
1512 pass
1514 class AugSubscript(Delegator):
1515 pass
1517 wrapper = {
1518 ast.Getattr: AugGetattr,
1519 ast.Name: AugName,
1520 ast.Slice: AugSlice,
1521 ast.Subscript: AugSubscript,
1524 def wrap_aug(node):
1525 return wrapper[node.__class__](node)
1527 if __name__ == "__main__":
1528 for file in sys.argv[1:]:
1529 compileFile(file)