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