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