From 6c627bca0122e3ec40c215f32c0398b708b8dd91 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Fri, 9 May 2025 17:53:04 +0200 Subject: [PATCH] Reworks function lookup Before this commit, finding the implementation for a type class method was done with a simple lookup table. This commit adds a router based on function signature. This also paves the way for adding type constructor arguments in function signatures. And it removes quite a few references to the prelude out of the compiler. Also adds a bunch of helper methods to render signatures as strings. --- phasm/compiler.py | 269 ++----------------------- phasm/prelude/__init__.py | 368 ++++++++++++++++++++++++++++++----- phasm/runtime.py | 3 +- phasm/type3/constraints.py | 13 +- phasm/type3/functions.py | 28 ++- phasm/type3/routers.py | 88 +++++++++ phasm/type3/typeclasses.py | 3 + phasm/type3/types.py | 45 ----- tests/integration/helpers.py | 9 +- 9 files changed, 457 insertions(+), 369 deletions(-) create mode 100644 phasm/type3/routers.py diff --git a/phasm/compiler.py b/phasm/compiler.py index 4b90740..43e4edb 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -11,6 +11,7 @@ from .stdlib import types as stdlib_types from .type3 import functions as type3functions from .type3 import typeclasses as type3classes from .type3 import types as type3types +from .type3.routers import NoRouteForTypeException, TypeApplicationRouter from .wasmgenerator import Generator as WasmGenerator TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled' @@ -29,239 +30,6 @@ LOAD_STORE_TYPE_MAP = { 'bytes': 'i32', # Bytes are passed around as pointers } -# For now this is nice & clean, but this will get messy quick -# Especially once we get functions with polymorphying applied types -INSTANCES = { - prelude.Eq.operators['==']: { - 'a=u8': stdlib_types.u8_eq_equals, - 'a=u32': stdlib_types.u32_eq_equals, - 'a=u64': stdlib_types.u64_eq_equals, - 'a=i8': stdlib_types.i8_eq_equals, - 'a=i32': stdlib_types.i32_eq_equals, - 'a=i64': stdlib_types.i64_eq_equals, - 'a=f32': stdlib_types.f32_eq_equals, - 'a=f64': stdlib_types.f64_eq_equals, - }, - prelude.Eq.operators['!=']: { - 'a=u8': stdlib_types.u8_eq_not_equals, - 'a=u32': stdlib_types.u32_eq_not_equals, - 'a=u64': stdlib_types.u64_eq_not_equals, - 'a=i8': stdlib_types.i8_eq_not_equals, - 'a=i32': stdlib_types.i32_eq_not_equals, - 'a=i64': stdlib_types.i64_eq_not_equals, - 'a=f32': stdlib_types.f32_eq_not_equals, - 'a=f64': stdlib_types.f64_eq_not_equals, - }, - prelude.Ord.methods['min']: { - 'a=u8': stdlib_types.u8_ord_min, - 'a=u32': stdlib_types.u32_ord_min, - 'a=u64': stdlib_types.u64_ord_min, - 'a=i8': stdlib_types.i8_ord_min, - 'a=i32': stdlib_types.i32_ord_min, - 'a=i64': stdlib_types.i64_ord_min, - 'a=f32': stdlib_types.f32_ord_min, - 'a=f64': stdlib_types.f64_ord_min, - }, - prelude.Ord.methods['max']: { - 'a=u8': stdlib_types.u8_ord_max, - 'a=u32': stdlib_types.u32_ord_max, - 'a=u64': stdlib_types.u64_ord_max, - 'a=i8': stdlib_types.i8_ord_max, - 'a=i32': stdlib_types.i32_ord_max, - 'a=i64': stdlib_types.i64_ord_max, - 'a=f32': stdlib_types.f32_ord_max, - 'a=f64': stdlib_types.f64_ord_max, - }, - prelude.Ord.operators['<']: { - 'a=u8': stdlib_types.u8_ord_less_than, - 'a=u32': stdlib_types.u32_ord_less_than, - 'a=u64': stdlib_types.u64_ord_less_than, - 'a=i8': stdlib_types.i8_ord_less_than, - 'a=i32': stdlib_types.i32_ord_less_than, - 'a=i64': stdlib_types.i64_ord_less_than, - 'a=f32': stdlib_types.f32_ord_less_than, - 'a=f64': stdlib_types.f64_ord_less_than, - }, - prelude.Ord.operators['<=']: { - 'a=u8': stdlib_types.u8_ord_less_than_or_equal, - 'a=u32': stdlib_types.u32_ord_less_than_or_equal, - 'a=u64': stdlib_types.u64_ord_less_than_or_equal, - 'a=i8': stdlib_types.i8_ord_less_than_or_equal, - 'a=i32': stdlib_types.i32_ord_less_than_or_equal, - 'a=i64': stdlib_types.i64_ord_less_than_or_equal, - 'a=f32': stdlib_types.f32_ord_less_than_or_equal, - 'a=f64': stdlib_types.f64_ord_less_than_or_equal, - }, - prelude.Ord.operators['>']: { - 'a=u8': stdlib_types.u8_ord_greater_than, - 'a=u32': stdlib_types.u32_ord_greater_than, - 'a=u64': stdlib_types.u64_ord_greater_than, - 'a=i8': stdlib_types.i8_ord_greater_than, - 'a=i32': stdlib_types.i32_ord_greater_than, - 'a=i64': stdlib_types.i64_ord_greater_than, - 'a=f32': stdlib_types.f32_ord_greater_than, - 'a=f64': stdlib_types.f64_ord_greater_than, - }, - prelude.Ord.operators['>=']: { - 'a=u8': stdlib_types.u8_ord_greater_than_or_equal, - 'a=u32': stdlib_types.u32_ord_greater_than_or_equal, - 'a=u64': stdlib_types.u64_ord_greater_than_or_equal, - 'a=i8': stdlib_types.i8_ord_greater_than_or_equal, - 'a=i32': stdlib_types.i32_ord_greater_than_or_equal, - 'a=i64': stdlib_types.i64_ord_greater_than_or_equal, - 'a=f32': stdlib_types.f32_ord_greater_than_or_equal, - 'a=f64': stdlib_types.f64_ord_greater_than_or_equal, - }, - prelude.Bits.methods['shl']: { - 'a=u8': stdlib_types.u8_bits_logical_shift_left, - 'a=u32': stdlib_types.u32_bits_logical_shift_left, - 'a=u64': stdlib_types.u64_bits_logical_shift_left, - }, - prelude.Bits.methods['shr']: { - 'a=u8': stdlib_types.u8_bits_logical_shift_right, - 'a=u32': stdlib_types.u32_bits_logical_shift_right, - 'a=u64': stdlib_types.u64_bits_logical_shift_right, - }, - prelude.Bits.methods['rotl']: { - 'a=u8': stdlib_types.u8_bits_rotate_left, - 'a=u32': stdlib_types.u32_bits_rotate_left, - 'a=u64': stdlib_types.u64_bits_rotate_left, - }, - prelude.Bits.methods['rotr']: { - 'a=u8': stdlib_types.u8_bits_rotate_right, - 'a=u32': stdlib_types.u32_bits_rotate_right, - 'a=u64': stdlib_types.u64_bits_rotate_right, - }, - prelude.Bits.operators['&']: { - 'a=u8': stdlib_types.u8_bits_bitwise_and, - 'a=u32': stdlib_types.u32_bits_bitwise_and, - 'a=u64': stdlib_types.u64_bits_bitwise_and, - }, - prelude.Bits.operators['|']: { - 'a=u8': stdlib_types.u8_bits_bitwise_or, - 'a=u32': stdlib_types.u32_bits_bitwise_or, - 'a=u64': stdlib_types.u64_bits_bitwise_or, - }, - prelude.Bits.operators['^']: { - 'a=u8': stdlib_types.u8_bits_bitwise_xor, - 'a=u32': stdlib_types.u32_bits_bitwise_xor, - 'a=u64': stdlib_types.u64_bits_bitwise_xor, - }, - prelude.Floating.methods['sqrt']: { - 'a=f32': stdlib_types.f32_floating_sqrt, - 'a=f64': stdlib_types.f64_floating_sqrt, - }, - prelude.Fractional.methods['ceil']: { - 'a=f32': stdlib_types.f32_fractional_ceil, - 'a=f64': stdlib_types.f64_fractional_ceil, - }, - prelude.Fractional.methods['floor']: { - 'a=f32': stdlib_types.f32_fractional_floor, - 'a=f64': stdlib_types.f64_fractional_floor, - }, - prelude.Fractional.methods['trunc']: { - 'a=f32': stdlib_types.f32_fractional_trunc, - 'a=f64': stdlib_types.f64_fractional_trunc, - }, - prelude.Fractional.methods['nearest']: { - 'a=f32': stdlib_types.f32_fractional_nearest, - 'a=f64': stdlib_types.f64_fractional_nearest, - }, - prelude.Fractional.operators['/']: { - 'a=f32': stdlib_types.f32_fractional_div, - 'a=f64': stdlib_types.f64_fractional_div, - }, - prelude.Integral.operators['//']: { - 'a=u32': stdlib_types.u32_integral_div, - 'a=u64': stdlib_types.u64_integral_div, - 'a=i32': stdlib_types.i32_integral_div, - 'a=i64': stdlib_types.i64_integral_div, - }, - prelude.Integral.operators['%']: { - 'a=u32': stdlib_types.u32_integral_rem, - 'a=u64': stdlib_types.u64_integral_rem, - 'a=i32': stdlib_types.i32_integral_rem, - 'a=i64': stdlib_types.i64_integral_rem, - }, - prelude.IntNum.methods['abs']: { - 'a=i32': stdlib_types.i32_intnum_abs, - 'a=i64': stdlib_types.i64_intnum_abs, - 'a=f32': stdlib_types.f32_intnum_abs, - 'a=f64': stdlib_types.f64_intnum_abs, - }, - prelude.IntNum.methods['neg']: { - 'a=i32': stdlib_types.i32_intnum_neg, - 'a=i64': stdlib_types.i64_intnum_neg, - 'a=f32': stdlib_types.f32_intnum_neg, - 'a=f64': stdlib_types.f64_intnum_neg, - }, - prelude.NatNum.operators['+']: { - 'a=u32': stdlib_types.u32_natnum_add, - 'a=u64': stdlib_types.u64_natnum_add, - 'a=i32': stdlib_types.i32_natnum_add, - 'a=i64': stdlib_types.i64_natnum_add, - 'a=f32': stdlib_types.f32_natnum_add, - 'a=f64': stdlib_types.f64_natnum_add, - }, - prelude.NatNum.operators['-']: { - 'a=u32': stdlib_types.u32_natnum_sub, - 'a=u64': stdlib_types.u64_natnum_sub, - 'a=i32': stdlib_types.i32_natnum_sub, - 'a=i64': stdlib_types.i64_natnum_sub, - 'a=f32': stdlib_types.f32_natnum_sub, - 'a=f64': stdlib_types.f64_natnum_sub, - }, - prelude.NatNum.operators['*']: { - 'a=u32': stdlib_types.u32_natnum_mul, - 'a=u64': stdlib_types.u64_natnum_mul, - 'a=i32': stdlib_types.i32_natnum_mul, - 'a=i64': stdlib_types.i64_natnum_mul, - 'a=f32': stdlib_types.f32_natnum_mul, - 'a=f64': stdlib_types.f64_natnum_mul, - }, - prelude.NatNum.operators['<<']: { - 'a=u32': stdlib_types.u32_natnum_arithmic_shift_left, - 'a=u64': stdlib_types.u64_natnum_arithmic_shift_left, - 'a=i32': stdlib_types.i32_natnum_arithmic_shift_left, - 'a=i64': stdlib_types.i64_natnum_arithmic_shift_left, - 'a=f32': stdlib_types.f32_natnum_arithmic_shift_left, - 'a=f64': stdlib_types.f64_natnum_arithmic_shift_left, - }, - prelude.NatNum.operators['>>']: { - 'a=u32': stdlib_types.u32_natnum_arithmic_shift_right, - 'a=u64': stdlib_types.u64_natnum_arithmic_shift_right, - 'a=i32': stdlib_types.i32_natnum_arithmic_shift_right, - 'a=i64': stdlib_types.i64_natnum_arithmic_shift_right, - 'a=f32': stdlib_types.f32_natnum_arithmic_shift_right, - 'a=f64': stdlib_types.f64_natnum_arithmic_shift_right, - }, - prelude.Sized_.methods['len']: { - 'a=bytes': stdlib_types.bytes_sized_len, - }, - prelude.Extendable.methods['extend']: { - 'a=u8,b=u32': stdlib_types.u8_u32_extend, - 'a=u8,b=u64': stdlib_types.u8_u64_extend, - 'a=u32,b=u64': stdlib_types.u32_u64_extend, - 'a=i8,b=i32': stdlib_types.i8_i32_extend, - 'a=i8,b=i64': stdlib_types.i8_i64_extend, - 'a=i32,b=i64': stdlib_types.i32_i64_extend, - }, - prelude.Extendable.methods['wrap']: { - 'a=u8,b=u32': stdlib_types.u8_u32_wrap, - 'a=u8,b=u64': stdlib_types.u8_u64_wrap, - 'a=u32,b=u64': stdlib_types.u32_u64_wrap, - 'a=i8,b=i32': stdlib_types.i8_i32_wrap, - 'a=i8,b=i64': stdlib_types.i8_i64_wrap, - 'a=i32,b=i64': stdlib_types.i32_i64_wrap, - }, - prelude.Promotable.methods['promote']: { - 'a=f32,b=f64': stdlib_types.f32_f64_promote, - }, - prelude.Promotable.methods['demote']: { - 'a=f32,b=f64': stdlib_types.f32_f64_demote, - }, -} - def phasm_compile(inp: ourlang.Module) -> wasm.Module: """ Public method for compiling a parsed Phasm module into @@ -450,7 +218,7 @@ def expression_subscript_tuple( wgn.add_statement(f'{mtyp}.load', f'offset={offset}') -SUBSCRIPT_ROUTER = type3types.TypeApplicationRouter[tuple[WasmGenerator, ourlang.Subscript], None]() +SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Subscript], None]() SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes) SUBSCRIPT_ROUTER.add(prelude.static_array, expression_subscript_static_array) SUBSCRIPT_ROUTER.add(prelude.tuple_, expression_subscript_tuple) @@ -524,8 +292,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: expression(wgn, inp.left) expression(wgn, inp.right) - assert inp.type3 is not None, TYPE3_ASSERTION_ERROR - type_var_map: Dict[type3functions.TypeVariable, type3types.Type3] = {} for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True): @@ -536,17 +302,9 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR type_var_map[type_var] = arg_expr.type3 - instance_key = ','.join( - f'{k.letter}={v.name}' - for k, v in type_var_map.items() - ) - - instance = INSTANCES.get(inp.operator, {}).get(instance_key, None) - if instance is not None: - instance(wgn) - return - - raise NotImplementedError(inp.operator, instance_key) + router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.operator] + router(wgn, type_var_map) + return if isinstance(inp, ourlang.FunctionCall): for arg in inp.arguments: @@ -564,17 +322,12 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR type_var_map[type_var] = arg_expr.type3 - instance_key = ','.join( - f'{k.letter}={v.name}' - for k, v in sorted(type_var_map.items(), key=lambda x: x[0].letter) - ) - - instance = INSTANCES.get(inp.function, {}).get(instance_key, None) - if instance is not None: - instance(wgn) - return - - raise NotImplementedError(inp.function, instance_key) + router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.function] + try: + router(wgn, type_var_map) + except NoRouteForTypeException: + raise NotImplementedError(str(inp.function), type_var_map) + return wgn.add_statement('call', '${}'.format(inp.function.name)) return diff --git a/phasm/prelude/__init__.py b/phasm/prelude/__init__.py index df34f77..d91128e 100644 --- a/phasm/prelude/__init__.py +++ b/phasm/prelude/__init__.py @@ -1,10 +1,15 @@ """ The prelude are all the builtin types, type classes and methods """ -from ..type3.functions import ( - TypeVariable, -) -from ..type3.typeclasses import Type3Class +from typing import Callable +from warnings import warn + +from phasm.stdlib import types as stdtypes +from phasm.wasmgenerator import Generator + +from ..type3.functions import TypeVariable +from ..type3.routers import FunctionSignatureRouter +from ..type3.typeclasses import Type3Class, Type3ClassMethod from ..type3.types import ( IntType3, Type3, @@ -16,14 +21,61 @@ from ..type3.types import ( PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3, ...]]] = set() +PRELUDE_TYPE_CLASS_INSTANCE_METHODS: dict[Type3ClassMethod, FunctionSignatureRouter[Generator, None]] = {} -def instance_type_class(cls: Type3Class, *typ: Type3) -> None: +class MissingImplementationException(Exception): + pass + +class MissingImplementationWarning(Warning): + pass + +def instance_type_class( + cls: Type3Class, + *typ: Type3, + methods: dict[str, Callable[[Generator], None]] = {}, + operators: dict[str, Callable[[Generator], None]] = {}, +) -> None: global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING + global PRELUDE_TYPE_CLASS_INSTANCE_METHODS + + assert len(cls.args) == len(typ) + + type_var_map = {} + for arg_tv, arg_tp in zip(cls.args, typ, strict=True): + type_var_map[arg_tv] = arg_tp # TODO: Check for required existing instantiations PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, tuple(typ), )) + for method_name, method in cls.methods.items(): + router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(method) + if router is None: + router = FunctionSignatureRouter[Generator, None](method.signature) + PRELUDE_TYPE_CLASS_INSTANCE_METHODS[method] = router + + try: + generator = methods[method_name] + except KeyError: + warn(MissingImplementationWarning(str(method), cls.name + ' ' + ' '.join(x.name for x in typ))) + continue + + router.add(type_var_map, generator) + + for operator_name, operator in cls.operators.items(): + router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(operator) + if router is None: + router = FunctionSignatureRouter[Generator, None](operator.signature) + PRELUDE_TYPE_CLASS_INSTANCE_METHODS[operator] = router + + try: + generator = operators[operator_name] + except KeyError: + warn(MissingImplementationWarning(str(operator), cls.name + ' ' + ' '.join(x.name for x in typ))) + continue + + router.add(type_var_map, generator) + none = Type3('none', TypeApplication_Nullary(None, None)) """ The none type, for when functions simply don't return anything. e.g., IO(). @@ -163,14 +215,38 @@ Eq = Type3Class('Eq', [a], methods={}, operators={ # FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization? }) -instance_type_class(Eq, u8) -instance_type_class(Eq, u32) -instance_type_class(Eq, u64) -instance_type_class(Eq, i8) -instance_type_class(Eq, i32) -instance_type_class(Eq, i64) -instance_type_class(Eq, f32) -instance_type_class(Eq, f64) +instance_type_class(Eq, u8, operators={ + '==': stdtypes.u8_eq_equals, + '!=': stdtypes.u8_eq_not_equals, +}) +instance_type_class(Eq, u32, operators={ + '==': stdtypes.u32_eq_equals, + '!=': stdtypes.u32_eq_not_equals, +}) +instance_type_class(Eq, u64, operators={ + '==': stdtypes.u64_eq_equals, + '!=': stdtypes.u64_eq_not_equals, +}) +instance_type_class(Eq, i8, operators={ + '==': stdtypes.i8_eq_equals, + '!=': stdtypes.i8_eq_not_equals, +}) +instance_type_class(Eq, i32, operators={ + '==': stdtypes.i32_eq_equals, + '!=': stdtypes.i32_eq_not_equals, +}) +instance_type_class(Eq, i64, operators={ + '==': stdtypes.i64_eq_equals, + '!=': stdtypes.i64_eq_not_equals, +}) +instance_type_class(Eq, f32, operators={ + '==': stdtypes.f32_eq_equals, + '!=': stdtypes.f32_eq_not_equals, +}) +instance_type_class(Eq, f64, operators={ + '==': stdtypes.f64_eq_equals, + '!=': stdtypes.f64_eq_not_equals, +}) Ord = Type3Class('Ord', [a], methods={ 'min': [a, a, a], @@ -182,14 +258,78 @@ Ord = Type3Class('Ord', [a], methods={ '>=': [a, a, bool_], }, inherited_classes=[Eq]) -instance_type_class(Ord, u8) -instance_type_class(Ord, u32) -instance_type_class(Ord, u64) -instance_type_class(Ord, i8) -instance_type_class(Ord, i32) -instance_type_class(Ord, i64) -instance_type_class(Ord, f32) -instance_type_class(Ord, f64) +instance_type_class(Ord, u8, methods={ + 'min': stdtypes.u8_ord_min, + 'max': stdtypes.u8_ord_max, +}, operators={ + '<': stdtypes.u8_ord_less_than, + '<=': stdtypes.u8_ord_less_than_or_equal, + '>': stdtypes.u8_ord_greater_than, + '>=': stdtypes.u8_ord_greater_than_or_equal, +}) +instance_type_class(Ord, u32, methods={ + 'min': stdtypes.u32_ord_min, + 'max': stdtypes.u32_ord_max, +}, operators={ + '<': stdtypes.u32_ord_less_than, + '<=': stdtypes.u32_ord_less_than_or_equal, + '>': stdtypes.u32_ord_greater_than, + '>=': stdtypes.u32_ord_greater_than_or_equal, +}) +instance_type_class(Ord, u64, methods={ + 'min': stdtypes.u64_ord_min, + 'max': stdtypes.u64_ord_max, +}, operators={ + '<': stdtypes.u64_ord_less_than, + '<=': stdtypes.u64_ord_less_than_or_equal, + '>': stdtypes.u64_ord_greater_than, + '>=': stdtypes.u64_ord_greater_than_or_equal, +}) +instance_type_class(Ord, i8, methods={ + 'min': stdtypes.i8_ord_min, + 'max': stdtypes.i8_ord_max, +}, operators={ + '<': stdtypes.i8_ord_less_than, + '<=': stdtypes.i8_ord_less_than_or_equal, + '>': stdtypes.i8_ord_greater_than, + '>=': stdtypes.i8_ord_greater_than_or_equal, +}) +instance_type_class(Ord, i32, methods={ + 'min': stdtypes.i32_ord_min, + 'max': stdtypes.i32_ord_max, +}, operators={ + '<': stdtypes.i32_ord_less_than, + '<=': stdtypes.i32_ord_less_than_or_equal, + '>': stdtypes.i32_ord_greater_than, + '>=': stdtypes.i32_ord_greater_than_or_equal, +}) +instance_type_class(Ord, i64, methods={ + 'min': stdtypes.i64_ord_min, + 'max': stdtypes.i64_ord_max, +}, operators={ + '<': stdtypes.i64_ord_less_than, + '<=': stdtypes.i64_ord_less_than_or_equal, + '>': stdtypes.i64_ord_greater_than, + '>=': stdtypes.i64_ord_greater_than_or_equal, +}) +instance_type_class(Ord, f32, methods={ + 'min': stdtypes.f32_ord_min, + 'max': stdtypes.f32_ord_max, +}, operators={ + '<': stdtypes.f32_ord_less_than, + '<=': stdtypes.f32_ord_less_than_or_equal, + '>': stdtypes.f32_ord_greater_than, + '>=': stdtypes.f32_ord_greater_than_or_equal, +}) +instance_type_class(Ord, f64, methods={ + 'min': stdtypes.f64_ord_min, + 'max': stdtypes.f64_ord_max, +}, operators={ + '<': stdtypes.f64_ord_less_than, + '<=': stdtypes.f64_ord_less_than_or_equal, + '>': stdtypes.f64_ord_greater_than, + '>=': stdtypes.f64_ord_greater_than_or_equal, +}) Bits = Type3Class('Bits', [a], methods={ 'shl': [a, u32, a], # Logical shift left @@ -203,9 +343,36 @@ Bits = Type3Class('Bits', [a], methods={ '^': [a, a, a], # Bit-wise xor }) -instance_type_class(Bits, u8) -instance_type_class(Bits, u32) -instance_type_class(Bits, u64) +instance_type_class(Bits, u8, methods={ + 'shl': stdtypes.u8_bits_logical_shift_left, + 'shr': stdtypes.u8_bits_logical_shift_right, + 'rotl': stdtypes.u8_bits_rotate_left, + 'rotr': stdtypes.u8_bits_rotate_right, +}, operators={ + '&': stdtypes.u8_bits_bitwise_and, + '|': stdtypes.u8_bits_bitwise_or, + '^': stdtypes.u8_bits_bitwise_xor, +}) +instance_type_class(Bits, u32, methods={ + 'shl': stdtypes.u32_bits_logical_shift_left, + 'shr': stdtypes.u32_bits_logical_shift_right, + 'rotl': stdtypes.u32_bits_rotate_left, + 'rotr': stdtypes.u32_bits_rotate_right, +}, operators={ + '&': stdtypes.u32_bits_bitwise_and, + '|': stdtypes.u32_bits_bitwise_or, + '^': stdtypes.u32_bits_bitwise_xor, +}) +instance_type_class(Bits, u64, methods={ + 'shl': stdtypes.u64_bits_logical_shift_left, + 'shr': stdtypes.u64_bits_logical_shift_right, + 'rotl': stdtypes.u64_bits_rotate_left, + 'rotr': stdtypes.u64_bits_rotate_right, +}, operators={ + '&': stdtypes.u64_bits_bitwise_and, + '|': stdtypes.u64_bits_bitwise_or, + '^': stdtypes.u64_bits_bitwise_xor, +}) NatNum = Type3Class('NatNum', [a], methods={}, operators={ '+': [a, a, a], @@ -215,22 +382,70 @@ NatNum = Type3Class('NatNum', [a], methods={}, operators={ '>>': [a, u32, a], # Arithmic shift right }) -instance_type_class(NatNum, u32) -instance_type_class(NatNum, u64) -instance_type_class(NatNum, i32) -instance_type_class(NatNum, i64) -instance_type_class(NatNum, f32) -instance_type_class(NatNum, f64) +instance_type_class(NatNum, u32, operators={ + '+': stdtypes.u32_natnum_add, + '-': stdtypes.u32_natnum_sub, + '*': stdtypes.u32_natnum_mul, + '<<': stdtypes.u32_natnum_arithmic_shift_left, + '>>': stdtypes.u32_natnum_arithmic_shift_right, +}) +instance_type_class(NatNum, u64, operators={ + '+': stdtypes.u64_natnum_add, + '-': stdtypes.u64_natnum_sub, + '*': stdtypes.u64_natnum_mul, + '<<': stdtypes.u64_natnum_arithmic_shift_left, + '>>': stdtypes.u64_natnum_arithmic_shift_right, +}) +instance_type_class(NatNum, i32, operators={ + '+': stdtypes.i32_natnum_add, + '-': stdtypes.i32_natnum_sub, + '*': stdtypes.i32_natnum_mul, + '<<': stdtypes.i32_natnum_arithmic_shift_left, + '>>': stdtypes.i32_natnum_arithmic_shift_right, +}) +instance_type_class(NatNum, i64, operators={ + '+': stdtypes.i64_natnum_add, + '-': stdtypes.i64_natnum_sub, + '*': stdtypes.i64_natnum_mul, + '<<': stdtypes.i64_natnum_arithmic_shift_left, + '>>': stdtypes.i64_natnum_arithmic_shift_right, +}) +instance_type_class(NatNum, f32, operators={ + '+': stdtypes.f32_natnum_add, + '-': stdtypes.f32_natnum_sub, + '*': stdtypes.f32_natnum_mul, + '<<': stdtypes.f32_natnum_arithmic_shift_left, + '>>': stdtypes.f32_natnum_arithmic_shift_right, +}) +instance_type_class(NatNum, f64, operators={ + '+': stdtypes.f64_natnum_add, + '-': stdtypes.f64_natnum_sub, + '*': stdtypes.f64_natnum_mul, + '<<': stdtypes.f64_natnum_arithmic_shift_left, + '>>': stdtypes.f64_natnum_arithmic_shift_right, +}) IntNum = Type3Class('IntNum', [a], methods={ 'abs': [a, a], 'neg': [a, a], }, operators={}, inherited_classes=[NatNum]) -instance_type_class(IntNum, i32) -instance_type_class(IntNum, i64) -instance_type_class(IntNum, f32) -instance_type_class(IntNum, f64) +instance_type_class(IntNum, i32, methods={ + 'abs': stdtypes.i32_intnum_abs, + 'neg': stdtypes.i32_intnum_neg, +}) +instance_type_class(IntNum, i64, methods={ + 'abs': stdtypes.i64_intnum_abs, + 'neg': stdtypes.i64_intnum_neg, +}) +instance_type_class(IntNum, f32, methods={ + 'abs': stdtypes.f32_intnum_abs, + 'neg': stdtypes.f32_intnum_neg, +}) +instance_type_class(IntNum, f64, methods={ + 'abs': stdtypes.f64_intnum_abs, + 'neg': stdtypes.f64_intnum_neg, +}) Integral = Type3Class('Eq', [a], methods={ }, operators={ @@ -238,10 +453,22 @@ Integral = Type3Class('Eq', [a], methods={ '%': [a, a, a], }, inherited_classes=[NatNum]) -instance_type_class(Integral, u32) -instance_type_class(Integral, u64) -instance_type_class(Integral, i32) -instance_type_class(Integral, i64) +instance_type_class(Integral, u32, operators={ + '//': stdtypes.u32_integral_div, + '%': stdtypes.u32_integral_rem, +}) +instance_type_class(Integral, u64, operators={ + '//': stdtypes.u64_integral_div, + '%': stdtypes.u64_integral_rem, +}) +instance_type_class(Integral, i32, operators={ + '//': stdtypes.i32_integral_div, + '%': stdtypes.i32_integral_rem, +}) +instance_type_class(Integral, i64, operators={ + '//': stdtypes.i64_integral_div, + '%': stdtypes.i64_integral_rem, +}) Fractional = Type3Class('Fractional', [a], methods={ 'ceil': [a, a], @@ -252,8 +479,22 @@ Fractional = Type3Class('Fractional', [a], methods={ '/': [a, a, a], }, inherited_classes=[NatNum]) -instance_type_class(Fractional, f32) -instance_type_class(Fractional, f64) +instance_type_class(Fractional, f32, methods={ + 'ceil': stdtypes.f32_fractional_ceil, + 'floor': stdtypes.f32_fractional_floor, + 'trunc': stdtypes.f32_fractional_trunc, + 'nearest': stdtypes.f32_fractional_nearest, +}, operators={ + '/': stdtypes.f32_fractional_div, +}) +instance_type_class(Fractional, f64, methods={ + 'ceil': stdtypes.f64_fractional_ceil, + 'floor': stdtypes.f64_fractional_floor, + 'trunc': stdtypes.f64_fractional_trunc, + 'nearest': stdtypes.f64_fractional_nearest, +}, operators={ + '/': stdtypes.f64_fractional_div, +}) Floating = Type3Class('Floating', [a], methods={ 'sqrt': [a, a], @@ -261,33 +502,60 @@ Floating = Type3Class('Floating', [a], methods={ # FIXME: Do we want to expose copysign? -instance_type_class(Floating, f32) -instance_type_class(Floating, f64) +instance_type_class(Floating, f32, methods={ + 'sqrt': stdtypes.f32_floating_sqrt, +}) +instance_type_class(Floating, f64, methods={ + 'sqrt': stdtypes.f64_floating_sqrt, +}) Sized_ = Type3Class('Sized', [a], methods={ 'len': [a, u32], }, operators={}) # FIXME: Once we get type class families, add [] here -instance_type_class(Sized_, bytes_) +instance_type_class(Sized_, bytes_, methods={ + 'len': stdtypes.bytes_sized_len, +}) Extendable = Type3Class('Extendable', [a, b], methods={ 'extend': [a, b], 'wrap': [b, a], }, operators={}) -instance_type_class(Extendable, u8, u32) -instance_type_class(Extendable, u8, u64) -instance_type_class(Extendable, u32, u64) -instance_type_class(Extendable, i8, i32) -instance_type_class(Extendable, i8, i64) -instance_type_class(Extendable, i32, i64) +instance_type_class(Extendable, u8, u32, methods={ + 'extend': stdtypes.u8_u32_extend, + 'wrap': stdtypes.u8_u32_wrap, +}) +instance_type_class(Extendable, u8, u64, methods={ + 'extend': stdtypes.u8_u64_extend, + 'wrap': stdtypes.u8_u64_wrap, +}) +instance_type_class(Extendable, u32, u64, methods={ + 'extend': stdtypes.u32_u64_extend, + 'wrap': stdtypes.u32_u64_wrap, +}) +instance_type_class(Extendable, i8, i32, methods={ + 'extend': stdtypes.i8_i32_extend, + 'wrap': stdtypes.i8_i32_wrap, +}) +instance_type_class(Extendable, i8, i64, methods={ + 'extend': stdtypes.i8_i64_extend, + 'wrap': stdtypes.i8_i64_wrap, +}) +instance_type_class(Extendable, i32, i64, methods={ + 'extend': stdtypes.i32_i64_extend, + 'wrap': stdtypes.i32_i64_wrap, +}) Promotable = Type3Class('Promotable', [a, b], methods={ 'promote': [a, b], 'demote': [b, a], }, operators={}) -instance_type_class(Promotable, f32, f64) +instance_type_class(Promotable, f32, f64, methods={ + 'promote': stdtypes.f32_f64_promote, + 'demote': stdtypes.f32_f64_demote, +}) PRELUDE_TYPE_CLASSES = { 'Eq': Eq, diff --git a/phasm/runtime.py b/phasm/runtime.py index c7bacb0..bec58bf 100644 --- a/phasm/runtime.py +++ b/phasm/runtime.py @@ -1,5 +1,6 @@ from . import prelude -from .type3.types import IntType3, NoRouteForTypeException, Type3, TypeApplicationRouter +from .type3.routers import NoRouteForTypeException, TypeApplicationRouter +from .type3.types import IntType3, Type3 def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntType3]) -> int: diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index 1eafac2..efa2181 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -8,6 +8,7 @@ from typing import Dict, Iterable, List, Optional, Tuple, Union from .. import ourlang, prelude from . import placeholders, typeclasses, types from .placeholders import PlaceholderForType +from .routers import NoRouteForTypeException, TypeApplicationRouter class Error: @@ -189,7 +190,7 @@ class TupleMatchConstraint(ConstraintBase): for arg, oth_arg in zip(self.args, tp_args, strict=True) ] - GENERATE_ROUTER = types.TypeApplicationRouter['TupleMatchConstraint', CheckResult]() + GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]() GENERATE_ROUTER.add(prelude.static_array, _generate_static_array) GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple) @@ -203,7 +204,7 @@ class TupleMatchConstraint(ConstraintBase): try: return self.__class__.GENERATE_ROUTER(self, exp_type) - except types.NoRouteForTypeException: + except NoRouteForTypeException: raise NotImplementedError(exp_type) class MustImplementTypeClassConstraint(ConstraintBase): @@ -363,7 +364,7 @@ class LiteralFitsConstraint(ConstraintBase): return res - GENERATE_ROUTER = types.TypeApplicationRouter['LiteralFitsConstraint', CheckResult]() + GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]() GENERATE_ROUTER.add(prelude.static_array, _generate_static_array) GENERATE_ROUTER.add(prelude.struct, _generate_struct) GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple) @@ -422,7 +423,7 @@ class LiteralFitsConstraint(ConstraintBase): try: return self.__class__.GENERATE_ROUTER(self, exp_type) - except types.NoRouteForTypeException: + except NoRouteForTypeException: raise NotImplementedError(exp_type) def human_readable(self) -> HumanReadableRet: @@ -502,7 +503,7 @@ class CanBeSubscriptedConstraint(ConstraintBase): SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'), ] - GENERATE_ROUTER = types.TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]() + GENERATE_ROUTER = TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]() GENERATE_ROUTER.add_n(prelude.bytes_, _generate_bytes) GENERATE_ROUTER.add(prelude.static_array, _generate_static_array) GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple) @@ -517,7 +518,7 @@ class CanBeSubscriptedConstraint(ConstraintBase): try: return self.__class__.GENERATE_ROUTER(self, exp_type) - except types.NoRouteForTypeException: + except NoRouteForTypeException: return Error(f'{exp_type.name} cannot be subscripted') def human_readable(self) -> HumanReadableRet: diff --git a/phasm/type3/functions.py b/phasm/type3/functions.py index d0147bc..4d924a5 100644 --- a/phasm/type3/functions.py +++ b/phasm/type3/functions.py @@ -51,22 +51,34 @@ class Constraint_TypeClassInstanceExists(ConstraintBase): # you can only add a constraint by supplying types for all variables assert len(self.type_class3.args) == len(self.types) -class TypeVariableContext: - __slots__ = ('variables', 'constraints', ) + def __str__(self) -> str: + return self.type_class3.name + ' ' + ' '.join(x.letter for x in self.types) + + def __repr__(self) -> str: + return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})' + +class TypeVariableContext: + __slots__ = ('constraints', ) - variables: set[TypeVariable] constraints: list[ConstraintBase] def __init__(self) -> None: - self.variables = set() self.constraints = [] def __copy__(self) -> 'TypeVariableContext': result = TypeVariableContext() - result.variables.update(self.variables) result.constraints.extend(self.constraints) return result + def __str__(self) -> str: + if not self.constraints: + return '' + + return '(' + ', '.join(str(x) for x in self.constraints) + ') => ' + + def __repr__(self) -> str: + return f'TypeVariableContext({self.constraints!r})' + class FunctionSignature: __slots__ = ('context', 'args', ) @@ -76,3 +88,9 @@ class FunctionSignature: def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable]]) -> None: self.context = context.__copy__() self.args = list(args) + + def __str__(self) -> str: + return str(self.context) + ' -> '.join(x.letter if isinstance(x, TypeVariable) else x.name for x in self.args) + + def __repr__(self) -> str: + return f'FunctionSignature({self.context!r}, {self.args!r})' diff --git a/phasm/type3/routers.py b/phasm/type3/routers.py new file mode 100644 index 0000000..d575bb8 --- /dev/null +++ b/phasm/type3/routers.py @@ -0,0 +1,88 @@ +from typing import Any, Callable, TypeVar + +from .functions import FunctionSignature, TypeVariable +from .types import Type3, TypeConstructor_Base + +T = TypeVar('T') + +class NoRouteForTypeException(Exception): + pass + +class TypeApplicationRouter[S, R]: + """ + Helper class to find a method based on a constructed type + """ + __slots__ = ('by_constructor', 'by_type', ) + + by_constructor: dict[Any, Callable[[S, Any], R]] + """ + Contains all the added routing functions for constructed types + """ + + by_type: dict[Type3, Callable[[S], R]] + """ + Contains all the added routing functions for constructed types + """ + + def __init__(self) -> None: + self.by_constructor = {} + self.by_type = {} + + def add_n(self, typ: Type3, helper: Callable[[S], R]) -> None: + """ + Lets you route to types that were not constructed + + Also known types of kind * + """ + self.by_type[typ] = helper + + def add(self, constructor: TypeConstructor_Base[T], helper: Callable[[S, T], R]) -> None: + self.by_constructor[constructor] = helper + + def __call__(self, arg0: S, typ: Type3) -> R: + t_helper = self.by_type.get(typ) + if t_helper is not None: + return t_helper(arg0) + + c_helper = self.by_constructor.get(typ.application.constructor) + if c_helper is not None: + return c_helper(arg0, typ.application.arguments) + + raise NoRouteForTypeException(arg0, typ) + +class FunctionSignatureRouter[S, R]: + """ + Helper class to find a method based on a function signature + """ + __slots__ = ('signature', 'data', ) + + signature: FunctionSignature + + data: dict[tuple[Type3, ...], Callable[[S], R]] + + def __init__(self, signature: FunctionSignature) -> None: + self.signature = signature + self.data = {} + + def add(self, tv_map: dict[TypeVariable, Type3], helper: Callable[[S], R]) -> None: + key = tuple( + tv_map[x] + for x in self.signature.args + if isinstance(x, TypeVariable) + ) + # assert len(key) == len(tv_map), (key, tv_map) + + self.data[key] = helper + + def __call__(self, arg0: S, tv_map: dict[TypeVariable, Type3]) -> R: + key = tuple( + tv_map[x] + for x in self.signature.args + if isinstance(x, TypeVariable) + ) + + t_helper = self.data.get(key) + if t_helper is not None: + return t_helper(arg0) + + raise NoRouteForTypeException(arg0, tv_map) diff --git a/phasm/type3/typeclasses.py b/phasm/type3/typeclasses.py index dcb63b6..4b09f66 100644 --- a/phasm/type3/typeclasses.py +++ b/phasm/type3/typeclasses.py @@ -19,6 +19,9 @@ class Type3ClassMethod: self.name = name self.signature = signature + def __str__(self) -> str: + return f'{self.name} :: {self.signature}' + def __repr__(self) -> str: return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})' diff --git a/phasm/type3/types.py b/phasm/type3/types.py index ceb86ab..5a971cf 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -248,48 +248,3 @@ class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]] class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]): pass - -class NoRouteForTypeException(Exception): - pass - -class TypeApplicationRouter[S, R]: - """ - Helper class to find a method based on a constructed type - """ - __slots__ = ('by_constructor', 'by_type', ) - - by_constructor: dict[Any, Callable[[S, Any], R]] - """ - Contains all the added routing functions for constructed types - """ - - by_type: dict[Type3, Callable[[S], R]] - """ - Contains all the added routing functions for constructed types - """ - - def __init__(self) -> None: - self.by_constructor = {} - self.by_type = {} - - def add_n(self, typ: Type3, helper: Callable[[S], R]) -> None: - """ - Lets you route to types that were not constructed - - Also known types of kind * - """ - self.by_type[typ] = helper - - def add(self, constructor: TypeConstructor_Base[T], helper: Callable[[S, T], R]) -> None: - self.by_constructor[constructor] = helper - - def __call__(self, arg0: S, typ: Type3) -> R: - t_helper = self.by_type.get(typ) - if t_helper is not None: - return t_helper(arg0) - - c_helper = self.by_constructor.get(typ.application.constructor) - if c_helper is not None: - return c_helper(arg0, typ.application.arguments) - - raise NoRouteForTypeException(arg0, typ) diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index c71f6ae..a335509 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -11,6 +11,7 @@ from phasm.runtime import ( calculate_alloc_size_tuple, ) from phasm.type3 import types as type3types +from phasm.type3.routers import NoRouteForTypeException, TypeApplicationRouter from . import runners @@ -87,7 +88,7 @@ class Suite: try: adr = ALLOCATE_MEMORY_STORED_ROUTER((runner, arg), arg_typ) wasm_args.append(adr) - except type3types.NoRouteForTypeException: + except NoRouteForTypeException: raise NotImplementedError(arg_typ, arg) write_header(sys.stderr, 'Memory (pre run)') @@ -131,7 +132,7 @@ def _write_memory_stored_value( adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ) runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2)) return 4 - except type3types.NoRouteForTypeException: + except NoRouteForTypeException: to_write = WRITE_LOOKUP_MAP[val_typ.name](val) runner.interpreter_write_memory(adr, to_write) return len(to_write) @@ -205,7 +206,7 @@ def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val) return adr -ALLOCATE_MEMORY_STORED_ROUTER = type3types.TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]() +ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]() ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes) ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array) ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct) @@ -373,7 +374,7 @@ def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tup for arg_typ, arg_bytes in zip(tp_args, _split_read_bytes(read_bytes, arg_sizes), strict=True) ) -LOAD_FROM_ADDRESS_ROUTER = type3types.TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]() +LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]() LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address) LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address) LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)