type5 is much more first principles based, so we get a lot of weird quirks removed: - FromLiteral no longer needs to understand AST - Type unifications works more like Haskell - Function types are just ordinary types, saving a lot of manual busywork and more.
151 lines
4.3 KiB
Python
151 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from ..type5.record import Record
|
|
from ..type5.typeexpr import (
|
|
AtomicType,
|
|
TypeApplication,
|
|
TypeConstructor,
|
|
TypeExpr,
|
|
TypeVariable,
|
|
)
|
|
from ..type5.typerouter import TypeRouter
|
|
|
|
if TYPE_CHECKING:
|
|
from .base import BuildBase
|
|
|
|
class BuildTypeRouter[T](TypeRouter[T]):
|
|
"""
|
|
Extends the general type router with phasm builtin types.
|
|
|
|
Like functions, tuples, static and dynamic arrays.
|
|
"""
|
|
__slots__ = ('build', )
|
|
|
|
def __init__(self, build: BuildBase[Any]) -> None:
|
|
self.build = build
|
|
|
|
def when_application(self, typ: TypeApplication) -> T:
|
|
da_arg = self.build.type5_is_dynamic_array(typ)
|
|
if da_arg is not None:
|
|
return self.when_dynamic_array(da_arg)
|
|
|
|
fn_args = self.build.type5_is_function(typ)
|
|
if fn_args is not None:
|
|
return self.when_function(fn_args)
|
|
|
|
sa_args = self.build.type5_is_static_array(typ)
|
|
if sa_args is not None:
|
|
sa_len, sa_typ = sa_args
|
|
return self.when_static_array(sa_len, sa_typ)
|
|
|
|
tp_args = self.build.type5_is_tuple(typ)
|
|
if tp_args is not None:
|
|
return self.when_tuple(tp_args)
|
|
|
|
return self.when_application_other(typ)
|
|
|
|
def when_record(self, typ: Record) -> T:
|
|
return self.when_struct(typ)
|
|
|
|
def when_application_other(self, typ: TypeApplication) -> T:
|
|
raise NotImplementedError
|
|
|
|
def when_dynamic_array(self, da_arg: TypeExpr) -> T:
|
|
raise NotImplementedError
|
|
|
|
def when_function(self, fn_args: list[TypeExpr]) -> T:
|
|
raise NotImplementedError
|
|
|
|
def when_struct(self, typ: Record) -> T:
|
|
raise NotImplementedError
|
|
|
|
def when_static_array(self, sa_len: int, sa_typ: TypeExpr) -> T:
|
|
raise NotImplementedError
|
|
|
|
def when_tuple(self, tp_args: list[TypeExpr]) -> T:
|
|
raise NotImplementedError
|
|
|
|
class TypeName(BuildTypeRouter[str]):
|
|
"""
|
|
Router to generate a type's name.
|
|
|
|
Also serves an example implementation.
|
|
"""
|
|
__slots__ = ()
|
|
|
|
def when_application_other(self, typ: TypeApplication) -> str:
|
|
return typ.name
|
|
|
|
def when_atomic(self, typ: AtomicType) -> str:
|
|
return typ.name
|
|
|
|
def when_constructor(self, typ: TypeConstructor) -> str:
|
|
return typ.name
|
|
|
|
def when_dynamic_array(self, da_arg: TypeExpr) -> str:
|
|
if da_arg == self.build.u8_type5:
|
|
return 'bytes'
|
|
|
|
return self(da_arg) + '[...]'
|
|
|
|
def when_function(self, fn_args: list[TypeExpr]) -> str:
|
|
return 'Callable[' + ', '.join(map(self, fn_args)) + ']'
|
|
|
|
def when_static_array(self, sa_len: int, sa_typ: TypeExpr) -> str:
|
|
return f'{self(sa_typ)}[{sa_len}]'
|
|
|
|
def when_struct(self, typ: Record) -> str:
|
|
return typ.name
|
|
|
|
def when_tuple(self, tp_args: list[TypeExpr]) -> str:
|
|
return '(' + ', '.join(map(self, tp_args)) + ', )'
|
|
|
|
def when_variable(self, typ: TypeVariable) -> str:
|
|
return typ.name
|
|
|
|
class TypeAllocSize(BuildTypeRouter[int]):
|
|
"""
|
|
Router to generate a type's allocation size.
|
|
"""
|
|
|
|
__slots__ = ('is_member', )
|
|
|
|
is_member: bool
|
|
|
|
def __init__(self, build: BuildBase[Any], is_member: bool) -> None:
|
|
super().__init__(build)
|
|
self.is_member = is_member
|
|
|
|
def when_atomic(self, typ: AtomicType) -> int:
|
|
typ_info = self.build.type_info_map.get(typ.name)
|
|
if typ_info is None:
|
|
raise NotImplementedError(typ)
|
|
|
|
return typ_info.alloc_size
|
|
|
|
def when_dynamic_array(self, da_arg: TypeExpr) -> int:
|
|
if self.is_member:
|
|
return self.build.type_info_constructed.alloc_size
|
|
|
|
raise RuntimeError("Cannot know size of dynamic array at type level")
|
|
|
|
def when_static_array(self, sa_len: int, sa_typ: TypeExpr) -> int:
|
|
if self.is_member:
|
|
return self.build.type_info_constructed.alloc_size
|
|
|
|
raise NotImplementedError
|
|
|
|
def when_struct(self, typ: Record) -> int:
|
|
if self.is_member:
|
|
return self.build.type_info_constructed.alloc_size
|
|
|
|
return sum(map(self.build.type5_alloc_size_member, (x[1] for x in typ.fields)))
|
|
|
|
def when_tuple(self, tp_args: list[TypeExpr]) -> int:
|
|
if self.is_member:
|
|
return self.build.type_info_constructed.alloc_size
|
|
|
|
return sum(map(self.build.type5_alloc_size_member, tp_args))
|