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 content = self.load(ref + 1) 295 print label, expected, content 296 297 success = success and (int(expected) == content) 298 299 return success 300 else: 301 return 0 302 303 def execute(self): 304 305 "Execute code in the memory at the current PC address." 306 307 if self.pc in self.breakpoints: 308 self.breakpoints.remove(self.pc) 309 raise BreakpointReached 310 311 self.instruction = self.load(self.pc) 312 313 # Process any inputs of the instruction. 314 315 self.process_inputs() 316 317 # Perform the instruction itself. 318 319 next_pc = self.perform(self.instruction) 320 321 # Update the program counter. 322 323 if next_pc is None: 324 self.pc += 1 325 else: 326 self.pc = next_pc 327 328 def get_method(self, instruction): 329 330 "Return the handler method for the given 'instruction'." 331 332 instruction_name = instruction.__class__.__name__ 333 if self.debug: 334 print "%8d %s" % (self.pc, instruction_name) 335 method = getattr(self, instruction_name, None) 336 if method is None: 337 raise IllegalInstruction, (self.pc, instruction_name) 338 return method 339 340 def perform(self, instruction): 341 342 "Perform the 'instruction', returning the next PC value or None." 343 344 self.operand = instruction.get_operand() 345 method = self.get_method(instruction) 346 return method() 347 348 def process_inputs(self): 349 350 """ 351 Process any inputs of the current instruction. This permits any directly 352 connected sub-instructions to produce the effects that separate 353 instructions would otherwise have. 354 """ 355 356 value = self.value 357 if self.instruction.source is not None: 358 self.perform(self.instruction.source) 359 self.source = self.value 360 self.value = value 361 if self.instruction.input is not None: 362 self.perform(self.instruction.input) 363 364 def jump(self, addr, next): 365 366 """ 367 Jump to the subroutine at (or identified by) 'addr'. If 'addr' 368 identifies a library function then invoke the library function and set 369 PC to 'next' afterwards; otherwise, set PC to 'addr'. 370 """ 371 372 # Trap library functions introduced through the use of strings instead 373 # of proper locations. 374 375 if isinstance(addr, str): 376 handler = self.native_functions[addr](self) 377 if handler is None: 378 return next 379 else: 380 return handler 381 else: 382 self.push_pc(self.pc + 1) 383 return addr 384 385 # Instructions. 386 387 def LoadConst(self): 388 self.value = self.operand, self.operand 389 390 def LoadClass(self): 391 self.value = PlaceholderContext, self.operand 392 393 def LoadFunction(self): 394 self.value = ReplaceableContext, self.operand 395 396 def LoadName(self): 397 frame = self.local_sp_stack[-1] 398 self.value = self.frame_stack[frame + self.operand] 399 400 def StoreName(self): 401 frame = self.local_sp_stack[-1] 402 self.frame_stack[frame + self.operand] = self.source # uses the source value 403 404 LoadTemp = LoadName 405 406 def StoreTemp(self): 407 frame = self.local_sp_stack[-1] 408 self.frame_stack[frame + self.operand] = self.value 409 410 def LoadAddress(self): 411 # Preserve context (potentially null). 412 self.value = self.load(self.operand) 413 414 def LoadAddressContext(self): 415 context, ref = self.load(self.operand) 416 inst_context, inst_ref = self.value 417 self.value = inst_ref, ref 418 419 def LoadAddressContextCond(self): 420 context, ref = self.load(self.operand) 421 inst_context, inst_ref = self.value 422 self.value = self._LoadAddressContextCond(context, ref, inst_ref) 423 424 def StoreAddress(self): 425 # Preserve context. 426 self.save(self.operand, self.source) 427 428 def StoreAddressContext(self): 429 # Overwrite context if null. 430 context_context, context_ref = self.value 431 source_context, source_ref = self.source 432 if source_context is ReplaceableContext: 433 context = context_ref 434 else: 435 context = source_context 436 self.save(self.operand, (context, source_ref)) 437 438 def MakeInstance(self): 439 size = self.operand 440 context, ref = self.value 441 # NOTE: Referencing the instance template. 442 addr = self._MakeObject(size, ref - 1) 443 # Introduce object as context for the new object. 444 self.value = addr, addr 445 446 def LoadAttr(self): 447 context, ref = self.value 448 # Retrieved context should already be appropriate for the instance. 449 # NOTE: Adding 1 to skip any header. 450 self.value = self.load(ref + self.operand + 1) 451 452 def StoreAttr(self): 453 context, ref = self.value 454 # Target should already be an instance. 455 # NOTE: Adding 1 to skip any header. 456 self.save(ref + self.operand + 1, self.source) 457 458 def LoadAttrIndex(self): 459 context, ref = self.value 460 data = self.load(ref) 461 element = self.objlist[data.classcode + self.operand] 462 463 if element is not None: 464 attr_index, class_attr, offset = element 465 if attr_index == self.operand: 466 if class_attr: 467 self.value = self.load(offset) # offset is address of class attribute 468 else: 469 self.value = self.load(ref + offset) 470 return 471 472 self.exception = self._MakeObject(2, self.attr_error_instance) 473 return self.RaiseException() 474 475 # LoadAttrIndexContext not defined. 476 477 def LoadAttrIndexContextCond(self): 478 inst_context, inst_ref = self.value 479 data = self.load(inst_ref) 480 element = self.objlist[data.classcode + self.operand] 481 482 if element is not None: 483 attr_index, class_attr, offset = element 484 if attr_index == self.operand: 485 if class_attr: 486 loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute 487 if data.attrcode is None: # absent attrcode == class 488 self.value = loaded_context, loaded_ref 489 else: 490 self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, inst_ref) 491 else: 492 self.value = self.load(inst_ref + offset) 493 return 494 495 self.exception = self._MakeObject(2, self.attr_error_instance) 496 return self.RaiseException() 497 498 def StoreAttrIndex(self): 499 context, ref = self.value 500 data = self.load(ref) 501 element = self.objlist[data.classcode + self.operand] 502 503 if element is not None: 504 attr_index, class_attr, offset = element 505 if attr_index == self.operand: 506 if class_attr: 507 self.exception = self._MakeObject(2, self.type_error_instance) 508 return self.RaiseException() 509 else: 510 self.save(ref + offset, self.source) 511 return 512 513 self.exception = self._MakeObject(2, self.attr_error_instance) 514 return self.RaiseException() 515 516 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 517 518 def MakeFrame(self): 519 self.invocation_sp_stack.append(len(self.frame_stack)) 520 self.frame_stack.extend([None] * self.operand) 521 522 def DropFrame(self): 523 self.local_sp_stack.pop() 524 frame = self.invocation_sp_stack.pop() 525 self.frame_stack = self.frame_stack[:frame] # reset stack before call 526 527 def StoreFrame(self): 528 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 529 self.frame_stack[frame + self.operand] = self.value 530 531 def StoreFrameIndex(self): 532 context, ref = self.value 533 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 534 data = self.load(ref) 535 element = self.paramlist[data.funccode + self.operand] 536 537 if element is not None: 538 # NOTE: Need to ensure correct positioning where a context has been generated. 539 param_index, offset = element 540 if param_index == self.operand: 541 self.frame_stack[frame + offset] = self.source 542 return 543 544 self.exception = self._MakeObject(2, self.type_error_instance) 545 return self.RaiseException() 546 547 def LoadCallable(self): 548 context, ref = self.value 549 data = self.load(ref) 550 self.callable = data.codeaddr 551 552 def StoreCallable(self): 553 context, ref = self.value 554 # NOTE: Should improve the representation and permit direct saving. 555 data = self.load(ref) 556 self.save(ref, data.with_callable(self.callable)) 557 558 def LoadContext(self): 559 context, ref = self.value 560 # NOTE: Omission of the context of the context would make things like 561 # NOTE: self() inside methods impossible. 562 self.value = context, context 563 564 def CheckContext(self): 565 self.status = self.value[1] is not ReplaceableContext 566 567 def CheckClass(self): 568 context, ref = self.value 569 if ref in (ReplaceableContext, PlaceholderContext): 570 self.status = 0 571 return 572 573 data = self.load(ref) 574 575 # Classes are not themselves usable as the self argument. 576 # NOTE: This may change at some point. 577 # However, where classes appear as the context, instance 578 # compatibility is required in the first argument. 579 580 self.status = data.attrcode is None # absent attrcode == class 581 582 def CheckFrame(self): 583 (nargs, ndefaults, has_star) = self.operand 584 585 # The frame is actually installed as the locals. 586 # Retrieve the context from the first local. 587 588 frame = self.local_sp_stack[-1] 589 nlocals = len(self.frame_stack[frame:]) 590 591 # NOTE: Not testing (nlocals <= nargs or has_star) due to imprecise 592 # NOTE: invocation frame removal (after frame adjustment). 593 594 if not ((nargs - ndefaults) <= nlocals): 595 raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 596 self.exception = self._MakeObject(2, self.type_error_instance) 597 return self.RaiseException() 598 599 def FillDefaults(self): 600 context, ref = self.value 601 (nargs, ndefaults) = self.operand 602 603 # The frame is actually installed as the locals. 604 605 frame = self.local_sp_stack[-1] 606 nlocals = len(self.frame_stack[frame:]) 607 608 # Support population of defaults. 609 # This involves copying the "attributes" of a function into the frame. 610 611 default = nlocals - (nargs - ndefaults) 612 self.frame_stack.extend([None] * (nargs - nlocals)) 613 pos = nlocals 614 615 while pos < nargs: 616 self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header 617 default += 1 618 pos += 1 619 620 def CheckSelf(self): 621 context, ref = self.value 622 context_context, context_ref = self.source 623 624 # Check the details of the proposed context and the target's context. 625 626 self.status = self._CheckInstance(ref, context_ref) 627 628 def JumpInFrame(self): 629 codeaddr = self.callable 630 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 631 632 def JumpWithFrame(self): 633 codeaddr = self.callable 634 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 635 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 636 637 def JumpWithFrameDirect(self): 638 operand = self.operand 639 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 640 return self.jump(operand, self.pc + 1) # return to the instruction after this one 641 642 def ExtendFrame(self): 643 self.frame_stack.extend([None] * self.operand) 644 645 def AdjustFrame(self): 646 self.invocation_sp_stack[-1] += self.operand 647 648 def Return(self): 649 return self.pull_pc() 650 651 def LoadResult(self): 652 self.value = self.result 653 654 def StoreResult(self): 655 self.result = self.value 656 657 def Jump(self): 658 return self.operand 659 660 def JumpIfTrue(self): 661 if self.status: 662 return self.operand 663 664 def JumpIfFalse(self): 665 if not self.status: 666 return self.operand 667 668 def LoadException(self): 669 self.value = self.exception, self.exception 670 671 def StoreException(self): 672 self.exception = self.value[1] 673 674 def ClearException(self): 675 self.exception = None 676 677 def RaiseException(self): 678 # NOTE: Adding the program counter as the first attribute. 679 self.save(self.exception + 1, self.pc) 680 # Jumping to the current handler. 681 return self.handler_stack[-1] 682 683 def PushHandler(self): 684 self.handler_stack.append(self.operand) 685 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 686 self.handler_pc_stack.append(len(self.pc_stack)) 687 688 def PopHandler(self): 689 # Reduce the local frame pointer stack to refer to the handler's frame. 690 self.local_sp_stack = self.local_sp_stack[:self.handler_local_sp_stack.pop()] 691 # Reduce the PC stack to discard all superfluous return addresses. 692 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 693 self.handler_stack.pop() 694 695 def CheckException(self): 696 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1]) 697 698 def TestIdentity(self): 699 self.status = self.value[1] == self.source[1] 700 701 def TestIdentityAddress(self): 702 self.status = self.value[1] == self.operand 703 704 # LoadBoolean is implemented in the generated code. 705 # StoreBoolean is implemented by testing against the True value. 706 707 def InvertBoolean(self): 708 self.status = not self.status 709 710 # Common implementation details. 711 712 def _CheckInstance(self, ref, cls): 713 data = self.load(ref) 714 target_data = self.load(cls) 715 716 # Insist on instance vs. class. 717 718 if data.attrcode is None: # absent attrcode == class 719 return 0 720 721 if target_data.attrcode is not None: # present attrcode == instance 722 return 0 723 724 # Find the table entry for the descendant. 725 726 element = self.objlist[target_data.classcode + data.attrcode] 727 728 if element is not None: 729 attr_index, class_attr, offset = element 730 return attr_index == data.attrcode 731 else: 732 return 0 733 734 def _MakeObject(self, size, ref): 735 # Load the template. 736 data = self.load(ref) 737 addr = self.new(size) 738 # Save the header, overriding the size. 739 self.save(addr, data.with_size(size)) 740 return addr 741 742 def _LoadAddressContextCond(self, context, ref, inst_ref): 743 # Check the instance context against the target's context. 744 # This provides the context overriding for methods. 745 if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_ref, context): 746 # Replace the context with the instance. 747 return inst_ref, ref 748 else: 749 return context, ref 750 751 # Native function implementations. 752 753 def builtins_int_add(self): 754 frame = self.local_sp_stack[-1] 755 756 # Get operands addresses. 757 758 left_context, left = self.frame_stack[frame] 759 right_context, right = self.frame_stack[frame + 1] 760 761 # Test operand suitability. 762 # NOTE: Support other types. 763 764 if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): 765 self.exception = self._MakeObject(2, self.type_error_instance) 766 return self.RaiseException() 767 768 # NOTE: Assume single location for data. 769 770 left_data = left + 1 771 right_data = right + 1 772 773 # Make a new object. 774 775 addr = self._MakeObject(2, self.int_instance) 776 777 # Store the result. 778 # NOTE: The data is considered ready to use. 779 780 self.save(addr + 1, self.load(left_data) + self.load(right_data)) 781 782 # Return the new object. 783 # Introduce object as context for the new object. 784 785 self.result = addr, addr 786 787 def builtins_int_bool(self): 788 frame = self.local_sp_stack[-1] 789 790 # Get operands addresses. 791 792 left_context, left = self.frame_stack[frame] 793 794 # Test operand suitability. 795 796 if not self._CheckInstance(left, self.int_class): 797 self.exception = self._MakeObject(2, self.type_error_instance) 798 return self.RaiseException() 799 800 # NOTE: Assume single location for data. 801 802 left_data = left + 1 803 804 # Test the data. 805 # NOTE: The data is considered ready to use. 806 807 if self.load(left_data) != 0: 808 self.result = self.true_constant, self.true_constant 809 else: 810 self.result = self.false_constant, self.false_constant 811 812 def builtins_int_neg(self): 813 frame = self.local_sp_stack[-1] 814 815 # Get operands addresses. 816 817 left_context, left = self.frame_stack[frame] 818 819 # Test operand suitability. 820 821 if not self._CheckInstance(left, self.int_class): 822 self.exception = self._MakeObject(2, self.type_error_instance) 823 return self.RaiseException() 824 825 # NOTE: Assume single location for data. 826 827 left_data = left + 1 828 829 # Make a new object. 830 831 addr = self._MakeObject(2, self.int_instance) 832 833 # Store the result. 834 # NOTE: The data is considered ready to use. 835 836 self.save(addr + 1, -self.load(left_data)) 837 838 # Return the new object. 839 # Introduce object as context for the new object. 840 841 self.result = addr, addr 842 843 def builtins_int_op(self, op, true_if_incompatible): 844 frame = self.local_sp_stack[-1] 845 846 # Get operands addresses. 847 848 left_context, left = self.frame_stack[frame] 849 right_context, right = self.frame_stack[frame + 1] 850 851 # Test operand suitability. 852 # NOTE: Support other types. 853 # NOTE: Handle comparisons of incompatible types more appropriately. 854 855 if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): 856 if true_if_incompatible: 857 self.result = self.true_constant, self.true_constant 858 else: 859 self.result = self.false_constant, self.false_constant 860 return 861 862 # NOTE: Assume single location for data. 863 864 left_data = left + 1 865 right_data = right + 1 866 867 # Test the data. 868 # NOTE: The data is considered ready to use. 869 870 if op(self.load(left_data), self.load(right_data)): 871 self.result = self.true_constant, self.true_constant 872 else: 873 self.result = self.false_constant, self.false_constant 874 875 def builtins_int_lt(self): 876 return self.builtins_int_op(operator.lt, 0) 877 878 def builtins_int_le(self): 879 return self.builtins_int_op(operator.le, 0) 880 881 def builtins_int_gt(self): 882 return self.builtins_int_op(operator.gt, 0) 883 884 def builtins_int_ge(self): 885 return self.builtins_int_op(operator.ge, 0) 886 887 def builtins_int_eq(self): 888 return self.builtins_int_op(operator.eq, 0) 889 890 def builtins_int_ne(self): 891 return self.builtins_int_op(operator.ne, 1) 892 893 def builtins_bool_bool(self): 894 frame = self.local_sp_stack[-1] 895 896 # Get operands addresses. 897 898 left_context, left = self.frame_stack[frame] 899 self.result = left, left 900 901 def builtins_list_new(self): 902 frame = self.local_sp_stack[-1] 903 904 # NOTE: Specific copying of tuples/lists. 905 906 args_context, args = self.frame_stack[frame] 907 header = self.load(args) 908 909 list = self._MakeObject(header.size, self.list_instance) 910 for i in range(1, header.size): 911 self.save(list + i, self.load(args + i)) 912 913 self.result = list, list 914 915 def builtins_list_getitem(self): 916 frame = self.local_sp_stack[-1] 917 918 # Get operands addresses. 919 920 obj_context, obj = self.frame_stack[frame] 921 item_context, item = self.frame_stack[frame + 1] 922 923 header = self.load(obj) 924 nelements = header.size - 1 925 926 # NOTE: Assume single location for data. 927 928 item_pos = self.load(item + 1) 929 if item_pos >= 0 and item_pos < nelements: 930 pass 931 elif item_pos < 0 and item_pos >= -nelements: 932 item_pos = nelements + item_pos 933 else: 934 self.exception = self._MakeObject(2, self.index_error_instance) 935 return self.RaiseException() 936 937 self.result = self.load(obj + 1 + item_pos) 938 939 def builtins_object_init(self): 940 pass 941 942 native_functions = { 943 "__builtins__.int.__add__" : builtins_int_add, 944 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 945 "__builtins__.int.__iadd__" : builtins_int_add, 946 "__builtins__.int.__bool__" : builtins_int_bool, 947 "__builtins__.int.__neg__" : builtins_int_neg, 948 "__builtins__.int.__lt__" : builtins_int_lt, 949 "__builtins__.int.__le__" : builtins_int_le, 950 "__builtins__.int.__gt__" : builtins_int_gt, 951 "__builtins__.int.__ge__" : builtins_int_ge, 952 "__builtins__.int.__eq__" : builtins_int_eq, 953 "__builtins__.int.__ne__" : builtins_int_ne, 954 "__builtins__.bool.__bool__" : builtins_bool_bool, 955 "__builtins__.list" : builtins_list_new, 956 "__builtins__.list.__getitem__" : builtins_list_getitem, 957 "__builtins__.object.__init__" : builtins_object_init, 958 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct. 959 } 960 961 # Convenience functions. 962 963 def machine(program, with_builtins=0, debug=0): 964 print "Making the image..." 965 code = program.get_image(with_builtins) 966 print "Getting raw structures..." 967 ot = program.get_object_table() 968 pt = program.get_parameter_table() 969 objlist = ot.as_list() 970 paramlist = pt.as_list() 971 print "Getting raw image..." 972 rc = program.get_raw_image() 973 print "Initialising the machine..." 974 importer = program.get_importer() 975 true_constant = importer.get_constant(True).location 976 false_constant = importer.get_constant(False).location 977 rm = RSVPMachine(rc, objlist, paramlist, true_constant, false_constant, debug=debug) 978 rm.pc = program.code_location 979 return rm 980 981 # vim: tabstop=4 expandtab shiftwidth=4