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