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.
This commit is contained in:
Johan B.W. de Vries 2025-05-09 17:53:04 +02:00
parent 78c98b1e61
commit 6c627bca01
9 changed files with 457 additions and 369 deletions

View File

@ -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

View File

@ -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,

View File

@ -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:

View File

@ -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:

View File

@ -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})'

88
phasm/type3/routers.py Normal file
View File

@ -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)

View File

@ -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)})'

View File

@ -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)

View File

@ -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)