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