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