Replaces did_construct with a proper router
By annotating types with the constructor application that was used to create them. Later on we can use the router to replace compiler's INSTANCES or for user defined types.
This commit is contained in:
parent
6b66935c67
commit
f8d107f4fa
1
TODO.md
1
TODO.md
@ -15,6 +15,7 @@
|
||||
- Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type?
|
||||
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
||||
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
||||
- Why is expression_subscript_bytes using a helper method but expression_subscript_static_array is not?
|
||||
|
||||
- Parser is putting stuff in ModuleDataBlock
|
||||
- Surely the compiler should build data blocks
|
||||
|
||||
@ -6,7 +6,7 @@ It's intented to be a "any color, as long as it's black" kind of renderer
|
||||
from typing import Generator
|
||||
|
||||
from . import ourlang, prelude
|
||||
from .type3.types import Type3
|
||||
from .type3.types import Type3, TypeApplication_Struct
|
||||
|
||||
|
||||
def phasm_render(inp: ourlang.Module) -> str:
|
||||
@ -30,11 +30,10 @@ def struct_definition(inp: ourlang.StructDefinition) -> str:
|
||||
"""
|
||||
Render: TypeStruct's definition
|
||||
"""
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
result = f'class {inp.struct_type3.name}:\n'
|
||||
for mem, typ in st_args.items():
|
||||
for mem, typ in inp.struct_type3.application.arguments:
|
||||
result += f' {mem}: {type3(typ)}\n'
|
||||
|
||||
return result
|
||||
|
||||
@ -330,19 +330,24 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
"""
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
args: list[type3types.Type3] = []
|
||||
args: tuple[type3types.Type3, ...]
|
||||
|
||||
sa_args = prelude.static_array.did_construct(inp.type3)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
args = [sa_type for _ in range(sa_len.value)]
|
||||
if isinstance(inp.type3.application, type3types.TypeApplication_TypeStar):
|
||||
# Possibly paranoid assert. If we have a future variadic type,
|
||||
# does it also do this tuple instantation like this?
|
||||
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_Tuple)
|
||||
|
||||
if not args:
|
||||
tp_args = prelude.tuple_.did_construct(inp.type3)
|
||||
if tp_args is None:
|
||||
raise NotImplementedError
|
||||
args = inp.type3.application.arguments
|
||||
elif isinstance(inp.type3.application, type3types.TypeApplication_TypeInt):
|
||||
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
||||
# does it also do this tuple instantation like this?
|
||||
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_StaticArray)
|
||||
|
||||
args = list(tp_args)
|
||||
sa_type, sa_len = inp.type3.application.arguments
|
||||
|
||||
args = tuple(sa_type for _ in range(sa_len.value))
|
||||
else:
|
||||
raise NotImplementedError('tuple_instantiation', inp.type3)
|
||||
|
||||
comment_elements = ''
|
||||
for element in inp.elements:
|
||||
@ -378,6 +383,81 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
# Return the allocated address
|
||||
wgn.local.get(tmp_var)
|
||||
|
||||
def expression_subscript_bytes(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
) -> bool:
|
||||
wgn, inp = attrs
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
expression(wgn, inp.index)
|
||||
wgn.call(stdlib_types.__subscript_bytes__)
|
||||
return True
|
||||
|
||||
def expression_subscript_static_array(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
args: tuple[type3types.Type3, type3types.IntType3],
|
||||
) -> bool:
|
||||
wgn, inp = attrs
|
||||
|
||||
el_type, el_len = args
|
||||
|
||||
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
||||
# and we don't need to do the out of bounds check
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
tmp_var = wgn.temp_var_i32('index')
|
||||
expression(wgn, inp.index)
|
||||
wgn.local.tee(tmp_var)
|
||||
|
||||
# Out of bounds check based on el_len.value
|
||||
wgn.i32.const(el_len.value)
|
||||
wgn.i32.ge_u()
|
||||
with wgn.if_():
|
||||
wgn.unreachable(comment='Out of bounds')
|
||||
|
||||
wgn.local.get(tmp_var)
|
||||
wgn.i32.const(calculate_alloc_size(el_type))
|
||||
wgn.i32.mul()
|
||||
wgn.i32.add()
|
||||
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load')
|
||||
return True
|
||||
|
||||
def expression_subscript_tuple(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
args: tuple[type3types.Type3, ...],
|
||||
) -> bool:
|
||||
wgn, inp = attrs
|
||||
|
||||
assert isinstance(inp.index, ourlang.ConstantPrimitive)
|
||||
assert isinstance(inp.index.value, int)
|
||||
|
||||
offset = 0
|
||||
for el_type in args[0:inp.index.value]:
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
offset += calculate_alloc_size(el_type)
|
||||
|
||||
el_type = args[inp.index.value]
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
||||
return True
|
||||
|
||||
SUBSCRIPT_ROUTER = type3types.TypeApplicationRouter[tuple[WasmGenerator, ourlang.Subscript], bool]()
|
||||
SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes)
|
||||
SUBSCRIPT_ROUTER.add(prelude.static_array, expression_subscript_static_array)
|
||||
SUBSCRIPT_ROUTER.add(prelude.tuple_, expression_subscript_tuple)
|
||||
|
||||
def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
"""
|
||||
Compile: Any expression
|
||||
@ -509,81 +589,21 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
if isinstance(inp, ourlang.Subscript):
|
||||
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.varref.type3 is prelude.bytes_:
|
||||
expression(wgn, inp.varref)
|
||||
expression(wgn, inp.index)
|
||||
wgn.call(stdlib_types.__subscript_bytes__)
|
||||
return
|
||||
|
||||
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
sa_args = prelude.static_array.did_construct(inp.varref.type3)
|
||||
if sa_args is not None:
|
||||
el_type, el_len = sa_args
|
||||
|
||||
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
||||
# and we don't need to do the out of bounds check
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
tmp_var = wgn.temp_var_i32('index')
|
||||
expression(wgn, inp.index)
|
||||
wgn.local.tee(tmp_var)
|
||||
|
||||
# Out of bounds check based on el_len.value
|
||||
wgn.i32.const(el_len.value)
|
||||
wgn.i32.ge_u()
|
||||
with wgn.if_():
|
||||
wgn.unreachable(comment='Out of bounds')
|
||||
|
||||
wgn.local.get(tmp_var)
|
||||
wgn.i32.const(calculate_alloc_size(el_type))
|
||||
wgn.i32.mul()
|
||||
wgn.i32.add()
|
||||
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load')
|
||||
return
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(inp.varref.type3)
|
||||
if tp_args is not None:
|
||||
assert isinstance(inp.index, ourlang.ConstantPrimitive)
|
||||
assert isinstance(inp.index.value, int)
|
||||
|
||||
offset = 0
|
||||
for el_type in tp_args[0:inp.index.value]:
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
offset += calculate_alloc_size(el_type)
|
||||
|
||||
el_type = tp_args[inp.index.value]
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp, inp.varref.type3)
|
||||
# Type checker guarantees we don't get routing errors
|
||||
return SUBSCRIPT_ROUTER((wgn, inp, ), inp.varref.type3)
|
||||
|
||||
if isinstance(inp, ourlang.AccessStructMember):
|
||||
assert inp.struct_type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None
|
||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
|
||||
|
||||
member_type = st_args[inp.member]
|
||||
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||
|
||||
mtyp = LOAD_STORE_TYPE_MAP[member_type.name]
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(calculate_member_offset(
|
||||
inp.struct_type3.name, st_args, inp.member
|
||||
inp.struct_type3.name, inp.struct_type3.application.arguments, inp.member
|
||||
)))
|
||||
return
|
||||
|
||||
@ -958,8 +978,9 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
||||
return result
|
||||
|
||||
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None
|
||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
|
||||
|
||||
st_args = inp.struct_type3.application.arguments
|
||||
|
||||
tmp_var = wgn.temp_var_i32('struct_adr')
|
||||
|
||||
@ -969,7 +990,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
||||
wgn.local.set(tmp_var)
|
||||
|
||||
# Store each member individually
|
||||
for memname, mtyp3 in st_args.items():
|
||||
for memname, mtyp3 in st_args:
|
||||
mtyp: Optional[str]
|
||||
if (prelude.InternalPassAsPointer, (mtyp3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
|
||||
@ -7,7 +7,7 @@ from typing import Dict, Iterable, List, Optional, Union
|
||||
from . import prelude
|
||||
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import Type3
|
||||
from .type3.types import Type3, TypeApplication_Struct
|
||||
|
||||
|
||||
class Expression:
|
||||
@ -341,9 +341,9 @@ class StructConstructor(Function):
|
||||
def __init__(self, struct_type3: Type3) -> None:
|
||||
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
||||
|
||||
st_args = prelude.struct.did_construct(struct_type3)
|
||||
assert st_args is not None
|
||||
for mem, typ in st_args.items():
|
||||
assert isinstance(struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
for mem, typ in struct_type3.application.arguments:
|
||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||
self.signature.args.append(typ)
|
||||
|
||||
|
||||
@ -246,7 +246,7 @@ class OurVisitor:
|
||||
|
||||
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
||||
|
||||
return StructDefinition(prelude.struct(node.name, members), node.lineno)
|
||||
return StructDefinition(prelude.struct(node.name, tuple(members.items())), node.lineno)
|
||||
|
||||
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
||||
if not isinstance(node.target, ast.Name):
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
"""
|
||||
The prelude are all the builtin types, type classes and methods
|
||||
"""
|
||||
|
||||
from ..type3.functions import TypeVariable
|
||||
from ..type3.functions import (
|
||||
TypeVariable,
|
||||
)
|
||||
from ..type3.typeclasses import Type3Class
|
||||
from ..type3.types import (
|
||||
IntType3,
|
||||
Type3,
|
||||
TypeApplication_Nullary,
|
||||
TypeConstructor_StaticArray,
|
||||
TypeConstructor_Struct,
|
||||
TypeConstructor_Tuple,
|
||||
@ -21,40 +24,40 @@ def instance_type_class(cls: Type3Class, *typ: Type3) -> None:
|
||||
|
||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, tuple(typ), ))
|
||||
|
||||
none = Type3('none')
|
||||
none = Type3('none', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The none type, for when functions simply don't return anything. e.g., IO().
|
||||
"""
|
||||
|
||||
bool_ = Type3('bool')
|
||||
bool_ = Type3('bool', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The bool type, either True or False
|
||||
|
||||
Suffixes with an underscores, as it's a Python builtin
|
||||
"""
|
||||
|
||||
u8 = Type3('u8')
|
||||
u8 = Type3('u8', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 8-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^8.
|
||||
"""
|
||||
|
||||
u32 = Type3('u32')
|
||||
u32 = Type3('u32', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 32-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^32.
|
||||
"""
|
||||
|
||||
u64 = Type3('u64')
|
||||
u64 = Type3('u64', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 64-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^64.
|
||||
"""
|
||||
|
||||
i8 = Type3('i8')
|
||||
i8 = Type3('i8', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The signed 8-bit integer type.
|
||||
|
||||
@ -62,7 +65,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
i32 = Type3('i32')
|
||||
i32 = Type3('i32', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 32-bit integer type.
|
||||
|
||||
@ -70,7 +73,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
i64 = Type3('i64')
|
||||
i64 = Type3('i64', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 64-bit integer type.
|
||||
|
||||
@ -78,22 +81,22 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
f32 = Type3('f32')
|
||||
f32 = Type3('f32', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
A 32-bits IEEE 754 float, of 32 bits width.
|
||||
"""
|
||||
|
||||
f64 = Type3('f64')
|
||||
f64 = Type3('f64', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
A 32-bits IEEE 754 float, of 64 bits width.
|
||||
"""
|
||||
|
||||
bytes_ = Type3('bytes')
|
||||
bytes_ = Type3('bytes', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
||||
"""
|
||||
|
||||
def sa_on_create(typ: Type3) -> None:
|
||||
def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
|
||||
@ -106,7 +109,7 @@ It should be applied with one argument. It has a runtime-dynamic length
|
||||
of the same type repeated.
|
||||
"""
|
||||
|
||||
def tp_on_create(typ: Type3) -> None:
|
||||
def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
tuple_ = TypeConstructor_Tuple('tuple', on_create=tp_on_create)
|
||||
@ -117,7 +120,7 @@ It should be applied with zero or more arguments. It has a compile time
|
||||
determined length, and each argument can be different.
|
||||
"""
|
||||
|
||||
def st_on_create(typ: Type3) -> None:
|
||||
def st_on_create(args: tuple[tuple[str, Type3], ...], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
struct = TypeConstructor_Struct('struct', on_create=st_on_create)
|
||||
|
||||
@ -1,8 +1,39 @@
|
||||
from . import prelude
|
||||
from .type3 import types as type3types
|
||||
from .type3.types import IntType3, NoRouteForTypeException, Type3, TypeApplicationRouter
|
||||
|
||||
|
||||
def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
|
||||
def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntType3]) -> int:
|
||||
if is_member:
|
||||
return 4
|
||||
|
||||
sa_type, sa_len = args
|
||||
|
||||
return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
|
||||
|
||||
def calculate_alloc_size_tuple(is_member: bool, args: tuple[Type3, ...]) -> int:
|
||||
if is_member:
|
||||
return 4
|
||||
|
||||
return sum(
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in args
|
||||
)
|
||||
|
||||
def calculate_alloc_size_struct(is_member: bool, args: tuple[tuple[str, Type3], ...]) -> int:
|
||||
if is_member:
|
||||
return 4
|
||||
|
||||
return sum(
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for _, x in args
|
||||
)
|
||||
|
||||
ALLOC_SIZE_ROUTER = TypeApplicationRouter[bool, int]()
|
||||
ALLOC_SIZE_ROUTER.add(prelude.static_array, calculate_alloc_size_static_array)
|
||||
ALLOC_SIZE_ROUTER.add(prelude.struct, calculate_alloc_size_struct)
|
||||
ALLOC_SIZE_ROUTER.add(prelude.tuple_, calculate_alloc_size_tuple)
|
||||
|
||||
def calculate_alloc_size(typ: Type3, is_member: bool = False) -> int:
|
||||
if typ in (prelude.u8, prelude.i8, ):
|
||||
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32
|
||||
|
||||
@ -12,51 +43,20 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
|
||||
if typ in (prelude.u64, prelude.i64, prelude.f64, ):
|
||||
return 8
|
||||
|
||||
if typ == prelude.bytes_:
|
||||
try:
|
||||
return ALLOC_SIZE_ROUTER(is_member, typ)
|
||||
except NoRouteForTypeException:
|
||||
if is_member:
|
||||
# By default, 'boxed' or 'constructed' types are
|
||||
# stored as pointers when a member of a struct or tuple
|
||||
return 4
|
||||
|
||||
raise NotImplementedError # When does this happen?
|
||||
raise NotImplementedError(typ)
|
||||
|
||||
st_args = prelude.struct.did_construct(typ)
|
||||
if st_args is not None:
|
||||
if is_member:
|
||||
# Structs referred to by other structs or tuples are pointers
|
||||
return 4
|
||||
|
||||
return sum(
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in st_args.values()
|
||||
)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(typ)
|
||||
if sa_args is not None:
|
||||
if is_member:
|
||||
# tuples referred to by other structs or tuples are pointers
|
||||
return 4
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(typ)
|
||||
if tp_args is not None:
|
||||
if is_member:
|
||||
# tuples referred to by other structs or tuples are pointers
|
||||
return 4
|
||||
|
||||
size = 0
|
||||
for arg in tp_args:
|
||||
size += calculate_alloc_size(arg, is_member=True)
|
||||
|
||||
return size
|
||||
|
||||
raise NotImplementedError(calculate_alloc_size, typ)
|
||||
|
||||
def calculate_member_offset(st_name: str, st_args: dict[str, type3types.Type3], needle: str) -> int:
|
||||
def calculate_member_offset(st_name: str, st_args: tuple[tuple[str, Type3], ...], needle: str) -> int:
|
||||
result = 0
|
||||
|
||||
for memnam, memtyp in st_args.items():
|
||||
for memnam, memtyp in st_args:
|
||||
if needle == memnam:
|
||||
return result
|
||||
|
||||
|
||||
@ -158,12 +158,41 @@ class SameTypeConstraint(ConstraintBase):
|
||||
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
|
||||
|
||||
class TupleMatchConstraint(ConstraintBase):
|
||||
__slots__ = ('exp_type', 'args', )
|
||||
|
||||
exp_type: placeholders.Type3OrPlaceholder
|
||||
args: list[placeholders.Type3OrPlaceholder]
|
||||
|
||||
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: Iterable[placeholders.Type3OrPlaceholder], comment: str):
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.exp_type = exp_type
|
||||
self.args = list(args)
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.args):
|
||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||
|
||||
return [
|
||||
SameTypeConstraint(arg, sa_type)
|
||||
for arg in self.args
|
||||
]
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
||||
if len(tp_args) != len(self.args):
|
||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||
|
||||
return [
|
||||
SameTypeConstraint(arg, oth_arg)
|
||||
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
||||
]
|
||||
|
||||
GENERATE_ROUTER = types.TypeApplicationRouter['TupleMatchConstraint', CheckResult]()
|
||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
exp_type = self.exp_type
|
||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||
@ -172,31 +201,10 @@ class TupleMatchConstraint(ConstraintBase):
|
||||
|
||||
exp_type = exp_type.resolve_as
|
||||
|
||||
assert isinstance(exp_type, types.Type3)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(exp_type)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.args):
|
||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||
|
||||
return [
|
||||
SameTypeConstraint(arg, sa_type)
|
||||
for arg in self.args
|
||||
]
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(exp_type)
|
||||
if tp_args is not None:
|
||||
if len(tp_args) != len(self.args):
|
||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||
|
||||
return [
|
||||
SameTypeConstraint(arg, oth_arg)
|
||||
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
||||
]
|
||||
|
||||
raise NotImplementedError(exp_type)
|
||||
try:
|
||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
||||
except types.NoRouteForTypeException:
|
||||
raise NotImplementedError(exp_type)
|
||||
|
||||
class MustImplementTypeClassConstraint(ConstraintBase):
|
||||
"""
|
||||
@ -281,6 +289,85 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
self.type3 = type3
|
||||
self.literal = literal
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.literal.value):
|
||||
return Error('Member count mismatch', comment=self.comment)
|
||||
|
||||
res: list[ConstraintBase] = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(sa_type, y)
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(sa_type, PlaceholderForType([y]))
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
def _generate_struct(self, st_args: tuple[tuple[str, types.Type3], ...]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantStruct):
|
||||
return Error('Must be struct')
|
||||
|
||||
if len(st_args) != len(self.literal.value):
|
||||
return Error('Struct element count mismatch')
|
||||
|
||||
res: list[ConstraintBase] = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for (_, x), y in zip(st_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_name}.{x_n}')
|
||||
for (x_n, x_t, ), y in zip(st_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
if len(tp_args) != len(self.literal.value):
|
||||
return Error('Tuple element count mismatch', comment=self.comment)
|
||||
|
||||
res: list[ConstraintBase] = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x, PlaceholderForType([y]))
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
GENERATE_ROUTER = types.TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
|
||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||
GENERATE_ROUTER.add(prelude.struct, _generate_struct)
|
||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
int_table: Dict[str, Tuple[int, bool]] = {
|
||||
'u8': (1, False),
|
||||
@ -331,92 +418,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
return Error('Must be bytes', comment=self.comment) # FIXME: Add line information
|
||||
|
||||
res: NewConstraintList
|
||||
exp_type = self.type3
|
||||
|
||||
assert isinstance(self.type3, types.Type3)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(self.type3)
|
||||
if tp_args is not None:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
if len(tp_args) != len(self.literal.value):
|
||||
return Error('Tuple element count mismatch', comment=self.comment)
|
||||
|
||||
res = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x, PlaceholderForType([y]))
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
sa_args = prelude.static_array.did_construct(self.type3)
|
||||
if sa_args is not None:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.literal.value):
|
||||
return Error('Member count mismatch', comment=self.comment)
|
||||
|
||||
res = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(sa_type, y)
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(sa_type, PlaceholderForType([y]))
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
st_args = prelude.struct.did_construct(self.type3)
|
||||
if st_args is not None:
|
||||
if not isinstance(self.literal, ourlang.ConstantStruct):
|
||||
return Error('Must be struct')
|
||||
|
||||
if self.literal.struct_name != self.type3.name:
|
||||
return Error('Struct mismatch')
|
||||
|
||||
|
||||
if len(st_args) != len(self.literal.value):
|
||||
return Error('Struct element count mismatch')
|
||||
|
||||
res = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(st_args.values(), self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_name}.{x_n}')
|
||||
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
raise NotImplementedError(self.type3, self.literal)
|
||||
try:
|
||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
||||
except types.NoRouteForTypeException:
|
||||
raise NotImplementedError(exp_type)
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
return (
|
||||
@ -456,6 +463,50 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
self.index = index
|
||||
self.index_phft = index_phft
|
||||
|
||||
def _generate_bytes(self) -> CheckResult:
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: bytes -> u32 -> u8'),
|
||||
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
]
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
assert isinstance(self.index.value, int)
|
||||
|
||||
if self.index.value < 0 or sa_len.value <= self.index.value:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
]
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
||||
# We special case tuples to allow for ease of use to the programmer
|
||||
# e.g. rather than having to do `fst a` and `snd a` and only have to-sized tuples
|
||||
# we use a[0] and a[1] and allow for a[2] and on.
|
||||
|
||||
if not isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
return Error('Must index with literal')
|
||||
|
||||
if not isinstance(self.index.value, int):
|
||||
return Error('Must index with integer literal')
|
||||
|
||||
if self.index.value < 0 or len(tp_args) <= self.index.value:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment=f'Tuple subscript index {self.index.value}'),
|
||||
SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
|
||||
]
|
||||
|
||||
GENERATE_ROUTER = types.TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]()
|
||||
GENERATE_ROUTER.add_n(prelude.bytes_, _generate_bytes)
|
||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
exp_type = self.type3
|
||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||
@ -464,51 +515,10 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
|
||||
exp_type = exp_type.resolve_as
|
||||
|
||||
assert isinstance(exp_type, types.Type3)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(exp_type)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
result: List[ConstraintBase] = [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
]
|
||||
|
||||
if isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
assert isinstance(self.index.value, int)
|
||||
|
||||
if self.index.value < 0 or sa_len.value <= self.index.value:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return result
|
||||
|
||||
# We special case tuples to allow for ease of use to the programmer
|
||||
# e.g. rather than having to do `fst a` and `snd a` and only have to-sized tuples
|
||||
# we use a[0] and a[1] and allow for a[2] and on.
|
||||
tp_args = prelude.tuple_.did_construct(exp_type)
|
||||
if tp_args is not None:
|
||||
if not isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
return Error('Must index with literal')
|
||||
|
||||
if not isinstance(self.index.value, int):
|
||||
return Error('Must index with integer literal')
|
||||
|
||||
if self.index.value < 0 or len(tp_args) <= self.index.value:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment=f'Tuple subscript index {self.index.value}'),
|
||||
SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
|
||||
]
|
||||
|
||||
if exp_type is prelude.bytes_:
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: bytes -> u32 -> u8'),
|
||||
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
]
|
||||
|
||||
return Error(f'{exp_type.name} cannot be subscripted')
|
||||
try:
|
||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
||||
except types.NoRouteForTypeException:
|
||||
return Error(f'{exp_type.name} cannot be subscripted')
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
return (
|
||||
|
||||
@ -124,12 +124,12 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.AccessStructMember):
|
||||
assert isinstance(inp.struct_type3, type3types.Type3) # When does this happen?
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None # FIXME: See test_struct.py::test_struct_not_accessible
|
||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible
|
||||
|
||||
mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||
|
||||
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
|
||||
yield SameTypeConstraint(st_args[inp.member], phft,
|
||||
yield SameTypeConstraint(mem_typ, phft,
|
||||
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||
return
|
||||
|
||||
|
||||
@ -4,15 +4,34 @@ Contains the final types for use in Phasm, as well as construtors.
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Generic,
|
||||
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 __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
||||
|
||||
class Type3(KindArgument):
|
||||
"""
|
||||
Base class for the type3 types
|
||||
@ -20,18 +39,25 @@ class Type3(KindArgument):
|
||||
(Having a separate name makes it easier to distinguish from
|
||||
Python's Type)
|
||||
"""
|
||||
__slots__ = ('name', )
|
||||
__slots__ = ('name', 'application', )
|
||||
|
||||
name: str
|
||||
"""
|
||||
The name of the string, as parsed and outputted by codestyle.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
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({repr(self.name)})'
|
||||
return f'Type3({self.name!r}, {self.application!r})'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
@ -57,6 +83,9 @@ class Type3(KindArgument):
|
||||
def __bool__(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
class TypeApplication_Nullary(TypeApplication_Base[None, None]):
|
||||
pass
|
||||
|
||||
class IntType3(KindArgument):
|
||||
"""
|
||||
Sometimes you can have an int on the type level, e.g. when using static arrays
|
||||
@ -93,20 +122,18 @@ class IntType3(KindArgument):
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.value)
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class TypeConstructor(Generic[T]):
|
||||
class TypeConstructor_Base[T]:
|
||||
"""
|
||||
Base class for type construtors
|
||||
"""
|
||||
__slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
|
||||
__slots__ = ('name', 'on_create', '_cache', )
|
||||
|
||||
name: str
|
||||
"""
|
||||
The name of the type constructor
|
||||
"""
|
||||
|
||||
on_create: Callable[[Type3], None]
|
||||
on_create: Callable[[T, Type3], None]
|
||||
"""
|
||||
Who to let know if a type is created
|
||||
"""
|
||||
@ -117,31 +144,26 @@ class TypeConstructor(Generic[T]):
|
||||
it should produce the exact same result.
|
||||
"""
|
||||
|
||||
_reverse_cache: dict[Type3, T]
|
||||
"""
|
||||
Sometimes we need to know the key that created a type.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
|
||||
def __init__(self, name: str, on_create: Callable[[T, Type3], None]) -> None:
|
||||
self.name = name
|
||||
self.on_create = on_create
|
||||
|
||||
self._cache = {}
|
||||
self._reverse_cache = {}
|
||||
|
||||
def make_name(self, key: T) -> str:
|
||||
"""
|
||||
Renders the type's name based on the given arguments
|
||||
"""
|
||||
raise NotImplementedError
|
||||
raise NotImplementedError('make_name', self)
|
||||
|
||||
def did_construct(self, typ: Type3) -> T | None:
|
||||
def make_application(self, key: T) -> TypeApplication_Base[Self, T]:
|
||||
"""
|
||||
Was the given type constructed by this constructor?
|
||||
Records how the type was constructed into type.
|
||||
|
||||
If so, which arguments where used?
|
||||
The type checker and compiler will need to know what
|
||||
arguments where made to construct the type.
|
||||
"""
|
||||
return self._reverse_cache.get(typ)
|
||||
raise NotImplementedError('make_application', self)
|
||||
|
||||
def construct(self, key: T) -> Type3:
|
||||
"""
|
||||
@ -150,22 +172,12 @@ class TypeConstructor(Generic[T]):
|
||||
"""
|
||||
result = self._cache.get(key, None)
|
||||
if result is None:
|
||||
self._cache[key] = result = Type3(self.make_name(key))
|
||||
self._reverse_cache[result] = key
|
||||
self.on_create(result)
|
||||
self._cache[key] = result = Type3(self.make_name(key), self.make_application(key))
|
||||
self.on_create(key, result)
|
||||
|
||||
return result
|
||||
|
||||
class TypeConstructor_Type(TypeConstructor[Type3]):
|
||||
"""
|
||||
Base class type constructors of kind: * -> *
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def __call__(self, arg: Type3) -> Type3:
|
||||
raise NotImplementedError
|
||||
|
||||
class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
|
||||
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
|
||||
"""
|
||||
Base class type constructors of kind: * -> Int -> *
|
||||
|
||||
@ -173,22 +185,34 @@ class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
|
||||
"""
|
||||
__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 TypeConstructor_TypeStar(TypeConstructor[Tuple[Type3, ...]]):
|
||||
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
|
||||
|
||||
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
|
||||
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
||||
return f'{key[0].name}[{key[1].value}]'
|
||||
@ -197,52 +221,75 @@ class TypeConstructor_Tuple(TypeConstructor_TypeStar):
|
||||
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
||||
return '(' + ', '.join(x.name for x in key) + ', )'
|
||||
|
||||
class TypeConstructor_Struct:
|
||||
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
||||
"""
|
||||
Base class for type construtors
|
||||
Constructs struct types
|
||||
"""
|
||||
__slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
|
||||
def make_application(self, key: tuple[tuple[str, Type3], ...]) -> 'TypeApplication_Struct':
|
||||
return TypeApplication_Struct(self, key)
|
||||
|
||||
name: str
|
||||
"""
|
||||
The name of the type constructor
|
||||
"""
|
||||
def make_name(self, key: tuple[tuple[str, Type3], ...]) -> str:
|
||||
return f'{self.name}(' + ', '.join(
|
||||
f'{n}: {t.name}'
|
||||
for n, t in key
|
||||
) + ')'
|
||||
|
||||
on_create: Callable[[Type3], None]
|
||||
"""
|
||||
Who to let know if a type is created
|
||||
"""
|
||||
|
||||
_cache: dict[str, Type3]
|
||||
"""
|
||||
When constructing a type with the same arguments,
|
||||
it should produce the exact same result.
|
||||
"""
|
||||
|
||||
_reverse_cache: dict[Type3, dict[str, Type3]]
|
||||
"""
|
||||
After construction you may need to look up the arguments
|
||||
used for making the type
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
|
||||
self.name = name
|
||||
self.on_create = on_create
|
||||
|
||||
self._cache = {}
|
||||
self._reverse_cache = {}
|
||||
|
||||
def did_construct(self, typ: Type3) -> dict[str, Type3] | None:
|
||||
def construct(self, key: T) -> Type3:
|
||||
"""
|
||||
Was the given type constructed by this constructor?
|
||||
|
||||
If so, which arguments where used?
|
||||
Constructs the type by applying the given arguments to this
|
||||
constructor.
|
||||
"""
|
||||
return self._reverse_cache.get(typ)
|
||||
|
||||
def __call__(self, name: str, args: dict[str, Type3]) -> Type3:
|
||||
result = Type3(name)
|
||||
self._reverse_cache[result] = args
|
||||
self.on_create(result)
|
||||
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))
|
||||
self.on_create(args, result)
|
||||
return result
|
||||
|
||||
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
||||
pass
|
||||
|
||||
class NoRouteForTypeException(Exception):
|
||||
pass
|
||||
|
||||
class TypeApplicationRouter[S, R]:
|
||||
"""
|
||||
Helper class to find a method based on a constructed type
|
||||
"""
|
||||
__slots__ = ('by_constructor', 'by_type', )
|
||||
|
||||
by_constructor: dict[Any, Callable[[S, Any], R]]
|
||||
"""
|
||||
Contains all the added routing functions for constructed types
|
||||
"""
|
||||
|
||||
by_type: dict[Type3, Callable[[S], R]]
|
||||
"""
|
||||
Contains all the added routing functions for constructed types
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.by_constructor = {}
|
||||
self.by_type = {}
|
||||
|
||||
def add_n(self, typ: Type3, helper: Callable[[S], R]) -> None:
|
||||
"""
|
||||
Lets you route to types that were not constructed
|
||||
|
||||
Also known types of kind *
|
||||
"""
|
||||
self.by_type[typ] = helper
|
||||
|
||||
def add(self, constructor: TypeConstructor_Base[T], helper: Callable[[S, T], R]) -> None:
|
||||
self.by_constructor[constructor] = helper
|
||||
|
||||
def __call__(self, arg0: S, typ: Type3) -> R:
|
||||
t_helper = self.by_type.get(typ)
|
||||
if t_helper is not None:
|
||||
return t_helper(arg0)
|
||||
|
||||
c_helper = self.by_constructor.get(typ.application.constructor)
|
||||
if c_helper is not None:
|
||||
return c_helper(arg0, typ.application.arguments)
|
||||
|
||||
raise NoRouteForTypeException(arg0, typ)
|
||||
|
||||
@ -4,7 +4,12 @@ from typing import Any, Generator, Iterable, List, TextIO, Union
|
||||
|
||||
from phasm import compiler, prelude
|
||||
from phasm.codestyle import phasm_render
|
||||
from phasm.runtime import calculate_alloc_size
|
||||
from phasm.runtime import (
|
||||
calculate_alloc_size,
|
||||
calculate_alloc_size_static_array,
|
||||
calculate_alloc_size_struct,
|
||||
calculate_alloc_size_tuple,
|
||||
)
|
||||
from phasm.type3 import types as type3types
|
||||
|
||||
from . import runners
|
||||
@ -79,30 +84,11 @@ class Suite:
|
||||
wasm_args.append(arg)
|
||||
continue
|
||||
|
||||
if arg_typ is prelude.bytes_:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
try:
|
||||
adr = ALLOCATE_MEMORY_STORED_ROUTER((runner, arg), arg_typ)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
sa_args = prelude.static_array.did_construct(arg_typ)
|
||||
if sa_args is not None:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(arg_typ)
|
||||
if tp_args is not None:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
st_args = prelude.struct.did_construct(arg_typ)
|
||||
if st_args is not None:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
raise NotImplementedError(arg_typ, arg)
|
||||
except type3types.NoRouteForTypeException:
|
||||
raise NotImplementedError(arg_typ, arg)
|
||||
|
||||
write_header(sys.stderr, 'Memory (pre run)')
|
||||
runner.interpreter_dump_memory(sys.stderr)
|
||||
@ -141,103 +127,89 @@ def _write_memory_stored_value(
|
||||
val_typ: type3types.Type3,
|
||||
val: Any,
|
||||
) -> int:
|
||||
if val_typ is prelude.bytes_:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
try:
|
||||
adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
except type3types.NoRouteForTypeException:
|
||||
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
||||
runner.interpreter_write_memory(adr, to_write)
|
||||
return len(to_write)
|
||||
|
||||
st_args = prelude.struct.did_construct(val_typ)
|
||||
if st_args is not None:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
def _allocate_memory_stored_bytes(attrs: tuple[runners.RunnerBase, bytes]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
sa_args = prelude.static_array.did_construct(val_typ)
|
||||
if sa_args is not None:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
assert isinstance(val, bytes)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(val_typ)
|
||||
if tp_args is not None:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
adr = runner.call('stdlib.types.__alloc_bytes__', len(val))
|
||||
assert isinstance(adr, int)
|
||||
|
||||
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
||||
runner.interpreter_write_memory(adr, to_write)
|
||||
return len(to_write)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
runner.interpreter_write_memory(adr + 4, val)
|
||||
return adr
|
||||
|
||||
def _allocate_memory_stored_value(
|
||||
runner: runners.RunnerBase,
|
||||
val_typ: type3types.Type3,
|
||||
val: Any
|
||||
) -> int:
|
||||
if val_typ is prelude.bytes_:
|
||||
assert isinstance(val, bytes)
|
||||
def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
adr = runner.call('stdlib.types.__alloc_bytes__', len(val))
|
||||
assert isinstance(adr, int)
|
||||
assert isinstance(val, tuple)
|
||||
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
runner.interpreter_write_memory(adr + 4, val)
|
||||
return adr
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
sa_args = prelude.static_array.did_construct(val_typ)
|
||||
if sa_args is not None:
|
||||
assert isinstance(val, tuple)
|
||||
alloc_size = calculate_alloc_size_static_array(False, sa_args)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
tuple_len = sa_len.value
|
||||
assert tuple_len == len(val)
|
||||
|
||||
alloc_size = calculate_alloc_size(val_typ)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
offset = adr
|
||||
for val_el_val in val:
|
||||
offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val)
|
||||
return adr
|
||||
|
||||
tuple_len = sa_len.value
|
||||
assert tuple_len == len(val)
|
||||
def _allocate_memory_stored_struct(attrs: tuple[runners.RunnerBase, Any], st_args: tuple[tuple[str, type3types.Type3], ...]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
offset = adr
|
||||
for val_el_val in val:
|
||||
offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val)
|
||||
return adr
|
||||
assert isinstance(val, dict)
|
||||
|
||||
val_el_typ: type3types.Type3
|
||||
alloc_size = calculate_alloc_size_struct(False, st_args)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(val_typ)
|
||||
if tp_args is not None:
|
||||
assert isinstance(val, tuple)
|
||||
offset = adr
|
||||
for val_el_name, val_el_typ in st_args:
|
||||
assert val_el_name in val, f'Missing key value {val_el_name}'
|
||||
val_el_val = val.pop(val_el_name)
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
|
||||
alloc_size = calculate_alloc_size(val_typ)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
assert not val, f'Additional values: {list(val)!r}'
|
||||
|
||||
assert len(val) == len(tp_args)
|
||||
return adr
|
||||
|
||||
offset = adr
|
||||
for val_el_val, val_el_typ in zip(val, tp_args, strict=True):
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
return adr
|
||||
def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args: tuple[type3types.Type3, ...]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
st_args = prelude.struct.did_construct(val_typ)
|
||||
if st_args is not None:
|
||||
assert isinstance(val, dict)
|
||||
assert isinstance(val, tuple)
|
||||
|
||||
alloc_size = calculate_alloc_size(val_typ)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
alloc_size = calculate_alloc_size_tuple(False, tp_args)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
|
||||
assert list(val.keys()) == list(st_args)
|
||||
assert len(val) == len(tp_args)
|
||||
|
||||
offset = adr
|
||||
for val_el_name, val_el_typ in st_args.items():
|
||||
val_el_val = val[val_el_name]
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
return adr
|
||||
offset = adr
|
||||
for val_el_val, val_el_typ in zip(val, tp_args, strict=True):
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
return adr
|
||||
|
||||
raise NotImplementedError(val_typ, val)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER = type3types.TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]()
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple)
|
||||
|
||||
def _load_memory_stored_returned_value(
|
||||
runner: runners.RunnerBase,
|
||||
@ -285,28 +257,9 @@ def _load_memory_stored_returned_value(
|
||||
assert isinstance(wasm_value, float), wasm_value
|
||||
return wasm_value
|
||||
|
||||
if ret_type3 is prelude.bytes_:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
|
||||
return _load_bytes_from_address(runner, ret_type3, wasm_value)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(ret_type3)
|
||||
if sa_args is not None:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
|
||||
return _load_static_array_from_address(runner, sa_args[0], sa_args[1], wasm_value)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(ret_type3)
|
||||
if tp_args is not None:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
|
||||
return _load_tuple_from_address(runner, tp_args, wasm_value)
|
||||
|
||||
st_args = prelude.struct.did_construct(ret_type3)
|
||||
if st_args is not None:
|
||||
return _load_struct_from_address(runner, st_args, wasm_value)
|
||||
|
||||
raise NotImplementedError(ret_type3, wasm_value)
|
||||
return LOAD_FROM_ADDRESS_ROUTER((runner, wasm_value), ret_type3)
|
||||
|
||||
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
||||
if typ is prelude.u8:
|
||||
@ -343,39 +296,19 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
|
||||
assert len(inp) == 8
|
||||
return struct.unpack('<d', inp)[0]
|
||||
|
||||
if typ is prelude.bytes_:
|
||||
# Note: For bytes, inp should contain a 4 byte pointer
|
||||
assert len(inp) == 4
|
||||
adr = struct.unpack('<I', inp)[0]
|
||||
|
||||
return _load_bytes_from_address(runner, typ, adr)
|
||||
|
||||
if (prelude.InternalPassAsPointer, (typ, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
# Note: For applied types, inp should contain a 4 byte pointer
|
||||
assert len(inp) == 4
|
||||
adr = struct.unpack('<I', inp)[0]
|
||||
|
||||
sa_args = prelude.static_array.did_construct(typ)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
return _load_static_array_from_address(runner, sa_type, sa_len, adr)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(typ)
|
||||
if tp_args is not None:
|
||||
return _load_tuple_from_address(runner, tp_args, adr)
|
||||
|
||||
st_args = prelude.struct.did_construct(typ)
|
||||
if st_args is not None:
|
||||
# Note: For structs, inp should contain a 4 byte pointer
|
||||
assert len(inp) == 4
|
||||
adr = struct.unpack('<I', inp)[0]
|
||||
|
||||
return _load_struct_from_address(runner, st_args, adr)
|
||||
return LOAD_FROM_ADDRESS_ROUTER((runner, adr), typ)
|
||||
|
||||
raise NotImplementedError(typ, inp)
|
||||
|
||||
def _load_bytes_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> bytes:
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} {typ:s}\n')
|
||||
def _load_bytes_from_address(attrs: tuple[runners.RunnerBase, int]) -> bytes:
|
||||
runner, adr = attrs
|
||||
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} bytes\n')
|
||||
read_bytes = runner.interpreter_read_memory(adr, 4)
|
||||
bytes_len, = struct.unpack('<I', read_bytes)
|
||||
|
||||
@ -388,7 +321,10 @@ def _split_read_bytes(all_bytes: bytes, split_sizes: Iterable[int]) -> Generator
|
||||
yield all_bytes[offset:offset + size]
|
||||
offset += size
|
||||
|
||||
def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3types.Type3, len_typ: type3types.IntType3, adr: int) -> Any:
|
||||
def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> Any:
|
||||
runner, adr = attrs
|
||||
sub_typ, len_typ = sa_args
|
||||
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} {sub_typ:s} {len_typ:s}\n')
|
||||
|
||||
sa_len = len_typ.value
|
||||
@ -403,37 +339,42 @@ def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3ty
|
||||
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
|
||||
)
|
||||
|
||||
def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3types.Type3, ...], adr: int) -> Any:
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(typ_args)}\n')
|
||||
def _load_struct_from_address(attrs: tuple[runners.RunnerBase, int], st_args: tuple[tuple[str, type3types.Type3], ...]) -> dict[str, Any]:
|
||||
runner, adr = attrs
|
||||
|
||||
arg_sizes = [
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in typ_args
|
||||
]
|
||||
|
||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||
|
||||
return tuple(
|
||||
_unpack(runner, arg_typ, arg_bytes)
|
||||
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
)
|
||||
|
||||
def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, type3types.Type3], adr: int) -> Any:
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} struct {list(st_args)}\n')
|
||||
|
||||
name_list = list(st_args)
|
||||
|
||||
typ_list = list(st_args.values())
|
||||
assert len(typ_list) == len(st_args)
|
||||
|
||||
arg_sizes = [
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in typ_list
|
||||
for _, x in st_args
|
||||
]
|
||||
|
||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||
|
||||
return {
|
||||
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
||||
for arg_name, arg_typ, arg_bytes in zip(name_list, typ_list, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
for (arg_name, arg_typ, ), arg_bytes in zip(st_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
}
|
||||
|
||||
def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tuple[type3types.Type3, ...]) -> Any:
|
||||
runner, adr = attrs
|
||||
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(tp_args)}\n')
|
||||
|
||||
arg_sizes = [
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in tp_args
|
||||
]
|
||||
|
||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||
|
||||
return tuple(
|
||||
_unpack(runner, arg_typ, arg_bytes)
|
||||
for arg_typ, arg_bytes in zip(tp_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
)
|
||||
|
||||
LOAD_FROM_ADDRESS_ROUTER = type3types.TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]()
|
||||
LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address)
|
||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address)
|
||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)
|
||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address)
|
||||
|
||||
30
tests/integration/test_lang/test_bytes.py
Normal file
30
tests/integration/test_lang/test_bytes.py
Normal file
@ -0,0 +1,30 @@
|
||||
import pytest
|
||||
|
||||
from ..helpers import Suite
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_bytes_export_constant():
|
||||
code_py = """
|
||||
CONSTANT: bytes = b'Hello'
|
||||
|
||||
@exported
|
||||
def testEntry() -> bytes:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert b"Hello" == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_bytes_export_instantiation():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> bytes:
|
||||
return b'Hello'
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert b"Hello" == result.returned_value
|
||||
@ -30,3 +30,29 @@ def testEntry() -> i32:
|
||||
|
||||
with pytest.raises(Type3Exception, match='Member count mismatch'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_static_array_export_constant():
|
||||
code_py = """
|
||||
CONSTANT: u8[3] = (1, 2, 3, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> u8[3]:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (1, 2, 3) == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_static_array_export_instantiation():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> u8[3]:
|
||||
return (1, 2, 3, )
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (1, 2, 3) == result.returned_value
|
||||
|
||||
@ -112,3 +112,35 @@ def testEntry(x: u8) -> u8:
|
||||
|
||||
with pytest.raises(Type3Exception, match='u8 is not struct'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_struct_export_constant():
|
||||
code_py = """
|
||||
class CheckedValue:
|
||||
value: i32
|
||||
|
||||
CONSTANT: CheckedValue = CheckedValue(32)
|
||||
|
||||
@exported
|
||||
def testEntry() -> CheckedValue:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert {"value": 32} == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_struct_export_instantiation():
|
||||
code_py = """
|
||||
class CheckedValue:
|
||||
value: i32
|
||||
|
||||
@exported
|
||||
def testEntry() -> CheckedValue:
|
||||
return CheckedValue(32)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert {"value": 32} == result.returned_value
|
||||
|
||||
@ -23,6 +23,23 @@ def testEntry(f: {type_}) -> u8:
|
||||
|
||||
assert exp_result == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||
('(u8, u8, u8, )', (45, 46, 47), 47, ),
|
||||
('u8[5]', (45, 46, 47, 48, 49), 47, ),
|
||||
('bytes', b'This is a test', 105)
|
||||
])
|
||||
def test_subscript_2(type_, in_put, exp_result):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry(f: {type_}) -> u8:
|
||||
return f[2]
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code(in_put)
|
||||
|
||||
assert exp_result == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||
('(u8, u8, )', (45, 46), 45, ),
|
||||
|
||||
@ -70,3 +70,29 @@ CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
||||
|
||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_tuple_export_constant():
|
||||
code_py = """
|
||||
CONSTANT: (u32, u8, u8, ) = (4000, 20, 20, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> (u32, u8, u8, ):
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (4000, 20, 20, ) == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_tuple_export_instantiation():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> (u32, u8, u8, ):
|
||||
return (4000, 20, 20, )
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (4000, 20, 20, ) == result.returned_value
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user