Previously, it was hardcoded at 'compile' time (in as much Python has that). This would make it more difficult to add stuff to it. Also, in a lot of places we made assumptions about prelude instead of checking properly.
291 lines
8.3 KiB
Python
291 lines
8.3 KiB
Python
"""
|
|
Contains the final types for use in Phasm, as well as construtors.
|
|
"""
|
|
from typing import (
|
|
Any,
|
|
Hashable,
|
|
Self,
|
|
Tuple,
|
|
TypeVar,
|
|
)
|
|
|
|
S = TypeVar('S')
|
|
T = TypeVar('T')
|
|
|
|
class KindArgument:
|
|
pass
|
|
|
|
class TypeApplication_Base[T: Hashable, S: Hashable]:
|
|
"""
|
|
Records the constructor and arguments used to create this type.
|
|
|
|
Nullary types, or types of kind *, have both arguments set to None.
|
|
"""
|
|
constructor: T
|
|
arguments: S
|
|
|
|
def __init__(self, constructor: T, arguments: S) -> None:
|
|
self.constructor = constructor
|
|
self.arguments = arguments
|
|
|
|
def __hash__(self) -> int:
|
|
return hash((self.constructor, self.arguments, ))
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
if not isinstance(other, TypeApplication_Base):
|
|
raise NotImplementedError
|
|
|
|
return (self.constructor == other.constructor # type: ignore[no-any-return]
|
|
and self.arguments == other.arguments)
|
|
|
|
def __repr__(self) -> str:
|
|
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
|
|
|
class Type3(KindArgument):
|
|
"""
|
|
Base class for the type3 types
|
|
|
|
(Having a separate name makes it easier to distinguish from
|
|
Python's Type)
|
|
"""
|
|
__slots__ = ('name', 'application', )
|
|
|
|
name: str
|
|
"""
|
|
The name of the string, as parsed and outputted by codestyle.
|
|
"""
|
|
|
|
application: TypeApplication_Base[Any, Any]
|
|
"""
|
|
How the type was constructed; i.e. which constructor was used and which
|
|
type level arguments were applied to the constructor.
|
|
"""
|
|
|
|
def __init__(self, name: str, application: TypeApplication_Base[Any, Any]) -> None:
|
|
self.name = name
|
|
self.application = application
|
|
|
|
def __repr__(self) -> str:
|
|
return f'Type3({self.name!r}, {self.application!r})'
|
|
|
|
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(other)
|
|
|
|
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 TypeApplication_Nullary(TypeApplication_Base[None, None]):
|
|
"""
|
|
There was no constructor used to create this type - it's a 'simple' type like u32
|
|
"""
|
|
|
|
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 __repr__(self) -> str:
|
|
return f'IntType3({self.value!r})'
|
|
|
|
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)
|
|
|
|
class TypeConstructor_Base[T]:
|
|
"""
|
|
Base class for type construtors
|
|
"""
|
|
__slots__ = ('name', '_cache', )
|
|
|
|
name: str
|
|
"""
|
|
The name of the type constructor
|
|
"""
|
|
|
|
_cache: dict[T, Type3]
|
|
"""
|
|
When constructing a type with the same arguments,
|
|
it should produce the exact same result.
|
|
"""
|
|
|
|
def __init__(self, name: str) -> None:
|
|
self.name = name
|
|
|
|
self._cache = {}
|
|
|
|
def make_name(self, key: T) -> str:
|
|
"""
|
|
Renders the type's name based on the given arguments
|
|
"""
|
|
raise NotImplementedError('make_name', self)
|
|
|
|
def make_application(self, key: T) -> TypeApplication_Base[Self, T]:
|
|
"""
|
|
Records how the type was constructed into type.
|
|
|
|
The type checker and compiler will need to know what
|
|
arguments where made to construct the type.
|
|
"""
|
|
raise NotImplementedError('make_application', self)
|
|
|
|
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.make_application(key))
|
|
|
|
return result
|
|
|
|
def __repr__(self) -> str:
|
|
return f'{self.__class__.__name__}({self.name!r}, ...)'
|
|
|
|
class TypeConstructor_Type(TypeConstructor_Base[Tuple[Type3]]):
|
|
"""
|
|
Base class type constructors of kind: * -> *
|
|
|
|
Notably, static array.
|
|
"""
|
|
__slots__ = ()
|
|
|
|
def make_application(self, key: Tuple[Type3]) -> 'TypeApplication_Type':
|
|
return TypeApplication_Type(self, key)
|
|
|
|
def make_name(self, key: Tuple[Type3]) -> str:
|
|
return f'{self.name} {key[0].name} '
|
|
|
|
def __call__(self, arg0: Type3) -> Type3:
|
|
return self.construct((arg0, ))
|
|
|
|
class TypeApplication_Type(TypeApplication_Base[TypeConstructor_Type, Tuple[Type3]]):
|
|
pass
|
|
|
|
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
|
|
"""
|
|
Base class type constructors of kind: * -> Int -> *
|
|
|
|
Notably, static array.
|
|
"""
|
|
__slots__ = ()
|
|
|
|
def make_application(self, key: Tuple[Type3, IntType3]) -> 'TypeApplication_TypeInt':
|
|
return TypeApplication_TypeInt(self, key)
|
|
|
|
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 TypeApplication_TypeInt(TypeApplication_Base[TypeConstructor_TypeInt, Tuple[Type3, IntType3]]):
|
|
pass
|
|
|
|
class TypeConstructor_TypeStar(TypeConstructor_Base[Tuple[Type3, ...]]):
|
|
"""
|
|
Base class type constructors of variadic kind
|
|
|
|
Notably, tuple.
|
|
"""
|
|
def make_application(self, key: Tuple[Type3, ...]) -> 'TypeApplication_TypeStar':
|
|
return TypeApplication_TypeStar(self, key)
|
|
|
|
def __call__(self, *args: Type3) -> Type3:
|
|
key: Tuple[Type3, ...] = tuple(args)
|
|
return self.construct(key)
|
|
|
|
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
|
|
pass
|
|
|
|
class TypeConstructor_DynamicArray(TypeConstructor_Type):
|
|
def make_name(self, key: Tuple[Type3]) -> str:
|
|
if 'u8' == key[0].name:
|
|
return 'bytes'
|
|
|
|
return f'{key[0].name}[...]'
|
|
|
|
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_Function(TypeConstructor_TypeStar):
|
|
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
|
return 'Callable[' + ', '.join(x.name for x in key) + ']'
|
|
|
|
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
|
"""
|
|
Constructs struct types
|
|
"""
|
|
def make_application(self, key: tuple[tuple[str, Type3], ...]) -> 'TypeApplication_Struct':
|
|
return TypeApplication_Struct(self, key)
|
|
|
|
def make_name(self, key: tuple[tuple[str, Type3], ...]) -> str:
|
|
return f'{self.name}(' + ', '.join(
|
|
f'{n}: {t.name}'
|
|
for n, t in key
|
|
) + ')'
|
|
|
|
def construct(self, key: T) -> Type3:
|
|
"""
|
|
Constructs the type by applying the given arguments to this
|
|
constructor.
|
|
"""
|
|
raise Exception('This does not work with the caching system')
|
|
|
|
def __call__(self, name: str, args: tuple[tuple[str, Type3], ...]) -> Type3:
|
|
result = Type3(name, self.make_application(args))
|
|
return result
|
|
|
|
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
|
pass
|