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