414 lines
10 KiB
Python
414 lines
10 KiB
Python
"""
|
|
Contains the final types for use in Phasm
|
|
|
|
These are actual, instantiated types; not the abstract types that the
|
|
constraint generator works with.
|
|
"""
|
|
from typing import (
|
|
Any,
|
|
Dict,
|
|
Generic,
|
|
Iterable,
|
|
List,
|
|
Optional,
|
|
Protocol,
|
|
Set,
|
|
Tuple,
|
|
TypeVar,
|
|
Union,
|
|
)
|
|
|
|
from .typeclasses import (
|
|
Bits,
|
|
Eq,
|
|
Floating,
|
|
Fractional,
|
|
Integral,
|
|
InternalPassAsPointer,
|
|
IntNum,
|
|
NatNum,
|
|
Ord,
|
|
Type3Class,
|
|
)
|
|
|
|
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
|
|
|
|
class ExpressionProtocol(Protocol):
|
|
"""
|
|
A protocol for classes that should be updated on substitution
|
|
"""
|
|
|
|
type3: 'Type3OrPlaceholder'
|
|
"""
|
|
The type to update
|
|
"""
|
|
|
|
class Type3:
|
|
"""
|
|
Base class for the type3 types
|
|
"""
|
|
__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 isinstance(other, PlaceholderForType):
|
|
return False
|
|
|
|
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 PrimitiveType3(Type3):
|
|
"""
|
|
Intermediate class to tell primitive types from others
|
|
"""
|
|
|
|
__slots__ = ()
|
|
|
|
class IntType3(Type3):
|
|
"""
|
|
Sometimes you can have an int as type, e.g. when using static arrays
|
|
|
|
This is not the same as an int on the language level.
|
|
[1.0, 1.2] :: Float[2] :: * -> Int -> *
|
|
"""
|
|
|
|
__slots__ = ('value', )
|
|
|
|
value: int
|
|
|
|
def __init__(self, value: int) -> None:
|
|
super().__init__(str(value), [])
|
|
|
|
assert 0 <= value
|
|
self.value = value
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
if isinstance(other, IntType3):
|
|
return self.value == other.value
|
|
|
|
if isinstance(other, Type3):
|
|
return False
|
|
|
|
raise NotImplementedError
|
|
|
|
def __hash__(self) -> int:
|
|
return hash(self.value)
|
|
|
|
class PlaceholderForType:
|
|
"""
|
|
A placeholder type, for when we don't know the final type yet
|
|
"""
|
|
__slots__ = ('update_on_substitution', 'resolve_as', )
|
|
|
|
update_on_substitution: List[ExpressionProtocol]
|
|
resolve_as: Optional[Type3]
|
|
|
|
def __init__(self, update_on_substitution: Iterable[ExpressionProtocol]) -> None:
|
|
self.update_on_substitution = [*update_on_substitution]
|
|
self.resolve_as = None
|
|
|
|
def __repr__(self) -> str:
|
|
uos = ', '.join(repr(x) for x in self.update_on_substitution)
|
|
|
|
return f'PlaceholderForType({id(self)}, [{uos}])'
|
|
|
|
def __str__(self) -> str:
|
|
return f'PhFT_{id(self)}'
|
|
|
|
def __format__(self, format_spec: str) -> str:
|
|
if format_spec != 's':
|
|
raise TypeError('unsupported format string passed to Type3.__format__')
|
|
|
|
return str(self)
|
|
|
|
def __eq__(self, other: Any) -> bool:
|
|
if isinstance(other, Type3):
|
|
return False
|
|
|
|
if not isinstance(other, PlaceholderForType):
|
|
raise NotImplementedError
|
|
|
|
return self is other
|
|
|
|
def __ne__(self, other: Any) -> bool:
|
|
return not self.__eq__(other)
|
|
|
|
def __hash__(self) -> int:
|
|
return 0 # Valid but performs badly
|
|
|
|
def __bool__(self) -> bool:
|
|
raise NotImplementedError
|
|
|
|
Type3OrPlaceholder = Union[Type3, PlaceholderForType]
|
|
|
|
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, PrimitiveType3]
|
|
"""
|
|
When constructing a type with the same arguments,
|
|
it should produce the exact same result.
|
|
"""
|
|
|
|
_reverse_cache: dict[PrimitiveType3, 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:
|
|
raise NotImplementedError
|
|
|
|
def did_construct(self, typ: PrimitiveType3) -> T | None:
|
|
return self._reverse_cache.get(typ)
|
|
|
|
def construct(self, key: T) -> PrimitiveType3:
|
|
result = self._cache.get(key, None)
|
|
if result is None:
|
|
self._cache[key] = result = PrimitiveType3(self.make_name(key), self.type_classes)
|
|
self._reverse_cache[result] = key
|
|
|
|
return result
|
|
|
|
class TypeConstructor_Type(TypeConstructor[PrimitiveType3]):
|
|
"""
|
|
Base class type constructors of kind: * -> *
|
|
"""
|
|
__slots__ = ()
|
|
|
|
def __call__(self, arg: PrimitiveType3) -> PrimitiveType3:
|
|
raise NotImplementedError
|
|
|
|
class TypeConstructor_TypeInt(TypeConstructor[Tuple[PrimitiveType3, IntType3]]):
|
|
"""
|
|
Base class type constructors of kind: * -> Int -> *
|
|
"""
|
|
__slots__ = ()
|
|
|
|
def make_name(self, key: Tuple[PrimitiveType3, IntType3]) -> str:
|
|
return f'{self.name} {key[0].name} {key[1].value}'
|
|
|
|
def __call__(self, arg0: PrimitiveType3, arg1: IntType3) -> PrimitiveType3:
|
|
return self.construct((arg0, arg1))
|
|
|
|
class TypeConstructor_TypeStar(TypeConstructor[Tuple[PrimitiveType3, ...]]):
|
|
"""
|
|
Base class type constructors of variadic kind
|
|
"""
|
|
def __call__(self, *args: PrimitiveType3) -> PrimitiveType3:
|
|
key: Tuple[PrimitiveType3, ...] = tuple(args)
|
|
return self.construct(key)
|
|
|
|
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
|
|
def make_name(self, key: Tuple[PrimitiveType3, IntType3]) -> str:
|
|
return f'{key[0].name}[{key[1].value}]'
|
|
|
|
class TypeConstructor_Tuple(TypeConstructor_TypeStar):
|
|
def make_name(self, key: Tuple[PrimitiveType3, ...]) -> str:
|
|
return '(' + ', '.join(x.name for x in key) + ', )'
|
|
|
|
class StructType3(PrimitiveType3):
|
|
"""
|
|
A Type3 struct with named members
|
|
"""
|
|
__slots__ = ('name', 'members', )
|
|
|
|
name: str
|
|
"""
|
|
The structs fully qualified name
|
|
"""
|
|
|
|
members: Dict[str, Type3]
|
|
"""
|
|
The struct's field definitions
|
|
"""
|
|
|
|
def __init__(self, name: str, members: Dict[str, Type3]) -> None:
|
|
super().__init__(name, [])
|
|
|
|
self.name = name
|
|
self.members = dict(members)
|
|
|
|
def __repr__(self) -> str:
|
|
return f'StructType3(repr({self.name}), repr({self.members}))'
|
|
|
|
none = PrimitiveType3('none', [])
|
|
"""
|
|
The none type, for when functions simply don't return anything. e.g., IO().
|
|
"""
|
|
|
|
bool_ = PrimitiveType3('bool', [])
|
|
"""
|
|
The bool type, either True or False
|
|
|
|
Suffixes with an underscores, as it's a Python builtin
|
|
"""
|
|
|
|
u8 = PrimitiveType3('u8', [Bits, Eq, Ord])
|
|
"""
|
|
The unsigned 8-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^8.
|
|
"""
|
|
|
|
u32 = PrimitiveType3('u32', [Bits, Eq, Integral, NatNum, Ord])
|
|
"""
|
|
The unsigned 32-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^32.
|
|
"""
|
|
|
|
u64 = PrimitiveType3('u64', [Bits, Eq, Integral, NatNum, Ord])
|
|
"""
|
|
The unsigned 64-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^64.
|
|
"""
|
|
|
|
i8 = PrimitiveType3('i8', [Eq, Ord])
|
|
"""
|
|
The signed 8-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^8, but
|
|
with the middel point being 0.
|
|
"""
|
|
|
|
i32 = PrimitiveType3('i32', [Eq, Integral, IntNum, NatNum, Ord])
|
|
"""
|
|
The unsigned 32-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^32, but
|
|
with the middel point being 0.
|
|
"""
|
|
|
|
i64 = PrimitiveType3('i64', [Eq, Integral, IntNum, NatNum, Ord])
|
|
"""
|
|
The unsigned 64-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^64, but
|
|
with the middel point being 0.
|
|
"""
|
|
|
|
f32 = PrimitiveType3('f32', [Eq, Floating, Fractional, IntNum, NatNum, Ord])
|
|
"""
|
|
A 32-bits IEEE 754 float, of 32 bits width.
|
|
"""
|
|
|
|
f64 = PrimitiveType3('f64', [Eq, Floating, Fractional, IntNum, NatNum, Ord])
|
|
"""
|
|
A 32-bits IEEE 754 float, of 64 bits width.
|
|
"""
|
|
|
|
bytes_ = PrimitiveType3('bytes', [])
|
|
"""
|
|
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
|
"""
|
|
|
|
static_array = TypeConstructor_StaticArray('static_array', [], [
|
|
Eq,
|
|
InternalPassAsPointer,
|
|
])
|
|
"""
|
|
A type constructor.
|
|
|
|
Any static array is a fixed length piece of memory that can be indexed at runtime.
|
|
|
|
It should be applied with one argument. It has a runtime-dynamic length
|
|
of the same type repeated.
|
|
"""
|
|
|
|
tuple_ = TypeConstructor_Tuple('tuple', [], [
|
|
InternalPassAsPointer,
|
|
])
|
|
"""
|
|
This is a fixed length piece of memory.
|
|
|
|
It should be applied with zero or more arguments. It has a compile time
|
|
determined length, and each argument can be different.
|
|
"""
|
|
|
|
LOOKUP_TABLE: Dict[str, PrimitiveType3] = {
|
|
'none': none,
|
|
'bool': bool_,
|
|
'u8': u8,
|
|
'u32': u32,
|
|
'u64': u64,
|
|
'i8': i8,
|
|
'i32': i32,
|
|
'i64': i64,
|
|
'f32': f32,
|
|
'f64': f64,
|
|
'bytes': bytes_,
|
|
}
|