micropython

Annotated rsvp.py

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