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, FragmentObject 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, pc=None, debug=0, abort_upon_exception=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', and the 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.library = None 96 97 self.pc = pc or 0 98 self.debug = debug 99 self.abort_upon_exception = abort_upon_exception 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__", "tuple") 131 self.tuple_class = cls.location 132 self.tuple_instance = cls.instance_template_location 133 134 # Debugging attributes. 135 136 self.breakpoints = set() 137 138 def _get_class(self, module, name): 139 return self._objlist.access(module, name).get_value() 140 141 # Debugging methods. 142 143 def dump(self): 144 print "PC", self.pc, "->", self.load(self.pc) 145 print "PC stack", self.pc_stack 146 print "Frame stack", self.frame_stack 147 print "Local stack pointers", self.local_sp_stack 148 print "Invocation stack pointers", self.invocation_sp_stack 149 print "Handler stack", self.handler_stack 150 print "Handler frame stack", self.handler_local_sp_stack 151 print "Handler PC stack", self.handler_pc_stack 152 print 153 print "Instruction", self.instruction 154 print "Operand", self.operand 155 print "Value", self.value 156 print "Status", self.status 157 print "Source", self.source 158 print "Callable", self.callable 159 print "Result", self.result 160 print "Exception", self.exception 161 162 def show(self): 163 self.show_memory(self.memory, 0) 164 165 def show_pc(self, run_in=10): 166 start = max(0, self.pc - run_in) 167 end = self.pc + run_in 168 memory = self.memory[start:end] 169 self.show_memory(memory, start) 170 171 def show_memory(self, memory, start): 172 for i, x in enumerate(memory): 173 location = start + i 174 if location == self.pc: 175 print "->", 176 else: 177 print " ", 178 print "%5d %r" % (location, x) 179 180 def step(self, dump=0): 181 self.execute() 182 self.show_pc() 183 if dump: 184 self.dump() 185 186 def set_break(self, location): 187 self.breakpoints.add(location) 188 189 # Internal operations. 190 191 def load(self, address): 192 193 "Return the value at the given 'address'." 194 195 try: 196 return self.memory[address] 197 except IndexError: 198 raise IllegalAddress(address) 199 except TypeError: 200 raise IllegalAddress(address) 201 202 def save(self, address, value): 203 204 "Save to the given 'address' the specified 'value'." 205 206 try: 207 self.memory[address] = value 208 except IndexError: 209 raise IllegalAddress(address) 210 except TypeError: 211 raise IllegalAddress(address) 212 213 def new(self, size): 214 215 """ 216 Allocate space of the given 'size', returning the address of the space. 217 """ 218 219 addr = len(self.memory) 220 for i in range(0, size): 221 self.memory.append(None) 222 return addr 223 224 def push_pc(self, data): 225 226 "Push 'data' onto the PC stack." 227 228 self.pc_stack.append(data) 229 230 def pull_pc(self): 231 232 "Pull a value from the PC stack and return it." 233 234 try: 235 return self.pc_stack.pop() 236 except IndexError: 237 raise EmptyPCStack 238 239 def run(self): 240 241 "Execute code in the memory, starting from the current PC address." 242 243 breakpoint = 0 244 245 try: 246 while 1: 247 self.execute() 248 except EmptyPCStack: 249 pass 250 except BreakpointReached: 251 breakpoint = 1 252 253 print "Execution terminated", 254 if self.exception is not None: 255 ref = self.exception 256 addr = self.load(ref + 1) 257 print "with exception:", self.load(ref) 258 print "At address %d: %r" % (addr, self.load(addr)) 259 elif breakpoint: 260 print "with breakpoint." 261 print "At address", self.pc 262 else: 263 print "successfully." 264 265 def test(self, module): 266 267 """ 268 Test the code in the memory by running the code and investigating the 269 contents of variables. Use 'module' to identify result variables. 270 """ 271 272 self.run() 273 success = 1 274 275 if self.exception is None: 276 for name in module.keys(): 277 if name.startswith("result"): 278 label, expected = name.split("_") 279 attr = module[name] 280 281 # NOTE: Assumptions about headers and content made. 282 283 attr_location = module.location + 1 + attr.position 284 context, ref = self.load(attr_location) 285 286 if ref is not None: 287 content = self.load(ref + 1) 288 print label, expected, content 289 success = success and (int(expected) == content) 290 else: 291 print label, expected, "missing" 292 success = 0 293 294 return success 295 else: 296 return 0 297 298 def execute(self): 299 300 "Execute code in the memory at the current PC address." 301 302 if self.pc in self.breakpoints: 303 self.breakpoints.remove(self.pc) 304 raise BreakpointReached 305 306 self.instruction = self.load(self.pc) 307 308 # Process any inputs of the instruction. 309 310 self.process_inputs() 311 312 # Perform the instruction itself. 313 314 next_pc = self.perform(self.instruction) 315 316 # Update the program counter. 317 318 if next_pc is None: 319 self.pc += 1 320 else: 321 self.pc = next_pc 322 323 def get_method(self, instruction): 324 325 "Return the handler method for the given 'instruction'." 326 327 instruction_name = instruction.__class__.__name__ 328 if self.debug: 329 print "%8d %s" % (self.pc, instruction_name) 330 method = getattr(self, instruction_name, None) 331 if method is None: 332 raise IllegalInstruction, (self.pc, instruction_name) 333 return method 334 335 def perform(self, instruction): 336 337 "Perform the 'instruction', returning the next PC value or None." 338 339 self.operand = instruction.get_operand() 340 method = self.get_method(instruction) 341 return method() 342 343 def process_inputs(self): 344 345 """ 346 Process any inputs of the current instruction. This permits any directly 347 connected sub-instructions to produce the effects that separate 348 instructions would otherwise have. 349 """ 350 351 value = self.value 352 if self.instruction.source is not None: 353 self.perform(self.instruction.source) 354 self.source = self.value 355 self.value = value 356 if self.instruction.input is not None: 357 self.perform(self.instruction.input) 358 359 def jump(self, addr, next): 360 361 """ 362 Jump to the subroutine at (or identified by) 'addr'. If 'addr' 363 identifies a library function then invoke the library function and set 364 PC to 'next' afterwards; otherwise, set PC to 'addr'. 365 """ 366 367 # Trap library functions introduced through the use of strings instead 368 # of proper locations. 369 370 if isinstance(addr, str): 371 handler = self.library and self.library.native_functions[addr](self.library) 372 if handler is None: 373 return next 374 else: 375 return handler 376 else: 377 self.push_pc(self.pc + 1) 378 return addr 379 380 # Instructions. 381 382 def LoadConst(self): 383 self.value = self.operand, self.operand 384 385 def LoadClass(self): 386 self.value = PlaceholderContext, self.operand 387 388 def LoadFunction(self): 389 self.value = ReplaceableContext, self.operand 390 391 def LoadName(self): 392 frame = self.local_sp_stack[-1] 393 self.value = self.frame_stack[frame + self.operand] 394 395 def StoreName(self): 396 frame = self.local_sp_stack[-1] 397 self.frame_stack[frame + self.operand] = self.source # uses the source value 398 399 LoadTemp = LoadName 400 401 def StoreTemp(self): 402 frame = self.local_sp_stack[-1] 403 self.frame_stack[frame + self.operand] = self.value 404 405 def LoadAddress(self): 406 # Preserve context (potentially null). 407 self.value = self.load(self.operand) 408 409 def LoadAddressContext(self): 410 context, ref = self.load(self.operand) 411 inst_context, inst_ref = self.value 412 self.value = inst_ref, ref 413 414 def LoadAddressContextCond(self): 415 context, ref = self.load(self.operand) 416 inst_context, inst_ref = self.value 417 self.value = self._LoadAddressContextCond(context, ref, inst_ref) 418 419 def StoreAddress(self): 420 # Preserve context. 421 self.save(self.operand, self.source) 422 423 def StoreAddressContext(self): 424 # Overwrite context if null. 425 context_context, context_ref = self.value 426 source_context, source_ref = self.source 427 if source_context is ReplaceableContext: 428 context = context_ref 429 else: 430 context = source_context 431 self.save(self.operand, (context, source_ref)) 432 433 def MakeInstance(self): 434 size = self.operand 435 context, ref = self.value 436 # NOTE: Referencing the instance template. 437 addr = self._MakeObject(size, ref - 1) 438 # Introduce object as context for the new object. 439 self.value = addr, addr 440 441 def MakeFragment(self): 442 size = self.operand 443 addr = self._MakeFragment(size) 444 # NOTE: Context is not relevant for fragments. 445 self.value = None, addr 446 447 def LoadAttr(self): 448 context, ref = self.value 449 # Retrieved context should already be appropriate for the instance. 450 # NOTE: Adding 1 to skip any header. 451 self.value = self.load(ref + self.operand + 1) 452 453 def StoreAttr(self): 454 context, ref = self.value 455 # Target should already be an instance. 456 # NOTE: Adding 1 to skip any header. 457 self.save(ref + self.operand + 1, self.source) 458 459 def LoadAttrIndex(self): 460 context, ref = self.value 461 data = self.load(ref) 462 element = self.objlist[data.classcode + self.operand] 463 464 if element is not None: 465 attr_index, static_attr, offset = element 466 if attr_index == self.operand: 467 if static_attr: 468 self.value = self.load(offset) # offset is address of class/module attribute 469 else: 470 self.value = self.load(ref + offset) 471 return 472 473 self.exception = self._MakeObject(2, self.attr_error_instance) 474 return self.RaiseException() 475 476 # LoadAttrIndexContext not defined. 477 478 def LoadAttrIndexContextCond(self): 479 inst_context, inst_ref = self.value 480 data = self.load(inst_ref) 481 element = self.objlist[data.classcode + self.operand] 482 483 if element is not None: 484 attr_index, static_attr, offset = element 485 if attr_index == self.operand: 486 if static_attr: 487 loaded_context, loaded_ref = self.load(offset) # offset is address of class/module attribute 488 if data.attrcode is None: # absent attrcode == class/module 489 self.value = loaded_context, loaded_ref 490 else: 491 self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, inst_ref) 492 else: 493 self.value = self.load(inst_ref + offset) 494 return 495 496 self.exception = self._MakeObject(2, self.attr_error_instance) 497 return self.RaiseException() 498 499 def StoreAttrIndex(self): 500 context, ref = self.value 501 data = self.load(ref) 502 element = self.objlist[data.classcode + self.operand] 503 504 if element is not None: 505 attr_index, static_attr, offset = element 506 if attr_index == self.operand: 507 if static_attr: 508 self.exception = self._MakeObject(2, self.type_error_instance) 509 return self.RaiseException() 510 else: 511 self.save(ref + offset, self.source) 512 return 513 514 self.exception = self._MakeObject(2, self.attr_error_instance) 515 return self.RaiseException() 516 517 # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden. 518 519 def MakeFrame(self): 520 self.invocation_sp_stack.append(len(self.frame_stack)) 521 self.frame_stack.extend([None] * self.operand) 522 523 def DropFrame(self): 524 self.local_sp_stack.pop() 525 frame = self.invocation_sp_stack.pop() 526 del self.frame_stack[frame:] # reset stack before call 527 528 def StoreFrame(self): 529 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 530 self.frame_stack[frame + self.operand] = self.value 531 532 def StoreFrameIndex(self): 533 context, ref = self.value 534 frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame 535 data = self.load(ref) 536 element = self.paramlist[data.funccode + self.operand] 537 538 if element is not None: 539 # NOTE: Need to ensure correct positioning where a context has been generated. 540 param_index, offset = element 541 if param_index == self.operand: 542 self.frame_stack[frame + offset] = self.source 543 return 544 545 self.exception = self._MakeObject(2, self.type_error_instance) 546 return self.RaiseException() 547 548 def LoadCallable(self): 549 context, ref = self.value 550 data = self.load(ref) 551 self.callable = data.codeaddr 552 553 def StoreCallable(self): 554 context, ref = self.value 555 # NOTE: Should improve the representation and permit direct saving. 556 data = self.load(ref) 557 self.save(ref, data.with_callable(self.callable)) 558 559 def LoadContext(self): 560 context, ref = self.value 561 # NOTE: Omission of the context of the context would make things like 562 # NOTE: self() inside methods impossible. 563 self.value = context, context 564 565 def CheckContext(self): 566 self.status = self.value[1] is not ReplaceableContext 567 568 def CheckClass(self): 569 context, ref = self.value 570 if ref in (ReplaceableContext, PlaceholderContext): 571 self.status = 0 572 return 573 574 data = self.load(ref) 575 576 # Classes are not themselves usable as the self argument. 577 # NOTE: This may change at some point. 578 # However, where classes appear as the context, instance 579 # compatibility is required in the first argument. 580 581 self.status = data.attrcode is None # absent attrcode == class 582 583 def CheckFrame(self): 584 (nargs, ndefaults) = self.operand 585 586 # The frame is actually installed as the locals. 587 # Retrieve the context from the first local. 588 589 frame = self.local_sp_stack[-1] 590 nlocals = len(self.frame_stack[frame:]) 591 592 if not ((nargs - ndefaults) <= nlocals): 593 raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs) 594 self.exception = self._MakeObject(2, self.type_error_instance) 595 return self.RaiseException() 596 597 def CheckExtra(self): 598 nargs = self.operand 599 600 # The frame is actually installed as the locals. 601 # Retrieve the context from the first local. 602 603 frame = self.local_sp_stack[-1] 604 nlocals = len(self.frame_stack[frame:]) 605 606 # Provide the extra star parameter if necessary. 607 608 if nlocals == nargs: 609 self.frame_stack.extend([None]) # ExtendFrame(1) 610 611 def FillDefaults(self): 612 context, ref = self.value 613 (nargs, ndefaults) = self.operand 614 615 # The frame is actually installed as the locals. 616 617 frame = self.local_sp_stack[-1] 618 nlocals = len(self.frame_stack[frame:]) 619 620 # Support population of defaults. 621 # This involves copying the "attributes" of a function into the frame. 622 623 default = nlocals - (nargs - ndefaults) 624 self.frame_stack.extend([None] * (nargs - nlocals)) 625 pos = nlocals 626 627 while pos < nargs: 628 self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header 629 default += 1 630 pos += 1 631 632 def CopyExtra(self): 633 start = self.operand 634 635 # The frame is the source of the extra arguments. 636 637 frame = self.local_sp_stack[-1] 638 nlocals = len(self.frame_stack[frame:]) 639 640 # Make a tuple to hold the arguments. 641 642 ref = self._MakeObject(nlocals - start + 1, self.tuple_instance) 643 644 extra = 0 645 pos = start 646 647 while pos < nlocals: 648 self.save(ref + extra + 1, self.frame_stack[frame + pos]) # skip header when storing 649 extra += 1 650 pos += 1 651 652 self.value = ref, ref 653 654 def CheckSelf(self): 655 context, ref = self.value 656 context_context, context_ref = self.source 657 658 # Check the details of the proposed context and the target's context. 659 660 self.status = self._CheckInstance(ref, context_ref) 661 662 def JumpInFrame(self): 663 codeaddr = self.callable 664 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 665 666 def JumpWithFrame(self): 667 codeaddr = self.callable 668 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 669 return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one 670 671 def JumpWithFrameDirect(self): 672 operand = self.operand 673 self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame 674 return self.jump(operand, self.pc + 1) # return to the instruction after this one 675 676 def ExtendFrame(self): 677 self.frame_stack.extend([None] * self.operand) 678 679 def AdjustFrame(self): 680 self.invocation_sp_stack[-1] += self.operand 681 682 def Return(self): 683 return self.pull_pc() 684 685 def LoadResult(self): 686 self.value = self.result 687 688 def StoreResult(self): 689 self.result = self.value 690 691 def Jump(self): 692 return self.operand 693 694 def JumpIfTrue(self): 695 if self.status: 696 return self.operand 697 698 def JumpIfFalse(self): 699 if not self.status: 700 return self.operand 701 702 def LoadException(self): 703 self.value = self.exception, self.exception 704 705 def StoreException(self): 706 self.exception = self.value[1] 707 708 def ClearException(self): 709 self.exception = None 710 711 def RaiseException(self): 712 # NOTE: Adding the program counter as the first attribute. 713 self.save(self.exception + 1, self.pc) 714 # Jumping to the current handler. 715 if self.abort_upon_exception: 716 raise Exception 717 return self.handler_stack[-1] 718 719 def PushHandler(self): 720 self.handler_stack.append(self.operand) 721 self.handler_local_sp_stack.append(len(self.local_sp_stack)) 722 self.handler_pc_stack.append(len(self.pc_stack)) 723 724 def PopHandler(self): 725 # Reduce the local frame pointer stack to refer to the handler's frame. 726 del self.local_sp_stack[self.handler_local_sp_stack.pop():] 727 # Reduce the PC stack to discard all superfluous return addresses. 728 self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()] 729 self.handler_stack.pop() 730 731 def CheckException(self): 732 self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1]) 733 734 def TestIdentity(self): 735 self.status = self.value[1] == self.source[1] 736 737 def TestIdentityAddress(self): 738 self.status = self.value[1] == self.operand 739 740 # LoadBoolean is implemented in the generated code. 741 # StoreBoolean is implemented by testing against the True value. 742 743 def InvertBoolean(self): 744 self.status = not self.status 745 746 # Common implementation details. 747 748 def _CheckInstance(self, ref, cls): 749 data = self.load(ref) 750 target_data = self.load(cls) 751 752 # Insist on instance vs. class. 753 754 if data.attrcode is None: # absent attrcode == class/module 755 return 0 756 757 if target_data.attrcode is not None: # present attrcode == instance 758 return 0 759 760 # Find the table entry for the descendant. 761 762 element = self.objlist[target_data.classcode + data.attrcode] 763 764 if element is not None: 765 attr_index, static_attr, offset = element 766 return attr_index == data.attrcode 767 else: 768 return 0 769 770 def _MakeObject(self, size, ref): 771 # Load the template. 772 data = self.load(ref) 773 addr = self.new(size) 774 # Save the header, overriding the size. 775 self.save(addr, data.with_size(size)) 776 return addr 777 778 def _MakeFragment(self, size): 779 # Reserve twice the amount of space. 780 addr = self.new(size * 2) 781 # Save the header, overriding the size. 782 self.save(addr, FragmentObject(size, size * 2)) 783 return addr 784 785 def _LoadAddressContextCond(self, context, ref, inst_ref): 786 # Check the instance context against the target's context. 787 # This provides the context overriding for methods. 788 if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_ref, context): 789 # Replace the context with the instance. 790 return inst_ref, ref 791 else: 792 return context, ref 793 794 class Library: 795 796 "Native function implementations." 797 798 def __init__(self, machine, true_constant, false_constant): 799 800 """ 801 Initialise the library with the 'machine' and the addresses 802 'true_constant' and 'false_constant'. 803 """ 804 805 self.machine = machine 806 self.true_constant = true_constant 807 self.false_constant = false_constant 808 809 # Native class constants. 810 811 cls = self.machine._get_class("__builtins__", "int") 812 self.int_class = cls.location 813 self.int_instance = cls.instance_template_location 814 cls = self.machine._get_class("__builtins__", "list") 815 self.list_class = cls.location 816 self.list_instance = cls.instance_template_location 817 cls = self.machine._get_class("__builtins__", "IndexError") 818 self.index_error = cls.location 819 self.index_error_instance = cls.instance_template_location 820 821 self.tuple_class = self.machine.tuple_class 822 self.type_error_instance = self.machine.type_error_instance 823 824 self.frame_stack = self.machine.frame_stack 825 self.local_sp_stack = self.machine.local_sp_stack 826 827 def builtins_int_arithmetic_op(self, op): 828 frame = self.local_sp_stack[-1] 829 830 # Get operands addresses. 831 832 left_context, left = self.frame_stack[frame] 833 right_context, right = self.frame_stack[frame + 1] 834 835 # Test operand suitability. 836 # NOTE: Support other types. 837 838 if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)): 839 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 840 return self.machine.RaiseException() 841 842 # NOTE: Assume single location for data. 843 844 left_data = left + 1 845 right_data = right + 1 846 847 # Make a new object. 848 849 addr = self.machine._MakeObject(2, self.int_instance) 850 851 # Store the result. 852 # NOTE: The data is considered ready to use. 853 854 self.machine.save(addr + 1, op(self.machine.load(left_data), self.machine.load(right_data))) 855 856 # Return the new object. 857 # Introduce object as context for the new object. 858 859 self.machine.result = addr, addr 860 861 def builtins_int_bool(self): 862 frame = self.local_sp_stack[-1] 863 864 # Get operands addresses. 865 866 left_context, left = self.frame_stack[frame] 867 868 # Test operand suitability. 869 870 if not self.machine._CheckInstance(left, self.int_class): 871 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 872 return self.machine.RaiseException() 873 874 # NOTE: Assume single location for data. 875 876 left_data = left + 1 877 878 # Test the data. 879 # NOTE: The data is considered ready to use. 880 881 if self.machine.load(left_data) != 0: 882 self.machine.result = self.true_constant, self.true_constant 883 else: 884 self.machine.result = self.false_constant, self.false_constant 885 886 def builtins_int_neg(self): 887 frame = self.local_sp_stack[-1] 888 889 # Get operands addresses. 890 891 left_context, left = self.frame_stack[frame] 892 893 # Test operand suitability. 894 895 if not self.machine._CheckInstance(left, self.int_class): 896 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 897 return self.machine.RaiseException() 898 899 # NOTE: Assume single location for data. 900 901 left_data = left + 1 902 903 # Make a new object. 904 905 addr = self.machine._MakeObject(2, self.int_instance) 906 907 # Store the result. 908 # NOTE: The data is considered ready to use. 909 910 self.machine.save(addr + 1, -self.machine.load(left_data)) 911 912 # Return the new object. 913 # Introduce object as context for the new object. 914 915 self.machine.result = addr, addr 916 917 def builtins_int_op(self, op, true_if_incompatible): 918 frame = self.local_sp_stack[-1] 919 920 # Get operands addresses. 921 922 left_context, left = self.frame_stack[frame] 923 right_context, right = self.frame_stack[frame + 1] 924 925 # Test operand suitability. 926 # NOTE: Support other types. 927 # NOTE: Handle comparisons of incompatible types more appropriately. 928 929 if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)): 930 if true_if_incompatible: 931 self.machine.result = self.true_constant, self.true_constant 932 else: 933 self.machine.result = self.false_constant, self.false_constant 934 return 935 936 # NOTE: Assume single location for data. 937 938 left_data = left + 1 939 right_data = right + 1 940 941 # Test the data. 942 # NOTE: The data is considered ready to use. 943 944 if op(self.machine.load(left_data), self.machine.load(right_data)): 945 self.machine.result = self.true_constant, self.true_constant 946 else: 947 self.machine.result = self.false_constant, self.false_constant 948 949 def builtins_int_add(self): 950 return self.builtins_int_arithmetic_op(operator.add) 951 952 def builtins_int_sub(self): 953 return self.builtins_int_arithmetic_op(operator.sub) 954 955 def builtins_int_lt(self): 956 return self.builtins_int_op(operator.lt, 0) 957 958 def builtins_int_le(self): 959 return self.builtins_int_op(operator.le, 0) 960 961 def builtins_int_gt(self): 962 return self.builtins_int_op(operator.gt, 0) 963 964 def builtins_int_ge(self): 965 return self.builtins_int_op(operator.ge, 0) 966 967 def builtins_int_eq(self): 968 return self.builtins_int_op(operator.eq, 0) 969 970 def builtins_int_ne(self): 971 return self.builtins_int_op(operator.ne, 1) 972 973 def builtins_int_and(self): 974 return self.builtins_int_arithmetic_op(operator.and_) 975 976 def builtins_int_or(self): 977 return self.builtins_int_arithmetic_op(operator.or_) 978 979 def builtins_bool_bool(self): 980 frame = self.local_sp_stack[-1] 981 982 # Get operands addresses. 983 984 left_context, left = self.frame_stack[frame] 985 self.machine.result = left, left 986 987 def builtins_list_new(self): 988 frame = self.local_sp_stack[-1] 989 990 # The first parameter should be empty. 991 # NOTE: Specific copying of tuples/lists. 992 993 args_context, args = self.frame_stack[frame + 1] 994 995 # Test operand suitability. 996 997 if self.machine._CheckInstance(args, self.list_class): 998 _x, sequence = self.machine.load(args + 1) 999 header = self.machine.load(sequence) 1000 size = header.occupied_size 1001 elif self.machine._CheckInstance(args, self.tuple_class): 1002 sequence = args 1003 header = self.machine.load(sequence) 1004 size = header.size 1005 else: 1006 self.machine.exception = self.machine._MakeObject(2, self.type_error_instance) 1007 return self.machine.RaiseException() 1008 1009 # Copy the sequence contents. 1010 1011 new_fragment = self.machine._MakeFragment(size) 1012 for i in range(1, size): 1013 self.machine.save(new_fragment + i, self.machine.load(sequence + i)) 1014 1015 # Make the list instance. 1016 1017 addr = self.machine._MakeObject(2, self.list_instance) 1018 self.machine.save(addr + 1, (None, new_fragment)) 1019 1020 self.machine.result = addr, addr 1021 1022 def builtins_list_getitem(self): 1023 frame = self.local_sp_stack[-1] 1024 1025 # Get the operand address. 1026 1027 item_context, item = self.frame_stack[frame + 1] 1028 1029 # Get the list address. 1030 1031 obj_context, obj = self.frame_stack[frame] 1032 1033 # Get the fragment address. 1034 # NOTE: Assume single location for header. 1035 1036 _x, fragment = self.machine.load(obj + 1) 1037 1038 # Get the fragment header. 1039 1040 header = self.machine.load(fragment) 1041 nelements = header.occupied_size - 1 1042 1043 # NOTE: Assume single location for data and header. 1044 1045 item_pos = self.machine.load(item + 1) 1046 1047 if item_pos >= 0 and item_pos < nelements: 1048 pass 1049 elif item_pos < 0 and item_pos >= -nelements: 1050 item_pos = nelements + item_pos 1051 else: 1052 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 1053 return self.machine.RaiseException() 1054 1055 # NOTE: Assume single location for header. 1056 1057 self.machine.result = self.machine.load(fragment + 1 + item_pos) 1058 1059 def builtins_list_len(self): 1060 frame = self.local_sp_stack[-1] 1061 1062 # Get the list address. 1063 1064 obj_context, obj = self.frame_stack[frame] 1065 1066 # Get the fragment address. 1067 # NOTE: Assume single location for header. 1068 1069 _x, fragment = self.machine.load(obj + 1) 1070 1071 # Get the fragment header. 1072 1073 header = self.machine.load(fragment) 1074 nelements = header.occupied_size - 1 1075 1076 # Make a new object. 1077 1078 addr = self.machine._MakeObject(2, self.int_instance) 1079 1080 # Store the result. 1081 # NOTE: The data is considered ready to use. 1082 1083 self.machine.save(addr + 1, nelements) 1084 1085 # Return the new object. 1086 # Introduce object as context for the new object. 1087 1088 self.machine.result = addr, addr 1089 1090 def builtins_list_append(self): 1091 frame = self.local_sp_stack[-1] 1092 1093 # Get operand address. 1094 1095 arg_context, arg = self.frame_stack[frame + 1] 1096 1097 # Get the list address. 1098 1099 obj_context, obj = self.frame_stack[frame] 1100 1101 # Get the fragment address. 1102 # NOTE: Assume single location for header. 1103 1104 _x, fragment = self.machine.load(obj + 1) 1105 1106 # Get the fragment header. 1107 1108 header = self.machine.load(fragment) 1109 1110 # Attempt to add the reference. 1111 1112 if header.occupied_size < header.allocated_size: 1113 self.machine.save(fragment + header.occupied_size, (arg_context, arg)) 1114 header.occupied_size += 1 1115 else: 1116 1117 # Make a new fragment, maintaining more space than currently 1118 # occupied in order to avoid reallocation. 1119 1120 new_fragment = self.machine._MakeFragment(header.allocated_size + 1) 1121 1122 # Copy existing elements. 1123 1124 for i in range(1, header.allocated_size): 1125 self.machine.save(new_fragment + i, self.machine.load(fragment + i)) 1126 1127 self.machine.save(new_fragment + header.allocated_size, (arg_context, arg)) 1128 1129 # Set the new fragment in the object. 1130 # NOTE: The old fragment could be deallocated. 1131 1132 self.machine.save(obj + 1, (None, new_fragment)) 1133 1134 def builtins_tuple_len(self): 1135 frame = self.local_sp_stack[-1] 1136 1137 # Get the tuple address. 1138 1139 obj_context, obj = self.frame_stack[frame] 1140 1141 # Get the header. 1142 # NOTE: Assume single location for header. 1143 1144 header = self.machine.load(obj) 1145 nelements = header.size - 1 1146 1147 # Make a new object. 1148 1149 addr = self.machine._MakeObject(2, self.int_instance) 1150 1151 # Store the result. 1152 # NOTE: The data is considered ready to use. 1153 1154 self.machine.save(addr + 1, nelements) 1155 1156 # Return the new object. 1157 # Introduce object as context for the new object. 1158 1159 self.machine.result = addr, addr 1160 1161 def builtins_tuple_getitem(self): 1162 frame = self.local_sp_stack[-1] 1163 1164 # Get the operand address. 1165 1166 item_context, item = self.frame_stack[frame + 1] 1167 1168 # Get the tuple address. 1169 1170 obj_context, obj = self.frame_stack[frame] 1171 1172 # Get the header. 1173 # NOTE: Assume single location for header. 1174 1175 header = self.machine.load(obj) 1176 nelements = header.size - 1 1177 1178 # NOTE: Assume single location for data and header. 1179 1180 item_pos = self.machine.load(item + 1) 1181 1182 if item_pos >= 0 and item_pos < nelements: 1183 pass 1184 elif item_pos < 0 and item_pos >= -nelements: 1185 item_pos = nelements + item_pos 1186 else: 1187 self.machine.exception = self.machine._MakeObject(2, self.index_error_instance) 1188 return self.machine.RaiseException() 1189 1190 # NOTE: Assume single location for header. 1191 1192 self.machine.result = self.machine.load(obj + 1 + item_pos) 1193 1194 def builtins_object_init(self): 1195 pass 1196 1197 native_functions = { 1198 1199 # Native method implementations: 1200 1201 "__builtins__.int.__add__" : builtins_int_add, 1202 "__builtins__.int.__radd__" : builtins_int_add, # NOTE: To be made distinct. 1203 "__builtins__.int.__sub__" : builtins_int_sub, 1204 "__builtins__.int.__iadd__" : builtins_int_add, 1205 "__builtins__.int.__bool__" : builtins_int_bool, 1206 "__builtins__.int.__neg__" : builtins_int_neg, 1207 "__builtins__.int.__lt__" : builtins_int_lt, 1208 "__builtins__.int.__le__" : builtins_int_le, 1209 "__builtins__.int.__gt__" : builtins_int_gt, 1210 "__builtins__.int.__ge__" : builtins_int_ge, 1211 "__builtins__.int.__eq__" : builtins_int_eq, 1212 "__builtins__.int.__ne__" : builtins_int_ne, 1213 "__builtins__.int.__and__" : builtins_int_and, 1214 "__builtins__.int.__rand__" : builtins_int_and, 1215 "__builtins__.int.__or__" : builtins_int_or, 1216 "__builtins__.int.__ror__" : builtins_int_or, 1217 "__builtins__.bool.__bool__" : builtins_bool_bool, 1218 "__builtins__.list.__getitem__" : builtins_list_getitem, 1219 "__builtins__.list.__len__" : builtins_list_len, 1220 "__builtins__.list.append" : builtins_list_append, 1221 "__builtins__.tuple.__len__" : builtins_tuple_len, 1222 "__builtins__.tuple.__getitem__" : builtins_tuple_getitem, 1223 1224 # Native initialisers: 1225 1226 "__builtins__.object.__init__" : builtins_object_init, # NOTE: A no-operation. 1227 "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module. 1228 1229 # Native instantiators: 1230 1231 "__builtins__.list" : builtins_list_new, 1232 } 1233 1234 # Convenience functions. 1235 1236 def machine(program, with_builtins=0, debug=0, abort_upon_exception=0): 1237 print "Making the image..." 1238 code = program.get_image(with_builtins) 1239 print "Getting raw structures..." 1240 ot = program.get_object_table() 1241 pt = program.get_parameter_table() 1242 objlist = ot.as_list() 1243 paramlist = pt.as_list() 1244 print "Getting raw image..." 1245 rc = program.get_raw_image() 1246 print "Initialising the machine..." 1247 importer = program.get_importer() 1248 true_constant = importer.get_constant(True).location 1249 false_constant = importer.get_constant(False).location 1250 rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception) 1251 library = Library(rm, true_constant, false_constant) 1252 rm.library = library 1253 rm.pc = program.code_location 1254 print "Returning program occupying %d locations." % len(rm.memory) 1255 return rm 1256 1257 # vim: tabstop=4 expandtab shiftwidth=4