micropython

Annotated rsvp.py

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