Re-implementing some things that were broken. Also, if a typing error is found, and we detect an infinite loop, we return the errors instead, as that's probably what causing the loop anyhow.
251 lines
5.7 KiB
Python
251 lines
5.7 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, Iterable, List, Protocol, Union
|
|
|
|
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', )
|
|
|
|
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("{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:
|
|
raise NotImplementedError
|
|
|
|
def __ne__(self, other: Any) -> bool:
|
|
raise NotImplementedError
|
|
|
|
def __hash__(self) -> int:
|
|
raise NotImplementedError
|
|
|
|
def __bool__(self) -> bool:
|
|
raise NotImplementedError
|
|
|
|
class PlaceholderForType:
|
|
"""
|
|
A placeholder type, for when we don't know the final type yet
|
|
"""
|
|
__slots__ = ('update_on_substitution', )
|
|
|
|
update_on_substitution: List[ExpressionProtocol]
|
|
|
|
def __init__(self, update_on_substitution: Iterable[ExpressionProtocol]) -> None:
|
|
self.update_on_substitution = [*update_on_substitution]
|
|
|
|
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 not isinstance(other, PlaceholderForType):
|
|
raise NotImplementedError
|
|
|
|
return self is other
|
|
|
|
def __ne__(self, other: Any) -> bool:
|
|
raise NotImplementedError
|
|
|
|
def __hash__(self) -> int:
|
|
return 0 # Valid but performs badly
|
|
|
|
def __bool__(self) -> bool:
|
|
raise NotImplementedError
|
|
|
|
Type3OrPlaceholder = Union[Type3, PlaceholderForType]
|
|
|
|
class AppliedType3(Type3):
|
|
"""
|
|
A Type3 that has been applied to another type
|
|
"""
|
|
__slots__ = ('base', 'args', )
|
|
|
|
base: Type3
|
|
"""
|
|
The base type
|
|
"""
|
|
|
|
args: List[Type3OrPlaceholder]
|
|
"""
|
|
The applied types (or placeholders there for)
|
|
"""
|
|
|
|
def __init__(self, base: Type3, args: Iterable[Type3OrPlaceholder]) -> None:
|
|
args = [*args]
|
|
|
|
super().__init__(
|
|
base.name
|
|
+ ' ('
|
|
+ ') ('.join(str(x) for x in args) # FIXME: Do we need to redo the name on substitution?
|
|
+ ')'
|
|
)
|
|
|
|
self.base = base
|
|
self.args = args
|
|
|
|
def __repr__(self) -> str:
|
|
return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
|
|
|
|
class StructType3(Type3):
|
|
"""
|
|
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 = Type3('none')
|
|
"""
|
|
The none type, for when functions simply don't return anything. e.g., IO().
|
|
"""
|
|
|
|
u8 = Type3('u8')
|
|
"""
|
|
The unsigned 8-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^8.
|
|
"""
|
|
|
|
u32 = Type3('u32')
|
|
"""
|
|
The unsigned 32-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^32.
|
|
"""
|
|
|
|
u64 = Type3('u64')
|
|
"""
|
|
The unsigned 64-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^64.
|
|
"""
|
|
|
|
i8 = Type3('i8')
|
|
"""
|
|
The signed 8-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^8, but
|
|
with the middel point being 0.
|
|
"""
|
|
|
|
i32 = Type3('i32')
|
|
"""
|
|
The unsigned 32-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^32, but
|
|
with the middel point being 0.
|
|
"""
|
|
|
|
i64 = Type3('i64')
|
|
"""
|
|
The unsigned 64-bit integer type.
|
|
|
|
Operations on variables employ modular arithmetic, with modulus 2^64, but
|
|
with the middel point being 0.
|
|
"""
|
|
|
|
f32 = Type3('f32')
|
|
"""
|
|
A 32-bits IEEE 754 float, of 32 bits width.
|
|
"""
|
|
|
|
f64 = Type3('f64')
|
|
"""
|
|
A 32-bits IEEE 754 float, of 64 bits width.
|
|
"""
|
|
|
|
bytes = Type3('bytes')
|
|
"""
|
|
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
|
"""
|
|
|
|
static_array = Type3('static_array')
|
|
"""
|
|
This 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 = Type3('tuple') # pylint: disable=W0622
|
|
"""
|
|
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, Type3] = {
|
|
'none': none,
|
|
'u8': u8,
|
|
'u32': u32,
|
|
'u64': u64,
|
|
'i8': i8,
|
|
'i32': i32,
|
|
'i64': i64,
|
|
'f32': f32,
|
|
'f64': f64,
|
|
'bytes': bytes,
|
|
}
|