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