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