Snippets

Created by Василий Шередеко
import abc
import pytest
from typing import Sequence


class Context:
    def __init__(self):
        self.function_type = None
        self.array_type = None
        self.boolean_type = None
        self.void_type = None

        self.void_type = VoidType(self, location='builtin.orx')
        self.boolean_type = BooleanType(self, location='builtin.orx')

        func_args = VariadicType(self, 'Args', location="builtin.orx")
        func_r = GenericType(self, 'R', location="builtin.orx")
        self.function_type = FunctionType(self, [func_args], func_r, generic_parameters=[func_args, func_r],
                                          location='builtin.orx')

        array_t = GenericType(self, 'T', location="builtin.orx")
        self.array_type = ArrayType(self, array_t, generic_parameters=[array_t], location="builtin.orx")


class Type(metaclass=abc.ABCMeta):
    def __init__(self, context: Context, *, name: str, location):
        self.__context = context
        self.__name = name
        self.__location = location

        self.generator = None
        self.generic_parameters = []
        self.generic_arguments = []

    @property
    def context(self) -> Context:
        return self.__context

    @property
    def name(self) -> str:
        return self.__name

    @property
    def generic_name(self) -> str:
        if self.generic_arguments:
            arguments = ", ".join(map(str, (arg for arg in self.generic_arguments)))
        elif self.generic_parameters:
            arguments = ", ".join(map(str, (arg for arg in self.generic_parameters)))
        else:
            arguments = None

        if arguments:
            return f"{self.name}[{arguments}]"
        return self.name

    @property
    def location(self):
        return self.__location

    @property
    def is_generic(self) -> bool:
        if self.generic_parameters:
            return True
        if any(arg.is_generic for arg in self.generic_arguments):
            return True
        return False

    def __str__(self):
        return self.generic_name


class GenericParameter:
    @property
    def is_generic(self) -> bool:
        return True


class GenericType(GenericParameter, Type):
    def __init__(self, context: Context, name, *, location):
        super().__init__(context, name=name, location=location)


class VariadicType(GenericParameter, Type):
    def __init__(self, context: Context, name, *, location):
        super().__init__(context, name=name, location=location)

    def __str__(self):
        return f"{self.name}..."


class LazyMetaclass(abc.ABCMeta):
    def __new__(metacls, name, bases, namespace, *, storage):
        cls = super().__new__(metacls, name, bases, namespace)
        cls.__lazy_storage = storage
        return cls

    def __init__(cls, name, bases, namespace, *, storage):
        super().__init__(name, bases, namespace)

    def __call__(cls, context: Context, *args, **kwargs):
        storage_name = cls.__lazy_storage

        storage_type = getattr(context, storage_name, None)
        if storage_type:
            return storage_type

        storage_type = type.__call__(cls, context, *args, **kwargs)
        setattr(context, storage_name, storage_type)
        return storage_type


class GenericMetaclass(abc.ABCMeta):
    def __new__(metacls, name, bases, namespace, *, storage):
        cls = super().__new__(metacls, name, bases, namespace)
        cls.__lazy_storage = storage
        return cls

    def __init__(cls, name, bases, namespace, *, storage):
        super().__init__(name, bases, namespace)

    def __call__(cls, context: Context, *args, generator=None, generic_parameters=None, generic_arguments=None,
                 **kwargs):
        storage_name = cls.__lazy_storage
        generic_type = getattr(context, storage_name, None)

        if not args and not kwargs:
            return generic_type
        if generic_type and not generator:
            return generic_type.instantiate(*args, **kwargs)

        function_type = type.__call__(cls, context, *args, **kwargs)
        function_type.generator = generator
        function_type.generic_parameters = generic_parameters
        function_type.generic_arguments = generic_arguments
        return function_type


class BooleanType(Type, metaclass=LazyMetaclass, storage='boolean_type'):
    def __init__(self, context: Context, *, location=None):
        super().__init__(context, name="Boolean", location=location)

    def __str__(self):
        return "bool"


class VoidType(Type, metaclass=LazyMetaclass, storage='void_type'):
    def __init__(self, context: Context, *, location=None):
        super().__init__(context, name="Void", location=location)

    def __str__(self):
        return "void"


class FunctionType(Type, metaclass=GenericMetaclass, storage='function_type'):
    def __init__(self, context: Context, parameters: Sequence[Type] = None, result_type: Type = None, *, location=None):
        assert result_type, "Function type is required result type"
        assert parameters, "Function type is required parameters"

        super().__init__(context, name="Function", location=location)

        self.parameters = tuple(parameters)
        self.result_type = result_type

    def __str__(self):
        parameters = ", ".join(str(param) for param in self.parameters)
        return f"({parameters}) -> {self.result_type}"

    def instantiate(self, parameters, result_type):
        kwargs = dict(
            location=self.location,
            generator=self,
            generic_arguments=parameters + [result_type]
        )

        return FunctionType(self.context, parameters, result_type, **kwargs)


class ArrayType(Type, metaclass=GenericMetaclass, storage='array_type'):
    def __init__(self, context: Context, element_type: Type = None, *, location=None):
        assert element_type, "Function type is required element type"

        super().__init__(context, name="Array", location=location)

        self.element_type = element_type

    def __str__(self):
        return f"{self.element_type}*"

    def instantiate(self, element_type):
        kwargs = dict(
            location=self.location,
            generator=self,
            generic_arguments=[element_type]
        )
        return ArrayType(self.context, element_type, **kwargs)


def test_aaa():
    context = Context()

    function_generic = FunctionType(context)
    function_type = FunctionType(context, [BooleanType(context), BooleanType(context)], VoidType(context))

    array_generic = ArrayType(context)
    array_type = ArrayType(context, BooleanType(context))

    assert FunctionType(context) is FunctionType(context)
    assert BooleanType(context) is BooleanType(context)
    assert VoidType(context) is VoidType(context)

    assert context.function_type == function_generic
    assert function_generic != function_type

    assert context.array_type == array_generic
    assert array_generic != array_type

    assert function_generic.generator is None
    assert function_generic.is_generic
    assert not function_type.is_generic
    assert function_type.generator == function_generic

    assert array_generic.generator is None
    assert array_generic.is_generic
    assert not array_type.is_generic
    assert array_type.generator == array_generic

    def dump(type: Type):
        print("----------------------------")
        print(f"type: {type}")
        print(f"name: {type.name}")
        print(f"generator: {type.generator}")
        print(f"is generic: {type.is_generic}")
        print(f"generic name: {type.generic_name}")

    dump(VoidType(context))
    dump(BooleanType(context))
    dump(FunctionType(context))
    dump(ArrayType(context))
    dump(function_type)
    dump(array_type)


if __name__ == '__main__':
    pytest.main()

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.