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