phasm/phasm/type3/types.py
Johan B.W. de Vries faaf7912b1 Various cleanup to type system
- Make struct into a type constuctor
- Rework placeholders
- Got rid of 'PrimitiveType' as a concept
- Moved out the prelude to its own folder
2025-04-21 16:49:04 +02:00

278 lines
7.2 KiB
Python

"""
Contains the final types for use in Phasm, as well as construtors.
"""
from typing import (
TYPE_CHECKING,
Any,
Generic,
Iterable,
Set,
Tuple,
TypeVar,
)
if TYPE_CHECKING:
from .typeclasses import Type3Class
class KindArgument:
pass
class Type3(KindArgument):
"""
Base class for the type3 types
(Having a separate name makes it easier to distinguish from
Python's Type)
"""
__slots__ = ('name', 'classes', )
name: str
"""
The name of the string, as parsed and outputted by codestyle.
"""
classes: Set['Type3Class']
"""
The type classes that this type implements
"""
def __init__(self, name: str, classes: Iterable['Type3Class']) -> None:
self.name = name
self.classes = set(classes)
for cls in self.classes:
for inh_cls in cls.inherited_classes:
if inh_cls not in self.classes:
raise Exception(
f'No instance for ({inh_cls} {self.name})'
f'; required for ({cls} {self.name})'
)
def __repr__(self) -> str:
return f'Type3({repr(self.name)}, {repr(self.classes)})'
def __str__(self) -> str:
return self.name
def __format__(self, format_spec: str) -> str:
if format_spec != 's':
raise TypeError(f'unsupported format string passed to Type3.__format__: {format_spec}')
return str(self)
def __eq__(self, other: Any) -> bool:
if not isinstance(other, Type3):
raise NotImplementedError
return self is other
def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)
def __hash__(self) -> int:
return hash(self.name)
def __bool__(self) -> bool:
raise NotImplementedError
class IntType3(KindArgument):
"""
Sometimes you can have an int on the type level, e.g. when using static arrays
This is not the same as an int on the language level.
[1.0, 1.2] :: f32[2] :: * -> Int -> *
That is to say, you can create a static array of size two with each element
a f32 using f32[2].
"""
__slots__ = ('value', )
value: int
def __init__(self, value: int) -> None:
self.value = value
def __format__(self, format_spec: str) -> str:
if format_spec != 's':
raise TypeError(f'unsupported format string passed to Type3.__format__: {format_spec}')
return str(self.value)
def __eq__(self, other: Any) -> bool:
if isinstance(other, IntType3):
return self.value == other.value
if isinstance(other, KindArgument):
return False
raise NotImplementedError
def __hash__(self) -> int:
return hash(self.value)
T = TypeVar('T')
class TypeConstructor(Generic[T]):
"""
Base class for type construtors
"""
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
name: str
"""
The name of the type constructor
"""
classes: Set['Type3Class']
"""
The type classes that this constructor implements
"""
type_classes: Set['Type3Class']
"""
The type classes that the constructed types implement
"""
_cache: dict[T, Type3]
"""
When constructing a type with the same arguments,
it should produce the exact same result.
"""
_reverse_cache: dict[Type3, T]
"""
Sometimes we need to know the key that created a type.
"""
def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None:
self.name = name
self.classes = set(classes)
self.type_classes = set(type_classes)
self._cache = {}
self._reverse_cache = {}
def make_name(self, key: T) -> str:
"""
Renders the type's name based on the given arguments
"""
raise NotImplementedError
def did_construct(self, typ: Type3) -> T | None:
"""
Was the given type constructed by this constructor?
If so, which arguments where used?
"""
return self._reverse_cache.get(typ)
def construct(self, key: T) -> Type3:
"""
Constructs the type by applying the given arguments to this
constructor.
"""
result = self._cache.get(key, None)
if result is None:
self._cache[key] = result = Type3(self.make_name(key), self.type_classes)
self._reverse_cache[result] = key
return result
class TypeConstructor_Type(TypeConstructor[Type3]):
"""
Base class type constructors of kind: * -> *
"""
__slots__ = ()
def __call__(self, arg: Type3) -> Type3:
raise NotImplementedError
class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
"""
Base class type constructors of kind: * -> Int -> *
Notably, static array.
"""
__slots__ = ()
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
return f'{self.name} {key[0].name} {key[1].value}'
def __call__(self, arg0: Type3, arg1: IntType3) -> Type3:
return self.construct((arg0, arg1))
class TypeConstructor_TypeStar(TypeConstructor[Tuple[Type3, ...]]):
"""
Base class type constructors of variadic kind
Notably, tuple.
"""
def __call__(self, *args: Type3) -> Type3:
key: Tuple[Type3, ...] = tuple(args)
return self.construct(key)
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
return f'{key[0].name}[{key[1].value}]'
class TypeConstructor_Tuple(TypeConstructor_TypeStar):
def make_name(self, key: Tuple[Type3, ...]) -> str:
return '(' + ', '.join(x.name for x in key) + ', )'
class TypeConstructor_Struct:
"""
Base class for type construtors
"""
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
name: str
"""
The name of the type constructor
"""
classes: Set['Type3Class']
"""
The type classes that this constructor implements
"""
type_classes: Set['Type3Class']
"""
The type classes that the constructed types implement
"""
_cache: dict[str, Type3]
"""
When constructing a type with the same arguments,
it should produce the exact same result.
"""
_reverse_cache: dict[Type3, dict[str, Type3]]
"""
After construction you may need to look up the arguments
used for making the type
"""
def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None:
self.name = name
self.classes = set(classes)
self.type_classes = set(type_classes)
self._cache = {}
self._reverse_cache = {}
def did_construct(self, typ: Type3) -> dict[str, Type3] | None:
"""
Was the given type constructed by this constructor?
If so, which arguments where used?
"""
return self._reverse_cache.get(typ)
def __call__(self, name: str, args: dict[str, Type3], classes: Set['Type3Class']) -> Type3:
result = Type3(name, classes | self.type_classes)
self._reverse_cache[result] = args
return result