1 #!/usr/bin/env python 2 3 """ 4 Simplify AST structures for easier type propagation and analysis. The code in 5 this module processes AST trees originating from the compiler module and 6 produces a result tree consisting of instruction-oriented program nodes. 7 8 Some of the simplified nodes will be altered by the fixnames module, principally 9 LoadName and StoreName nodes which, in the case of globals and builtins, will be 10 changed to LoadAttr and StoreAttr respectively. 11 12 -------- 13 14 Copyright (C) 2006, 2007 Paul Boddie <paul@boddie.org.uk> 15 16 This software is free software; you can redistribute it and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version. 20 21 This software is distributed in the hope that it will be useful, 22 but WITHOUT ANY WARRANTY; without even the implied warranty of 23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 GNU General Public License for more details. 25 26 You should have received a copy of the GNU General Public 27 License along with this library; see the file LICENCE.txt 28 If not, write to the Free Software Foundation, Inc., 29 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 30 31 -------- 32 33 To use this module, the easiest approach is to use the simplify function: 34 35 simplify(filename) 36 37 The more complicated approach involves first instantiating a Simplifier object: 38 39 simplifier = Simplifier() 40 41 Then, applying the simplifier to an AST tree: 42 43 module = compiler.parseFile(filename) 44 simplifier.process(module) 45 """ 46 47 from simplify.simplified import * 48 import compiler.ast 49 import os 50 51 class Simplifier(Visitor): 52 53 """ 54 A simplifying visitor for AST nodes. 55 56 Covered: Add, And, Assert, AssAttr, AssList, AssName, AssTuple, Assign, 57 AugAssign, Bitand, Break, CallFunc, Class, Compare, Const, 58 Continue, Dict, Discard, Div, FloorDiv, For, From, Function, 59 Getattr, Global, If, Import, Invert, Keyword, Lambda, List, 60 ListComp, ListCompFor, ListCompIf, Mod, Module, Mul, Name, Not, Or, 61 Pass, Power, Print, Printnl, Raise, Return, Slice, Sliceobj, Stmt, 62 Sub, Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd, 63 UnarySub. 64 65 Missing: Backquote, Bitor, Bitxor, Decorators, Ellipsis, Exec, LeftShift, 66 RightShift, Yield. 67 """ 68 69 def __init__(self, builtins=0): 70 71 """ 72 Initialise the simplifier with the optional 'builtins' parameter 73 indicating whether the module contains the built-in classes and 74 functions. 75 """ 76 77 Visitor.__init__(self) 78 self.subprograms = [] # Subprograms outside the tree. 79 self.structures = [] # Structures/classes. 80 self.constants = {} # Constants. 81 self.current_subprograms = [] # Current subprograms being processed. 82 self.current_structures = [] # Current structures being processed. 83 self.within_class = 0 # Whether a class is being defined. 84 self.builtins = builtins # Whether the builtins are being processed. 85 86 # Convenience attributes. 87 88 self.subnames = {} 89 90 # For compiler package mechanisms. 91 92 self.visitor = self 93 94 def process(self, module, name): 95 96 """ 97 Process the 'module' having the given 'name'. Return the simplified node 98 representing the 'module'. 99 """ 100 101 result = self.dispatch(module, name) 102 result.simplifier = self 103 return result 104 105 def dispatch_or_none(self, node, *args): 106 107 """ 108 Dispatch to a handler for 'node', returning the result, or if 'node' is 109 None then return a node which loads None in the simplified program. 110 """ 111 112 if node is not None: 113 return self.dispatch(node, *args) 114 else: 115 return LoadName(node, name="None") 116 117 # Top-level transformation. 118 119 def visitModule(self, module, name=None): 120 121 """ 122 Process the given 'module', producing a Module object which contains the 123 resulting program nodes. If the optional 'name' is provided, the 'name' 124 attribute is set on the Module object using a value other than None. 125 """ 126 127 result = self.module = Module(module, 1, name=name) 128 result.code = self.dispatch(module.node) 129 return result 130 131 # Node transformations. 132 133 def visitAdd(self, add): 134 return self._visitBinary(add, "__add__", "__radd__") 135 136 def visitAnd(self, and_): 137 138 """ 139 Make a subprogram for the 'and_' node and record its contents inside the 140 subprogram. Convert... 141 142 And (test) 143 (test) 144 ... 145 146 ...to: 147 148 Subprogram -> Conditional (test) -> ReturnFromBlock ... 149 (else) -> Conditional (test) -> ReturnFromBlock ... 150 (else) -> ... 151 """ 152 153 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 154 self.current_subprograms.append(subprogram) 155 156 # In the subprogram, make instructions which store each operand, test 157 # for each operand's truth status, and if appropriate return from the 158 # subprogram with the value of the operand. 159 160 last = and_.nodes[-1] 161 results = nodes = [] 162 163 for node in and_.nodes: 164 expr = self.dispatch(node) 165 166 # Return from the subprogram where the test is not satisfied. 167 168 if node is not last: 169 nodes += [ 170 StoreTemp(expr=expr), 171 Conditional( 172 test=self._visitNot(LoadTemp()), 173 body=[ 174 ReturnFromBlock( 175 expr=LoadTemp() 176 ) 177 ], 178 else_=[ 179 ReleaseTemp() 180 # Subsequent operations go here! 181 ] 182 ) 183 ] 184 185 # Put subsequent operations in the else section of this conditional. 186 187 nodes = nodes[-1].else_ 188 189 # For the last operation, return the result. 190 191 else: 192 nodes.append(ReturnFromBlock(expr=expr)) 193 194 # Finish the subprogram definition. 195 196 subprogram.code = results 197 198 self.current_subprograms.pop() 199 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 200 201 # Make an invocation of the subprogram. 202 203 result = InvokeRef(and_, 1, produces_result=1, ref=subprogram) 204 return result 205 206 def visitAssert(self, assert_): 207 if assert_.fail: 208 fail_args = [self.dispatch(assert_.fail)] 209 else: 210 fail_args = [] 211 212 result = Conditional(assert_, 1, 213 test=self.dispatch(assert_.test), 214 body=[], 215 else_=[ 216 Raise(assert_, 217 expr=InvokeFunction(assert_, 218 expr=LoadName(name="AssertionError"), 219 args=fail_args, 220 star=None, 221 dstar=None 222 ) 223 ) 224 ] 225 ) 226 227 # Make nice annotations for the viewer. 228 229 assert_._raises = result.else_[0].expr 230 return result 231 232 # Assignments. 233 234 def visitAssAttr(self, assattr, element=None): 235 expr = self._visitAssNameOrAttr(assattr, element) 236 lvalue = self.dispatch(assattr.expr) 237 result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr) 238 return result 239 240 def visitAssign(self, assign): 241 result = Assign(assign, 1) 242 store = StoreTemp(expr=self.dispatch(assign.expr)) 243 release = ReleaseTemp() 244 result.code = [store] + self.dispatches(assign.nodes) + [release] 245 return result 246 247 def visitAssList(self, asslist, element=None): 248 if element is None: 249 expr = LoadTemp() 250 else: 251 expr = LoadAttr(expr=LoadTemp(), name=("__value__%d" % element)) 252 result = Assign(asslist, 1) 253 result.code = [StoreTemp(expr=expr)] 254 for i, node in enumerate(asslist.nodes): 255 result.code.append(self.dispatch(node, i)) 256 result.code.append(ReleaseTemp()) 257 return result 258 259 visitAssTuple = visitAssList 260 261 def _visitAssNameOrAttr(self, node, element=None): 262 263 "Produce a suitable value for assignment from the given 'node'." 264 265 if element is None: 266 return LoadTemp() 267 else: 268 return LoadAttr(expr=LoadTemp(), name=("__value__%d" % element)) 269 270 def visitAssName(self, assname, element=None): 271 272 """ 273 Visit the 'assname' node, producing an appropriate StoreName simplified 274 node which if 'element' is None will store a temporary value; otherwise, 275 a special attribute will be obtained from the current temporary value in 276 order to attempt to support optimised sequence assignment. 277 """ 278 279 expr = self._visitAssNameOrAttr(assname, element) 280 result = StoreName(assname, 1, name=assname.name, expr=expr) 281 return result 282 283 augassign_methods = { 284 "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__", 285 "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__", 286 "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__" 287 } 288 289 def visitAugAssign(self, augassign): 290 291 """ 292 Convert the augmented assignment... 293 294 AugAssign (node) -> Name | Getattr | Slice | Subscript 295 (op) 296 (expr) 297 298 ...to: 299 300 Assign (code) -> StoreTemp (expr) -> InvokeFunction (expr) -> LoadAttr (expr) -> <name> 301 (name) -> <op> 302 StoreName (name) -> <name> 303 (expr) -> LoadTemp 304 ReleaseTemp 305 """ 306 307 result = Assign(augassign, 1) 308 expr = self.dispatch(augassign.expr) 309 310 # Simple augmented assignment: name += expr 311 312 if isinstance(augassign.node, compiler.ast.Name): 313 result.code = [ 314 StoreTemp( 315 expr=InvokeFunction( # referenced below 316 augassign, 317 args=[expr], 318 star=None, 319 dstar=None, 320 expr=LoadAttr( 321 expr=self.dispatch(augassign.node), 322 name=self.augassign_methods[augassign.op] 323 ) 324 ) 325 ), 326 StoreName( 327 expr=LoadTemp(), 328 name=augassign.node.name), 329 ReleaseTemp() 330 ] 331 332 # Make nice annotations for the viewer. 333 334 augassign._op_call = result.code[0].expr 335 336 # Complicated augmented assignment: lvalue.attr += expr 337 338 elif isinstance(augassign.node, compiler.ast.Getattr): 339 340 # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr)) 341 342 result.code = [ 343 StoreTemp( 344 index="expr", 345 expr=self.dispatch(augassign.node.expr) 346 ), 347 StoreTemp( 348 expr=InvokeFunction( # referenced below 349 augassign, 350 args=[expr], star=None, dstar=None, 351 expr=LoadAttr( 352 expr=LoadAttr(augassign.node, 1, 353 expr=LoadTemp(index="expr"), 354 name=augassign.node.attrname 355 ), 356 name=self.augassign_methods[augassign.op] 357 ) 358 ) 359 ), 360 StoreAttr( 361 expr=LoadTemp(), 362 lvalue=LoadTemp(index="expr"), 363 name=augassign.node.attrname 364 ), 365 ReleaseTemp(index="expr"), 366 ReleaseTemp() 367 ] 368 369 # Make nice annotations for the viewer. 370 371 augassign._op_call = result.code[1].expr 372 373 # Complicated augassign using slices: lvalue[lower:upper] += expr 374 375 elif isinstance(augassign.node, compiler.ast.Slice): 376 377 # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr)) 378 379 result.code = [ 380 StoreTemp( 381 index="expr", 382 expr=self.dispatch(augassign.node.expr) 383 ), 384 StoreTemp( 385 index="lower", 386 expr=self.dispatch_or_none(augassign.node.lower) 387 ), 388 StoreTemp( 389 index="upper", 390 expr=self.dispatch_or_none(augassign.node.upper) 391 ), 392 StoreTemp( 393 expr=InvokeFunction( # referenced below 394 augassign, 395 args=[expr], star=None, dstar=None, 396 expr=LoadAttr( 397 expr=self._visitSlice( 398 augassign.node, 399 LoadTemp(index="expr"), 400 LoadTemp(index="lower"), 401 LoadTemp(index="upper"), 402 "OP_APPLY"), 403 name=self.augassign_methods[augassign.op] 404 ) 405 ) 406 ), 407 self._visitSlice( 408 augassign.node, 409 LoadTemp(index="expr"), 410 LoadTemp(index="lower"), 411 LoadTemp(index="upper"), 412 "OP_ASSIGN", 413 LoadTemp() 414 ), 415 ReleaseTemp(index="expr"), 416 ReleaseTemp(index="lower"), 417 ReleaseTemp(index="upper"), 418 ReleaseTemp() 419 ] 420 421 # Make nice annotations for the viewer. 422 423 augassign._op_call = result.code[3].expr 424 425 # Complicated augassign using subscripts: lvalue[subs] += expr 426 427 elif isinstance(augassign.node, compiler.ast.Subscript): 428 429 # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr)) 430 431 result.code = [ 432 StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)), 433 StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)), 434 StoreTemp( 435 expr=InvokeFunction( # referenced below 436 augassign, 437 args=[expr], star=None, dstar=None, 438 expr=LoadAttr( 439 expr=self._visitSubscript( 440 augassign.node, 441 LoadTemp(index="expr"), 442 LoadTemp(index="subs"), 443 "OP_APPLY" 444 ), 445 name=self.augassign_methods[augassign.op] 446 ) 447 ) 448 ), 449 self._visitSubscript( 450 augassign.node, 451 LoadTemp(index="expr"), 452 LoadTemp(index="subs"), 453 "OP_ASSIGN", 454 LoadTemp() 455 ), 456 ReleaseTemp(index="expr"), 457 ReleaseTemp(index="subs"), 458 ReleaseTemp() 459 ] 460 461 # Make nice annotations for the viewer. 462 463 augassign._op_call = result.code[2].expr 464 465 else: 466 raise NotImplementedError, augassign.node.__class__ 467 468 return result 469 470 def visitBitand(self, bitand): 471 472 """ 473 Make a subprogram for the 'bitand' node and record its contents inside the 474 subprogram. Convert... 475 476 Bitand (node) 477 (node) 478 ... 479 480 ...to: 481 482 Subprogram -> Conditional (test) -> ReturnFromBlock ... 483 (else) -> Conditional (test) -> ReturnFromBlock ... 484 (else) -> ... 485 """ 486 487 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 488 self.current_subprograms.append(subprogram) 489 490 # In the subprogram, make instructions which store each operand, test 491 # for each operand's truth status, and if appropriate return from the 492 # subprogram with the value of the operand. 493 494 last = bitand.nodes[-1] 495 results = nodes = [] 496 497 # Start by storing the first operand. 498 499 nodes += [ 500 StoreTemp(expr=self.dispatch(bitand.nodes[0])) 501 ] 502 503 # For viewing purposes, record invocations on the AST node. 504 505 bitand._ops = [] 506 507 for node in bitand.nodes[1:]: 508 509 # Make a new AST-style node to wrap the operation program nodes. 510 511 new_op = Op("&", node) 512 bitand._ops.append(new_op) 513 514 # Generate the operation involving the previous result and the 515 # current operand. 516 517 expr = self._visitBinaryOp(new_op, LoadTemp(), self.dispatch(node), "__and__", "__rand__") 518 519 # Return from the subprogram where the test is not satisfied. 520 521 if node is not last: 522 nodes += [ 523 StoreTemp(expr=expr), 524 Conditional( 525 test=self._visitNot(LoadTemp()), 526 body=[ 527 ReturnFromBlock( 528 expr=LoadTemp() 529 ) 530 ], 531 else_=[ 532 # Subsequent operations go here! 533 ] 534 ) 535 ] 536 537 # Put subsequent operations in the else section of this conditional. 538 539 nodes = nodes[-1].else_ 540 541 # For the last operation, return the result. 542 543 else: 544 nodes.append(ReturnFromBlock(expr=expr)) 545 546 # Finish the subprogram definition. 547 548 subprogram.code = results 549 550 self.current_subprograms.pop() 551 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 552 553 # Make an invocation of the subprogram. 554 555 result = InvokeRef(bitand, 1, produces_result=1, ref=subprogram) 556 return result 557 558 def visitBreak(self, break_): 559 result = ReturnFromBlock(break_, 1) 560 return result 561 562 def visitCallFunc(self, callfunc): 563 result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args)) 564 if callfunc.star_args is not None: 565 result.star = self.dispatch(callfunc.star_args) 566 if callfunc.dstar_args is not None: 567 result.dstar = self.dispatch(callfunc.dstar_args) 568 result.expr = self.dispatch(callfunc.node) 569 return result 570 571 def visitClass(self, class_): 572 573 # Add "object" if the class is not "object" and has an empty bases list. 574 575 if class_.name != "object" and not class_.bases: 576 bases = [compiler.ast.Name("object")] 577 else: 578 bases = class_.bases 579 580 structure = get_class()(name=class_.name, module=self.module, bases=self.dispatches(bases)) 581 self.structures.append(structure) 582 within_class = self.within_class 583 self.within_class = 1 584 585 # Make a subprogram which initialises the class structure. 586 587 subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None) 588 self.current_subprograms.append(subprogram) 589 self.current_structures.append(structure) # mostly for name construction 590 591 # The class is initialised using the code found inside. 592 593 subprogram.code = self.dispatch(class_.code) + [ReturnFromBlock()] 594 595 self.within_class = within_class 596 self.current_structures.pop() 597 self.current_subprograms.pop() 598 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 599 600 # Make a definition of the class associating it with a name. 601 602 result = Assign( 603 code=[ 604 StoreName(class_, 1, # defines the class 605 name=class_.name, 606 expr=LoadRef(ref=structure) 607 ), 608 InvokeRef( 609 class_, 610 share_locals=0, # override the local sharing usually in InvokeRef 611 ref=subprogram 612 ) 613 ] 614 ) 615 return result 616 617 comparison_methods = { 618 "==" : ("__eq__", "__ne__"), 619 "!=" : ("__ne__", "__eq__"), 620 "<" : ("__lt__", "__gt__"), 621 "<=" : ("__le__", "__ge__"), 622 ">=" : ("__ge__", "__le__"), 623 ">" : ("__gt__", "__lt__"), 624 "is" : None, 625 "is not" : None, 626 "in" : None, 627 "not in" : None 628 } 629 630 def visitCompare(self, compare): 631 632 """ 633 Make a subprogram for the 'compare' node and record its contents inside 634 the subprogram. Convert... 635 636 Compare (expr) 637 (name/node) 638 ... 639 640 ...to: 641 642 InvokeRef -> Subprogram -> Conditional (test) -> (body) 643 (else) -> Conditional (test) -> (body) 644 (else) -> ... 645 """ 646 647 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 648 self.current_subprograms.append(subprogram) 649 650 # In the subprogram, make instructions which invoke a method on the 651 # first operand of each operand pair and, if appropriate, return with 652 # the value from that method. 653 654 last = compare.ops[-1] 655 previous = self.dispatch(compare.expr) 656 results = nodes = [] 657 658 # For viewing purposes, record invocations on the AST node. 659 660 compare._ops = [] 661 662 for op in compare.ops: 663 op_name, node = op 664 665 # Make a new AST-style node to wrap the operation program nodes. 666 667 new_op = Op(op_name, node) 668 compare._ops.append(new_op) 669 670 expr = self.dispatch(node) 671 672 # Identify the operation and produce the appropriate method call. 673 674 method_names = self.comparison_methods[op_name] 675 if method_names: 676 first_name, alternative_name = method_names 677 invocation = self._visitBinaryCompareOp(new_op, previous, expr, first_name, alternative_name) 678 679 elif op_name == "is": 680 invocation = InvokeFunction( 681 new_op, 1, 682 expr=LoadName(name="__is__"), 683 args=[previous, expr], 684 star=None, 685 dstar=None) 686 687 elif op_name == "is not": 688 invocation = Not( 689 new_op, 1, 690 expr=InvokeFunction( 691 new_op, 692 expr=LoadName(name="__is__"), 693 args=[previous, expr], 694 star=None, 695 dstar=None) 696 ) 697 698 elif op_name == "in": 699 invocation = InvokeFunction( 700 new_op, 1, 701 expr=LoadAttr( 702 expr=previous, 703 name="__contains__" 704 ), 705 args=[expr], 706 star=None, 707 dstar=None) 708 709 elif op_name == "not in": 710 invocation = Not( 711 new_op, 1, 712 expr=InvokeFunction( 713 new_op, 714 expr=LoadAttr( 715 expr=previous, 716 name="__contains__" 717 ), 718 args=[expr], 719 star=None, 720 dstar=None) 721 ) 722 723 else: 724 raise NotImplementedError, op_name 725 726 nodes.append(StoreTemp(expr=invocation)) 727 728 # Return from the subprogram where the test is not satisfied. 729 730 if op is not last: 731 nodes.append( 732 Conditional( 733 test=self._visitNot(LoadTemp()), 734 body=[ 735 ReturnFromBlock(expr=LoadTemp()) 736 ], 737 else_=[ 738 ReleaseTemp() 739 # Subsequent operations go here! 740 ] 741 ) 742 ) 743 744 # Put subsequent operations in the else section of this conditional. 745 746 nodes = nodes[-1].else_ 747 748 # For the last operation, return the result. 749 750 else: 751 nodes.append( 752 ReturnFromBlock(expr=LoadTemp(release=1)) 753 ) 754 755 previous = expr 756 757 # Finish the subprogram definition. 758 759 subprogram.code = results 760 761 self.current_subprograms.pop() 762 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 763 764 # Make an invocation of the subprogram. 765 766 result = InvokeRef(compare, 1, produces_result=1, ref=subprogram) 767 return result 768 769 def visitConst(self, const): 770 key = "%s-%s" % (const.value.__class__.__name__, const.value) 771 if not self.constants.has_key(key): 772 self.constants[key] = Constant(name=repr(const.value), value=const.value) 773 result = InvokeFunction(const, 1, expr=LoadName(name=self.constants[key].typename)) 774 return result 775 776 def visitContinue(self, continue_): 777 result = InvokeRef(continue_, 1, ref=self.current_subprograms[-1]) 778 return result 779 780 def visitDict(self, dict): 781 result = InvokeFunction(dict, 1, expr=LoadName(name="dict")) 782 args = [] 783 for key, value in dict.items: 784 tuple = InvokeFunction(dict, expr=LoadName(name="tuple")) 785 tuple.set_args([self.dispatch(key), self.dispatch(value)]) 786 args.append(tuple) 787 result.set_args(args) 788 return result 789 790 def visitDiscard(self, discard): 791 return self.dispatch(discard.expr) 792 793 def visitDiv(self, div): 794 return self._visitBinary(div, "__div__", "__rdiv__") 795 796 def visitFloorDiv(self, floordiv): 797 return self._visitBinary(floordiv, "__floordiv__", "__rfloordiv__") 798 799 def visitFor(self, for_): 800 801 """ 802 Make a subprogram for the 'for_' node and record its contents inside the 803 subprogram. Convert... 804 805 For (assign) 806 (body) 807 (else) 808 809 ...to: 810 811 Assign (assign #1) 812 Invoke -> Subprogram -> Try (body) -> (assign #2) 813 (body) 814 Invoke subprogram 815 (handler) -> ... 816 (else) -> ... 817 """ 818 819 return self._visitFor(for_, self.dispatches(for_.body), for_.else_) 820 821 def _visitFor(self, node, body_stmt, else_=None): 822 823 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[]) 824 self.current_subprograms.append(subprogram) 825 826 # Always return from conditional sections/subprograms. 827 828 if else_ is not None: 829 else_stmt = self.dispatch(else_) + [ReturnFromBlock()] 830 else: 831 else_stmt = [ReturnFromBlock()] 832 833 # Wrap the assignment in a try...except statement. 834 # Inside the body, add a recursive invocation to the subprogram. 835 836 subprogram.code = [ 837 Try( 838 body=[ 839 Assign( 840 code=[ 841 StoreTemp( 842 expr=InvokeFunction(node, 843 expr=LoadAttr( 844 expr=LoadTemp(), 845 name="next" 846 ) 847 ) 848 ), 849 self.dispatch(node.assign), 850 ReleaseTemp() 851 ]) 852 ] + body_stmt + [ 853 InvokeRef( 854 node, 855 ref=subprogram 856 ) 857 ], 858 handler=[ 859 Conditional( 860 test=InvokeFunction( 861 node, 862 expr=LoadName(name="isinstance"), 863 args=[LoadExc(), LoadName(name="StopIteration")], 864 star=None, 865 dstar=None), 866 body=else_stmt, 867 else_=[ 868 Raise( 869 expr=LoadExc() 870 ) 871 ] 872 ) 873 ], 874 else_=[], 875 finally_=[] 876 ), 877 ReturnFromBlock() 878 ] 879 880 # Finish the subprogram definition. 881 882 self.current_subprograms.pop() 883 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 884 885 # Obtain an iterator for the sequence involved. 886 # Then, make an invocation of the subprogram. 887 888 result = Assign(node, 1, 889 code=[ 890 StoreTemp( 891 expr=InvokeFunction( 892 node, 893 expr=LoadAttr( 894 name="__iter__", 895 expr=self.dispatch(node.list) 896 ) 897 ) 898 ), 899 InvokeRef(node, ref=subprogram), 900 ReleaseTemp() 901 ] 902 ) 903 904 # Make nice annotations for the viewer. 905 906 node._iter_call = result.code[0].expr 907 node._next_call = subprogram.code[0].body[0].code[0].expr 908 909 return result 910 911 def visitFrom(self, from_): 912 result = Assign(from_, 1) 913 code = [] 914 _names = [] 915 code.append( 916 StoreTemp( 917 expr=Import(name=from_.modname, alias=1) 918 ) 919 ) 920 from_._modname = code[-1].expr 921 for name, alias in from_.names: 922 code.append( 923 StoreName( 924 expr=LoadAttr( 925 expr=LoadTemp(), 926 name=name), 927 name=(alias or name) 928 ) 929 ) 930 _names.append(code[-1].expr) 931 code.append(ReleaseTemp()) 932 result.code = code 933 from_._names = _names 934 return result 935 936 def _visitFunction(self, function, subprogram): 937 938 """ 939 A common function generator which transforms the given 'function' node 940 and initialises the given 'subprogram' appropriately. 941 """ 942 943 # Discover star and dstar parameters. 944 945 if function.flags & 4 != 0: 946 has_star = 1 947 else: 948 has_star = 0 949 if function.flags & 8 != 0: 950 has_dstar = 1 951 else: 952 has_dstar = 0 953 954 # Discover the number of defaults and positional parameters. 955 956 ndefaults = len(function.defaults) 957 npositional = len(function.argnames) - has_star - has_dstar 958 959 # Produce star and dstar parameters with appropriate defaults. 960 961 if has_star: 962 star = ( 963 function.argnames[npositional], 964 self.dispatch(compiler.ast.List([])) 965 ) 966 else: 967 star = None 968 if has_dstar: 969 dstar = ( 970 function.argnames[npositional + has_star], 971 self.dispatch(compiler.ast.Dict([])) 972 ) 973 else: 974 dstar = None 975 976 params = [] 977 for i in range(0, npositional - ndefaults): 978 params.append((function.argnames[i], None)) 979 980 # Process defaults. 981 982 for i in range(0, ndefaults): 983 default = function.defaults[i] 984 if default is not None: 985 params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) 986 else: 987 params.append((function.argnames[npositional - ndefaults + i], None)) 988 989 subprogram.params = params 990 subprogram.star = star 991 subprogram.dstar = dstar 992 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 993 994 def visitFunction(self, function): 995 996 """ 997 Make a subprogram for the 'function' and record it outside the main 998 tree. Produce something like the following: 999 1000 StoreName (name) 1001 (expr) -> LoadRef (ref) -> Subprogram (params) 1002 (star) 1003 (dstar) 1004 """ 1005 1006 subprogram = Subprogram(function, name=function.name, module=self.module, structures=self.current_structures[:], 1007 internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class, original_def=function) 1008 1009 # Make nice annotations for the viewer. 1010 1011 function._subprogram = subprogram 1012 1013 self.current_subprograms.append(subprogram) 1014 within_class = self.within_class 1015 self.within_class = 0 1016 1017 subprogram.code = self.dispatch(function.code) + [ReturnFromFunction()] 1018 1019 self.within_class = within_class 1020 self.current_subprograms.pop() 1021 self._visitFunction(function, subprogram) 1022 1023 # Make a definition of the function associating it with a name. 1024 1025 result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram)) 1026 return result 1027 1028 def visitGetattr(self, getattr): 1029 result = LoadAttr(getattr, 1, 1030 name=getattr.attrname, 1031 expr=self.dispatch(getattr.expr) 1032 ) 1033 return result 1034 1035 def visitGlobal(self, global_): 1036 result = Global(global_, 1, 1037 names=global_.names 1038 ) 1039 return result 1040 1041 def visitIf(self, if_): 1042 1043 """ 1044 Make conditionals for each test from an 'if_' AST node, adding the body 1045 and putting each subsequent test as part of the conditional's else 1046 section. 1047 1048 Convert... 1049 1050 If (test/body) 1051 (test/body) 1052 ... 1053 (else/body) 1054 1055 ...to: 1056 1057 Conditional (test) -> (body) 1058 (else) -> Conditional (test) -> (body) 1059 (else) -> ... 1060 """ 1061 1062 1063 results = nodes = [] 1064 1065 # Produce something like... 1066 # expr.__bool__() ? body 1067 1068 first = 1 1069 for compare, stmt in if_.tests: 1070 1071 # Set the first as the defining node. 1072 1073 test = Conditional(if_, first, 1074 test=InvokeFunction( 1075 if_, 1076 expr=LoadAttr( 1077 expr=self.dispatch(compare), 1078 name="__bool__" 1079 ), 1080 ) 1081 ) 1082 test.body = self.dispatch(stmt) 1083 nodes.append(test) 1084 nodes = test.else_ = [] 1085 first = 0 1086 1087 # Add the compound statement from any else clause to the end. 1088 1089 if if_.else_ is not None: 1090 nodes += self.dispatch(if_.else_) 1091 1092 result = results[0] 1093 return result 1094 1095 def visitImport(self, import_): 1096 result = Assign(import_, 1) 1097 code = [] 1098 _names = [] 1099 for path, alias in import_.names: 1100 importer = Import(name=path, alias=alias) 1101 top = alias or path.split(".")[0] 1102 code.append(StoreName(expr=importer, name=top)) 1103 _names.append(code[-1].expr) 1104 result.code = code 1105 import_._names = _names 1106 return result 1107 1108 def visitInvert(self, invert): 1109 return self._visitUnary(invert, "__invert__") 1110 1111 def visitKeyword(self, keyword): 1112 result = Keyword(keyword, 1, 1113 name=keyword.name, 1114 expr=self.dispatch(keyword.expr) 1115 ) 1116 return result 1117 1118 def visitLambda(self, lambda_): 1119 1120 # Make a subprogram for the function and record it outside the main 1121 # tree. 1122 1123 subprogram = Subprogram(lambda_, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=lambda_) 1124 1125 # Make nice annotations for the viewer. 1126 1127 lambda_._subprogram = subprogram 1128 1129 # Process the lambda contents. 1130 1131 self.current_subprograms.append(subprogram) 1132 subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))] 1133 self.current_subprograms.pop() 1134 self._visitFunction(lambda_, subprogram) 1135 1136 # Get the subprogram reference to the lambda. 1137 1138 return LoadRef(lambda_, 1, ref=subprogram) 1139 1140 def visitList(self, list): 1141 1142 # Make a subprogram for the list construction and record it outside the 1143 # main tree. 1144 1145 subprogram = Subprogram(list, name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None, original_def=list) 1146 self.current_subprograms.append(subprogram) 1147 1148 # Make nice annotations for the viewer. 1149 1150 list._subprogram = subprogram 1151 1152 subprogram.code=[ 1153 StoreTemp( 1154 expr=InvokeFunction( 1155 list, 1156 expr=LoadName( 1157 name="list" 1158 ), 1159 args=[], 1160 star=None, 1161 dstar=None 1162 ) 1163 ) 1164 ] 1165 1166 for node in list.nodes: 1167 subprogram.code.append( 1168 InvokeFunction( 1169 list, 1170 expr=LoadAttr( 1171 expr=LoadTemp(), 1172 name="append" 1173 ), 1174 args=[self.dispatch(node)], 1175 star=None, 1176 dstar=None 1177 ) 1178 ) 1179 1180 subprogram.code.append( 1181 ReturnFromBlock( 1182 expr=LoadTemp(release=1) 1183 ) 1184 ) 1185 1186 self.current_subprograms.pop() 1187 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1188 1189 # Make an invocation of the subprogram. 1190 1191 result = InvokeRef(list, 1, 1192 produces_result=1, 1193 ref=subprogram 1194 ) 1195 return result 1196 1197 def visitListComp(self, listcomp): 1198 1199 # Make a subprogram for the list comprehension and record it outside the 1200 # main tree. 1201 1202 subprogram = Subprogram(listcomp, name=None, module=self.module, internal=1, returns_value=1, star=None, dstar=None, original_def=listcomp) 1203 self.current_subprograms.append(subprogram) 1204 1205 # Make nice annotations for the viewer. 1206 1207 listcomp._subprogram = subprogram 1208 1209 # Add a temporary variable. 1210 # Produce for loops within the subprogram. 1211 # Return the result. 1212 1213 subprogram.code = [ 1214 StoreTemp( 1215 index="listcomp", 1216 expr=InvokeFunction( 1217 expr=LoadName(name="list"), 1218 args=[], 1219 star=None, 1220 dstar=None 1221 ) 1222 ) 1223 ] + self._visitListCompFor(listcomp, listcomp.quals) + [ 1224 ReturnFromBlock( 1225 expr=LoadTemp( 1226 index="listcomp", 1227 release=1 1228 ) 1229 ) 1230 ] 1231 1232 self.current_subprograms.pop() 1233 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1234 1235 # Make an invocation of the subprogram. 1236 1237 result = InvokeRef(listcomp, 1, 1238 produces_result=1, 1239 ref=subprogram 1240 ) 1241 return result 1242 1243 def _visitListCompFor(self, node, quals): 1244 qual = quals[0] 1245 if len(quals) > 1: 1246 body = self._visitListCompFor(node, quals[1:]) 1247 if qual.ifs: 1248 body = self._visitListCompIf(node, qual.ifs, body) 1249 elif qual.ifs: 1250 body = self._visitListCompIf(node, qual.ifs) 1251 else: 1252 body = self._visitListCompBody(node) 1253 return [self._visitFor(qual, body)] 1254 1255 def _visitListCompIf(self, node, ifs, expr=None): 1256 if_ = ifs[0] 1257 if len(ifs) > 1: 1258 body = self._visitListCompIf(node, ifs[1:], expr) 1259 elif expr is None: 1260 body = self._visitListCompBody(node) 1261 else: 1262 body = expr 1263 return [ 1264 Conditional(if_, 1, 1265 test=InvokeFunction( 1266 if_, 1267 expr=LoadAttr( 1268 expr=self.dispatch(if_.test), 1269 name="__bool__" 1270 ), 1271 ), 1272 body=body, 1273 else_=[] 1274 ) 1275 ] 1276 1277 def _visitListCompBody(self, node): 1278 return [ 1279 InvokeFunction( 1280 expr=LoadAttr( 1281 expr=LoadTemp(index="listcomp"), 1282 name="append" 1283 ), 1284 args=[self.dispatch(node.expr)], 1285 star=None, 1286 dstar=None 1287 ) 1288 ] 1289 1290 def visitMod(self, mod): 1291 return self._visitBinary(mod, "__mod__", "__rmod__") 1292 1293 def visitMul(self, mul): 1294 return self._visitBinary(mul, "__mul__", "__rmul__") 1295 1296 def visitName(self, name): 1297 result = LoadName(name, 1, name=name.name) 1298 return result 1299 1300 def _visitNot(self, expr, not_=None): 1301 invocation = InvokeFunction( 1302 not_, # NOTE: May need a real original node. 1303 expr=LoadAttr( 1304 expr=expr, 1305 name="__bool__" 1306 ), 1307 ) 1308 if not_ is not None: 1309 result = Not(not_, 1, expr=invocation) 1310 else: 1311 result = Not(expr=invocation) 1312 return result 1313 1314 def visitNot(self, not_): 1315 return self._visitNot(self.dispatch(not_.expr), not_) 1316 1317 def visitOr(self, or_): 1318 1319 """ 1320 Make a subprogram for the 'or_' node and record its contents inside the 1321 subprogram. Convert... 1322 1323 Or (test) 1324 (test) 1325 ... 1326 1327 ...to: 1328 1329 Subprogram -> Conditional (test) -> ReturnFromBlock ... 1330 (else) -> Conditional (test) -> ReturnFromBlock ... 1331 (else) -> ... 1332 """ 1333 1334 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 1335 self.current_subprograms.append(subprogram) 1336 1337 # In the subprogram, make instructions which store each operand, test 1338 # for each operand's truth status, and if appropriate return from the 1339 # subprogram with the value of the operand. 1340 1341 last = or_.nodes[-1] 1342 results = nodes = [] 1343 1344 for node in or_.nodes: 1345 expr = self.dispatch(node) 1346 1347 # Return from the subprogram where the test is satisfied. 1348 1349 if node is not last: 1350 nodes.append(StoreTemp(expr=expr)) 1351 invocation = InvokeFunction(or_, expr=LoadAttr(expr=LoadTemp(), name="__bool__")) 1352 test = Conditional(test=invocation, body=[ReturnFromBlock(expr=LoadTemp())]) 1353 nodes.append(test) 1354 1355 # Put subsequent operations in the else section of this conditional. 1356 1357 nodes = test.else_ = [ReleaseTemp()] 1358 1359 # For the last operation, return the result. 1360 1361 else: 1362 nodes.append( 1363 ReturnFromBlock(expr=expr) 1364 ) 1365 1366 # Finish the subprogram definition. 1367 1368 subprogram.code = results 1369 1370 self.current_subprograms.pop() 1371 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1372 1373 # Make an invocation of the subprogram. 1374 1375 result = InvokeRef(or_, 1, 1376 produces_result=1, 1377 ref=subprogram 1378 ) 1379 return result 1380 1381 def visitPass(self, pass_): 1382 return Pass(pass_, 1) 1383 1384 def visitPower(self, power): 1385 return self._visitBinary(power, "__pow__", "__rpow__") 1386 1387 def visitPrint(self, print_): 1388 1389 """ 1390 Convert... 1391 1392 Print (dest) -> 1393 (nodes) 1394 1395 ...to: 1396 1397 StoreTemp (index) -> "print" 1398 (expr) -> LoadAttr (expr) -> (dest) 1399 (name) -> "write" 1400 InvokeFunction (expr) -> LoadTemp (index) -> "print" 1401 (args) -> [(node)] 1402 ReleaseTemp (index) -> "print" 1403 """ 1404 1405 if print_.dest is not None: 1406 dest = self.dispatch(print_.dest) 1407 else: 1408 dest = self.dispatch(compiler.ast.Name("stdout")) 1409 1410 result = Assign(print_, 1, 1411 code=[ 1412 StoreTemp( 1413 index="print", 1414 expr=LoadAttr( 1415 expr=dest, 1416 name="write" 1417 ) 1418 ) 1419 ] 1420 ) 1421 1422 for node in print_.nodes: 1423 result.code.append( 1424 InvokeFunction( 1425 print_, 1426 expr=LoadTemp(index="print"), 1427 args=[self.dispatch(node)], 1428 star=None, 1429 dstar=None 1430 ) 1431 ) 1432 1433 result.code.append( 1434 ReleaseTemp(index="print") 1435 ) 1436 1437 return result 1438 1439 def visitPrintnl(self, printnl): 1440 result = self.visitPrint(printnl) 1441 result.code.insert( 1442 len(result.code) - 1, 1443 InvokeFunction( 1444 printnl, 1445 expr=LoadTemp(index="print"), 1446 args=[self.dispatch(compiler.ast.Const("\n"))], 1447 star=None, 1448 dstar=None 1449 ) 1450 ) 1451 return result 1452 1453 def visitRaise(self, raise_): 1454 result = Raise(raise_, 1) 1455 if raise_.expr2 is None: 1456 result.expr = self.dispatch(raise_.expr1) 1457 else: 1458 result.expr = InvokeFunction( 1459 raise_, 1460 expr=self.dispatch(raise_.expr1), 1461 args=[self.dispatch(raise_.expr2)], 1462 star=None, 1463 dstar=None 1464 ) 1465 if raise_.expr3 is not None: 1466 result.traceback = self.dispatch(raise_.expr3) 1467 else: 1468 result.traceback = None 1469 return result 1470 1471 def visitReturn(self, return_): 1472 result = ReturnFromFunction(return_, 1, 1473 expr=self.dispatch(return_.value) 1474 ) 1475 return result 1476 1477 def _visitSlice(self, slice, expr, lower, upper, flags, value=None): 1478 if flags == "OP_ASSIGN": 1479 result = InvokeFunction(slice, 1, 1480 expr=LoadAttr( 1481 expr=expr, 1482 name="__setslice__" 1483 ), 1484 star=None, 1485 dstar=None, 1486 args=[lower, upper, value] 1487 ) 1488 elif flags == "OP_APPLY": 1489 args = [] 1490 result = InvokeFunction(slice, 1, 1491 expr=LoadAttr( 1492 expr=expr, 1493 name="__getslice__" 1494 ), 1495 star=None, 1496 dstar=None, 1497 args=[lower, upper] 1498 ) 1499 elif flags == "OP_DELETE": 1500 args = [] 1501 result = InvokeFunction(slice, 1, 1502 expr=LoadAttr( 1503 expr=expr, 1504 name="__delslice__" 1505 ), 1506 star=None, 1507 dstar=None, 1508 args=[lower, upper] 1509 ) 1510 else: 1511 raise NotImplementedError, flags 1512 1513 return result 1514 1515 def visitSlice(self, slice, element=None): 1516 return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower), 1517 self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, element)) 1518 1519 def visitSliceobj(self, sliceobj): 1520 return InvokeFunction(sliceobj, 1, 1521 expr=LoadName(name="slice"), 1522 args=self.dispatches(sliceobj.nodes), 1523 star=None, 1524 dstar=None 1525 ) 1526 1527 def visitStmt(self, stmt): 1528 return self.dispatches(stmt.nodes) 1529 1530 def visitSub(self, sub): 1531 return self._visitBinary(sub, "__sub__", "__rsub__") 1532 1533 def _visitSubscript(self, subscript, expr, subs, flags, value=None): 1534 if flags == "OP_ASSIGN": 1535 result = InvokeFunction(subscript, 1, 1536 expr=LoadAttr( 1537 expr=expr, 1538 name="__setitem__" 1539 ), 1540 star=None, 1541 dstar=None, 1542 args=[subs, value] 1543 ) 1544 elif flags == "OP_APPLY": 1545 args = [] 1546 result = InvokeFunction(subscript, 1, 1547 expr=LoadAttr( 1548 expr=expr, 1549 name="__getitem__" 1550 ), 1551 star=None, 1552 dstar=None, 1553 args=[subs] 1554 ) 1555 elif flags == "OP_DELETE": 1556 args = [] 1557 result = InvokeFunction(subscript, 1, 1558 expr=LoadAttr( 1559 expr=expr, 1560 name="__delitem__" 1561 ), 1562 star=None, 1563 dstar=None, 1564 args=[subs] 1565 ) 1566 else: 1567 raise NotImplementedError, flags 1568 1569 return result 1570 1571 def _visitSubscriptSubs(self, node, subs): 1572 if len(subs) == 1: 1573 return self.dispatch(subs[0]) 1574 else: 1575 return InvokeFunction(node, 1, 1576 expr=LoadName(name="tuple"), 1577 args=self.dispatches(subs), 1578 star=None, 1579 dstar=None 1580 ) 1581 1582 def visitSubscript(self, subscript, element=None): 1583 return self._visitSubscript( 1584 subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags, 1585 self._visitAssNameOrAttr(subscript, element) 1586 ) 1587 1588 def visitTryExcept(self, tryexcept): 1589 1590 """ 1591 Make conditionals for each handler associated with a 'tryexcept' node. 1592 1593 Convert... 1594 1595 TryExcept (body) 1596 (else) 1597 (spec/assign/stmt) 1598 ... 1599 1600 ...to: 1601 1602 Try (body) 1603 (else) 1604 (handler) -> Conditional (test) -> (stmt) 1605 (body) -> ResetExc ... 1606 (else) -> Conditional (test) -> (stmt) 1607 (body) -> ResetExc ... 1608 (else) -> ... 1609 """ 1610 1611 result = Try(tryexcept, 1, body=[], else_=[], finally_=[]) 1612 1613 if tryexcept.body is not None: 1614 result.body = self.dispatch(tryexcept.body) 1615 if tryexcept.else_ is not None: 1616 result.else_ = self.dispatch(tryexcept.else_) 1617 1618 results = nodes = [] 1619 catch_all = 0 1620 1621 for spec, assign, stmt in tryexcept.handlers: 1622 1623 # If no specification exists, produce an unconditional block. 1624 1625 if spec is None: 1626 nodes += self.dispatch(stmt) 1627 catch_all = 1 1628 1629 # Produce an exception value check. 1630 1631 else: 1632 test = Conditional( 1633 isolate_test=1, 1634 test=CheckType(expr=LoadExc(), choices=self._visitTryExcept(spec)) 1635 ) 1636 test.body = [] 1637 1638 if assign is not None: 1639 test.body.append( 1640 Assign( 1641 code=[ 1642 StoreTemp(expr=LoadExc()), 1643 self.dispatch(assign), 1644 ReleaseTemp() 1645 ] 1646 ) 1647 ) 1648 1649 test.body += [ResetExc()] + self.dispatch(stmt) 1650 nodes.append(test) 1651 nodes = test.else_ = [] 1652 1653 # Add a raise operation to deal with unhandled exceptions. 1654 1655 if not catch_all: 1656 nodes.append( 1657 Raise( 1658 expr=LoadExc()) 1659 ) 1660 1661 result.handler = results 1662 return result 1663 1664 def _visitTryExcept(self, spec): 1665 1666 "Return a list of nodes for the given exception type 'spec'." 1667 1668 if isinstance(spec, compiler.ast.Tuple): 1669 nodes = [] 1670 for node in spec.nodes: 1671 nodes += self._visitTryExcept(node) 1672 else: 1673 nodes = [self.dispatch(spec)] 1674 return nodes 1675 1676 def visitTryFinally(self, tryfinally): 1677 result = Try(tryfinally, 1, body=[], else_=[], finally_=[]) 1678 if tryfinally.body is not None: 1679 result.body = self.dispatch(tryfinally.body) 1680 if tryfinally.final is not None: 1681 result.finally_ = self.dispatch(tryfinally.final) 1682 return result 1683 1684 def visitTuple(self, tuple): 1685 1686 "Make a MakeTuple node containing the original 'tuple' contents." 1687 1688 result = MakeTuple(tuple, 1, 1689 nodes=self.dispatches(tuple.nodes) 1690 ) 1691 return result 1692 1693 def visitUnaryAdd(self, unaryadd): 1694 return self._visitUnary(unaryadd, "__pos__") 1695 1696 def visitUnarySub(self, unarysub): 1697 return self._visitUnary(unarysub, "__neg__") 1698 1699 def visitWhile(self, while_): 1700 1701 """ 1702 Make a subprogram for the 'while' node and record its contents inside the 1703 subprogram. Convert... 1704 1705 While (test) -> (body) 1706 (else) 1707 1708 ...to: 1709 1710 Subprogram -> Conditional (test) -> (body) -> Invoke subprogram 1711 (else) -> Conditional (test) -> ReturnFromBlock ... 1712 (else) -> ... 1713 """ 1714 1715 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None) 1716 self.current_subprograms.append(subprogram) 1717 1718 # Include a conditional statement in the subprogram. 1719 # Inside the conditional, add a recursive invocation to the subprogram 1720 # if the test condition was satisfied. 1721 # Return within the main section of the loop. 1722 1723 test = Conditional( 1724 test=InvokeFunction( 1725 while_, 1726 expr=LoadAttr( 1727 expr=self.dispatch(while_.test), 1728 name="__bool__"), 1729 ), 1730 body=self.dispatch(while_.body) + [ 1731 InvokeRef( 1732 while_, 1733 ref=subprogram 1734 ), 1735 ReturnFromBlock() 1736 ], 1737 else_=[] 1738 ) 1739 1740 # Provide the else section, if present, along with an explicit return. 1741 1742 if while_.else_ is not None: 1743 test.else_ = self.dispatch(while_.else_) + [ReturnFromBlock()] 1744 1745 # Finish the subprogram definition. 1746 1747 subprogram.code = [test] 1748 1749 self.current_subprograms.pop() 1750 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1751 1752 # Make an invocation of the subprogram. 1753 1754 result = InvokeRef(while_, 1, ref=subprogram) 1755 1756 # Make nice annotations for the viewer. 1757 1758 while_._test_call = subprogram.code[0].test 1759 1760 return result 1761 1762 # NOTE: Not actually supported. 1763 # NOTE: Virtually the same as visitReturn... 1764 1765 def visitYield(self, yield_): 1766 result = Yield(yield_, 1, 1767 expr=self.dispatch(yield_.value) 1768 ) 1769 return result 1770 1771 # Convenience methods. 1772 1773 def _visitBinary(self, binary, left_name, right_name): 1774 return self._visitBinaryOp(binary, self.dispatch(binary.left), self.dispatch(binary.right), left_name, right_name) 1775 1776 def _visitBinaryCompareOp(self, binary, left, right, left_name, right_name): 1777 1778 """ 1779 Emulate the current mechanisms by producing nodes as follows: 1780 1781 InvokeRef -> Subprogram -> StoreTemp (expr) -> x.__lt__(y) 1782 Conditional (test) -> __is__(LoadTemp, NotImplemented) 1783 (body) -> ReleaseTemp 1784 StoreTemp (expr) -> y.__gt__(x) 1785 Conditional (test) -> __is__(LoadTemp, NotImplemented) 1786 (body) -> ReturnFromBlock (expr) -> False 1787 (else) -> ReturnFromBlock (expr) -> LoadTemp 1788 (else) -> ReturnFromBlock (expr) -> LoadTemp 1789 """ 1790 1791 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 1792 self.current_subprograms.append(subprogram) 1793 1794 subprogram.code = [ 1795 StoreTemp( 1796 expr=left, 1797 index="left" 1798 ), 1799 StoreTemp( 1800 expr=right, 1801 index="right" 1802 ), 1803 StoreTemp( 1804 expr=InvokeFunction( 1805 binary, 1806 expr=LoadAttr( 1807 expr=LoadTemp(index="left"), 1808 name=left_name), 1809 args=[ 1810 LoadTemp(index="right") 1811 ], 1812 star=None, 1813 dstar=None) 1814 ), 1815 Conditional( 1816 isolate_test=1, 1817 test=CheckType( 1818 expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] 1819 ), 1820 body=[ 1821 ReleaseTemp(), 1822 StoreTemp( 1823 expr=InvokeFunction( 1824 binary, 1825 expr=LoadAttr( 1826 expr=LoadTemp(index="right"), 1827 name=right_name), 1828 args=[ 1829 LoadTemp(index="left") 1830 ], 1831 star=None, 1832 dstar=None) 1833 ), 1834 Conditional( 1835 isolate_test=1, 1836 test=CheckType( 1837 expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] 1838 ), 1839 body=[ 1840 ReturnFromBlock( 1841 expr=LoadName(name="False") 1842 ) 1843 ], 1844 else_=[ 1845 CheckType( 1846 inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] 1847 ), 1848 ReturnFromBlock( 1849 expr=LoadTemp() 1850 ) 1851 ] 1852 ) 1853 ], 1854 else_=[ 1855 CheckType( 1856 inverted=1, expr=LoadTemp(), choices=[LoadName(name="NotImplementedType")] 1857 ), 1858 ReturnFromBlock( 1859 expr=LoadTemp() 1860 ) 1861 ] 1862 ) 1863 ] 1864 1865 self.current_subprograms.pop() 1866 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1867 1868 result = InvokeRef( 1869 binary, 1870 produces_result=1, 1871 ref=subprogram 1872 ) 1873 1874 # Make nice annotations for the viewer. 1875 1876 binary._left_call = subprogram.code[2].expr 1877 binary._right_call = subprogram.code[3].body[1].expr 1878 1879 return result 1880 1881 def _visitBinaryOp(self, binary, left, right, left_name, right_name): 1882 1883 """ 1884 Emulate the current mechanisms by producing nodes as follows: 1885 1886 InvokeRef -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y) 1887 (else) 1888 (handler) -> Conditional (test) -> CheckType (expr) -> LoadExc 1889 (choices) -> LoadName TypeError 1890 (body) -> ReturnFromBlock (expr) -> y.__radd__(x) 1891 (else) 1892 """ 1893 1894 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 1895 self.current_subprograms.append(subprogram) 1896 1897 subprogram.code = [ 1898 StoreTemp( 1899 expr=left, 1900 index="left" 1901 ), 1902 StoreTemp( 1903 expr=right, 1904 index="right" 1905 ), 1906 Try(binary, 1, 1907 body=[ 1908 ReturnFromBlock( 1909 expr=InvokeFunction( 1910 binary, 1911 expr=LoadAttr(expr=LoadTemp(index="left"), name=left_name), 1912 args=[LoadTemp(index="right")], 1913 star=None, 1914 dstar=None) 1915 ) 1916 ], 1917 else_=[], 1918 finally_=[], 1919 handler=[ 1920 Conditional( 1921 test=CheckType(expr=LoadExc(), choices=[LoadName(name="TypeError")]), 1922 body=[ 1923 ReturnFromBlock( 1924 expr=InvokeFunction( 1925 binary, 1926 expr=LoadAttr(expr=LoadTemp(index="right"), name=right_name), 1927 args=[LoadTemp(index="left")], 1928 star=None, 1929 dstar=None) 1930 ) 1931 ], 1932 else_=[] 1933 ) 1934 ] 1935 ) 1936 ] 1937 1938 self.current_subprograms.pop() 1939 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1940 1941 result = InvokeRef( 1942 binary, 1943 produces_result=1, 1944 ref=subprogram 1945 ) 1946 1947 # Make nice annotations for the viewer. 1948 1949 binary._left_call = subprogram.code[2].body[0].expr 1950 binary._right_call = subprogram.code[2].handler[0].body[0].expr 1951 1952 return result 1953 1954 def _visitBuiltin(self, builtin, name): 1955 result = InvokeFunction(builtin, 1, expr=LoadName(name=name), args=self.dispatches(builtin.nodes)) 1956 return result 1957 1958 def _visitUnary(self, unary, name): 1959 result = InvokeFunction(unary, 1, 1960 expr=LoadAttr( 1961 expr=self.dispatch(unary.expr), 1962 name=name 1963 ) 1964 ) 1965 1966 # Make nice annotations for the viewer. 1967 1968 unary._unary_call = result 1969 1970 return result 1971 1972 # Convenience functions. 1973 1974 def simplify(filename, builtins=0, module_name=None): 1975 1976 """ 1977 Simplify the module stored in the file with the given 'filename'. 1978 1979 If the optional 'builtins' parameter is set to a true value (the default 1980 being a false value), then the module is considered as the builtins module. 1981 """ 1982 1983 simplifier = Simplifier(builtins) 1984 module = compiler.parseFile(filename) 1985 compiler.misc.set_filename(filename, module) 1986 if builtins: 1987 name = module_name or "__builtins__" 1988 else: 1989 path, ext = os.path.splitext(filename) 1990 path, name = os.path.split(path) 1991 name = module_name or name 1992 simplified = simplifier.process(module, name) 1993 return simplified 1994 1995 # vim: tabstop=4 expandtab shiftwidth=4