From 7f0acf00fea855169200091cb170c6bcada04f5e Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sat, 31 Dec 2022 15:31:39 +0100 Subject: [PATCH] Started work on applied type constraints --- phasm/type3/constraints.py | 41 +++++++++++++++++++++++++++++++----- phasm/type3/entry.py | 32 ++++++++++++++++++++-------- phasm/type3/types.py | 43 +++++++++++++++++++++++++------------- 3 files changed, 88 insertions(+), 28 deletions(-) diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index 6999d64..3610e6c 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -104,27 +104,58 @@ class SameTypeConstraint(ConstraintBase): self.type_list = [*type_list] def check(self, smap: SubstitutionMap) -> CheckResult: - known_types = [] + known_types: List[types.Type3] = [] placeholders = [] + do_applied_placeholder_check: bool = False for typ in self.type_list: - if isinstance(typ, types.Type3): + if isinstance(typ, types.PrimitiveType3): known_types.append(typ) continue - if typ in smap: - known_types.append(smap[typ]) + if isinstance(typ, types.AppliedType3): + known_types.append(typ) + do_applied_placeholder_check = True continue - placeholders.append(typ) + if isinstance(typ, types.PlaceholderForType): + if typ in smap: + known_types.append(smap[typ]) + else: + placeholders.append(typ) + continue + + raise NotImplementedError(typ) if not known_types: return RequireTypeSubstitutes() + new_constraint_list: List[ConstraintBase] = [] + first_type = known_types[0] for typ in known_types[1:]: + if isinstance(first_type, types.AppliedType3) and isinstance(typ, types.AppliedType3): + if len(first_type.args) != len(typ.args): + return Error('Mismatch between applied types argument count') + + if first_type.base != typ.base: + return Error('Mismatch between applied types base') + + for first_type_arg, typ_arg in zip(first_type.args, typ.args): + new_constraint_list.append(SameTypeConstraint( + first_type_arg, typ_arg + )) + + continue + if typ != first_type: return Error(f'{typ:s} must be {first_type:s} instead') + if new_constraint_list: + # If this happens, make CheckResult a class that can have both + assert not placeholders, 'Cannot (yet) return both new placeholders and new constraints' + + return new_constraint_list + if not placeholders: return None diff --git a/phasm/type3/entry.py b/phasm/type3/entry.py index f119d95..fe529fa 100644 --- a/phasm/type3/entry.py +++ b/phasm/type3/entry.py @@ -8,7 +8,7 @@ from .. import ourlang from .constraints import ConstraintBase, Error, RequireTypeSubstitutes, SameTypeConstraint, SubstitutionMap from .constraintsgenerator import phasm_type3_generate_constraints -from .types import PlaceholderForType, Type3 +from .types import AppliedType3, PlaceholderForType, PrimitiveType3, Type3, Type3OrPlaceholder MAX_RESTACK_COUNT = 100 @@ -111,14 +111,8 @@ def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintB if isinstance(fmt_val, ourlang.Expression): fmt_val = codestyle.expression(fmt_val) - if isinstance(fmt_val, Type3): - fmt_val = fmt_val.name - - if isinstance(fmt_val, PlaceholderForType): - placeholder_id = id(fmt_val) - if placeholder_id not in placeholder_id_map: - placeholder_id_map[placeholder_id] = 'T' + str(len(placeholder_id_map) + 1) - fmt_val = placeholder_id_map[placeholder_id] + if isinstance(fmt_val, Type3) or isinstance(fmt_val, PlaceholderForType): + fmt_val = get_printable_type_name(fmt_val, placeholder_id_map) if not isinstance(fmt_val, str): fmt_val = repr(fmt_val) @@ -130,6 +124,26 @@ def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintB else: print('- ' + txt.format(**act_fmt)) +def get_printable_type_name(inp: Type3OrPlaceholder, placeholder_id_map: Dict[int, str]) -> str: + if isinstance(inp, PrimitiveType3): + return inp.name + + if isinstance(inp, PlaceholderForType): + placeholder_id = id(inp) + if placeholder_id not in placeholder_id_map: + placeholder_id_map[placeholder_id] = 'T' + str(len(placeholder_id_map) + 1) + return placeholder_id_map[placeholder_id] + + if isinstance(inp, AppliedType3): + return ( + get_printable_type_name(inp.base, placeholder_id_map) + + ' (' + + ') ('.join(get_printable_type_name(x, placeholder_id_map) for x in inp.args) + + ')' + ) + + raise NotImplementedError(inp) + def print_constraint_list(placeholder_id_map: Dict[int, str], constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None: print('=== v type3 constraint_list v === ') for psk, psv in placeholder_substitutes.items(): diff --git a/phasm/type3/types.py b/phasm/type3/types.py index d56f86a..b5ae5ea 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -62,6 +62,13 @@ class Type3: def __bool__(self) -> bool: raise NotImplementedError +class PrimitiveType3(Type3): + """ + Intermediate class to tell primitive types from others + """ + + __slots__ = () + class PlaceholderForType: """ A placeholder type, for when we don't know the final type yet @@ -113,7 +120,7 @@ class AppliedType3(Type3): """ __slots__ = ('base', 'args', ) - base: Type3 + base: PrimitiveType3 """ The base type """ @@ -123,8 +130,9 @@ class AppliedType3(Type3): The applied types (or placeholders there for) """ - def __init__(self, base: Type3, args: Iterable[Type3OrPlaceholder]) -> None: + def __init__(self, base: PrimitiveType3, args: Iterable[Type3OrPlaceholder]) -> None: args = [*args] + assert args, 'Must at least one argument' super().__init__( base.name @@ -136,6 +144,13 @@ class AppliedType3(Type3): self.base = base self.args = args + @property + def has_placeholders(self) -> bool: + return any( + isinstance(x, PlaceholderForType) + for x in self.args + ) + def __eq__(self, other: Any) -> bool: if not isinstance(other, Type3): raise NotImplementedError @@ -180,33 +195,33 @@ class StructType3(Type3): def __repr__(self) -> str: return f'StructType3(repr({self.name}), repr({self.members}))' -none = Type3('none') +none = PrimitiveType3('none') """ The none type, for when functions simply don't return anything. e.g., IO(). """ -u8 = Type3('u8') +u8 = PrimitiveType3('u8') """ The unsigned 8-bit integer type. Operations on variables employ modular arithmetic, with modulus 2^8. """ -u32 = Type3('u32') +u32 = PrimitiveType3('u32') """ The unsigned 32-bit integer type. Operations on variables employ modular arithmetic, with modulus 2^32. """ -u64 = Type3('u64') +u64 = PrimitiveType3('u64') """ The unsigned 64-bit integer type. Operations on variables employ modular arithmetic, with modulus 2^64. """ -i8 = Type3('i8') +i8 = PrimitiveType3('i8') """ The signed 8-bit integer type. @@ -214,7 +229,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but with the middel point being 0. """ -i32 = Type3('i32') +i32 = PrimitiveType3('i32') """ The unsigned 32-bit integer type. @@ -222,7 +237,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but with the middel point being 0. """ -i64 = Type3('i64') +i64 = PrimitiveType3('i64') """ The unsigned 64-bit integer type. @@ -230,22 +245,22 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but with the middel point being 0. """ -f32 = Type3('f32') +f32 = PrimitiveType3('f32') """ A 32-bits IEEE 754 float, of 32 bits width. """ -f64 = Type3('f64') +f64 = PrimitiveType3('f64') """ A 32-bits IEEE 754 float, of 64 bits width. """ -bytes = Type3('bytes') +bytes = PrimitiveType3('bytes') """ This is a runtime-determined length piece of memory that can be indexed at runtime. """ -static_array = Type3('static_array') +static_array = PrimitiveType3('static_array') """ This is a fixed length piece of memory that can be indexed at runtime. @@ -253,7 +268,7 @@ It should be applied with one argument. It has a runtime-dynamic length of the same type repeated. """ -tuple = Type3('tuple') # pylint: disable=W0622 +tuple = PrimitiveType3('tuple') # pylint: disable=W0622 """ This is a fixed length piece of memory.