Compare commits

...

1 Commits

Author SHA1 Message Date
Johan B.W. de Vries
6231c6b279 Tuple instantiation works again
Typing is getting ugly again
2023-01-01 16:00:18 +01:00
7 changed files with 105 additions and 77 deletions

View File

@ -108,12 +108,16 @@ def expression(inp: ourlang.Expression) -> str:
if isinstance(inp.function, ourlang.StructConstructor):
return f'{inp.function.struct_type3.name}({args})'
# TODO: Broken after new type system
# if isinstance(inp.function, ourlang.TupleConstructor):
# return f'({args}, )'
return f'{inp.function.name}({args})'
if isinstance(inp, ourlang.TupleInstantiation):
args = ', '.join(
expression(arg)
for arg in inp.elements
)
return f'({args}, )'
if isinstance(inp, ourlang.Subscript):
varref = expression(inp.varref)
index = expression(inp.index)

View File

@ -80,6 +80,10 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
# Static Arrays are passed as pointer, which are i32
return wasm.WasmTypeInt32()
if inp.base == type3types.tuple:
# Tuples are passed as pointer, which are i32
return wasm.WasmTypeInt32()
raise NotImplementedError(type3, inp)
# Operators that work for i32, i64, f32, f64
@ -150,6 +154,45 @@ F64_OPERATOR_MAP = {
'/': 'div' # Division by zero is a trap and the program will panic
}
def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) -> None:
assert isinstance(inp.type3, type3types.AppliedType3)
assert inp.type3.base is type3types.tuple
assert len(inp.elements) == len(inp.type3.args)
comment_elements = ''
for element in inp.elements:
assert isinstance(element.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
comment_elements += f'{element.type3.name}, '
tmp_var = wgn.temp_var_i32('tuple_adr')
wgn.add_statement('nop', comment=f'tmp_var := ({comment_elements})')
# Allocated the required amounts of bytes in memory
wgn.i32.const(_calculate_alloc_size(inp.type3))
wgn.call(stdlib_alloc.__alloc__)
wgn.local.set(tmp_var)
# Store each element individually
offset = 0
for element, exp_type3 in zip(inp.elements, inp.type3.args):
if isinstance(exp_type3, type3types.PlaceholderForType):
assert exp_type3.resolve_as is not None
exp_type3 = exp_type3.resolve_as
assert element.type3 == exp_type3
assert isinstance(exp_type3, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name]
wgn.local.get(tmp_var)
expression(wgn, element)
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(offset))
offset += _calculate_alloc_size(exp_type3)
# Return the allocated address
wgn.local.get(tmp_var)
def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
"""
Compile: Any expression
@ -311,6 +354,10 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.add_statement('call', '${}'.format(inp.function.name))
return
if isinstance(inp, ourlang.TupleInstantiation):
tuple_instantiation(wgn, inp)
return
if isinstance(inp, ourlang.Subscript):
assert isinstance(inp.varref.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
@ -336,11 +383,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.i32.mul()
wgn.i32.add()
mtyp = LOAD_STORE_TYPE_MAP.get(el_type.name)
if mtyp is None:
# In the future might extend this by having structs or tuples
# as members of struct or tuples
raise NotImplementedError(expression, inp, el_type)
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load')
return
@ -360,11 +404,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
expression(wgn, inp.varref)
mtyp = LOAD_STORE_TYPE_MAP.get(el_type.name)
if mtyp is None:
# In the future might extend this by having structs or tuples
# as members of struct or tuples
raise NotImplementedError(expression, inp, el_type)
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
return
@ -372,11 +413,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
raise NotImplementedError(expression, inp, inp.varref.type3)
if isinstance(inp, ourlang.AccessStructMember):
mtyp = LOAD_STORE_TYPE_MAP.get(inp.struct_type3.members[inp.member].name)
if mtyp is None:
# In the future might extend this by having structs or tuples
# as members of struct or tuples
raise NotImplementedError(expression, inp, inp.struct_type3)
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]
expression(wgn, inp.varref)
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(_calculate_member_offset(
@ -538,9 +576,7 @@ def function(inp: ourlang.Function) -> wasm.Function:
wgn = WasmGenerator()
if False: # TODO: isinstance(inp, ourlang.TupleConstructor):
pass # _generate_tuple_constructor(wgn, inp)
elif isinstance(inp, ourlang.StructConstructor):
if isinstance(inp, ourlang.StructConstructor):
_generate_struct_constructor(wgn, inp)
else:
for stat in inp.statements:
@ -706,30 +742,6 @@ def module(inp: ourlang.Module) -> wasm.Module:
return result
# TODO: Broken after new type system
# def _generate_tuple_constructor(wgn: WasmGenerator, inp: ourlang.TupleConstructor) -> None:
# tmp_var = wgn.temp_var_i32('tuple_adr')
#
# # Allocated the required amounts of bytes in memory
# wgn.i32.const(inp.tuple.alloc_size())
# wgn.call(stdlib_alloc.__alloc__)
# wgn.local.set(tmp_var)
#
# # Store each member individually
# for member in inp.tuple.members:
# mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__)
# if mtyp is None:
# # In the future might extend this by having structs or tuples
# # as members of struct or tuples
# raise NotImplementedError(expression, inp, member)
#
# wgn.local.get(tmp_var)
# wgn.add_statement('local.get', f'$arg{member.idx}')
# wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset))
#
# # Return the allocated address
# wgn.local.get(tmp_var)
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
tmp_var = wgn.temp_var_i32('struct_adr')
@ -771,6 +783,17 @@ def _calculate_alloc_size(typ: Union[type3types.StructType3, type3types.Type3])
for x in typ.members.values()
)
if isinstance(typ, type3types.AppliedType3):
if typ.base is type3types.tuple:
size = 0
for arg in typ.args:
if isinstance(arg, type3types.PlaceholderForType):
assert not arg.resolve_as is None
arg = arg.resolve_as
size += _calculate_alloc_size(arg)
return size
raise NotImplementedError(_calculate_alloc_size, typ)
def _calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int:

View File

@ -142,14 +142,14 @@ class TupleInstantiation(Expression):
"""
Instantiation a tuple
"""
__slots__ = ('args', )
__slots__ = ('elements', )
args: List[Expression]
elements: List[Expression]
def __init__(self, args: List[Expression]) -> None:
def __init__(self, elements: List[Expression]) -> None:
super().__init__()
self.args = args
self.elements = elements
class Subscript(Expression):
"""

View File

@ -60,14 +60,10 @@ class ConstraintBase:
def __init__(self, comment: Optional[str] = None) -> None:
self.comment = comment
def check(self, smap: SubstitutionMap) -> CheckResult:
def check(self) -> CheckResult:
"""
Checks if the constraint hold
smap will contain a mapping from placeholders to types, for
placeholders discovered from either other constraints or from
earlier calls to check in this constraint.
This function can return an error, if the constraint does not hold,
which indicates an error in the typing of the input program.
@ -103,7 +99,7 @@ class SameTypeConstraint(ConstraintBase):
assert len(type_list) > 1
self.type_list = [*type_list]
def check(self, smap: SubstitutionMap) -> CheckResult:
def check(self) -> CheckResult:
known_types: List[types.Type3] = []
placeholders = []
do_applied_placeholder_check: bool = False
@ -118,8 +114,8 @@ class SameTypeConstraint(ConstraintBase):
continue
if isinstance(typ, types.PlaceholderForType):
if typ in smap:
known_types.append(smap[typ])
if typ.resolve_as is not None:
known_types.append(typ.resolve_as)
else:
placeholders.append(typ)
continue
@ -159,6 +155,9 @@ class SameTypeConstraint(ConstraintBase):
if not placeholders:
return None
for typ in placeholders:
typ.resolve_as = first_type
return {
typ: first_type
for typ in placeholders
@ -193,14 +192,14 @@ class CastableConstraint(ConstraintBase):
self.from_type3 = from_type3
self.to_type3 = to_type3
def check(self, smap: SubstitutionMap) -> CheckResult:
def check(self) -> CheckResult:
ftyp = self.from_type3
if isinstance(ftyp, types.PlaceholderForType) and ftyp in smap:
ftyp = smap[ftyp]
if isinstance(ftyp, types.PlaceholderForType) and ftyp.resolve_as is not None:
ftyp = ftyp.resolve_as
ttyp = self.to_type3
if isinstance(ttyp, types.PlaceholderForType) and ttyp in smap:
ttyp = smap[ttyp]
if isinstance(ttyp, types.PlaceholderForType) and ttyp.resolve_as is not None:
ttyp = ttyp.resolve_as
if isinstance(ftyp, types.PlaceholderForType) or isinstance(ttyp, types.PlaceholderForType):
return RequireTypeSubstitutes()
@ -248,10 +247,10 @@ class MustImplementTypeClassConstraint(ConstraintBase):
self.type_class3 = type_class3
self.type3 = type3
def check(self, smap: SubstitutionMap) -> CheckResult:
def check(self) -> CheckResult:
typ = self.type3
if isinstance(typ, types.PlaceholderForType) and typ in smap:
typ = smap[typ]
if isinstance(typ, types.PlaceholderForType) and typ.resolve_as is not None:
typ = typ.resolve_as
if isinstance(typ, types.PlaceholderForType):
return RequireTypeSubstitutes()
@ -293,7 +292,7 @@ class LiteralFitsConstraint(ConstraintBase):
self.type3 = type3
self.literal = literal
def check(self, smap: SubstitutionMap) -> CheckResult:
def check(self) -> CheckResult:
int_table: Dict[str, Tuple[int, bool]] = {
'u8': (1, False),
'u32': (4, False),
@ -309,10 +308,10 @@ class LiteralFitsConstraint(ConstraintBase):
}
if isinstance(self.type3, types.PlaceholderForType):
if self.type3 not in smap:
if self.type3.resolve_as is None:
return RequireTypeSubstitutes()
self.type3 = smap[self.type3]
self.type3 = self.type3.resolve_as
if self.type3.name in int_table:
bts, sgn = int_table[self.type3.name]
@ -440,12 +439,12 @@ class CanBeSubscriptedConstraint(ConstraintBase):
self.index = index
self.index_type3 = index.type3
def check(self, smap: SubstitutionMap) -> CheckResult:
def check(self) -> CheckResult:
if isinstance(self.type3, types.PlaceholderForType):
if self.type3 not in smap:
if self.type3.resolve_as is None:
return RequireTypeSubstitutes()
self.type3 = smap[self.type3]
self.type3 = self.type3.resolve_as
if isinstance(self.type3, types.AppliedType3):
if self.type3.base == types.static_array:

View File

@ -104,7 +104,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas
if isinstance(inp, ourlang.TupleInstantiation):
r_type = []
for arg in inp.args:
for arg in inp.elements:
yield from expression(ctx, arg)
r_type.append(arg.type3)

View File

@ -1,7 +1,7 @@
"""
Entry point to the type3 system
"""
from typing import Dict, List
from typing import Any, Dict, List, Set
from .. import codestyle
from .. import ourlang
@ -35,7 +35,7 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
new_constraint_list = []
for constraint in constraint_list:
check_result = constraint.check(placeholder_substitutes)
check_result = constraint.check()
if check_result is None:
if verbose:
print_constraint(placeholder_id_map, constraint)

View File

@ -4,7 +4,7 @@ Contains the final types for use in Phasm
These are actual, instantiated types; not the abstract types that the
constraint generator works with.
"""
from typing import Any, Dict, Iterable, List, Protocol, Union
from typing import Any, Dict, Iterable, List, Optional, Protocol, Union
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
@ -73,12 +73,14 @@ class PlaceholderForType:
"""
A placeholder type, for when we don't know the final type yet
"""
__slots__ = ('update_on_substitution', )
__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)