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 from micropython.common import * 23 from micropython.data import * 24 from micropython.rsvp import * 25 import compiler.ast 26 from compiler.visitor import ASTVisitor 27 28 # Program visitors. 29 30 class Translation(ASTVisitor): 31 32 "A translated module." 33 34 supported_optimisations = ["constant_storage", "known_target", "self_access", "temp_storage", "load_operations", "unused_results"] 35 36 attribute_load_instructions = (LoadAddress, LoadAddressContext, LoadAttr, LoadAttrIndex) 37 attribute_store_instructions = (None, None, StoreAttr, StoreAttrIndex) 38 name_load_instructions = (LoadName, LoadAddress) 39 name_store_instructions = (StoreName, StoreAddress) 40 41 def __init__(self, module, importer, optimisations=None): 42 43 """ 44 Initialise the translation with an inspected 'module', the 'importer' 45 and optional 'optimisations'. See the 'supported_optimisations' 46 attribute of this class for permitted values. 47 """ 48 49 ASTVisitor.__init__(self) 50 self.visitor = self 51 self.module = module 52 53 # Global program dependencies. 54 55 self.importer = importer 56 self.objtable = self.importer.get_object_table() 57 self.paramtable = self.importer.get_parameter_table() 58 self.builtins = self.importer.modules.get("__builtins__") 59 60 # Desired optimisations. 61 62 self.optimisations = set(optimisations or []) 63 64 # The current unit being translated. 65 66 self.unit = None 67 68 # The current "active" instruction. 69 # As a rule, this will become the last instruction, but some 70 # control-flow operations will flush the "active" instruction. 71 72 self.active = None 73 74 # The temporary storage used by the current assignment expression. 75 76 self.expr_temp = None 77 78 # Wiring within the code. 79 80 self.labels = {} 81 self.label_number = 0 82 self.loop_labels = [] 83 self.exception_labels = [] 84 85 # The code itself. This is limited to the code for a particular block 86 # being processed. Also retained is information about temporary values. 87 88 self.code = None 89 self.temp_position = 0 90 91 def __repr__(self): 92 return "Translation(%r)" % self.module 93 94 def get_module_code(self): 95 96 "Return the top-level module code." 97 98 self.unit = self.module 99 self.code = [] 100 self.temp_position = 0 101 102 if self.module.module is not None: 103 self.dispatch(self.module.module) 104 105 return self.code 106 107 def get_code(self, unit): 108 109 "Return the code for the given 'unit'." 110 111 self.unit = unit 112 self.code = [] 113 self.temp_position = 0 114 115 if unit.astnode is not None: 116 self.dispatch(unit.astnode) 117 118 return self.code 119 120 # Name-related methods. 121 122 def get_scope(self, name): 123 124 "Return the scope for the given 'name'." 125 126 if self.unit.has_key(name): 127 return "local" 128 elif self.module.has_key(name): 129 return "global" 130 else: 131 return "builtins" 132 133 def load_builtin(self, name, node): 134 135 "Generate an instruction loading 'name' for the given 'node'." 136 137 self.new_op(LoadAddress(self.get_builtin(name, node))) 138 139 def get_builtin(self, name, node): 140 141 """ 142 Return the built-in module definition for the given 'name', used by the 143 given 'node'. 144 """ 145 146 if self.builtins is not None: 147 try: 148 return self.builtins[name] 149 except KeyError: 150 raise TranslateError(self.module.full_name(), node, "No __builtins__ definition is available for name %r." % name) 151 else: 152 raise TranslateError(self.module.full_name(), node, "No __builtins__ module is available for name %r." % name) 153 154 # Code feature methods. 155 156 def new_label(self): 157 158 "Return a new label object for use with set_label." 159 160 number = self.label_number 161 label = Label(number) 162 self.labels[label] = label 163 self.label_number += 1 164 return label 165 166 def set_label(self, label): 167 168 """ 169 Set the location of 'label' to that within the entire image: the 170 location within the code combined with location of the code unit. 171 """ 172 173 label.location = len(self.code) + self.unit.code_location 174 175 def get_loop_labels(self): 176 return self.loop_labels[-1] 177 178 def add_loop_labels(self, next_label, exit_label): 179 self.loop_labels.append((next_label, exit_label)) 180 181 def drop_loop_labels(self): 182 self.loop_labels.pop() 183 184 def get_exception_labels(self): 185 return self.exception_labels[-1] 186 187 def add_exception_labels(self, handler_label, exit_label): 188 self.exception_labels.append((handler_label, exit_label)) 189 190 def drop_exception_labels(self): 191 self.exception_labels.pop() 192 193 # Assignment expression values. 194 195 def record_value(self): 196 self.expr_temp = self._optimise_temp_storage() 197 198 def discard_value(self): 199 self.discard_temp(self.expr_temp) 200 self.expr_temp = None 201 202 def set_source(self): 203 if self.active is not None: 204 self.active.source = self.expr_temp 205 206 # Optimise away constant storage if appropriate. 207 208 self._optimise_constant_storage() 209 210 # Temporary storage administration. 211 212 def get_temp(self): 213 214 """ 215 Add a temporary storage instruction for the current value and return a 216 sequence of access instructions. 217 """ 218 219 temp_position = self.reserve_temp(1) 220 self.new_op(StoreTemp(temp_position)) 221 return LoadTemp(temp_position) 222 223 def ensure_temp(self): 224 if isinstance(self.active, LoadTemp): 225 self.temp_position = max(self.temp_position, self.active.attr + 1) 226 227 def reserve_temp(self, n): 228 temp_position = self.temp_position 229 self.temp_position += n 230 return temp_position 231 232 def discard_temp(self, instruction): 233 if isinstance(instruction, LoadTemp): 234 self.temp_position -= 1 235 236 # Code writing methods. 237 238 def new_op(self, op): 239 240 "Add 'op' to the generated code." 241 242 # Optimise load operations employed by this instruction. 243 244 self._optimise_load_operations(op) 245 246 self.code.append(op) 247 self.active = op 248 249 def remove_op(self): 250 251 "Remove the last instruction." 252 253 op = self.code.pop() 254 self.active = None 255 256 def replace_op(self, op): 257 258 "Replace the last added instruction with 'op'." 259 260 self.remove_op() 261 self.new_op(op) 262 263 def last_op(self): 264 265 "Return the last added instruction." 266 267 try: 268 return self.code[-1] 269 except IndexError: 270 return None 271 272 # Optimisation tests. 273 274 def _should_optimise_constant_storage(self): 275 return "constant_storage" in self.optimisations 276 277 def _should_optimise_known_target(self): 278 return "known_target" in self.optimisations 279 280 def _should_optimise_self_access(self): 281 return "self_access" in self.optimisations 282 283 def _should_optimise_temp_storage(self): 284 return "temp_storage" in self.optimisations 285 286 def _should_optimise_load_operations(self): 287 return "load_operations" in self.optimisations 288 289 def _should_optimise_unused_results(self): 290 return "unused_results" in self.optimisations 291 292 # Simple tests. 293 294 def _is_constant_input(self, instruction): 295 296 "Return whether 'instruction' provides a constant input." 297 298 return isinstance(instruction, LoadAddress) and instruction.attr.assignments == 1 or \ 299 isinstance(instruction, LoadConst) 300 301 def _is_constant_target(self, instruction): 302 303 "Return whether 'instruction' provides a constant target." 304 305 return isinstance(instruction, (StoreName, StoreAddress)) and \ 306 instruction.attr.assignments == 1 307 308 def _is_simple_input(self, instruction): 309 310 "Return whether 'instruction' provides a simple input." 311 312 return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult, LoadAddress, MakeObject)) 313 314 def _is_simple_input_user(self, instruction): 315 316 "Return whether 'instruction' can use simple input from the current value." 317 318 return isinstance(instruction, ( 319 StoreTemp, StoreFrame, StoreResult, StoreException, # as the value being stored 320 LoadAddressContext, LoadAttr, LoadAttrIndex, # as the object being referenced 321 StoreAttr, StoreAttrIndex, StoreCallable # as the object being referenced 322 )) 323 324 def _is_input(self, instruction): 325 326 "Return whether 'instruction' provides an input." 327 328 return isinstance(instruction, (LoadConst, LoadName, LoadTemp, LoadResult) + self.attribute_load_instructions) 329 330 # Convenience tests. 331 332 def _have_constant_input(self): 333 334 "Return whether the active instruction provides a constant input." 335 336 return self._is_constant_input(self.active) 337 338 _have_known_target = _have_constant_input 339 340 def _have_constant_target(self): 341 342 "Return whether the active instruction provides a constant target." 343 344 return self._is_constant_target(self.active) 345 346 def _have_constant_source(self): 347 348 "Return whether the active instruction has a constant source." 349 350 return self._is_constant_input(self.active.source) 351 352 def _have_simple_input(self): 353 354 "Return whether the active instruction provides a simple input." 355 356 return self._is_simple_input(self.active) 357 358 def _have_input(self): 359 360 "Return whether the active instruction provides an input." 361 362 return self._is_input(self.active) 363 364 def _have_self_input(self): 365 366 "Return whether the active instruction is a reference to self." 367 368 return isinstance(self.unit, Function) and \ 369 self.unit.is_method() and isinstance(self.active, LoadName) and \ 370 self.active.attr.name == "self" 371 372 def _have_temp_compatible_access(self): 373 374 """ 375 Indicate whether the active instruction can be used in place of access 376 to a temporary variable retaining the result of the last instruction. 377 """ 378 379 return isinstance(self.active, (LoadName, LoadTemp, LoadAddress, LoadConst, LoadResult)) 380 381 # Optimisation methods. See the supported_optimisations class attribute. 382 383 def _optimise_constant_storage(self): 384 385 """ 386 Where the last operation stores a constant into a target which is also 387 constant, optimise away both operations. 388 """ 389 390 if self._should_optimise_constant_storage() and \ 391 self._have_constant_target() and \ 392 self._have_constant_source(): 393 394 self.remove_op() 395 return 1 396 else: 397 return 0 398 399 def _optimise_known_target(self): 400 401 """ 402 Where the target of an invocation is known, provide information about it 403 and its context. If a class is being invoked and the conditions are 404 appropriate, get information about the specific initialiser. 405 """ 406 407 if self._should_optimise_known_target() and self._have_known_target(): 408 last = self.last_op() 409 target = last.attr.value 410 context = last.attr.context 411 412 # Handle calls to classes. 413 414 if isinstance(target, Class): 415 target = target.get_instantiator() 416 context = Undefined() 417 418 # A special context is chosen to avoid generating unnecessary 419 # context loading and checking instructions. 420 421 return target, context 422 else: 423 return None 424 425 def _optimise_self_access(self, attrname, classes, node): 426 427 """ 428 Where the provided 'attrname' accesses an attribute which occupies the 429 same position in all possible objects which can be accessed, generate an 430 instruction using one of the given 'classes', accessing the attribute 431 directly. 432 """ 433 434 AddressInstruction, AddressContextInstruction, AttrInstruction = classes 435 436 if self._should_optimise_self_access() and self._have_self_input() and \ 437 not self.unit.is_relocated(attrname): 438 439 # Either generate an instruction operating on an instance attribute. 440 441 try: 442 attr = self.unit.parent.instance_attributes()[attrname] 443 self.new_op(AttrInstruction(attr)) 444 445 # Or generate an instruction operating on a class attribute. 446 447 except KeyError: 448 attr = self.unit.parent.all_attributes()[attrname] 449 new_attr = attr.via_instance() 450 451 # Only permit loading (not storing) of class attributes via self. 452 453 if AddressContextInstruction is not None: 454 self.new_op(AddressContextInstruction(new_attr)) 455 else: 456 raise TranslateError(self.module.full_name(), node, 457 "Storing of class attribute %r via self not permitted." % attrname) 458 459 return 1 460 else: 461 return 0 462 463 def _optimise_temp_storage(self): 464 465 """ 466 Where the next operation would involve storing a value into temporary 467 storage at 'temp_position', record and remove any simple instruction 468 which produced the value to be stored such that instead of subsequently 469 accessing the temporary storage, that instruction is substituted. 470 471 If no optimisation can be achieved, a StoreTemp instruction is produced 472 and the appropriate LoadTemp instruction is returned. 473 474 Restriction: for use only in situations where the source of the 475 temporary data will not be disturbed between its first access and its 476 subsequent use. 477 """ 478 479 if self._should_optimise_temp_storage() and \ 480 self._have_temp_compatible_access(): 481 482 # Where an existing temporary storage reference will be retained, 483 # keep it active. 484 485 self.ensure_temp() 486 487 last = self.last_op() 488 self.remove_op() 489 return last 490 else: 491 return self.get_temp() 492 493 def _optimise_load_operations(self, instruction): 494 495 """ 496 Incorporate previous load operations into other operations. 497 """ 498 499 if self._should_optimise_load_operations() and \ 500 self._have_simple_input() and \ 501 self._is_simple_input_user(instruction): 502 503 last = self.last_op() 504 self.remove_op() 505 instruction.input = last 506 507 def _optimise_unused_results(self): 508 509 "Discard results which will not be used." 510 511 if self._have_input(): 512 self.remove_op() 513 514 # Visitor methods. 515 516 def default(self, node, *args): 517 raise TranslateError(self.module.full_name(), node, "Node class %r is not supported." % node.__class__) 518 519 def dispatch(self, node, *args): 520 return ASTVisitor.dispatch(self, node, *args) 521 522 # Internal helper methods. 523 524 def _visitAttr(self, node, classes): 525 526 """ 527 Visit the attribute-related 'node', generating instructions based on the 528 given 'classes'. 529 """ 530 531 self.dispatch(node.expr) 532 self._generateAttr(node, node.attrname, classes) 533 534 def _generateAttr(self, node, attrname, classes): 535 536 """ 537 Generate code for the access to 'attrname' using the given 'classes'. 538 """ 539 540 AddressInstruction, AddressContextInstruction, AttrInstruction, AttrIndexInstruction = classes 541 542 # Where the last operation (defining the attribute owner) yields a 543 # constant... 544 545 if self._have_constant_input(): 546 last = self.active 547 548 # Get the details of the access. 549 550 if isinstance(last.attr, Const): 551 target_name = last.attr.value_type_name() 552 else: 553 target = last.attr.value 554 555 if isinstance(target, Const): 556 target_name = target.value_type_name() 557 elif isinstance(target, Instance): 558 target_name = None # skip production of optimised code 559 else: 560 target_name = target.full_name() 561 562 # Only try and discover the position if the target can be resolved. 563 564 if target_name is not None: 565 566 # Access the object table to get the attribute position. 567 568 try: 569 table_entry = self.objtable.table[target_name] 570 except KeyError: 571 raise TranslateError(self.module.full_name(), node, 572 "No object entry exists for target %r." % target_name) 573 574 try: 575 pos = table_entry[attrname] 576 except KeyError: 577 raise TranslateError(self.module.full_name(), node, 578 "No attribute entry exists for name %r in target %r." % (attrname, target_name)) 579 580 # Produce a suitable instruction. 581 582 if AddressInstruction is not None: 583 self.replace_op(AddressInstruction(pos)) 584 else: 585 raise TranslateError(self.module.full_name(), node, 586 "Storing of class or module attribute %r via an object is not permitted." % attrname) 587 588 return 589 590 # Where the last operation involves the special 'self' name, check to 591 # see if the attribute is acceptably positioned and produce a direct 592 # access to the attribute. 593 594 elif self._optimise_self_access(attrname, (AddressInstruction, AddressContextInstruction, AttrInstruction), node): 595 return 596 597 # Otherwise, perform a normal operation. 598 599 try: 600 index = self.objtable.get_index(attrname) 601 except self.objtable.TableError: 602 raise TranslateError(self.module.full_name(), node, 603 "No attribute entry exists for name %r." % attrname) 604 605 self.new_op(AttrIndexInstruction(index)) 606 607 # Invocations involve the following: 608 # 609 # 1. Reservation of a frame for the arguments 610 # 2. Identification of the target which is then held in temporary storage 611 # 3. Optional inclusion of a context (important for methods) 612 # 4. Preparation of the argument frame 613 # 5. Invocation of the target 614 # 6. Discarding of the frame 615 # 616 # In order to support nested invocations - eg. a(b(c)) - use of the 617 # temporary storage is essential. 618 619 def _startCallFunc(self): 620 621 "Record the location of the invocation." 622 623 self.new_op(MakeFrame()) # records the start of the frame 624 625 def _generateCallFunc(self, args, node): 626 627 """ 628 Support a generic function invocation using the given 'args', occurring 629 on the given 'node', where the expression providing the invocation 630 target has just been generated. 631 632 In other situations, the invocation is much simpler and does not need to 633 handle the full flexibility of a typical Python invocation. Internal 634 invocations, such as those employed by operators and certain 635 control-flow mechanisms, use predetermined arguments and arguably do not 636 need to support the same things as the more general invocations. 637 """ 638 639 target, context, temp = self._generateCallFuncContext() 640 self._generateCallFuncArgs(target, context, temp, args, node) 641 return temp 642 643 def _generateCallFuncContext(self): 644 645 """ 646 Produce code which loads and checks the context of the current 647 invocation, the instructions for whose target have already been 648 produced, returning a list of instructions which reference the 649 invocation target. 650 """ 651 652 t = self._optimise_known_target() 653 if t: 654 target, context = t 655 else: 656 target, context = None, None 657 658 # Store the target in temporary storage for subsequent referencing. 659 660 temp = self._optimise_temp_storage() 661 662 # Where a target or context are not known or where an instance is known 663 # to be the context, load the context. 664 665 if context is None or isinstance(context, Instance): 666 self.new_op(temp) 667 self.new_op(LoadContext()) 668 self.new_op(StoreFrame(0)) 669 670 # Otherwise omit the context. 671 672 else: 673 pass # NOTE: Class methods should be supported. 674 675 return target, context, temp 676 677 def _generateCallFuncArgs(self, target, context, temp, args, node): 678 679 """ 680 Given invocation 'target' and 'context' information, the 'temp' 681 reference to the target, a list of nodes representing the 'args' 682 (arguments), generate instructions which load the arguments for the 683 invocation defined by the given 'node'. 684 """ 685 686 # Evaluate the arguments. 687 688 employed_positions = set() 689 extra_keywords = [] 690 691 # Note the presence of the context in the frame where appropriate. 692 693 if context is None or isinstance(context, Instance): 694 ncontext = 1 695 expect_context = 0 696 elif isinstance(context, Class): 697 ncontext = 0 698 expect_context = 1 699 else: 700 ncontext = 0 701 expect_context = 0 702 703 first = 1 704 frame_pos = ncontext 705 706 for arg in args: 707 708 # Handle positional and keyword arguments separately. 709 710 if isinstance(arg, compiler.ast.Keyword): 711 712 # Optimise where the target is known now. 713 714 if target is not None: 715 716 # Find the parameter table entry for the target. 717 718 target_name = target.full_name() 719 720 # Look for a callable with the precise target name. 721 722 table_entry = self.paramtable.table[target_name] 723 724 # Look the name up in the parameter table entry. 725 726 try: 727 pos = table_entry[arg.name] 728 729 # Where no position is found, this could be an extra keyword 730 # argument. 731 732 except KeyError: 733 extra_keywords.append(arg) 734 continue 735 736 # Test for illegal conditions. 737 738 if pos in employed_positions: 739 raise TranslateError(self.module.full_name(), node, 740 "Keyword argument %r overwrites parameter %r." % (arg.name, pos)) 741 742 employed_positions.add(pos) 743 744 # Generate code for the keyword and the positioning 745 # operation. 746 747 self.dispatch(arg.expr) 748 self.new_op(StoreFrame(pos)) 749 750 # Otherwise, generate the code needed to obtain the details of 751 # the parameter location. 752 753 else: 754 755 # Combine the target details with the name to get the location. 756 # See the access method on the List class. 757 758 try: 759 paramindex = self.paramtable.get_index(arg.name) 760 761 # Where no position is found, this could be an extra keyword 762 # argument. 763 764 except self.paramtable.TableError: 765 extra_keywords.append(arg) 766 continue 767 768 # Generate code for the keyword and the positioning 769 # operation. 770 771 self.dispatch(arg.expr) 772 self.new_op(StoreFrameIndex(paramindex)) 773 774 # use (callable+0)+paramindex+table 775 # checks embedded offset against (callable+0) 776 # moves the current value to frame+position 777 778 else: 779 self.dispatch(arg) 780 self.new_op(StoreFrame(frame_pos)) 781 782 employed_positions.add(frame_pos) 783 784 # Check to see if the first argument is appropriate (compatible with 785 # the target where methods are being invoked via classes). 786 787 if first and expect_context: 788 continue_label = self.new_label() 789 self.new_op(CheckSelf()) 790 self.new_op(JumpIfTrue(continue_label)) 791 792 # Where the context is inappropriate, drop the incomplete frame and 793 # raise an exception. 794 795 self.new_op(DropFrame()) 796 797 self.new_op(LoadResult()) 798 799 self.load_builtin("TypeError", node) 800 self.new_op(StoreException()) 801 self.new_op(RaiseException()) 802 self.set_label(continue_label) 803 804 first = 0 805 frame_pos += 1 806 807 # NOTE: Extra keywords are not supported. 808 # NOTE: Somehow, the above needs to be combined with * arguments. 809 810 # Either test for a complete set of arguments. 811 812 if target is not None: 813 814 # Make sure that enough arguments have been given. 815 816 nargs_max = len(target.positional_names) 817 ndefaults = len(target.defaults) 818 nargs_min = nargs_max - ndefaults 819 820 for i in range(ncontext, nargs_min): 821 if i not in employed_positions: 822 raise TranslateError(self.module.full_name(), node, 823 "Argument %r not supplied for %r: need at least %d argument(s)." % (i+1, target.name, nargs_min)) 824 825 nargs = len(args) 826 827 if nargs > nargs_max and not target.has_star and not target.has_dstar: 828 raise TranslateError(self.module.full_name(), node, 829 "Too many arguments for %r: need at most %d argument(s)." % (target.name, nargs_max)) 830 831 # Where defaults are involved, put them into the frame. 832 833 self._generateCallFuncDefaultArgs(target, temp, nargs_min, nargs_max, employed_positions) 834 835 # Or generate instructions to do this at run-time. 836 # NOTE: CheckFrame has to check the number of arguments and to fill in 837 # NOTE: defaults; it also has to shift the invocation frame according to 838 # NOTE: the context in use. 839 840 else: 841 self.new_op(CheckFrame()) 842 843 def _generateCallFuncDefaultArgs(self, target, temp, nargs_min, nargs_max, employed_positions): 844 845 """ 846 For the given 'target' and 'temp' reference to the target, generate 847 default arguments for those positions in the range 'nargs_min'... 848 'nargs_max' which are not present in the 'employed_positions' 849 collection. 850 """ 851 852 # Where a lambda is involved, construct a dynamic object to hold the 853 # defaults. 854 855 dynamic = target.name is None 856 857 # Here, we use negative index values to visit the right hand end of 858 # the defaults list. 859 860 for pos in range(nargs_min, nargs_max): 861 if pos not in employed_positions: 862 if dynamic: 863 self.new_op(temp) 864 self.new_op(LoadAttr(target.default_attrs[pos - nargs_min])) 865 else: 866 self.new_op(LoadAddress(target.default_attrs[pos - nargs_min])) 867 self.new_op(StoreFrame(pos)) 868 869 def _doCallFunc(self, instruction): 870 871 "Make the invocation." 872 873 self.new_op(instruction) 874 self.new_op(JumpWithFrame()) 875 876 def _endCallFunc(self, instruction=None): 877 878 "Finish the invocation and tidy up afterwards." 879 880 self.new_op(DropFrame()) 881 self.new_op(LoadResult()) 882 883 # Discard any temporary storage instructions. 884 885 if instruction is not None: 886 self.discard_temp(instruction) 887 888 def _generateFunctionDefaults(self, function): 889 890 """ 891 Generate the default initialisation code for 'function', returning 892 a temporary storage reference if a dynamic object was created for the 893 function. 894 """ 895 896 attr_to_default = zip(function.default_attrs, function.defaults) 897 if not attr_to_default: 898 return None 899 900 # Where a lambda is involved, construct a dynamic object to hold the 901 # defaults. 902 903 dynamic = function.name is None 904 905 if dynamic: 906 self.new_op(MakeObject(len(attr_to_default))) 907 temp = self.get_temp() 908 909 for attr, default in attr_to_default: 910 self.dispatch(default) 911 912 self.record_value() 913 if dynamic: 914 self.new_op(temp) 915 self.new_op(StoreAttr(attr)) 916 else: 917 self.new_op(StoreAddress(attr)) 918 self.set_source() 919 self.discard_value() 920 921 if dynamic: 922 return temp 923 else: 924 return None 925 926 def _visitName(self, node, classes): 927 928 """ 929 Visit the name-related 'node', generating instructions based on the 930 given 'classes'. 931 """ 932 933 name = node.name 934 scope = self.get_scope(name) 935 #print self.module.name, node.lineno, name, scope 936 self._generateName(name, scope, classes, node) 937 938 def _generateName(self, name, scope, classes, node): 939 940 """ 941 Generate code for the access to 'name' in 'scope' using the given 942 'classes', and using the given 'node' as the source of the access. 943 """ 944 945 NameInstruction, AddressInstruction = classes 946 947 if scope == "local": 948 unit = self.unit 949 if isinstance(unit, Function): 950 self.new_op(NameInstruction(unit.all_locals()[name])) 951 elif isinstance(unit, Class): 952 self.new_op(AddressInstruction(unit.all_class_attributes()[name])) 953 elif isinstance(unit, Module): 954 self.new_op(AddressInstruction(unit.module_attributes()[name])) 955 else: 956 raise TranslateError(self.module.full_name(), node, "Program unit %r has no local %r." % (unit, name)) 957 958 elif scope == "global": 959 globals = self.module.module_attributes() 960 if globals.has_key(name): 961 self.new_op(AddressInstruction(globals[name])) 962 else: 963 raise TranslateError(self.module.full_name(), node, "Module %r has no attribute %r." % (self.module, name)) 964 965 else: 966 self.new_op(AddressInstruction(self.get_builtin(name, node))) 967 968 def _visitUnary(self, node, method): 969 970 """ 971 _t = node.expr 972 try: 973 _result = _t.__pos__() 974 except AttributeError: 975 raise TypeError 976 """ 977 978 end_call_label = self.new_label() 979 end_label = self.new_label() 980 981 # Evaluate and store the operand in temporary storage. 982 983 self.dispatch(node.expr) 984 temp = self._optimise_temp_storage() 985 986 # Produce the invocation. 987 988 self._startCallFunc() 989 self.new_op(temp) 990 991 # Get the method on temp. 992 993 self._generateAttr(node, method, self.attribute_load_instructions) 994 995 # Add exception handling to the method acquisition instructions where 996 # the attribute access cannot be resolved at compile-time. 997 998 if not self._optimise_known_target(): 999 self.load_builtin("AttributeError", node) 1000 self.new_op(CheckException()) 1001 self.new_op(JumpIfTrue(end_call_label)) 1002 1003 temp_method = self._optimise_temp_storage() 1004 1005 # Add arguments. 1006 # NOTE: No support for defaults. 1007 1008 self.new_op(temp) # Explicit context as first argument. 1009 self._doCallFunc(temp_method) 1010 self._endCallFunc(temp_method) 1011 self.new_op(Jump(end_label)) 1012 1013 # End method attempt. 1014 1015 self.set_label(end_call_label) 1016 self._endCallFunc() # From the method call. 1017 1018 # Raise a TypeError. 1019 1020 self.load_builtin("TypeError", node) 1021 self.new_op(StoreException()) 1022 self.new_op(RaiseException()) 1023 1024 self.set_label(end_label) 1025 1026 # Compilation duties... 1027 1028 self.discard_temp(temp) 1029 1030 def _visitBinary(self, node, left_method, right_method): 1031 1032 """ 1033 _t1 = node.left 1034 _t2 = node.right 1035 try: 1036 _result = _t1.__add__(_t2) 1037 if _result is NotImplemented: 1038 raise AttributeError 1039 except AttributeError: 1040 try: 1041 _result = _t2.__radd__(_t1) 1042 if _result is NotImplemented: 1043 raise AttributeError 1044 except AttributeError: 1045 raise TypeError 1046 """ 1047 1048 end_left_label = self.new_label() 1049 right_label = self.new_label() 1050 end_right_label = self.new_label() 1051 type_error_label = self.new_label() 1052 end_label = self.new_label() 1053 1054 # Evaluate and store the left operand in temporary storage. 1055 1056 self.dispatch(node.left) 1057 temp1 = self._optimise_temp_storage() 1058 1059 # Evaluate and store the right operand in temporary storage. 1060 1061 self.dispatch(node.right) 1062 temp2 = self._optimise_temp_storage() 1063 1064 # Left method. 1065 1066 self._startCallFunc() 1067 self.new_op(temp1) 1068 1069 # Get left method on temp1. 1070 1071 self._generateAttr(node, left_method, self.attribute_load_instructions) 1072 1073 # Add exception handling to the method acquisition instructions where 1074 # the attribute access cannot be resolved at compile-time. 1075 1076 if not self._optimise_known_target(): 1077 self.load_builtin("AttributeError", node) 1078 self.new_op(CheckException()) 1079 self.new_op(JumpIfTrue(end_left_label)) 1080 1081 temp_method = self._optimise_temp_storage() 1082 1083 # Add arguments. 1084 # NOTE: No support for defaults. 1085 1086 self.new_op(temp1) # Explicit context as first argument. 1087 self.new_op(temp2) 1088 self._doCallFunc(temp_method) 1089 self._endCallFunc(temp_method) 1090 1091 # Test for NotImplemented. 1092 # Don't actually raise an exception. 1093 1094 self.new_op(TestIdentityAddress(self.get_builtin("NotImplemented", node))) 1095 self.new_op(JumpIfTrue(right_label)) 1096 self.new_op(Jump(end_label)) 1097 1098 # End left method attempt. 1099 1100 self.set_label(end_left_label) 1101 self._endCallFunc() # From the left method call. 1102 1103 # Right method. 1104 1105 self.set_label(right_label) 1106 self._startCallFunc() 1107 self.new_op(temp2) 1108 1109 # Get right method on temp2. 1110 1111 self._generateAttr(node, right_method, self.attribute_load_instructions) 1112 1113 # Add exception handling to the method acquisition instructions where 1114 # the attribute access cannot be resolved at compile-time. 1115 1116 if not self._optimise_known_target(): 1117 self.load_builtin("AttributeError", node) 1118 self.new_op(CheckException()) 1119 self.new_op(JumpIfTrue(end_right_label)) 1120 1121 temp_method = self._optimise_temp_storage() 1122 1123 # Add arguments. 1124 # NOTE: No support for defaults. 1125 1126 self.new_op(temp2) # Explicit context as first argument. 1127 self.new_op(temp1) 1128 self._doCallFunc(temp_method) 1129 self._endCallFunc(temp_method) 1130 1131 # Test for NotImplemented. 1132 # Don't actually raise an exception. 1133 1134 self.new_op(TestIdentityAddress(self.get_builtin("NotImplemented", node))) 1135 self.new_op(JumpIfTrue(type_error_label)) 1136 self.new_op(Jump(end_label)) 1137 1138 # End right method attempt. 1139 1140 self.set_label(end_right_label) 1141 self._endCallFunc() # From the right method call. 1142 1143 # Raise a TypeError. 1144 1145 self.set_label(type_error_label) 1146 self.load_builtin("TypeError", node) 1147 self.new_op(StoreException()) 1148 self.new_op(RaiseException()) 1149 1150 self.set_label(end_label) 1151 1152 # Compilation duties... 1153 1154 self.discard_temp(temp1) 1155 self.discard_temp(temp2) 1156 1157 # Concrete visitor methods. 1158 1159 def visitAdd(self, node): 1160 self._visitBinary(node, "__add__", "__radd__") 1161 1162 def visitAnd(self, node): 1163 next_label = self.new_label() 1164 1165 for n in node.nodes[:-1]: 1166 self.dispatch(n) 1167 self.new_op(TestBoolean()) 1168 self.new_op(JumpIfFalse(next_label)) 1169 1170 self.dispatch(node.nodes[-1]) 1171 self.set_label(next_label) 1172 1173 # Prevent incorrect optimisation. 1174 1175 self.active = None 1176 1177 def visitAssert(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Assert") 1178 1179 def visitAssign(self, node): 1180 self.dispatch(node.expr) 1181 self.record_value() 1182 1183 for n in node.nodes: 1184 self.dispatch(n) 1185 1186 self.discard_value() 1187 1188 def visitAssAttr(self, node): 1189 self._visitAttr(node, self.attribute_store_instructions) 1190 self.set_source() 1191 1192 def visitAssList(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "AssList") 1193 1194 def visitAssName(self, node): 1195 self._visitName(node, self.name_store_instructions) 1196 self.set_source() 1197 1198 visitAssTuple = visitAssList 1199 1200 def visitAugAssign(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "AugAssign") 1201 1202 def visitBackquote(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Backquote") 1203 1204 def visitBitand(self, node): 1205 self._visitBinary(node, "__and__", "__rand__") 1206 1207 def visitBitor(self, node): 1208 self._visitBinary(node, "__or__", "__ror__") 1209 1210 def visitBitxor(self, node): 1211 self._visitBinary(node, "__xor__", "__rxor__") 1212 1213 def visitBreak(self, node): 1214 next_label, exit_label = self.get_loop_labels() 1215 self.new_op(Jump(exit_label)) 1216 1217 def visitCallFunc(self, node): 1218 1219 """ 1220 Evaluate positional arguments, evaluate and store keyword arguments in 1221 the correct location, then invoke the function. 1222 """ 1223 1224 # Mark the frame, evaluate the target, generate the call. 1225 1226 self._startCallFunc() 1227 self.dispatch(node.node) 1228 temp = self._generateCallFunc(node.args, node) 1229 self._doCallFunc(temp) 1230 self._endCallFunc(temp) 1231 1232 def visitClass(self, node): 1233 1234 # Store the name. 1235 1236 self.new_op(LoadConst(node.unit)) 1237 self.record_value() 1238 self._visitName(node, self.name_store_instructions) 1239 self.set_source() 1240 self.discard_value() 1241 1242 # Visit the code. 1243 1244 unit = self.unit 1245 self.unit = node.unit 1246 self.unit.code_location = self.module.code_location # class body code is not independently addressable 1247 self.dispatch(node.code) 1248 self.unit = unit 1249 1250 def visitCompare(self, node): 1251 1252 """ 1253 self.dispatch(node.expr) 1254 for op_name, next_node in compare.ops: 1255 methods = self.comparison_methods[op_name] 1256 if methods is not None: 1257 # Generate method call using evaluated argument and next node. 1258 else: 1259 # Deal with the special operators. 1260 # Provide short-circuiting. 1261 """ 1262 1263 def visitConst(self, node): 1264 const = self.module.constant_values[node.value] 1265 self.new_op(LoadConst(const)) 1266 1267 def visitContinue(self, node): 1268 next_label, exit_label = self.get_loop_labels() 1269 self.new_op(Jump(next_label)) 1270 1271 def visitDecorators(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Decorators") 1272 1273 def visitDict(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Dict") 1274 1275 def visitDiscard(self, node): 1276 self.dispatch(node.expr) 1277 self._optimise_unused_results() 1278 1279 def visitDiv(self, node): 1280 self._visitBinary(node, "__div__", "__rdiv__") 1281 1282 def visitEllipsis(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Ellipsis") 1283 1284 def visitExec(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Exec") 1285 1286 def visitExpression(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Expression") 1287 1288 def visitFloorDiv(self, node): 1289 self._visitBinary(node, "__floordiv__", "__rfloordiv__") 1290 1291 def visitFor(self, node): 1292 exit_label = self.new_label() 1293 next_label = self.new_label() 1294 else_label = self.new_label() 1295 1296 # Get the "list" to be iterated over, obtain its iterator. 1297 1298 self._startCallFunc() 1299 self.dispatch(node.list) 1300 self._generateAttr(node, "__iter__", self.attribute_load_instructions) 1301 temp = self._generateCallFunc([], node) 1302 self._doCallFunc(temp) 1303 self._endCallFunc(temp) 1304 1305 temp_iterator = self._optimise_temp_storage() 1306 1307 # In the loop... 1308 1309 self.set_label(next_label) 1310 1311 # Use the iterator to get the next value. 1312 1313 self._startCallFunc() 1314 self.new_op(temp_iterator) 1315 self._generateAttr(node, "next", self.attribute_load_instructions) 1316 temp = self._generateCallFunc([], node) 1317 self._doCallFunc(temp) 1318 self._endCallFunc(temp) 1319 1320 # Test for StopIteration. 1321 1322 self.load_builtin("StopIteration", node) 1323 self.new_op(CheckException()) 1324 if node.else_ is not None: 1325 self.new_op(JumpIfTrue(else_label)) 1326 else: 1327 self.new_op(JumpIfTrue(exit_label)) 1328 1329 # Assign to the target. 1330 1331 self.dispatch(node.assign) 1332 1333 # Process the body with the current next and exit points. 1334 1335 self.add_loop_labels(next_label, exit_label) 1336 self.dispatch(node.body) 1337 self.drop_loop_labels() 1338 1339 # Repeat the loop. 1340 1341 self.new_op(Jump(next_label)) 1342 1343 # Produce the "else" section. 1344 1345 if node.else_ is not None: 1346 self.set_label(exit_label) 1347 self.dispatch(node.else_) 1348 1349 # After the loop... 1350 1351 self.set_label(exit_label) 1352 1353 # Compilation duties... 1354 1355 self.discard_temp(temp_iterator) 1356 1357 def visitFrom(self, node): pass 1358 1359 def visitFunction(self, node): 1360 1361 # Only store the name when visiting this node from outside. 1362 1363 if self.unit is not node.unit: 1364 self.new_op(LoadConst(node.unit)) 1365 1366 self.record_value() 1367 self._visitName(node, self.name_store_instructions) # AssName equivalent 1368 self.set_source() 1369 self.discard_value() 1370 1371 self._generateFunctionDefaults(node.unit) 1372 1373 # Visiting of the code occurs when get_code is invoked on this node. 1374 1375 else: 1376 self.dispatch(node.code) 1377 if not isinstance(self.last_op(), Return): 1378 self.dispatch(compiler.ast.Name("None")) 1379 self.new_op(StoreResult()) 1380 1381 self.new_op(Return()) 1382 1383 def visitGenExpr(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "GenExpr") 1384 1385 def visitGenExprFor(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "GenExprFor") 1386 1387 def visitGenExprIf(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "GenExprIf") 1388 1389 def visitGenExprInner(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "GenExprInner") 1390 1391 def visitGetattr(self, node): 1392 self._visitAttr(node, self.attribute_load_instructions) 1393 1394 def visitGlobal(self, node): pass 1395 1396 def visitIf(self, node): 1397 first = 1 1398 exit_label = self.new_label() 1399 1400 clauses = node.tests + [(None, node.else_)] 1401 last_clause = clauses[-1] 1402 1403 for clause in clauses: 1404 test, body = clause 1405 if body is None: 1406 break 1407 if not first: 1408 self.set_label(next_label) 1409 if test is not None: 1410 self.dispatch(test) 1411 next_label = self.new_label() 1412 self.new_op(JumpIfFalse(next_label)) 1413 self.dispatch(body) 1414 if clause is not last_clause: 1415 self.new_op(Jump(exit_label)) 1416 first = 0 1417 1418 self.set_label(exit_label) 1419 1420 def visitImport(self, node): pass 1421 1422 def visitInvert(self, node): 1423 self._visitUnary(node, "__invert__") 1424 1425 def visitKeyword(self, node): pass 1426 1427 def visitLambda(self, node): 1428 1429 # Produce the reference to this function when visiting this node from 1430 # outside. 1431 1432 if self.unit is not node.unit: 1433 temp = self._generateFunctionDefaults(node.unit) 1434 self.new_op(LoadConst(node.unit)) 1435 1436 # Populate the new object required for the function. 1437 1438 if temp is not None: 1439 self.record_value() 1440 self.new_op(temp) 1441 self.new_op(StoreCallable()) 1442 self.set_source() 1443 self.discard_value() 1444 1445 self.new_op(temp) 1446 self.discard_temp(temp) 1447 1448 # Visiting of the code occurs when get_code is invoked on this node. 1449 1450 else: 1451 self.dispatch(node.code) 1452 self.new_op(StoreResult()) 1453 self.new_op(Return()) 1454 1455 def visitLeftShift(self, node): 1456 self._visitBinary(node, "__lshift__", "__rlshift__") 1457 1458 def visitList(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "List") 1459 1460 def visitListComp(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "ListComp") 1461 1462 def visitListCompFor(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "ListCompFor") 1463 1464 def visitListCompIf(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "ListCompIf") 1465 1466 def visitMod(self, node): 1467 self._visitBinary(node, "__mod__", "__rmod__") 1468 1469 def visitModule(self, node): 1470 self.dispatch(node.node) 1471 1472 def visitMul(self, node): 1473 self._visitBinary(node, "__mul__", "__rmul__") 1474 1475 def visitName(self, node): 1476 if node.name == "None": 1477 const = self.module.constant_values[None] 1478 self.new_op(LoadConst(const)) 1479 else: 1480 self._visitName(node, self.name_load_instructions) 1481 1482 def visitNot(self, node): 1483 next_label = self.new_label() 1484 true_label = self.new_label() 1485 1486 self.dispatch(node.expr) 1487 self.new_op(TestBoolean()) 1488 self.new_op(JumpIfTrue(true_label)) 1489 self.load_builtin("True", node) 1490 self.new_op(Jump(next_label)) 1491 1492 self.set_label(true_label) 1493 self.load_builtin("False", node) 1494 self.set_label(next_label) 1495 1496 # Prevent incorrect optimisation. 1497 1498 self.active = None 1499 1500 def visitOr(self, node): 1501 next_label = self.new_label() 1502 1503 for n in node.nodes[:-1]: 1504 self.dispatch(n) 1505 self.new_op(TestBoolean()) 1506 self.new_op(JumpIfTrue(next_label)) 1507 1508 self.dispatch(node.nodes[-1]) 1509 self.set_label(next_label) 1510 1511 # Prevent incorrect optimisation. 1512 1513 self.active = None 1514 1515 def visitPass(self, node): pass 1516 1517 def visitPower(self, node): 1518 self._visitBinary(node, "__pow__", "__rpow__") 1519 1520 def visitPrint(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Print") 1521 1522 def visitPrintnl(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Printnl") 1523 1524 def visitRaise(self, node): 1525 # NOTE: expr1 only => instance provided 1526 self.dispatch(node.expr1) 1527 1528 if node.expr2 is not None: 1529 temp = self._optimise_temp_storage() 1530 1531 self.dispatch(node.expr2) 1532 temp_arg = self._optimise_temp_storage() 1533 1534 self._startCallFunc() 1535 self.new_op(temp_arg) 1536 self.new_op(StoreFrame(0)) 1537 self._doCallFunc(temp) 1538 self._endCallFunc(temp) 1539 1540 self.discard_temp(temp_arg) 1541 1542 self.new_op(StoreException()) 1543 self.new_op(RaiseException()) 1544 1545 def visitReturn(self, node): 1546 if node.value is not None: 1547 self.dispatch(node.value) 1548 else: 1549 self.dispatch(compiler.ast.Name("None")) 1550 1551 self.new_op(StoreResult()) 1552 self.new_op(Return()) 1553 1554 def visitRightShift(self, node): 1555 self._visitBinary(node, "__rshift__", "__rrshift__") 1556 1557 def visitSlice(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Slice") 1558 1559 def visitStmt(self, node): 1560 for n in node.nodes: 1561 self.dispatch(n) 1562 1563 def visitSub(self, node): 1564 self._visitBinary(node, "__sub__", "__rsub__") 1565 1566 def visitSubscript(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Subscript") 1567 1568 def visitTryExcept(self, node): 1569 exit_label = self.new_label() 1570 success_label = self.new_label() 1571 handler_label = self.new_label() 1572 1573 self.add_exception_labels(handler_label, exit_label) 1574 1575 # Try... 1576 # Produce the code, then jump to the exit. 1577 1578 self.new_op(PushHandler(handler_label)) 1579 self.dispatch(node.body) 1580 self.new_op(PopHandler()) 1581 self.new_op(Jump(exit_label)) 1582 1583 # Start of handlers. 1584 1585 self.set_label(handler_label) 1586 self.new_op(PopHandler()) 1587 1588 for name, assignment, handler in node.handlers: 1589 next_label = self.new_label() 1590 1591 # Test the given exception against the current exception. 1592 1593 if name is not None: 1594 self.dispatch(name) 1595 self.new_op(CheckException()) 1596 self.new_op(JumpIfFalse(next_label)) 1597 1598 # Handle assignment to exception variable. 1599 1600 if assignment is not None: 1601 self.dispatch(assignment) 1602 1603 # Produce the handler code, then jump to the exit. 1604 1605 self.dispatch(handler) 1606 self.new_op(Jump(exit_label)) 1607 1608 self.set_label(next_label) 1609 1610 # Unhandled exceptions. 1611 1612 #self.new_op(LoadException()) 1613 self.new_op(RaiseException()) 1614 1615 # Optional else clause. 1616 1617 if node.else_ is not None: 1618 self.dispatch(node.else_) 1619 1620 self.set_label(exit_label) 1621 self.drop_exception_labels() 1622 1623 def visitTryFinally(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "TryFinally") 1624 1625 def visitTuple(self, node): 1626 self.new_op(MakeObject(len(node.nodes))) 1627 temp = self.get_temp() 1628 1629 for i, n in enumerate(node.nodes): 1630 self.dispatch(n) 1631 self.record_value() 1632 self.new_op(temp) 1633 self.new_op(StoreAttr(Attr(i, None, None, None))) 1634 self.set_source() 1635 self.discard_value() 1636 1637 self.new_op(temp) 1638 self.discard_temp(temp) 1639 1640 def visitUnaryAdd(self, node): 1641 self._visitUnary(node, "__pos__") 1642 1643 def visitUnarySub(self, node): 1644 self._visitUnary(node, "__neg__") 1645 1646 def visitWhile(self, node): 1647 exit_label = self.new_label() 1648 next_label = self.new_label() 1649 else_label = self.new_label() 1650 1651 self.set_label(next_label) 1652 self.dispatch(node.test) 1653 if node.else_ is not None: 1654 self.new_op(JumpIfFalse(else_label)) 1655 else: 1656 self.new_op(JumpIfFalse(exit_label)) 1657 1658 self.add_loop_labels(next_label, exit_label) 1659 1660 self.dispatch(node.body) 1661 self.new_op(Jump(next_label)) 1662 1663 if node.else_ is not None: 1664 self.set_label(else_label) 1665 self.dispatch(node.else_) 1666 1667 self.set_label(exit_label) 1668 self.drop_loop_labels() 1669 1670 # Prevent incorrect optimisation. 1671 1672 self.active = None 1673 1674 def visitWith(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "With") 1675 1676 def visitYield(self, node): raise TranslationNotImplementedError(self.module.full_name(), node, "Yield") 1677 1678 # Useful data. 1679 1680 comparison_methods = { 1681 "==" : ("__eq__", "__ne__"), 1682 "!=" : ("__ne__", "__eq__"), 1683 "<" : ("__lt__", "__gt__"), 1684 "<=" : ("__le__", "__ge__"), 1685 ">=" : ("__ge__", "__le__"), 1686 ">" : ("__gt__", "__lt__"), 1687 "is" : None, 1688 "is not" : None, 1689 "in" : None, 1690 "not in" : None 1691 } 1692 1693 # vim: tabstop=4 expandtab shiftwidth=4