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