1 #!/usr/bin/env python 2 3 """ 4 Translate the AST of a Python program into a more interpretable representation. 5 6 Copyright (C) 2007, 2008 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 import micropython.inspect 23 from micropython.rsvp import * 24 from micropython.common import * 25 import compiler.ast 26 from compiler.visitor import ASTVisitor 27 try: 28 set 29 except NameError: 30 from sets import Set as set 31 32 class TranslateError(NodeProcessingError): pass 33 34 class Label: 35 36 "A reference to a location." 37 38 def __init__(self, number, location=None): 39 self.number = number 40 self.location = location 41 42 def __repr__(self): 43 return "Label(%r, location=%r)" % (self.number, self.location) 44 45 # Program visitors. 46 47 class Translation(ASTVisitor): 48 49 "A translated module." 50 51 supported_optimisations = ["constant_storage", "known_target", "self_access", "temp_storage", "constant_test"] 52 53 def __init__(self, module, importer, optimisations=None): 54 55 """ 56 Initialise the translation with an inspected 'module', the 'importer' 57 and optional 'optimisations'. See the 'supported_optimisations' 58 attribute of this class for permitted values. 59 """ 60 61 ASTVisitor.__init__(self) 62 self.visitor = self 63 self.module = module 64 65 # Global program dependencies. 66 67 self.importer = importer 68 self.objtable = self.importer.get_object_table() 69 self.paramtable = self.importer.get_parameter_table() 70 self.builtins = self.importer.modules.get("__builtins__") 71 72 # Desired optimisations. 73 74 self.optimisations = set(optimisations or []) 75 76 # The current unit being translated. 77 78 self.unit = None 79 80 # Wiring within the code. 81 82 self.labels = {} 83 self.label_number = 0 84 self.loop_labels = [] 85 self.exception_labels = [] 86 87 # The code itself. This is limited to the code for a particular block 88 # being processed. 89 90 self.code = None 91 self.temp_position = 0 92 93 def calculate_stack_usage(self): 94 max_stack_usage = 0 95 stack_usage = 0 96 97 for op in self.code: 98 stack_usage += op.stack_usage 99 max_stack_usage = max(max_stack_usage, stack_usage) 100 101 self.unit.stack_usage = max_stack_usage 102 103 def get_module_code(self): 104 105 "Return the top-level module code." 106 107 self.unit = self.module 108 self.code = [] 109 self.temp_position = self.unit.stack_local_usage 110 111 if self.module.module is not None: 112 self.dispatch(self.module.module) 113 114 self.calculate_stack_usage() 115 return self.code 116 117 def get_code(self, unit): 118 119 "Return the code for the given 'unit'." 120 121 self.unit = unit 122 self.code = [] 123 self.temp_position = self.unit.stack_local_usage 124 125 if unit.node is not None: 126 self.dispatch(unit.node) 127 128 self.calculate_stack_usage() 129 return self.code 130 131 def __repr__(self): 132 return "Translation(%r)" % self.module 133 134 def get_scope(self, name): 135 if self.unit.has_key(name): 136 return "local" 137 elif self.module.has_key(name): 138 return "global" 139 else: 140 return "builtins" 141 142 # Code writing methods. 143 144 def new_label(self): 145 146 "Return a new label object for use with set_label." 147 148 number = self.label_number 149 label = Label(number) 150 self.labels[label] = label 151 self.label_number += 1 152 return label 153 154 def set_label(self, label): 155 156 """ 157 Set the location of 'label' to that within the entire image: the 158 location within the code combined with location of the code unit. 159 """ 160 161 label.location = len(self.code) + self.unit.code_location 162 163 def get_loop_labels(self): 164 return self.loop_labels[-1] 165 166 def add_loop_labels(self, next_label, exit_label): 167 self.loop_labels.append((next_label, exit_label)) 168 169 def drop_loop_labels(self): 170 self.loop_labels.pop() 171 172 def get_exception_labels(self): 173 return self.exception_labels[-1] 174 175 def add_exception_labels(self, handler_label, exit_label): 176 self.exception_labels.append((handler_label, exit_label)) 177 178 def drop_exception_labels(self): 179 self.exception_labels.pop() 180 181 def reserve_temp(self, n): 182 temp_position = self.temp_position 183 self.temp_position += n 184 self.unit.stack_temp_usage = max(self.unit.stack_temp_usage, self.temp_position) 185 return temp_position 186 187 def discard_temp(self, instructions): 188 for temp in instructions: 189 if isinstance(temp, LoadTemp): 190 self.temp_position -= 1 191 192 def new_op(self, op): 193 194 "Add 'op' to the generated code." 195 196 self.code.append(op) 197 198 def replace_op(self, op): 199 200 "Replace the last added instruction with 'op'." 201 202 self.code[-1] = op 203 204 def remove_ops(self, n): 205 206 "Remove the last 'n' instructions." 207 208 del self.code[-n:] 209 210 def last_ops(self, n): 211 212 "Return the last 'n' added instructions in reverse chronological order." 213 214 ops = self.code[-n:] 215 ops.reverse() 216 return ops 217 218 def last_op(self): 219 220 "Return the last added instruction." 221 222 try: 223 return self.code[-1] 224 except IndexError: 225 return None 226 227 # Internal helper methods. 228 229 def _visitAttr(self, node, classes): 230 231 """ 232 Visit the attribute-related 'node', generating instructions based on the 233 given 'classes'. 234 """ 235 236 self.dispatch(node.expr) 237 self._generateAttr(node, node.attrname, classes) 238 239 def _generateAttr(self, node, attrname, classes): 240 241 """ 242 Generate code for the access to 'attrname' using the given 'classes'. 243 """ 244 245 AddressInstruction, AttrInstruction, AttrIndexInstruction = classes 246 247 last = self.last_op() 248 249 # Where the last operation (defining the attribute owner) yields a 250 # constant... 251 252 if self._have_constant_input(0): 253 254 # Optimise away the constant storage if appropriate. 255 256 if self._optimise_constant_storage(AddressInstruction, 1): 257 return 258 259 # Get the details of the access. 260 261 target = last.attr.value 262 263 if isinstance(target, micropython.inspect.Const): 264 target_name = target.value_type_name() 265 else: 266 target_name = target.full_name() 267 268 # Access the object table to get the attribute position. 269 270 try: 271 table_entry = self.objtable.table[target_name] 272 except KeyError: 273 raise TranslateError(self.module.full_name(), node, 274 "No object entry exists for target %r." % target_name) 275 276 try: 277 pos = table_entry[attrname] 278 except KeyError: 279 raise TranslateError(self.module.full_name(), node, 280 "No attribute entry exists for name %r in target %r." % (attrname, target_name)) 281 282 # Produce a suitable instruction. 283 284 self.replace_op(AddressInstruction(pos)) 285 286 # Where the last operation involves the special 'self' name, check to 287 # see if the attribute is acceptably positioned and produce a direct 288 # access to the attribute. 289 290 elif self._optimise_self_access(attrname, AttrInstruction): 291 pass 292 293 # Otherwise, perform a normal operation. 294 295 else: 296 try: 297 index = self.objtable.get_index(attrname) 298 except self.objtable.TableError: 299 raise TranslateError(self.module.full_name(), node, 300 "No attribute entry exists for name %r." % attrname) 301 302 self.new_op(AttrIndexInstruction(index)) 303 304 def _startCallFunc(self): 305 306 "Record the location of the invocation." 307 308 self.new_op(MakeFrame()) # records the start of the frame 309 310 def _generateCallFunc(self, args, node): 311 312 # NOTE: Only simple cases are used for optimisations. 313 314 t = self._optimise_known_target() 315 if t: 316 target, context = t 317 else: 318 target, context = None, None 319 320 # Store the target in temporary storage for subsequent referencing. 321 322 temp = self._optimise_temp_storage() 323 324 # Where a target is known and has a known context, avoid generating any 325 # first argument. Instance methods do not have a known target since they 326 # are accessed via an instance whose identity cannot generally be known 327 # at compile-time. 328 329 if context is None: 330 continue_label = self.new_label() 331 self.new_op(LoadContext()) 332 self.new_op(CheckContext()) 333 self.new_op(JumpIfTrue(continue_label)) 334 self.dispatch(compiler.ast.Name("TypeError")) 335 self.new_op(RaiseException()) 336 self.set_label(continue_label) 337 else: 338 pass # NOTE: Class methods should be supported. 339 340 # Evaluate the arguments. 341 342 employed_positions = set() 343 extra_keywords = [] 344 345 for frame_pos, arg in enumerate(args): 346 347 # Handle positional and keyword arguments separately. 348 349 if isinstance(arg, compiler.ast.Keyword): 350 351 # Optimise where the target is known now. 352 353 if target is not None: 354 355 # Find the parameter table entry for the target. 356 357 target_name = target.full_name() 358 359 # Look for a callable with the precise target name. 360 361 table_entry = self.paramtable.table[target_name] 362 363 # Look the name up in the parameter table entry. 364 365 try: 366 pos = table_entry[arg.name] 367 368 # Where no position is found, this could be an extra keyword 369 # argument. 370 371 except KeyError: 372 extra_keywords.append(arg) 373 continue 374 375 # Test for illegal conditions. 376 377 if pos in employed_positions: 378 raise TranslateError(self.module.full_name(), node, 379 "Keyword argument %r overwrites parameter %r." % (arg.name, pos)) 380 381 employed_positions.add(pos) 382 383 # Add space for arguments appearing before this one. 384 385 if frame_pos < pos: 386 self.new_op(ReserveFrame(pos - frame_pos)) 387 388 # Generate code for the keyword and the positioning 389 # operation. 390 391 self.dispatch(arg.expr) 392 393 # If the position corresponds to the current frame element, 394 # skip generating the store instruction. 395 396 if frame_pos > pos: 397 self.new_op(StoreFrame(pos)) 398 399 # Otherwise, generate the code needed to obtain the details of 400 # the parameter location. 401 402 else: 403 404 # Combine the target details with the name to get the location. 405 # See the access method on the List class. 406 407 try: 408 paramindex = self.paramtable.get_index(arg.name) 409 410 # Where no position is found, this could be an extra keyword 411 # argument. 412 413 except self.paramtable.TableError: 414 extra_keywords.append(arg) 415 continue 416 417 # Generate code for the keyword and the positioning 418 # operation. 419 420 self.dispatch(arg.expr) 421 self.new_op(StoreFrameIndex(paramindex)) 422 423 # use (callable+0)+paramindex+table 424 # checks embedded offset against (callable+0) 425 # moves the top of stack to frame+position 426 427 else: 428 self.dispatch(arg) 429 employed_positions.add(frame_pos) 430 431 frame_pos = len(args) 432 433 # NOTE: Extra keywords are not supported. 434 # NOTE: Somehow, the above needs to be combined with * arguments. 435 436 # Either test for a complete set of arguments. 437 438 if target is not None: 439 440 # Make sure that enough arguments have been given. 441 442 nargs_max = len(target.positional_names) 443 ndefaults = len(target.defaults) 444 nargs_min = nargs_max - ndefaults 445 446 for i in range(0, nargs_min): 447 if i not in employed_positions: 448 raise TranslateError(self.module.full_name(), node, 449 "Argument %r not supplied for %r: need at least %d arguments." % (i+1, target.name, nargs_min)) 450 451 nargs = len(args) 452 453 if nargs > nargs_max and not target.has_star and not target.has_dstar: 454 raise TranslateError(self.module.full_name(), node, 455 "Too many arguments for %r: need at most %d arguments." % (target.name, nargs_max)) 456 457 # Where defaults are involved, put them into the frame. 458 # Here, we use negative index values to visit the right hand end of 459 # the defaults list. 460 461 for pos in range(nargs_min, nargs_max): 462 if pos not in employed_positions: 463 self.new_op(LoadConst(target)) 464 self.new_op(LoadAttr(target.default_attrs[pos - nargs_min])) 465 466 # If the position corresponds to the current frame element, 467 # skip generating the instruction. 468 469 if frame_pos != pos: 470 self.new_op(StoreFrame(pos)) 471 472 frame_pos += 1 473 474 # Or generate instructions to do this at run-time. 475 # NOTE: CheckFrame has to check the number of arguments and to fill in 476 # NOTE: defaults. 477 478 else: 479 self.new_op(CheckFrame()) 480 481 return temp 482 483 def _endCallFunc(self, temp): 484 485 "Make the invocation and tidy up afterwards." 486 487 self.new_op(temp) 488 self.new_op(JumpWithFrame()) 489 490 # NOTE: Exception handling required. 491 492 self.new_op(DropFrame()) 493 self.discard_temp([temp]) 494 495 def _visitName(self, node, classes): 496 497 """ 498 Visit the name-related 'node', generating instructions based on the 499 given 'classes'. 500 """ 501 502 name = node.name 503 scope = self.get_scope(name) 504 #print self.module.name, node.lineno, name, scope 505 self._generateName(name, scope, classes, node) 506 507 def _generateName(self, name, scope, classes, node): 508 509 """ 510 Generate code for the access to 'name' in 'scope' using the given 511 'classes', and using the given 'node' as the source of the access. 512 """ 513 514 NameInstruction, AddressInstruction = classes 515 516 if self._optimise_constant_storage(NameInstruction, 0): 517 return 518 519 if scope == "local": 520 unit = self.unit 521 if isinstance(unit, micropython.inspect.Function): 522 self.new_op(NameInstruction(unit.all_locals()[name])) 523 elif isinstance(unit, micropython.inspect.Class): 524 self.new_op(AddressInstruction(unit.all_class_attributes()[name])) 525 elif isinstance(unit, micropython.inspect.Module): 526 self.new_op(AddressInstruction(unit.module_attributes()[name])) 527 else: 528 raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r." % (unit, name)) 529 530 elif scope == "global": 531 globals = self.module.module_attributes() 532 if globals.has_key(name): 533 self.new_op(AddressInstruction(globals[name])) 534 else: 535 raise TranslateError(self.module.full_name(), node, "Module %r has no attribute %r." % (self.module, name)) 536 537 else: 538 self.new_op(AddressInstruction(self._get_builtin(name, node))) 539 540 def _get_builtin(self, name, node): 541 if self.builtins is not None: 542 try: 543 return self.builtins[name] 544 except KeyError: 545 raise TranslateError(self.module.full_name(), node, "No __builtins__ definition is available for name %r." % name) 546 else: 547 raise TranslateError(self.module.full_name(), node, "No __builtins__ module is available for name %r." % name) 548 549 # Optimisation tests. 550 551 def _should_optimise_constant_storage(self): 552 return "constant_storage" in self.optimisations 553 554 def _should_optimise_constant_test(self): 555 return "constant_test" in self.optimisations 556 557 def _should_optimise_known_target(self): 558 return "known_target" in self.optimisations 559 560 def _should_optimise_self_access(self): 561 return "self_access" in self.optimisations 562 563 def _should_optimise_temp_storage(self): 564 return "temp_storage" in self.optimisations 565 566 def _have_constant_input(self, n): 567 last = self.last_ops(n+1) 568 return len(last) > n and (isinstance(last[n], LoadAddress) and last[n].attr.assignments == 1 or 569 isinstance(last[n], LoadConst)) # and not isinstance(last[n].attr, micropython.inspect.Instance) 570 571 def _have_known_target(self): 572 return self._have_constant_input(0) 573 574 def _have_self_input(self): 575 last = self.last_op() 576 return isinstance(self.unit, micropython.inspect.Function) and \ 577 self.unit.is_method() and isinstance(last, LoadName) and \ 578 last.attr.name == "self" 579 580 def _have_temp_compatible_access(self): 581 last = self.last_op() 582 # NOTE: Should expand to cover LoadAttr and LoadAttrIndex, but this 583 # NOTE: would require inspection of the stack operands. 584 return isinstance(last, (LoadName, LoadTemp, LoadAddress, LoadConst)) 585 586 # Optimisation methods. See the supported_optimisations class attribute. 587 588 def _optimise_temp_storage(self): 589 590 """ 591 Where the next operation would involve storing a value into temporary 592 storage at 'temp_position', record and remove any simple instruction 593 which produced the value to be stored such that instead of subsequently 594 accessing the temporary storage, that instruction is substituted. 595 596 If no optimisation can be achieved, a StoreTemp instruction is produced 597 and the appropriate LoadTemp instruction is returned. 598 """ 599 600 if self._should_optimise_temp_storage() and \ 601 self._have_temp_compatible_access(): 602 603 last = self.last_op() 604 self.remove_ops(1) 605 return last 606 else: 607 temp_position = self.reserve_temp(1) 608 self.new_op(StoreTemp(temp_position)) 609 return LoadTemp(temp_position) 610 611 def _optimise_constant_storage(self, instruction, n): 612 613 """ 614 Where this operation should store a constant into a target which is 615 also constant, optimise away both operations. 616 """ 617 618 if self._should_optimise_constant_storage() and \ 619 instruction in (StoreAddress, StoreName) and \ 620 self._have_constant_input(n) and \ 621 (n == 0 or self._have_constant_input(n-1)): 622 623 self.remove_ops(n+1) 624 return 1 625 else: 626 return 0 627 628 def _optimise_constant_test(self, instruction): 629 630 """ 631 Where this operation tests the topmost stack value which happens to be 632 a constant against another stack value, merge the last instruction which 633 loaded the constant into the current 'instruction'. 634 """ 635 636 if self._should_optimise_constant_test() and \ 637 instruction is TestIdentity and \ 638 self._have_constant_input(0): 639 640 last = self.last_op() 641 self.replace_op(TestIdentityAddress(last.attr)) 642 return 1 643 else: 644 return 0 645 646 def _optimise_known_target(self): 647 648 """ 649 Where the target of an invocation is known, provide information about it 650 and its context. If a class is being invoked and the conditions are 651 appropriate, get information about the specific initialiser. 652 """ 653 654 if self._should_optimise_known_target() and self._have_known_target(): 655 last = self.last_op() 656 target = last.attr.value 657 context = last.attr.parent 658 659 # Handle calls to classes. 660 661 if isinstance(target, micropython.inspect.Class): 662 target = target.get_instantiator() 663 context = micropython.inspect.Instance() 664 665 # A special context is chosen to avoid generating unnecessary 666 # context loading and checking instructions. 667 668 return target, context 669 else: 670 return None 671 672 def _optimise_self_access(self, attrname, instruction): 673 674 """ 675 Where the provided 'attrname' accesses an attribute which occupies the 676 same position in all possible objects which can be accessed, generate an 677 'instruction' accessing the attribute directly. 678 """ 679 680 if self._should_optimise_self_access() and self._have_self_input() and \ 681 not self.unit.is_relocated(attrname): 682 683 attr = self.unit.parent.all_attributes()[attrname] 684 self.new_op(instruction(attr)) 685 return 1 686 else: 687 return 0 688 689 # Visitor methods. 690 691 def default(self, node, *args): 692 raise TranslateError(self.module.full_name(), node, "Node class %r is not supported." % node.__class__) 693 694 def dispatch(self, node, *args): 695 return ASTVisitor.dispatch(self, node, *args) 696 697 def _visitBinary(self, node, left_method, right_method): 698 699 """ 700 _t1 = node.left 701 _t2 = node.right 702 try: 703 _result = _t1.__add__(_t2) 704 if _result is NotImplemented: 705 raise AttributeError 706 except AttributeError: 707 try: 708 _result = _t2.__radd__(_t1) 709 if _result is NotImplemented: 710 raise AttributeError 711 except AttributeError: 712 raise TypeError 713 """ 714 715 end_left_label = self.new_label() 716 right_label = self.new_label() 717 type_error_label = self.new_label() 718 end_label = self.new_label() 719 720 # Evaluate and store the left operand in temporary storage. 721 722 self.dispatch(node.left) 723 temp1 = self._optimise_temp_storage() 724 725 # Evaluate and store the right operand in temporary storage. 726 727 self.dispatch(node.right) 728 temp2 = self._optimise_temp_storage() 729 730 # Left method. 731 732 self._startCallFunc() 733 self.new_op(temp1) 734 735 # Get left method on temp1. 736 737 self._generateAttr(node, left_method, (LoadAddress, LoadAttr, LoadAttrIndex)) 738 temp_method = self._optimise_temp_storage() 739 740 if not self._optimise_known_target(): 741 self.dispatch(compiler.ast.Name("AttributeError")) 742 self.new_op(CheckException()) 743 self.new_op(JumpIfTrue(end_left_label)) 744 745 # Add arguments. 746 747 self.new_op(temp1) # Explicit context as first argument. 748 self.new_op(temp2) 749 self._endCallFunc(temp_method) 750 751 # Test for NotImplemented. 752 # Don't actually raise an exception. 753 754 self.dispatch(compiler.ast.Name("NotImplemented")) 755 if not self._optimise_constant_test(TestIdentity): 756 self.new_op(TestIdentity()) 757 self.new_op(JumpIfTrue(right_label)) 758 self.new_op(Jump(end_label)) 759 760 # End left method attempt. 761 762 self.set_label(end_left_label) 763 self.new_op(DropFrame()) # From the left method call. 764 765 # Right method. 766 767 self.set_label(right_label) 768 self._startCallFunc() 769 self.new_op(temp2) 770 771 # Get right method on temp2. 772 773 self._generateAttr(node, right_method, (LoadAddress, LoadAttr, LoadAttrIndex)) 774 temp_method = self._optimise_temp_storage() 775 776 if not self._optimise_known_target(): 777 self.dispatch(compiler.ast.Name("AttributeError")) 778 self.new_op(CheckException()) 779 self.new_op(JumpIfTrue(type_error_label)) 780 781 # Add arguments. 782 783 self.new_op(temp2) # Explicit context as first argument. 784 self.new_op(temp1) 785 self._endCallFunc(temp_method) 786 787 # Test for NotImplemented. 788 # Don't actually raise an exception. 789 790 self.dispatch(compiler.ast.Name("NotImplemented")) 791 if not self._optimise_constant_test(TestIdentity): 792 self.new_op(TestIdentity()) 793 self.new_op(JumpIfTrue(type_error_label)) 794 self.new_op(Jump(end_label)) 795 796 # Raise a TypeError. 797 798 self.set_label(type_error_label) 799 self.dispatch(compiler.ast.Name("TypeError")) 800 self.new_op(RaiseException()) 801 802 self.set_label(end_label) 803 804 # Compilation duties... 805 # NOTE: Potentially remove this when optimised away. 806 807 self.discard_temp([temp1, temp2]) 808 809 def visitAdd(self, node): 810 self._visitBinary(node, "__add__", "__radd__") 811 812 def visitAnd(self, node): pass 813 814 def visitAssert(self, node): pass 815 816 def visitAssign(self, node): 817 self.dispatch(node.expr) 818 for n in node.nodes: 819 self.dispatch(n) 820 821 def visitAssAttr(self, node): 822 self._visitAttr(node, (StoreAddress, StoreAttr, StoreAttrIndex)) 823 824 def visitAssList(self, node): pass 825 826 def visitAssName(self, node): 827 self._visitName(node, (StoreName, StoreAddress)) 828 829 visitAssTuple = visitAssList 830 831 def visitAugAssign(self, node): pass 832 833 def visitBackquote(self, node): pass 834 835 def visitBitand(self, node): pass 836 837 def visitBitor(self, node): pass 838 839 def visitBitxor(self, node): pass 840 841 def visitBreak(self, node): 842 next_label, exit_label = self.get_loop_labels() 843 self.new_op(Jump(exit_label)) 844 845 def visitCallFunc(self, node): 846 847 """ 848 Evaluate positional arguments, evaluate and store keyword arguments in 849 the correct location, then invoke the function. 850 """ 851 852 # Mark the frame, evaluate the target, generate the call. 853 854 self._startCallFunc() 855 self.dispatch(node.node) 856 temp = self._generateCallFunc(node.args, node) 857 self._endCallFunc(temp) 858 859 def visitClass(self, node): 860 unit = self.unit 861 self.unit = node.unit 862 self.unit.code_location = self.module.code_location # class body code is not independently addressable 863 self.dispatch(node.code) 864 self.unit = unit 865 866 def visitCompare(self, node): 867 868 """ 869 self.dispatch(node.expr) 870 for op_name, next_node in compare.ops: 871 methods = self.comparison_methods[op_name] 872 if methods is not None: 873 # Generate method call using evaluated argument and next node. 874 else: 875 # Deal with the special operators. 876 # Provide short-circuiting. 877 """ 878 879 def visitConst(self, node): 880 const = self.module.constant_values[node.value] 881 self.new_op(LoadConst(const)) 882 883 def visitContinue(self, node): 884 next_label, exit_label = self.get_loop_labels() 885 self.new_op(Jump(next_label)) 886 887 def visitDecorators(self, node): pass 888 889 def visitDict(self, node): pass 890 891 def visitDiscard(self, node): 892 self.dispatch(node.expr) 893 894 def visitDiv(self, node): 895 self._visitBinary(node, "__div__", "__rdiv__") 896 897 def visitEllipsis(self, node): pass 898 899 def visitExec(self, node): pass 900 901 def visitExpression(self, node): pass 902 903 def visitFloorDiv(self, node): 904 self._visitBinary(node, "__floordiv__", "__rfloordiv__") 905 906 def visitFor(self, node): 907 exit_label = self.new_label() 908 next_label = self.new_label() 909 else_label = self.new_label() 910 911 # Get the "list" to be iterated over, obtain its iterator. 912 913 self._startCallFunc() 914 self.dispatch(node.list) 915 self._generateAttr(node, "__iter__", (LoadAddress, LoadAttr, LoadAttrIndex)) 916 temp = self._generateCallFunc([], node) 917 self._endCallFunc(temp) 918 919 # Iterator on stack. 920 921 # In the loop... 922 923 self.set_label(next_label) 924 925 # Use the iterator to get the next value. 926 927 self._startCallFunc() 928 self.new_op(Duplicate()) 929 self._generateAttr(node, "next", (LoadAddress, LoadAttr, LoadAttrIndex)) 930 temp = self._generateCallFunc([], node) 931 self._endCallFunc(temp) 932 933 # Test for StopIteration. 934 935 self.dispatch(compiler.ast.Name("StopIteration")) 936 self.new_op(CheckException()) 937 if node.else_ is not None: 938 self.new_op(JumpIfTrue(else_label)) 939 else: 940 self.new_op(JumpIfTrue(exit_label)) 941 942 # Assign to the target. 943 944 self.dispatch(node.assign) 945 946 # Process the body with the current next and exit points. 947 948 self.add_loop_labels(next_label, exit_label) 949 self.dispatch(node.body) 950 self.drop_loop_labels() 951 952 # Repeat the loop. 953 954 self.new_op(Jump(next_label)) 955 956 # Produce the "else" section. 957 958 if node.else_ is not None: 959 self.set_label(exit_label) 960 self.dispatch(node.else_) 961 962 # Pop the iterator. 963 964 self.set_label(exit_label) 965 self.new_op(Pop()) 966 967 def visitFrom(self, node): pass 968 969 def visitFunction(self, node): 970 971 # Only store the name when visiting this node from outside. 972 973 if self.unit is not node.unit: 974 self.new_op(LoadConst(node.unit)) 975 self._visitName(node, (StoreName, StoreAddress)) 976 977 # Generate the default initialisation code. 978 979 for attr, default in zip(node.unit.default_attrs, node.unit.defaults): 980 self.dispatch(default) 981 self.new_op(StoreAddress(attr)) 982 983 # Visiting of the code occurs when get_code is invoked on this node. 984 985 else: 986 self.dispatch(node.code) 987 if not isinstance(self.last_op(), Return): 988 self.dispatch(compiler.ast.Name("None")) 989 self.new_op(Return()) 990 991 def visitGenExpr(self, node): pass 992 993 def visitGenExprFor(self, node): pass 994 995 def visitGenExprIf(self, node): pass 996 997 def visitGenExprInner(self, node): pass 998 999 def visitGetattr(self, node): 1000 self._visitAttr(node, (LoadAddress, LoadAttr, LoadAttrIndex)) 1001 1002 def visitGlobal(self, node): pass 1003 1004 def visitIf(self, node): 1005 first = 1 1006 exit_label = self.new_label() 1007 1008 for test, body in node.tests + [(None, node.else_)]: 1009 if body is None: 1010 break 1011 if not first: 1012 self.set_label(next_label) 1013 if test is not None: 1014 self.dispatch(test) 1015 next_label = self.new_label() 1016 self.new_op(JumpIfFalse(next_label)) 1017 self.dispatch(body) 1018 self.new_op(Jump(exit_label)) 1019 first = 0 1020 1021 self.set_label(exit_label) 1022 1023 def visitImport(self, node): pass 1024 1025 def visitInvert(self, node): pass 1026 1027 def visitKeyword(self, node): pass 1028 1029 def visitLambda(self, node): pass 1030 1031 def visitLeftShift(self, node): pass 1032 1033 def visitList(self, node): pass 1034 1035 def visitListComp(self, node): pass 1036 1037 def visitListCompFor(self, node): pass 1038 1039 def visitListCompIf(self, node): pass 1040 1041 def visitMod(self, node): 1042 self._visitBinary(node, "__mod__", "__rmod__") 1043 1044 def visitModule(self, node): 1045 self.dispatch(node.node) 1046 1047 def visitMul(self, node): 1048 self._visitBinary(node, "__mul__", "__rmul__") 1049 1050 def visitName(self, node): 1051 self._visitName(node, (LoadName, LoadAddress)) 1052 1053 def visitNot(self, node): pass 1054 1055 def visitOr(self, node): pass 1056 1057 def visitPass(self, node): pass 1058 1059 def visitPower(self, node): pass 1060 1061 def visitPrint(self, node): pass 1062 1063 def visitPrintnl(self, node): pass 1064 1065 def visitRaise(self, node): pass 1066 1067 def visitReturn(self, node): 1068 if node.value is not None: 1069 self.dispatch(node.value) 1070 else: 1071 self.dispatch(compiler.ast.Name("None")) 1072 self.new_op(Return()) 1073 1074 def visitRightShift(self, node): pass 1075 1076 def visitSlice(self, node): pass 1077 1078 def visitStmt(self, node): 1079 for n in node.nodes: 1080 self.dispatch(n) 1081 1082 def visitSub(self, node): 1083 self._visitBinary(node, "__sub__", "__rsub__") 1084 1085 def visitSubscript(self, node): pass 1086 1087 def visitTryExcept(self, node): 1088 1089 """ 1090 Enter try block. 1091 Dispatch to code. 1092 1093 """ 1094 1095 exit_label = self.new_label() 1096 handler_label = self.new_label() 1097 1098 self.add_exception_labels(handler_label, exit_label) 1099 1100 # Try... 1101 # Produce the code, then jump to the exit. 1102 1103 self.dispatch(node.body) 1104 self.new_op(Jump(exit_label)) 1105 1106 # Start of handlers. 1107 1108 self.set_label(handler_label) 1109 for name, assignment, handler in node.handlers: 1110 next_label = self.new_label() 1111 1112 # Test the given exception against the current exception. 1113 1114 if name is not None: 1115 self.dispatch(name) 1116 self.new_op(CheckException()) 1117 self.new_op(JumpIfFalse(next_label)) 1118 1119 # Handle assignment to exception variable. 1120 1121 if assignment is not None: 1122 self.dispatch(assignment) 1123 1124 # Produce the handler code, then jump to the exit. 1125 1126 self.dispatch(handler) 1127 self.new_op(Jump(exit_label)) 1128 1129 self.set_label(next_label) 1130 1131 # Unhandled exceptions. 1132 1133 self.new_op(RaiseException()) 1134 1135 # After exception 1136 1137 self.set_label(exit_label) 1138 1139 # Optional else clause. 1140 1141 if node.else_ is not None: 1142 self.dispatch(node.else_) 1143 1144 self.drop_exception_labels() 1145 1146 def visitTryFinally(self, node): pass 1147 1148 def visitTuple(self, node): pass 1149 1150 def visitUnaryAdd(self, node): pass 1151 1152 def visitUnarySub(self, node): pass 1153 1154 def visitWhile(self, node): 1155 exit_label = self.new_label() 1156 next_label = self.new_label() 1157 else_label = self.new_label() 1158 1159 self.set_label(next_label) 1160 self.dispatch(node.test) 1161 if node.else_ is not None: 1162 self.new_op(JumpIfFalse(else_label)) 1163 else: 1164 self.new_op(JumpIfFalse(exit_label)) 1165 1166 self.add_loop_labels(next_label, exit_label) 1167 1168 self.dispatch(node.body) 1169 self.new_op(Jump(next_label)) 1170 1171 if node.else_ is not None: 1172 self.set_label(else_label) 1173 self.dispatch(node.else_) 1174 1175 self.set_label(exit_label) 1176 self.drop_loop_labels() 1177 1178 def visitWith(self, node): pass 1179 1180 def visitYield(self, node): pass 1181 1182 # Useful data. 1183 1184 comparison_methods = { 1185 "==" : ("__eq__", "__ne__"), 1186 "!=" : ("__ne__", "__eq__"), 1187 "<" : ("__lt__", "__gt__"), 1188 "<=" : ("__le__", "__ge__"), 1189 ">=" : ("__ge__", "__le__"), 1190 ">" : ("__gt__", "__lt__"), 1191 "is" : None, 1192 "is not" : None, 1193 "in" : None, 1194 "not in" : None 1195 } 1196 1197 # vim: tabstop=4 expandtab shiftwidth=4