phasm/phasm/prelude/__init__.py
Johan B.W. de Vries 46b06dbcf1 Cleanup: TYPE_INFO_MAP
This also removes the InternalPassAsPointer experiment.

This also fixes that u8 values were stores as 32 bits
in structs and tuples (but not dynamic arrays since that
is special cased as bytes).

Also, fixes allocation issue wi	th dynamic arrays, it
would allocate quadratic amount of memory.
2025-05-19 21:04:13 +02:00

617 lines
18 KiB
Python

"""
The prelude are all the builtin types, type classes and methods
"""
from typing import Any, Callable
from warnings import warn
from phasm.stdlib import types as stdtypes
from phasm.wasmgenerator import Generator
from ..type3.functions import (
Constraint_TypeClassInstanceExists,
TypeConstructorVariable,
TypeVariable,
TypeVariableApplication_Nullary,
)
from ..type3.routers import TypeClassArgsRouter, TypeVariableLookup
from ..type3.typeclasses import Type3Class, Type3ClassMethod
from ..type3.types import (
Type3,
TypeApplication_Nullary,
TypeConstructor_Base,
TypeConstructor_DynamicArray,
TypeConstructor_Function,
TypeConstructor_StaticArray,
TypeConstructor_Struct,
TypeConstructor_Tuple,
)
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3 | TypeConstructor_Base[Any], ...]]] = set()
PRELUDE_TYPE_CLASS_INSTANCE_METHODS: dict[Type3ClassMethod, TypeClassArgsRouter[Generator, None]] = {}
class MissingImplementationException(Exception):
pass
class MissingImplementationWarning(Warning):
pass
def instance_type_class(
cls: Type3Class,
*typ: Type3 | TypeConstructor_Base[Any],
methods: dict[str, Callable[[Generator, TypeVariableLookup], None]] = {},
operators: dict[str, Callable[[Generator, TypeVariableLookup], None]] = {},
) -> None:
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
global PRELUDE_TYPE_CLASS_INSTANCE_METHODS
assert len(cls.args) == len(typ)
tv_map: dict[TypeVariable, Type3] = {}
tc_map: dict[TypeConstructorVariable, TypeConstructor_Base[Any]] = {}
for arg_tv, arg_tp in zip(cls.args, typ, strict=True):
if isinstance(arg_tv, TypeVariable):
assert isinstance(arg_tp, Type3)
tv_map[arg_tv] = arg_tp
elif isinstance(arg_tv, TypeConstructorVariable):
assert isinstance(arg_tp, TypeConstructor_Base)
tc_map[arg_tv] = arg_tp
else:
raise NotImplementedError(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 = TypeClassArgsRouter[Generator, None](cls.args)
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(tv_map, tc_map, generator)
for operator_name, operator in cls.operators.items():
router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(operator)
if router is None:
router = TypeClassArgsRouter[Generator, None](cls.args)
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(tv_map, tc_map, generator)
none = Type3('none', TypeApplication_Nullary(None, None))
"""
The none type, for when functions simply don't return anything. e.g., IO().
"""
bool_ = Type3('bool', TypeApplication_Nullary(None, None))
"""
The bool type, either True or False
Suffixes with an underscores, as it's a Python builtin
"""
u8 = Type3('u8', TypeApplication_Nullary(None, None))
"""
The unsigned 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8.
"""
u32 = Type3('u32', TypeApplication_Nullary(None, None))
"""
The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32.
"""
u64 = Type3('u64', TypeApplication_Nullary(None, None))
"""
The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64.
"""
i8 = Type3('i8', TypeApplication_Nullary(None, None))
"""
The signed 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8, but
with the middel point being 0.
"""
i32 = Type3('i32', TypeApplication_Nullary(None, None))
"""
The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32, but
with the middel point being 0.
"""
i64 = Type3('i64', TypeApplication_Nullary(None, None))
"""
The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64, but
with the middel point being 0.
"""
f32 = Type3('f32', TypeApplication_Nullary(None, None))
"""
A 32-bits IEEE 754 float, of 32 bits width.
"""
f64 = Type3('f64', TypeApplication_Nullary(None, None))
"""
A 32-bits IEEE 754 float, of 64 bits width.
"""
dynamic_array = TypeConstructor_DynamicArray('dynamic_array')
"""
This is a dynamic length piece of memory.
It should be applied with two arguments. It has a runtime
determined length, and each argument is the same.
"""
static_array = TypeConstructor_StaticArray('static_array')
"""
This is a fixed length piece of memory.
It should be applied with two arguments. It has a compile time
determined length, and each argument is the same.
"""
tuple_ = TypeConstructor_Tuple('tuple')
"""
This is a fixed length piece of memory.
It should be applied with zero or more arguments. It has a compile time
determined length, and each argument can be different.
"""
function = TypeConstructor_Function('function')
"""
This is a function.
It should be applied with one or more arguments. The last argument is the 'return' type.
"""
struct = TypeConstructor_Struct('struct')
"""
This is like a tuple, but each argument is named, so that developers
can get and set fields by name.
"""
a = TypeVariable('a', TypeVariableApplication_Nullary(None, None))
b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
t = TypeConstructorVariable('t')
Eq = Type3Class('Eq', (a, ), methods={}, operators={
'==': [a, a, bool_],
'!=': [a, a, bool_],
# FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization?
})
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],
'max': [a, a, a],
}, operators={
'<': [a, a, bool_],
'<=': [a, a, bool_],
'>': [a, a, bool_],
'>=': [a, a, bool_],
}, inherited_classes=[Eq])
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
'shr': [a, u32, a], # Logical shift right
'rotl': [a, u32, a], # Rotate bits left
'rotr': [a, u32, a], # Rotate bits right
# FIXME: Do we want to expose clz, ctz, popcnt?
}, operators={
'&': [a, a, a], # Bit-wise and
'|': [a, a, a], # Bit-wise or
'^': [a, a, a], # Bit-wise xor
})
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],
'-': [a, a, a],
'*': [a, a, a],
'<<': [a, u32, a], # Arithmic shift left
'>>': [a, u32, a], # Arithmic shift right
})
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, 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('Integral', (a, ), methods={
}, operators={
'//': [a, a, a],
'%': [a, a, a],
}, inherited_classes=[NatNum])
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],
'floor': [a, a],
'trunc': [a, a],
'nearest': [a, a],
}, operators={
'/': [a, a, a],
}, inherited_classes=[NatNum])
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],
}, operators={}, inherited_classes=[Fractional])
# FIXME: Do we want to expose copysign?
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', (t, ), methods={
'len': [t(a), u32],
}, operators={}) # FIXME: Once we get type class families, add [] here
instance_type_class(Sized_, dynamic_array, methods={
'len': stdtypes.dynamic_array_sized_len,
})
instance_type_class(Sized_, static_array, methods={
'len': stdtypes.static_array_sized_len,
})
Extendable = Type3Class('Extendable', (a, b, ), methods={
'extend': [a, b],
'wrap': [b, a],
}, operators={})
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, methods={
'promote': stdtypes.f32_f64_promote,
'demote': stdtypes.f32_f64_demote,
})
Foldable = Type3Class('Foldable', (t, ), methods={
'sum': [t(a), a],
}, operators={}, additional_context={
'sum': [Constraint_TypeClassInstanceExists(NatNum, (a, ))],
})
instance_type_class(Foldable, static_array, methods={
'sum': stdtypes.static_array_sum,
})
bytes_ = dynamic_array(u8)
PRELUDE_TYPES: dict[str, Type3] = {
'none': none,
'bool': bool_,
'u8': u8,
'u32': u32,
'u64': u64,
'i8': i8,
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
'bytes': bytes_,
}
PRELUDE_TYPE_CLASSES = {
'Eq': Eq,
'Ord': Ord,
'Bits': Bits,
'NatNum': NatNum,
'IntNum': IntNum,
'Integral': Integral,
'Fractional': Fractional,
'Floating': Floating,
'Extendable': Extendable,
'Promotable': Promotable,
}
PRELUDE_OPERATORS = {
**Bits.operators,
**Eq.operators,
**Ord.operators,
**Fractional.operators,
**Integral.operators,
**IntNum.operators,
**NatNum.operators,
}
PRELUDE_METHODS = {
**Bits.methods,
**Eq.methods,
**Ord.methods,
**Floating.methods,
**Fractional.methods,
**Integral.methods,
**IntNum.methods,
**NatNum.methods,
**Sized_.methods,
**Extendable.methods,
**Promotable.methods,
**Foldable.methods,
}