micropython

Annotated rsvp.py

217:62219b8d6baf
2009-05-17 Paul Boddie Fixed context detection in CheckFrame.
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@201 55
from micropython.program import DataObject # for creating "nice" new objects
paul@182 56
paul@68 57
class IllegalInstruction(Exception):
paul@68 58
    pass
paul@68 59
paul@68 60
class IllegalAddress(Exception):
paul@180 61
    def __init__(self, address):
paul@180 62
        self.address = address
paul@180 63
    def __repr__(self):
paul@180 64
        return "IllegalAddress(%r)" % self.address
paul@180 65
    def __str__(self):
paul@180 66
        return repr(self)
paul@68 67
paul@68 68
class EmptyPCStack(Exception):
paul@68 69
    pass
paul@68 70
paul@92 71
class EmptyFrameStack(Exception):
paul@68 72
    pass
paul@68 73
paul@194 74
class BreakpointReached(Exception):
paul@194 75
    pass
paul@194 76
paul@68 77
class RSVPMachine:
paul@68 78
paul@68 79
    "A really simple virtual processor."
paul@68 80
paul@196 81
    def __init__(self, memory, objlist, paramlist, true_constant, false_constant, pc=None, debug=0):
paul@68 82
paul@68 83
        """
paul@68 84
        Initialise the processor with a 'memory' (a list of values containing
paul@196 85
        instructions and data), the object and parameter lists 'objlist' and
paul@196 86
        'paramlist', the addresses 'true_constant' and 'false_constant', and the
paul@196 87
        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@196 95
        self.true_constant = true_constant
paul@196 96
        self.false_constant = false_constant
paul@196 97
paul@68 98
        self.pc = pc or 0
paul@68 99
        self.debug = debug
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@192 124
        self.attr_error = objlist.access("__builtins__", "AttributeError").get_value().location
paul@192 125
        self.type_error = objlist.access("__builtins__", "TypeError").get_value().location
paul@216 126
        self.index_error = objlist.access("__builtins__", "IndexError").get_value().location
paul@181 127
paul@181 128
        # Native class constants.
paul@181 129
paul@182 130
        cls = objlist.access("__builtins__", "int")
paul@196 131
        self.int_class_location = cls and cls.get_value() and cls.get_value().location
paul@192 132
        self.int_instance_location = cls and cls.get_value() and cls.get_value().instance_template_location
paul@203 133
        cls = objlist.access("__builtins__", "list")
paul@203 134
        self.list_instance_location = cls and cls.get_value() and cls.get_value().instance_template_location
paul@146 135
paul@194 136
        # Debugging attributes.
paul@194 137
paul@194 138
        self.breakpoints = set()
paul@194 139
paul@162 140
    # Debugging methods.
paul@162 141
paul@137 142
    def dump(self):
paul@138 143
        print "PC", self.pc, "->", self.load(self.pc)
paul@137 144
        print "PC stack", self.pc_stack
paul@137 145
        print "Frame stack", self.frame_stack
paul@137 146
        print "Local stack pointers", self.local_sp_stack
paul@137 147
        print "Invocation stack pointers", self.invocation_sp_stack
paul@137 148
        print "Handler stack", self.handler_stack
paul@145 149
        print "Handler frame stack", self.handler_local_sp_stack
paul@145 150
        print "Handler PC stack", self.handler_pc_stack
paul@137 151
        print
paul@137 152
        print "Instruction", self.instruction
paul@137 153
        print "Operand", self.operand
paul@137 154
        print "Value", self.value
paul@137 155
        print "Status", self.status
paul@137 156
        print "Source", self.source
paul@137 157
        print "Callable", self.callable
paul@137 158
        print "Result", self.result
paul@137 159
        print "Exception", self.exception
paul@137 160
paul@162 161
    def show(self):
paul@162 162
        self.show_memory(self.memory, 0)
paul@162 163
paul@162 164
    def show_pc(self, run_in=10):
paul@162 165
        start = max(0, self.pc - run_in)
paul@162 166
        end = self.pc + run_in
paul@162 167
        memory = self.memory[start:end]
paul@162 168
        self.show_memory(memory, start)
paul@162 169
paul@162 170
    def show_memory(self, memory, start):
paul@162 171
        for i, x in enumerate(memory):
paul@162 172
            location = start + i
paul@162 173
            if location == self.pc:
paul@162 174
                print "->",
paul@162 175
            else:
paul@162 176
                print "  ",
paul@162 177
            print "%5d %r" % (location, x)
paul@162 178
paul@162 179
    def step(self, dump=0):
paul@138 180
        self.execute()
paul@162 181
        self.show_pc()
paul@162 182
        if dump:
paul@162 183
            self.dump()
paul@162 184
paul@194 185
    def set_break(self, location):
paul@194 186
        self.breakpoints.add(location)
paul@194 187
paul@162 188
    # Internal operations.
paul@138 189
paul@68 190
    def load(self, address):
paul@68 191
paul@68 192
        "Return the value at the given 'address'."
paul@68 193
paul@68 194
        try:
paul@68 195
            return self.memory[address]
paul@68 196
        except IndexError:
paul@180 197
            raise IllegalAddress(address)
paul@180 198
        except TypeError:
paul@180 199
            raise IllegalAddress(address)
paul@68 200
paul@68 201
    def save(self, address, value):
paul@68 202
paul@68 203
        "Save to the given 'address' the specified 'value'."
paul@68 204
paul@68 205
        try:
paul@68 206
            self.memory[address] = value
paul@68 207
        except IndexError:
paul@180 208
            raise IllegalAddress(address)
paul@180 209
        except TypeError:
paul@180 210
            raise IllegalAddress(address)
paul@68 211
paul@68 212
    def new(self, size):
paul@68 213
paul@68 214
        """
paul@68 215
        Allocate space of the given 'size', returning the address of the space.
paul@68 216
        """
paul@68 217
paul@68 218
        addr = len(self.memory)
paul@68 219
        for i in range(0, size):
paul@68 220
            self.memory.append(None)
paul@68 221
        return addr
paul@68 222
paul@68 223
    def push_pc(self, data):
paul@68 224
paul@68 225
        "Push 'data' onto the PC stack."
paul@68 226
paul@68 227
        self.pc_stack.append(data)
paul@68 228
paul@68 229
    def pull_pc(self):
paul@68 230
paul@68 231
        "Pull a value from the PC stack and return it."
paul@68 232
paul@68 233
        try:
paul@68 234
            return self.pc_stack.pop()
paul@68 235
        except IndexError:
paul@68 236
            raise EmptyPCStack
paul@68 237
paul@90 238
    def run(self):
paul@68 239
paul@68 240
        "Execute code in the memory, starting from the current PC address."
paul@68 241
paul@68 242
        try:
paul@68 243
            while 1:
paul@90 244
                self.execute()
paul@68 245
        except EmptyPCStack:
paul@68 246
            pass
paul@68 247
paul@90 248
    def execute(self):
paul@90 249
paul@90 250
        "Execute code in the memory at the current PC address."
paul@90 251
paul@194 252
        if self.pc in self.breakpoints:
paul@194 253
            self.breakpoints.remove(self.pc)
paul@194 254
            raise BreakpointReached
paul@194 255
paul@119 256
        self.instruction = self.load(self.pc)
paul@118 257
paul@118 258
        # Process any inputs of the instruction.
paul@118 259
paul@119 260
        self.process_inputs()
paul@137 261
paul@137 262
        # Perform the instruction itself.
paul@137 263
paul@137 264
        next_pc = self.perform(self.instruction)
paul@118 265
paul@119 266
        # Update the program counter.
paul@118 267
paul@119 268
        if next_pc is None:
paul@119 269
            self.pc += 1
paul@119 270
        else:
paul@119 271
            self.pc = next_pc
paul@118 272
paul@137 273
    def get_method(self, instruction):
paul@137 274
paul@137 275
        "Return the handler method for the given 'instruction'."
paul@119 276
paul@137 277
        instruction_name = instruction.__class__.__name__
paul@137 278
        if self.debug:
paul@137 279
            print "%8d %s" % (self.pc, instruction_name)
paul@119 280
        method = getattr(self, instruction_name, None)
paul@90 281
        if method is None:
paul@119 282
            raise IllegalInstruction, (self.pc, instruction_name)
paul@118 283
        return method
paul@118 284
paul@137 285
    def perform(self, instruction):
paul@137 286
paul@137 287
        "Perform the 'instruction', returning the next PC value or None."
paul@137 288
paul@137 289
        self.operand = instruction.get_operand()
paul@137 290
        method = self.get_method(instruction)
paul@137 291
        return method()
paul@137 292
paul@119 293
    def process_inputs(self):
paul@118 294
paul@118 295
        """
paul@119 296
        Process any inputs of the current instruction. This permits any directly
paul@118 297
        connected sub-instructions to produce the effects that separate
paul@118 298
        instructions would otherwise have.
paul@118 299
        """
paul@118 300
paul@190 301
        value = self.value
paul@174 302
        if self.instruction.source is not None:
paul@174 303
            self.perform(self.instruction.source)
paul@174 304
            self.source = self.value
paul@190 305
        self.value = value
paul@174 306
        if self.instruction.input is not None:
paul@174 307
            self.perform(self.instruction.input)
paul@90 308
paul@68 309
    def jump(self, addr, next):
paul@68 310
paul@68 311
        """
paul@68 312
        Jump to the subroutine at (or identified by) 'addr'. If 'addr'
paul@68 313
        identifies a library function then invoke the library function and set
paul@68 314
        PC to 'next' afterwards; otherwise, set PC to 'addr'.
paul@68 315
        """
paul@68 316
paul@181 317
        # Trap library functions introduced through the use of strings instead
paul@181 318
        # of proper locations.
paul@181 319
paul@68 320
        if isinstance(addr, str):
paul@196 321
            handler = self.native_functions[addr](self)
paul@196 322
            if handler is None:
paul@196 323
                return next
paul@196 324
            else:
paul@196 325
                return handler
paul@68 326
        else:
paul@138 327
            self.push_pc(self.pc + 1)
paul@119 328
            return addr
paul@68 329
paul@68 330
    # Instructions.
paul@68 331
paul@98 332
    def LoadConst(self):
paul@198 333
        self.value = None, self.operand # context of constant is not interesting
paul@68 334
paul@68 335
    def LoadName(self):
paul@117 336
        frame = self.local_sp_stack[-1]
paul@119 337
        self.value = self.frame_stack[frame + self.operand]
paul@68 338
paul@68 339
    def StoreName(self):
paul@117 340
        frame = self.local_sp_stack[-1]
paul@202 341
        self.frame_stack[frame + self.operand] = self.source
paul@68 342
paul@72 343
    LoadTemp = LoadName
paul@203 344
paul@203 345
    def StoreTemp(self):
paul@203 346
        frame = self.local_sp_stack[-1]
paul@203 347
        self.frame_stack[frame + self.operand] = self.value
paul@72 348
paul@72 349
    def LoadAddress(self):
paul@119 350
        # Preserve context (potentially null).
paul@119 351
        self.value = self.load(self.operand)
paul@72 352
paul@117 353
    def LoadAddressContext(self):
paul@191 354
        context, ref = self.load(self.operand)
paul@191 355
        inst_context, inst_ref = self.value
paul@194 356
        self.value = inst_ref, ref
paul@194 357
paul@194 358
    def LoadAddressContextCond(self):
paul@194 359
        context, ref = self.load(self.operand)
paul@194 360
        inst_context, inst_ref = self.value
paul@194 361
        self.value = self._LoadAddressContextCond(context, ref, inst_context, inst_ref)
paul@72 362
paul@72 363
    def StoreAddress(self):
paul@119 364
        # Preserve context.
paul@190 365
        self.save(self.operand, self.source)
paul@72 366
paul@117 367
    def MakeObject(self):
paul@138 368
        size = self.operand
paul@138 369
        context, ref = self.value
paul@184 370
        # NOTE: Referencing the instance template.
paul@184 371
        addr = self._MakeObject(size, ref - 1)
paul@184 372
        # Introduce object as context for the new object.
paul@185 373
        self.value = addr, addr
paul@72 374
paul@118 375
    def LoadAttr(self):
paul@118 376
        context, ref = self.value
paul@127 377
        # Retrieved context should already be appropriate for the instance.
paul@184 378
        # NOTE: Adding 1 to skip any header.
paul@174 379
        self.value = self.load(ref + self.operand + 1)
paul@118 380
paul@118 381
    def StoreAttr(self):
paul@119 382
        context, ref = self.value
paul@129 383
        # Target should already be an instance.
paul@184 384
        # NOTE: Adding 1 to skip any header.
paul@174 385
        self.save(ref + self.operand + 1, self.source)
paul@119 386
paul@119 387
    def LoadAttrIndex(self):
paul@118 388
        context, ref = self.value
paul@182 389
        data = self.load(ref)
paul@182 390
        element = self.objlist[data.classcode + self.operand]
paul@194 391
        attr_index, class_attr, offset = element
paul@135 392
        if attr_index == self.operand:
paul@127 393
            if class_attr:
paul@194 394
                self.value = self.load(offset) # offset is address of class attribute
paul@127 395
            else:
paul@127 396
                self.value = self.load(ref + offset)
paul@119 397
        else:
paul@146 398
            self.exception = self.attr_error
paul@190 399
            return self.RaiseException()
paul@118 400
paul@194 401
    def LoadAttrIndexContext(self):
paul@194 402
        context, ref = self.value
paul@194 403
        data = self.load(ref)
paul@194 404
        element = self.objlist[data.classcode + self.operand]
paul@194 405
        attr_index, class_attr, offset = element
paul@194 406
        if attr_index == self.operand:
paul@194 407
            loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute
paul@194 408
            self.value = ref, loaded_ref
paul@194 409
        else:
paul@194 410
            self.exception = self.attr_error
paul@194 411
            return self.RaiseException()
paul@194 412
paul@194 413
    def LoadAttrIndexContextCond(self):
paul@194 414
        context, ref = self.value
paul@194 415
        data = self.load(ref)
paul@194 416
        element = self.objlist[data.classcode + self.operand]
paul@194 417
        attr_index, class_attr, offset = element
paul@194 418
        if attr_index == self.operand:
paul@194 419
            if class_attr:
paul@194 420
                loaded_context, loaded_ref = self.load(offset) # offset is address of class attribute
paul@194 421
                self.value = self._LoadAddressContextCond(loaded_context, loaded_ref, context, ref)
paul@194 422
            else:
paul@194 423
                self.value = self.load(ref + offset)
paul@194 424
        else:
paul@194 425
            self.exception = self.attr_error
paul@194 426
            return self.RaiseException()
paul@194 427
paul@129 428
    def StoreAttrIndex(self):
paul@129 429
        context, ref = self.value
paul@182 430
        data = self.load(ref)
paul@182 431
        element = self.objlist[data.classcode + self.operand]
paul@194 432
        attr_index, class_attr, offset = element
paul@135 433
        if attr_index == self.operand:
paul@129 434
            if class_attr:
paul@146 435
                self.exception = self.type_error
paul@190 436
                return self.RaiseException()
paul@129 437
            else:
paul@129 438
                self.save(ref + offset, self.source)
paul@129 439
        else:
paul@146 440
            self.exception = self.attr_error
paul@190 441
            return self.RaiseException()
paul@118 442
paul@127 443
    # NOTE: LoadAttrIndexContext is a possibility if a particular attribute can always be overridden.
paul@127 444
paul@98 445
    def MakeFrame(self):
paul@117 446
        self.invocation_sp_stack.append(len(self.frame_stack))
paul@119 447
        self.frame_stack.extend([None] * self.operand)
paul@98 448
paul@98 449
    def DropFrame(self):
paul@117 450
        self.local_sp_stack.pop()
paul@117 451
        frame = self.invocation_sp_stack.pop()
paul@117 452
        self.frame_stack = self.frame_stack[:frame] # reset stack before call
paul@98 453
paul@137 454
    def RecoverFrame(self):
paul@137 455
        self.local_sp_stack.pop()
paul@137 456
paul@129 457
    def StoreFrame(self):
paul@129 458
        frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame
paul@129 459
        self.frame_stack[frame + self.operand] = self.value
paul@129 460
paul@129 461
    def StoreFrameIndex(self):
paul@184 462
        context, ref = self.value
paul@129 463
        frame = self.invocation_sp_stack[-1] # different from the current frame after MakeFrame
paul@182 464
        data = self.load(ref)
paul@190 465
        element = self.paramlist[data.funccode + self.operand]
paul@190 466
        # NOTE: Need to ensure correct positioning where a context has been generated.
paul@190 467
        param_index, offset = element
paul@190 468
        if param_index == self.operand:
paul@190 469
            self.frame_stack[frame + offset + 1] = self.source # add 1 to skip the context always generated
paul@129 470
        else:
paul@146 471
            self.exception = self.type_error
paul@190 472
            return self.RaiseException()
paul@129 473
paul@132 474
    def LoadCallable(self):
paul@132 475
        context, ref = self.value
paul@182 476
        data = self.load(ref)
paul@182 477
        self.callable = data.codeaddr, data.codedetails
paul@132 478
paul@132 479
    def StoreCallable(self):
paul@132 480
        context, ref = self.value
paul@134 481
        # NOTE: Should improve the representation and permit direct saving.
paul@182 482
        data = self.load(ref)
paul@207 483
        self.save(ref, (data.classcode, data.attrcode) + self.callable)
paul@132 484
paul@132 485
    def LoadContext(self):
paul@132 486
        context, ref = self.value
paul@198 487
        self.value = None, context # context of context is not interesting
paul@132 488
paul@132 489
    def CheckFrame(self):
paul@215 490
        (nargs, ndefaults, has_star) = self.operand
paul@215 491
paul@215 492
        # The frame is actually installed as the locals.
paul@215 493
        # Retrieve the context from the first local.
paul@215 494
paul@215 495
        frame = self.local_sp_stack[-1]
paul@217 496
        context_context, context_ref = self.frame_stack[frame] # + 0
paul@215 497
        nlocals = len(self.frame_stack[frame:])
paul@134 498
paul@134 499
        # Support sliding of the frame to exclude any inappropriate context.
paul@134 500
paul@217 501
        if context_ref is None:
paul@215 502
            self.local_sp_stack[-1] += 1
paul@215 503
            nlocals -= 1
paul@134 504
        else:
paul@217 505
            context_data = self.load(context_ref)
paul@207 506
            if context_data.attrcode is None: # absent attrcode == class
paul@215 507
                self.local_sp_stack[-1] += 1
paul@215 508
                nlocals -= 1
paul@134 509
paul@190 510
        # Test the frame size.
paul@215 511
        # NOTE: Raise a proper exception here.
paul@190 512
paul@215 513
        if not ((nargs - ndefaults) <= nlocals and (nlocals <= nargs or has_star)):
paul@215 514
            raise Exception, "CheckFrame %r (%r <= %r <= %r)" % (self.operand, nargs - ndefaults, nlocals, nargs)
paul@215 515
paul@215 516
    def FillDefaults(self):
paul@215 517
        (nargs, ndefaults) = self.operand
paul@215 518
paul@215 519
        # The frame is actually installed as the locals.
paul@215 520
paul@215 521
        frame = self.local_sp_stack[-1]
paul@215 522
        nlocals = len(self.frame_stack[frame:])
paul@134 523
paul@190 524
        # Support population of defaults.
paul@190 525
        # This involves copying the "attributes" of a function into the frame.
paul@190 526
paul@215 527
        default = nlocals - (nargs - ndefaults)
paul@215 528
        self.frame_stack.extend([None] * (nargs - nlocals))
paul@215 529
        pos = nlocals
paul@190 530
paul@215 531
        while pos < nargs:
paul@190 532
            self.frame_stack[frame + pos] = self.load(ref + default + 1) # skip header
paul@190 533
            default += 1
paul@190 534
            pos += 1
paul@117 535
paul@134 536
    def CheckSelf(self):
paul@134 537
        context, ref = self.value
paul@134 538
        target_context, target_ref = self.source
paul@135 539
paul@145 540
        # Check the details of the proposed context and the target's context.
paul@135 541
paul@181 542
        self.status = self._CheckInstance(ref, target_context)
paul@124 543
paul@132 544
    def JumpWithFrame(self):
paul@138 545
        codeaddr, codedetails = self.callable
paul@132 546
        self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame
paul@138 547
        return self.jump(codeaddr, self.pc + 1) # return to the instruction after this one
paul@98 548
paul@215 549
    def JumpWithFrameDirect(self):
paul@215 550
        operand = self.operand
paul@215 551
        self.local_sp_stack.append(self.invocation_sp_stack[-1]) # adopt the invocation frame
paul@215 552
        return self.jump(operand, self.pc + 1) # return to the instruction after this one
paul@215 553
paul@132 554
    def ExtendFrame(self):
paul@138 555
        self.frame_stack.extend([None] * self.operand)
paul@98 556
paul@137 557
    def AdjustFrame(self):
paul@208 558
        self.invocation_sp_stack[-1] += self.operand
paul@137 559
paul@68 560
    def Return(self):
paul@138 561
        return self.pull_pc()
paul@68 562
paul@117 563
    def LoadResult(self):
paul@117 564
        self.value = self.result
paul@117 565
paul@117 566
    def StoreResult(self):
paul@117 567
        self.result = self.value
paul@117 568
paul@118 569
    def Jump(self):
paul@119 570
        return self.operand
paul@98 571
paul@68 572
    def JumpIfTrue(self):
paul@117 573
        if self.status:
paul@119 574
            return self.operand
paul@68 575
paul@68 576
    def JumpIfFalse(self):
paul@117 577
        if not self.status:
paul@119 578
            return self.operand
paul@68 579
paul@129 580
    def LoadException(self):
paul@197 581
        self.value = self.exception, self.exception
paul@129 582
paul@129 583
    def StoreException(self):
paul@145 584
        self.exception = self.value[1]
paul@129 585
paul@129 586
    def RaiseException(self):
paul@145 587
        return self.handler_stack[-1]
paul@129 588
paul@129 589
    def PushHandler(self):
paul@129 590
        self.handler_stack.append(self.operand)
paul@145 591
        self.handler_local_sp_stack.append(len(self.local_sp_stack))
paul@145 592
        self.handler_pc_stack.append(len(self.pc_stack))
paul@129 593
paul@129 594
    def PopHandler(self):
paul@145 595
        # Reduce the local frame pointer stack to refer to the handler's frame.
paul@145 596
        self.local_sp_stack = self.local_sp_stack[:self.handler_local_sp_stack.pop()]
paul@145 597
        # Reduce the PC stack to discard all superfluous return addresses.
paul@145 598
        self.pc_stack = self.pc_stack[:self.handler_pc_stack.pop()]
paul@129 599
        self.handler_stack.pop()
paul@129 600
paul@129 601
    def CheckException(self):
paul@181 602
        self.status = self.exception is not None and self._CheckInstance(self.exception, self.value[1])
paul@129 603
paul@129 604
    def TestIdentity(self):
paul@145 605
        self.status = self.value[1] == self.source[1]
paul@129 606
paul@129 607
    def TestIdentityAddress(self):
paul@129 608
        self.status = self.value[1] == self.operand
paul@129 609
paul@129 610
    # LoadBoolean is implemented in the generated code.
paul@129 611
    # StoreBoolean is implemented by testing against the True value.
paul@68 612
paul@132 613
    def InvertBoolean(self):
paul@132 614
        self.status = not self.status
paul@132 615
paul@145 616
    # Common implementation details.
paul@145 617
paul@145 618
    def _CheckInstance(self, ref, cls):
paul@182 619
        data = self.load(ref)
paul@182 620
        target_data = self.load(cls)
paul@145 621
paul@191 622
        # Insist on a class.
paul@191 623
paul@207 624
        if target_data.attrcode is not None: # present attrcode == instance
paul@191 625
            return 0
paul@191 626
paul@145 627
        # Find the table entry for the descendant.
paul@145 628
paul@182 629
        element = self.objlist[target_data.classcode + data.attrcode]
paul@194 630
        attr_index, class_attr, offset = element
paul@182 631
        return attr_index == data.attrcode
paul@181 632
paul@181 633
    def _MakeObject(self, size, ref):
paul@203 634
        # Load the template.
paul@182 635
        data = self.load(ref)
paul@181 636
        addr = self.new(size)
paul@203 637
        # Save the header, overriding the size.
paul@203 638
        self.save(addr, data.with_size(size))
paul@181 639
        return addr
paul@181 640
paul@194 641
    def _LoadAddressContextCond(self, context, ref, inst_context, inst_ref):
paul@191 642
paul@191 643
        # Check the instance context against the target's context.
paul@191 644
paul@191 645
        if self._CheckInstance(inst_ref, context):
paul@191 646
            # Replace the context with the instance.
paul@191 647
            return inst_ref, ref
paul@191 648
        else:
paul@191 649
            return context, ref
paul@191 650
paul@181 651
    # Native function implementations.
paul@181 652
paul@181 653
    def builtins_int_add(self):
paul@181 654
        frame = self.local_sp_stack[-1]
paul@181 655
paul@181 656
        # Get operands addresses.
paul@181 657
paul@181 658
        left_context, left = self.frame_stack[frame]
paul@181 659
        right_context, right = self.frame_stack[frame + 1]
paul@181 660
paul@181 661
        # Test operand suitability.
paul@181 662
paul@196 663
        if not self._CheckInstance(left, self.int_class_location) and self._CheckInstance(right, self.int_class_location):
paul@181 664
            self.exception = self.type_error
paul@181 665
            return self.RaiseException()
paul@181 666
paul@181 667
        # NOTE: Assume single location for data.
paul@181 668
paul@181 669
        left_data = left + 1
paul@181 670
        right_data = right + 1
paul@181 671
paul@181 672
        # Make a new object.
paul@181 673
paul@184 674
        addr = self._MakeObject(2, self.int_instance_location)
paul@196 675
paul@196 676
        # Store the result.
paul@196 677
        # NOTE: The data is considered ready to use.
paul@196 678
paul@181 679
        self.save(addr + 1, self.load(left_data) + self.load(right_data))
paul@181 680
paul@187 681
        # Return the new object.
paul@187 682
        # Introduce object as context for the new object.
paul@181 683
paul@187 684
        self.result = addr, addr
paul@181 685
paul@196 686
    def builtins_int_bool(self):
paul@196 687
        frame = self.local_sp_stack[-1]
paul@196 688
paul@196 689
        # Get operands addresses.
paul@196 690
paul@196 691
        left_context, left = self.frame_stack[frame]
paul@196 692
paul@196 693
        # Test operand suitability.
paul@196 694
paul@196 695
        if not self._CheckInstance(left, self.int_class_location):
paul@196 696
            self.exception = self.type_error
paul@196 697
            return self.RaiseException()
paul@196 698
paul@196 699
        # NOTE: Assume single location for data.
paul@196 700
paul@196 701
        left_data = left + 1
paul@196 702
paul@196 703
        # Test the data.
paul@196 704
        # NOTE: The data is considered ready to use.
paul@196 705
paul@196 706
        if self.load(left_data) != 0:
paul@196 707
            self.result = self.true_constant, self.true_constant
paul@196 708
        else:
paul@196 709
            self.result = self.false_constant, self.false_constant
paul@196 710
paul@216 711
    def builtins_int_neg(self):
paul@216 712
        frame = self.local_sp_stack[-1]
paul@216 713
paul@216 714
        # Get operands addresses.
paul@216 715
paul@216 716
        left_context, left = self.frame_stack[frame]
paul@216 717
paul@216 718
        # Test operand suitability.
paul@216 719
paul@216 720
        if not self._CheckInstance(left, self.int_class_location):
paul@216 721
            self.exception = self.type_error
paul@216 722
            return self.RaiseException()
paul@216 723
paul@216 724
        # NOTE: Assume single location for data.
paul@216 725
paul@216 726
        left_data = left + 1
paul@216 727
paul@216 728
        # Make a new object.
paul@216 729
paul@216 730
        addr = self._MakeObject(2, self.int_instance_location)
paul@216 731
paul@216 732
        # Store the result.
paul@216 733
        # NOTE: The data is considered ready to use.
paul@216 734
paul@216 735
        self.save(addr + 1, -self.load(left_data))
paul@216 736
paul@216 737
        # Return the new object.
paul@216 738
        # Introduce object as context for the new object.
paul@216 739
paul@216 740
        self.result = addr, addr
paul@216 741
paul@197 742
    def builtins_bool_bool(self):
paul@197 743
        frame = self.local_sp_stack[-1]
paul@197 744
paul@197 745
        # Get operands addresses.
paul@197 746
paul@197 747
        left_context, left = self.frame_stack[frame]
paul@197 748
        self.result = left, left
paul@197 749
paul@203 750
    def builtins_list_new(self):
paul@203 751
        frame = self.local_sp_stack[-1]
paul@203 752
paul@203 753
        # NOTE: Specific copying of tuples/lists.
paul@203 754
paul@203 755
        args_context, args = self.frame_stack[frame]
paul@203 756
        header = self.load(args)
paul@203 757
paul@203 758
        list = self._MakeObject(header.size, self.list_instance_location)
paul@203 759
        for i in range(1, header.size):
paul@203 760
            self.save(list + i, self.load(args + i))
paul@203 761
paul@203 762
        self.result = list, list
paul@203 763
paul@216 764
    def builtins_list_getitem(self):
paul@216 765
        frame = self.local_sp_stack[-1]
paul@216 766
paul@216 767
        # Get operands addresses.
paul@216 768
paul@216 769
        obj_context, obj = self.frame_stack[frame]
paul@216 770
        item_context, item = self.frame_stack[frame + 1]
paul@216 771
paul@216 772
        header = self.load(obj)
paul@216 773
        nelements = header.size - 1
paul@216 774
paul@216 775
        # NOTE: Assume single location for data.
paul@216 776
paul@216 777
        item_pos = self.load(item + 1)
paul@216 778
        if item_pos >= 0 and item_pos < nelements:
paul@216 779
            pass
paul@216 780
        elif item_pos < 0 and item_pos >= -nelements:
paul@216 781
            item_pos = nelements + item_pos
paul@216 782
        else:
paul@216 783
            self.exception = self.index_error
paul@216 784
            return self.RaiseException()
paul@216 785
paul@216 786
        self.result = self.load(obj + 1 + item_pos)
paul@216 787
paul@208 788
    def builtins_object_init(self):
paul@208 789
        pass
paul@208 790
paul@181 791
    native_functions = {
paul@181 792
        "__builtins__.int.__add__" : builtins_int_add,
paul@196 793
        "__builtins__.int.__bool__" : builtins_int_bool,
paul@216 794
        "__builtins__.int.__neg__" : builtins_int_neg,
paul@197 795
        "__builtins__.bool.__bool__" : builtins_bool_bool,
paul@203 796
        "__builtins__.list" : builtins_list_new,
paul@216 797
        "__builtins__.list.__getitem__" : builtins_list_getitem,
paul@208 798
        "__builtins__.object.__init__" : builtins_object_init,
paul@181 799
        }
paul@145 800
paul@68 801
# vim: tabstop=4 expandtab shiftwidth=4