micropython

Annotated rsvp.py

260:25c1660165db
2009-09-25 Paul Boddie Added __name__ attribute definition to each module. Added more int method declarations.
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@256 130
        cls = self._get_class("__builtins__", "tuple")
paul@255 131
        self.tuple_class = cls.location
paul@255 132
        self.tuple_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@256 584
        (nargs, ndefaults) = 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
        if not ((nargs - ndefaults) <= nlocals):
paul@234 593
            raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs)
paul@222 594
            self.exception = self._MakeObject(2, self.type_error_instance)
paul@221 595
            return self.RaiseException()
paul@215 596
paul@256 597
    def CheckExtra(self):
paul@256 598
        nargs = self.operand
paul@256 599
paul@256 600
        # The frame is actually installed as the locals.
paul@256 601
        # Retrieve the context from the first local.
paul@256 602
paul@256 603
        frame = self.local_sp_stack[-1]
paul@256 604
        nlocals = len(self.frame_stack[frame:])
paul@256 605
paul@256 606
        # Provide the extra star parameter if necessary.
paul@256 607
paul@256 608
        if nlocals == nargs:
paul@256 609
            self.frame_stack.extend([None]) # ExtendFrame(1)
paul@256 610
paul@215 611
    def FillDefaults(self):
paul@233 612
        context, ref = self.value
paul@233 613
        (nargs, ndefaults) = self.operand
paul@215 614
paul@215 615
        # The frame is actually installed as the locals.
paul@215 616
paul@215 617
        frame = self.local_sp_stack[-1]
paul@215 618
        nlocals = len(self.frame_stack[frame:])
paul@134 619
paul@190 620
        # Support population of defaults.
paul@190 621
        # This involves copying the "attributes" of a function into the frame.
paul@190 622
paul@215 623
        default = nlocals - (nargs - ndefaults)
paul@215 624
        self.frame_stack.extend([None] * (nargs - nlocals))
paul@215 625
        pos = nlocals
paul@190 626
paul@215 627
        while pos < nargs:
paul@190 628
            self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header
paul@190 629
            default += 1
paul@190 630
            pos += 1
paul@117 631
paul@255 632
    def CopyExtra(self):
paul@255 633
        start = self.operand
paul@255 634
paul@255 635
        # The frame is the source of the extra arguments.
paul@255 636
paul@255 637
        frame = self.local_sp_stack[-1]
paul@255 638
        nlocals = len(self.frame_stack[frame:])
paul@255 639
paul@255 640
        # Make a tuple to hold the arguments.
paul@255 641
paul@255 642
        ref = self._MakeObject(nlocals - start + 1, self.tuple_instance)
paul@255 643
paul@255 644
        extra = 0
paul@255 645
        pos = start
paul@255 646
paul@255 647
        while pos < nlocals:
paul@255 648
            self.save(ref + extra + 1, self.frame_stack[frame + pos]) # skip header when storing
paul@255 649
            extra += 1
paul@255 650
            pos += 1
paul@255 651
paul@255 652
        self.value = ref, ref
paul@255 653
paul@134 654
    def CheckSelf(self):
paul@134 655
        context, ref = self.value
paul@234 656
        context_context, context_ref = self.source
paul@135 657
paul@145 658
        # Check the details of the proposed context and the target's context.
paul@135 659
paul@234 660
        self.status = self._CheckInstance(ref, context_ref)
paul@124 661
paul@230 662
    def JumpInFrame(self):
paul@230 663
        codeaddr = self.callable
paul@230 664
        return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one
paul@230 665
paul@132 666
    def JumpWithFrame(self):
paul@219 667
        codeaddr = self.callable
paul@132 668
        self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame
paul@138 669
        return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one
paul@98 670
paul@215 671
    def JumpWithFrameDirect(self):
paul@215 672
        operand = self.operand
paul@215 673
        self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame
paul@215 674
        return self.jump(operand, self.pc + 1) # return to the instruction after this one
paul@215 675
paul@132 676
    def ExtendFrame(self):
paul@138 677
        self.frame_stack.extend([None] * self.operand)
paul@98 678
paul@137 679
    def AdjustFrame(self):
paul@234 680
        self.invocation_sp_stack[-1] += self.operand
paul@137 681
paul@68 682
    def Return(self):
paul@138 683
        return self.pull_pc()
paul@68 684
paul@117 685
    def LoadResult(self):
paul@117 686
        self.value = self.result
paul@117 687
paul@117 688
    def StoreResult(self):
paul@117 689
        self.result = self.value
paul@117 690
paul@118 691
    def Jump(self):
paul@119 692
        return self.operand
paul@98 693
paul@68 694
    def JumpIfTrue(self):
paul@117 695
        if self.status:
paul@119 696
            return self.operand
paul@68 697
paul@68 698
    def JumpIfFalse(self):
paul@117 699
        if not self.status:
paul@119 700
            return self.operand
paul@68 701
paul@129 702
    def LoadException(self):
paul@197 703
        self.value = self.exception, self.exception
paul@129 704
paul@129 705
    def StoreException(self):
paul@145 706
        self.exception = self.value[1]
paul@129 707
paul@232 708
    def ClearException(self):
paul@232 709
        self.exception = None
paul@232 710
paul@129 711
    def RaiseException(self):
paul@221 712
        # NOTE: Adding the program counter as the first attribute.
paul@222 713
        self.save(self.exception + 1, self.pc)
paul@221 714
        # Jumping to the current handler.
paul@246 715
        if self.abort_upon_exception:
paul@246 716
            raise Exception
paul@145 717
        return self.handler_stack[-1]
paul@129 718
paul@129 719
    def PushHandler(self):
paul@129 720
        self.handler_stack.append(self.operand)
paul@145 721
        self.handler_local_sp_stack.append(len(self.local_sp_stack))
paul@145 722
        self.handler_pc_stack.append(len(self.pc_stack))
paul@129 723
paul@129 724
    def PopHandler(self):
paul@145 725
        # Reduce the local frame pointer stack to refer to the handler's frame.
paul@248 726
        del self.local_sp_stack[self.handler_local_sp_stack.pop():]
paul@145 727
        # Reduce the PC stack to discard all superfluous return addresses.
paul@145 728
        self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()]
paul@129 729
        self.handler_stack.pop()
paul@129 730
paul@129 731
    def CheckException(self):
paul@181 732
        self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1])
paul@129 733
paul@129 734
    def TestIdentity(self):
paul@145 735
        self.status = self.value[1] == self.source[1]
paul@129 736
paul@129 737
    def TestIdentityAddress(self):
paul@129 738
        self.status = self.value[1] == self.operand
paul@129 739
paul@129 740
    # LoadBoolean is implemented in the generated code.
paul@129 741
    # StoreBoolean is implemented by testing against the True value.
paul@68 742
paul@132 743
    def InvertBoolean(self):
paul@132 744
        self.status = not self.status
paul@132 745
paul@145 746
    # Common implementation details.
paul@145 747
paul@145 748
    def _CheckInstance(self, ref, cls):
paul@182 749
        data = self.load(ref)
paul@182 750
        target_data = self.load(cls)
paul@145 751
paul@223 752
        # Insist on instance vs. class.
paul@223 753
paul@242 754
        if data.attrcode is None: # absent attrcode == class/module
paul@223 755
            return 0
paul@191 756
paul@207 757
        if target_data.attrcode is not None: # present attrcode == instance
paul@191 758
            return 0
paul@191 759
paul@145 760
        # Find the table entry for the descendant.
paul@145 761
paul@182 762
        element = self.objlist[target_data.classcode + data.attrcode]
paul@238 763
paul@220 764
        if element is not None:
paul@242 765
            attr_index, static_attr, offset = element
paul@220 766
            return attr_index == data.attrcode
paul@220 767
        else:
paul@220 768
            return 0
paul@181 769
paul@181 770
    def _MakeObject(self, size, ref):
paul@203 771
        # Load the template.
paul@182 772
        data = self.load(ref)
paul@181 773
        addr = self.new(size)
paul@203 774
        # Save the header, overriding the size.
paul@203 775
        self.save(addr, data.with_size(size))
paul@181 776
        return addr
paul@181 777
paul@246 778
    def _MakeFragment(self, size):
paul@246 779
        # Reserve twice the amount of space.
paul@246 780
        addr = self.new(size * 2)
paul@246 781
        # Save the header, overriding the size.
paul@246 782
        self.save(addr, FragmentObject(size, size * 2))
paul@246 783
        return addr
paul@246 784
paul@223 785
    def _LoadAddressContextCond(self, context, ref, inst_ref):
paul@191 786
        # Check the instance context against the target's context.
paul@230 787
        # This provides the context overriding for methods.
paul@238 788
        if context is ReplaceableContext or context is not PlaceholderContext and self._CheckInstance(inst_ref, context):
paul@191 789
            # Replace the context with the instance.
paul@191 790
            return inst_ref, ref
paul@191 791
        else:
paul@191 792
            return context, ref
paul@191 793
paul@248 794
class Library:
paul@248 795
paul@248 796
    "Native function implementations."
paul@248 797
paul@248 798
    def __init__(self, machine, true_constant, false_constant):
paul@248 799
paul@248 800
        """
paul@248 801
        Initialise the library with the 'machine' and the addresses
paul@248 802
        'true_constant' and 'false_constant'.
paul@248 803
        """
paul@248 804
paul@248 805
        self.machine = machine
paul@248 806
        self.true_constant = true_constant
paul@248 807
        self.false_constant = false_constant
paul@248 808
paul@248 809
        # Native class constants.
paul@248 810
paul@248 811
        cls = self.machine._get_class("__builtins__", "int")
paul@248 812
        self.int_class = cls.location
paul@248 813
        self.int_instance = cls.instance_template_location
paul@248 814
        cls = self.machine._get_class("__builtins__", "list")
paul@248 815
        self.list_class = cls.location
paul@248 816
        self.list_instance = cls.instance_template_location
paul@254 817
        cls = self.machine._get_class("__builtins__", "IndexError")
paul@254 818
        self.index_error = cls.location
paul@254 819
        self.index_error_instance = cls.instance_template_location
paul@248 820
paul@255 821
        self.tuple_class = self.machine.tuple_class
paul@248 822
        self.type_error_instance = self.machine.type_error_instance
paul@255 823
paul@248 824
        self.frame_stack = self.machine.frame_stack
paul@248 825
        self.local_sp_stack = self.machine.local_sp_stack
paul@181 826
paul@244 827
    def builtins_int_arithmetic_op(self, op):
paul@181 828
        frame = self.local_sp_stack[-1]
paul@181 829
paul@181 830
        # Get operands addresses.
paul@181 831
paul@181 832
        left_context, left = self.frame_stack[frame]
paul@181 833
        right_context, right = self.frame_stack[frame + 1]
paul@181 834
paul@181 835
        # Test operand suitability.
paul@232 836
        # NOTE: Support other types.
paul@181 837
paul@248 838
        if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)):
paul@248 839
            self.machine.exception = self.machine._MakeObject(2, self.type_error_instance)
paul@248 840
            return self.machine.RaiseException()
paul@181 841
paul@181 842
        # NOTE: Assume single location for data.
paul@181 843
paul@181 844
        left_data = left + 1
paul@181 845
        right_data = right + 1
paul@181 846
paul@181 847
        # Make a new object.
paul@181 848
paul@248 849
        addr = self.machine._MakeObject(2, self.int_instance)
paul@196 850
paul@196 851
        # Store the result.
paul@196 852
        # NOTE: The data is considered ready to use.
paul@196 853
paul@248 854
        self.machine.save(addr + 1, op(self.machine.load(left_data), self.machine.load(right_data)))
paul@181 855
paul@187 856
        # Return the new object.
paul@187 857
        # Introduce object as context for the new object.
paul@181 858
paul@248 859
        self.machine.result = addr, addr
paul@181 860
paul@196 861
    def builtins_int_bool(self):
paul@196 862
        frame = self.local_sp_stack[-1]
paul@196 863
paul@196 864
        # Get operands addresses.
paul@196 865
paul@196 866
        left_context, left = self.frame_stack[frame]
paul@196 867
paul@196 868
        # Test operand suitability.
paul@196 869
paul@248 870
        if not self.machine._CheckInstance(left, self.int_class):
paul@248 871
            self.machine.exception = self.machine._MakeObject(2, self.type_error_instance)
paul@248 872
            return self.machine.RaiseException()
paul@196 873
paul@196 874
        # NOTE: Assume single location for data.
paul@196 875
paul@196 876
        left_data = left + 1
paul@196 877
paul@196 878
        # Test the data.
paul@196 879
        # NOTE: The data is considered ready to use.
paul@196 880
paul@248 881
        if self.machine.load(left_data) != 0:
paul@248 882
            self.machine.result = self.true_constant, self.true_constant
paul@196 883
        else:
paul@248 884
            self.machine.result = self.false_constant, self.false_constant
paul@196 885
paul@216 886
    def builtins_int_neg(self):
paul@216 887
        frame = self.local_sp_stack[-1]
paul@216 888
paul@216 889
        # Get operands addresses.
paul@216 890
paul@216 891
        left_context, left = self.frame_stack[frame]
paul@216 892
paul@216 893
        # Test operand suitability.
paul@216 894
paul@248 895
        if not self.machine._CheckInstance(left, self.int_class):
paul@248 896
            self.machine.exception = self.machine._MakeObject(2, self.type_error_instance)
paul@248 897
            return self.machine.RaiseException()
paul@216 898
paul@216 899
        # NOTE: Assume single location for data.
paul@216 900
paul@216 901
        left_data = left + 1
paul@216 902
paul@216 903
        # Make a new object.
paul@216 904
paul@248 905
        addr = self.machine._MakeObject(2, self.int_instance)
paul@216 906
paul@216 907
        # Store the result.
paul@216 908
        # NOTE: The data is considered ready to use.
paul@216 909
paul@248 910
        self.machine.save(addr + 1, -self.machine.load(left_data))
paul@216 911
paul@216 912
        # Return the new object.
paul@216 913
        # Introduce object as context for the new object.
paul@216 914
paul@248 915
        self.machine.result = addr, addr
paul@216 916
paul@232 917
    def builtins_int_op(self, op, true_if_incompatible):
paul@232 918
        frame = self.local_sp_stack[-1]
paul@232 919
paul@232 920
        # Get operands addresses.
paul@232 921
paul@232 922
        left_context, left = self.frame_stack[frame]
paul@232 923
        right_context, right = self.frame_stack[frame + 1]
paul@232 924
paul@232 925
        # Test operand suitability.
paul@232 926
        # NOTE: Support other types.
paul@232 927
        # NOTE: Handle comparisons of incompatible types more appropriately.
paul@232 928
paul@248 929
        if not (self.machine._CheckInstance(left, self.int_class) and self.machine._CheckInstance(right, self.int_class)):
paul@232 930
            if true_if_incompatible:
paul@248 931
                self.machine.result = self.true_constant, self.true_constant
paul@232 932
            else:
paul@248 933
                self.machine.result = self.false_constant, self.false_constant
paul@232 934
            return
paul@232 935
paul@232 936
        # NOTE: Assume single location for data.
paul@232 937
paul@232 938
        left_data = left + 1
paul@232 939
        right_data = right + 1
paul@232 940
paul@232 941
        # Test the data.
paul@232 942
        # NOTE: The data is considered ready to use.
paul@232 943
paul@248 944
        if op(self.machine.load(left_data), self.machine.load(right_data)):
paul@248 945
            self.machine.result = self.true_constant, self.true_constant
paul@232 946
        else:
paul@248 947
            self.machine.result = self.false_constant, self.false_constant
paul@232 948
paul@259 949
    def builtins_int_add(self):
paul@259 950
        return self.builtins_int_arithmetic_op(operator.add)
paul@259 951
paul@259 952
    def builtins_int_sub(self):
paul@259 953
        return self.builtins_int_arithmetic_op(operator.sub)
paul@259 954
paul@232 955
    def builtins_int_lt(self):
paul@249 956
        return self.builtins_int_op(operator.lt, 0)
paul@232 957
paul@240 958
    def builtins_int_le(self):
paul@249 959
        return self.builtins_int_op(operator.le, 0)
paul@240 960
paul@232 961
    def builtins_int_gt(self):
paul@249 962
        return self.builtins_int_op(operator.gt, 0)
paul@232 963
paul@240 964
    def builtins_int_ge(self):
paul@249 965
        return self.builtins_int_op(operator.ge, 0)
paul@240 966
paul@232 967
    def builtins_int_eq(self):
paul@249 968
        return self.builtins_int_op(operator.eq, 0)
paul@232 969
paul@232 970
    def builtins_int_ne(self):
paul@249 971
        return self.builtins_int_op(operator.ne, 1)
paul@232 972
paul@259 973
    def builtins_int_and(self):
paul@259 974
        return self.builtins_int_arithmetic_op(operator.and_)
paul@259 975
paul@259 976
    def builtins_int_or(self):
paul@259 977
        return self.builtins_int_arithmetic_op(operator.or_)
paul@259 978
paul@197 979
    def builtins_bool_bool(self):
paul@197 980
        frame = self.local_sp_stack[-1]
paul@197 981
paul@197 982
        # Get operands addresses.
paul@197 983
paul@197 984
        left_context, left = self.frame_stack[frame]
paul@248 985
        self.machine.result = left, left
paul@197 986
paul@203 987
    def builtins_list_new(self):
paul@203 988
        frame = self.local_sp_stack[-1]
paul@203 989
paul@243 990
        # The first parameter should be empty.
paul@203 991
        # NOTE: Specific copying of tuples/lists.
paul@203 992
paul@243 993
        args_context, args = self.frame_stack[frame + 1]
paul@246 994
paul@246 995
        # Test operand suitability.
paul@203 996
paul@248 997
        if self.machine._CheckInstance(args, self.list_class):
paul@248 998
            _x, sequence = self.machine.load(args + 1)
paul@248 999
            header = self.machine.load(sequence)
paul@246 1000
            size = header.occupied_size
paul@248 1001
        elif self.machine._CheckInstance(args, self.tuple_class):
paul@246 1002
            sequence = args
paul@248 1003
            header = self.machine.load(sequence)
paul@246 1004
            size = header.size
paul@246 1005
        else:
paul@248 1006
            self.machine.exception = self.machine._MakeObject(2, self.type_error_instance)
paul@248 1007
            return self.machine.RaiseException()
paul@203 1008
paul@246 1009
        # Copy the sequence contents.
paul@246 1010
paul@248 1011
        new_fragment = self.machine._MakeFragment(size)
paul@246 1012
        for i in range(1, size):
paul@248 1013
            self.machine.save(new_fragment + i, self.machine.load(sequence + i))
paul@246 1014
paul@246 1015
        # Make the list instance.
paul@246 1016
paul@248 1017
        addr = self.machine._MakeObject(2, self.list_instance)
paul@248 1018
        self.machine.save(addr + 1, (None, new_fragment))
paul@246 1019
paul@248 1020
        self.machine.result = addr, addr
paul@203 1021
paul@216 1022
    def builtins_list_getitem(self):
paul@216 1023
        frame = self.local_sp_stack[-1]
paul@216 1024
paul@246 1025
        # Get the operand address.
paul@216 1026
paul@216 1027
        item_context, item = self.frame_stack[frame + 1]
paul@216 1028
paul@246 1029
        # Get the list address.
paul@246 1030
paul@246 1031
        obj_context, obj = self.frame_stack[frame]
paul@246 1032
paul@246 1033
        # Get the fragment address.
paul@246 1034
        # NOTE: Assume single location for header.
paul@216 1035
paul@248 1036
        _x, fragment = self.machine.load(obj + 1)
paul@246 1037
paul@246 1038
        # Get the fragment header.
paul@246 1039
paul@248 1040
        header = self.machine.load(fragment)
paul@246 1041
        nelements = header.occupied_size - 1
paul@246 1042
paul@246 1043
        # NOTE: Assume single location for data and header.
paul@216 1044
paul@248 1045
        item_pos = self.machine.load(item + 1)
paul@254 1046
paul@216 1047
        if item_pos >= 0 and item_pos < nelements:
paul@216 1048
            pass
paul@216 1049
        elif item_pos < 0 and item_pos >= -nelements:
paul@216 1050
            item_pos = nelements + item_pos
paul@216 1051
        else:
paul@248 1052
            self.machine.exception = self.machine._MakeObject(2, self.index_error_instance)
paul@248 1053
            return self.machine.RaiseException()
paul@216 1054
paul@246 1055
        # NOTE: Assume single location for header.
paul@246 1056
paul@248 1057
        self.machine.result = self.machine.load(fragment + 1 + item_pos)
paul@246 1058
paul@246 1059
    def builtins_list_len(self):
paul@246 1060
        frame = self.local_sp_stack[-1]
paul@246 1061
paul@246 1062
        # Get the list address.
paul@246 1063
paul@246 1064
        obj_context, obj = self.frame_stack[frame]
paul@246 1065
paul@246 1066
        # Get the fragment address.
paul@246 1067
        # NOTE: Assume single location for header.
paul@246 1068
paul@248 1069
        _x, fragment = self.machine.load(obj + 1)
paul@246 1070
paul@246 1071
        # Get the fragment header.
paul@246 1072
paul@248 1073
        header = self.machine.load(fragment)
paul@246 1074
        nelements = header.occupied_size - 1
paul@246 1075
paul@246 1076
        # Make a new object.
paul@246 1077
paul@248 1078
        addr = self.machine._MakeObject(2, self.int_instance)
paul@246 1079
paul@246 1080
        # Store the result.
paul@246 1081
        # NOTE: The data is considered ready to use.
paul@246 1082
paul@248 1083
        self.machine.save(addr + 1, nelements)
paul@246 1084
paul@246 1085
        # Return the new object.
paul@246 1086
        # Introduce object as context for the new object.
paul@246 1087
paul@248 1088
        self.machine.result = addr, addr
paul@246 1089
paul@246 1090
    def builtins_list_append(self):
paul@246 1091
        frame = self.local_sp_stack[-1]
paul@246 1092
paul@246 1093
        # Get operand address.
paul@246 1094
paul@246 1095
        arg_context, arg = self.frame_stack[frame + 1]
paul@246 1096
paul@246 1097
        # Get the list address.
paul@246 1098
paul@246 1099
        obj_context, obj = self.frame_stack[frame]
paul@246 1100
paul@246 1101
        # Get the fragment address.
paul@246 1102
        # NOTE: Assume single location for header.
paul@246 1103
paul@248 1104
        _x, fragment = self.machine.load(obj + 1)
paul@246 1105
paul@246 1106
        # Get the fragment header.
paul@246 1107
paul@248 1108
        header = self.machine.load(fragment)
paul@246 1109
paul@246 1110
        # Attempt to add the reference.
paul@246 1111
paul@246 1112
        if header.occupied_size < header.allocated_size:
paul@248 1113
            self.machine.save(fragment + header.occupied_size, (arg_context, arg))
paul@246 1114
            header.occupied_size += 1
paul@246 1115
        else:
paul@246 1116
paul@246 1117
            # Make a new fragment, maintaining more space than currently
paul@246 1118
            # occupied in order to avoid reallocation.
paul@246 1119
paul@251 1120
            new_fragment = self.machine._MakeFragment(header.allocated_size + 1)
paul@246 1121
paul@246 1122
            # Copy existing elements.
paul@246 1123
paul@246 1124
            for i in range(1, header.allocated_size):
paul@248 1125
                self.machine.save(new_fragment + i, self.machine.load(fragment + i))
paul@246 1126
paul@248 1127
            self.machine.save(new_fragment + header.allocated_size, (arg_context, arg))
paul@246 1128
paul@246 1129
            # Set the new fragment in the object.
paul@246 1130
            # NOTE: The old fragment could be deallocated.
paul@246 1131
paul@248 1132
            self.machine.save(obj + 1, (None, new_fragment))
paul@216 1133
paul@254 1134
    def builtins_tuple_len(self):
paul@254 1135
        frame = self.local_sp_stack[-1]
paul@254 1136
paul@254 1137
        # Get the tuple address.
paul@254 1138
paul@254 1139
        obj_context, obj = self.frame_stack[frame]
paul@254 1140
paul@254 1141
        # Get the header.
paul@254 1142
        # NOTE: Assume single location for header.
paul@254 1143
paul@254 1144
        header = self.machine.load(obj)
paul@254 1145
        nelements = header.size - 1
paul@254 1146
paul@254 1147
        # Make a new object.
paul@254 1148
paul@254 1149
        addr = self.machine._MakeObject(2, self.int_instance)
paul@254 1150
paul@254 1151
        # Store the result.
paul@254 1152
        # NOTE: The data is considered ready to use.
paul@254 1153
paul@254 1154
        self.machine.save(addr + 1, nelements)
paul@254 1155
paul@254 1156
        # Return the new object.
paul@254 1157
        # Introduce object as context for the new object.
paul@254 1158
paul@254 1159
        self.machine.result = addr, addr
paul@254 1160
paul@254 1161
    def builtins_tuple_getitem(self):
paul@254 1162
        frame = self.local_sp_stack[-1]
paul@254 1163
paul@254 1164
        # Get the operand address.
paul@254 1165
paul@254 1166
        item_context, item = self.frame_stack[frame + 1]
paul@254 1167
paul@254 1168
        # Get the tuple address.
paul@254 1169
paul@254 1170
        obj_context, obj = self.frame_stack[frame]
paul@254 1171
paul@254 1172
        # Get the header.
paul@254 1173
        # NOTE: Assume single location for header.
paul@254 1174
paul@254 1175
        header = self.machine.load(obj)
paul@254 1176
        nelements = header.size - 1
paul@254 1177
paul@254 1178
        # NOTE: Assume single location for data and header.
paul@254 1179
paul@254 1180
        item_pos = self.machine.load(item + 1)
paul@254 1181
paul@254 1182
        if item_pos >= 0 and item_pos < nelements:
paul@254 1183
            pass
paul@254 1184
        elif item_pos < 0 and item_pos >= -nelements:
paul@254 1185
            item_pos = nelements + item_pos
paul@254 1186
        else:
paul@254 1187
            self.machine.exception = self.machine._MakeObject(2, self.index_error_instance)
paul@254 1188
            return self.machine.RaiseException()
paul@254 1189
paul@254 1190
        # NOTE: Assume single location for header.
paul@254 1191
paul@254 1192
        self.machine.result = self.machine.load(obj + 1 + item_pos)
paul@254 1193
paul@208 1194
    def builtins_object_init(self):
paul@208 1195
        pass
paul@208 1196
paul@181 1197
    native_functions = {
paul@241 1198
paul@241 1199
        # Native method implementations:
paul@241 1200
paul@181 1201
        "__builtins__.int.__add__" : builtins_int_add,
paul@241 1202
        "__builtins__.int.__radd__" : builtins_int_add,               # NOTE: To be made distinct.
paul@244 1203
        "__builtins__.int.__sub__" : builtins_int_sub,
paul@240 1204
        "__builtins__.int.__iadd__" : builtins_int_add,
paul@196 1205
        "__builtins__.int.__bool__" : builtins_int_bool,
paul@216 1206
        "__builtins__.int.__neg__" : builtins_int_neg,
paul@232 1207
        "__builtins__.int.__lt__" : builtins_int_lt,
paul@240 1208
        "__builtins__.int.__le__" : builtins_int_le,
paul@232 1209
        "__builtins__.int.__gt__" : builtins_int_gt,
paul@240 1210
        "__builtins__.int.__ge__" : builtins_int_ge,
paul@232 1211
        "__builtins__.int.__eq__" : builtins_int_eq,
paul@232 1212
        "__builtins__.int.__ne__" : builtins_int_ne,
paul@259 1213
        "__builtins__.int.__and__" : builtins_int_and,
paul@259 1214
        "__builtins__.int.__rand__" : builtins_int_and,
paul@259 1215
        "__builtins__.int.__or__" : builtins_int_or,
paul@259 1216
        "__builtins__.int.__ror__" : builtins_int_or,
paul@197 1217
        "__builtins__.bool.__bool__" : builtins_bool_bool,
paul@241 1218
        "__builtins__.list.__getitem__" : builtins_list_getitem,
paul@246 1219
        "__builtins__.list.__len__" : builtins_list_len,
paul@246 1220
        "__builtins__.list.append" : builtins_list_append,
paul@254 1221
        "__builtins__.tuple.__len__" : builtins_tuple_len,
paul@254 1222
        "__builtins__.tuple.__getitem__" : builtins_tuple_getitem,
paul@254 1223
paul@254 1224
        # Native initialisers:
paul@254 1225
paul@241 1226
        "__builtins__.object.__init__" : builtins_object_init,        # NOTE: A no-operation.
paul@241 1227
        "__builtins__.BaseException.__init__" : builtins_object_init, # NOTE: To be made distinct, potentially in the builtins module.
paul@241 1228
paul@241 1229
        # Native instantiators:
paul@241 1230
paul@203 1231
        "__builtins__.list" : builtins_list_new,
paul@181 1232
        }
paul@145 1233
paul@228 1234
# Convenience functions.
paul@228 1235
paul@246 1236
def machine(program, with_builtins=0, debug=0, abort_upon_exception=0):
paul@228 1237
    print "Making the image..."
paul@228 1238
    code = program.get_image(with_builtins)
paul@228 1239
    print "Getting raw structures..."
paul@228 1240
    ot = program.get_object_table()
paul@228 1241
    pt = program.get_parameter_table()
paul@228 1242
    objlist = ot.as_list()
paul@228 1243
    paramlist = pt.as_list()
paul@228 1244
    print "Getting raw image..."
paul@228 1245
    rc = program.get_raw_image()
paul@228 1246
    print "Initialising the machine..."
paul@228 1247
    importer = program.get_importer()
paul@228 1248
    true_constant = importer.get_constant(True).location
paul@228 1249
    false_constant = importer.get_constant(False).location
paul@248 1250
    rm = RSVPMachine(rc, objlist, paramlist, debug=debug, abort_upon_exception=abort_upon_exception)
paul@248 1251
    library = Library(rm, true_constant, false_constant)
paul@248 1252
    rm.library = library
paul@228 1253
    rm.pc = program.code_location
paul@241 1254
    print "Returning program occupying %d locations." % len(rm.memory)
paul@228 1255
    return rm
paul@228 1256
paul@68 1257
# vim: tabstop=4 expandtab shiftwidth=4