phasm/phasm/type3/types.py
Johan B.W. de Vries f6b4f7c20a First attempts
2025-07-12 17:28:52 +02:00

351 lines
10 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
"""
def make_type(name: str) -> Type3:
"""
Make a type of kind *
"""
return Type3(name, TypeApplication_Nullary(None, None))
class TypeApplication_Variable(TypeApplication_Base[None, None]):
"""
We don't know what the actual type is, it's to be determined.
"""
def make_type_variable(name: str) -> Type3:
"""
Make a type variable
Type variables always have kind *. But you can construct a type
from a type constructor using a type variable.
"""
return Type3(name, TypeApplication_Variable(None, None))
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
# head :: [a] -> a
# (+) :: NatNum a => a -> a -> a
# instancen NatNum f32
# (+) :: wasm_f32_add
# inc1 :: NatNum a => a -> a
# inc1 = (+) 1
# data Type3
# | Type3KindS String None
# | Type3KindS_S String Type3
# | Type3KindS_S_S String Type3 Type3
# data TypeVariable3
# | TypeVariable3KindS String None
# | TypeVariable3KindS_S String Type3
# | TypeVariable3KindS_S_S String Type3 Type3
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) + ']'
def expect_function_type(typ: Type3) -> list[Type3]:
"""
Checks for a function type and returns its argument types.
Raises an AssertionError if typ is not a function type.
"""
assert isinstance(typ.application, TypeApplication_TypeStar)
assert isinstance(typ.application.constructor, TypeConstructor_Function)
return list(typ.application.arguments)
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
def expect_struct_type(typ: Type3) -> list[tuple[str, Type3]]:
"""
Checks for a struct type and returns its argument types.
Raises an AssertionError if typ is not a struct type.
"""
assert isinstance(typ.application, TypeApplication_Struct)
assert isinstance(typ.application.constructor, TypeConstructor_Struct)
return list(typ.application.arguments)