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