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 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, Mod, Module, Mul, Name, Not, Or, 54 Pass, Power, Print, Printnl, Raise, Return, Slice, Stmt, Sub, 55 Subscript, TryExcept, TryFinally, Tuple, While, UnaryAdd, UnarySub. 56 57 Missing: Assert, Backquote, Bitand, Bitor, Bitxor, Decorators, Ellipsis, 58 Exec, LeftShift, ListComp, ListCompFor, ListCompIf, RightShift, 59 Sliceobj, 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(expr=LoadAttr(expr=LoadTemp(), name="next")) 228 result = Assign(asslist, 1) 229 store = StoreTemp(expr=InvokeFunction(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(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( 280 args=[expr], 281 star=None, 282 dstar=None, 283 expr=LoadAttr( 284 expr=self.dispatch(augassign.node), 285 name=self.augassign_methods[augassign.op] 286 ) 287 ) 288 ), 289 StoreName( 290 expr=LoadTemp(), 291 name=augassign.node.name), 292 ReleaseTemp() 293 ] 294 295 # Complicated augmented assignment: lvalue.attr += expr 296 297 elif isinstance(augassign.node, compiler.ast.Getattr): 298 299 # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr)) 300 301 result.code = [ 302 StoreTemp( 303 index="expr", 304 expr=self.dispatch(augassign.node.expr) 305 ), 306 StoreTemp( 307 expr=InvokeFunction( 308 args=[expr], star=None, dstar=None, 309 expr=LoadAttr( 310 expr=LoadAttr(augassign.node, 1, 311 expr=LoadTemp(index="expr"), 312 name=augassign.node.attrname 313 ), 314 name=self.augassign_methods[augassign.op] 315 ) 316 ) 317 ), 318 StoreAttr( 319 expr=LoadTemp(), 320 lvalue=LoadTemp(index="expr"), 321 name=augassign.node.attrname 322 ), 323 ReleaseTemp(index="expr"), 324 ReleaseTemp() 325 ] 326 327 # Complicated augassign using slices: lvalue[lower:upper] += expr 328 329 elif isinstance(augassign.node, compiler.ast.Slice): 330 331 # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr)) 332 333 result.code = [ 334 StoreTemp( 335 index="expr", 336 expr=self.dispatch(augassign.node.expr) 337 ), 338 StoreTemp( 339 index="lower", 340 expr=self.dispatch_or_none(augassign.node.lower) 341 ), 342 StoreTemp( 343 index="upper", 344 expr=self.dispatch_or_none(augassign.node.upper) 345 ), 346 StoreTemp( 347 expr=InvokeFunction( 348 args=[expr], star=None, dstar=None, 349 expr=LoadAttr( 350 expr=self._visitSlice( 351 augassign.node, 352 LoadTemp(index="expr"), 353 LoadTemp(index="lower"), 354 LoadTemp(index="upper"), 355 "OP_APPLY"), 356 name=self.augassign_methods[augassign.op] 357 ) 358 ) 359 ), 360 self._visitSlice( 361 augassign.node, 362 LoadTemp(index="expr"), 363 LoadTemp(index="lower"), 364 LoadTemp(index="upper"), 365 "OP_ASSIGN", 366 LoadTemp() 367 ), 368 ReleaseTemp(index="expr"), 369 ReleaseTemp(index="lower"), 370 ReleaseTemp(index="upper"), 371 ReleaseTemp() 372 ] 373 374 # Complicated augassign using subscripts: lvalue[subs] += expr 375 376 elif isinstance(augassign.node, compiler.ast.Subscript): 377 378 # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr)) 379 380 result.code = [ 381 StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)), 382 StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)), 383 StoreTemp( 384 expr=InvokeFunction( 385 args=[expr], star=None, dstar=None, 386 expr=LoadAttr( 387 expr=self._visitSubscript( 388 augassign.node, 389 LoadTemp(index="expr"), 390 LoadTemp(index="subs"), 391 "OP_APPLY" 392 ), 393 name=self.augassign_methods[augassign.op] 394 ) 395 ) 396 ), 397 self._visitSubscript( 398 augassign.node, 399 LoadTemp(index="expr"), 400 LoadTemp(index="subs"), 401 "OP_ASSIGN", 402 LoadTemp() 403 ), 404 ReleaseTemp(index="expr"), 405 ReleaseTemp(index="subs"), 406 ReleaseTemp() 407 ] 408 409 else: 410 raise NotImplementedError, augassign.node.__class__ 411 412 return result 413 414 def visitBreak(self, break_): 415 result = ReturnFromBlock(break_, 1) 416 return result 417 418 def visitCallFunc(self, callfunc): 419 result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args)) 420 if callfunc.star_args is not None: 421 result.star = self.dispatch(callfunc.star_args) 422 if callfunc.dstar_args is not None: 423 result.dstar = self.dispatch(callfunc.dstar_args) 424 result.expr = self.dispatch(callfunc.node) 425 return result 426 427 def visitClass(self, class_): 428 429 # Add "object" if the class is not "object" and has an empty bases list. 430 431 if class_.name != "object" and not class_.bases: 432 bases = [compiler.ast.Name("object")] 433 else: 434 bases = class_.bases 435 436 structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases)) 437 self.structures.append(structure) 438 within_class = self.within_class 439 self.within_class = 1 440 441 # Make a subprogram which initialises the class structure. 442 443 subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None) 444 self.current_subprograms.append(subprogram) 445 self.current_structures.append(structure) # mostly for name construction 446 447 # The class is initialised using the code found inside. 448 449 subprogram.code = self.dispatch(class_.code) + [ReturnFromBlock()] 450 451 self.within_class = within_class 452 self.current_structures.pop() 453 self.current_subprograms.pop() 454 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 455 456 # Make a definition of the class associating it with a name. 457 458 result = Assign( 459 code=[ 460 StoreName(class_, 1, # defines the class 461 name=class_.name, 462 expr=LoadRef(ref=structure) 463 ), 464 InvokeBlock( 465 share_locals=0, # override the local sharing usually in InvokeBlock 466 expr=LoadRef(ref=subprogram) 467 ) 468 ] 469 ) 470 return result 471 472 comparison_methods = { 473 "==" : "__eq__", "!=" : "__ne__", "<" : "__lt__", "<=" : "__le__", 474 ">=" : "__ge__", ">" : "__gt__", "is" : None, "is not" : None 475 } 476 477 def visitCompare(self, compare): 478 479 """ 480 Make a subprogram for the 'compare' node and record its contents inside 481 the subprogram. Convert... 482 483 Compare (expr) 484 (name/node) 485 ... 486 487 ...to: 488 489 InvokeBlock -> Subprogram -> Conditional (test) -> (body) 490 (else) -> Conditional (test) -> (body) 491 (else) -> ... 492 """ 493 494 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 495 self.current_subprograms.append(subprogram) 496 497 # In the subprogram, make instructions which invoke a method on the 498 # first operand of each operand pair and, if appropriate, return with 499 # the value from that method. 500 501 last = compare.ops[-1] 502 previous = self.dispatch(compare.expr) 503 results = nodes = [] 504 505 # For viewing purposes, record invocations on the AST node. 506 507 compare._ops = [] 508 509 for op in compare.ops: 510 op_name, node = op 511 expr = self.dispatch(node) 512 513 # Identify the operation and produce the appropriate method call. 514 515 method_name = self.comparison_methods[op_name] 516 if method_name: 517 invocation = InvokeFunction( 518 expr=LoadAttr( 519 expr=previous, 520 name=method_name), 521 args=[expr], 522 star=None, 523 dstar=None) 524 525 elif op_name == "is": 526 invocation = InvokeFunction( 527 expr=LoadName(name="__is__"), 528 args=[previous, expr], 529 star=None, 530 dstar=None) 531 532 elif op_name == "is not": 533 invocation = Not( 534 expr=InvokeFunction( 535 expr=LoadName(name="__is__"), 536 args=[previous, expr], 537 star=None, 538 dstar=None) 539 ) 540 else: 541 raise NotImplementedError, op_name 542 543 nodes.append(StoreTemp(expr=invocation)) 544 compare._ops.append(invocation) 545 546 # Return from the subprogram where the test is not satisfied. 547 548 if op is not last: 549 nodes.append( 550 Conditional( 551 test=self._visitNot(LoadTemp()), 552 body=[ 553 ReturnFromBlock(expr=LoadTemp()) 554 ], 555 else_=[ 556 ReleaseTemp() 557 # Subsequent operations go here! 558 ] 559 ) 560 ) 561 562 # Put subsequent operations in the else section of this conditional. 563 564 nodes = nodes[-1].else_ 565 566 # For the last operation, return the result. 567 568 else: 569 nodes.append( 570 ReturnFromBlock(expr=LoadTemp(release=1)) 571 ) 572 573 previous = expr 574 575 # Finish the subprogram definition. 576 577 subprogram.code = results 578 579 self.current_subprograms.pop() 580 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 581 582 # Make an invocation of the subprogram. 583 584 result = InvokeBlock(compare, 1, produces_result=1) 585 result.expr = LoadRef(ref=subprogram) 586 return result 587 588 def visitConst(self, const): 589 key = "%s-%s" % (const.value.__class__.__name__, const.value) 590 if not self.constants.has_key(key): 591 self.constants[key] = Constant(name=repr(const.value), value=const.value) 592 result = LoadRef(const, 1, ref=self.constants[key]) 593 return result 594 595 def visitContinue(self, continue_): 596 result = InvokeBlock(continue_, 1, 597 expr=LoadRef(ref=self.current_subprograms[-1]) 598 ) 599 return result 600 601 def visitDict(self, dict): 602 result = InvokeFunction(dict, 1, expr=LoadName(name="dict"), star=None, dstar=None) 603 args = [] 604 for key, value in dict.items: 605 tuple = InvokeFunction(expr=LoadName(name="tuple"), star=None, dstar=None) 606 tuple.set_args([self.dispatch(key), self.dispatch(value)]) 607 args.append(tuple) 608 result.set_args(args) 609 return result 610 611 def visitDiscard(self, discard): 612 return self.dispatch(discard.expr) 613 614 def visitDiv(self, div): 615 return self._visitBinary(div, "__div__", "__rdiv__") 616 617 def visitFloorDiv(self, floordiv): 618 return self._visitBinary(floordiv, "__floordiv__", "__rfloordiv__") 619 620 def visitFor(self, for_): 621 622 """ 623 Make a subprogram for the 'for_' node and record its contents inside the 624 subprogram. Convert... 625 626 For (assign) 627 (body) 628 (else) 629 630 ...to: 631 632 Assign (assign #1) 633 Invoke -> Subprogram -> Try (body) -> (assign #2) 634 (body) 635 Invoke subprogram 636 (handler) -> ... 637 (else) -> ... 638 """ 639 640 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None) 641 self.current_subprograms.append(subprogram) 642 643 # Always return from conditional sections/subprograms. 644 645 if for_.else_ is not None: 646 else_stmt = self.dispatch(for_.else_) + [ReturnFromBlock()] 647 else: 648 else_stmt = [ReturnFromBlock()] 649 650 # Wrap the assignment in a try...except statement. 651 # Inside the body, add a recursive invocation to the subprogram. 652 653 subprogram.code = [ 654 Try( 655 body=[ 656 Assign( 657 code=[ 658 StoreTemp(expr=InvokeFunction(expr=LoadAttr(expr=LoadTemp(), name="next"))), 659 self.dispatch(for_.assign), 660 ReleaseTemp() 661 ]) 662 ] + self.dispatch(for_.body) + [ 663 InvokeBlock( 664 expr=LoadRef(ref=subprogram) 665 ) 666 ], 667 handler=[ 668 Conditional( 669 test=InvokeFunction( 670 expr=LoadName(name="isinstance"), 671 args=[LoadExc(), LoadName(name="StopIteration")], 672 star=None, 673 dstar=None), 674 body=else_stmt, 675 else_=[Raise(expr=LoadExc())] 676 ) 677 ], 678 else_=[], 679 finally_=[] 680 ), 681 ReturnFromBlock() 682 ] 683 684 # Finish the subprogram definition. 685 686 self.current_subprograms.pop() 687 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 688 689 # Obtain an iterator for the sequence involved. 690 # Then, make an invocation of the subprogram. 691 692 result = Assign(for_, 1, 693 code=[ 694 StoreTemp( 695 expr=InvokeFunction( 696 expr=LoadAttr( 697 name="__iter__", 698 expr=self.dispatch(for_.list) 699 ) 700 ) 701 ), 702 InvokeBlock(expr=LoadRef(ref=subprogram)), 703 ReleaseTemp() 704 ] 705 ) 706 707 # Make nice annotations for the viewer. 708 709 for_._iter_call = result.code[0].expr 710 for_._next_call = subprogram.code[0].body[0].code[0].expr 711 712 return result 713 714 def visitFrom(self, from_): 715 result = Assign(from_, 1) 716 code = [] 717 _names = [] 718 code.append( 719 StoreTemp( 720 expr=Import(name=from_.modname, alias=1) 721 ) 722 ) 723 from_._modname = code[-1].expr 724 for name, alias in from_.names: 725 code.append( 726 StoreName( 727 expr=LoadAttr( 728 expr=LoadTemp(), 729 name=name), 730 name=(alias or name) 731 ) 732 ) 733 _names.append(code[-1].expr) 734 code.append(ReleaseTemp()) 735 result.code = code 736 from_._names = _names 737 return result 738 739 def _visitFunction(self, function, subprogram): 740 741 """ 742 A common function generator which transforms the given 'function' node 743 and initialises the given 'subprogram' appropriately. 744 """ 745 746 # Discover star and dstar parameters. 747 748 if function.flags & 4 != 0: 749 has_star = 1 750 else: 751 has_star = 0 752 if function.flags & 8 != 0: 753 has_dstar = 1 754 else: 755 has_dstar = 0 756 757 # Discover the number of defaults and positional parameters. 758 759 ndefaults = len(function.defaults) 760 npositional = len(function.argnames) - has_star - has_dstar 761 762 # Produce star and dstar parameters with appropriate defaults. 763 764 if has_star: 765 star = ( 766 function.argnames[npositional], 767 InvokeFunction(expr=LoadName(name="list")) 768 ) 769 else: 770 star = None 771 if has_dstar: 772 dstar = ( 773 function.argnames[npositional + has_star], 774 InvokeFunction(expr=LoadName(name="dict")) 775 ) 776 else: 777 dstar = None 778 779 params = [] 780 for i in range(0, npositional - ndefaults): 781 params.append((function.argnames[i], None)) 782 783 # Process defaults. 784 785 for i in range(0, ndefaults): 786 default = function.defaults[i] 787 if default is not None: 788 params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) 789 else: 790 params.append((function.argnames[npositional - ndefaults + i], None)) 791 792 subprogram.params = params 793 subprogram.star = star 794 subprogram.dstar = dstar 795 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 796 797 def visitFunction(self, function): 798 799 """ 800 Make a subprogram for the 'function' and record it outside the main 801 tree. Produce something like the following: 802 803 StoreName (name) 804 (expr) -> LoadRef (ref) -> Subprogram (params) 805 (star) 806 (dstar) 807 """ 808 809 subprogram = Subprogram(name=function.name, module=self.module, structures=self.current_structures[:], 810 internal=0, returns_value=1, star=None, dstar=None, is_method=self.within_class) 811 812 self.current_subprograms.append(subprogram) 813 within_class = self.within_class 814 self.within_class = 0 815 816 subprogram.code = self.dispatch(function.code) + [ReturnFromFunction()] 817 818 self.within_class = within_class 819 self.current_subprograms.pop() 820 self._visitFunction(function, subprogram) 821 822 # Make a definition of the function associating it with a name. 823 824 result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram)) 825 return result 826 827 def visitGetattr(self, getattr): 828 result = LoadAttr(getattr, 1, 829 name=getattr.attrname, 830 expr=self.dispatch(getattr.expr) 831 ) 832 return result 833 834 def visitGlobal(self, global_): 835 result = Global(global_, 1, 836 names=global_.names 837 ) 838 return result 839 840 def visitIf(self, if_): 841 842 """ 843 Make conditionals for each test from an 'if_' AST node, adding the body 844 and putting each subsequent test as part of the conditional's else 845 section. 846 847 Convert... 848 849 If (test/body) 850 (test/body) 851 ... 852 (else/body) 853 854 ...to: 855 856 Conditional (test) -> (body) 857 (else) -> Conditional (test) -> (body) 858 (else) -> ... 859 """ 860 861 862 results = nodes = [] 863 864 # Produce something like... 865 # expr.__bool__() ? body 866 867 first = 1 868 for compare, stmt in if_.tests: 869 870 # Set the first as the defining node. 871 872 test = Conditional(if_, first, 873 test=InvokeFunction( 874 expr=LoadAttr( 875 expr=self.dispatch(compare), 876 name="__bool__" 877 ), 878 ) 879 ) 880 test.body = self.dispatch(stmt) 881 nodes.append(test) 882 nodes = test.else_ = [] 883 first = 0 884 885 # Add the compound statement from any else clause to the end. 886 887 if if_.else_ is not None: 888 nodes += self.dispatch(if_.else_) 889 890 result = results[0] 891 return result 892 893 def visitImport(self, import_): 894 result = Assign(import_, 1) 895 code = [] 896 _names = [] 897 for path, alias in import_.names: 898 importer = Import(name=path, alias=alias) 899 top = alias or path.split(".")[0] 900 code.append(StoreName(expr=importer, name=top)) 901 _names.append(code[-1].expr) 902 result.code = code 903 import_._names = _names 904 return result 905 906 def visitInvert(self, invert): 907 return self._visitUnary(invert, "__invert__") 908 909 def visitKeyword(self, keyword): 910 result = Keyword(keyword, 1, 911 name=keyword.name, 912 expr=self.dispatch(keyword.expr) 913 ) 914 return result 915 916 def visitLambda(self, lambda_): 917 918 # Make a subprogram for the function and record it outside the main 919 # tree. 920 921 subprogram = Subprogram(name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None) 922 self.current_subprograms.append(subprogram) 923 subprogram.code = [ReturnFromFunction(expr=self.dispatch(lambda_.code))] 924 self.current_subprograms.pop() 925 self._visitFunction(lambda_, subprogram) 926 927 # Get the subprogram reference to the lambda. 928 929 return LoadRef(lambda_, 1, ref=subprogram) 930 931 def visitList(self, list): 932 return self._visitBuiltin(list, "list") 933 934 def visitMod(self, mod): 935 return self._visitBinary(mod, "__mod__", "__rmod__") 936 937 def visitMul(self, mul): 938 return self._visitBinary(mul, "__mul__", "__rmul__") 939 940 def visitName(self, name): 941 result = LoadName(name, 1, name=name.name) 942 return result 943 944 def _visitNot(self, expr, not_=None): 945 invocation = InvokeFunction( 946 expr=LoadAttr( 947 expr=expr, 948 name="__bool__" 949 ), 950 ) 951 if not_ is not None: 952 result = Not(not_, 1, expr=invocation) 953 else: 954 result = Not(expr=invocation) 955 return result 956 957 def visitNot(self, not_): 958 return self._visitNot(self.dispatch(not_.expr), not_) 959 960 def visitOr(self, or_): 961 962 """ 963 Make a subprogram for the 'or_' node and record its contents inside the 964 subprogram. Convert... 965 966 Or (test) 967 (test) 968 ... 969 970 ...to: 971 972 Subprogram -> Conditional (test) -> ReturnFromBlock ... 973 (else) -> Conditional (test) -> ReturnFromBlock ... 974 (else) -> ... 975 """ 976 977 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 978 self.current_subprograms.append(subprogram) 979 980 # In the subprogram, make instructions which store each operand, test 981 # for each operand's truth status, and if appropriate return from the 982 # subprogram with the value of the operand. 983 984 last = or_.nodes[-1] 985 results = nodes = [] 986 987 for node in or_.nodes: 988 expr = self.dispatch(node) 989 990 # Return from the subprogram where the test is satisfied. 991 992 if node is not last: 993 nodes.append(StoreTemp(expr=expr)) 994 invocation = InvokeFunction(expr=LoadAttr(expr=LoadTemp(), name="__bool__")) 995 test = Conditional(test=invocation, body=[ReturnFromBlock(expr=LoadTemp())]) 996 nodes.append(test) 997 998 # Put subsequent operations in the else section of this conditional. 999 1000 nodes = test.else_ = [ReleaseTemp()] 1001 1002 # For the last operation, return the result. 1003 1004 else: 1005 nodes.append( 1006 ReturnFromBlock(expr=expr) 1007 ) 1008 1009 # Finish the subprogram definition. 1010 1011 subprogram.code = results 1012 1013 self.current_subprograms.pop() 1014 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1015 1016 # Make an invocation of the subprogram. 1017 1018 result = InvokeBlock(or_, 1, produces_result=1) 1019 result.expr = LoadRef(ref=subprogram) 1020 return result 1021 1022 def visitPass(self, pass_): 1023 return Pass(pass_, 1) 1024 1025 def visitPower(self, power): 1026 return self._visitBinary(power, "__pow__", "__rpow__") 1027 1028 def visitPrint(self, print_): 1029 1030 """ 1031 Convert... 1032 1033 Print (dest) -> 1034 (nodes) 1035 1036 ...to: 1037 1038 StoreTemp (index) -> "print" 1039 (expr) -> LoadAttr (expr) -> (dest) 1040 (name) -> "write" 1041 InvokeFunction (expr) -> LoadTemp (index) -> "print" 1042 (args) -> [(node)] 1043 ReleaseTemp (index) -> "print" 1044 """ 1045 1046 if print_.dest is not None: 1047 dest = self.dispatch(print_.dest) 1048 else: 1049 dest = self.dispatch(compiler.ast.Name("stdout")) 1050 1051 result = Assign(print_, 1, 1052 code=[ 1053 StoreTemp( 1054 index="print", 1055 expr=LoadAttr( 1056 expr=dest, 1057 name="write" 1058 ) 1059 ) 1060 ] 1061 ) 1062 1063 for node in print_.nodes: 1064 result.code.append( 1065 InvokeFunction( 1066 expr=LoadTemp(index="print"), 1067 args=[self.dispatch(node)], 1068 star=None, 1069 dstar=None 1070 ) 1071 ) 1072 1073 result.code.append( 1074 ReleaseTemp(index="print") 1075 ) 1076 1077 return result 1078 1079 def visitPrintnl(self, printnl): 1080 result = self.visitPrint(printnl) 1081 result.code.insert( 1082 len(result.code) - 1, 1083 InvokeFunction( 1084 expr=LoadTemp(index="print"), 1085 args=[self.dispatch(compiler.ast.Const("\n"))], 1086 star=None, 1087 dstar=None 1088 ) 1089 ) 1090 return result 1091 1092 def visitRaise(self, raise_): 1093 result = Raise(raise_, 1) 1094 if raise_.expr2 is None: 1095 result.expr = self.dispatch(raise_.expr1) 1096 else: 1097 result.expr = InvokeFunction( 1098 expr=self.dispatch(raise_.expr1), 1099 args=[self.dispatch(raise_.expr2)], 1100 star=None, 1101 dstar=None 1102 ) 1103 if raise_.expr3 is not None: 1104 result.traceback = self.dispatch(raise_.expr3) 1105 else: 1106 result.traceback = None 1107 return result 1108 1109 def visitReturn(self, return_): 1110 result = ReturnFromFunction(return_, 1, 1111 expr=self.dispatch(return_.value) 1112 ) 1113 return result 1114 1115 def _visitSlice(self, slice, expr, lower, upper, flags, value=None): 1116 if flags == "OP_ASSIGN": 1117 result = InvokeFunction(slice, 1, 1118 expr=LoadAttr( 1119 expr=expr, 1120 name="__setslice__" 1121 ), 1122 star=None, 1123 dstar=None, 1124 args=[lower, upper, value] 1125 ) 1126 elif flags == "OP_APPLY": 1127 args = [] 1128 result = InvokeFunction(slice, 1, 1129 expr=LoadAttr( 1130 expr=expr, 1131 name="__getslice__" 1132 ), 1133 star=None, 1134 dstar=None, 1135 args=[lower, upper] 1136 ) 1137 elif flags == "OP_DELETE": 1138 args = [] 1139 result = InvokeFunction(slice, 1, 1140 expr=LoadAttr( 1141 expr=expr, 1142 name="__delslice__" 1143 ), 1144 star=None, 1145 dstar=None, 1146 args=[lower, upper] 1147 ) 1148 else: 1149 raise NotImplementedError, flags 1150 1151 return result 1152 1153 def visitSlice(self, slice, in_sequence=0): 1154 return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower), 1155 self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence)) 1156 1157 def visitStmt(self, stmt): 1158 return self.dispatches(stmt.nodes) 1159 1160 def visitSub(self, sub): 1161 return self._visitBinary(sub, "__sub__", "__rsub__") 1162 1163 def _visitSubscript(self, subscript, expr, subs, flags, value=None): 1164 if flags == "OP_ASSIGN": 1165 result = InvokeFunction(subscript, 1, 1166 expr=LoadAttr( 1167 expr=expr, 1168 name="__setitem__" 1169 ), 1170 star=None, 1171 dstar=None, 1172 args=[subs, value] 1173 ) 1174 elif flags == "OP_APPLY": 1175 args = [] 1176 result = InvokeFunction(subscript, 1, 1177 expr=LoadAttr( 1178 expr=expr, 1179 name="__getitem__" 1180 ), 1181 star=None, 1182 dstar=None, 1183 args=[subs] 1184 ) 1185 elif flags == "OP_DELETE": 1186 args = [] 1187 result = InvokeFunction(subscript, 1, 1188 expr=LoadAttr( 1189 expr=expr, 1190 name="__delitem__" 1191 ), 1192 star=None, 1193 dstar=None, 1194 args=[subs] 1195 ) 1196 else: 1197 raise NotImplementedError, flags 1198 1199 return result 1200 1201 def _visitSubscriptSubs(self, node, subs): 1202 if len(subs) == 1: 1203 return self.dispatch(subs[0]) 1204 else: 1205 return InvokeFunction(node, 1, 1206 expr=LoadName(name="tuple"), 1207 args=self.dispatches(subs), 1208 star=None, 1209 dstar=None 1210 ) 1211 1212 def visitSubscript(self, subscript, in_sequence=0): 1213 return self._visitSubscript( 1214 subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags, 1215 self._visitAssNameOrAttr(subscript, in_sequence) 1216 ) 1217 1218 def visitTryExcept(self, tryexcept): 1219 1220 """ 1221 Make conditionals for each handler associated with a 'tryexcept' node. 1222 1223 Convert... 1224 1225 TryExcept (body) 1226 (else) 1227 (spec/assign/stmt) 1228 ... 1229 1230 ...to: 1231 1232 Try (body) 1233 (else) 1234 (handler) -> Conditional (test) -> (stmt) 1235 (body) -> ... 1236 (else) -> Conditional (test) -> (stmt) 1237 (body) -> ... 1238 (else) -> ... 1239 """ 1240 1241 result = Try(tryexcept, 1, body=[], else_=[], finally_=[]) 1242 1243 if tryexcept.body is not None: 1244 result.body = self.dispatch(tryexcept.body) 1245 if tryexcept.else_ is not None: 1246 result.else_ = self.dispatch(tryexcept.else_) 1247 1248 results = nodes = [] 1249 catch_all = 0 1250 1251 for spec, assign, stmt in tryexcept.handlers: 1252 1253 # If no specification exists, produce an unconditional block. 1254 1255 if spec is None: 1256 nodes += self.dispatch(stmt) 1257 catch_all = 1 1258 1259 # Produce an exception value check. 1260 1261 else: 1262 test = Conditional( 1263 isolate_test=1, 1264 test=CheckExc(expr=LoadExc(), choices=self._visitTryExcept(spec)) 1265 ) 1266 test.body = [] 1267 1268 if assign is not None: 1269 test.body.append( 1270 Assign( 1271 code=[ 1272 StoreTemp(expr=LoadExc()), 1273 self.dispatch(assign), 1274 ReleaseTemp() 1275 ] 1276 ) 1277 ) 1278 1279 test.body += self.dispatch(stmt) 1280 nodes.append(test) 1281 nodes = test.else_ = [] 1282 1283 # Add a raise operation to deal with unhandled exceptions. 1284 1285 if not catch_all: 1286 nodes.append( 1287 Raise( 1288 expr=LoadExc()) 1289 ) 1290 1291 result.handler = results 1292 return result 1293 1294 def _visitTryExcept(self, spec): 1295 1296 "Return a list of nodes for the given exception type 'spec'." 1297 1298 if isinstance(spec, compiler.ast.Tuple): 1299 nodes = [] 1300 for node in spec.nodes: 1301 nodes += self._visitTryExcept(node) 1302 else: 1303 nodes = [self.dispatch(spec)] 1304 return nodes 1305 1306 def visitTryFinally(self, tryfinally): 1307 result = Try(tryfinally, 1, body=[], else_=[], finally_=[]) 1308 if tryfinally.body is not None: 1309 result.body = self.dispatch(tryfinally.body) 1310 if tryfinally.final is not None: 1311 result.finally_ = self.dispatch(tryfinally.final) 1312 return result 1313 1314 def visitTuple(self, tuple): 1315 return self._visitBuiltin(tuple, "tuple") 1316 1317 def visitUnaryAdd(self, unaryadd): 1318 return self._visitUnary(unaryadd, "__pos__") 1319 1320 def visitUnarySub(self, unarysub): 1321 return self._visitUnary(unarysub, "__neg__") 1322 1323 def visitWhile(self, while_): 1324 1325 """ 1326 Make a subprogram for the 'while' node and record its contents inside the 1327 subprogram. Convert... 1328 1329 While (test) -> (body) 1330 (else) 1331 1332 ...to: 1333 1334 Subprogram -> Conditional (test) -> (body) -> Invoke subprogram 1335 (else) -> Conditional (test) -> ReturnFromBlock ... 1336 (else) -> ... 1337 """ 1338 1339 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None) 1340 self.current_subprograms.append(subprogram) 1341 1342 # Include a conditional statement in the subprogram. 1343 # Inside the conditional, add a recursive invocation to the subprogram 1344 # if the test condition was satisfied. 1345 # Return within the main section of the loop. 1346 1347 test = Conditional( 1348 test=InvokeFunction( 1349 expr=LoadAttr( 1350 expr=self.dispatch(while_.test), 1351 name="__bool__"), 1352 ), 1353 body=self.dispatch(while_.body) + [ 1354 InvokeBlock( 1355 expr=LoadRef(ref=subprogram) 1356 ), 1357 ReturnFromBlock() 1358 ], 1359 else_=[] 1360 ) 1361 1362 # Provide the else section, if present, along with an explicit return. 1363 1364 if while_.else_ is not None: 1365 test.else_ = self.dispatch(while_.else_) + [ReturnFromBlock()] 1366 1367 # Finish the subprogram definition. 1368 1369 subprogram.code = [test] 1370 1371 self.current_subprograms.pop() 1372 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1373 1374 # Make an invocation of the subprogram. 1375 1376 result = InvokeBlock(while_, 1, 1377 expr=LoadRef(ref=subprogram) 1378 ) 1379 1380 # Make nice annotations for the viewer. 1381 1382 while_._test_call = subprogram.code[0].test 1383 1384 return result 1385 1386 # Convenience methods. 1387 1388 def _visitBinary(self, binary, left_name, right_name): 1389 1390 """ 1391 Emulate the current mechanisms by producing nodes as follows: 1392 1393 InvokeBlock -> Subprogram -> Try (body) -> ReturnFromBlock (expr) -> x.__add__(y) 1394 (else) 1395 (handler) -> Conditional (test) -> CheckExc (expr) -> LoadExc 1396 (choices) -> LoadName TypeError 1397 (body) -> ReturnFromBlock (expr) -> y.__radd__(x) 1398 (else) 1399 """ 1400 1401 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=1, params=[], star=None, dstar=None) 1402 self.current_subprograms.append(subprogram) 1403 1404 subprogram.code = [ 1405 Try(binary, 1, 1406 body=[ 1407 ReturnFromBlock( 1408 expr=InvokeFunction( 1409 expr=LoadAttr(expr=self.dispatch(binary.left), name=left_name), 1410 args=[self.dispatch(binary.right)], 1411 star=None, 1412 dstar=None) 1413 ) 1414 ], 1415 else_=[], 1416 finally_=[], 1417 handler=[ 1418 Conditional( 1419 test=CheckExc(expr=LoadExc(), choices=[LoadName(name="TypeError")]), 1420 body=[ 1421 ReturnFromBlock( 1422 expr=InvokeFunction( 1423 expr=LoadAttr(expr=self.dispatch(binary.right), name=right_name), 1424 args=[self.dispatch(binary.left)], 1425 star=None, 1426 dstar=None) 1427 ) 1428 ], 1429 else_=[] 1430 ) 1431 ] 1432 ) 1433 ] 1434 1435 self.current_subprograms.pop() 1436 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1437 1438 result = InvokeBlock( 1439 produces_result=1, 1440 expr=LoadRef(ref=subprogram) 1441 ) 1442 1443 # Make nice annotations for the viewer. 1444 1445 binary._left_call = subprogram.code[0].body[0].expr 1446 binary._right_call = subprogram.code[0].handler[0].body[0].expr 1447 1448 return result 1449 1450 def _visitBuiltin(self, builtin, name): 1451 result = InvokeFunction(builtin, 1, expr=LoadName(name=name), args=self.dispatches(builtin.nodes), star=None, dstar=None) 1452 return result 1453 1454 def _visitUnary(self, unary, name): 1455 result = InvokeFunction(unary, 1, 1456 expr=LoadAttr( 1457 expr=self.dispatch(unary.expr), 1458 name=name 1459 ) 1460 ) 1461 1462 # Make nice annotations for the viewer. 1463 1464 unary._unary_call = result 1465 1466 return result 1467 1468 # Convenience functions. 1469 1470 def simplify(filename, builtins=0, module_name=None): 1471 1472 """ 1473 Simplify the module stored in the file with the given 'filename'. 1474 1475 If the optional 'builtins' parameter is set to a true value (the default 1476 being a false value), then the module is considered as the builtins module. 1477 """ 1478 1479 simplifier = Simplifier(builtins) 1480 module = compiler.parseFile(filename) 1481 compiler.misc.set_filename(filename, module) 1482 if builtins: 1483 name = module_name or "__builtins__" 1484 else: 1485 path, ext = os.path.splitext(filename) 1486 path, name = os.path.split(path) 1487 name = module_name or name 1488 simplified = simplifier.process(module, name) 1489 return simplified 1490 1491 # vim: tabstop=4 expandtab shiftwidth=4