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, expr=InvokeFunction(not_, expr=LoadAttr(not_, expr=self.dispatch(not_.expr), name="__true__"), args=[], star=None, dstar=None)) 607 return result 608 609 # Operators. 610 611 def visitUnaryAdd(self, unaryadd): 612 return InvokeFunction(unaryadd, 1, expr=LoadAttr(unaryadd, expr=self.dispatch(unaryadd.expr), name="__pos__"), args=[], star=None, dstar=None) 613 614 def visitUnarySub(self, unarysub): 615 return InvokeFunction(unarysub, 1, expr=LoadAttr(unarysub, expr=self.dispatch(unarysub.expr), name="__neg__"), args=[], star=None, dstar=None) 616 617 def visitInvert(self, invert): 618 return InvokeFunction(invert, 1, expr=LoadAttr(invert, expr=self.dispatch(invert.expr), name="__invert__"), args=[], star=None, dstar=None) 619 620 def visitAdd(self, add): 621 622 """ 623 Emulate the current mechanisms by producing nodes as follows: 624 625 Try (body) -> x.__add__(y) 626 (else) 627 (handler) -> Conditional (test) -> isinstance(exc, TypeError) 628 (body) -> y.__radd__(x) 629 (else) 630 """ 631 632 result = Try(add, 1, 633 body=[ 634 InvokeFunction(add, 635 expr=LoadAttr(add, expr=self.dispatch(add.left), name="__add__"), 636 args=[self.dispatch(add.right)], 637 star=None, 638 dstar=None) 639 ], 640 else_=[], 641 finally_=[], 642 handler=[ 643 Conditional(add, 644 test=InvokeFunction(add, 645 expr=LoadName(add, name="isinstance"), 646 args=[LoadExc(add), LoadName(add, name="TypeError")], 647 star=None, 648 dstar=None), 649 body=[ 650 InvokeFunction(add, 651 expr=LoadAttr(add, expr=self.dispatch(add.right), name="__radd__"), 652 args=[self.dispatch(add.left)], 653 star=None, 654 dstar=None) 655 ], 656 else_=[] 657 ) 658 ] 659 ) 660 661 return result 662 663 # Assignments. 664 665 augassign_methods = { 666 "+=" : "__iadd__", "-=" : "__isub__", "*=" : "__imul__", "/=" : "__idiv__", 667 "%=" : "__imod__", "**=" : "__ipow__", "<<=" : "__ilshift__", ">>=" : "__irshift__", 668 "&=" : "__iand__", "^=" : "__ixor__", "|=" : "__ior__" 669 } 670 671 def visitAugAssign(self, augassign): 672 result = Assign(augassign, 1) 673 expr = self.dispatch(augassign.expr) 674 675 # Simple augmented assignment: name += expr 676 677 if isinstance(augassign.node, compiler.ast.Name): 678 result.code = [ 679 StoreTemp(augassign, 680 expr=InvokeFunction(augassign, 681 args=[expr], 682 star=None, 683 dstar=None, 684 expr=LoadAttr(augassign, 685 expr=self.dispatch(augassign.node), 686 name=self.augassign_methods[augassign.op] 687 ) 688 ) 689 ), 690 StoreName(augassign, 691 expr=LoadTemp(augassign), 692 name=augassign.node.name), 693 ReleaseTemp(augassign) 694 ] 695 696 # Complicated augmented assignment: lvalue.attr += expr 697 698 elif isinstance(augassign.node, compiler.ast.Getattr): 699 700 # <lvalue> -> setattr(<lvalue>, getattr(<lvalue>, "attr").__xxx__(expr)) 701 702 result.code = [ 703 StoreTemp(augassign, 704 index="expr", 705 expr=self.dispatch(augassign.node.expr) 706 ), 707 StoreTemp(augassign, 708 expr=InvokeFunction(augassign, 709 args=[expr], star=None, dstar=None, 710 expr=LoadAttr(augassign, 711 expr=LoadAttr(augassign, 712 expr=LoadTemp(augassign, index="expr"), 713 name=augassign.node.attrname 714 ), 715 name=self.augassign_methods[augassign.op] 716 ) 717 ) 718 ), 719 StoreAttr(augassign, 720 expr=LoadTemp(augassign), 721 lvalue=LoadTemp(augassign, index="expr"), 722 name=augassign.node.attrname 723 ), 724 ReleaseTemp(augassign, index="expr"), 725 ReleaseTemp(augassign) 726 ] 727 728 # Complicated augassign using slices: lvalue[lower:upper] += expr 729 730 elif isinstance(augassign.node, compiler.ast.Slice): 731 732 # <lvalue>, <lower>, <upper> -> <lvalue>.__setslice__(<lower>, <upper>, <lvalue>.__getslice__(<lower>, <upper>).__xxx__(expr)) 733 734 result.code = [ 735 StoreTemp(augassign, 736 index="expr", 737 expr=self.dispatch(augassign.node.expr) 738 ), 739 StoreTemp(augassign, 740 index="lower", 741 expr=self.dispatch_or_none(augassign.node.lower) 742 ), 743 StoreTemp(augassign, 744 index="upper", 745 expr=self.dispatch_or_none(augassign.node.upper) 746 ), 747 StoreTemp(augassign, 748 expr=InvokeFunction(augassign, 749 args=[expr], star=None, dstar=None, 750 expr=LoadAttr(augassign, 751 expr=self._visitSlice( 752 augassign.node, 753 LoadTemp(augassign, index="expr"), 754 LoadTemp(augassign, index="lower"), 755 LoadTemp(augassign, index="upper"), 756 "OP_APPLY"), 757 name=self.augassign_methods[augassign.op] 758 ) 759 ) 760 ), 761 self._visitSlice( 762 augassign.node, 763 LoadTemp(augassign, index="expr"), 764 LoadTemp(augassign, index="lower"), 765 LoadTemp(augassign, index="upper"), 766 "OP_ASSIGN", 767 LoadTemp(augassign) 768 ), 769 ReleaseTemp(augassign, index="expr"), 770 ReleaseTemp(augassign, index="lower"), 771 ReleaseTemp(augassign, index="upper"), 772 ReleaseTemp(augassign) 773 ] 774 775 # Complicated augassign using subscripts: lvalue[subs] += expr 776 777 elif isinstance(augassign.node, compiler.ast.Subscript): 778 779 # <lvalue>, <subs> -> <lvalue>.__setitem__(<subs>, <lvalue>.__getitem__(<subs>).__xxx__(expr)) 780 781 result.code = [ 782 StoreTemp(augassign, index="expr", expr=self.dispatch(augassign.node.expr)), 783 StoreTemp(augassign, index="subs", expr=self._visitSubscriptSubs(augassign.node, augassign.node.subs)), 784 StoreTemp(augassign, 785 expr=InvokeFunction(augassign, 786 args=[expr], star=None, dstar=None, 787 expr=LoadAttr(augassign, 788 expr=self._visitSubscript( 789 augassign.node, 790 LoadTemp(augassign, index="expr"), 791 LoadTemp(augassign, index="subs"), 792 "OP_APPLY" 793 ), 794 name=self.augassign_methods[augassign.op] 795 ) 796 ) 797 ), 798 self._visitSubscript( 799 augassign.node, 800 LoadTemp(augassign, index="expr"), 801 LoadTemp(augassign, index="subs"), 802 "OP_ASSIGN", 803 LoadTemp(augassign) 804 ), 805 ReleaseTemp(augassign, index="expr"), 806 ReleaseTemp(augassign, index="subs"), 807 ReleaseTemp(augassign) 808 ] 809 810 else: 811 raise NotImplementedError, augassign.node.__class__ 812 813 return result 814 815 def visitAssign(self, assign): 816 result = Assign(assign, 1) 817 store = StoreTemp(assign, expr=self.dispatch(assign.expr)) 818 release = ReleaseTemp(assign) 819 result.code = [store] + self.dispatches(assign.nodes, 0) + [release] 820 return result 821 822 def visitAssList(self, asslist, in_sequence=0): 823 if not in_sequence: 824 expr = LoadTemp(asslist) 825 else: 826 expr = InvokeFunction(asslist, expr=LoadAttr(asslist, expr=LoadTemp(asslist), name="next"), star=None, dstar=None, args=[]) 827 result = Assign(asslist, 1) 828 store = StoreTemp(asslist, expr=InvokeFunction(asslist, expr=LoadAttr(asslist, name="__iter__", expr=expr), star=None, dstar=None, args=[])) 829 release = ReleaseTemp(asslist) 830 result.code = [store] + self.dispatches(asslist.nodes, 1) + [release] 831 return result 832 833 visitAssTuple = visitAssList 834 835 def _visitAssNameOrAttr(self, node, in_sequence): 836 if not in_sequence: 837 return LoadTemp(node) 838 else: 839 return InvokeFunction(node, expr=LoadAttr(node, expr=LoadTemp(node), name="next"), star=None, dstar=None, args=[]) 840 841 def visitAssName(self, assname, in_sequence=0): 842 expr = self._visitAssNameOrAttr(assname, in_sequence) 843 result = StoreName(assname, 1, name=assname.name, expr=expr) 844 return result 845 846 def visitAssAttr(self, assattr, in_sequence=0): 847 expr = self._visitAssNameOrAttr(assattr, in_sequence) 848 lvalue = self.dispatch(assattr.expr) 849 result = StoreAttr(assattr, 1, name=assattr.attrname, lvalue=lvalue, expr=expr) 850 return result 851 852 def _visitSlice(self, slice, expr, lower, upper, flags, value=None): 853 if flags == "OP_ASSIGN": 854 args = [value] 855 result = InvokeFunction(slice, 1, expr=LoadAttr(slice, expr=expr, name="__setslice__"), star=None, dstar=None, args=[]) 856 elif flags == "OP_APPLY": 857 args = [] 858 result = InvokeFunction(slice, 1, expr=LoadAttr(slice, expr=expr, name="__getslice__"), star=None, dstar=None, args=[]) 859 elif flags == "OP_DELETE": 860 args = [] 861 result = InvokeFunction(slice, 1, expr=LoadAttr(slice, expr=expr, name="__delslice__"), star=None, dstar=None, args=[]) 862 else: 863 raise NotImplementedError, flags 864 865 # Add the dimensions. 866 867 args.insert(0, lower) 868 args.insert(1, upper) 869 870 result.original = slice 871 result.set_args(args) 872 return result 873 874 def visitSlice(self, slice, in_sequence=0): 875 return self._visitSlice(slice, self.dispatch(slice.expr), self.dispatch_or_none(slice.lower), 876 self.dispatch_or_none(slice.upper), slice.flags, self._visitAssNameOrAttr(slice, in_sequence)) 877 878 def _visitSubscript(self, subscript, expr, subs, flags, value=None): 879 if flags == "OP_ASSIGN": 880 args = [value] 881 result = InvokeFunction(subscript, 1, expr=LoadAttr(subscript, expr=expr, name="__setitem__"), star=None, dstar=None, args=[]) 882 elif flags == "OP_APPLY": 883 args = [] 884 result = InvokeFunction(subscript, 1, expr=LoadAttr(subscript, expr=expr, name="__getitem__"), star=None, dstar=None, args=[]) 885 elif flags == "OP_DELETE": 886 args = [] 887 result = InvokeFunction(subscript, 1, expr=LoadAttr(subscript, expr=expr, name="__delitem__"), star=None, dstar=None, args=[]) 888 else: 889 raise NotImplementedError, flags 890 891 # Add the dimensions. 892 893 args.insert(0, subs) 894 895 result.original = subscript 896 result.set_args(args) 897 return result 898 899 def _visitSubscriptSubs(self, node, subs): 900 if len(subs) == 1: 901 return self.dispatch(subs[0]) 902 else: 903 return InvokeFunction(node, 1, 904 expr=LoadName(node, name="tuple"), 905 args=self.dispatches(subs), 906 star=None, 907 dstar=None) 908 909 def visitSubscript(self, subscript, in_sequence=0): 910 return self._visitSubscript( 911 subscript, self.dispatch(subscript.expr), self._visitSubscriptSubs(subscript, subscript.subs), subscript.flags, 912 self._visitAssNameOrAttr(subscript, in_sequence) 913 ) 914 915 # Invocation and subprogram transformations. 916 917 def visitClass(self, class_): 918 structure = Class(class_, name=class_.name, bases=self.dispatches(class_.bases)) 919 self.structures.append(structure) 920 921 # Make a subprogram which initialises the class structure. 922 923 subprogram = Subprogram(class_, name=None, structure=structure, params=[], star=None, dstar=None) 924 self.current_subprograms.append(subprogram) 925 926 # The class is initialised using the code found inside. 927 928 subprogram.code = self.dispatch(class_.code) + [Return(class_)] 929 930 self.current_subprograms.pop() 931 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 932 933 # Make a definition of the class associating it with a name. 934 935 result = Assign(class_, 936 code=[ 937 StoreName(class_, 1, # defines the class 938 name=class_.name, 939 expr=LoadRef(class_, ref=structure) 940 ), 941 InvokeBlock(class_, 942 expr=LoadRef(class_, ref=subprogram) 943 ) 944 ] 945 ) 946 return result 947 948 def _visitFunction(self, function, subprogram): 949 950 """ 951 A common function generator which transforms the given 'function' node 952 and initialises the given 'subprogram' appropriately. 953 """ 954 955 # Discover star and dstar parameters. 956 957 if function.flags & 4 != 0: 958 has_star = 1 959 else: 960 has_star = 0 961 if function.flags & 8 != 0: 962 has_dstar = 1 963 else: 964 has_dstar = 0 965 966 # Discover the number of defaults and positional parameters. 967 968 ndefaults = len(function.defaults) 969 npositional = len(function.argnames) - has_star - has_dstar 970 971 # Produce star and dstar parameters with appropriate defaults. 972 973 if has_star: 974 star = ( 975 function.argnames[npositional], 976 InvokeFunction(function, expr=LoadName(function, name="list"), args=[], star=None, dstar=None) 977 ) 978 else: 979 star = None 980 if has_dstar: 981 dstar = ( 982 function.argnames[npositional + has_star], 983 InvokeFunction(function, expr=LoadName(function, name="dict"), args=[], star=None, dstar=None) 984 ) 985 else: 986 dstar = None 987 988 params = [] 989 for i in range(0, npositional - ndefaults): 990 params.append((function.argnames[i], None)) 991 992 # NOTE: Fix/process defaults. 993 994 for i in range(0, ndefaults): 995 default = function.defaults[i] 996 if default is not None: 997 params.append((function.argnames[npositional - ndefaults + i], self.dispatch(default))) 998 else: 999 params.append((function.argnames[npositional - ndefaults + i], default)) 1000 1001 subprogram.params = params 1002 subprogram.star = star 1003 subprogram.dstar = dstar 1004 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1005 1006 def visitFunction(self, function): 1007 1008 """ 1009 Make a subprogram for the 'function' and record it outside the main 1010 tree. Produce something like the following: 1011 1012 StoreName (name) 1013 (expr) -> Subprogram (params) 1014 (star) 1015 (dstar) 1016 """ 1017 1018 subprogram = Subprogram(function, name=function.name, acquire_locals=0, returns_value=1, star=None, dstar=None) 1019 self.current_subprograms.append(subprogram) 1020 subprogram.code = self.dispatch(function.code) + [Return(function)] 1021 self.current_subprograms.pop() 1022 self._visitFunction(function, subprogram) 1023 1024 # Make a definition of the function associating it with a name. 1025 1026 result = StoreName(function, 1, name=function.name, expr=LoadRef(function, ref=subprogram)) 1027 return result 1028 1029 def visitLambda(self, lambda_): 1030 1031 # Make a subprogram for the function and record it outside the main 1032 # tree. 1033 1034 subprogram = Subprogram(lambda_, name=None, acquire_locals=0, returns_value=1, star=None, dstar=None) 1035 self.current_subprograms.append(subprogram) 1036 subprogram.code = [Return(lambda_, expr=self.dispatch(lambda_.code))] 1037 self.current_subprograms.pop() 1038 self._visitFunction(lambda_, subprogram) 1039 1040 # Get the subprogram reference to the lambda. 1041 1042 return LoadRef(lambda_, 1, ref=subprogram) 1043 1044 def visitCallFunc(self, callfunc): 1045 result = InvokeFunction(callfunc, 1, star=None, dstar=None, args=self.dispatches(callfunc.args)) 1046 if callfunc.star_args is not None: 1047 result.star = self.dispatch(callfunc.star_args) 1048 if callfunc.dstar_args is not None: 1049 result.dstar = self.dispatch(callfunc.dstar_args) 1050 result.expr = self.dispatch(callfunc.node) 1051 return result 1052 1053 def visitWhile(self, while_): 1054 1055 """ 1056 Make a subprogram for the 'while' node and record its contents inside the 1057 subprogram. Convert... 1058 1059 While (test) -> (body) 1060 (else) 1061 1062 ...to: 1063 1064 Subprogram -> Conditional (test) -> (body) -> Invoke subprogram 1065 (else) -> Conditional (test) -> Return ... 1066 (else) -> ... 1067 """ 1068 1069 subprogram = Subprogram(while_, name=None, acquire_locals=1, returns_value=0, params=[], star=None, dstar=None) 1070 self.current_subprograms.append(subprogram) 1071 1072 # Include a conditional statement in the subprogram. 1073 1074 test = Conditional(while_, else_=[]) 1075 test.test = InvokeFunction(while_, expr=LoadAttr(while_, expr=self.dispatch(while_.test), name="__true__"), args=[], star=None, dstar=None) 1076 1077 # Inside the conditional, add a recursive invocation to the subprogram 1078 # if the test condition was satisfied. 1079 1080 continuation = InvokeBlock(while_) 1081 continuation.expr = LoadRef(while_, ref=subprogram) 1082 1083 # Return within the main section of the loop. 1084 1085 test.body = self.dispatch(while_.body) + [continuation, Return(while_)] 1086 1087 # Provide the else section, if present, along with an explicit return. 1088 1089 if while_.else_ is not None: 1090 test.else_ = self.dispatch(while_.else_) + [Return(while_)] 1091 1092 # Finish the subprogram definition. 1093 1094 subprogram.code = [test] 1095 1096 self.current_subprograms.pop() 1097 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1098 1099 # Make an invocation of the subprogram. 1100 1101 result = InvokeBlock(while_, 1) 1102 result.expr = LoadRef(while_, ref=subprogram) 1103 return result 1104 1105 def visitFor(self, for_): 1106 1107 """ 1108 Make a subprogram for the 'for_' node and record its contents inside the 1109 subprogram. Convert... 1110 1111 For (assign) 1112 (body) 1113 (else) 1114 1115 ...to: 1116 1117 Assign (assign #1) 1118 Invoke -> Subprogram -> Try (body) -> (assign #2) 1119 (body) 1120 Invoke subprogram 1121 (handler) -> ... 1122 (else) -> ... 1123 """ 1124 1125 subprogram = Subprogram(for_, name=None, acquire_locals=1, returns_value=0, params=[], star=None, dstar=None) 1126 self.current_subprograms.append(subprogram) 1127 1128 # Always return from conditional sections/subprograms. 1129 1130 if for_.else_ is not None: 1131 else_stmt = self.dispatch(for_.else_) + [Return(for_)] 1132 else: 1133 else_stmt = [Return(for_)] 1134 1135 # Wrap the assignment in a try...except statement. 1136 1137 try_except = Try(for_, body=[], else_=[], finally_=[]) 1138 test = Conditional(for_, 1139 test=InvokeFunction(for_, 1140 expr=LoadName(for_, name="isinstance"), 1141 args=[LoadExc(for_), LoadName(for_, name="StopIteration")], 1142 star=None, 1143 dstar=None), 1144 body=else_stmt, 1145 else_=[Raise(for_, expr=LoadExc(for_))]) 1146 try_except.handler = [test] 1147 1148 assign = Assign(for_, 1149 code=[ 1150 StoreTemp(for_, expr=InvokeFunction(for_, expr=LoadAttr(for_, expr=LoadTemp(for_), name="next"), args=[], star=None, dstar=None)), 1151 self.dispatch(for_.assign), 1152 ReleaseTemp(for_) 1153 ]) 1154 1155 # Inside the conditional, add a recursive invocation to the subprogram 1156 # if the test condition was satisfied. 1157 1158 continuation = InvokeBlock(for_) 1159 continuation.expr = LoadRef(for_, ref=subprogram) 1160 try_except.body = [assign] + self.dispatch(for_.body) + [continuation] 1161 subprogram.code = [try_except, Return(for_)] 1162 1163 # Finish the subprogram definition. 1164 1165 self.current_subprograms.pop() 1166 self.subprograms.append(subprogram); self.subnames[subprogram.full_name()] = subprogram 1167 1168 # Obtain an iterator for the sequence involved. 1169 # Then, make an invocation of the subprogram. 1170 1171 result = Assign(for_, 1) 1172 result.code = [ 1173 StoreTemp(for_, 1174 expr=InvokeFunction(for_, 1175 expr=LoadAttr(for_, 1176 name="__iter__", 1177 expr=self.dispatch(for_.list) 1178 ), 1179 args=[], 1180 star=None, 1181 dstar=None 1182 ) 1183 ), 1184 InvokeBlock(for_, expr=LoadRef(for_, ref=subprogram)), 1185 ReleaseTemp(for_) 1186 ] 1187 return result 1188 1189 # Exception node transformations. 1190 1191 def visitTryFinally(self, tryfinally): 1192 result = Try(tryfinally, 1, body=[], else_=[], finally_=[]) 1193 if tryfinally.body is not None: 1194 result.body = self.dispatch(tryfinally.body) 1195 if tryfinally.final is not None: 1196 result.finally_ = self.dispatch(tryfinally.final) 1197 return result 1198 1199 # Convenience functions. 1200 1201 def simplify(filename, builtins=0): 1202 1203 """ 1204 Simplify the module stored in the file with the given 'filename'. 1205 1206 If the optional 'builtins' parameter is set to a true value (the default 1207 being a false value), then the module is considered as the builtins module. 1208 """ 1209 1210 simplifier = Simplifier(builtins) 1211 module = compiler.parseFile(filename) 1212 compiler.misc.set_filename(filename, module) 1213 if builtins: 1214 name = "__builtins__" 1215 else: 1216 path, ext = os.path.splitext(filename) 1217 path, name = os.path.split(path) 1218 simplified = simplifier.process(module, name) 1219 return simplified 1220 1221 # vim: tabstop=4 expandtab shiftwidth=4