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