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:
parent
d6b483581b
commit
c9850e6638
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}')
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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):
|
||||
|
||||
67
phasm/type3/placeholders.py
Normal file
67
phasm/type3/placeholders.py
Normal 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]
|
||||
@ -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.
|
||||
"""
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user