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.__bool__() ? 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="__bool__" 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 = self._visitNot( 455 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=self._visitNot(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="__bool__"), args=[], star=None, dstar=None) 538 test = Conditional(test=self._visitNot(LoadTemp()), 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="__bool__"), 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, expr, not_=None): 626 invocation = InvokeFunction( 627 expr=LoadAttr( 628 expr=expr, 629 name="__bool__" 630 ), 631 args=[], 632 star=None, 633 dstar=None 634 ) 635 if not_ is not None: 636 result = Not(not_, 1, expr=invocation) 637 else: 638 result = Not(expr=invocation) 639 return result 640 641 def visitNot(self, not_): 642 return self._visitNot(self.dispatch(not_.expr), not_) 643 644 # Operators. 645 646 def visitUnaryAdd(self, unaryadd): 647 return InvokeFunction(unaryadd, 1, 648 expr=LoadAttr( 649 expr=self.dispatch(unaryadd.expr), 650 name="__pos__" 651 ), 652 args=[], 653 star=None, 654 dstar=None 655 ) 656 657 def visitUnarySub(self, unarysub): 658 return InvokeFunction(unarysub, 1, 659 expr=LoadAttr( 660 expr=self.dispatch(unarysub.expr), 661 name="__neg__" 662 ), 663 args=[], 664 star=None, 665 dstar=None 666 ) 667 668 def visitInvert(self, invert): 669 return InvokeFunction(invert, 1, 670 expr=LoadAttr( 671 expr=self.dispatch(invert.expr), 672 name="__invert__" 673 ), 674 args=[], 675 star=None, 676 dstar=None 677 ) 678 679 def visitAdd(self, add): 680 681 """ 682 Emulate the current mechanisms by producing nodes as follows: 683 684 Try (body) -> x.__add__(y) 685 (else) 686 (handler) -> Conditional (test) -> isinstance(exc, TypeError) 687 (body) -> y.__radd__(x) 688 (else) 689 """ 690 691 result = Try(add, 1, 692 body=[ 693 InvokeFunction( 694 expr=LoadAttr(expr=self.dispatch(add.left), name="__add__"), 695 args=[self.dispatch(add.right)], 696 star=None, 697 dstar=None) 698 ], 699 else_=[], 700 finally_=[], 701 handler=[ 702 Conditional( 703 test=InvokeFunction( 704 expr=LoadName(name="isinstance"), 705 args=[LoadExc(), LoadName(name="TypeError")], 706 star=None, 707 dstar=None), 708 body=[ 709 InvokeFunction( 710 expr=LoadAttr(expr=self.dispatch(add.right), name="__radd__"), 711 args=[self.dispatch(add.left)], 712 star=None, 713 dstar=None) 714 ], 715 else_=[] 716 ) 717 ] 718 ) 719 720 return result 721 722 # Assignments. 723 724 augassign_methods = { 725 "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__", 726 "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__", 727 "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__" 728 } 729 730 def visitAugAssign(self, augassign): 731 732 """ 733 Convert the augmented assignment... 734 735 AugAssign (node) -> Name | Getattr | Slice | Subscript 736 (op) 737 (expr) 738 739 ...to: 740 741 Assign (code) -> StoreTemp (expr) -> InvokeFunction (expr) -> LoadAttr (expr) -> <name> 742 (name) -> <op> 743 StoreName (name) -> <name> 744 (expr) -> LoadTemp 745 ReleaseTemp 746 """ 747 748 result = Assign(augassign, 1) 749 expr = self.dispatch(augassign.expr) 750 751 # Simple augmented assignment: name += expr 752 753 if isinstance(augassign.node, compiler.ast.Name): 754 result.code = [ 755 StoreTemp( 756 expr=InvokeFunction( 757 args=[expr], 758 star=None, 759 dstar=None, 760 expr=LoadAttr( 761 expr=self.dispatch(augassign.node), 762 name=self.augassign_methods[augassign.op] 763 ) 764 ) 765 ), 766 StoreName( 767 expr=LoadTemp(), 768 name=augassign.node.name), 769 ReleaseTemp() 770 ] 771 772 # Complicated augmented assignment: lvalue.attr += expr 773 774 elif isinstance(augassign.node, compiler.ast.Getattr): 775 776 # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr)) 777 778 result.code = [ 779 StoreTemp( 780 index="expr", 781 expr=self.dispatch(augassign.node.expr) 782 ), 783 StoreTemp( 784 expr=InvokeFunction( 785 args=[expr], star=None, dstar=None, 786 expr=LoadAttr( 787 expr=LoadAttr(augassign.node, 1, 788 expr=LoadTemp(index="expr"), 789 name=augassign.node.attrname 790 ), 791 name=self.augassign_methods[augassign.op] 792 ) 793 ) 794 ), 795 StoreAttr( 796 expr=LoadTemp(), 797 lvalue=LoadTemp(index="expr"), 798 name=augassign.node.attrname 799 ), 800 ReleaseTemp(index="expr"), 801 ReleaseTemp() 802 ] 803 804 # Complicated augassign using slices: lvalue[lower:upper] += expr 805 806 elif isinstance(augassign.node, compiler.ast.Slice): 807 808 # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr)) 809 810 result.code = [ 811 StoreTemp( 812 index="expr", 813 expr=self.dispatch(augassign.node.expr) 814 ), 815 StoreTemp( 816 index="lower", 817 expr=self.dispatch_or_none(augassign.node.lower) 818 ), 819 StoreTemp( 820 index="upper", 821 expr=self.dispatch_or_none(augassign.node.upper) 822 ), 823 StoreTemp( 824 expr=InvokeFunction( 825 args=[expr], star=None, dstar=None, 826 expr=LoadAttr( 827 expr=self._visitSlice( 828 augassign.node, 829 LoadTemp(index="expr"), 830 LoadTemp(index="lower"), 831 LoadTemp(index="upper"), 832 "OP_APPLY"), 833 name=self.augassign_methods[augassign.op] 834 ) 835 ) 836 ), 837 self._visitSlice( 838 augassign.node, 839 LoadTemp(index="expr"), 840 LoadTemp(index="lower"), 841 LoadTemp(index="upper"), 842 "OP_ASSIGN", 843 LoadTemp() 844 ), 845 ReleaseTemp(index="expr"), 846 ReleaseTemp(index="lower"), 847 ReleaseTemp(index="upper"), 848 ReleaseTemp() 849 ] 850 851 # Complicated augassign using subscripts: lvalue[subs] += expr 852 853 elif isinstance(augassign.node, compiler.ast.Subscript): 854 855 # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr)) 856 857 result.code = [ 858 StoreTemp(index="expr", expr=self.dispatch(augassign.node.expr)), 859 StoreTemp(index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)), 860 StoreTemp( 861 expr=InvokeFunction( 862 args=[expr], star=None, dstar=None, 863 expr=LoadAttr( 864 expr=self._visitSubscript( 865 augassign.node, 866 LoadTemp(index="expr"), 867 LoadTemp(index="subs"), 868 "OP_APPLY" 869 ), 870 name=self.augassign_methods[augassign.op] 871 ) 872 ) 873 ), 874 self._visitSubscript( 875 augassign.node, 876 LoadTemp(index="expr"), 877 LoadTemp(index="subs"), 878 "OP_ASSIGN", 879 LoadTemp() 880 ), 881 ReleaseTemp(index="expr"), 882 ReleaseTemp(index="subs"), 883 ReleaseTemp() 884 ] 885 886 else: 887 raise NotImplementedError, augassign.node.__class__ 888 889 return result 890 891 def visitAssign(self, assign): 892 result = Assign(assign, 1) 893 store = StoreTemp(expr=self.dispatch(assign.expr)) 894 release = ReleaseTemp() 895 result.code = [store] + self.dispatches(assign.nodes, 0) + [release] 896 return result 897 898 def visitAssList(self, asslist, in_sequence=0): 899 if not in_sequence: 900 expr = LoadTemp() 901 else: 902 expr = InvokeFunction(expr=LoadAttr(expr=LoadTemp(), name="next"), star=None, dstar=None, args=[]) 903 result = Assign(asslist, 1) 904 store = StoreTemp(expr=InvokeFunction(expr=LoadAttr(name="__iter__", expr=expr), star=None, dstar=None, args=[])) 905 release = ReleaseTemp() 906 result.code = [store] + self.dispatches(asslist.nodes, 1) + [release] 907 return result 908 909 visitAssTuple = visitAssList 910 911 def _visitAssNameOrAttr(self, node, in_sequence): 912 if not in_sequence: 913 return LoadTemp() 914 else: 915 return InvokeFunction(expr=LoadAttr(expr=LoadTemp(), name="next"), star=None, dstar=None, args=[]) 916 917 def visitAssName(self, assname, in_sequence=0): 918 expr = self._visitAssNameOrAttr(assname, in_sequence) 919 result = StoreName(assname, 1, name=assname.name, expr=expr) 920 return result 921 922 def visitAssAttr(self, assattr, in_sequence=0): 923 expr = self._visitAssNameOrAttr(assattr, in_sequence) 924 lvalue = self.dispatch(assattr.expr) 925 result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr) 926 return result 927 928 def _visitSlice(self, slice, expr, lower, upper, flags, value=None): 929 if flags == "OP_ASSIGN": 930 result = InvokeFunction(slice, 1, 931 expr=LoadAttr( 932 expr=expr, 933 name="__setslice__" 934 ), 935 star=None, 936 dstar=None, 937 args=[lower, upper, value] 938 ) 939 elif flags == "OP_APPLY": 940 args = [] 941 result = InvokeFunction(slice, 1, 942 expr=LoadAttr( 943 expr=expr, 944 name="__getslice__" 945 ), 946 star=None, 947 dstar=None, 948 args=[lower, upper] 949 ) 950 elif flags == "OP_DELETE": 951 args = [] 952 result = InvokeFunction(slice, 1, 953 expr=LoadAttr( 954 expr=expr, 955 name="__delslice__" 956 ), 957 star=None, 958 dstar=None, 959 args=[lower, upper] 960 ) 961 else: 962 raise NotImplementedError, flags 963 964 return result 965 966 def visitSlice(self, slice, in_sequence=0): 967 return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower), 968 self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence)) 969 970 def _visitSubscript(self, subscript, expr, subs, flags, value=None): 971 if flags == "OP_ASSIGN": 972 result = InvokeFunction(subscript, 1, 973 expr=LoadAttr( 974 expr=expr, 975 name="__setitem__" 976 ), 977 star=None, 978 dstar=None, 979 args=[subs, value] 980 ) 981 elif flags == "OP_APPLY": 982 args = [] 983 result = InvokeFunction(subscript, 1, 984 expr=LoadAttr( 985 expr=expr, 986 name="__getitem__" 987 ), 988 star=None, 989 dstar=None, 990 args=[subs] 991 ) 992 elif flags == "OP_DELETE": 993 args = [] 994 result = InvokeFunction(subscript, 1, 995 expr=LoadAttr( 996 expr=expr, 997 name="__delitem__" 998 ), 999 star=None, 1000 dstar=None, 1001 args=[subs] 1002 ) 1003 else: 1004 raise NotImplementedError, flags 1005 1006 return result 1007 1008 def _visitSubscriptSubs(self, node, subs): 1009 if len(subs) == 1: 1010 return self.dispatch(subs[0]) 1011 else: 1012 return InvokeFunction(node, 1, 1013 expr=LoadName(name="tuple"), 1014 args=self.dispatches(subs), 1015 star=None, 1016 dstar=None 1017 ) 1018 1019 def visitSubscript(self, subscript, in_sequence=0): 1020 return self._visitSubscript( 1021 subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags, 1022 self._visitAssNameOrAttr(subscript, in_sequence) 1023 ) 1024 1025 # Invocation and subprogram transformations. 1026 1027 def visitClass(self, class_): 1028 1029 # Add "object" if the class is not "object" and has an empty bases list. 1030 1031 if class_.name != "object" and not class_.bases: 1032 bases = [compiler.ast.Name("object")] 1033 else: 1034 bases = class_.bases 1035 1036 structure = Class(name=class_.name, module=self.module, bases=self.dispatches(bases)) 1037 self.structures.append(structure) 1038 1039 # Make a subprogram which initialises the class structure. 1040 1041 subprogram = Subprogram(name=None, module=self.module, structure=structure, params=[], star=None, dstar=None) 1042 self.current_subprograms.append(subprogram) 1043 self.current_structures.append(structure) # mostly for name construction 1044 1045 # The class is initialised using the code found inside. 1046 1047 subprogram.code = self.dispatch(class_.code) + [Return()] 1048 1049 self.current_structures.pop() 1050 self.current_subprograms.pop() 1051 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1052 1053 # Make a definition of the class associating it with a name. 1054 1055 result = Assign( 1056 code=[ 1057 StoreName(class_, 1, # defines the class 1058 name=class_.name, 1059 expr=LoadRef(ref=structure) 1060 ), 1061 InvokeBlock( 1062 share_locals=0, 1063 expr=LoadRef(ref=subprogram) 1064 ) 1065 ] 1066 ) 1067 return result 1068 1069 def _visitFunction(self, function, subprogram): 1070 1071 """ 1072 A common function generator which transforms the given 'function' node 1073 and initialises the given 'subprogram' appropriately. 1074 """ 1075 1076 # Discover star and dstar parameters. 1077 1078 if function.flags & 4 != 0: 1079 has_star = 1 1080 else: 1081 has_star = 0 1082 if function.flags & 8 != 0: 1083 has_dstar = 1 1084 else: 1085 has_dstar = 0 1086 1087 # Discover the number of defaults and positional parameters. 1088 1089 ndefaults = len(function.defaults) 1090 npositional = len(function.argnames) - has_star - has_dstar 1091 1092 # Produce star and dstar parameters with appropriate defaults. 1093 1094 if has_star: 1095 star = ( 1096 function.argnames[npositional], 1097 InvokeFunction(expr=LoadName(name="list"), args=[], star=None, dstar=None) 1098 ) 1099 else: 1100 star = None 1101 if has_dstar: 1102 dstar = ( 1103 function.argnames[npositional + has_star], 1104 InvokeFunction(expr=LoadName(name="dict"), args=[], star=None, dstar=None) 1105 ) 1106 else: 1107 dstar = None 1108 1109 params = [] 1110 for i in range(0, npositional - ndefaults): 1111 params.append((function.argnames[i], None)) 1112 1113 # Process defaults. 1114 1115 for i in range(0, ndefaults): 1116 default = function.defaults[i] 1117 if default is not None: 1118 params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) 1119 else: 1120 params.append((function.argnames[npositional - ndefaults + i], None)) 1121 1122 subprogram.params = params 1123 subprogram.star = star 1124 subprogram.dstar = dstar 1125 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1126 1127 def visitFunction(self, function): 1128 1129 """ 1130 Make a subprogram for the 'function' and record it outside the main 1131 tree. Produce something like the following: 1132 1133 StoreName (name) 1134 (expr) -> Subprogram (params) 1135 (star) 1136 (dstar) 1137 """ 1138 1139 subprogram = Subprogram(name=function.name, module=self.module, structures=self.current_structures[:], 1140 internal=0, returns_value=1, star=None, dstar=None) 1141 1142 self.current_subprograms.append(subprogram) 1143 subprogram.code = self.dispatch(function.code) + [Return()] 1144 self.current_subprograms.pop() 1145 self._visitFunction(function, subprogram) 1146 1147 # Make a definition of the function associating it with a name. 1148 1149 result = StoreName(function, 1, name=function.name, expr=LoadRef(ref=subprogram)) 1150 return result 1151 1152 def visitLambda(self, lambda_): 1153 1154 # Make a subprogram for the function and record it outside the main 1155 # tree. 1156 1157 subprogram = Subprogram(name=None, module=self.module, internal=0, returns_value=1, star=None, dstar=None) 1158 self.current_subprograms.append(subprogram) 1159 subprogram.code = [Return(expr=self.dispatch(lambda_.code))] 1160 self.current_subprograms.pop() 1161 self._visitFunction(lambda_, subprogram) 1162 1163 # Get the subprogram reference to the lambda. 1164 1165 return LoadRef(lambda_, 1, ref=subprogram) 1166 1167 def visitCallFunc(self, callfunc): 1168 result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args)) 1169 if callfunc.star_args is not None: 1170 result.star = self.dispatch(callfunc.star_args) 1171 if callfunc.dstar_args is not None: 1172 result.dstar = self.dispatch(callfunc.dstar_args) 1173 result.expr = self.dispatch(callfunc.node) 1174 return result 1175 1176 def visitWhile(self, while_): 1177 1178 """ 1179 Make a subprogram for the 'while' node and record its contents inside the 1180 subprogram. Convert... 1181 1182 While (test) -> (body) 1183 (else) 1184 1185 ...to: 1186 1187 Subprogram -> Conditional (test) -> (body) -> Invoke subprogram 1188 (else) -> Conditional (test) -> Return ... 1189 (else) -> ... 1190 """ 1191 1192 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None) 1193 self.current_subprograms.append(subprogram) 1194 1195 # Include a conditional statement in the subprogram. 1196 1197 test = Conditional(else_=[]) 1198 test.test = InvokeFunction(expr=LoadAttr(expr=self.dispatch(while_.test), name="__bool__"), args=[], star=None, dstar=None) 1199 1200 # Inside the conditional, add a recursive invocation to the subprogram 1201 # if the test condition was satisfied. 1202 1203 continuation = InvokeBlock() 1204 continuation.expr = LoadRef(ref=subprogram) 1205 1206 # Return within the main section of the loop. 1207 1208 test.body = self.dispatch(while_.body) + [continuation, Return()] 1209 1210 # Provide the else section, if present, along with an explicit return. 1211 1212 if while_.else_ is not None: 1213 test.else_ = self.dispatch(while_.else_) + [Return()] 1214 1215 # Finish the subprogram definition. 1216 1217 subprogram.code = [test] 1218 1219 self.current_subprograms.pop() 1220 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1221 1222 # Make an invocation of the subprogram. 1223 1224 result = InvokeBlock(while_, 1) 1225 result.expr = LoadRef(ref=subprogram) 1226 return result 1227 1228 def visitFor(self, for_): 1229 1230 """ 1231 Make a subprogram for the 'for_' node and record its contents inside the 1232 subprogram. Convert... 1233 1234 For (assign) 1235 (body) 1236 (else) 1237 1238 ...to: 1239 1240 Assign (assign #1) 1241 Invoke -> Subprogram -> Try (body) -> (assign #2) 1242 (body) 1243 Invoke subprogram 1244 (handler) -> ... 1245 (else) -> ... 1246 """ 1247 1248 subprogram = Subprogram(name=None, module=self.module, internal=1, returns_value=0, params=[], star=None, dstar=None) 1249 self.current_subprograms.append(subprogram) 1250 1251 # Always return from conditional sections/subprograms. 1252 1253 if for_.else_ is not None: 1254 else_stmt = self.dispatch(for_.else_) + [Return()] 1255 else: 1256 else_stmt = [Return()] 1257 1258 # Wrap the assignment in a try...except statement. 1259 1260 try_except = Try(body=[], else_=[], finally_=[]) 1261 test = Conditional( 1262 test=InvokeFunction( 1263 expr=LoadName(name="isinstance"), 1264 args=[LoadExc(), LoadName(name="StopIteration")], 1265 star=None, 1266 dstar=None), 1267 body=else_stmt, 1268 else_=[Raise(expr=LoadExc())]) 1269 try_except.handler = [test] 1270 1271 assign = Assign( 1272 code=[ 1273 StoreTemp(expr=InvokeFunction(expr=LoadAttr(expr=LoadTemp(), name="next"), args=[], star=None, dstar=None)), 1274 self.dispatch(for_.assign), 1275 ReleaseTemp() 1276 ]) 1277 1278 # Inside the conditional, add a recursive invocation to the subprogram 1279 # if the test condition was satisfied. 1280 1281 continuation = InvokeBlock( 1282 expr=LoadRef(ref=subprogram) 1283 ) 1284 try_except.body = [assign] + self.dispatch(for_.body) + [continuation] 1285 subprogram.code = [try_except, Return()] 1286 1287 # Finish the subprogram definition. 1288 1289 self.current_subprograms.pop() 1290 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1291 1292 # Obtain an iterator for the sequence involved. 1293 # Then, make an invocation of the subprogram. 1294 1295 result = Assign(for_, 1) 1296 result.code = [ 1297 StoreTemp( 1298 expr=InvokeFunction( 1299 expr=LoadAttr( 1300 name="__iter__", 1301 expr=self.dispatch(for_.list) 1302 ), 1303 args=[], 1304 star=None, 1305 dstar=None 1306 ) 1307 ), 1308 InvokeBlock(expr=LoadRef(ref=subprogram)), 1309 ReleaseTemp() 1310 ] 1311 return result 1312 1313 # Exception node transformations. 1314 1315 def visitTryFinally(self, tryfinally): 1316 result = Try(tryfinally, 1, body=[], else_=[], finally_=[]) 1317 if tryfinally.body is not None: 1318 result.body = self.dispatch(tryfinally.body) 1319 if tryfinally.final is not None: 1320 result.finally_ = self.dispatch(tryfinally.final) 1321 return result 1322 1323 # Convenience functions. 1324 1325 def simplify(filename, builtins=0): 1326 1327 """ 1328 Simplify the module stored in the file with the given 'filename'. 1329 1330 If the optional 'builtins' parameter is set to a true value (the default 1331 being a false value), then the module is considered as the builtins module. 1332 """ 1333 1334 simplifier = Simplifier(builtins) 1335 module = compiler.parseFile(filename) 1336 compiler.misc.set_filename(filename, module) 1337 if builtins: 1338 name = "__builtins__" 1339 else: 1340 path, ext = os.path.splitext(filename) 1341 path, name = os.path.split(path) 1342 simplified = simplifier.process(module, name) 1343 return simplified 1344 1345 # vim: tabstop=4 expandtab shiftwidth=4