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