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