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 attr_index, class_attr, offset = element 463 if attr_index == self.operand: 464 if class_attr: 465 self.value = self.load(offset) # offset is address of class attribute 466 else: 467 self.value = self.load(ref + offset) 468 else: 469 self.exception = self._MakeObject(2, self.attr_error_instance) 470 return self.RaiseException() 471 472 # LoadAttrIndexContext not defined. 473 474 def LoadAttrIndexContextCond(self): 475 context, ref = self.value 476 data = self.load(ref) 477 element = self.objlist[data.classcode + self.operand] 478 attr_index, class_attr, offset = element 479 if attr_index == self.operand: 480 if class_attr: 481 loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute 482 if data.attrcode is None: # absent attrcode == class 483 self.value = loaded_context, loaded_ref 484 else: 485 self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, ref) 486 else: 487 self.value = self.load(ref + offset) 488 else: 489 self.exception = self._MakeObject(2, self.attr_error_instance) 490 return self.RaiseException() 491 492 def StoreAttrIndex(self): 493 context, ref = self.value 494 data = self.load(ref) 495 element = self.objlist[data.classcode + self.operand] 496 attr_index, class_attr, offset = element 497 if attr_index == self.operand: 498 if class_attr: 499 self.exception = self._MakeObject(2, self.type_error_instance) 500 return self.RaiseException() 501 else: 502 self.save(ref + offset, self.source) 503 else: 504 self.exception = self._MakeObject(2, self.attr_error_instance) 505 return self.RaiseException() 506 507 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 508 509 def MakeFrame(self): 510 self.invocation_sp_stack.append(len(self.frame_stack)) 511 self.frame_stack.extend([None] * self.operand) 512 513 def DropFrame(self): 514 self.local_sp_stack.pop() 515 frame = self.invocation_sp_stack.pop() 516 self.frame_stack = self.frame_stack[:frame] # reset stack before call 517 518 def StoreFrame(self): 519 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 520 self.frame_stack[frame + self.operand] = self.value 521 522 def StoreFrameIndex(self): 523 context, ref = self.value 524 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 525 data = self.load(ref) 526 element = self.paramlist[data.funccode + self.operand] 527 # NOTE: Need to ensure correct positioning where a context has been generated. 528 param_index, offset = element 529 if param_index == self.operand: 530 self.frame_stack[frame + offset] = self.source 531 else: 532 self.exception = self._MakeObject(2, self.type_error_instance) 533 return self.RaiseException() 534 535 def LoadCallable(self): 536 context, ref = self.value 537 data = self.load(ref) 538 self.callable = data.codeaddr 539 540 def StoreCallable(self): 541 context, ref = self.value 542 # NOTE: Should improve the representation and permit direct saving. 543 data = self.load(ref) 544 self.save(ref, data.with_callable(self.callable)) 545 546 def LoadContext(self): 547 context, ref = self.value 548 # NOTE: Omission of the context of the context would make things like 549 # NOTE: self() inside methods impossible. 550 self.value = context, context 551 552 def CheckContext(self): 553 self.status = self.value[1] is not ReplaceableContext 554 555 def CheckClass(self): 556 context, ref = self.value 557 if ref in (ReplaceableContext, PlaceholderContext): 558 self.status = 0 559 return 560 561 data = self.load(ref) 562 563 # Classes are not themselves usable as the self argument. 564 # NOTE: This may change at some point. 565 # However, where classes appear as the context, instance 566 # compatibility is required in the first argument. 567 568 self.status = data.attrcode is None # absent attrcode == class 569 570 def CheckFrame(self): 571 (nargs, ndefaults, has_star) = self.operand 572 573 # The frame is actually installed as the locals. 574 # Retrieve the context from the first local. 575 576 frame = self.local_sp_stack[-1] 577 nlocals = len(self.frame_stack[frame:]) 578 579 # NOTE: Not testing (nlocals <= nargs or has_star) due to imprecise 580 # NOTE: invocation frame removal (after frame adjustment). 581 582 if not ((nargs - ndefaults) <= nlocals): 583 raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 584 self.exception = self._MakeObject(2, self.type_error_instance) 585 return self.RaiseException() 586 587 def FillDefaults(self): 588 context, ref = self.value 589 (nargs, ndefaults) = self.operand 590 591 # The frame is actually installed as the locals. 592 593 frame = self.local_sp_stack[-1] 594 nlocals = len(self.frame_stack[frame:]) 595 596 # Support population of defaults. 597 # This involves copying the "attributes" of a function into the frame. 598 599 default = nlocals - (nargs - ndefaults) 600 self.frame_stack.extend([None] * (nargs - nlocals)) 601 pos = nlocals 602 603 while pos < nargs: 604 self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header 605 default += 1 606 pos += 1 607 608 def CheckSelf(self): 609 context, ref = self.value 610 context_context, context_ref = self.source 611 612 # Check the details of the proposed context and the target's context. 613 614 self.status = self._CheckInstance(ref, context_ref) 615 616 def JumpInFrame(self): 617 codeaddr = self.callable 618 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 619 620 def JumpWithFrame(self): 621 codeaddr = self.callable 622 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 623 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 624 625 def JumpWithFrameDirect(self): 626 operand = self.operand 627 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 628 return self.jump(operand, self.pc + 1) # return to the instruction after this one 629 630 def ExtendFrame(self): 631 self.frame_stack.extend([None] * self.operand) 632 633 def AdjustFrame(self): 634 self.invocation_sp_stack[-1] += self.operand 635 636 def Return(self): 637 return self.pull_pc() 638 639 def LoadResult(self): 640 self.value = self.result 641 642 def StoreResult(self): 643 self.result = self.value 644 645 def Jump(self): 646 return self.operand 647 648 def JumpIfTrue(self): 649 if self.status: 650 return self.operand 651 652 def JumpIfFalse(self): 653 if not self.status: 654 return self.operand 655 656 def LoadException(self): 657 self.value = self.exception, self.exception 658 659 def StoreException(self): 660 self.exception = self.value[1] 661 662 def ClearException(self): 663 self.exception = None 664 665 def RaiseException(self): 666 # NOTE: Adding the program counter as the first attribute. 667 self.save(self.exception + 1, self.pc) 668 # Jumping to the current handler. 669 return self.handler_stack[-1] 670 671 def PushHandler(self): 672 self.handler_stack.append(self.operand) 673 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 674 self.handler_pc_stack.append(len(self.pc_stack)) 675 676 def PopHandler(self): 677 # Reduce the local frame pointer stack to refer to the handler's frame. 678 self.local_sp_stack = self.local_sp_stack[:self.handler_local_sp_stack.pop()] 679 # Reduce the PC stack to discard all superfluous return addresses. 680 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 681 self.handler_stack.pop() 682 683 def CheckException(self): 684 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1]) 685 686 def TestIdentity(self): 687 self.status = self.value[1] == self.source[1] 688 689 def TestIdentityAddress(self): 690 self.status = self.value[1] == self.operand 691 692 # LoadBoolean is implemented in the generated code. 693 # StoreBoolean is implemented by testing against the True value. 694 695 def InvertBoolean(self): 696 self.status = not self.status 697 698 # Common implementation details. 699 700 def _CheckInstance(self, ref, cls): 701 data = self.load(ref) 702 target_data = self.load(cls) 703 704 # Insist on instance vs. class. 705 706 if data.attrcode is None: # absent attrcode == class 707 return 0 708 709 if target_data.attrcode is not None: # present attrcode == instance 710 return 0 711 712 # Find the table entry for the descendant. 713 714 element = self.objlist[target_data.classcode + data.attrcode] 715 if element is not None: 716 attr_index, class_attr, offset = element 717 return attr_index == data.attrcode 718 else: 719 return 0 720 721 def _MakeObject(self, size, ref): 722 # Load the template. 723 data = self.load(ref) 724 addr = self.new(size) 725 # Save the header, overriding the size. 726 self.save(addr, data.with_size(size)) 727 return addr 728 729 def _LoadAddressContextCond(self, context, ref, inst_ref): 730 # Check the instance context against the target's context. 731 # This provides the context overriding for methods. 732 if context is not ReplaceableContext and self._CheckInstance(inst_ref, context): 733 # Replace the context with the instance. 734 return inst_ref, ref 735 else: 736 return context, ref 737 738 # Native function implementations. 739 740 def builtins_int_add(self): 741 frame = self.local_sp_stack[-1] 742 743 # Get operands addresses. 744 745 left_context, left = self.frame_stack[frame] 746 right_context, right = self.frame_stack[frame + 1] 747 748 # Test operand suitability. 749 # NOTE: Support other types. 750 751 if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): 752 self.exception = self._MakeObject(2, self.type_error_instance) 753 return self.RaiseException() 754 755 # NOTE: Assume single location for data. 756 757 left_data = left + 1 758 right_data = right + 1 759 760 # Make a new object. 761 762 addr = self._MakeObject(2, self.int_instance) 763 764 # Store the result. 765 # NOTE: The data is considered ready to use. 766 767 self.save(addr + 1, self.load(left_data) + self.load(right_data)) 768 769 # Return the new object. 770 # Introduce object as context for the new object. 771 772 self.result = addr, addr 773 774 def builtins_int_bool(self): 775 frame = self.local_sp_stack[-1] 776 777 # Get operands addresses. 778 779 left_context, left = self.frame_stack[frame] 780 781 # Test operand suitability. 782 783 if not self._CheckInstance(left, self.int_class): 784 self.exception = self._MakeObject(2, self.type_error_instance) 785 return self.RaiseException() 786 787 # NOTE: Assume single location for data. 788 789 left_data = left + 1 790 791 # Test the data. 792 # NOTE: The data is considered ready to use. 793 794 if self.load(left_data) != 0: 795 self.result = self.true_constant, self.true_constant 796 else: 797 self.result = self.false_constant, self.false_constant 798 799 def builtins_int_neg(self): 800 frame = self.local_sp_stack[-1] 801 802 # Get operands addresses. 803 804 left_context, left = self.frame_stack[frame] 805 806 # Test operand suitability. 807 808 if not self._CheckInstance(left, self.int_class): 809 self.exception = self._MakeObject(2, self.type_error_instance) 810 return self.RaiseException() 811 812 # NOTE: Assume single location for data. 813 814 left_data = left + 1 815 816 # Make a new object. 817 818 addr = self._MakeObject(2, self.int_instance) 819 820 # Store the result. 821 # NOTE: The data is considered ready to use. 822 823 self.save(addr + 1, -self.load(left_data)) 824 825 # Return the new object. 826 # Introduce object as context for the new object. 827 828 self.result = addr, addr 829 830 def builtins_int_op(self, op, true_if_incompatible): 831 frame = self.local_sp_stack[-1] 832 833 # Get operands addresses. 834 835 left_context, left = self.frame_stack[frame] 836 right_context, right = self.frame_stack[frame + 1] 837 838 # Test operand suitability. 839 # NOTE: Support other types. 840 # NOTE: Handle comparisons of incompatible types more appropriately. 841 842 if not (self._CheckInstance(left, self.int_class) and self._CheckInstance(right, self.int_class)): 843 if true_if_incompatible: 844 self.result = self.true_constant, self.true_constant 845 else: 846 self.result = self.false_constant, self.false_constant 847 return 848 849 # NOTE: Assume single location for data. 850 851 left_data = left + 1 852 right_data = right + 1 853 854 # Test the data. 855 # NOTE: The data is considered ready to use. 856 857 if op(self.load(left_data), self.load(right_data)): 858 self.result = self.true_constant, self.true_constant 859 else: 860 self.result = self.false_constant, self.false_constant 861 862 def builtins_int_lt(self): 863 return self.builtins_int_op(operator.lt, 0) 864 865 def builtins_int_gt(self): 866 return self.builtins_int_op(operator.gt, 0) 867 868 def builtins_int_eq(self): 869 return self.builtins_int_op(operator.eq, 0) 870 871 def builtins_int_ne(self): 872 return self.builtins_int_op(operator.ne, 1) 873 874 def builtins_bool_bool(self): 875 frame = self.local_sp_stack[-1] 876 877 # Get operands addresses. 878 879 left_context, left = self.frame_stack[frame] 880 self.result = left, left 881 882 def builtins_list_new(self): 883 frame = self.local_sp_stack[-1] 884 885 # NOTE: Specific copying of tuples/lists. 886 887 args_context, args = self.frame_stack[frame] 888 header = self.load(args) 889 890 list = self._MakeObject(header.size, self.list_instance) 891 for i in range(1, header.size): 892 self.save(list + i, self.load(args + i)) 893 894 self.result = list, list 895 896 def builtins_list_getitem(self): 897 frame = self.local_sp_stack[-1] 898 899 # Get operands addresses. 900 901 obj_context, obj = self.frame_stack[frame] 902 item_context, item = self.frame_stack[frame + 1] 903 904 header = self.load(obj) 905 nelements = header.size - 1 906 907 # NOTE: Assume single location for data. 908 909 item_pos = self.load(item + 1) 910 if item_pos >= 0 and item_pos < nelements: 911 pass 912 elif item_pos < 0 and item_pos >= -nelements: 913 item_pos = nelements + item_pos 914 else: 915 self.exception = self._MakeObject(2, self.index_error_instance) 916 return self.RaiseException() 917 918 self.result = self.load(obj + 1 + item_pos) 919 920 def builtins_object_init(self): 921 pass 922 923 native_functions = { 924 "__builtins__.int.__add__" : builtins_int_add, 925 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 926 "__builtins__.int.__bool__" : builtins_int_bool, 927 "__builtins__.int.__neg__" : builtins_int_neg, 928 "__builtins__.int.__lt__" : builtins_int_lt, 929 "__builtins__.int.__gt__" : builtins_int_gt, 930 "__builtins__.int.__eq__" : builtins_int_eq, 931 "__builtins__.int.__ne__" : builtins_int_ne, 932 "__builtins__.bool.__bool__" : builtins_bool_bool, 933 "__builtins__.list" : builtins_list_new, 934 "__builtins__.list.__getitem__" : builtins_list_getitem, 935 "__builtins__.object.__init__" : builtins_object_init, 936 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct. 937 } 938 939 # Convenience functions. 940 941 def machine(program, with_builtins=0, debug=0): 942 print "Making the image..." 943 code = program.get_image(with_builtins) 944 print "Getting raw structures..." 945 ot = program.get_object_table() 946 pt = program.get_parameter_table() 947 objlist = ot.as_list() 948 paramlist = pt.as_list() 949 print "Getting raw image..." 950 rc = program.get_raw_image() 951 print "Initialising the machine..." 952 importer = program.get_importer() 953 true_constant = importer.get_constant(True).location 954 false_constant = importer.get_constant(False).location 955 rm = RSVPMachine(rc, objlist, paramlist, true_constant, false_constant, debug=debug) 956 rm.pc = program.code_location 957 return rm 958 959 # vim: tabstop=4 expandtab shiftwidth=4