micropython

rsvp.py

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