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