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