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