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