Updates of recent changes to logging.
[python.git] / Lib / compiler / pycodegen.py
blobb46b1c1489e25e8a5519db07fb3c49f84d238a05
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._implicitNameOp('LOAD', exitvar)
853 self._implicitNameOp('DELETE', exitvar)
854 self.emit('WITH_CLEANUP')
855 self.emit('END_FINALLY')
856 self.setups.pop()
857 self.__with_count -= 1
859 # misc
861 def visitDiscard(self, node):
862 self.set_lineno(node)
863 self.visit(node.expr)
864 self.emit('POP_TOP')
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
875 pass
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:
888 if VERSION > 1:
889 self.emit('LOAD_CONST', level)
890 self.emit('LOAD_CONST', None)
891 self.emit('IMPORT_NAME', name)
892 mod = name.split(".")[0]
893 if alias:
894 self._resolveDots(name)
895 self.storeName(alias)
896 else:
897 self.storeName(mod)
899 def visitFrom(self, node):
900 self.set_lineno(node)
901 level = node.level
902 if level == 0 and not self.graph.checkFlag(CO_FUTURE_ABSIMPORT):
903 level = -1
904 fromlist = map(lambda (name, alias): name, node.names)
905 if VERSION > 1:
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:
910 if VERSION > 1:
911 if name == '*':
912 self.namespace = 0
913 self.emit('IMPORT_STAR')
914 # There can only be one name w/ from ... import *
915 assert len(node.names) == 1
916 return
917 else:
918 self.emit('IMPORT_FROM', name)
919 self._resolveDots(name)
920 self.storeName(alias or name)
921 else:
922 self.emit('IMPORT_FROM', name)
923 self.emit('POP_TOP')
925 def _resolveDots(self, name):
926 elts = name.split(".")
927 if len(elts) == 1:
928 return
929 for elt in elts[1:]:
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)):
943 elt = node.nodes[i]
944 if i < dups:
945 self.emit('DUP_TOP')
946 if isinstance(elt, ast.Node):
947 self.visit(elt)
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)
955 else:
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))
964 else:
965 print "warning: unexpected flags:", node.flags
966 print node
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:
972 self.visit(child)
974 if VERSION > 1:
975 visitAssTuple = _visitAssSequence
976 visitAssList = _visitAssSequence
977 else:
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):
1010 if mode == "load":
1011 self.loadName(node.name)
1012 elif mode == "store":
1013 self.storeName(node.name)
1015 def visitAugGetattr(self, node, mode):
1016 if mode == "load":
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):
1025 if mode == "load":
1026 self.visitSlice(node, 1)
1027 elif mode == "store":
1028 slice = 0
1029 if node.lower:
1030 slice = slice | 1
1031 if node.upper:
1032 slice = slice | 2
1033 if slice == 0:
1034 self.emit('ROT_TWO')
1035 elif slice == 3:
1036 self.emit('ROT_FOUR')
1037 else:
1038 self.emit('ROT_THREE')
1039 self.emit('STORE_SLICE+%d' % slice)
1041 def visitAugSubscript(self, node, mode):
1042 if mode == "load":
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)
1052 else:
1053 self.visit(node.locals)
1054 if node.globals is None:
1055 self.emit('DUP_TOP')
1056 else:
1057 self.visit(node.globals)
1058 self.emit('EXEC_STMT')
1060 def visitCallFunc(self, node):
1061 pos = 0
1062 kw = 0
1063 self.set_lineno(node)
1064 self.visit(node.node)
1065 for arg in node.args:
1066 self.visit(arg)
1067 if isinstance(arg, ast.Keyword):
1068 kw = kw + 1
1069 else:
1070 pos = pos + 1
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)
1082 if node.dest:
1083 self.visit(node.dest)
1084 for child in node.nodes:
1085 if node.dest:
1086 self.emit('DUP_TOP')
1087 self.visit(child)
1088 if node.dest:
1089 self.emit('ROT_TWO')
1090 self.emit('PRINT_ITEM_TO')
1091 else:
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)
1098 if node.dest:
1099 self.emit('PRINT_NEWLINE_TO')
1100 else:
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)
1118 slice = 0
1119 if node.lower:
1120 self.visit(node.lower)
1121 slice = slice | 1
1122 if node.upper:
1123 self.visit(node.upper)
1124 slice = slice | 2
1125 if aug_flag:
1126 if slice == 0:
1127 self.emit('DUP_TOP')
1128 elif slice == 3:
1129 self.emit('DUP_TOPX', 3)
1130 else:
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)
1138 else:
1139 print "weird slice", node.flags
1140 raise
1142 def visitSubscript(self, node, aug_flag=None):
1143 self.visit(node.expr)
1144 for sub in node.subs:
1145 self.visit(sub)
1146 if len(node.subs) > 1:
1147 self.emit('BUILD_TUPLE', len(node.subs))
1148 if aug_flag:
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')
1157 # binary ops
1159 def binaryOp(self, node, op):
1160 self.visit(node.left)
1161 self.visit(node.right)
1162 self.emit(op)
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')
1191 # unary ops
1193 def unaryOp(self, node, op):
1194 self.visit(node.expr)
1195 self.emit(op)
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')
1215 # bit ops
1217 def bitOp(self, nodes, op):
1218 self.visit(nodes[0])
1219 for node in nodes[1:]:
1220 self.visit(node)
1221 self.emit(op)
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:
1240 self.visit(elt)
1241 self.emit('BUILD_TUPLE', len(node.nodes))
1243 def visitList(self, node):
1244 self.set_lineno(node)
1245 for elt in node.nodes:
1246 self.visit(elt)
1247 self.emit('BUILD_LIST', len(node.nodes))
1249 def visitSliceobj(self, node):
1250 for child in node.nodes:
1251 self.visit(child)
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')
1259 self.visit(k)
1260 self.visit(v)
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__
1274 scopes = None
1276 def __init__(self, tree):
1277 self.graph = pyassem.PyFlowGraph("<module>", tree.filename)
1278 self.futures = future.find_futures(tree)
1279 self.__super_init()
1280 walk(tree, self)
1282 def get_module(self):
1283 return self
1285 class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator):
1286 __super_init = CodeGenerator.__init__
1288 scopes = None
1289 futures = ()
1291 def __init__(self, tree):
1292 self.graph = pyassem.PyFlowGraph("<expression>", tree.filename)
1293 self.__super_init()
1294 walk(tree, self)
1296 def get_module(self):
1297 return self
1299 class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator):
1301 __super_init = CodeGenerator.__init__
1303 scopes = None
1304 futures = ()
1306 def __init__(self, tree):
1307 self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename)
1308 self.__super_init()
1309 self.set_lineno(tree)
1310 walk(tree, self)
1311 self.emit('RETURN_VALUE')
1313 def get_module(self):
1314 return self
1316 def visitDiscard(self, node):
1317 # XXX Discard means it's an expression. Perhaps this is a bad
1318 # name.
1319 self.visit(node.expr)
1320 self.emit('PRINT_EXPR')
1322 class AbstractFunctionCode:
1323 optimized = 1
1324 lambdaCount = 0
1326 def __init__(self, func, scopes, isLambda, class_name, mod):
1327 self.class_name = class_name
1328 self.module = mod
1329 if isLambda:
1330 klass = FunctionCodeGenerator
1331 name = "<lambda.%d>" % klass.lambdaCount
1332 klass.lambdaCount = klass.lambdaCount + 1
1333 else:
1334 name = func.name
1336 args, hasTupleArg = generateArgList(func.argnames)
1337 self.graph = pyassem.PyFlowGraph(name, func.filename, args,
1338 optimized=1)
1339 self.isLambda = isLambda
1340 self.super_init()
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())
1347 if func.varargs:
1348 self.graph.setFlag(CO_VARARGS)
1349 if func.kwargs:
1350 self.graph.setFlag(CO_VARKEYWORDS)
1351 self.set_lineno(func)
1352 if hasTupleArg:
1353 self.generateArgUnpack(func.argnames)
1355 def get_module(self):
1356 return self.module
1358 def finish(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)):
1366 arg = args[i]
1367 if isinstance(arg, tuple):
1368 self.emit('LOAD_FAST', '.%d' % (i * 2))
1369 self.unpackSequence(arg)
1371 def unpackSequence(self, tup):
1372 if VERSION > 1:
1373 self.emit('UNPACK_SEQUENCE', len(tup))
1374 else:
1375 self.emit('UNPACK_TUPLE', len(tup))
1376 for elt in tup:
1377 if isinstance(elt, tuple):
1378 self.unpackSequence(elt)
1379 else:
1380 self._nameOp('STORE', elt)
1382 unpackTuple = unpackSequence
1384 class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
1385 CodeGenerator):
1386 super_init = CodeGenerator.__init__ # call be other init
1387 scopes = None
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,
1401 CodeGenerator):
1402 super_init = CodeGenerator.__init__ # call be other init
1403 scopes = None
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)
1422 self.super_init()
1423 lnf = walk(klass.code, self.NameFinder(), verbose=0)
1424 self.locals.push(lnf.getLocals())
1425 self.graph.setFlag(CO_NEWLOCALS)
1426 if klass.doc:
1427 self.setDocstring(klass.doc)
1429 def get_module(self):
1430 return self.module
1432 def finish(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__
1439 scopes = None
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__")
1452 if klass.doc:
1453 self.emit("LOAD_CONST", klass.doc)
1454 self.storeName('__doc__')
1456 def generateArgList(arglist):
1457 """Generate an arg list marking TupleArgs"""
1458 args = []
1459 extra = []
1460 count = 0
1461 for i in range(len(arglist)):
1462 elt = arglist[i]
1463 if isinstance(elt, str):
1464 args.append(elt)
1465 elif isinstance(elt, tuple):
1466 args.append(TupleArg(i * 2, elt))
1467 extra.extend(misc.flatten(elt))
1468 count = count + 1
1469 else:
1470 raise ValueError, "unexpect argument type:", elt
1471 return args + extra, count
1473 def findOp(node):
1474 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
1475 v = OpFinder()
1476 walk(node, v, verbose=0)
1477 return v.op
1479 class OpFinder:
1480 def __init__(self):
1481 self.op = None
1482 def visitAssName(self, node):
1483 if self.op is None:
1484 self.op = node.flags
1485 elif self.op != node.flags:
1486 raise ValueError, "mixed ops in stmt"
1487 visitAssAttr = visitAssName
1488 visitSubscript = visitAssName
1490 class Delegator:
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):
1502 self.obj = obj
1504 def __getattr__(self, attr):
1505 return getattr(self.obj, attr)
1507 class AugGetattr(Delegator):
1508 pass
1510 class AugName(Delegator):
1511 pass
1513 class AugSlice(Delegator):
1514 pass
1516 class AugSubscript(Delegator):
1517 pass
1519 wrapper = {
1520 ast.Getattr: AugGetattr,
1521 ast.Name: AugName,
1522 ast.Slice: AugSlice,
1523 ast.Subscript: AugSubscript,
1526 def wrap_aug(node):
1527 return wrapper[node.__class__](node)
1529 if __name__ == "__main__":
1530 for file in sys.argv[1:]:
1531 compileFile(file)