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