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