phasm/phasm/type3/types.py
Johan B.W. de Vries 8a027a0b10 Implements sum for Foldable types
Foldable take a TypeConstructor. The first argument must be a
NatNum.
2025-05-05 14:09:38 +02:00

249 lines
6.2 KiB
Python

"""
Contains the final types for use in Phasm, as well as construtors.
"""
from typing import (
Any,
Callable,
Generic,
Tuple,
TypeVar,
)
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', )
name: str
"""
The name of the string, as parsed and outputted by codestyle.
"""
def __init__(self, name: str) -> None:
self.name = name
def __repr__(self) -> str:
return f'Type3({repr(self.name)})'
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', 'on_create', '_cache', '_reverse_cache')
name: str
"""
The name of the type constructor
"""
on_create: Callable[[T, Type3], None]
"""
Who to let know if a type is created
"""
_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, on_create: Callable[[T, Type3], None]) -> None:
self.name = name
self.on_create = on_create
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._reverse_cache[result] = key
self.on_create(key, result)
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', 'on_create', '_cache', '_reverse_cache')
name: str
"""
The name of the type constructor
"""
on_create: Callable[[Type3], None]
"""
Who to let know if a type is created
"""
_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, on_create: Callable[[Type3], None]) -> None:
self.name = name
self.on_create = on_create
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]) -> Type3:
result = Type3(name)
self._reverse_cache[result] = args
self.on_create(result)
return result