git @ Cat's Eye Technologies SixtyPical / master src / sixtypical / model.py
master

Tree @master (Download .tar.gz)

model.py @masterraw · history · blame

# Copyright (c) 2014-2024, Chris Pressey, Cat's Eye Technologies.
# This file is distributed under a 2-clause BSD license.  See LICENSES/ dir.
# SPDX-License-Identifier: LicenseRef-BSD-2-Clause-X-SixtyPical

"""Data/storage model for SixtyPical."""

from collections import namedtuple


class BitType(namedtuple('BitType', ['typename'])):
    max_range = (0, 1)
    def __new__(cls):
        return super(BitType, cls).__new__(cls, 'bit')
TYPE_BIT = BitType()


class ByteType(namedtuple('ByteType', ['typename'])):
    max_range = (0, 255)
    def __new__(cls):
        return super(ByteType, cls).__new__(cls, 'byte')
TYPE_BYTE = ByteType()


class WordType(namedtuple('WordType', ['typename'])):
    max_range = (0, 65535)
    def __new__(cls):
        return super(WordType, cls).__new__(cls, 'word')
TYPE_WORD = WordType()


class RoutineType(namedtuple('RoutineType', ['typename', 'inputs', 'outputs', 'trashes'])):
    """This memory location contains the code for a routine."""
    max_range = (0, 0)

    def __new__(cls, *args):
        return super(RoutineType, cls).__new__(cls, 'routine', *args)

    @classmethod
    def executable_types_compatible(cls_, src, dest):
        """Returns True iff a value of type `src` can be assigned to a storage location of type `dest`."""
        if isinstance(src, VectorType):
            src = src.of_type
        if isinstance(dest, VectorType):
            dest = dest.of_type
        if isinstance(src, RoutineType) and isinstance(dest, RoutineType):
            # TODO: I'm sure we can replace some of these with subset-containment, but that requires thought
            return (
                src.inputs == dest.inputs and
                src.outputs == dest.outputs and
                src.trashes == dest.trashes
            )
        else:
            return False


class VectorType(namedtuple('VectorType', ['typename', 'of_type'])):
    """This memory location contains the address of some other type (currently, only RoutineType)."""
    max_range = (0, 65535)

    def __new__(cls, *args):
        return super(VectorType, cls).__new__(cls, 'vector', *args)


class TableType(namedtuple('TableType', ['typename', 'of_type', 'size'])):

    def __new__(cls, *args):
        return super(TableType, cls).__new__(cls, 'table', *args)

    @property
    def max_range(self):
        return (0, self.size - 1)

    @classmethod
    def is_a_table_type(cls_, x, of_type):
        return isinstance(x, TableType) and x.of_type == of_type


class PointerType(namedtuple('PointerType', ['typename'])):
    max_range = (0, 65535)

    def __new__(cls):
        return super(PointerType, cls).__new__(cls, 'pointer')


# --------------------------------------------------------


class LocationRef(namedtuple('LocationRef', ['reftype', 'name'])):
    def __new__(cls, *args):
        return super(LocationRef, cls).__new__(cls, 'location', *args)

    @classmethod
    def format_set(cls, location_refs):
        return '{%s}' % ', '.join([str(loc) for loc in sorted(location_refs, key=lambda x: x.name)])


class IndirectRef(namedtuple('IndirectRef', ['reftype', 'ref'])):
    def __new__(cls, *args):
        return super(IndirectRef, cls).__new__(cls, 'indirect', *args)

    @property
    def name(self):
        return '[{}]+y'.format(self.ref.name)


class IndexedRef(namedtuple('IndexedRef', ['reftype', 'ref', 'offset', 'index'])):
    def __new__(cls, *args):
        return super(IndexedRef, cls).__new__(cls, 'indexed', *args)

    @property
    def name(self):
        return '{}+{}+{}'.format(self.ref.name, self.offset, self.index.name)


class ConstantRef(namedtuple('ConstantRef', ['reftype', 'type', 'value'])):
    def __new__(cls, *args):
        return super(ConstantRef, cls).__new__(cls, 'constant', *args)

    def high_byte(self):
        return (self.value >> 8) & 255

    def low_byte(self):
        return self.value & 255

    def pred(self):
        assert self.type == TYPE_BYTE
        value = self.value - 1
        while value < 0:
            value += 256
        return ConstantRef(self.type, value)

    def succ(self):
        assert self.type == TYPE_BYTE
        value = self.value + 1
        while value > 255:
            value -= 256
        return ConstantRef(self.type, value)

    @property
    def name(self):
        return 'constant({})'.format(self.value)


REG_A = LocationRef('a')
REG_X = LocationRef('x')
REG_Y = LocationRef('y')

FLAG_Z = LocationRef('z')
FLAG_C = LocationRef('c')
FLAG_N = LocationRef('n')
FLAG_V = LocationRef('v')