249 lines
6.2 KiB
Python
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
|