diff --git a/phasm/codestyle.py b/phasm/codestyle.py index cb3cdc8..be85381 100644 --- a/phasm/codestyle.py +++ b/phasm/codestyle.py @@ -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 diff --git a/phasm/compiler.py b/phasm/compiler.py index 3ad79df..8f91a93 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -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 diff --git a/phasm/ourlang.py b/phasm/ourlang.py index 2c0a4d9..1759202 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -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 diff --git a/phasm/parser.py b/phasm/parser.py index 227d84a..f160e81 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -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 diff --git a/phasm/runtime.py b/phasm/runtime.py index 45019da..9a7ae9f 100644 --- a/phasm/runtime.py +++ b/phasm/runtime.py @@ -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}') diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index c630def..09ace34 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -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: diff --git a/phasm/type3/entry.py b/phasm/type3/entry.py index 0e0fda8..2bf05c7 100644 --- a/phasm/type3/entry.py +++ b/phasm/type3/entry.py @@ -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): diff --git a/phasm/type3/placeholders.py b/phasm/type3/placeholders.py new file mode 100644 index 0000000..6821eac --- /dev/null +++ b/phasm/type3/placeholders.py @@ -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] diff --git a/phasm/type3/types.py b/phasm/type3/types.py index e14778c..604a39d 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -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. +""" diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index d413b87..c710957 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -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(' 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(' 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)