source: python/trunk/Lib/compiler/pycodegen.py@ 388

Last change on this file since 388 was 2, checked in by Yuri Dario, 15 years ago

Initial import for vendor code.

  • Property svn:eol-style set to native
File size: 46.0 KB
Line 
1import imp
2import os
3import marshal
4import struct
5import sys
6from cStringIO import StringIO
7
8from compiler import ast, parse, walk, syntax
9from compiler import pyassem, misc, future, symbols
10from compiler.consts import SC_LOCAL, SC_GLOBAL, SC_FREE, SC_CELL
11from 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, CO_FUTURE_PRINT_FUNCTION)
14from compiler.pyassem import TupleArg
15
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?
18try:
19 VERSION = sys.version_info[0]
20except AttributeError:
21 VERSION = 1
22
23callfunc_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",
29}
30
31LOOP = 1
32EXCEPT = 2
33TRY_FINALLY = 3
34END_FINALLY = 4
35
36def 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()
49
50def 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"
54
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
66
67class AbstractCompileMode:
68
69 mode = None # defined by subclass
70
71 def __init__(self, source, filename):
72 self.source = source
73 self.filename = filename
74 self.code = None
75
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
81
82 def compile(self):
83 pass # implemented by subclass
84
85 def getCode(self):
86 return self.code
87
88class Expression(AbstractCompileMode):
89
90 mode = "eval"
91
92 def compile(self):
93 tree = self._get_tree()
94 gen = ExpressionCodeGenerator(tree)
95 self.code = gen.getCode()
96
97class Interactive(AbstractCompileMode):
98
99 mode = "single"
100
101 def compile(self):
102 tree = self._get_tree()
103 gen = InteractiveCodeGenerator(tree)
104 self.code = gen.getCode()
105
106class Module(AbstractCompileMode):
107
108 mode = "exec"
109
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()
117
118 def dump(self, f):
119 f.write(self.getPycHeader())
120 marshal.dump(self.code, f)
121
122 MAGIC = imp.get_magic()
123
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
132
133class 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)
140
141 # XXX list comprehensions and for loops
142
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
148
149 def visitDict(self, node):
150 pass
151
152 def visitGlobal(self, node):
153 for name in node.names:
154 self.globals.add(name)
155
156 def visitFunction(self, node):
157 self.names.add(node.name)
158
159 def visitLambda(self, node):
160 pass
161
162 def visitImport(self, node):
163 for name, alias in node.names:
164 self.names.add(alias or name)
165
166 def visitFrom(self, node):
167 for name, alias in node.names:
168 self.names.add(alias or name)
169
170 def visitClass(self, node):
171 self.names.add(node.name)
172
173 def visitAssName(self, node):
174 self.names.add(node.name)
175
176def is_constant_false(node):
177 if isinstance(node, ast.Const):
178 if not node.value:
179 return 1
180 return 0
181
182class CodeGenerator:
183 """Defines basic code generator for Python bytecode
184
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.
188
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.
194 """
195
196 optimized = 0 # is namespace access optimized?
197 __initialized = None
198 class_name = None # provide default for instance variable
199
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"
210
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)
221 elif feature == "print_function":
222 self.graph.setFlag(CO_FUTURE_PRINT_FUNCTION)
223
224 def initClass(self):
225 """This method is called once for each class"""
226
227 def checkClass(self):
228 """Verify that class is constructed correctly"""
229 try:
230 assert hasattr(self, 'graph')
231 assert getattr(self, 'NameFinder')
232 assert getattr(self, 'FunctionGen')
233 assert getattr(self, 'ClassGen')
234 except AssertionError, msg:
235 intro = "Bad class construction for %s" % self.__class__.__name__
236 raise AssertionError, intro
237
238 def _setupGraphDelegation(self):
239 self.emit = self.graph.emit
240 self.newBlock = self.graph.newBlock
241 self.startBlock = self.graph.startBlock
242 self.nextBlock = self.graph.nextBlock
243 self.setDocstring = self.graph.setDocstring
244
245 def getCode(self):
246 """Return a code object"""
247 return self.graph.getCode()
248
249 def mangle(self, name):
250 if self.class_name is not None:
251 return misc.mangle(name, self.class_name)
252 else:
253 return name
254
255 def parseSymbols(self, tree):
256 s = symbols.SymbolVisitor()
257 walk(tree, s)
258 return s.scopes
259
260 def get_module(self):
261 raise RuntimeError, "should be implemented by subclasses"
262
263 # Next five methods handle name access
264
265 def isLocalName(self, name):
266 return self.locals.top().has_elt(name)
267
268 def storeName(self, name):
269 self._nameOp('STORE', name)
270
271 def loadName(self, name):
272 self._nameOp('LOAD', name)
273
274 def delName(self, name):
275 self._nameOp('DELETE', name)
276
277 def _nameOp(self, prefix, name):
278 name = self.mangle(name)
279 scope = self.scope.check_name(name)
280 if scope == SC_LOCAL:
281 if not self.optimized:
282 self.emit(prefix + '_NAME', name)
283 else:
284 self.emit(prefix + '_FAST', name)
285 elif scope == SC_GLOBAL:
286 if not self.optimized:
287 self.emit(prefix + '_NAME', name)
288 else:
289 self.emit(prefix + '_GLOBAL', name)
290 elif scope == SC_FREE or scope == SC_CELL:
291 self.emit(prefix + '_DEREF', name)
292 else:
293 raise RuntimeError, "unsupported scope for var %s: %d" % \
294 (name, scope)
295
296 def _implicitNameOp(self, prefix, name):
297 """Emit name ops for names generated implicitly by for loops
298
299 The interpreter generates names that start with a period or
300 dollar sign. The symbol table ignores these names because
301 they aren't present in the program text.
302 """
303 if self.optimized:
304 self.emit(prefix + '_FAST', name)
305 else:
306 self.emit(prefix + '_NAME', name)
307
308 # The set_lineno() function and the explicit emit() calls for
309 # SET_LINENO below are only used to generate the line number table.
310 # As of Python 2.3, the interpreter does not have a SET_LINENO
311 # instruction. pyassem treats SET_LINENO opcodes as a special case.
312
313 def set_lineno(self, node, force=False):
314 """Emit SET_LINENO if necessary.
315
316 The instruction is considered necessary if the node has a
317 lineno attribute and it is different than the last lineno
318 emitted.
319
320 Returns true if SET_LINENO was emitted.
321
322 There are no rules for when an AST node should have a lineno
323 attribute. The transformer and AST code need to be reviewed
324 and a consistent policy implemented and documented. Until
325 then, this method works around missing line numbers.
326 """
327 lineno = getattr(node, 'lineno', None)
328 if lineno is not None and (lineno != self.last_lineno
329 or force):
330 self.emit('SET_LINENO', lineno)
331 self.last_lineno = lineno
332 return True
333 return False
334
335 # The first few visitor methods handle nodes that generator new
336 # code objects. They use class attributes to determine what
337 # specialized code generators to use.
338
339 NameFinder = LocalNameFinder
340 FunctionGen = None
341 ClassGen = None
342
343 def visitModule(self, node):
344 self.scopes = self.parseSymbols(node)
345 self.scope = self.scopes[node]
346 self.emit('SET_LINENO', 0)
347 if node.doc:
348 self.emit('LOAD_CONST', node.doc)
349 self.storeName('__doc__')
350 lnf = walk(node.node, self.NameFinder(), verbose=0)
351 self.locals.push(lnf.getLocals())
352 self.visit(node.node)
353 self.emit('LOAD_CONST', None)
354 self.emit('RETURN_VALUE')
355
356 def visitExpression(self, node):
357 self.set_lineno(node)
358 self.scopes = self.parseSymbols(node)
359 self.scope = self.scopes[node]
360 self.visit(node.node)
361 self.emit('RETURN_VALUE')
362
363 def visitFunction(self, node):
364 self._visitFuncOrLambda(node, isLambda=0)
365 if node.doc:
366 self.setDocstring(node.doc)
367 self.storeName(node.name)
368
369 def visitLambda(self, node):
370 self._visitFuncOrLambda(node, isLambda=1)
371
372 def _visitFuncOrLambda(self, node, isLambda=0):
373 if not isLambda and node.decorators:
374 for decorator in node.decorators.nodes:
375 self.visit(decorator)
376 ndecorators = len(node.decorators.nodes)
377 else:
378 ndecorators = 0
379
380 gen = self.FunctionGen(node, self.scopes, isLambda,
381 self.class_name, self.get_module())
382 walk(node.code, gen)
383 gen.finish()
384 self.set_lineno(node)
385 for default in node.defaults:
386 self.visit(default)
387 self._makeClosure(gen, len(node.defaults))
388 for i in range(ndecorators):
389 self.emit('CALL_FUNCTION', 1)
390
391 def visitClass(self, node):
392 gen = self.ClassGen(node, self.scopes,
393 self.get_module())
394 walk(node.code, gen)
395 gen.finish()
396 self.set_lineno(node)
397 self.emit('LOAD_CONST', node.name)
398 for base in node.bases:
399 self.visit(base)
400 self.emit('BUILD_TUPLE', len(node.bases))
401 self._makeClosure(gen, 0)
402 self.emit('CALL_FUNCTION', 0)
403 self.emit('BUILD_CLASS')
404 self.storeName(node.name)
405
406 # The rest are standard visitor methods
407
408 # The next few implement control-flow statements
409
410 def visitIf(self, node):
411 end = self.newBlock()
412 numtests = len(node.tests)
413 for i in range(numtests):
414 test, suite = node.tests[i]
415 if is_constant_false(test):
416 # XXX will need to check generator stuff here
417 continue
418 self.set_lineno(test)
419 self.visit(test)
420 nextTest = self.newBlock()
421 self.emit('JUMP_IF_FALSE', nextTest)
422 self.nextBlock()
423 self.emit('POP_TOP')
424 self.visit(suite)
425 self.emit('JUMP_FORWARD', end)
426 self.startBlock(nextTest)
427 self.emit('POP_TOP')
428 if node.else_:
429 self.visit(node.else_)
430 self.nextBlock(end)
431
432 def visitWhile(self, node):
433 self.set_lineno(node)
434
435 loop = self.newBlock()
436 else_ = self.newBlock()
437
438 after = self.newBlock()
439 self.emit('SETUP_LOOP', after)
440
441 self.nextBlock(loop)
442 self.setups.push((LOOP, loop))
443
444 self.set_lineno(node, force=True)
445 self.visit(node.test)
446 self.emit('JUMP_IF_FALSE', else_ or after)
447
448 self.nextBlock()
449 self.emit('POP_TOP')
450 self.visit(node.body)
451 self.emit('JUMP_ABSOLUTE', loop)
452
453 self.startBlock(else_) # or just the POPs if not else clause
454 self.emit('POP_TOP')
455 self.emit('POP_BLOCK')
456 self.setups.pop()
457 if node.else_:
458 self.visit(node.else_)
459 self.nextBlock(after)
460
461 def visitFor(self, node):
462 start = self.newBlock()
463 anchor = self.newBlock()
464 after = self.newBlock()
465 self.setups.push((LOOP, start))
466
467 self.set_lineno(node)
468 self.emit('SETUP_LOOP', after)
469 self.visit(node.list)
470 self.emit('GET_ITER')
471
472 self.nextBlock(start)
473 self.set_lineno(node, force=1)
474 self.emit('FOR_ITER', anchor)
475 self.visit(node.assign)
476 self.visit(node.body)
477 self.emit('JUMP_ABSOLUTE', start)
478 self.nextBlock(anchor)
479 self.emit('POP_BLOCK')
480 self.setups.pop()
481 if node.else_:
482 self.visit(node.else_)
483 self.nextBlock(after)
484
485 def visitBreak(self, node):
486 if not self.setups:
487 raise SyntaxError, "'break' outside loop (%s, %d)" % \
488 (node.filename, node.lineno)
489 self.set_lineno(node)
490 self.emit('BREAK_LOOP')
491
492 def visitContinue(self, node):
493 if not self.setups:
494 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
495 (node.filename, node.lineno)
496 kind, block = self.setups.top()
497 if kind == LOOP:
498 self.set_lineno(node)
499 self.emit('JUMP_ABSOLUTE', block)
500 self.nextBlock()
501 elif kind == EXCEPT or kind == TRY_FINALLY:
502 self.set_lineno(node)
503 # find the block that starts the loop
504 top = len(self.setups)
505 while top > 0:
506 top = top - 1
507 kind, loop_block = self.setups[top]
508 if kind == LOOP:
509 break
510 if kind != LOOP:
511 raise SyntaxError, "'continue' outside loop (%s, %d)" % \
512 (node.filename, node.lineno)
513 self.emit('CONTINUE_LOOP', loop_block)
514 self.nextBlock()
515 elif kind == END_FINALLY:
516 msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
517 raise SyntaxError, msg % (node.filename, node.lineno)
518
519 def visitTest(self, node, jump):
520 end = self.newBlock()
521 for child in node.nodes[:-1]:
522 self.visit(child)
523 self.emit(jump, end)
524 self.nextBlock()
525 self.emit('POP_TOP')
526 self.visit(node.nodes[-1])
527 self.nextBlock(end)
528
529 def visitAnd(self, node):
530 self.visitTest(node, 'JUMP_IF_FALSE')
531
532 def visitOr(self, node):
533 self.visitTest(node, 'JUMP_IF_TRUE')
534
535 def visitIfExp(self, node):
536 endblock = self.newBlock()
537 elseblock = self.newBlock()
538 self.visit(node.test)
539 self.emit('JUMP_IF_FALSE', elseblock)
540 self.emit('POP_TOP')
541 self.visit(node.then)
542 self.emit('JUMP_FORWARD', endblock)
543 self.nextBlock(elseblock)
544 self.emit('POP_TOP')
545 self.visit(node.else_)
546 self.nextBlock(endblock)
547
548 def visitCompare(self, node):
549 self.visit(node.expr)
550 cleanup = self.newBlock()
551 for op, code in node.ops[:-1]:
552 self.visit(code)
553 self.emit('DUP_TOP')
554 self.emit('ROT_THREE')
555 self.emit('COMPARE_OP', op)
556 self.emit('JUMP_IF_FALSE', cleanup)
557 self.nextBlock()
558 self.emit('POP_TOP')
559 # now do the last comparison
560 if node.ops:
561 op, code = node.ops[-1]
562 self.visit(code)
563 self.emit('COMPARE_OP', op)
564 if len(node.ops) > 1:
565 end = self.newBlock()
566 self.emit('JUMP_FORWARD', end)
567 self.startBlock(cleanup)
568 self.emit('ROT_TWO')
569 self.emit('POP_TOP')
570 self.nextBlock(end)
571
572 # list comprehensions
573 __list_count = 0
574
575 def visitListComp(self, node):
576 self.set_lineno(node)
577 # setup list
578 tmpname = "$list%d" % self.__list_count
579 self.__list_count = self.__list_count + 1
580 self.emit('BUILD_LIST', 0)
581 self.emit('DUP_TOP')
582 self._implicitNameOp('STORE', tmpname)
583
584 stack = []
585 for i, for_ in zip(range(len(node.quals)), node.quals):
586 start, anchor = self.visit(for_)
587 cont = None
588 for if_ in for_.ifs:
589 if cont is None:
590 cont = self.newBlock()
591 self.visit(if_, cont)
592 stack.insert(0, (start, cont, anchor))
593
594 self._implicitNameOp('LOAD', tmpname)
595 self.visit(node.expr)
596 self.emit('LIST_APPEND')
597
598 for start, cont, anchor in stack:
599 if cont:
600 skip_one = self.newBlock()
601 self.emit('JUMP_FORWARD', skip_one)
602 self.startBlock(cont)
603 self.emit('POP_TOP')
604 self.nextBlock(skip_one)
605 self.emit('JUMP_ABSOLUTE', start)
606 self.startBlock(anchor)
607 self._implicitNameOp('DELETE', tmpname)
608
609 self.__list_count = self.__list_count - 1
610
611 def visitListCompFor(self, node):
612 start = self.newBlock()
613 anchor = self.newBlock()
614
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)
620 self.nextBlock()
621 self.visit(node.assign)
622 return start, anchor
623
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)
628 self.newBlock()
629 self.emit('POP_TOP')
630
631 def _makeClosure(self, gen, args):
632 frees = gen.scope.get_free_vars()
633 if frees:
634 for name in frees:
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)
639 else:
640 self.emit('LOAD_CONST', gen)
641 self.emit('MAKE_FUNCTION', args)
642
643 def visitGenExpr(self, node):
644 gen = GenExprCodeGenerator(node, self.scopes, self.class_name,
645 self.get_module())
646 walk(node.code, gen)
647 gen.finish()
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)
654
655 def visitGenExprInner(self, node):
656 self.set_lineno(node)
657 # setup list
658
659 stack = []
660 for i, for_ in zip(range(len(node.quals)), node.quals):
661 start, anchor, end = self.visit(for_)
662 cont = None
663 for if_ in for_.ifs:
664 if cont is None:
665 cont = self.newBlock()
666 self.visit(if_, cont)
667 stack.insert(0, (start, cont, anchor, end))
668
669 self.visit(node.expr)
670 self.emit('YIELD_VALUE')
671 self.emit('POP_TOP')
672
673 for start, cont, anchor, end in stack:
674 if cont:
675 skip_one = self.newBlock()
676 self.emit('JUMP_FORWARD', skip_one)
677 self.startBlock(cont)
678 self.emit('POP_TOP')
679 self.nextBlock(skip_one)
680 self.emit('JUMP_ABSOLUTE', start)
681 self.startBlock(anchor)
682 self.emit('POP_BLOCK')
683 self.setups.pop()
684 self.startBlock(end)
685
686 self.emit('LOAD_CONST', None)
687
688 def visitGenExprFor(self, node):
689 start = self.newBlock()
690 anchor = self.newBlock()
691 end = self.newBlock()
692
693 self.setups.push((LOOP, start))
694 self.emit('SETUP_LOOP', end)
695
696 if node.is_outmost:
697 self.loadName('.0')
698 else:
699 self.visit(node.iter)
700 self.emit('GET_ITER')
701
702 self.nextBlock(start)
703 self.set_lineno(node, force=True)
704 self.emit('FOR_ITER', anchor)
705 self.nextBlock()
706 self.visit(node.assign)
707 return start, anchor, end
708
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)
713 self.newBlock()
714 self.emit('POP_TOP')
715
716 # exception related
717
718 def visitAssert(self, node):
719 # XXX would be interesting to implement this via a
720 # transformation of the AST before this stage
721 if __debug__:
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.
727 self.nextBlock()
728 self.visit(node.test)
729 self.emit('JUMP_IF_TRUE', end)
730 self.nextBlock()
731 self.emit('POP_TOP')
732 self.emit('LOAD_GLOBAL', 'AssertionError')
733 if node.fail:
734 self.visit(node.fail)
735 self.emit('RAISE_VARARGS', 2)
736 else:
737 self.emit('RAISE_VARARGS', 1)
738 self.nextBlock(end)
739 self.emit('POP_TOP')
740
741 def visitRaise(self, node):
742 self.set_lineno(node)
743 n = 0
744 if node.expr1:
745 self.visit(node.expr1)
746 n = n + 1
747 if node.expr2:
748 self.visit(node.expr2)
749 n = n + 1
750 if node.expr3:
751 self.visit(node.expr3)
752 n = n + 1
753 self.emit('RAISE_VARARGS', n)
754
755 def visitTryExcept(self, node):
756 body = self.newBlock()
757 handlers = self.newBlock()
758 end = self.newBlock()
759 if node.else_:
760 lElse = self.newBlock()
761 else:
762 lElse = end
763 self.set_lineno(node)
764 self.emit('SETUP_EXCEPT', handlers)
765 self.nextBlock(body)
766 self.setups.push((EXCEPT, body))
767 self.visit(node.body)
768 self.emit('POP_BLOCK')
769 self.setups.pop()
770 self.emit('JUMP_FORWARD', lElse)
771 self.startBlock(handlers)
772
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)
777 if expr:
778 self.emit('DUP_TOP')
779 self.visit(expr)
780 self.emit('COMPARE_OP', 'exception match')
781 next = self.newBlock()
782 self.emit('JUMP_IF_FALSE', next)
783 self.nextBlock()
784 self.emit('POP_TOP')
785 self.emit('POP_TOP')
786 if target:
787 self.visit(target)
788 else:
789 self.emit('POP_TOP')
790 self.emit('POP_TOP')
791 self.visit(body)
792 self.emit('JUMP_FORWARD', end)
793 if expr:
794 self.nextBlock(next)
795 else:
796 self.nextBlock()
797 if expr: # XXX
798 self.emit('POP_TOP')
799 self.emit('END_FINALLY')
800 if node.else_:
801 self.nextBlock(lElse)
802 self.visit(node.else_)
803 self.nextBlock(end)
804
805 def visitTryFinally(self, node):
806 body = self.newBlock()
807 final = self.newBlock()
808 self.set_lineno(node)
809 self.emit('SETUP_FINALLY', final)
810 self.nextBlock(body)
811 self.setups.push((TRY_FINALLY, body))
812 self.visit(node.body)
813 self.emit('POP_BLOCK')
814 self.setups.pop()
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')
820 self.setups.pop()
821
822 __with_count = 0
823
824 def visitWith(self, node):
825 body = self.newBlock()
826 final = self.newBlock()
827 valuevar = "$value%d" % self.__with_count
828 self.__with_count += 1
829 self.set_lineno(node)
830 self.visit(node.expr)
831 self.emit('DUP_TOP')
832 self.emit('LOAD_ATTR', '__exit__')
833 self.emit('ROT_TWO')
834 self.emit('LOAD_ATTR', '__enter__')
835 self.emit('CALL_FUNCTION', 0)
836 if node.vars is None:
837 self.emit('POP_TOP')
838 else:
839 self._implicitNameOp('STORE', valuevar)
840 self.emit('SETUP_FINALLY', final)
841 self.nextBlock(body)
842 self.setups.push((TRY_FINALLY, body))
843 if node.vars is not None:
844 self._implicitNameOp('LOAD', valuevar)
845 self._implicitNameOp('DELETE', valuevar)
846 self.visit(node.vars)
847 self.visit(node.body)
848 self.emit('POP_BLOCK')
849 self.setups.pop()
850 self.emit('LOAD_CONST', None)
851 self.nextBlock(final)
852 self.setups.push((END_FINALLY, final))
853 self.emit('WITH_CLEANUP')
854 self.emit('END_FINALLY')
855 self.setups.pop()
856 self.__with_count -= 1
857
858 # misc
859
860 def visitDiscard(self, node):
861 self.set_lineno(node)
862 self.visit(node.expr)
863 self.emit('POP_TOP')
864
865 def visitConst(self, node):
866 self.emit('LOAD_CONST', node.value)
867
868 def visitKeyword(self, node):
869 self.emit('LOAD_CONST', node.name)
870 self.visit(node.expr)
871
872 def visitGlobal(self, node):
873 # no code to generate
874 pass
875
876 def visitName(self, node):
877 self.set_lineno(node)
878 self.loadName(node.name)
879
880 def visitPass(self, node):
881 self.set_lineno(node)
882
883 def visitImport(self, node):
884 self.set_lineno(node)
885 level = 0 if self.graph.checkFlag(CO_FUTURE_ABSIMPORT) else -1
886 for name, alias in node.names:
887 if VERSION > 1:
888 self.emit('LOAD_CONST', level)
889 self.emit('LOAD_CONST', None)
890 self.emit('IMPORT_NAME', name)
891 mod = name.split(".")[0]
892 if alias:
893 self._resolveDots(name)
894 self.storeName(alias)
895 else:
896 self.storeName(mod)
897
898 def visitFrom(self, node):
899 self.set_lineno(node)
900 level = node.level
901 if level == 0 and not self.graph.checkFlag(CO_FUTURE_ABSIMPORT):
902 level = -1
903 fromlist = map(lambda (name, alias): name, node.names)
904 if VERSION > 1:
905 self.emit('LOAD_CONST', level)
906 self.emit('LOAD_CONST', tuple(fromlist))
907 self.emit('IMPORT_NAME', node.modname)
908 for name, alias in node.names:
909 if VERSION > 1:
910 if name == '*':
911 self.namespace = 0
912 self.emit('IMPORT_STAR')
913 # There can only be one name w/ from ... import *
914 assert len(node.names) == 1
915 return
916 else:
917 self.emit('IMPORT_FROM', name)
918 self._resolveDots(name)
919 self.storeName(alias or name)
920 else:
921 self.emit('IMPORT_FROM', name)
922 self.emit('POP_TOP')
923
924 def _resolveDots(self, name):
925 elts = name.split(".")
926 if len(elts) == 1:
927 return
928 for elt in elts[1:]:
929 self.emit('LOAD_ATTR', elt)
930
931 def visitGetattr(self, node):
932 self.visit(node.expr)
933 self.emit('LOAD_ATTR', self.mangle(node.attrname))
934
935 # next five implement assignments
936
937 def visitAssign(self, node):
938 self.set_lineno(node)
939 self.visit(node.expr)
940 dups = len(node.nodes) - 1
941 for i in range(len(node.nodes)):
942 elt = node.nodes[i]
943 if i < dups:
944 self.emit('DUP_TOP')
945 if isinstance(elt, ast.Node):
946 self.visit(elt)
947
948 def visitAssName(self, node):
949 if node.flags == 'OP_ASSIGN':
950 self.storeName(node.name)
951 elif node.flags == 'OP_DELETE':
952 self.set_lineno(node)
953 self.delName(node.name)
954 else:
955 print "oops", node.flags
956
957 def visitAssAttr(self, node):
958 self.visit(node.expr)
959 if node.flags == 'OP_ASSIGN':
960 self.emit('STORE_ATTR', self.mangle(node.attrname))
961 elif node.flags == 'OP_DELETE':
962 self.emit('DELETE_ATTR', self.mangle(node.attrname))
963 else:
964 print "warning: unexpected flags:", node.flags
965 print node
966
967 def _visitAssSequence(self, node, op='UNPACK_SEQUENCE'):
968 if findOp(node) != 'OP_DELETE':
969 self.emit(op, len(node.nodes))
970 for child in node.nodes:
971 self.visit(child)
972
973 if VERSION > 1:
974 visitAssTuple = _visitAssSequence
975 visitAssList = _visitAssSequence
976 else:
977 def visitAssTuple(self, node):
978 self._visitAssSequence(node, 'UNPACK_TUPLE')
979
980 def visitAssList(self, node):
981 self._visitAssSequence(node, 'UNPACK_LIST')
982
983 # augmented assignment
984
985 def visitAugAssign(self, node):
986 self.set_lineno(node)
987 aug_node = wrap_aug(node.node)
988 self.visit(aug_node, "load")
989 self.visit(node.expr)
990 self.emit(self._augmented_opcode[node.op])
991 self.visit(aug_node, "store")
992
993 _augmented_opcode = {
994 '+=' : 'INPLACE_ADD',
995 '-=' : 'INPLACE_SUBTRACT',
996 '*=' : 'INPLACE_MULTIPLY',
997 '/=' : 'INPLACE_DIVIDE',
998 '//=': 'INPLACE_FLOOR_DIVIDE',
999 '%=' : 'INPLACE_MODULO',
1000 '**=': 'INPLACE_POWER',
1001 '>>=': 'INPLACE_RSHIFT',
1002 '<<=': 'INPLACE_LSHIFT',
1003 '&=' : 'INPLACE_AND',
1004 '^=' : 'INPLACE_XOR',
1005 '|=' : 'INPLACE_OR',
1006 }
1007
1008 def visitAugName(self, node, mode):
1009 if mode == "load":
1010 self.loadName(node.name)
1011 elif mode == "store":
1012 self.storeName(node.name)
1013
1014 def visitAugGetattr(self, node, mode):
1015 if mode == "load":
1016 self.visit(node.expr)
1017 self.emit('DUP_TOP')
1018 self.emit('LOAD_ATTR', self.mangle(node.attrname))
1019 elif mode == "store":
1020 self.emit('ROT_TWO')
1021 self.emit('STORE_ATTR', self.mangle(node.attrname))
1022
1023 def visitAugSlice(self, node, mode):
1024 if mode == "load":
1025 self.visitSlice(node, 1)
1026 elif mode == "store":
1027 slice = 0
1028 if node.lower:
1029 slice = slice | 1
1030 if node.upper:
1031 slice = slice | 2
1032 if slice == 0:
1033 self.emit('ROT_TWO')
1034 elif slice == 3:
1035 self.emit('ROT_FOUR')
1036 else:
1037 self.emit('ROT_THREE')
1038 self.emit('STORE_SLICE+%d' % slice)
1039
1040 def visitAugSubscript(self, node, mode):
1041 if mode == "load":
1042 self.visitSubscript(node, 1)
1043 elif mode == "store":
1044 self.emit('ROT_THREE')
1045 self.emit('STORE_SUBSCR')
1046
1047 def visitExec(self, node):
1048 self.visit(node.expr)
1049 if node.locals is None:
1050 self.emit('LOAD_CONST', None)
1051 else:
1052 self.visit(node.locals)
1053 if node.globals is None:
1054 self.emit('DUP_TOP')
1055 else:
1056 self.visit(node.globals)
1057 self.emit('EXEC_STMT')
1058
1059 def visitCallFunc(self, node):
1060 pos = 0
1061 kw = 0
1062 self.set_lineno(node)
1063 self.visit(node.node)
1064 for arg in node.args:
1065 self.visit(arg)
1066 if isinstance(arg, ast.Keyword):
1067 kw = kw + 1
1068 else:
1069 pos = pos + 1
1070 if node.star_args is not None:
1071 self.visit(node.star_args)
1072 if node.dstar_args is not None:
1073 self.visit(node.dstar_args)
1074 have_star = node.star_args is not None
1075 have_dstar = node.dstar_args is not None
1076 opcode = callfunc_opcode_info[have_star, have_dstar]
1077 self.emit(opcode, kw << 8 | pos)
1078
1079 def visitPrint(self, node, newline=0):
1080 self.set_lineno(node)
1081 if node.dest:
1082 self.visit(node.dest)
1083 for child in node.nodes:
1084 if node.dest:
1085 self.emit('DUP_TOP')
1086 self.visit(child)
1087 if node.dest:
1088 self.emit('ROT_TWO')
1089 self.emit('PRINT_ITEM_TO')
1090 else:
1091 self.emit('PRINT_ITEM')
1092 if node.dest and not newline:
1093 self.emit('POP_TOP')
1094
1095 def visitPrintnl(self, node):
1096 self.visitPrint(node, newline=1)
1097 if node.dest:
1098 self.emit('PRINT_NEWLINE_TO')
1099 else:
1100 self.emit('PRINT_NEWLINE')
1101
1102 def visitReturn(self, node):
1103 self.set_lineno(node)
1104 self.visit(node.value)
1105 self.emit('RETURN_VALUE')
1106
1107 def visitYield(self, node):
1108 self.set_lineno(node)
1109 self.visit(node.value)
1110 self.emit('YIELD_VALUE')
1111
1112 # slice and subscript stuff
1113
1114 def visitSlice(self, node, aug_flag=None):
1115 # aug_flag is used by visitAugSlice
1116 self.visit(node.expr)
1117 slice = 0
1118 if node.lower:
1119 self.visit(node.lower)
1120 slice = slice | 1
1121 if node.upper:
1122 self.visit(node.upper)
1123 slice = slice | 2
1124 if aug_flag:
1125 if slice == 0:
1126 self.emit('DUP_TOP')
1127 elif slice == 3:
1128 self.emit('DUP_TOPX', 3)
1129 else:
1130 self.emit('DUP_TOPX', 2)
1131 if node.flags == 'OP_APPLY':
1132 self.emit('SLICE+%d' % slice)
1133 elif node.flags == 'OP_ASSIGN':
1134 self.emit('STORE_SLICE+%d' % slice)
1135 elif node.flags == 'OP_DELETE':
1136 self.emit('DELETE_SLICE+%d' % slice)
1137 else:
1138 print "weird slice", node.flags
1139 raise
1140
1141 def visitSubscript(self, node, aug_flag=None):
1142 self.visit(node.expr)
1143 for sub in node.subs:
1144 self.visit(sub)
1145 if len(node.subs) > 1:
1146 self.emit('BUILD_TUPLE', len(node.subs))
1147 if aug_flag:
1148 self.emit('DUP_TOPX', 2)
1149 if node.flags == 'OP_APPLY':
1150 self.emit('BINARY_SUBSCR')
1151 elif node.flags == 'OP_ASSIGN':
1152 self.emit('STORE_SUBSCR')
1153 elif node.flags == 'OP_DELETE':
1154 self.emit('DELETE_SUBSCR')
1155
1156 # binary ops
1157
1158 def binaryOp(self, node, op):
1159 self.visit(node.left)
1160 self.visit(node.right)
1161 self.emit(op)
1162
1163 def visitAdd(self, node):
1164 return self.binaryOp(node, 'BINARY_ADD')
1165
1166 def visitSub(self, node):
1167 return self.binaryOp(node, 'BINARY_SUBTRACT')
1168
1169 def visitMul(self, node):
1170 return self.binaryOp(node, 'BINARY_MULTIPLY')
1171
1172 def visitDiv(self, node):
1173 return self.binaryOp(node, self._div_op)
1174
1175 def visitFloorDiv(self, node):
1176 return self.binaryOp(node, 'BINARY_FLOOR_DIVIDE')
1177
1178 def visitMod(self, node):
1179 return self.binaryOp(node, 'BINARY_MODULO')
1180
1181 def visitPower(self, node):
1182 return self.binaryOp(node, 'BINARY_POWER')
1183
1184 def visitLeftShift(self, node):
1185 return self.binaryOp(node, 'BINARY_LSHIFT')
1186
1187 def visitRightShift(self, node):
1188 return self.binaryOp(node, 'BINARY_RSHIFT')
1189
1190 # unary ops
1191
1192 def unaryOp(self, node, op):
1193 self.visit(node.expr)
1194 self.emit(op)
1195
1196 def visitInvert(self, node):
1197 return self.unaryOp(node, 'UNARY_INVERT')
1198
1199 def visitUnarySub(self, node):
1200 return self.unaryOp(node, 'UNARY_NEGATIVE')
1201
1202 def visitUnaryAdd(self, node):
1203 return self.unaryOp(node, 'UNARY_POSITIVE')
1204
1205 def visitUnaryInvert(self, node):
1206 return self.unaryOp(node, 'UNARY_INVERT')
1207
1208 def visitNot(self, node):
1209 return self.unaryOp(node, 'UNARY_NOT')
1210
1211 def visitBackquote(self, node):
1212 return self.unaryOp(node, 'UNARY_CONVERT')
1213
1214 # bit ops
1215
1216 def bitOp(self, nodes, op):
1217 self.visit(nodes[0])
1218 for node in nodes[1:]:
1219 self.visit(node)
1220 self.emit(op)
1221
1222 def visitBitand(self, node):
1223 return self.bitOp(node.nodes, 'BINARY_AND')
1224
1225 def visitBitor(self, node):
1226 return self.bitOp(node.nodes, 'BINARY_OR')
1227
1228 def visitBitxor(self, node):
1229 return self.bitOp(node.nodes, 'BINARY_XOR')
1230
1231 # object constructors
1232
1233 def visitEllipsis(self, node):
1234 self.emit('LOAD_CONST', Ellipsis)
1235
1236 def visitTuple(self, node):
1237 self.set_lineno(node)
1238 for elt in node.nodes:
1239 self.visit(elt)
1240 self.emit('BUILD_TUPLE', len(node.nodes))
1241
1242 def visitList(self, node):
1243 self.set_lineno(node)
1244 for elt in node.nodes:
1245 self.visit(elt)
1246 self.emit('BUILD_LIST', len(node.nodes))
1247
1248 def visitSliceobj(self, node):
1249 for child in node.nodes:
1250 self.visit(child)
1251 self.emit('BUILD_SLICE', len(node.nodes))
1252
1253 def visitDict(self, node):
1254 self.set_lineno(node)
1255 self.emit('BUILD_MAP', 0)
1256 for k, v in node.items:
1257 self.emit('DUP_TOP')
1258 self.visit(k)
1259 self.visit(v)
1260 self.emit('ROT_THREE')
1261 self.emit('STORE_SUBSCR')
1262
1263class NestedScopeMixin:
1264 """Defines initClass() for nested scoping (Python 2.2-compatible)"""
1265 def initClass(self):
1266 self.__class__.NameFinder = LocalNameFinder
1267 self.__class__.FunctionGen = FunctionCodeGenerator
1268 self.__class__.ClassGen = ClassCodeGenerator
1269
1270class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator):
1271 __super_init = CodeGenerator.__init__
1272
1273 scopes = None
1274
1275 def __init__(self, tree):
1276 self.graph = pyassem.PyFlowGraph("<module>", tree.filename)
1277 self.futures = future.find_futures(tree)
1278 self.__super_init()
1279 walk(tree, self)
1280
1281 def get_module(self):
1282 return self
1283
1284class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator):
1285 __super_init = CodeGenerator.__init__
1286
1287 scopes = None
1288 futures = ()
1289
1290 def __init__(self, tree):
1291 self.graph = pyassem.PyFlowGraph("<expression>", tree.filename)
1292 self.__super_init()
1293 walk(tree, self)
1294
1295 def get_module(self):
1296 return self
1297
1298class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator):
1299
1300 __super_init = CodeGenerator.__init__
1301
1302 scopes = None
1303 futures = ()
1304
1305 def __init__(self, tree):
1306 self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename)
1307 self.__super_init()
1308 self.set_lineno(tree)
1309 walk(tree, self)
1310 self.emit('RETURN_VALUE')
1311
1312 def get_module(self):
1313 return self
1314
1315 def visitDiscard(self, node):
1316 # XXX Discard means it's an expression. Perhaps this is a bad
1317 # name.
1318 self.visit(node.expr)
1319 self.emit('PRINT_EXPR')
1320
1321class AbstractFunctionCode:
1322 optimized = 1
1323 lambdaCount = 0
1324
1325 def __init__(self, func, scopes, isLambda, class_name, mod):
1326 self.class_name = class_name
1327 self.module = mod
1328 if isLambda:
1329 klass = FunctionCodeGenerator
1330 name = "<lambda.%d>" % klass.lambdaCount
1331 klass.lambdaCount = klass.lambdaCount + 1
1332 else:
1333 name = func.name
1334
1335 args, hasTupleArg = generateArgList(func.argnames)
1336 self.graph = pyassem.PyFlowGraph(name, func.filename, args,
1337 optimized=1)
1338 self.isLambda = isLambda
1339 self.super_init()
1340
1341 if not isLambda and func.doc:
1342 self.setDocstring(func.doc)
1343
1344 lnf = walk(func.code, self.NameFinder(args), verbose=0)
1345 self.locals.push(lnf.getLocals())
1346 if func.varargs:
1347 self.graph.setFlag(CO_VARARGS)
1348 if func.kwargs:
1349 self.graph.setFlag(CO_VARKEYWORDS)
1350 self.set_lineno(func)
1351 if hasTupleArg:
1352 self.generateArgUnpack(func.argnames)
1353
1354 def get_module(self):
1355 return self.module
1356
1357 def finish(self):
1358 self.graph.startExitBlock()
1359 if not self.isLambda:
1360 self.emit('LOAD_CONST', None)
1361 self.emit('RETURN_VALUE')
1362
1363 def generateArgUnpack(self, args):
1364 for i in range(len(args)):
1365 arg = args[i]
1366 if isinstance(arg, tuple):
1367 self.emit('LOAD_FAST', '.%d' % (i * 2))
1368 self.unpackSequence(arg)
1369
1370 def unpackSequence(self, tup):
1371 if VERSION > 1:
1372 self.emit('UNPACK_SEQUENCE', len(tup))
1373 else:
1374 self.emit('UNPACK_TUPLE', len(tup))
1375 for elt in tup:
1376 if isinstance(elt, tuple):
1377 self.unpackSequence(elt)
1378 else:
1379 self._nameOp('STORE', elt)
1380
1381 unpackTuple = unpackSequence
1382
1383class FunctionCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
1384 CodeGenerator):
1385 super_init = CodeGenerator.__init__ # call be other init
1386 scopes = None
1387
1388 __super_init = AbstractFunctionCode.__init__
1389
1390 def __init__(self, func, scopes, isLambda, class_name, mod):
1391 self.scopes = scopes
1392 self.scope = scopes[func]
1393 self.__super_init(func, scopes, isLambda, class_name, mod)
1394 self.graph.setFreeVars(self.scope.get_free_vars())
1395 self.graph.setCellVars(self.scope.get_cell_vars())
1396 if self.scope.generator is not None:
1397 self.graph.setFlag(CO_GENERATOR)
1398
1399class GenExprCodeGenerator(NestedScopeMixin, AbstractFunctionCode,
1400 CodeGenerator):
1401 super_init = CodeGenerator.__init__ # call be other init
1402 scopes = None
1403
1404 __super_init = AbstractFunctionCode.__init__
1405
1406 def __init__(self, gexp, scopes, class_name, mod):
1407 self.scopes = scopes
1408 self.scope = scopes[gexp]
1409 self.__super_init(gexp, scopes, 1, class_name, mod)
1410 self.graph.setFreeVars(self.scope.get_free_vars())
1411 self.graph.setCellVars(self.scope.get_cell_vars())
1412 self.graph.setFlag(CO_GENERATOR)
1413
1414class AbstractClassCode:
1415
1416 def __init__(self, klass, scopes, module):
1417 self.class_name = klass.name
1418 self.module = module
1419 self.graph = pyassem.PyFlowGraph(klass.name, klass.filename,
1420 optimized=0, klass=1)
1421 self.super_init()
1422 lnf = walk(klass.code, self.NameFinder(), verbose=0)
1423 self.locals.push(lnf.getLocals())
1424 self.graph.setFlag(CO_NEWLOCALS)
1425 if klass.doc:
1426 self.setDocstring(klass.doc)
1427
1428 def get_module(self):
1429 return self.module
1430
1431 def finish(self):
1432 self.graph.startExitBlock()
1433 self.emit('LOAD_LOCALS')
1434 self.emit('RETURN_VALUE')
1435
1436class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator):
1437 super_init = CodeGenerator.__init__
1438 scopes = None
1439
1440 __super_init = AbstractClassCode.__init__
1441
1442 def __init__(self, klass, scopes, module):
1443 self.scopes = scopes
1444 self.scope = scopes[klass]
1445 self.__super_init(klass, scopes, module)
1446 self.graph.setFreeVars(self.scope.get_free_vars())
1447 self.graph.setCellVars(self.scope.get_cell_vars())
1448 self.set_lineno(klass)
1449 self.emit("LOAD_GLOBAL", "__name__")
1450 self.storeName("__module__")
1451 if klass.doc:
1452 self.emit("LOAD_CONST", klass.doc)
1453 self.storeName('__doc__')
1454
1455def generateArgList(arglist):
1456 """Generate an arg list marking TupleArgs"""
1457 args = []
1458 extra = []
1459 count = 0
1460 for i in range(len(arglist)):
1461 elt = arglist[i]
1462 if isinstance(elt, str):
1463 args.append(elt)
1464 elif isinstance(elt, tuple):
1465 args.append(TupleArg(i * 2, elt))
1466 extra.extend(misc.flatten(elt))
1467 count = count + 1
1468 else:
1469 raise ValueError, "unexpect argument type:", elt
1470 return args + extra, count
1471
1472def findOp(node):
1473 """Find the op (DELETE, LOAD, STORE) in an AssTuple tree"""
1474 v = OpFinder()
1475 walk(node, v, verbose=0)
1476 return v.op
1477
1478class OpFinder:
1479 def __init__(self):
1480 self.op = None
1481 def visitAssName(self, node):
1482 if self.op is None:
1483 self.op = node.flags
1484 elif self.op != node.flags:
1485 raise ValueError, "mixed ops in stmt"
1486 visitAssAttr = visitAssName
1487 visitSubscript = visitAssName
1488
1489class Delegator:
1490 """Base class to support delegation for augmented assignment nodes
1491
1492 To generator code for augmented assignments, we use the following
1493 wrapper classes. In visitAugAssign, the left-hand expression node
1494 is visited twice. The first time the visit uses the normal method
1495 for that node . The second time the visit uses a different method
1496 that generates the appropriate code to perform the assignment.
1497 These delegator classes wrap the original AST nodes in order to
1498 support the variant visit methods.
1499 """
1500 def __init__(self, obj):
1501 self.obj = obj
1502
1503 def __getattr__(self, attr):
1504 return getattr(self.obj, attr)
1505
1506class AugGetattr(Delegator):
1507 pass
1508
1509class AugName(Delegator):
1510 pass
1511
1512class AugSlice(Delegator):
1513 pass
1514
1515class AugSubscript(Delegator):
1516 pass
1517
1518wrapper = {
1519 ast.Getattr: AugGetattr,
1520 ast.Name: AugName,
1521 ast.Slice: AugSlice,
1522 ast.Subscript: AugSubscript,
1523 }
1524
1525def wrap_aug(node):
1526 return wrapper[node.__class__](node)
1527
1528if __name__ == "__main__":
1529 for file in sys.argv[1:]:
1530 compileFile(file)
Note: See TracBrowser for help on using the repository browser.