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