Implements sum for Foldable types
Foldable take a TypeConstructor. The first argument must be a NatNum.
This commit is contained in:
parent
f6cb1a8c1d
commit
8a027a0b10
@ -2,7 +2,7 @@
|
||||
This module contains the code to convert parsed Ourlang into WebAssembly code
|
||||
"""
|
||||
import struct
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from . import codestyle, ourlang, prelude, wasm
|
||||
from .runtime import calculate_alloc_size, calculate_member_offset
|
||||
@ -259,6 +259,10 @@ INSTANCES = {
|
||||
prelude.Promotable.methods['demote']: {
|
||||
'a=f32,b=f64': stdlib_types.f32_f64_demote,
|
||||
},
|
||||
prelude.Foldable.methods['sum']: {
|
||||
'a=i32,t=i32[4]': stdlib_types.static_array_i32_4_sum,
|
||||
'a=i32,t=i32[5]': stdlib_types.static_array_i32_5_sum,
|
||||
},
|
||||
}
|
||||
|
||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||
@ -457,7 +461,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
|
||||
type_var_map: Dict[type3functions.TypeVariable, type3types.Type3] = {}
|
||||
type_var_map: Dict[Union[type3functions.TypeVariable, type3functions.TypeConstructorVariable], type3types.Type3] = {}
|
||||
|
||||
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):
|
||||
@ -488,12 +492,16 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
type_var_map = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
|
||||
if not isinstance(type_var, type3functions.TypeVariable):
|
||||
if isinstance(type_var, type3types.Type3):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
if isinstance(type_var, (type3functions.TypeVariable, type3functions.TypeConstructorVariable, )):
|
||||
assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
continue
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
instance_key = ','.join(
|
||||
f'{k.letter}={v.name}'
|
||||
@ -960,6 +968,7 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
||||
stdlib_types.__u32_pow2__,
|
||||
stdlib_types.__u8_rotl__,
|
||||
stdlib_types.__u8_rotr__,
|
||||
stdlib_types.__sa_i32_sum__,
|
||||
] + [
|
||||
function(x)
|
||||
for x in inp.functions.values()
|
||||
|
||||
@ -1,20 +1,29 @@
|
||||
"""
|
||||
The prelude are all the builtin types, type classes and methods
|
||||
"""
|
||||
from typing import Any, Union
|
||||
|
||||
from ..type3.functions import TypeVariable
|
||||
from ..type3.functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
FunctionSignature,
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
TypeVariableContext,
|
||||
)
|
||||
from ..type3.typeclasses import Type3Class
|
||||
from ..type3.types import (
|
||||
IntType3,
|
||||
Type3,
|
||||
TypeConstructor,
|
||||
TypeConstructor_StaticArray,
|
||||
TypeConstructor_Struct,
|
||||
TypeConstructor_Tuple,
|
||||
)
|
||||
|
||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3, ...]]] = set()
|
||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor[Any], TypeConstructor_Struct], ...]]] = set()
|
||||
|
||||
|
||||
def instance_type_class(cls: Type3Class, *typ: Type3) -> None:
|
||||
def instance_type_class(cls: Type3Class, *typ: Union[Type3, TypeConstructor[Any], TypeConstructor_Struct]) -> None:
|
||||
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
|
||||
|
||||
# TODO: Check for required existing instantiations
|
||||
@ -93,7 +102,7 @@ bytes_ = Type3('bytes')
|
||||
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
||||
"""
|
||||
|
||||
def sa_on_create(typ: Type3) -> None:
|
||||
def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
|
||||
@ -106,7 +115,7 @@ It should be applied with one argument. It has a runtime-dynamic length
|
||||
of the same type repeated.
|
||||
"""
|
||||
|
||||
def tp_on_create(typ: Type3) -> None:
|
||||
def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
tuple_ = TypeConstructor_Tuple('tuple', on_create=tp_on_create)
|
||||
@ -143,6 +152,7 @@ PRELUDE_TYPES: dict[str, Type3] = {
|
||||
a = TypeVariable('a')
|
||||
b = TypeVariable('b')
|
||||
|
||||
t = TypeConstructorVariable('t', [])
|
||||
|
||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
||||
"""
|
||||
@ -286,6 +296,17 @@ Promotable = Type3Class('Promotable', [a, b], methods={
|
||||
|
||||
instance_type_class(Promotable, f32, f64)
|
||||
|
||||
Foldable = Type3Class('Foldable', [t], methods={
|
||||
'sum': FunctionSignature(
|
||||
TypeVariableContext([
|
||||
Constraint_TypeClassInstanceExists(NatNum, [a]),
|
||||
]),
|
||||
[t(a), a]
|
||||
),
|
||||
}, operators={})
|
||||
|
||||
instance_type_class(Foldable, static_array)
|
||||
|
||||
PRELUDE_TYPE_CLASSES = {
|
||||
'Eq': Eq,
|
||||
'Ord': Ord,
|
||||
@ -321,4 +342,5 @@ PRELUDE_METHODS = {
|
||||
**Sized_.methods,
|
||||
**Extendable.methods,
|
||||
**Promotable.methods,
|
||||
**Foldable.methods,
|
||||
}
|
||||
|
||||
@ -384,6 +384,50 @@ def __u8_rotr__(g: Generator, x: i32, r: i32) -> i32:
|
||||
|
||||
return i32('return') # To satisfy mypy
|
||||
|
||||
@func_wrapper()
|
||||
def __sa_i32_sum__(g: Generator, adr: i32, arlen: i32) -> i32:
|
||||
i32_size = 4
|
||||
|
||||
s = i32('s')
|
||||
stop = i32('stop')
|
||||
|
||||
# stop = adr + ar_len * i32_size
|
||||
g.local.get(adr)
|
||||
g.local.get(arlen)
|
||||
g.i32.const(i32_size)
|
||||
g.i32.mul()
|
||||
g.i32.add()
|
||||
g.local.set(stop)
|
||||
|
||||
# sum = 0
|
||||
g.i32.const(0)
|
||||
g.local.set(s)
|
||||
|
||||
with g.loop():
|
||||
# sum = sum + *adr
|
||||
g.local.get(adr)
|
||||
g.i32.load()
|
||||
g.local.get(s)
|
||||
g.i32.add()
|
||||
g.local.set(s)
|
||||
|
||||
# adr = adr + i32_size
|
||||
g.local.get(adr)
|
||||
g.i32.const(i32_size)
|
||||
g.i32.add()
|
||||
g.local.tee(adr)
|
||||
|
||||
# loop if adr < stop
|
||||
g.local.get(stop)
|
||||
g.i32.lt_u()
|
||||
g.br_if(0)
|
||||
|
||||
# return sum
|
||||
g.local.get(s)
|
||||
g.return_()
|
||||
|
||||
return i32('return') # To satisfy mypy
|
||||
|
||||
## ###
|
||||
## class Eq
|
||||
|
||||
@ -920,3 +964,11 @@ def f32_f64_promote(g: Generator) -> None:
|
||||
|
||||
def f32_f64_demote(g: Generator) -> None:
|
||||
g.f32.demote_f64()
|
||||
|
||||
def static_array_i32_4_sum(g: Generator) -> None:
|
||||
g.i32.const(4)
|
||||
g.add_statement('call $stdlib.types.__sa_i32_sum__')
|
||||
|
||||
def static_array_i32_5_sum(g: Generator) -> None:
|
||||
g.i32.const(5)
|
||||
g.add_statement('call $stdlib.types.__sa_i32_sum__')
|
||||
|
||||
@ -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.
|
||||
"""
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from .. import ourlang, prelude
|
||||
from . import placeholders, typeclasses, types
|
||||
@ -48,7 +48,7 @@ class Context:
|
||||
__slots__ = ('type_class_instances_existing', )
|
||||
|
||||
# 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[Any], types.TypeConstructor_Struct], ...]]]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.type_class_instances_existing = set()
|
||||
|
||||
@ -58,6 +58,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
x: placeholders.PlaceholderForType([])
|
||||
for x in signature.args
|
||||
if isinstance(x, functions.TypeVariable)
|
||||
or isinstance(x, functions.TypeConstructorVariable)
|
||||
}
|
||||
|
||||
for call_arg in arguments:
|
||||
@ -80,7 +81,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
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):
|
||||
if isinstance(sig_part, (functions.TypeVariable, functions.TypeConstructorVariable, )):
|
||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3, comment=comment)
|
||||
continue
|
||||
|
||||
|
||||
@ -34,6 +34,22 @@ class TypeVariable:
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeVariable({repr(self.letter)})'
|
||||
|
||||
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.
|
||||
"""
|
||||
__slots__ = ('letter', 'args', )
|
||||
|
||||
def __init__(self, letter: str, args: Iterable[TypeVariable]) -> None:
|
||||
self.letter = letter
|
||||
self.args = list(args)
|
||||
|
||||
def __call__(self, tvar: TypeVariable) -> 'TypeConstructorVariable':
|
||||
return TypeConstructorVariable(self.letter, self.args + [tvar])
|
||||
|
||||
class ConstraintBase:
|
||||
__slots__ = ()
|
||||
|
||||
@ -41,9 +57,9 @@ class Constraint_TypeClassInstanceExists(ConstraintBase):
|
||||
__slots__ = ('type_class3', 'types', )
|
||||
|
||||
type_class3: 'Type3Class'
|
||||
types: list[TypeVariable]
|
||||
types: list[Union[TypeVariable, TypeConstructorVariable]]
|
||||
|
||||
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
||||
def __init__(self, type_class3: 'Type3Class', types: Iterable[Union[TypeVariable, TypeConstructorVariable]]) -> None:
|
||||
self.type_class3 = type_class3
|
||||
self.types = list(types)
|
||||
|
||||
@ -52,27 +68,22 @@ class Constraint_TypeClassInstanceExists(ConstraintBase):
|
||||
assert len(self.type_class3.args) == len(self.types)
|
||||
|
||||
class TypeVariableContext:
|
||||
__slots__ = ('variables', 'constraints', )
|
||||
__slots__ = ('constraints', )
|
||||
|
||||
variables: set[TypeVariable]
|
||||
constraints: list[ConstraintBase]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.variables = set()
|
||||
self.constraints = []
|
||||
def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
|
||||
self.constraints = list(constraints)
|
||||
|
||||
def __copy__(self) -> 'TypeVariableContext':
|
||||
result = TypeVariableContext()
|
||||
result.variables.update(self.variables)
|
||||
result.constraints.extend(self.constraints)
|
||||
return result
|
||||
return TypeVariableContext(self.constraints)
|
||||
|
||||
class FunctionSignature:
|
||||
__slots__ = ('context', 'args', )
|
||||
|
||||
context: TypeVariableContext
|
||||
args: List[Union['Type3', TypeVariable]]
|
||||
args: List[Union['Type3', TypeVariable, TypeConstructorVariable]]
|
||||
|
||||
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable]]) -> None:
|
||||
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable, TypeConstructorVariable]]) -> None:
|
||||
self.context = context.__copy__()
|
||||
self.args = list(args)
|
||||
|
||||
@ -3,6 +3,7 @@ from typing import Dict, Iterable, List, Mapping, Optional, Union
|
||||
from .functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
FunctionSignature,
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
TypeVariableContext,
|
||||
)
|
||||
@ -26,7 +27,7 @@ class Type3Class:
|
||||
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
||||
|
||||
name: str
|
||||
args: List[TypeVariable]
|
||||
args: List[Union[TypeVariable, TypeConstructorVariable]]
|
||||
methods: Dict[str, Type3ClassMethod]
|
||||
operators: Dict[str, Type3ClassMethod]
|
||||
inherited_classes: List['Type3Class']
|
||||
@ -34,9 +35,9 @@ class Type3Class:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
args: Iterable[TypeVariable],
|
||||
methods: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
||||
operators: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
||||
args: Iterable[Union[TypeVariable, TypeConstructorVariable]],
|
||||
methods: Mapping[str, Union[FunctionSignature, Iterable[Union[Type3, TypeVariable, TypeConstructorVariable]]]],
|
||||
operators: Mapping[str, Union[FunctionSignature, Iterable[Union[Type3, TypeVariable, TypeConstructorVariable]]]],
|
||||
inherited_classes: Optional[List['Type3Class']] = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
@ -46,11 +47,11 @@ class Type3Class:
|
||||
context.constraints.append(Constraint_TypeClassInstanceExists(self, args))
|
||||
|
||||
self.methods = {
|
||||
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||
k: Type3ClassMethod(k, v if isinstance(v, FunctionSignature) else FunctionSignature(context, v))
|
||||
for k, v in methods.items()
|
||||
}
|
||||
self.operators = {
|
||||
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||
k: Type3ClassMethod(k, v if isinstance(v, FunctionSignature) else FunctionSignature(context, v))
|
||||
for k, v in operators.items()
|
||||
}
|
||||
self.inherited_classes = inherited_classes or []
|
||||
|
||||
@ -106,7 +106,7 @@ class TypeConstructor(Generic[T]):
|
||||
The name of the type constructor
|
||||
"""
|
||||
|
||||
on_create: Callable[[Type3], None]
|
||||
on_create: Callable[[T, Type3], None]
|
||||
"""
|
||||
Who to let know if a type is created
|
||||
"""
|
||||
@ -122,7 +122,7 @@ class TypeConstructor(Generic[T]):
|
||||
Sometimes we need to know the key that created a type.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
|
||||
def __init__(self, name: str, on_create: Callable[[T, Type3], None]) -> None:
|
||||
self.name = name
|
||||
self.on_create = on_create
|
||||
|
||||
@ -152,7 +152,7 @@ class TypeConstructor(Generic[T]):
|
||||
if result is None:
|
||||
self._cache[key] = result = Type3(self.make_name(key))
|
||||
self._reverse_cache[result] = key
|
||||
self.on_create(result)
|
||||
self.on_create(key, result)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
41
tests/integration/test_lang/test_foldable.py
Normal file
41
tests/integration/test_lang/test_foldable.py
Normal file
@ -0,0 +1,41 @@
|
||||
import pytest
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
from ..helpers import Suite
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldable_sum():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(x: i32[5]) -> i32:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code((4, 5, 6, 7, 8, ))
|
||||
|
||||
assert 30 == result.returned_value
|
||||
|
||||
|
||||
@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='f64 must be i32 instead'):
|
||||
Suite(code_py).run_code((4, 5, 6, 7, 8, ))
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldable_not_foldable():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(x: u8) -> u8:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Foldable u8'):
|
||||
Suite(code_py).run_code()
|
||||
Loading…
x
Reference in New Issue
Block a user