276 lines
6.3 KiB
Python
276 lines
6.3 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:
|
|
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:
|
|
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 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]
|
|
|
|
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 __eq__(self, other: Any) -> bool:
|
|
if not isinstance(other, Type3):
|
|
raise NotImplementedError
|
|
|
|
if not isinstance(other, AppliedType3):
|
|
return False
|
|
|
|
return (
|
|
self.base == other.base
|
|
and len(self.args) == len(other.args)
|
|
and all(
|
|
s == x
|
|
for s, x in zip(self.args, other.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,
|
|
}
|