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