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