1 #!/usr/bin/env python 2 3 """ 4 A really simple virtual processor employing a simple set of instructions which 5 ignore low-level operations and merely concentrate on variable access, structure 6 access, structure allocation and function invocations. 7 8 Copyright (C) 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk> 9 10 This program is free software; you can redistribute it and/or modify it under 11 the terms of the GNU General Public License as published by the Free Software 12 Foundation; either version 3 of the License, or (at your option) any later 13 version. 14 15 This program is distributed in the hope that it will be useful, but WITHOUT 16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 18 details. 19 20 You should have received a copy of the GNU General Public License along with 21 this program. If not, see <http://www.gnu.org/licenses/>. 22 23 -------- 24 25 The execution model of the virtual processor involves the following things: 26 27 * Memory contains constants, global variable 28 references and program code 29 30 * PC (program counter) stack contains the return address associated 31 with each function invocation 32 33 * Frame stack contains invocation frames in use and in 34 preparation plus temporary storage 35 36 * Local frame pointer stack refers to the frames in the frame stack 37 38 * Invocation frame pointer stack 39 40 * Exception handler stack 41 42 * Exception handler locals stack refers to the state of the local frame 43 pointer stack 44 45 * Exception handler PC stack refers to the state of the PC stack 46 47 * Registers: current value, 48 boolean status value, 49 source value, 50 current result, 51 current exception, 52 current callable 53 """ 54 55 from micropython.program import ReplaceableContext, PlaceholderContext 56 import operator 57 58 class IllegalInstruction(Exception): 59 pass 60 61 class IllegalAddress(Exception): 62 def __init__(self, address): 63 self.address = address 64 def __repr__(self): 65 return "IllegalAddress(%r)" % self.address 66 def __str__(self): 67 return repr(self) 68 69 class EmptyPCStack(Exception): 70 pass 71 72 class EmptyFrameStack(Exception): 73 pass 74 75 class BreakpointReached(Exception): 76 pass 77 78 class RSVPMachine: 79 80 "A really simple virtual processor." 81 82 def __init__(self, memory, objlist, paramlist, true_constant, false_constant, pc=None, debug=0): 83 84 """ 85 Initialise the processor with a 'memory' (a list of values containing 86 instructions and data), the object and parameter lists 'objlist' and 87 'paramlist', the addresses 'true_constant' and 'false_constant', and the 88 optional program counter 'pc'. 89 """ 90 91 self.memory = memory 92 self._objlist = objlist 93 self._paramlist = paramlist 94 self.objlist = objlist.as_raw() 95 self.paramlist = paramlist.as_raw() 96 self.true_constant = true_constant 97 self.false_constant = false_constant 98 99 self.pc = pc or 0 100 self.debug = debug 101 102 # Stacks. 103 104 self.pc_stack = [] 105 self.frame_stack = [] 106 self.local_sp_stack = [0] 107 self.invocation_sp_stack = [] 108 self.handler_stack = [len(self.memory) - 1] # final handler is the end of the code 109 self.handler_local_sp_stack = [] 110 self.handler_pc_stack = [] 111 112 # Registers. 113 114 self.instruction = None 115 self.operand = None 116 self.value = None 117 self.status = None 118 self.source = None 119 self.callable = None 120 self.result = None 121 self.exception = None 122 123 # Constants. 124 125 cls = self._get_class("__builtins__", "AttributeError") 126 self.attr_error = cls.location 127 self.attr_error_instance = cls.instance_template_location 128 cls = self._get_class("__builtins__", "TypeError") 129 self.type_error = cls.location 130 self.type_error_instance = cls.instance_template_location 131 cls = self._get_class("__builtins__", "IndexError") 132 self.index_error = cls.location 133 self.index_error_instance = cls.instance_template_location 134 135 # Native class constants. 136 137 cls = self._get_class("__builtins__", "int") 138 self.int_class = cls.location 139 self.int_instance = cls.instance_template_location 140 cls = self._get_class("__builtins__", "list") 141 self.list_instance = cls.instance_template_location 142 143 # Debugging attributes. 144 145 self.breakpoints = set() 146 147 def _get_class(self, module, name): 148 return self._objlist.access(module, name).get_value() 149 150 # Debugging methods. 151 152 def dump(self): 153 print "PC", self.pc, "->", self.load(self.pc) 154 print "PC stack", self.pc_stack 155 print "Frame stack", self.frame_stack 156 print "Local stack pointers", self.local_sp_stack 157 print "Invocation stack pointers", self.invocation_sp_stack 158 print "Handler stack", self.handler_stack 159 print "Handler frame stack", self.handler_local_sp_stack 160 print "Handler PC stack", self.handler_pc_stack 161 print 162 print "Instruction", self.instruction 163 print "Operand", self.operand 164 print "Value", self.value 165 print "Status", self.status 166 print "Source", self.source 167 print "Callable", self.callable 168 print "Result", self.result 169 print "Exception", self.exception 170 171 def show(self): 172 self.show_memory(self.memory, 0) 173 174 def show_pc(self, run_in=10): 175 start = max(0, self.pc - run_in) 176 end = self.pc + run_in 177 memory = self.memory[start:end] 178 self.show_memory(memory, start) 179 180 def show_memory(self, memory, start): 181 for i, x in enumerate(memory): 182 location = start + i 183 if location == self.pc: 184 print "->", 185 else: 186 print " ", 187 print "%5d %r" % (location, x) 188 189 def step(self, dump=0): 190 self.execute() 191 self.show_pc() 192 if dump: 193 self.dump() 194 195 def set_break(self, location): 196 self.breakpoints.add(location) 197 198 # Internal operations. 199 200 def load(self, address): 201 202 "Return the value at the given 'address'." 203 204 try: 205 return self.memory[address] 206 except IndexError: 207 raise IllegalAddress(address) 208 except TypeError: 209 raise IllegalAddress(address) 210 211 def save(self, address, value): 212 213 "Save to the given 'address' the specified 'value'." 214 215 try: 216 self.memory[address] = value 217 except IndexError: 218 raise IllegalAddress(address) 219 except TypeError: 220 raise IllegalAddress(address) 221 222 def new(self, size): 223 224 """ 225 Allocate space of the given 'size', returning the address of the space. 226 """ 227 228 addr = len(self.memory) 229 for i in range(0, size): 230 self.memory.append(None) 231 return addr 232 233 def push_pc(self, data): 234 235 "Push 'data' onto the PC stack." 236 237 self.pc_stack.append(data) 238 239 def pull_pc(self): 240 241 "Pull a value from the PC stack and return it." 242 243 try: 244 return self.pc_stack.pop() 245 except IndexError: 246 raise EmptyPCStack 247 248 def run(self): 249 250 "Execute code in the memory, starting from the current PC address." 251 252 breakpoint = 0 253 254 try: 255 while 1: 256 self.execute() 257 except EmptyPCStack: 258 pass 259 except BreakpointReached: 260 breakpoint = 1 261 262 print "Execution terminated", 263 if self.exception is not None: 264 ref = self.exception 265 addr = self.load(ref + 1) 266 print "with exception:", self.load(ref) 267 print "At address %d: %r" % (addr, self.load(addr)) 268 elif breakpoint: 269 print "with breakpoint." 270 print "At address", self.pc 271 else: 272 print "successfully." 273 274 def test(self, module): 275 276 """ 277 Test the code in the memory by running the code and investigating the 278 contents of variables. Use 'module' to identify result variables. 279 """ 280 281 self.run() 282 success = 1 283 284 if self.exception is None: 285 for name in module.keys(): 286 if name.startswith("result"): 287 label, expected = name.split("_") 288 attr = module[name] 289 290 # NOTE: Assumptions about headers and content made. 291 292 attr_location = module.location + 1 + attr.position 293 context, ref = self.load(attr_location) 294 295 if ref is not None: 296 content = self.load(ref + 1) 297 print label, expected, content 298 success = success and (int(expected) == content) 299 else: 300 print label, expected, "missing" 301 success = 0 302 303 return success 304 else: 305 return 0 306 307 def execute(self): 308 309 "Execute code in the memory at the current PC address." 310 311 if self.pc in self.breakpoints: 312 self.breakpoints.remove(self.pc) 313 raise BreakpointReached 314 315 self.instruction = self.load(self.pc) 316 317 # Process any inputs of the instruction. 318 319 self.process_inputs() 320 321 # Perform the instruction itself. 322 323 next_pc = self.perform(self.instruction) 324 325 # Update the program counter. 326 327 if next_pc is None: 328 self.pc += 1 329 else: 330 self.pc = next_pc 331 332 def get_method(self, instruction): 333 334 "Return the handler method for the given 'instruction'." 335 336 instruction_name = instruction.__class__.__name__ 337 if self.debug: 338 print "%8d %s" % (self.pc, instruction_name) 339 method = getattr(self, instruction_name, None) 340 if method is None: 341 raise IllegalInstruction, (self.pc, instruction_name) 342 return method 343 344 def perform(self, instruction): 345 346 "Perform the 'instruction', returning the next PC value or None." 347 348 self.operand = instruction.get_operand() 349 method = self.get_method(instruction) 350 return method() 351 352 def process_inputs(self): 353 354 """ 355 Process any inputs of the current instruction. This permits any directly 356 connected sub-instructions to produce the effects that separate 357 instructions would otherwise have. 358 """ 359 360 value = self.value 361 if self.instruction.source is not None: 362 self.perform(self.instruction.source) 363 self.source = self.value 364 self.value = value 365 if self.instruction.input is not None: 366 self.perform(self.instruction.input) 367 368 def jump(self, addr, next): 369 370 """ 371 Jump to the subroutine at (or identified by) 'addr'. If 'addr' 372 identifies a library function then invoke the library function and set 373 PC to 'next' afterwards; otherwise, set PC to 'addr'. 374 """ 375 376 # Trap library functions introduced through the use of strings instead 377 # of proper locations. 378 379 if isinstance(addr, str): 380 handler = self.native_functions[addr](self) 381 if handler is None: 382 return next 383 else: 384 return handler 385 else: 386 self.push_pc(self.pc + 1) 387 return addr 388 389 # Instructions. 390 391 def LoadConst(self): 392 self.value = self.operand, self.operand 393 394 def LoadClass(self): 395 self.value = PlaceholderContext, self.operand 396 397 def LoadFunction(self): 398 self.value = ReplaceableContext, self.operand 399 400 def LoadName(self): 401 frame = self.local_sp_stack[-1] 402 self.value = self.frame_stack[frame + self.operand] 403 404 def StoreName(self): 405 frame = self.local_sp_stack[-1] 406 self.frame_stack[frame + self.operand] = self.source # uses the source value 407 408 LoadTemp = LoadName 409 410 def StoreTemp(self): 411 frame = self.local_sp_stack[-1] 412 self.frame_stack[frame + self.operand] = self.value 413 414 def LoadAddress(self): 415 # Preserve context (potentially null). 416 self.value = self.load(self.operand) 417 418 def LoadAddressContext(self): 419 context, ref = self.load(self.operand) 420 inst_context, inst_ref = self.value 421 self.value = inst_ref, ref 422 423 def LoadAddressContextCond(self): 424 context, ref = self.load(self.operand) 425 inst_context, inst_ref = self.value 426 self.value = self._LoadAddressContextCond(context, ref, inst_ref) 427 428 def StoreAddress(self): 429 # Preserve context. 430 self.save(self.operand, self.source) 431 432 def StoreAddressContext(self): 433 # Overwrite context if null. 434 context_context, context_ref = self.value 435 source_context, source_ref = self.source 436 if source_context is ReplaceableContext: 437 context = context_ref 438 else: 439 context = source_context 440 self.save(self.operand, (context, source_ref)) 441 442 def MakeInstance(self): 443 size = self.operand 444 context, ref = self.value 445 # NOTE: Referencing the instance template. 446 addr = self._MakeObject(size, ref - 1) 447 # Introduce object as context for the new object. 448 self.value = addr, addr 449 450 def LoadAttr(self): 451 context, ref = self.value 452 # Retrieved context should already be appropriate for the instance. 453 # NOTE: Adding 1 to skip any header. 454 self.value = self.load(ref + self.operand + 1) 455 456 def StoreAttr(self): 457 context, ref = self.value 458 # Target should already be an instance. 459 # NOTE: Adding 1 to skip any header. 460 self.save(ref + self.operand + 1, self.source) 461 462 def LoadAttrIndex(self): 463 context, ref = self.value 464 data = self.load(ref) 465 element = self.objlist[data.classcode + self.operand] 466 467 if element is not None: 468 attr_index, static_attr, offset = element 469 if attr_index == self.operand: 470 if static_attr: 471 self.value = self.load(offset) # offset is address of class/module attribute 472 else: 473 self.value = self.load(ref + offset) 474 return 475 476 self.exception = self._MakeObject(2, self.attr_error_instance) 477 return self.RaiseException() 478 479 # LoadAttrIndexContext not defined. 480 481 def LoadAttrIndexContextCond(self): 482 inst_context, inst_ref = self.value 483 data = self.load(inst_ref) 484 element = self.objlist[data.classcode + self.operand] 485 486 if element is not None: 487 attr_index, static_attr, offset = element 488 if attr_index == self.operand: 489 if static_attr: 490 loaded_context, loaded_ref = self.load(offset) # offset is address of class/module attribute 491 if data.attrcode is None: # absent attrcode == class/module 492 self.value = loaded_context, loaded_ref 493 else: 494 self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, inst_ref) 495 else: 496 self.value = self.load(inst_ref + offset) 497 return 498 499 self.exception = self._MakeObject(2, self.attr_error_instance) 500 return self.RaiseException() 501 502 def StoreAttrIndex(self): 503 context, ref = self.value 504 data = self.load(ref) 505 element = self.objlist[data.classcode + self.operand] 506 507 if element is not None: 508 attr_index, static_attr, offset = element 509 if attr_index == self.operand: 510 if static_attr: 511 self.exception = self._MakeObject(2, self.type_error_instance) 512 return self.RaiseException() 513 else: 514 self.save(ref + offset, self.source) 515 return 516 517 self.exception = self._MakeObject(2, self.attr_error_instance) 518 return self.RaiseException() 519 520 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 521 522 def MakeFrame(self): 523 self.invocation_sp_stack.append(len(self.frame_stack)) 524 self.frame_stack.extend([None] * self.operand) 525 526 def DropFrame(self): 527 self.local_sp_stack.pop() 528 frame = self.invocation_sp_stack.pop() 529 self.frame_stack = self.frame_stack[:frame] # reset stack before call 530 531 def StoreFrame(self): 532 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 533 self.frame_stack[frame + self.operand] = self.value 534 535 def StoreFrameIndex(self): 536 context, ref = self.value 537 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 538 data = self.load(ref) 539 element = self.paramlist[data.funccode + self.operand] 540 541 if element is not None: 542 # NOTE: Need to ensure correct positioning where a context has been generated. 543 param_index, offset = element 544 if param_index == self.operand: 545 self.frame_stack[frame + offset] = self.source 546 return 547 548 self.exception = self._MakeObject(2, self.type_error_instance) 549 return self.RaiseException() 550 551 def LoadCallable(self): 552 context, ref = self.value 553 data = self.load(ref) 554 self.callable = data.codeaddr 555 556 def StoreCallable(self): 557 context, ref = self.value 558 # NOTE: Should improve the representation and permit direct saving. 559 data = self.load(ref) 560 self.save(ref, data.with_callable(self.callable)) 561 562 def LoadContext(self): 563 context, ref = self.value 564 # NOTE: Omission of the context of the context would make things like 565 # NOTE: self() inside methods impossible. 566 self.value = context, context 567 568 def CheckContext(self): 569 self.status = self.value[1] is not ReplaceableContext 570 571 def CheckClass(self): 572 context, ref = self.value 573 if ref in (ReplaceableContext, PlaceholderContext): 574 self.status = 0 575 return 576 577 data = self.load(ref) 578 579 # Classes are not themselves usable as the self argument. 580 # NOTE: This may change at some point. 581 # However, where classes appear as the context, instance 582 # compatibility is required in the first argument. 583 584 self.status = data.attrcode is None # absent attrcode == class 585 586 def CheckFrame(self): 587 (nargs, ndefaults, has_star) = self.operand 588 589 # The frame is actually installed as the locals. 590 # Retrieve the context from the first local. 591 592 frame = self.local_sp_stack[-1] 593 nlocals = len(self.frame_stack[frame:]) 594 595 # NOTE: Not testing (nlocals <= nargs or has_star) due to imprecise 596 # NOTE: invocation frame removal (after frame adjustment). 597 598 if not ((nargs - ndefaults) <= nlocals): 599 raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 600 self.exception = self._MakeObject(2, self.type_error_instance) 601 return self.RaiseException() 602 603 def FillDefaults(self): 604 context, ref = self.value 605 (nargs, ndefaults) = self.operand 606 607 # The frame is actually installed as the locals. 608 609 frame = self.local_sp_stack[-1] 610 nlocals = len(self.frame_stack[frame:]) 611 612 # Support population of defaults. 613 # This involves copying the "attributes" of a function into the frame. 614 615 default = nlocals - (nargs - ndefaults) 616 self.frame_stack.extend([None] * (nargs - nlocals)) 617 pos = nlocals 618 619 while pos < nargs: 620 self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header 621 default += 1 622 pos += 1 623 624 def CheckSelf(self): 625 context, ref = self.value 626 context_context, context_ref = self.source 627 628 # Check the details of the proposed context and the target's context. 629 630 self.status = self._CheckInstance(ref, context_ref) 631 632 def JumpInFrame(self): 633 codeaddr = self.callable 634 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 635 636 def JumpWithFrame(self): 637 codeaddr = self.callable 638 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 639 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 640 641 def JumpWithFrameDirect(self): 642 operand = self.operand 643 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 644 return self.jump(operand, self.pc + 1) # return to the instruction after this one 645 646 def ExtendFrame(self): 647 self.frame_stack.extend([None] * self.operand) 648 649 def AdjustFrame(self): 650 self.invocation_sp_stack[-1] += self.operand 651 652 def Return(self): 653 return self.pull_pc() 654 655 def LoadResult(self): 656 self.value = self.result 657 658 def StoreResult(self): 659 self.result = self.value 660 661 def Jump(self): 662 return self.operand 663 664 def JumpIfTrue(self): 665 if self.status: 666 return self.operand 667 668 def JumpIfFalse(self): 669 if not self.status: 670 return self.operand 671 672 def LoadException(self): 673 self.value = self.exception, self.exception 674 675 def StoreException(self): 676 self.exception = self.value[1] 677 678 def ClearException(self): 679 self.exception = None 680 681 def RaiseException(self): 682 # NOTE: Adding the program counter as the first attribute. 683 self.save(self.exception + 1, self.pc) 684 # Jumping to the current handler. 685 return self.handler_stack[-1] 686 687 def PushHandler(self): 688 self.handler_stack.append(self.operand) 689 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 690 self.handler_pc_stack.append(len(self.pc_stack)) 691 692 def PopHandler(self): 693 # Reduce the local frame pointer stack to refer to the handler's frame. 694 self.local_sp_stack = self.local_sp_stack[:self.handler_local_sp_stack.pop()] 695 # Reduce the PC stack to discard all superfluous return addresses. 696 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 697 self.handler_stack.pop() 698 699 def CheckException(self): 700 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1]) 701 702 def TestIdentity(self): 703 self.status = self.value[1] == self.source[1] 704 705 def TestIdentityAddress(self): 706 self.status = self.value[1] == self.operand 707 708 # LoadBoolean is implemented in the generated code. 709 # StoreBoolean is implemented by testing against the True value. 710 711 def InvertBoolean(self): 712 self.status = not self.status 713 714 # Common implementation details. 715 716 def _CheckInstance(self, ref, cls): 717 data = self.load(ref) 718 target_data = self.load(cls) 719 720 # Insist on instance vs. class. 721 722 if data.attrcode is None: # absent attrcode == class/module 723 return 0 724 725 if target_data.attrcode is not None: # present attrcode == instance 726 return 0 727 728 # Find the table entry for the descendant. 729 730 element = self.objlist[target_data.classcode + data.attrcode] 731 732 if element is not None: 733 attr_index, static_attr, offset = element 734 return attr_index == data.attrcode 735 else: 736 return 0 737 738 def _MakeObject(self, size, ref): 739 # Load the template. 740 data = self.load(ref) 741 addr = self.new(size) 742 # Save the header, overriding the size. 743 self.save(addr, data.with_size(size)) 744 return addr 745 746 def _LoadAddressContextCond(self, context, ref, inst_ref): 747 # Check the instance context against the target's context. 748 # This provides the context overriding for methods. 749 if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_ref, context): 750 # Replace the context with the instance. 751 return inst_ref, ref 752 else: 753 return context, ref 754 755 # Native function implementations. 756 757 def builtins_int_arithmetic_op(self, op): 758 frame = self.local_sp_stack[-1] 759 760 # Get operands addresses. 761 762 left_context, left = self.frame_stack[frame] 763 right_context, right = self.frame_stack[frame + 1] 764 765 # Test operand suitability. 766 # NOTE: Support other types. 767 768 if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): 769 self.exception = self._MakeObject(2, self.type_error_instance) 770 return self.RaiseException() 771 772 # NOTE: Assume single location for data. 773 774 left_data = left + 1 775 right_data = right + 1 776 777 # Make a new object. 778 779 addr = self._MakeObject(2, self.int_instance) 780 781 # Store the result. 782 # NOTE: The data is considered ready to use. 783 784 self.save(addr + 1, op(self.load(left_data), self.load(right_data))) 785 786 # Return the new object. 787 # Introduce object as context for the new object. 788 789 self.result = addr, addr 790 791 def builtins_int_add(self): 792 return self.builtins_int_arithmetic_op(operator.add) 793 794 def builtins_int_sub(self): 795 return self.builtins_int_arithmetic_op(operator.sub) 796 797 def builtins_int_bool(self): 798 frame = self.local_sp_stack[-1] 799 800 # Get operands addresses. 801 802 left_context, left = self.frame_stack[frame] 803 804 # Test operand suitability. 805 806 if not self._CheckInstance(left, self.int_class): 807 self.exception = self._MakeObject(2, self.type_error_instance) 808 return self.RaiseException() 809 810 # NOTE: Assume single location for data. 811 812 left_data = left + 1 813 814 # Test the data. 815 # NOTE: The data is considered ready to use. 816 817 if self.load(left_data) != 0: 818 self.result = self.true_constant, self.true_constant 819 else: 820 self.result = self.false_constant, self.false_constant 821 822 def builtins_int_neg(self): 823 frame = self.local_sp_stack[-1] 824 825 # Get operands addresses. 826 827 left_context, left = self.frame_stack[frame] 828 829 # Test operand suitability. 830 831 if not self._CheckInstance(left, self.int_class): 832 self.exception = self._MakeObject(2, self.type_error_instance) 833 return self.RaiseException() 834 835 # NOTE: Assume single location for data. 836 837 left_data = left + 1 838 839 # Make a new object. 840 841 addr = self._MakeObject(2, self.int_instance) 842 843 # Store the result. 844 # NOTE: The data is considered ready to use. 845 846 self.save(addr + 1, -self.load(left_data)) 847 848 # Return the new object. 849 # Introduce object as context for the new object. 850 851 self.result = addr, addr 852 853 def builtins_int_op(self, op, true_if_incompatible): 854 frame = self.local_sp_stack[-1] 855 856 # Get operands addresses. 857 858 left_context, left = self.frame_stack[frame] 859 right_context, right = self.frame_stack[frame + 1] 860 861 # Test operand suitability. 862 # NOTE: Support other types. 863 # NOTE: Handle comparisons of incompatible types more appropriately. 864 865 if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): 866 if true_if_incompatible: 867 self.result = self.true_constant, self.true_constant 868 else: 869 self.result = self.false_constant, self.false_constant 870 return 871 872 # NOTE: Assume single location for data. 873 874 left_data = left + 1 875 right_data = right + 1 876 877 # Test the data. 878 # NOTE: The data is considered ready to use. 879 880 if op(self.load(left_data), self.load(right_data)): 881 self.result = self.true_constant, self.true_constant 882 else: 883 self.result = self.false_constant, self.false_constant 884 885 def builtins_int_lt(self): 886 return self.builtins_int_op(operator.lt, 0) 887 888 def builtins_int_le(self): 889 return self.builtins_int_op(operator.le, 0) 890 891 def builtins_int_gt(self): 892 return self.builtins_int_op(operator.gt, 0) 893 894 def builtins_int_ge(self): 895 return self.builtins_int_op(operator.ge, 0) 896 897 def builtins_int_eq(self): 898 return self.builtins_int_op(operator.eq, 0) 899 900 def builtins_int_ne(self): 901 return self.builtins_int_op(operator.ne, 1) 902 903 def builtins_bool_bool(self): 904 frame = self.local_sp_stack[-1] 905 906 # Get operands addresses. 907 908 left_context, left = self.frame_stack[frame] 909 self.result = left, left 910 911 def builtins_list_new(self): 912 frame = self.local_sp_stack[-1] 913 914 # The first parameter should be empty. 915 # NOTE: Specific copying of tuples/lists. 916 917 args_context, args = self.frame_stack[frame + 1] 918 header = self.load(args) 919 920 list = self._MakeObject(header.size, self.list_instance) 921 for i in range(1, header.size): 922 self.save(list + i, self.load(args + i)) 923 924 self.result = list, list 925 926 def builtins_list_getitem(self): 927 frame = self.local_sp_stack[-1] 928 929 # Get operands addresses. 930 931 obj_context, obj = self.frame_stack[frame] 932 item_context, item = self.frame_stack[frame + 1] 933 934 header = self.load(obj) 935 nelements = header.size - 1 936 937 # NOTE: Assume single location for data. 938 939 item_pos = self.load(item + 1) 940 if item_pos >= 0 and item_pos < nelements: 941 pass 942 elif item_pos < 0 and item_pos >= -nelements: 943 item_pos = nelements + item_pos 944 else: 945 self.exception = self._MakeObject(2, self.index_error_instance) 946 return self.RaiseException() 947 948 self.result = self.load(obj + 1 + item_pos) 949 950 def builtins_object_init(self): 951 pass 952 953 native_functions = { 954 955 # Native method implementations: 956 957 "__builtins__.int.__add__" : builtins_int_add, 958 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 959 "__builtins__.int.__sub__" : builtins_int_sub, 960 "__builtins__.int.__iadd__" : builtins_int_add, 961 "__builtins__.int.__bool__" : builtins_int_bool, 962 "__builtins__.int.__neg__" : builtins_int_neg, 963 "__builtins__.int.__lt__" : builtins_int_lt, 964 "__builtins__.int.__le__" : builtins_int_le, 965 "__builtins__.int.__gt__" : builtins_int_gt, 966 "__builtins__.int.__ge__" : builtins_int_ge, 967 "__builtins__.int.__eq__" : builtins_int_eq, 968 "__builtins__.int.__ne__" : builtins_int_ne, 969 "__builtins__.bool.__bool__" : builtins_bool_bool, 970 "__builtins__.list.__getitem__" : builtins_list_getitem, 971 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 972 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 973 974 # Native instantiators: 975 976 "__builtins__.list" : builtins_list_new, 977 } 978 979 # Convenience functions. 980 981 def machine(program, with_builtins=0, debug=0): 982 print "Making the image..." 983 code = program.get_image(with_builtins) 984 print "Getting raw structures..." 985 ot = program.get_object_table() 986 pt = program.get_parameter_table() 987 objlist = ot.as_list() 988 paramlist = pt.as_list() 989 print "Getting raw image..." 990 rc = program.get_raw_image() 991 print "Initialising the machine..." 992 importer = program.get_importer() 993 true_constant = importer.get_constant(True).location 994 false_constant = importer.get_constant(False).location 995 rm = RSVPMachine(rc, objlist, paramlist, true_constant, false_constant, debug=debug) 996 rm.pc = program.code_location 997 print "Returning program occupying %d locations." % len(rm.memory) 998 return rm 999 1000 # vim: tabstop=4 expandtab shiftwidth=4