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