Various cleanup to type system

- Make struct into a type constuctor
- Rework placeholders
- Got rid of 'PrimitiveType' as a concept

Still in idea form [skip-ci]
This commit is contained in:
Johan B.W. de Vries 2025-04-21 12:32:47 +02:00
parent d6b483581b
commit c9850e6638
10 changed files with 335 additions and 304 deletions

View File

@ -6,8 +6,8 @@ It's intented to be a "any color, as long as it's black" kind of renderer
from typing import Generator
from . import ourlang
from .type3 import placeholders as type3placeholders
from .type3 import types as type3types
from .type3.types import TYPE3_ASSERTION_ERROR, Type3, Type3OrPlaceholder
def phasm_render(inp: ourlang.Module) -> str:
@ -18,11 +18,12 @@ def phasm_render(inp: ourlang.Module) -> str:
Statements = Generator[str, None, None]
def type3(inp: Type3OrPlaceholder) -> str:
def type3(inp: type3placeholders.Type3OrPlaceholder) -> str:
"""
Render: type's name
"""
assert isinstance(inp, Type3), TYPE3_ASSERTION_ERROR
if isinstance(inp, type3placeholders.PlaceholderForType):
raise NotImplementedError
if inp is type3types.none:
return 'None'
@ -33,8 +34,11 @@ def struct_definition(inp: ourlang.StructDefinition) -> str:
"""
Render: TypeStruct's definition
"""
st_args = type3types.struct.did_construct(inp.struct_type3)
assert st_args is not None
result = f'class {inp.struct_type3.name}:\n'
for mem, typ in inp.struct_type3.members.items():
for mem, typ in st_args.items():
result += f' {mem}: {type3(typ)}\n'
return result

View File

@ -8,6 +8,7 @@ from . import codestyle, ourlang, wasm
from .runtime import calculate_alloc_size, calculate_member_offset
from .stdlib import alloc as stdlib_alloc
from .stdlib import types as stdlib_types
from .type3 import placeholders as type3placeholders
from .type3 import typeclasses as type3classes
from .type3 import types as type3types
from .wasmgenerator import Generator as WasmGenerator
@ -241,14 +242,14 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module:
"""
return module(inp)
def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
def type3(inp: type3placeholders.Type3OrPlaceholder) -> wasm.WasmType:
"""
Compile: type
Types are used for example in WebAssembly function parameters
and return types.
"""
assert isinstance(inp, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(inp, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp == type3types.none:
return wasm.WasmTypeNone()
@ -291,10 +292,6 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
# And pointers are i32
return wasm.WasmTypeInt32()
if isinstance(inp, type3types.StructType3):
# Structs are passed as pointer, which are i32
return wasm.WasmTypeInt32()
if type3classes.InternalPassAsPointer in inp.classes:
return wasm.WasmTypeInt32()
@ -304,9 +301,9 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
"""
Compile: Instantiation (allocation) of a tuple
"""
assert isinstance(inp.type3, type3types.PrimitiveType3)
assert isinstance(inp.type3, type3types.Type3)
args: list[type3types.PrimitiveType3] = []
args: list[type3types.Type3] = []
sa_args = type3types.static_array.did_construct(inp.type3)
if sa_args is not None:
@ -322,7 +319,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
comment_elements = ''
for element in inp.elements:
assert isinstance(element.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(element.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
comment_elements += f'{element.type3.name}, '
tmp_var = wgn.temp_var_i32('tuple_adr')
@ -336,19 +333,17 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
# Store each element individually
offset = 0
for element, exp_type3 in zip(inp.elements, args):
if isinstance(exp_type3, type3types.PlaceholderForType):
if isinstance(exp_type3, type3placeholders.PlaceholderForType):
assert exp_type3.resolve_as is not None
assert isinstance(exp_type3.resolve_as, type3types.PrimitiveType3)
assert isinstance(exp_type3.resolve_as, type3types.Type3)
exp_type3 = exp_type3.resolve_as
assert element.type3 == exp_type3
if type3classes.InternalPassAsPointer in exp_type3.classes:
mtyp = 'i32'
elif isinstance(exp_type3, type3types.StructType3):
mtyp = 'i32'
else:
assert isinstance(exp_type3, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name]
wgn.add_statement('nop', comment='PRE')
@ -371,7 +366,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
raise Exception
if isinstance(inp, ourlang.ConstantPrimitive):
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.type3 in (type3types.i8, type3types.u8, ):
# No native u8 type - treat as i32, with caution
@ -412,34 +407,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
return
if isinstance(inp.variable, ourlang.ModuleConstantDef):
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if type3classes.InternalPassAsPointer in inp.type3.classes:
# FIXME: Artifact from older version
assert isinstance(inp.variable.constant, ourlang.ConstantTuple)
assert isinstance(inp.variable.constant, (ourlang.ConstantTuple, ourlang.ConstantStruct, ))
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
if inp.type3 is type3types.bytes_:
assert isinstance(inp.variable.constant, ourlang.ConstantBytes)
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
if isinstance(inp.type3, type3types.StructType3):
assert isinstance(inp.variable.constant, ourlang.ConstantStruct)
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
if isinstance(inp.type3, type3types.PrimitiveType3):
if isinstance(inp.type3, type3types.Type3):
expression(wgn, inp.variable.constant)
return
@ -451,7 +430,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
expression(wgn, inp.left)
expression(wgn, inp.right)
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
type_var_map: Dict[type3classes.TypeVariable, type3types.Type3] = {}
@ -460,7 +439,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
# Fixed type, not part of the lookup requirements
continue
assert isinstance(arg_expr.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
type_var_map[type_var] = arg_expr.type3
instance_key = ','.join(
@ -478,7 +457,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
if isinstance(inp, ourlang.UnaryOp):
expression(wgn, inp.right)
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.type3 == type3types.u32:
if inp.operator == 'len':
@ -506,7 +485,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
# Fixed type, not part of the lookup requirements
continue
assert isinstance(arg_expr.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
type_var_map[type_var] = arg_expr.type3
instance_key = ','.join(
@ -529,7 +508,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
return
if isinstance(inp, ourlang.Subscript):
assert isinstance(inp.varref.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(inp.varref.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.varref.type3 is type3types.bytes_:
expression(wgn, inp.varref)
@ -537,7 +516,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.call(stdlib_types.__subscript_bytes__)
return
assert isinstance(inp.varref.type3, type3types.PrimitiveType3)
assert isinstance(inp.varref.type3, type3types.Type3)
sa_args = type3types.static_array.did_construct(inp.varref.type3)
if sa_args is not None:
@ -563,7 +542,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.i32.mul()
wgn.i32.add()
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load')
@ -576,20 +555,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
offset = 0
for el_type in tp_args[0:inp.index.value]:
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
offset += calculate_alloc_size(el_type)
el_type = tp_args[inp.index.value]
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
expression(wgn, inp.varref)
if isinstance(el_type, type3types.StructType3):
mtyp = 'i32'
elif type3classes.InternalPassAsPointer in el_type.classes:
if type3classes.InternalPassAsPointer in el_type.classes:
mtyp = 'i32'
else:
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
@ -598,12 +575,19 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
raise NotImplementedError(expression, inp, inp.varref.type3)
if isinstance(inp, ourlang.AccessStructMember):
assert isinstance(inp.struct_type3.members[inp.member], type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[inp.struct_type3.members[inp.member].name]
assert isinstance(inp.struct_type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
st_args = type3types.struct.did_construct(inp.struct_type3)
assert st_args is not None
member_type = st_args[inp.member]
assert isinstance(member_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
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, inp.member
inp.struct_type3.name, st_args, inp.member
)))
return
@ -617,7 +601,7 @@ def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
"""
Compile: Fold expression
"""
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.iter.type3 is not type3types.bytes_:
raise NotImplementedError(expression_fold, inp, inp.iter.type3)
@ -848,7 +832,7 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
data_list: List[bytes] = []
for constant in block.data:
assert isinstance(constant.type3, type3types.Type3), (id(constant), type3types.TYPE3_ASSERTION_ERROR)
assert isinstance(constant.type3, type3types.Type3), (id(constant), type3placeholders.TYPE3_ASSERTION_ERROR)
if isinstance(constant, ourlang.ConstantMemoryStored) and block is not constant.data_block:
# It's stored in a different block
@ -978,6 +962,9 @@ def module(inp: ourlang.Module) -> wasm.Module:
return result
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
st_args = type3types.struct.did_construct(inp.struct_type3)
assert st_args is not None
tmp_var = wgn.temp_var_i32('struct_adr')
# Allocated the required amounts of bytes in memory
@ -986,12 +973,10 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
wgn.local.set(tmp_var)
# Store each member individually
for memname, mtyp3 in inp.struct_type3.members.items():
for memname, mtyp3 in st_args.items():
mtyp: Optional[str]
if type3classes.InternalPassAsPointer in mtyp3.classes:
mtyp = 'i32'
elif isinstance(mtyp3, type3types.StructType3):
mtyp = 'i32'
else:
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
@ -1001,7 +986,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
wgn.local.get(tmp_var)
wgn.add_statement('local.get', f'${memname}')
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(calculate_member_offset(
inp.struct_type3, memname
inp.struct_type3.name, st_args, memname
)))
# Return the allocated address

View File

@ -8,7 +8,8 @@ from typing_extensions import Final
from .type3 import typeclasses as type3typeclasses
from .type3 import types as type3types
from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
from .type3.types import Type3
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
@ -214,10 +215,10 @@ class AccessStructMember(Expression):
__slots__ = ('varref', 'struct_type3', 'member', )
varref: VariableReference
struct_type3: StructType3
struct_type3: Type3OrPlaceholder
member: str
def __init__(self, varref: VariableReference, struct_type3: StructType3, member: str) -> None:
def __init__(self, varref: VariableReference, struct_type3: Type3OrPlaceholder, member: str) -> None:
super().__init__()
self.varref = varref
@ -335,10 +336,10 @@ class StructDefinition:
"""
__slots__ = ('struct_type3', 'lineno', )
struct_type3: StructType3
struct_type3: Type3
lineno: int
def __init__(self, struct_type3: StructType3, lineno: int) -> None:
def __init__(self, struct_type3: Type3, lineno: int) -> None:
self.struct_type3 = struct_type3
self.lineno = lineno
@ -351,14 +352,16 @@ class StructConstructor(Function):
"""
__slots__ = ('struct_type3', )
struct_type3: StructType3
struct_type3: Type3
def __init__(self, struct_type3: StructType3) -> None:
def __init__(self, struct_type3: Type3) -> None:
super().__init__(f'@{struct_type3.name}@__init___@', -1)
self.returns_type3 = struct_type3
for mem, typ in struct_type3.members.items():
st_args = type3types.struct.did_construct(struct_type3)
assert st_args is not None
for mem, typ in st_args.items():
self.posonlyargs.append(FunctionParam(mem, typ, ))
self.struct_type3 = struct_type3

View File

@ -55,6 +55,20 @@ PRELUDE_METHODS = {
**type3typeclasses.NatNum.methods,
}
PRELUDE_TYPES: Dict[str, type3types.Type3] = {
'none': type3types.none,
'bool': type3types.bool_,
'u8': type3types.u8,
'u32': type3types.u32,
'u64': type3types.u64,
'i8': type3types.i8,
'i32': type3types.i32,
'i64': type3types.i64,
'f32': type3types.f32,
'f64': type3types.f64,
'bytes': type3types.bytes_,
}
def phasm_parse(source: str) -> Module:
"""
Public method for parsing Phasm code into a Phasm Module
@ -252,7 +266,7 @@ class OurVisitor:
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
return StructDefinition(type3types.StructType3(node.name, members), node.lineno)
return StructDefinition(type3types.struct(node.name, members, set()), node.lineno)
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
if not isinstance(node.target, ast.Name):
@ -570,9 +584,6 @@ class OurVisitor:
if not isinstance(varref, VariableReference):
_raise_static_error(node.value, 'Must refer to variable')
if not isinstance(varref.variable.type3, type3types.StructType3):
_raise_static_error(node.value, 'Must refer to struct')
return AccessStructMember(
varref,
varref.variable.type3,
@ -664,7 +675,7 @@ class OurVisitor:
raise NotImplementedError(f'{node.value} as constant')
def visit_type(self, module: Module, node: ast.expr) -> type3types.PrimitiveType3:
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
if isinstance(node, ast.Constant):
if node.value is None:
return type3types.none
@ -675,8 +686,8 @@ class OurVisitor:
if not isinstance(node.ctx, ast.Load):
_raise_static_error(node, 'Must be load context')
if node.id in type3types.LOOKUP_TABLE:
return type3types.LOOKUP_TABLE[node.id]
if node.id in PRELUDE_TYPES:
return PRELUDE_TYPES[node.id]
if node.id in module.struct_definitions:
return module.struct_definitions[node.id].struct_type3

View File

@ -17,18 +17,17 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
raise NotImplementedError # When does this happen?
if isinstance(typ, type3types.StructType3):
st_args = type3types.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 typ.members.values()
for x in st_args.values()
)
assert isinstance(typ, type3types.PrimitiveType3)
sa_args = type3types.static_array.did_construct(typ)
if sa_args is not None:
if is_member:
@ -47,25 +46,19 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
size = 0
for arg in tp_args:
assert not isinstance(arg, type3types.IntType3)
if isinstance(arg, type3types.PlaceholderForType):
assert isinstance(arg.resolve_as, type3types.PrimitiveType3)
arg = arg.resolve_as
size += calculate_alloc_size(arg, is_member=True)
return size
raise NotImplementedError(calculate_alloc_size, typ)
def calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int:
def calculate_member_offset(st_name: str, st_args: dict[str, type3types.Type3], needle: str) -> int:
result = 0
for mem, memtyp in struct_type3.members.items():
if member == mem:
for memnam, memtyp in st_args.items():
if needle == memnam:
return result
result += calculate_alloc_size(memtyp, is_member=True)
raise Exception(f'{member} not in {struct_type3}')
raise Exception(f'{needle} not in {st_name}')

View File

@ -6,7 +6,7 @@ These need to be resolved before the program can be compiled.
from typing import Dict, List, Optional, Tuple, Union
from .. import ourlang
from . import typeclasses, types
from . import placeholders, typeclasses, types
class Error:
@ -32,13 +32,13 @@ class RequireTypeSubstitutes:
typing of the program, so this constraint can be updated.
"""
SubstitutionMap = Dict[types.PlaceholderForType, types.Type3]
SubstitutionMap = Dict[placeholders.PlaceholderForType, types.Type3]
NewConstraintList = List['ConstraintBase']
CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes]
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, types.PlaceholderForType]]]
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, placeholders.PlaceholderForType]]]
class Context:
"""
@ -92,9 +92,9 @@ class SameTypeConstraint(ConstraintBase):
"""
__slots__ = ('type_list', )
type_list: List[types.Type3OrPlaceholder]
type_list: List[placeholders.Type3OrPlaceholder]
def __init__(self, *type_list: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
def __init__(self, *type_list: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
super().__init__(comment=comment)
assert len(type_list) > 1
@ -108,11 +108,11 @@ class SameTypeConstraint(ConstraintBase):
known_types.append(typ)
continue
if isinstance(typ, (types.PrimitiveType3, types.StructType3, )):
if isinstance(typ, (types.Type3, types.StructType3, )):
known_types.append(typ)
continue
if isinstance(typ, types.PlaceholderForType):
if isinstance(typ, placeholders.PlaceholderForType):
if typ.resolve_as is not None:
known_types.append(typ.resolve_as)
else:
@ -163,7 +163,7 @@ class SameTypeConstraint(ConstraintBase):
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
class TupleMatchConstraint(ConstraintBase):
def __init__(self, exp_type: types.Type3OrPlaceholder, args: List[types.Type3OrPlaceholder], comment: str):
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: List[placeholders.Type3OrPlaceholder], comment: str):
super().__init__(comment=comment)
self.exp_type = exp_type
@ -171,13 +171,13 @@ class TupleMatchConstraint(ConstraintBase):
def check(self) -> CheckResult:
exp_type = self.exp_type
if isinstance(exp_type, types.PlaceholderForType):
if isinstance(exp_type, placeholders.PlaceholderForType):
if exp_type.resolve_as is None:
return RequireTypeSubstitutes()
exp_type = exp_type.resolve_as
assert isinstance(exp_type, types.PrimitiveType3)
assert isinstance(exp_type, types.Type3)
sa_args = types.static_array.did_construct(exp_type)
if sa_args is not None:
@ -209,10 +209,10 @@ class CastableConstraint(ConstraintBase):
"""
__slots__ = ('from_type3', 'to_type3', )
from_type3: types.Type3OrPlaceholder
to_type3: types.Type3OrPlaceholder
from_type3: placeholders.Type3OrPlaceholder
to_type3: placeholders.Type3OrPlaceholder
def __init__(self, from_type3: types.Type3OrPlaceholder, to_type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
def __init__(self, from_type3: placeholders.Type3OrPlaceholder, to_type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
super().__init__(comment=comment)
self.from_type3 = from_type3
@ -220,14 +220,14 @@ class CastableConstraint(ConstraintBase):
def check(self) -> CheckResult:
ftyp = self.from_type3
if isinstance(ftyp, types.PlaceholderForType) and ftyp.resolve_as is not None:
if isinstance(ftyp, placeholders.PlaceholderForType) and ftyp.resolve_as is not None:
ftyp = ftyp.resolve_as
ttyp = self.to_type3
if isinstance(ttyp, types.PlaceholderForType) and ttyp.resolve_as is not None:
if isinstance(ttyp, placeholders.PlaceholderForType) and ttyp.resolve_as is not None:
ttyp = ttyp.resolve_as
if isinstance(ftyp, types.PlaceholderForType) or isinstance(ttyp, types.PlaceholderForType):
if isinstance(ftyp, placeholders.PlaceholderForType) or isinstance(ttyp, placeholders.PlaceholderForType):
return RequireTypeSubstitutes()
if ftyp is types.u8 and ttyp is types.u32:
@ -254,13 +254,13 @@ class MustImplementTypeClassConstraint(ConstraintBase):
__slots__ = ('type_class3', 'type3', )
type_class3: Union[str, typeclasses.Type3Class]
type3: types.Type3OrPlaceholder
type3: placeholders.Type3OrPlaceholder
DATA = {
'bytes': {'Foldable', 'Sized'},
}
def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
super().__init__(comment=comment)
self.type_class3 = type_class3
@ -268,10 +268,10 @@ class MustImplementTypeClassConstraint(ConstraintBase):
def check(self) -> CheckResult:
typ = self.type3
if isinstance(typ, types.PlaceholderForType) and typ.resolve_as is not None:
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
typ = typ.resolve_as
if isinstance(typ, types.PlaceholderForType):
if isinstance(typ, placeholders.PlaceholderForType):
return RequireTypeSubstitutes()
if isinstance(self.type_class3, typeclasses.Type3Class):
@ -301,12 +301,12 @@ class LiteralFitsConstraint(ConstraintBase):
"""
__slots__ = ('type3', 'literal', )
type3: types.Type3OrPlaceholder
type3: placeholders.Type3OrPlaceholder
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
def __init__(
self,
type3: types.Type3OrPlaceholder,
type3: placeholders.Type3OrPlaceholder,
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
comment: Optional[str] = None,
) -> None:
@ -330,7 +330,7 @@ class LiteralFitsConstraint(ConstraintBase):
'f64': None,
}
if isinstance(self.type3, types.PlaceholderForType):
if isinstance(self.type3, placeholders.PlaceholderForType):
if self.type3.resolve_as is None:
return RequireTypeSubstitutes()
@ -367,7 +367,7 @@ class LiteralFitsConstraint(ConstraintBase):
res: NewConstraintList
assert isinstance(self.type3, types.PrimitiveType3)
assert isinstance(self.type3, types.Type3)
tp_args = types.tuple_.did_construct(self.type3)
if tp_args is not None:
@ -457,12 +457,12 @@ class CanBeSubscriptedConstraint(ConstraintBase):
"""
__slots__ = ('ret_type3', 'type3', 'index', 'index_type3', )
ret_type3: types.Type3OrPlaceholder
type3: types.Type3OrPlaceholder
ret_type3: placeholders.Type3OrPlaceholder
type3: placeholders.Type3OrPlaceholder
index: ourlang.Expression
index_type3: types.Type3OrPlaceholder
index_type3: placeholders.Type3OrPlaceholder
def __init__(self, ret_type3: types.Type3OrPlaceholder, type3: types.Type3OrPlaceholder, index: ourlang.Expression, comment: Optional[str] = None) -> None:
def __init__(self, ret_type3: placeholders.Type3OrPlaceholder, type3: placeholders.Type3OrPlaceholder, index: ourlang.Expression, comment: Optional[str] = None) -> None:
super().__init__(comment=comment)
self.ret_type3 = ret_type3
@ -472,13 +472,13 @@ class CanBeSubscriptedConstraint(ConstraintBase):
def check(self) -> CheckResult:
exp_type = self.type3
if isinstance(exp_type, types.PlaceholderForType):
if isinstance(exp_type, placeholders.PlaceholderForType):
if exp_type.resolve_as is None:
return RequireTypeSubstitutes()
exp_type = exp_type.resolve_as
assert isinstance(exp_type, types.PrimitiveType3)
assert isinstance(exp_type, types.Type3)
sa_args = types.static_array.did_construct(exp_type)
if sa_args is not None:

View File

@ -12,14 +12,11 @@ from .constraints import (
SubstitutionMap,
)
from .constraintsgenerator import phasm_type3_generate_constraints
from .types import (
IntType3,
from .placeholders import (
PlaceholderForType,
PrimitiveType3,
StructType3,
Type3,
Type3OrPlaceholder,
)
from .types import Type3
MAX_RESTACK_COUNT = 100
@ -143,7 +140,7 @@ def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintB
print('- ' + txt.format(**act_fmt))
def get_printable_type_name(inp: Type3OrPlaceholder, placeholder_id_map: Dict[int, str]) -> str:
if isinstance(inp, (PrimitiveType3, StructType3, IntType3, )):
if isinstance(inp, Type3):
return inp.name
if isinstance(inp, PlaceholderForType):

View File

@ -0,0 +1,67 @@
"""
Contains the placeholder for types for use in Phasm.
These are temporary while the compiler is calculating all the types and validating them.
"""
from typing import Any, Iterable, List, Optional, Protocol, Union
from .types import Type3
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 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]

View File

@ -1,21 +1,13 @@
"""
Contains the final types for use in Phasm
These are actual, instantiated types; not the abstract types that the
constraint generator works with.
Contains the final types for use in Phasm, as well as construtors.
"""
from typing import (
Any,
Dict,
Generic,
Iterable,
List,
Optional,
Protocol,
Set,
Tuple,
TypeVar,
Union,
)
from .typeclasses import (
@ -31,21 +23,16 @@ from .typeclasses import (
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
"""
class KindArgument:
pass
type3: 'Type3OrPlaceholder'
"""
The type to update
"""
class Type3:
class Type3(KindArgument):
"""
Base class for the type3 types
(Having a separate name makes it easier to distinguish from
Python's Type)
"""
__slots__ = ('name', 'classes', )
@ -84,9 +71,6 @@ class Type3:
return str(self)
def __eq__(self, other: Any) -> bool:
if isinstance(other, PlaceholderForType):
return False
if not isinstance(other, Type3):
raise NotImplementedError
@ -101,19 +85,15 @@ class Type3:
def __bool__(self) -> bool:
raise NotImplementedError
class PrimitiveType3(Type3):
class IntType3(KindArgument):
"""
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
Sometimes you can have an int on the type level, 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 -> *
[1.0, 1.2] :: f32[2] :: * -> Int -> *
That is to say, you can create a static array of size two with each element
a f32 using f32[2].
"""
__slots__ = ('value', )
@ -121,15 +101,13 @@ class IntType3(Type3):
value: int
def __init__(self, value: int) -> None:
super().__init__(str(value), [])
self.value = value
def __eq__(self, other: Any) -> bool:
if isinstance(other, IntType3):
return self.value == other.value
if isinstance(other, Type3):
if isinstance(other, KindArgument):
return False
raise NotImplementedError
@ -137,53 +115,6 @@ class IntType3(Type3):
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]):
@ -207,13 +138,13 @@ class TypeConstructor(Generic[T]):
The type classes that the constructed types implement
"""
_cache: dict[T, PrimitiveType3]
_cache: dict[T, Type3]
"""
When constructing a type with the same arguments,
it should produce the exact same result.
"""
_reverse_cache: dict[PrimitiveType3, T]
_reverse_cache: dict[Type3, T]
"""
Sometimes we need to know the key that created a type.
"""
@ -227,115 +158,165 @@ class TypeConstructor(Generic[T]):
self._reverse_cache = {}
def make_name(self, key: T) -> str:
"""
Renders the type's name based on the given arguments
"""
raise NotImplementedError
def did_construct(self, typ: PrimitiveType3) -> T | None:
def did_construct(self, typ: Type3) -> T | None:
"""
Was the given type constructed by this constructor?
If so, which arguments where used?
"""
return self._reverse_cache.get(typ)
def construct(self, key: T) -> PrimitiveType3:
def construct(self, key: T) -> Type3:
"""
Constructs the type by applying the given arguments to this
constructor.
"""
result = self._cache.get(key, None)
if result is None:
self._cache[key] = result = PrimitiveType3(self.make_name(key), self.type_classes)
self._cache[key] = result = Type3(self.make_name(key), self.type_classes)
self._reverse_cache[result] = key
return result
class TypeConstructor_Type(TypeConstructor[PrimitiveType3]):
class TypeConstructor_Type(TypeConstructor[Type3]):
"""
Base class type constructors of kind: * -> *
"""
__slots__ = ()
def __call__(self, arg: PrimitiveType3) -> PrimitiveType3:
def __call__(self, arg: Type3) -> Type3:
raise NotImplementedError
class TypeConstructor_TypeInt(TypeConstructor[Tuple[PrimitiveType3, IntType3]]):
class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
"""
Base class type constructors of kind: * -> Int -> *
Notably, static array.
"""
__slots__ = ()
def make_name(self, key: Tuple[PrimitiveType3, IntType3]) -> str:
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
return f'{self.name} {key[0].name} {key[1].value}'
def __call__(self, arg0: PrimitiveType3, arg1: IntType3) -> PrimitiveType3:
def __call__(self, arg0: Type3, arg1: IntType3) -> Type3:
return self.construct((arg0, arg1))
class TypeConstructor_TypeStar(TypeConstructor[Tuple[PrimitiveType3, ...]]):
class TypeConstructor_TypeStar(TypeConstructor[Tuple[Type3, ...]]):
"""
Base class type constructors of variadic kind
Notably, tuple.
"""
def __call__(self, *args: PrimitiveType3) -> PrimitiveType3:
key: Tuple[PrimitiveType3, ...] = tuple(args)
def __call__(self, *args: Type3) -> Type3:
key: Tuple[Type3, ...] = tuple(args)
return self.construct(key)
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
def make_name(self, key: Tuple[PrimitiveType3, IntType3]) -> str:
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
return f'{key[0].name}[{key[1].value}]'
class TypeConstructor_Tuple(TypeConstructor_TypeStar):
def make_name(self, key: Tuple[PrimitiveType3, ...]) -> str:
def make_name(self, key: Tuple[Type3, ...]) -> str:
return '(' + ', '.join(x.name for x in key) + ', )'
class StructType3(PrimitiveType3):
class TypeConstructor_Struct:
"""
A Type3 struct with named members
Base class for type construtors
"""
__slots__ = ('name', 'members', )
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
name: str
"""
The structs fully qualified name
The name of the type constructor
"""
members: Dict[str, Type3]
classes: Set[Type3Class]
"""
The struct's field definitions
The type classes that this constructor implements
"""
def __init__(self, name: str, members: Dict[str, Type3]) -> None:
super().__init__(name, [])
type_classes: Set[Type3Class]
"""
The type classes that the constructed types implement
"""
_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, classes: Iterable[Type3Class], type_classes: Iterable[Type3Class]) -> None:
self.name = name
self.members = dict(members)
self.classes = set(classes)
self.type_classes = set(type_classes)
def __repr__(self) -> str:
return f'StructType3(repr({self.name}), repr({self.members}))'
self._cache = {}
self._reverse_cache = {}
none = PrimitiveType3('none', [])
def did_construct(self, typ: Type3) -> dict[str, Type3] | None:
"""
Was the given type constructed by this constructor?
If so, which arguments where used?
"""
return self._reverse_cache.get(typ)
def __call__(self, name: str, args: dict[str, Type3], classes: Set[Type3Class]) -> Type3:
result = self._cache.get(name)
if result is not None:
raise Exception('Duplicate struct name detected')
self._cache[name] = result = Type3(name, classes | self.type_classes)
self._reverse_cache[result] = args
return result
none = Type3('none', [])
"""
The none type, for when functions simply don't return anything. e.g., IO().
"""
bool_ = PrimitiveType3('bool', [])
bool_ = Type3('bool', [])
"""
The bool type, either True or False
Suffixes with an underscores, as it's a Python builtin
"""
u8 = PrimitiveType3('u8', [Bits, Eq, Ord])
u8 = Type3('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])
u32 = Type3('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])
u64 = Type3('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])
i8 = Type3('i8', [Eq, Ord])
"""
The signed 8-bit integer type.
@ -343,7 +324,7 @@ 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])
i32 = Type3('i32', [Eq, Integral, IntNum, NatNum, Ord])
"""
The unsigned 32-bit integer type.
@ -351,7 +332,7 @@ 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])
i64 = Type3('i64', [Eq, Integral, IntNum, NatNum, Ord])
"""
The unsigned 64-bit integer type.
@ -359,17 +340,17 @@ 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])
f32 = Type3('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])
f64 = Type3('f64', [Eq, Floating, Fractional, IntNum, NatNum, Ord])
"""
A 32-bits IEEE 754 float, of 64 bits width.
"""
bytes_ = PrimitiveType3('bytes', [])
bytes_ = Type3('bytes', [InternalPassAsPointer])
"""
This is a runtime-determined length piece of memory that can be indexed at runtime.
"""
@ -397,16 +378,10 @@ 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_,
}
struct = TypeConstructor_Struct('struct', [], [
InternalPassAsPointer,
])
"""
This is like a tuple, but each argument is named, so that developers
can get and set fields by name.
"""

View File

@ -5,6 +5,7 @@ from typing import Any, Generator, Iterable, List, TextIO, Union
from phasm import compiler
from phasm.codestyle import phasm_render
from phasm.runtime import calculate_alloc_size
from phasm.type3 import placeholders as type3placeholders
from phasm.type3 import typeclasses as type3classes
from phasm.type3 import types as type3types
@ -65,7 +66,7 @@ class Suite:
runner.interpreter_dump_memory(sys.stderr)
for arg, arg_typ in zip(args, func_args):
assert not isinstance(arg_typ, type3types.PlaceholderForType), \
assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
'Cannot call polymorphic function from outside'
if arg_typ in (type3types.u8, type3types.u32, type3types.u64, ):
@ -88,7 +89,7 @@ class Suite:
wasm_args.append(adr)
continue
assert isinstance(arg_typ, type3types.PrimitiveType3)
assert isinstance(arg_typ, type3types.Type3)
sa_args = type3types.static_array.did_construct(arg_typ)
if sa_args is not None:
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
@ -101,7 +102,8 @@ class Suite:
wasm_args.append(adr)
continue
if isinstance(arg_typ, type3types.StructType3):
st_args = type3types.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
@ -150,29 +152,27 @@ def _write_memory_stored_value(
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
return 4
if isinstance(val_typ, type3types.StructType3):
st_args = type3types.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
if isinstance(val_typ, type3types.PrimitiveType3):
sa_args = type3types.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
sa_args = type3types.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
tp_args = type3types.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
tp_args = type3types.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
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
runner.interpreter_write_memory(adr, to_write)
return len(to_write)
raise NotImplementedError(val_typ, val)
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
runner.interpreter_write_memory(adr, to_write)
return len(to_write)
def _allocate_memory_stored_value(
runner: runners.RunnerBase,
@ -189,7 +189,6 @@ def _allocate_memory_stored_value(
runner.interpreter_write_memory(adr + 4, val)
return adr
assert isinstance(val_typ, type3types.PrimitiveType3)
sa_args = type3types.static_array.did_construct(val_typ)
if sa_args is not None:
assert isinstance(val, tuple)
@ -224,12 +223,13 @@ def _allocate_memory_stored_value(
offset = adr
for val_el_val, val_el_typ in zip(val, tp_args):
assert not isinstance(val_el_typ, type3types.PlaceholderForType)
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
return adr
if isinstance(val_typ, type3types.StructType3):
st_args = type3types.struct.did_construct(val_typ)
if st_args is not None:
assert isinstance(val, dict)
alloc_size = calculate_alloc_size(val_typ)
@ -237,11 +237,11 @@ def _allocate_memory_stored_value(
assert isinstance(adr, int)
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
assert list(val.keys()) == list(val_typ.members.keys())
assert list(val.keys()) == list(st_args)
offset = adr
for val_el_name, val_el_typ in val_typ.members.items():
assert not isinstance(val_el_typ, type3types.PlaceholderForType)
for val_el_name, val_el_typ in st_args.items():
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
val_el_val = val[val_el_name]
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
@ -293,7 +293,7 @@ def _load_memory_stored_returned_value(
return _load_bytes_from_address(runner, ret_type3, wasm_value)
assert isinstance(ret_type3, type3types.PrimitiveType3) # Type hint
assert isinstance(ret_type3, type3types.Type3) # Type hint
sa_args = type3types.static_array.did_construct(ret_type3)
if sa_args is not None:
@ -307,8 +307,9 @@ def _load_memory_stored_returned_value(
return _load_tuple_from_address(runner, tp_args, wasm_value)
if isinstance(ret_type3, type3types.StructType3):
return _load_struct_from_address(runner, ret_type3, wasm_value)
st_args = type3types.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)
@ -359,7 +360,7 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
assert len(inp) == 4
adr = struct.unpack('<I', inp)[0]
assert isinstance(typ, type3types.PrimitiveType3)
assert isinstance(typ, type3types.Type3)
sa_args = type3types.static_array.did_construct(typ)
if sa_args is not None:
@ -370,12 +371,13 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
if tp_args is not None:
return _load_tuple_from_address(runner, tp_args, adr)
if isinstance(typ, type3types.StructType3):
st_args = type3types.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, typ, adr)
return _load_struct_from_address(runner, st_args, adr)
raise NotImplementedError(typ, inp)
@ -393,10 +395,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.PrimitiveType3, len_typ: type3types.IntType3, adr: int) -> Any:
def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3types.Type3, len_typ: type3types.IntType3, adr: int) -> Any:
sys.stderr.write(f'Reading 0x{adr:08x} {sub_typ:s} {len_typ:s}\n')
assert not isinstance(sub_typ, type3types.PlaceholderForType)
assert not isinstance(sub_typ, type3placeholders.PlaceholderForType)
assert isinstance(len_typ, type3types.IntType3)
sa_len = len_typ.value
@ -411,7 +413,7 @@ 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.PrimitiveType3, ...], adr: int) -> Any:
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')
arg_sizes = [
@ -426,19 +428,13 @@ def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3ty
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes))
)
def _load_struct_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> Any:
sys.stderr.write(f'Reading 0x{adr:08x} {typ:s}\n')
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')
assert isinstance(typ, type3types.StructType3)
name_list = list(st_args)
name_list = list(typ.members)
typ_list = [
x
for x in typ.members.values()
if not isinstance(x, type3types.PlaceholderForType)
]
assert len(typ_list) == len(typ.members)
typ_list = list(st_args.values())
assert len(typ_list) == len(st_args)
arg_sizes = [
calculate_alloc_size(x, is_member=True)