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