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 DataObject # for creating "nice" new objects 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 LoadFunction(self): 391 self.value = None, self.operand # context of constant is not interesting 392 393 def LoadName(self): 394 frame = self.local_sp_stack[-1] 395 self.value = self.frame_stack[frame + self.operand] 396 397 def StoreName(self): 398 frame = self.local_sp_stack[-1] 399 self.frame_stack[frame + self.operand] = self.source # uses the source value 400 401 LoadTemp = LoadName 402 403 def StoreTemp(self): 404 frame = self.local_sp_stack[-1] 405 self.frame_stack[frame + self.operand] = self.value 406 407 def LoadAddress(self): 408 # Preserve context (potentially null). 409 self.value = self.load(self.operand) 410 411 def LoadAddressContext(self): 412 context, ref = self.load(self.operand) 413 inst_context, inst_ref = self.value 414 self.value = inst_ref, ref 415 416 def LoadAddressContextCond(self): 417 context, ref = self.load(self.operand) 418 inst_context, inst_ref = self.value 419 self.value = self._LoadAddressContextCond(context, ref, inst_ref) 420 421 def StoreAddress(self): 422 # Preserve context. 423 self.save(self.operand, self.source) 424 425 def StoreAddressContext(self): 426 # Overwrite context if null. 427 context_context, context_ref = self.value 428 source_context, source_ref = self.source 429 if source_context is None: 430 context = context_ref 431 else: 432 context = source_context 433 self.save(self.operand, (context, source_ref)) 434 435 def MakeObject(self): 436 size = self.operand 437 context, ref = self.value 438 # NOTE: Referencing the instance template. 439 addr = self._MakeObject(size, ref - 1) 440 # Introduce object as context for the new object. 441 self.value = addr, addr 442 443 def LoadAttr(self): 444 context, ref = self.value 445 # Retrieved context should already be appropriate for the instance. 446 # NOTE: Adding 1 to skip any header. 447 self.value = self.load(ref + self.operand + 1) 448 449 def StoreAttr(self): 450 context, ref = self.value 451 # Target should already be an instance. 452 # NOTE: Adding 1 to skip any header. 453 self.save(ref + self.operand + 1, self.source) 454 455 def LoadAttrIndex(self): 456 context, ref = self.value 457 data = self.load(ref) 458 element = self.objlist[data.classcode + self.operand] 459 attr_index, class_attr, offset = element 460 if attr_index == self.operand: 461 if class_attr: 462 self.value = self.load(offset) # offset is address of class attribute 463 else: 464 self.value = self.load(ref + offset) 465 else: 466 self.exception = self._MakeObject(2, self.attr_error_instance) 467 return self.RaiseException() 468 469 # LoadAttrIndexContext not defined. 470 471 def LoadAttrIndexContextCond(self): 472 context, ref = self.value 473 data = self.load(ref) 474 element = self.objlist[data.classcode + self.operand] 475 attr_index, class_attr, offset = element 476 if attr_index == self.operand: 477 if class_attr: 478 loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute 479 if data.attrcode is None: # absent attrcode == class 480 self.value = loaded_context, loaded_ref 481 else: 482 self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, ref) 483 else: 484 self.value = self.load(ref + offset) 485 else: 486 self.exception = self._MakeObject(2, self.attr_error_instance) 487 return self.RaiseException() 488 489 def StoreAttrIndex(self): 490 context, ref = self.value 491 data = self.load(ref) 492 element = self.objlist[data.classcode + self.operand] 493 attr_index, class_attr, offset = element 494 if attr_index == self.operand: 495 if class_attr: 496 self.exception = self._MakeObject(2, self.type_error_instance) 497 return self.RaiseException() 498 else: 499 self.save(ref + offset, self.source) 500 else: 501 self.exception = self._MakeObject(2, self.attr_error_instance) 502 return self.RaiseException() 503 504 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 505 506 def MakeFrame(self): 507 self.invocation_sp_stack.append(len(self.frame_stack)) 508 self.frame_stack.extend([None] * self.operand) 509 510 def DropFrame(self): 511 self.local_sp_stack.pop() 512 frame = self.invocation_sp_stack.pop() 513 self.frame_stack = self.frame_stack[:frame] # reset stack before call 514 515 def StoreFrame(self): 516 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 517 self.frame_stack[frame + self.operand] = self.value 518 519 def StoreFrameIndex(self): 520 context, ref = self.value 521 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 522 data = self.load(ref) 523 element = self.paramlist[data.funccode + self.operand] 524 # NOTE: Need to ensure correct positioning where a context has been generated. 525 param_index, offset = element 526 if param_index == self.operand: 527 self.frame_stack[frame + offset + 1] = self.source # add 1 to skip the context always generated 528 else: 529 self.exception = self._MakeObject(2, self.type_error_instance) 530 return self.RaiseException() 531 532 def LoadCallable(self): 533 context, ref = self.value 534 data = self.load(ref) 535 self.callable = data.codeaddr 536 537 def StoreCallable(self): 538 context, ref = self.value 539 # NOTE: Should improve the representation and permit direct saving. 540 data = self.load(ref) 541 self.save(ref, (data.classcode, data.attrcode) + self.callable) 542 543 def LoadContext(self): 544 context, ref = self.value 545 # NOTE: Omission of the context of the context would make things like 546 # NOTE: self() inside methods impossible. 547 self.value = context, context 548 549 def CheckContext(self): 550 self.status = self.value[1] is not None 551 552 def CheckClassContext(self): 553 context_context, context_ref = self.value 554 context_data = self.load(context_ref) 555 556 # Classes are not themselves usable as the self argument. 557 # NOTE: This may change at some point. 558 # However, where classes appear as the context, instance 559 # compatibility is required in the first argument. 560 561 self.status = context_data.attrcode is None # absent attrcode == class 562 563 def CheckFrame(self): 564 (nargs, ndefaults, has_star) = self.operand 565 566 # The frame is actually installed as the locals. 567 # Retrieve the context from the first local. 568 569 frame = self.local_sp_stack[-1] 570 nlocals = len(self.frame_stack[frame:]) 571 572 if not ((nargs - ndefaults) <= nlocals and (nlocals <= nargs or has_star)): 573 #raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 574 self.exception = self._MakeObject(2, self.type_error_instance) 575 return self.RaiseException() 576 577 def FillDefaults(self): 578 # NOTE: Make the get_operand method of the instruction provide the 579 # NOTE: function location. 580 (nargs, ndefaults, fn) = self.operand 581 582 # The frame is actually installed as the locals. 583 584 frame = self.local_sp_stack[-1] 585 nlocals = len(self.frame_stack[frame:]) 586 587 # Support population of defaults. 588 # This involves copying the "attributes" of a function into the frame. 589 590 default = nlocals - (nargs - ndefaults) 591 self.frame_stack.extend([None] * (nargs - nlocals)) 592 pos = nlocals 593 ref = fn.location 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 target_context, target_ref = self.source 603 604 # Check the details of the proposed context and the target's context. 605 606 self.status = self._CheckInstance(ref, target_context) 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.local_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.__bool__" : builtins_int_bool, 918 "__builtins__.int.__neg__" : builtins_int_neg, 919 "__builtins__.int.__lt__" : builtins_int_lt, 920 "__builtins__.int.__gt__" : builtins_int_gt, 921 "__builtins__.int.__eq__" : builtins_int_eq, 922 "__builtins__.int.__ne__" : builtins_int_ne, 923 "__builtins__.bool.__bool__" : builtins_bool_bool, 924 "__builtins__.list" : builtins_list_new, 925 "__builtins__.list.__getitem__" : builtins_list_getitem, 926 "__builtins__.object.__init__" : builtins_object_init, 927 } 928 929 # Convenience functions. 930 931 def machine(program, with_builtins=0, debug=0): 932 print "Making the image..." 933 code = program.get_image(with_builtins) 934 print "Getting raw structures..." 935 ot = program.get_object_table() 936 pt = program.get_parameter_table() 937 objlist = ot.as_list() 938 paramlist = pt.as_list() 939 print "Getting raw image..." 940 rc = program.get_raw_image() 941 print "Initialising the machine..." 942 importer = program.get_importer() 943 true_constant = importer.get_constant(True).location 944 false_constant = importer.get_constant(False).location 945 rm = RSVPMachine(rc, objlist, paramlist, true_constant, false_constant, debug=debug) 946 rm.pc = program.code_location 947 return rm 948 949 # vim: tabstop=4 expandtab shiftwidth=4