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