OILS / opy / _regtest / src / opy / compiler2 / pycodegen.py View on Github | oils.pub

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