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