Implements sum for Foldable types

Foldable take a TypeConstructor. The first argument must be a
NatNum.

The FunctionSignatureRouter wasn't completely on point, instead
this commit adds an TypeClassArgsRouter lookup router. This
makes sense since the only available arguments we have to find
a router is the list of type class arguments.
This commit is contained in:
Johan B.W. de Vries 2025-05-05 14:09:38 +02:00
parent 6c627bca01
commit b5f0fda133
13 changed files with 986 additions and 313 deletions

View File

@ -12,6 +12,7 @@
- Also, check the codes for FIXME and TODO - Also, check the codes for FIXME and TODO
- Allocation is done using pointers for members, is this desired? - Allocation is done using pointers for members, is this desired?
- See if we want to replace Fractional with Real, and add Rational, Irrationl, Algebraic, Transendental - See if we want to replace Fractional with Real, and add Rational, Irrationl, Algebraic, Transendental
- Implement q32? q64? Two i32/i64 divided?
- Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type? - Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type?
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types. - Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
- Have a set of rules or guidelines for the constraint comments, they're messy. - Have a set of rules or guidelines for the constraint comments, they're messy.

View File

@ -2,7 +2,7 @@
This module contains the code to convert parsed Ourlang into WebAssembly code This module contains the code to convert parsed Ourlang into WebAssembly code
""" """
import struct import struct
from typing import Dict, List, Optional from typing import List, Optional
from . import codestyle, ourlang, prelude, wasm from . import codestyle, ourlang, prelude, wasm
from .runtime import calculate_alloc_size, calculate_member_offset from .runtime import calculate_alloc_size, calculate_member_offset
@ -292,15 +292,20 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
expression(wgn, inp.left) expression(wgn, inp.left)
expression(wgn, inp.right) expression(wgn, inp.right)
type_var_map: Dict[type3functions.TypeVariable, type3types.Type3] = {} 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): for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
if not isinstance(type_var, type3functions.TypeVariable): assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
if isinstance(type_var, type3types.Type3):
# Fixed type, not part of the lookup requirements # Fixed type, not part of the lookup requirements
continue continue
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR if isinstance(type_var, type3functions.TypeVariable):
type_var_map[type_var] = arg_expr.type3 type_var_map[type_var] = arg_expr.type3
continue
raise NotImplementedError(type_var, arg_expr.type3)
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.operator] router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.operator]
router(wgn, type_var_map) router(wgn, type_var_map)
@ -315,12 +320,17 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
type_var_map = {} type_var_map = {}
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True): for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
if not isinstance(type_var, type3functions.TypeVariable): assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
if isinstance(type_var, type3types.Type3):
# Fixed type, not part of the lookup requirements # Fixed type, not part of the lookup requirements
continue continue
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR if isinstance(type_var, type3functions.TypeVariable):
type_var_map[type_var] = arg_expr.type3 type_var_map[type_var] = arg_expr.type3
continue
raise NotImplementedError(type_var, arg_expr.type3)
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.function] router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.function]
try: try:

View File

@ -1,27 +1,33 @@
""" """
The prelude are all the builtin types, type classes and methods The prelude are all the builtin types, type classes and methods
""" """
from typing import Callable from typing import Any, Callable
from warnings import warn from warnings import warn
from phasm.stdlib import types as stdtypes from phasm.stdlib import types as stdtypes
from phasm.wasmgenerator import Generator from phasm.wasmgenerator import Generator
from ..type3.functions import TypeVariable from ..type3.functions import (
from ..type3.routers import FunctionSignatureRouter Constraint_TypeClassInstanceExists,
TypeConstructorVariable,
TypeVariable,
TypeVariableApplication_Nullary,
)
from ..type3.routers import TypeClassArgsRouter, TypeVariableLookup
from ..type3.typeclasses import Type3Class, Type3ClassMethod from ..type3.typeclasses import Type3Class, Type3ClassMethod
from ..type3.types import ( from ..type3.types import (
IntType3, IntType3,
Type3, Type3,
TypeApplication_Nullary, TypeApplication_Nullary,
TypeConstructor_Base,
TypeConstructor_StaticArray, TypeConstructor_StaticArray,
TypeConstructor_Struct, TypeConstructor_Struct,
TypeConstructor_Tuple, TypeConstructor_Tuple,
) )
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3, ...]]] = set() PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3 | TypeConstructor_Base[Any], ...]]] = set()
PRELUDE_TYPE_CLASS_INSTANCE_METHODS: dict[Type3ClassMethod, FunctionSignatureRouter[Generator, None]] = {} PRELUDE_TYPE_CLASS_INSTANCE_METHODS: dict[Type3ClassMethod, TypeClassArgsRouter[Generator, None]] = {}
class MissingImplementationException(Exception): class MissingImplementationException(Exception):
pass pass
@ -31,18 +37,26 @@ class MissingImplementationWarning(Warning):
def instance_type_class( def instance_type_class(
cls: Type3Class, cls: Type3Class,
*typ: Type3, *typ: Type3 | TypeConstructor_Base[Any],
methods: dict[str, Callable[[Generator], None]] = {}, methods: dict[str, Callable[[Generator, TypeVariableLookup], None]] = {},
operators: dict[str, Callable[[Generator], None]] = {}, operators: dict[str, Callable[[Generator, TypeVariableLookup], None]] = {},
) -> None: ) -> None:
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
global PRELUDE_TYPE_CLASS_INSTANCE_METHODS global PRELUDE_TYPE_CLASS_INSTANCE_METHODS
assert len(cls.args) == len(typ) assert len(cls.args) == len(typ)
type_var_map = {} tv_map: dict[TypeVariable, Type3] = {}
tc_map: dict[TypeConstructorVariable, TypeConstructor_Base[Any]] = {}
for arg_tv, arg_tp in zip(cls.args, typ, strict=True): for arg_tv, arg_tp in zip(cls.args, typ, strict=True):
type_var_map[arg_tv] = arg_tp 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 # TODO: Check for required existing instantiations
@ -51,7 +65,7 @@ def instance_type_class(
for method_name, method in cls.methods.items(): for method_name, method in cls.methods.items():
router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(method) router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(method)
if router is None: if router is None:
router = FunctionSignatureRouter[Generator, None](method.signature) router = TypeClassArgsRouter[Generator, None](cls.args)
PRELUDE_TYPE_CLASS_INSTANCE_METHODS[method] = router PRELUDE_TYPE_CLASS_INSTANCE_METHODS[method] = router
try: try:
@ -60,12 +74,12 @@ def instance_type_class(
warn(MissingImplementationWarning(str(method), cls.name + ' ' + ' '.join(x.name for x in typ))) warn(MissingImplementationWarning(str(method), cls.name + ' ' + ' '.join(x.name for x in typ)))
continue continue
router.add(type_var_map, generator) router.add(tv_map, tc_map, generator)
for operator_name, operator in cls.operators.items(): for operator_name, operator in cls.operators.items():
router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(operator) router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(operator)
if router is None: if router is None:
router = FunctionSignatureRouter[Generator, None](operator.signature) router = TypeClassArgsRouter[Generator, None](cls.args)
PRELUDE_TYPE_CLASS_INSTANCE_METHODS[operator] = router PRELUDE_TYPE_CLASS_INSTANCE_METHODS[operator] = router
try: try:
@ -74,7 +88,7 @@ def instance_type_class(
warn(MissingImplementationWarning(str(operator), cls.name + ' ' + ' '.join(x.name for x in typ))) warn(MissingImplementationWarning(str(operator), cls.name + ' ' + ' '.join(x.name for x in typ)))
continue continue
router.add(type_var_map, generator) router.add(tv_map, tc_map, generator)
none = Type3('none', TypeApplication_Nullary(None, None)) none = Type3('none', TypeApplication_Nullary(None, None))
""" """
@ -195,11 +209,12 @@ PRELUDE_TYPES: dict[str, Type3] = {
'bytes': bytes_, 'bytes': bytes_,
} }
a = TypeVariable('a') a = TypeVariable('a', TypeVariableApplication_Nullary(None, None))
b = TypeVariable('b') b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
t = TypeConstructorVariable('t')
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={}) InternalPassAsPointer = Type3Class('InternalPassAsPointer', (a, ), methods={}, operators={})
""" """
Internal type class to keep track which types we pass arounds as a pointer. Internal type class to keep track which types we pass arounds as a pointer.
""" """
@ -209,7 +224,7 @@ instance_type_class(InternalPassAsPointer, bytes_)
# instance_type_class(InternalPassAsPointer, tuple_) # instance_type_class(InternalPassAsPointer, tuple_)
# instance_type_class(InternalPassAsPointer, struct) # instance_type_class(InternalPassAsPointer, struct)
Eq = Type3Class('Eq', [a], methods={}, operators={ Eq = Type3Class('Eq', (a, ), methods={}, operators={
'==': [a, a, bool_], '==': [a, a, bool_],
'!=': [a, a, bool_], '!=': [a, a, bool_],
# FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization? # FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization?
@ -248,7 +263,7 @@ instance_type_class(Eq, f64, operators={
'!=': stdtypes.f64_eq_not_equals, '!=': stdtypes.f64_eq_not_equals,
}) })
Ord = Type3Class('Ord', [a], methods={ Ord = Type3Class('Ord', (a, ), methods={
'min': [a, a, a], 'min': [a, a, a],
'max': [a, a, a], 'max': [a, a, a],
}, operators={ }, operators={
@ -331,7 +346,7 @@ instance_type_class(Ord, f64, methods={
'>=': stdtypes.f64_ord_greater_than_or_equal, '>=': stdtypes.f64_ord_greater_than_or_equal,
}) })
Bits = Type3Class('Bits', [a], methods={ Bits = Type3Class('Bits', (a, ), methods={
'shl': [a, u32, a], # Logical shift left 'shl': [a, u32, a], # Logical shift left
'shr': [a, u32, a], # Logical shift right 'shr': [a, u32, a], # Logical shift right
'rotl': [a, u32, a], # Rotate bits left 'rotl': [a, u32, a], # Rotate bits left
@ -374,7 +389,7 @@ instance_type_class(Bits, u64, methods={
'^': stdtypes.u64_bits_bitwise_xor, '^': stdtypes.u64_bits_bitwise_xor,
}) })
NatNum = Type3Class('NatNum', [a], methods={}, operators={ NatNum = Type3Class('NatNum', (a, ), methods={}, operators={
'+': [a, a, a], '+': [a, a, a],
'-': [a, a, a], '-': [a, a, a],
'*': [a, a, a], '*': [a, a, a],
@ -425,7 +440,7 @@ instance_type_class(NatNum, f64, operators={
'>>': stdtypes.f64_natnum_arithmic_shift_right, '>>': stdtypes.f64_natnum_arithmic_shift_right,
}) })
IntNum = Type3Class('IntNum', [a], methods={ IntNum = Type3Class('IntNum', (a, ), methods={
'abs': [a, a], 'abs': [a, a],
'neg': [a, a], 'neg': [a, a],
}, operators={}, inherited_classes=[NatNum]) }, operators={}, inherited_classes=[NatNum])
@ -447,7 +462,7 @@ instance_type_class(IntNum, f64, methods={
'neg': stdtypes.f64_intnum_neg, 'neg': stdtypes.f64_intnum_neg,
}) })
Integral = Type3Class('Eq', [a], methods={ Integral = Type3Class('Eq', (a, ), methods={
}, operators={ }, operators={
'//': [a, a, a], '//': [a, a, a],
'%': [a, a, a], '%': [a, a, a],
@ -470,7 +485,7 @@ instance_type_class(Integral, i64, operators={
'%': stdtypes.i64_integral_rem, '%': stdtypes.i64_integral_rem,
}) })
Fractional = Type3Class('Fractional', [a], methods={ Fractional = Type3Class('Fractional', (a, ), methods={
'ceil': [a, a], 'ceil': [a, a],
'floor': [a, a], 'floor': [a, a],
'trunc': [a, a], 'trunc': [a, a],
@ -496,7 +511,7 @@ instance_type_class(Fractional, f64, methods={
'/': stdtypes.f64_fractional_div, '/': stdtypes.f64_fractional_div,
}) })
Floating = Type3Class('Floating', [a], methods={ Floating = Type3Class('Floating', (a, ), methods={
'sqrt': [a, a], 'sqrt': [a, a],
}, operators={}, inherited_classes=[Fractional]) }, operators={}, inherited_classes=[Fractional])
@ -509,7 +524,7 @@ instance_type_class(Floating, f64, methods={
'sqrt': stdtypes.f64_floating_sqrt, 'sqrt': stdtypes.f64_floating_sqrt,
}) })
Sized_ = Type3Class('Sized', [a], methods={ Sized_ = Type3Class('Sized', (a, ), methods={
'len': [a, u32], 'len': [a, u32],
}, operators={}) # FIXME: Once we get type class families, add [] here }, operators={}) # FIXME: Once we get type class families, add [] here
@ -517,7 +532,7 @@ instance_type_class(Sized_, bytes_, methods={
'len': stdtypes.bytes_sized_len, 'len': stdtypes.bytes_sized_len,
}) })
Extendable = Type3Class('Extendable', [a, b], methods={ Extendable = Type3Class('Extendable', (a, b, ), methods={
'extend': [a, b], 'extend': [a, b],
'wrap': [b, a], 'wrap': [b, a],
}, operators={}) }, operators={})
@ -547,7 +562,7 @@ instance_type_class(Extendable, i32, i64, methods={
'wrap': stdtypes.i32_i64_wrap, 'wrap': stdtypes.i32_i64_wrap,
}) })
Promotable = Type3Class('Promotable', [a, b], methods={ Promotable = Type3Class('Promotable', (a, b, ), methods={
'promote': [a, b], 'promote': [a, b],
'demote': [b, a], 'demote': [b, a],
}, operators={}) }, operators={})
@ -557,6 +572,16 @@ instance_type_class(Promotable, f32, f64, methods={
'demote': stdtypes.f32_f64_demote, '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,
})
PRELUDE_TYPE_CLASSES = { PRELUDE_TYPE_CLASSES = {
'Eq': Eq, 'Eq': Eq,
'Ord': Ord, 'Ord': Ord,
@ -592,4 +617,5 @@ PRELUDE_METHODS = {
**Sized_.methods, **Sized_.methods,
**Extendable.methods, **Extendable.methods,
**Promotable.methods, **Promotable.methods,
**Foldable.methods,
} }

View File

@ -2,6 +2,8 @@
stdlib: Standard types that are not wasm primitives stdlib: Standard types that are not wasm primitives
""" """
from phasm.stdlib import alloc from phasm.stdlib import alloc
from phasm.type3.routers import TypeVariableLookup
from phasm.type3.types import IntType3, Type3
from phasm.wasmgenerator import Generator, func_wrapper from phasm.wasmgenerator import Generator, func_wrapper
from phasm.wasmgenerator import VarType_i32 as i32 from phasm.wasmgenerator import VarType_i32 as i32
from phasm.wasmgenerator import VarType_i64 as i64 from phasm.wasmgenerator import VarType_i64 as i64
@ -387,444 +389,579 @@ def __u8_rotr__(g: Generator, x: i32, r: i32) -> i32:
## ### ## ###
## class Eq ## class Eq
def u8_eq_equals(g: Generator) -> None: def u8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq() g.i32.eq()
def u32_eq_equals(g: Generator) -> None: def u32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq() g.i32.eq()
def u64_eq_equals(g: Generator) -> None: def u64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.eq() g.i64.eq()
def i8_eq_equals(g: Generator) -> None: def i8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq() g.i32.eq()
def i32_eq_equals(g: Generator) -> None: def i32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.eq() g.i32.eq()
def i64_eq_equals(g: Generator) -> None: def i64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.eq() g.i64.eq()
def f32_eq_equals(g: Generator) -> None: def f32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.eq() g.f32.eq()
def f64_eq_equals(g: Generator) -> None: def f64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.eq() g.f64.eq()
def u8_eq_not_equals(g: Generator) -> None: def u8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne() g.i32.ne()
def u32_eq_not_equals(g: Generator) -> None: def u32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne() g.i32.ne()
def u64_eq_not_equals(g: Generator) -> None: def u64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ne() g.i64.ne()
def i8_eq_not_equals(g: Generator) -> None: def i8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne() g.i32.ne()
def i32_eq_not_equals(g: Generator) -> None: def i32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ne() g.i32.ne()
def i64_eq_not_equals(g: Generator) -> None: def i64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ne() g.i64.ne()
def f32_eq_not_equals(g: Generator) -> None: def f32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.ne() g.f32.ne()
def f64_eq_not_equals(g: Generator) -> None: def f64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.ne() g.f64.ne()
## ### ## ###
## class Ord ## class Ord
def u8_ord_min(g: Generator) -> None: def u8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_min__') g.add_statement('call $stdlib.types.__u32_ord_min__')
def u32_ord_min(g: Generator) -> None: def u32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_min__') g.add_statement('call $stdlib.types.__u32_ord_min__')
def u64_ord_min(g: Generator) -> None: def u64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u64_ord_min__') g.add_statement('call $stdlib.types.__u64_ord_min__')
def i8_ord_min(g: Generator) -> None: def i8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_min__') g.add_statement('call $stdlib.types.__i32_ord_min__')
def i32_ord_min(g: Generator) -> None: def i32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_min__') g.add_statement('call $stdlib.types.__i32_ord_min__')
def i64_ord_min(g: Generator) -> None: def i64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i64_ord_min__') g.add_statement('call $stdlib.types.__i64_ord_min__')
def f32_ord_min(g: Generator) -> None: def f32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.min() g.f32.min()
def f64_ord_min(g: Generator) -> None: def f64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.min() g.f64.min()
def u8_ord_max(g: Generator) -> None: def u8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_max__') g.add_statement('call $stdlib.types.__u32_ord_max__')
def u32_ord_max(g: Generator) -> None: def u32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_ord_max__') g.add_statement('call $stdlib.types.__u32_ord_max__')
def u64_ord_max(g: Generator) -> None: def u64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u64_ord_max__') g.add_statement('call $stdlib.types.__u64_ord_max__')
def i8_ord_max(g: Generator) -> None: def i8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_max__') g.add_statement('call $stdlib.types.__i32_ord_max__')
def i32_ord_max(g: Generator) -> None: def i32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_ord_max__') g.add_statement('call $stdlib.types.__i32_ord_max__')
def i64_ord_max(g: Generator) -> None: def i64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i64_ord_max__') g.add_statement('call $stdlib.types.__i64_ord_max__')
def f32_ord_max(g: Generator) -> None: def f32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.max() g.f32.max()
def f64_ord_max(g: Generator) -> None: def f64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.max() g.f64.max()
def u8_ord_less_than(g: Generator) -> None: def u8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_u() g.i32.lt_u()
def u32_ord_less_than(g: Generator) -> None: def u32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_u() g.i32.lt_u()
def u64_ord_less_than(g: Generator) -> None: def u64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.lt_u() g.i64.lt_u()
def i8_ord_less_than(g: Generator) -> None: def i8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_s() g.i32.lt_s()
def i32_ord_less_than(g: Generator) -> None: def i32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.lt_s() g.i32.lt_s()
def i64_ord_less_than(g: Generator) -> None: def i64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.lt_s() g.i64.lt_s()
def f32_ord_less_than(g: Generator) -> None: def f32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.lt() g.f32.lt()
def f64_ord_less_than(g: Generator) -> None: def f64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.lt() g.f64.lt()
def u8_ord_less_than_or_equal(g: Generator) -> None: def u8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_u() g.i32.le_u()
def u32_ord_less_than_or_equal(g: Generator) -> None: def u32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_u() g.i32.le_u()
def u64_ord_less_than_or_equal(g: Generator) -> None: def u64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.le_u() g.i64.le_u()
def i8_ord_less_than_or_equal(g: Generator) -> None: def i8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_s() g.i32.le_s()
def i32_ord_less_than_or_equal(g: Generator) -> None: def i32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.le_s() g.i32.le_s()
def i64_ord_less_than_or_equal(g: Generator) -> None: def i64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.le_s() g.i64.le_s()
def f32_ord_less_than_or_equal(g: Generator) -> None: def f32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.le() g.f32.le()
def f64_ord_less_than_or_equal(g: Generator) -> None: def f64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.le() g.f64.le()
def u8_ord_greater_than(g: Generator) -> None: def u8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_u() g.i32.gt_u()
def u32_ord_greater_than(g: Generator) -> None: def u32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_u() g.i32.gt_u()
def u64_ord_greater_than(g: Generator) -> None: def u64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.gt_u() g.i64.gt_u()
def i8_ord_greater_than(g: Generator) -> None: def i8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_s() g.i32.gt_s()
def i32_ord_greater_than(g: Generator) -> None: def i32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.gt_s() g.i32.gt_s()
def i64_ord_greater_than(g: Generator) -> None: def i64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.gt_s() g.i64.gt_s()
def f32_ord_greater_than(g: Generator) -> None: def f32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.gt() g.f32.gt()
def f64_ord_greater_than(g: Generator) -> None: def f64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.gt() g.f64.gt()
def u8_ord_greater_than_or_equal(g: Generator) -> None: def u8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_u() g.i32.ge_u()
def u32_ord_greater_than_or_equal(g: Generator) -> None: def u32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_u() g.i32.ge_u()
def u64_ord_greater_than_or_equal(g: Generator) -> None: def u64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ge_u() g.i64.ge_u()
def i8_ord_greater_than_or_equal(g: Generator) -> None: def i8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_s() g.i32.ge_s()
def i32_ord_greater_than_or_equal(g: Generator) -> None: def i32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.ge_s() g.i32.ge_s()
def i64_ord_greater_than_or_equal(g: Generator) -> None: def i64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.ge_s() g.i64.ge_s()
def f32_ord_greater_than_or_equal(g: Generator) -> None: def f32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.ge() g.f32.ge()
def f64_ord_greater_than_or_equal(g: Generator) -> None: def f64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.ge() g.f64.ge()
## ### ## ###
## class Bits ## class Bits
def u8_bits_logical_shift_left(g: Generator) -> None: def u8_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl() g.i32.shl()
g.i32.const(255) g.i32.const(255)
g.i32.and_() g.i32.and_()
def u32_bits_logical_shift_left(g: Generator) -> None: def u32_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl() g.i32.shl()
def u64_bits_logical_shift_left(g: Generator) -> None: def u64_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.shl() g.i64.shl()
def u8_bits_logical_shift_right(g: Generator) -> None: def u8_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_u() g.i32.shr_u()
def u32_bits_logical_shift_right(g: Generator) -> None: def u32_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_u() g.i32.shr_u()
def u64_bits_logical_shift_right(g: Generator) -> None: def u64_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.shr_u() g.i64.shr_u()
def u8_bits_rotate_left(g: Generator) -> None: def u8_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u8_rotl__') g.add_statement('call $stdlib.types.__u8_rotl__')
def u32_bits_rotate_left(g: Generator) -> None: def u32_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.rotl() g.i32.rotl()
def u64_bits_rotate_left(g: Generator) -> None: def u64_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.rotl() g.i64.rotl()
def u8_bits_rotate_right(g: Generator) -> None: def u8_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u8_rotr__') g.add_statement('call $stdlib.types.__u8_rotr__')
def u32_bits_rotate_right(g: Generator) -> None: def u32_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.rotr() g.i32.rotr()
def u64_bits_rotate_right(g: Generator) -> None: def u64_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.rotr() g.i64.rotr()
def u8_bits_bitwise_and(g: Generator) -> None: def u8_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.and_() g.i32.and_()
def u32_bits_bitwise_and(g: Generator) -> None: def u32_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.and_() g.i32.and_()
def u64_bits_bitwise_and(g: Generator) -> None: def u64_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.and_() g.i64.and_()
def u8_bits_bitwise_or(g: Generator) -> None: def u8_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.or_() g.i32.or_()
def u32_bits_bitwise_or(g: Generator) -> None: def u32_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.or_() g.i32.or_()
def u64_bits_bitwise_or(g: Generator) -> None: def u64_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.or_() g.i64.or_()
def u8_bits_bitwise_xor(g: Generator) -> None: def u8_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.xor() g.i32.xor()
def u32_bits_bitwise_xor(g: Generator) -> None: def u32_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.xor() g.i32.xor()
def u64_bits_bitwise_xor(g: Generator) -> None: def u64_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.xor() g.i64.xor()
## ### ## ###
## class Fractional ## class Fractional
def f32_fractional_ceil(g: Generator) -> None: def f32_fractional_ceil(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.ceil() g.f32.ceil()
def f64_fractional_ceil(g: Generator) -> None: def f64_fractional_ceil(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.ceil() g.f64.ceil()
def f32_fractional_floor(g: Generator) -> None: def f32_fractional_floor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.floor() g.f32.floor()
def f64_fractional_floor(g: Generator) -> None: def f64_fractional_floor(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.floor() g.f64.floor()
def f32_fractional_trunc(g: Generator) -> None: def f32_fractional_trunc(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.trunc() g.f32.trunc()
def f64_fractional_trunc(g: Generator) -> None: def f64_fractional_trunc(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.trunc() g.f64.trunc()
def f32_fractional_nearest(g: Generator) -> None: def f32_fractional_nearest(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.nearest() g.f32.nearest()
def f64_fractional_nearest(g: Generator) -> None: def f64_fractional_nearest(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.nearest() g.f64.nearest()
def f32_fractional_div(g: Generator) -> None: def f32_fractional_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.div() g.f32.div()
def f64_fractional_div(g: Generator) -> None: def f64_fractional_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.div() g.f64.div()
## ### ## ###
## class Floating ## class Floating
def f32_floating_sqrt(g: Generator) -> None: def f32_floating_sqrt(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.sqrt') g.add_statement('f32.sqrt')
def f64_floating_sqrt(g: Generator) -> None: def f64_floating_sqrt(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.sqrt') g.add_statement('f64.sqrt')
## ### ## ###
## class Integral ## class Integral
def u32_integral_div(g: Generator) -> None: def u32_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.div_u') g.add_statement('i32.div_u')
def u64_integral_div(g: Generator) -> None: def u64_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.div_u') g.add_statement('i64.div_u')
def i32_integral_div(g: Generator) -> None: def i32_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.div_s') g.add_statement('i32.div_s')
def i64_integral_div(g: Generator) -> None: def i64_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.div_s') g.add_statement('i64.div_s')
def u32_integral_rem(g: Generator) -> None: def u32_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.rem_u') g.add_statement('i32.rem_u')
def u64_integral_rem(g: Generator) -> None: def u64_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.rem_u') g.add_statement('i64.rem_u')
def i32_integral_rem(g: Generator) -> None: def i32_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.rem_s') g.add_statement('i32.rem_s')
def i64_integral_rem(g: Generator) -> None: def i64_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.rem_s') g.add_statement('i64.rem_s')
## ### ## ###
## class NatNum ## class NatNum
def u32_natnum_add(g: Generator) -> None: def u32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.add') g.add_statement('i32.add')
def u64_natnum_add(g: Generator) -> None: def u64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.add') g.add_statement('i64.add')
def i32_natnum_add(g: Generator) -> None: def i32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.add') g.add_statement('i32.add')
def i64_natnum_add(g: Generator) -> None: def i64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.add') g.add_statement('i64.add')
def f32_natnum_add(g: Generator) -> None: def f32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.add') g.add_statement('f32.add')
def f64_natnum_add(g: Generator) -> None: def f64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.add') g.add_statement('f64.add')
def u32_natnum_sub(g: Generator) -> None: def u32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.sub') g.add_statement('i32.sub')
def u64_natnum_sub(g: Generator) -> None: def u64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.sub') g.add_statement('i64.sub')
def i32_natnum_sub(g: Generator) -> None: def i32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.sub') g.add_statement('i32.sub')
def i64_natnum_sub(g: Generator) -> None: def i64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.sub') g.add_statement('i64.sub')
def f32_natnum_sub(g: Generator) -> None: def f32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.sub') g.add_statement('f32.sub')
def f64_natnum_sub(g: Generator) -> None: def f64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.sub') g.add_statement('f64.sub')
def u32_natnum_mul(g: Generator) -> None: def u32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.mul') g.add_statement('i32.mul')
def u64_natnum_mul(g: Generator) -> None: def u64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.mul') g.add_statement('i64.mul')
def i32_natnum_mul(g: Generator) -> None: def i32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i32.mul') g.add_statement('i32.mul')
def i64_natnum_mul(g: Generator) -> None: def i64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('i64.mul') g.add_statement('i64.mul')
def f32_natnum_mul(g: Generator) -> None: def f32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f32.mul') g.add_statement('f32.mul')
def f64_natnum_mul(g: Generator) -> None: def f64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('f64.mul') g.add_statement('f64.mul')
def u32_natnum_arithmic_shift_left(g: Generator) -> None: def u32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl() g.i32.shl()
def u64_natnum_arithmic_shift_left(g: Generator) -> None: def u64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.shl() g.i64.shl()
def i32_natnum_arithmic_shift_left(g: Generator) -> None: def i32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shl() g.i32.shl()
def i64_natnum_arithmic_shift_left(g: Generator) -> None: def i64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.shl() g.i64.shl()
def f32_natnum_arithmic_shift_left(g: Generator) -> None: def f32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__') g.add_statement('call $stdlib.types.__u32_pow2__')
g.f32.convert_i32_u() g.f32.convert_i32_u()
g.f32.mul() g.f32.mul()
def f64_natnum_arithmic_shift_left(g: Generator) -> None: def f64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__') g.add_statement('call $stdlib.types.__u32_pow2__')
g.f64.convert_i32_u() g.f64.convert_i32_u()
g.f64.mul() g.f64.mul()
def u32_natnum_arithmic_shift_right(g: Generator) -> None: def u32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_u() g.i32.shr_u()
def u64_natnum_arithmic_shift_right(g: Generator) -> None: def u64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.shr_u() g.i64.shr_u()
def i32_natnum_arithmic_shift_right(g: Generator) -> None: def i32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.shr_s() g.i32.shr_s()
def i64_natnum_arithmic_shift_right(g: Generator) -> None: def i64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
g.i64.shr_s() g.i64.shr_s()
def f32_natnum_arithmic_shift_right(g: Generator) -> None: def f32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__') g.add_statement('call $stdlib.types.__u32_pow2__')
g.f32.convert_i32_u() g.f32.convert_i32_u()
g.f32.div() g.f32.div()
def f64_natnum_arithmic_shift_right(g: Generator) -> None: def f64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__u32_pow2__') g.add_statement('call $stdlib.types.__u32_pow2__')
g.f64.convert_i32_u() g.f64.convert_i32_u()
g.f64.div() g.f64.div()
@ -832,91 +969,219 @@ def f64_natnum_arithmic_shift_right(g: Generator) -> None:
## ### ## ###
## class IntNum ## class IntNum
def i32_intnum_abs(g: Generator) -> None: def i32_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i32_intnum_abs__') g.add_statement('call $stdlib.types.__i32_intnum_abs__')
def i64_intnum_abs(g: Generator) -> None: def i64_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.add_statement('call $stdlib.types.__i64_intnum_abs__') g.add_statement('call $stdlib.types.__i64_intnum_abs__')
def f32_intnum_abs(g: Generator) -> None: def f32_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.abs() g.f32.abs()
def f64_intnum_abs(g: Generator) -> None: def f64_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.abs() g.f64.abs()
def i32_intnum_neg(g: Generator) -> None: def i32_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(-1) g.i32.const(-1)
g.i32.mul() g.i32.mul()
def i64_intnum_neg(g: Generator) -> None: def i64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.const(-1) g.i64.const(-1)
g.i64.mul() g.i64.mul()
def f32_intnum_neg(g: Generator) -> None: def f32_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.neg() g.f32.neg()
def f64_intnum_neg(g: Generator) -> None: def f64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.neg() g.f64.neg()
## ### ## ###
## Class Sized ## Class Sized
def bytes_sized_len(g: Generator) -> None: def bytes_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# The length is stored in the first 4 bytes # The length is stored in the first 4 bytes
g.i32.load() g.i32.load()
## ### ## ###
## Extendable ## Extendable
def u8_u32_extend(g: Generator) -> None: def u8_u32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op # No-op
# u8 is already stored as u32 # u8 is already stored as u32
pass pass
def u8_u64_extend(g: Generator) -> None: def u8_u64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
def u32_u64_extend(g: Generator) -> None: def u32_u64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_u() g.i64.extend_i32_u()
def i8_i32_extend(g: Generator) -> None: def i8_i32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
# No-op # No-op
# i8 is already stored as i32 # i8 is already stored as i32
pass pass
def i8_i64_extend(g: Generator) -> None: def i8_i64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_s() g.i64.extend_i32_s()
def i32_i64_extend(g: Generator) -> None: def i32_i64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i64.extend_i32_s() g.i64.extend_i32_s()
def u8_u32_wrap(g: Generator) -> None: def u8_u32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF) g.i32.const(0xFF)
g.i32.and_() g.i32.and_()
def u8_u64_wrap(g: Generator) -> None: def u8_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64() g.i32.wrap_i64()
g.i32.const(0xFF) g.i32.const(0xFF)
g.i32.and_() g.i32.and_()
def u32_u64_wrap(g: Generator) -> None: def u32_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64() g.i32.wrap_i64()
def i8_i32_wrap(g: Generator) -> None: def i8_i32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.const(0xFF) g.i32.const(0xFF)
g.i32.and_() g.i32.and_()
def i8_i64_wrap(g: Generator) -> None: def i8_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64() g.i32.wrap_i64()
def i32_i64_wrap(g: Generator) -> None: def i32_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.i32.wrap_i64() g.i32.wrap_i64()
## ### ## ###
## Promotable ## Promotable
def f32_f64_promote(g: Generator) -> None: def f32_f64_promote(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f64.promote_f32() g.f64.promote_f32()
def f32_f64_demote(g: Generator) -> None: def f32_f64_demote(g: Generator, tv_map: TypeVariableLookup) -> None:
del tv_map
g.f32.demote_f64() g.f32.demote_f64()
def static_array_sum(g: Generator, tv_map: TypeVariableLookup) -> None:
assert len(tv_map) == 1
sa_type, sa_len = next(iter(tv_map.values()))
assert isinstance(sa_type, Type3)
assert isinstance(sa_len, IntType3)
if sa_len.value < 1:
raise NotImplementedError('Default value in case sum is empty')
# FIXME: We should probably use LOAD_STORE_TYPE_MAP for this?
mtyp_map = {
'u32': 'i32',
'u64': 'i64',
'i32': 'i32',
'i64': 'i64',
'f32': 'f32',
'f64': 'f64',
}
# FIXME: We should probably use calc_alloc_size for this?
type_var_size_map = {
'u32': 4,
'u64': 8,
'i32': 4,
'i64': 8,
'f32': 4,
'f64': 8,
}
type_var_add_generator = {
'u32': u32_natnum_add,
'u64': u64_natnum_add,
'i32': i32_natnum_add,
'i64': i64_natnum_add,
'f32': f32_natnum_add,
'f64': f64_natnum_add,
}
# By default, constructed types are passed as pointers
# FIXME: We don't know what add function to call
sa_type_mtyp = mtyp_map.get(sa_type.name, 'i32')
sa_type_alloc_size = type_var_size_map.get(sa_type.name, 4)
sa_type_add_gen = type_var_add_generator[sa_type.name]
# Definitions
sum_adr = g.temp_var(i32('sum_adr'))
sum_stop = g.temp_var(i32('sum_stop'))
# Stack before: [adr]
# Stack after: [sum]
# adr = {address of what's currently on stack}
# Stack: [adr] -> []
g.nop(comment=f'Start sum for {sa_type.name}[{sa_len.value}]')
g.local.set(sum_adr)
# stop = adr + ar_len * sa_type_alloc_size
# Stack: []
g.nop(comment='Calculate address at which to stop looping')
g.local.get(sum_adr)
g.i32.const(sa_len.value * sa_type_alloc_size)
g.i32.add()
g.local.set(sum_stop)
# sum = *adr
# Stack: [] -> [sum]
g.nop(comment='Get the first array value as starting point')
g.local.get(sum_adr)
g.add_statement(f'{sa_type_mtyp}.load')
# Since we did the first one, increase adr
# adr = adr + sa_type_alloc_size
# Stack: [sum] -> [sum]
g.local.get(sum_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.set(sum_adr)
if sa_len.value > 1:
with g.loop(params=[sa_type_mtyp], result=sa_type_mtyp):
# sum = sum + *adr
# Stack: [sum] -> [sum + *adr]
g.nop(comment='Add array value')
g.local.get(sum_adr)
g.add_statement(f'{sa_type_mtyp}.load')
sa_type_add_gen(g, {})
# adr = adr + sa_type_alloc_size
# Stack: [sum] -> [sum]
g.nop(comment='Calculate address of the next value')
g.local.get(sum_adr)
g.i32.const(sa_type_alloc_size)
g.i32.add()
g.local.tee(sum_adr)
# loop if adr < stop
g.nop(comment='Check if address exceeds array bounds')
g.local.get(sum_stop)
g.i32.lt_u()
g.br_if(0)
# else: sum x[1] === x => so we don't need to loop
g.nop(comment=f'Completed sum for {sa_type.name}[{sa_len.value}]')
# End result: [sum]

View File

@ -3,7 +3,7 @@ This module contains possible constraints generated based on the AST
These need to be resolved before the program can be compiled. These need to be resolved before the program can be compiled.
""" """
from typing import Dict, Iterable, List, Optional, Tuple, Union from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
from .. import ourlang, prelude from .. import ourlang, prelude
from . import placeholders, typeclasses, types from . import placeholders, typeclasses, types
@ -50,7 +50,7 @@ class Context:
__slots__ = ('type_class_instances_existing', ) __slots__ = ('type_class_instances_existing', )
# Constraint_TypeClassInstanceExists # Constraint_TypeClassInstanceExists
type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[types.Type3, ...]]] type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[Union[types.Type3, types.TypeConstructor_Base[Any], types.TypeConstructor_Struct], ...]]]
def __init__(self) -> None: def __init__(self) -> None:
self.type_class_instances_existing = set() self.type_class_instances_existing = set()
@ -158,6 +158,46 @@ class SameTypeConstraint(ConstraintBase):
return f'SameTypeConstraint({args}, comment={repr(self.comment)})' return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
class SameTypeArgumentConstraint(ConstraintBase):
__slots__ = ('tc_var', 'arg_var', )
tc_var: PlaceholderForType
arg_var: PlaceholderForType
def __init__(self, tc_var: PlaceholderForType, arg_var: PlaceholderForType, *, comment: str) -> None:
super().__init__(comment=comment)
self.tc_var = tc_var
self.arg_var = arg_var
def check(self) -> CheckResult:
if self.tc_var.resolve_as is None or self.arg_var.resolve_as is None:
return RequireTypeSubstitutes()
tc_typ = self.tc_var.resolve_as
arg_typ = self.arg_var.resolve_as
if isinstance(tc_typ.application, types.TypeApplication_Nullary):
return Error(f'{tc_typ:s} must be a constructed type instead')
if isinstance(tc_typ.application, types.TypeApplication_TypeStar):
# Sure, it's a constructed type. But it's like a struct,
# though without the way to implement type classes
# Presumably, doing a naked `foo :: t a -> a`
# doesn't work since you don't have any info on t
# So we can let the MustImplementTypeClassConstraint handle it.
return None
# FIXME: This feels sketchy. Shouldn't the type variable
# have the exact same number as arguments?
if isinstance(tc_typ.application, types.TypeApplication_TypeInt):
if tc_typ.application.arguments[0] == arg_typ:
return None
return Error(f'{tc_typ.application.arguments[0]:s} must be {arg_typ:s} instead')
raise NotImplementedError(tc_typ, arg_typ)
class TupleMatchConstraint(ConstraintBase): class TupleMatchConstraint(ConstraintBase):
__slots__ = ('exp_type', 'args', ) __slots__ = ('exp_type', 'args', )
@ -229,7 +269,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
self.types = types self.types = types
def check(self) -> CheckResult: def check(self) -> CheckResult:
typ_list = [] typ_list: list[types.Type3 | types.TypeConstructor_Base[Any] | types.TypeConstructor_Struct] = []
for typ in self.types: for typ in self.types:
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None: if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
typ = typ.resolve_as typ = typ.resolve_as
@ -237,7 +277,15 @@ class MustImplementTypeClassConstraint(ConstraintBase):
if isinstance(typ, placeholders.PlaceholderForType): if isinstance(typ, placeholders.PlaceholderForType):
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
typ_list.append(typ) if isinstance(typ.application, (types.TypeApplication_Nullary, types.TypeApplication_Struct, )):
typ_list.append(typ)
continue
if isinstance(typ.application, (types.TypeApplication_TypeInt, types.TypeApplication_TypeStar)):
typ_list.append(typ.application.constructor)
continue
raise NotImplementedError(typ, typ.application)
assert len(typ_list) == len(self.types) assert len(typ_list) == len(self.types)

View File

@ -16,6 +16,7 @@ from .constraints import (
Context, Context,
LiteralFitsConstraint, LiteralFitsConstraint,
MustImplementTypeClassConstraint, MustImplementTypeClassConstraint,
SameTypeArgumentConstraint,
SameTypeConstraint, SameTypeConstraint,
TupleMatchConstraint, TupleMatchConstraint,
) )
@ -39,6 +40,116 @@ def constant(ctx: Context, inp: ourlang.Constant, phft: placeholders.Placeholder
raise NotImplementedError(constant, inp) raise NotImplementedError(constant, inp)
def expression_binary_op(ctx: Context, inp: ourlang.BinaryOp, phft: PlaceholderForType) -> ConstraintGenerator:
return _expression_function_call(
ctx,
f'({inp.operator.name})',
inp.operator.signature,
[inp.left, inp.right],
inp,
phft,
)
def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: PlaceholderForType) -> ConstraintGenerator:
return _expression_function_call(
ctx,
inp.function.name,
inp.function.signature,
inp.arguments,
inp,
phft,
)
def _expression_function_call(
ctx: Context,
func_name: str,
signature: functions.FunctionSignature,
arguments: list[ourlang.Expression],
return_expr: ourlang.Expression,
return_phft: PlaceholderForType,
) -> ConstraintGenerator:
"""
Generates all type-level constraints for a function call.
A Binary operator functions pretty much the same as a function call
with two arguments - it's only a syntactic difference.
"""
# First create placeholders for all arguments, and generate their constraints
arg_placeholders = {
arg_expr: PlaceholderForType([arg_expr])
for arg_expr in arguments
}
arg_placeholders[return_expr] = return_phft
for call_arg in arguments:
yield from expression(ctx, call_arg, arg_placeholders[call_arg])
# Then generate placeholders the function signature
# and apply constraints that the function requires
# Skip any fully reference types
# Making this a map ensures that if a function signature has
# the same type on multiple arguments, we only get one
# placeholder here. These don't need to update anything once
# subsituted - that's done by arg_placeholders.
type_var_map = {
x: placeholders.PlaceholderForType([])
for x in signature.args
if isinstance(x, functions.TypeVariable)
}
for constraint in signature.context.constraints:
if isinstance(constraint, functions.Constraint_TypeClassInstanceExists):
yield MustImplementTypeClassConstraint(
ctx,
constraint.type_class3,
[type_var_map[x] for x in constraint.types],
)
continue
raise NotImplementedError(constraint)
# If some of the function arguments are type constructors,
# we need to deal with those separately.
# That is, given `foo :: t a -> a` we need to ensure
# that both a's are the same.
for sig_arg in signature.args:
if isinstance(sig_arg, type3types.Type3):
# Not a type variable at all
continue
if sig_arg.application.constructor is None:
# Not a type variable for a type constructor
continue
if not isinstance(sig_arg.application, functions.TypeVariableApplication_Unary):
raise NotImplementedError(sig_arg.application)
assert sig_arg.application.arguments in type_var_map # When does this happen?
yield SameTypeArgumentConstraint(
type_var_map[sig_arg],
type_var_map[sig_arg.application.arguments],
comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {signature}',
)
# Lastly, tie the signature and expression together
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [return_expr], strict=True)):
if arg_no == len(arguments):
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
else:
comment = f'The type of the value passed to argument {arg_no} of function {func_name} should match the type of that argument'
if isinstance(sig_part, functions.TypeVariable):
yield SameTypeConstraint(type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
continue
if isinstance(sig_part, type3types.Type3):
yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment)
continue
raise NotImplementedError(sig_part)
return
def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator: def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
if isinstance(inp, ourlang.Constant): if isinstance(inp, ourlang.Constant):
yield from constant(ctx, inp, phft) yield from constant(ctx, inp, phft)
@ -49,53 +160,12 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})') comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
return return
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall): if isinstance(inp, ourlang.BinaryOp):
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature yield from expression_binary_op(ctx, inp, phft)
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments return
func_name = f'({inp.operator.name})' if isinstance(inp, ourlang.BinaryOp) else inp.function.name if isinstance(inp, ourlang.FunctionCall):
yield from expression_function_call(ctx, inp, phft)
type_var_map = {
x: placeholders.PlaceholderForType([])
for x in signature.args
if isinstance(x, functions.TypeVariable)
}
arg_placeholders = {
arg_expr: PlaceholderForType([arg_expr])
for arg_expr in arguments
}
arg_placeholders[inp] = phft
for arg_expr in arguments:
yield from expression(ctx, arg_expr, arg_placeholders[arg_expr])
for constraint in signature.context.constraints:
if isinstance(constraint, functions.Constraint_TypeClassInstanceExists):
yield MustImplementTypeClassConstraint(
ctx,
constraint.type_class3,
[type_var_map[x] for x in constraint.types],
)
continue
raise NotImplementedError(constraint)
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [inp], strict=True)):
if arg_no == len(arguments):
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
else:
comment = f'The type of the value passed to argument {arg_no} of function {func_name} should match the type of that argument'
if isinstance(sig_part, functions.TypeVariable):
yield SameTypeConstraint(type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
continue
if isinstance(sig_part, type3types.Type3):
yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment)
continue
raise NotImplementedError(sig_part)
return return
if isinstance(inp, ourlang.TupleInstantiation): if isinstance(inp, ourlang.TupleInstantiation):

View File

@ -1,10 +1,36 @@
from typing import TYPE_CHECKING, Any, Iterable, List, Union from typing import TYPE_CHECKING, Any, Hashable, Iterable, List, Union
if TYPE_CHECKING: if TYPE_CHECKING:
from .typeclasses import Type3Class from .typeclasses import Type3Class
from .types import Type3 from .types import Type3
class TypeVariableApplication_Base[T: Hashable, S: Hashable]:
"""
Records the constructor and arguments used to create this type.
Nullary types, or types of kind *, have both arguments set to None.
"""
constructor: T
arguments: S
def __init__(self, constructor: T, arguments: S) -> None:
self.constructor = constructor
self.arguments = arguments
def __hash__(self) -> int:
return hash((self.constructor, self.arguments, ))
def __eq__(self, other: Any) -> bool:
if not isinstance(other, TypeVariableApplication_Base):
raise NotImplementedError
return (self.constructor == other.constructor # type: ignore[no-any-return]
and self.arguments == other.arguments)
def __repr__(self) -> str:
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
class TypeVariable: class TypeVariable:
""" """
Types variable are used in function definition. Types variable are used in function definition.
@ -14,25 +40,77 @@ class TypeVariable:
during type checking. These type variables are used solely in the during type checking. These type variables are used solely in the
function's definition function's definition
""" """
__slots__ = ('letter', ) __slots__ = ('name', 'application', )
letter: str name: str
application: TypeVariableApplication_Base[Any, Any]
def __init__(self, letter: str) -> None: def __init__(self, name: str, application: TypeVariableApplication_Base[Any, Any]) -> None:
assert len(letter) == 1, f'{letter} is not a valid type variable' self.name = name
self.letter = letter self.application = application
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(self.letter) return hash((self.name, self.application, ))
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
if not isinstance(other, TypeVariable): if not isinstance(other, TypeVariable):
raise NotImplementedError raise NotImplementedError
return self.letter == other.letter return (self.name == other.name
and self.application == other.application)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'TypeVariable({repr(self.letter)})' return f'TypeVariable({repr(self.name)})'
class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
"""
For the type for this function argument it's not relevant if it was constructed.
"""
class TypeConstructorVariable:
"""
Types constructor variable are used in function definition.
They are a lot like TypeVariable, except that they represent a
type constructor rather than a type directly.
For now, we only have type constructor variables for kind
* -> *.
"""
__slots__ = ('name', )
name: str
def __init__(self, name: str) -> None:
self.name = name
def __hash__(self) -> int:
return hash((self.name, ))
def __eq__(self, other: Any) -> bool:
if other is None:
return False
if not isinstance(other, TypeConstructorVariable):
raise NotImplementedError
return (self.name == other.name)
def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
return TypeVariable(
self.name + ' ' + tvar.name,
TypeVariableApplication_Unary(self, tvar)
)
def __repr__(self) -> str:
return f'TypeConstructorVariable({self.name!r})'
class TypeVariableApplication_Unary(TypeVariableApplication_Base[TypeConstructorVariable, TypeVariable]):
"""
The type for this function argument should be constructed from a type constructor.
And we need to know what construtor that was, since that's the one we support.
"""
class ConstraintBase: class ConstraintBase:
__slots__ = () __slots__ = ()
@ -52,7 +130,7 @@ class Constraint_TypeClassInstanceExists(ConstraintBase):
assert len(self.type_class3.args) == len(self.types) assert len(self.type_class3.args) == len(self.types)
def __str__(self) -> str: def __str__(self) -> str:
return self.type_class3.name + ' ' + ' '.join(x.letter for x in self.types) return self.type_class3.name + ' ' + ' '.join(x.name for x in self.types)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})' return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
@ -62,13 +140,11 @@ class TypeVariableContext:
constraints: list[ConstraintBase] constraints: list[ConstraintBase]
def __init__(self) -> None: def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
self.constraints = [] self.constraints = list(constraints)
def __copy__(self) -> 'TypeVariableContext': def __copy__(self) -> 'TypeVariableContext':
result = TypeVariableContext() return TypeVariableContext(self.constraints)
result.constraints.extend(self.constraints)
return result
def __str__(self) -> str: def __str__(self) -> str:
if not self.constraints: if not self.constraints:
@ -90,7 +166,7 @@ class FunctionSignature:
self.args = list(args) self.args = list(args)
def __str__(self) -> str: def __str__(self) -> str:
return str(self.context) + ' -> '.join(x.letter if isinstance(x, TypeVariable) else x.name for x in self.args) return str(self.context) + ' -> '.join(x.name for x in self.args)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'FunctionSignature({self.context!r}, {self.args!r})' return f'FunctionSignature({self.context!r}, {self.args!r})'

View File

@ -1,9 +1,13 @@
from typing import Any, Callable, TypeVar from typing import Any, Callable
from .functions import FunctionSignature, TypeVariable from .functions import (
from .types import Type3, TypeConstructor_Base TypeConstructorVariable,
TypeVariable,
TypeVariableApplication_Unary,
)
from .typeclasses import Type3ClassArgs
from .types import KindArgument, Type3, TypeApplication_TypeInt, TypeConstructor_Base
T = TypeVar('T')
class NoRouteForTypeException(Exception): class NoRouteForTypeException(Exception):
pass pass
@ -36,7 +40,7 @@ class TypeApplicationRouter[S, R]:
""" """
self.by_type[typ] = helper self.by_type[typ] = helper
def add(self, constructor: TypeConstructor_Base[T], helper: Callable[[S, T], R]) -> None: def add[T](self, constructor: TypeConstructor_Base[T], helper: Callable[[S, T], R]) -> None:
self.by_constructor[constructor] = helper self.by_constructor[constructor] = helper
def __call__(self, arg0: S, typ: Type3) -> R: def __call__(self, arg0: S, typ: Type3) -> R:
@ -50,39 +54,66 @@ class TypeApplicationRouter[S, R]:
raise NoRouteForTypeException(arg0, typ) raise NoRouteForTypeException(arg0, typ)
class FunctionSignatureRouter[S, R]: TypeVariableLookup = dict[TypeVariable, tuple[KindArgument, ...]]
class TypeClassArgsRouter[S, R]:
""" """
Helper class to find a method based on a function signature Helper class to find a method based on a type class argument list
""" """
__slots__ = ('signature', 'data', ) __slots__ = ('args', 'data', )
signature: FunctionSignature args: Type3ClassArgs
data: dict[tuple[Type3, ...], Callable[[S], R]] data: dict[tuple[Type3 | TypeConstructor_Base[Any], ...], Callable[[S, TypeVariableLookup], R]]
def __init__(self, signature: FunctionSignature) -> None: def __init__(self, args: Type3ClassArgs) -> None:
self.signature = signature self.args = args
self.data = {} self.data = {}
def add(self, tv_map: dict[TypeVariable, Type3], helper: Callable[[S], R]) -> None: def add(
key = tuple( self,
tv_map[x] tv_map: dict[TypeVariable, Type3],
for x in self.signature.args tc_map: dict[TypeConstructorVariable, TypeConstructor_Base[Any]],
if isinstance(x, TypeVariable) helper: Callable[[S, TypeVariableLookup], R],
) ) -> None:
# assert len(key) == len(tv_map), (key, tv_map)
self.data[key] = helper key: list[Type3 | TypeConstructor_Base[Any]] = []
for tc_arg in self.args:
if isinstance(tc_arg, TypeVariable):
key.append(tv_map[tc_arg])
else:
key.append(tc_map[tc_arg])
self.data[tuple(key)] = helper
def __call__(self, arg0: S, tv_map: dict[TypeVariable, Type3]) -> R: def __call__(self, arg0: S, tv_map: dict[TypeVariable, Type3]) -> R:
key = tuple( key: list[Type3 | TypeConstructor_Base[Any]] = []
tv_map[x] arguments: TypeVariableLookup = {}
for x in self.signature.args
if isinstance(x, TypeVariable)
)
t_helper = self.data.get(key) for tc_arg in self.args:
if isinstance(tc_arg, TypeVariable):
key.append(tv_map[tc_arg])
continue
for tvar, typ in tv_map.items():
tvar_constructor = tvar.application.constructor
if tvar_constructor != tc_arg:
continue
key.append(typ.application.constructor)
if isinstance(tvar.application, TypeVariableApplication_Unary):
# FIXME: This feels sketchy. Shouldn't the type variable
# have the exact same number as arguments?
if isinstance(typ.application, TypeApplication_TypeInt):
arguments[tvar.application.arguments] = typ.application.arguments
continue
raise NotImplementedError(tvar.application, typ.application)
t_helper = self.data.get(tuple(key))
if t_helper is not None: if t_helper is not None:
return t_helper(arg0) return t_helper(arg0, arguments)
raise NoRouteForTypeException(arg0, tv_map) raise NoRouteForTypeException(arg0, tv_map)

View File

@ -2,7 +2,9 @@ from typing import Dict, Iterable, List, Mapping, Optional, Union
from .functions import ( from .functions import (
Constraint_TypeClassInstanceExists, Constraint_TypeClassInstanceExists,
ConstraintBase,
FunctionSignature, FunctionSignature,
TypeConstructorVariable,
TypeVariable, TypeVariable,
TypeVariableContext, TypeVariableContext,
) )
@ -25,11 +27,13 @@ class Type3ClassMethod:
def __repr__(self) -> str: def __repr__(self) -> str:
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})' return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
Type3ClassArgs = tuple[TypeVariable] | tuple[TypeVariable, TypeVariable] | tuple[TypeConstructorVariable]
class Type3Class: class Type3Class:
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', ) __slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
name: str name: str
args: List[TypeVariable] args: Type3ClassArgs
methods: Dict[str, Type3ClassMethod] methods: Dict[str, Type3ClassMethod]
operators: Dict[str, Type3ClassMethod] operators: Dict[str, Type3ClassMethod]
inherited_classes: List['Type3Class'] inherited_classes: List['Type3Class']
@ -37,26 +41,60 @@ class Type3Class:
def __init__( def __init__(
self, self,
name: str, name: str,
args: Iterable[TypeVariable], args: Type3ClassArgs,
methods: Mapping[str, Iterable[Union[Type3, TypeVariable]]], methods: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
operators: Mapping[str, Iterable[Union[Type3, TypeVariable]]], operators: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
inherited_classes: Optional[List['Type3Class']] = None, inherited_classes: Optional[List['Type3Class']] = None,
additional_context: Optional[Mapping[str, Iterable[ConstraintBase]]] = None,
) -> None: ) -> None:
self.name = name self.name = name
self.args = list(args) self.args = args
context = TypeVariableContext()
context.constraints.append(Constraint_TypeClassInstanceExists(self, args))
self.methods = { self.methods = {
k: Type3ClassMethod(k, FunctionSignature(context, v)) k: Type3ClassMethod(k, _create_signature(v, self))
for k, v in methods.items() for k, v in methods.items()
} }
self.operators = { self.operators = {
k: Type3ClassMethod(k, FunctionSignature(context, v)) k: Type3ClassMethod(k, _create_signature(v, self))
for k, v in operators.items() for k, v in operators.items()
} }
self.inherited_classes = inherited_classes or [] self.inherited_classes = inherited_classes or []
if additional_context:
for func_name, constraint_list in additional_context.items():
func = self.methods.get(func_name) or self.operators.get(func_name)
assert func is not None # type hint
func.signature.context.constraints.extend(constraint_list)
def __repr__(self) -> str: def __repr__(self) -> str:
return self.name return self.name
def _create_signature(
method_arg_list: Iterable[Type3 | TypeVariable],
type_class3: Type3Class,
) -> FunctionSignature:
context = TypeVariableContext()
if not isinstance(type_class3.args[0], TypeConstructorVariable):
context.constraints.append(Constraint_TypeClassInstanceExists(type_class3, type_class3.args))
signature_args: list[Type3 | TypeVariable] = []
for method_arg in method_arg_list:
if isinstance(method_arg, Type3):
signature_args.append(method_arg)
continue
if isinstance(method_arg, TypeVariable):
type_constructor = method_arg.application.constructor
if type_constructor is None:
signature_args.append(method_arg)
continue
if (type_constructor, ) == type_class3.args:
context.constraints.append(Constraint_TypeClassInstanceExists(type_class3, [method_arg]))
signature_args.append(method_arg)
continue
raise NotImplementedError(method_arg)
return FunctionSignature(context, signature_args)

View File

@ -29,6 +29,16 @@ class TypeApplication_Base[T: Hashable, S: Hashable]:
self.constructor = constructor self.constructor = constructor
self.arguments = arguments self.arguments = arguments
def __hash__(self) -> int:
return hash((self.constructor, self.arguments, ))
def __eq__(self, other: Any) -> bool:
if not isinstance(other, TypeApplication_Base):
raise NotImplementedError
return (self.constructor == other.constructor # type: ignore[no-any-return]
and self.arguments == other.arguments)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})' return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
@ -84,7 +94,9 @@ class Type3(KindArgument):
raise NotImplementedError raise NotImplementedError
class TypeApplication_Nullary(TypeApplication_Base[None, None]): class TypeApplication_Nullary(TypeApplication_Base[None, None]):
pass """
There was no constructor used to create this type - it's a 'simple' type like u32
"""
class IntType3(KindArgument): class IntType3(KindArgument):
""" """
@ -104,6 +116,9 @@ class IntType3(KindArgument):
def __init__(self, value: int) -> None: def __init__(self, value: int) -> None:
self.value = value self.value = value
def __repr__(self) -> str:
return f'IntType3({self.value!r})'
def __format__(self, format_spec: str) -> str: def __format__(self, format_spec: str) -> str:
if format_spec != 's': if format_spec != 's':
raise TypeError(f'unsupported format string passed to Type3.__format__: {format_spec}') raise TypeError(f'unsupported format string passed to Type3.__format__: {format_spec}')
@ -177,6 +192,9 @@ class TypeConstructor_Base[T]:
return result return result
def __repr__(self) -> str:
return f'{self.__class__.__name__}({self.name!r}, ...)'
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]): class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
""" """
Base class type constructors of kind: * -> Int -> * Base class type constructors of kind: * -> Int -> *

View File

@ -2,7 +2,7 @@
Helper functions to generate WASM code by writing Python functions Helper functions to generate WASM code by writing Python functions
""" """
import functools import functools
from typing import Any, Callable, Dict, List, Optional, Type from typing import Any, Callable, Dict, Iterable, List, Optional, Type
from . import wasm from . import wasm
@ -24,6 +24,12 @@ class VarType_i32(VarType_Base):
class VarType_i64(VarType_Base): class VarType_i64(VarType_Base):
wasm_type = wasm.WasmTypeInt64 wasm_type = wasm.WasmTypeInt64
class VarType_f32(VarType_Base):
wasm_type = wasm.WasmTypeFloat32
class VarType_f64(VarType_Base):
wasm_type = wasm.WasmTypeFloat64
class Generator_i32i64: class Generator_i32i64:
def __init__(self, prefix: str, generator: 'Generator') -> None: def __init__(self, prefix: str, generator: 'Generator') -> None:
self.prefix = prefix self.prefix = prefix
@ -164,12 +170,23 @@ class Generator_Local:
self.generator.add_statement('local.tee', variable.name_ref, comment=comment) self.generator.add_statement('local.tee', variable.name_ref, comment=comment)
class GeneratorBlock: class GeneratorBlock:
def __init__(self, generator: 'Generator', name: str) -> None: def __init__(self, generator: 'Generator', name: str, params: Iterable[str] = (), result: str | None = None) -> None:
self.generator = generator self.generator = generator
self.name = name self.name = name
self.params = params
self.result = result
def __enter__(self) -> None: def __enter__(self) -> None:
self.generator.add_statement(self.name) stmt = self.name
if self.params:
stmt = f'{stmt} ' + ' '.join(
f'(param {typ})'
for typ in self.params
)
if self.result:
stmt = f'{stmt} (result {self.result})'
self.generator.add_statement(stmt)
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
if not exc_type: if not exc_type:
@ -210,19 +227,18 @@ class Generator:
def add_statement(self, name: str, *args: str, comment: Optional[str] = None) -> None: def add_statement(self, name: str, *args: str, comment: Optional[str] = None) -> None:
self.statements.append(wasm.Statement(name, *args, comment=comment)) self.statements.append(wasm.Statement(name, *args, comment=comment))
def temp_var_i32(self, infix: str) -> VarType_i32: def temp_var[T: VarType_Base](self, var: T) -> T:
idx = 0 idx = 0
while (varname := f'__{infix}_tmp_var_{idx}__') in self.locals: while (varname := f'__{var.name}_tmp_var_{idx}__') in self.locals:
idx += 1 idx += 1
return VarType_i32(varname) return var.__class__(varname)
def temp_var_i32(self, infix: str) -> VarType_i32:
return self.temp_var(VarType_i32(infix))
def temp_var_u8(self, infix: str) -> VarType_u8: def temp_var_u8(self, infix: str) -> VarType_u8:
idx = 0 return self.temp_var(VarType_u8(infix))
while (varname := f'__{infix}_tmp_var_{idx}__') in self.locals:
idx += 1
return VarType_u8(varname)
def func_wrapper(exported: bool = True) -> Callable[[Any], wasm.Function]: def func_wrapper(exported: bool = True) -> Callable[[Any], wasm.Function]:
""" """

View File

@ -17,6 +17,9 @@ from . import runners
DASHES = '-' * 16 DASHES = '-' * 16
class InvalidArgumentException(Exception):
pass
class SuiteResult: class SuiteResult:
def __init__(self) -> None: def __init__(self) -> None:
self.returned_value = None self.returned_value = None
@ -152,18 +155,18 @@ def _allocate_memory_stored_bytes(attrs: tuple[runners.RunnerBase, bytes]) -> in
def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int: def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int:
runner, val = attrs runner, val = attrs
assert isinstance(val, tuple)
sa_type, sa_len = sa_args sa_type, sa_len = sa_args
if not isinstance(val, tuple):
raise InvalidArgumentException(f'Expected tuple of length {sa_len.value}; got {val!r} instead')
if sa_len.value != len(val):
raise InvalidArgumentException(f'Expected tuple of length {sa_len.value}; got {val!r} instead')
alloc_size = calculate_alloc_size_static_array(False, sa_args) alloc_size = calculate_alloc_size_static_array(False, sa_args)
adr = runner.call('stdlib.alloc.__alloc__', alloc_size) adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
assert isinstance(adr, int) assert isinstance(adr, int) # Type int
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n') sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
tuple_len = sa_len.value
assert tuple_len == len(val)
offset = adr offset = adr
for val_el_val in val: for val_el_val in val:
offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val) offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val)

View File

@ -0,0 +1,71 @@
import pytest
from phasm.type3.entry import Type3Exception
from ..helpers import Suite
from .test_natnum import FLOAT_TYPES, INT_TYPES
@pytest.mark.integration_test
@pytest.mark.parametrize('length', [1, 5, 13])
@pytest.mark.parametrize('a_type', INT_TYPES + FLOAT_TYPES)
def test_foldable_sum(length, a_type):
code_py = f"""
@exported
def testEntry(x: {a_type}[{length}]) -> {a_type}:
return sum(x)
"""
in_put = tuple(range(length))
result = Suite(code_py).run_code(in_put)
assert sum(in_put) == result.returned_value
@pytest.mark.integration_test
def test_foldable_sum_not_natnum():
code_py = """
class Foo:
bar: i32
@exported
def testEntry(x: Foo[4]) -> Foo:
return sum(x)
"""
with pytest.raises(Type3Exception, match='Missing type class instantation: NatNum Foo'):
Suite(code_py).run_code()
@pytest.mark.integration_test
def test_foldable_invalid_return_type():
code_py = """
@exported
def testEntry(x: i32[5]) -> f64:
return sum(x)
"""
with pytest.raises(Type3Exception, match='i32 must be f64 instead'):
Suite(code_py).run_code((4, 5, 6, 7, 8, ))
@pytest.mark.integration_test
def test_foldable_not_constructed():
code_py = """
@exported
def testEntry(x: i32) -> i32:
return sum(x)
"""
with pytest.raises(Type3Exception, match='Missing type class instantation: Foldable i32.*i32 must be a constructed type instead'):
Suite(code_py).run_code()
@pytest.mark.integration_test
def test_foldable_not_foldable():
code_py = """
@exported
def testEntry(x: (i32, u32, )) -> i32:
return sum(x)
"""
with pytest.raises(Type3Exception, match='Missing type class instantation: Foldable tuple'):
Suite(code_py).run_code()