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