Compare commits
3 Commits
5a149b7796
...
23ca1799b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23ca1799b2 | ||
|
|
6b66935c67 | ||
|
|
d9a08cf0f7 |
3
TODO.md
3
TODO.md
@ -12,9 +12,12 @@
|
||||
- Also, check the codes for FIXME and TODO
|
||||
- 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
|
||||
- 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?
|
||||
- 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.
|
||||
- Do we need to store the placeholders on the expressions? They're only temporary while the type checker is running
|
||||
- Might not even need to store them at all outside the generated constraints?
|
||||
|
||||
- Parser is putting stuff in ModuleDataBlock
|
||||
- Surely the compiler should build data blocks
|
||||
|
||||
@ -6,7 +6,6 @@ It's intented to be a "any color, as long as it's black" kind of renderer
|
||||
from typing import Generator
|
||||
|
||||
from . import ourlang, prelude
|
||||
from .type3.placeholders import TYPE3_ASSERTION_ERROR, Type3OrPlaceholder
|
||||
from .type3.types import Type3
|
||||
|
||||
|
||||
@ -18,12 +17,10 @@ def phasm_render(inp: ourlang.Module) -> str:
|
||||
|
||||
Statements = Generator[str, None, None]
|
||||
|
||||
def type3(inp: Type3OrPlaceholder) -> str:
|
||||
def type3(inp: Type3) -> str:
|
||||
"""
|
||||
Render: type's name
|
||||
"""
|
||||
assert isinstance(inp, Type3), TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp is prelude.none:
|
||||
return 'None'
|
||||
|
||||
|
||||
@ -2,18 +2,19 @@
|
||||
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
|
||||
from .stdlib import alloc as stdlib_alloc
|
||||
from .stdlib import types as stdlib_types
|
||||
from .type3 import functions as type3functions
|
||||
from .type3 import placeholders as type3placeholders
|
||||
from .type3 import typeclasses as type3classes
|
||||
from .type3 import types as type3types
|
||||
from .wasmgenerator import Generator as WasmGenerator
|
||||
|
||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
||||
|
||||
LOAD_STORE_TYPE_MAP = {
|
||||
'i8': 'i32', # Have to use an u32, since there is no native i8 type
|
||||
'u8': 'i32', # Have to use an u32, since there is no native u8 type
|
||||
@ -259,6 +260,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:
|
||||
@ -268,14 +273,14 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||
"""
|
||||
return module(inp)
|
||||
|
||||
def type3(inp: type3placeholders.Type3OrPlaceholder) -> wasm.WasmType:
|
||||
def type3(inp: type3types.Type3) -> wasm.WasmType:
|
||||
"""
|
||||
Compile: type
|
||||
|
||||
Types are used for example in WebAssembly function parameters
|
||||
and return types.
|
||||
"""
|
||||
assert isinstance(inp, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp == prelude.none:
|
||||
return wasm.WasmTypeNone()
|
||||
@ -327,7 +332,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
"""
|
||||
Compile: Instantiation (allocation) of a tuple
|
||||
"""
|
||||
assert isinstance(inp.type3, type3types.Type3)
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
args: list[type3types.Type3] = []
|
||||
|
||||
@ -345,7 +350,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
|
||||
comment_elements = ''
|
||||
for element in inp.elements:
|
||||
assert isinstance(element.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert element.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
comment_elements += f'{element.type3.name}, '
|
||||
|
||||
tmp_var = wgn.temp_var_i32('tuple_adr')
|
||||
@ -359,17 +364,11 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
# Store each element individually
|
||||
offset = 0
|
||||
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
||||
if isinstance(exp_type3, type3placeholders.PlaceholderForType):
|
||||
assert exp_type3.resolve_as is not None
|
||||
assert isinstance(exp_type3.resolve_as, type3types.Type3)
|
||||
exp_type3 = exp_type3.resolve_as
|
||||
|
||||
assert element.type3 == exp_type3
|
||||
|
||||
if (prelude.InternalPassAsPointer, (exp_type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||
mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name]
|
||||
|
||||
wgn.add_statement('nop', comment='PRE')
|
||||
@ -392,7 +391,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
raise Exception
|
||||
|
||||
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.type3 in (prelude.i8, prelude.u8, ):
|
||||
# No native u8 type - treat as i32, with caution
|
||||
@ -433,7 +432,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
return
|
||||
|
||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if (prelude.InternalPassAsPointer, (inp.type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, ))
|
||||
@ -443,28 +442,25 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
wgn.i32.const(address)
|
||||
return
|
||||
|
||||
if isinstance(inp.type3, type3types.Type3):
|
||||
expression(wgn, inp.variable.constant)
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
raise NotImplementedError(expression, inp.variable)
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp):
|
||||
expression(wgn, inp.left)
|
||||
expression(wgn, inp.right)
|
||||
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.type3 is not None, 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):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
|
||||
instance_key = ','.join(
|
||||
@ -488,12 +484,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
|
||||
if isinstance(type_var, (type3functions.TypeVariable, type3functions.TypeConstructorVariable, )):
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
continue
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
instance_key = ','.join(
|
||||
f'{k.letter}={v.name}'
|
||||
@ -515,7 +515,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.Subscript):
|
||||
assert isinstance(inp.varref.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.varref.type3 is prelude.bytes_:
|
||||
expression(wgn, inp.varref)
|
||||
@ -523,7 +523,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
wgn.call(stdlib_types.__subscript_bytes__)
|
||||
return
|
||||
|
||||
assert isinstance(inp.varref.type3, type3types.Type3)
|
||||
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
sa_args = prelude.static_array.did_construct(inp.varref.type3)
|
||||
if sa_args is not None:
|
||||
@ -549,7 +549,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
wgn.i32.mul()
|
||||
wgn.i32.add()
|
||||
|
||||
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load')
|
||||
@ -562,18 +561,17 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
|
||||
offset = 0
|
||||
for el_type in tp_args[0:inp.index.value]:
|
||||
assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
offset += calculate_alloc_size(el_type)
|
||||
|
||||
el_type = tp_args[inp.index.value]
|
||||
assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
||||
@ -582,14 +580,13 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
raise NotImplementedError(expression, inp, inp.varref.type3)
|
||||
|
||||
if isinstance(inp, ourlang.AccessStructMember):
|
||||
assert isinstance(inp.struct_type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.struct_type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None
|
||||
|
||||
member_type = st_args[inp.member]
|
||||
|
||||
assert isinstance(member_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||
mtyp = LOAD_STORE_TYPE_MAP[member_type.name]
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
@ -608,7 +605,7 @@ def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
|
||||
"""
|
||||
Compile: Fold expression
|
||||
"""
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.iter.type3 is not prelude.bytes_:
|
||||
raise NotImplementedError(expression_fold, inp, inp.iter.type3)
|
||||
@ -839,7 +836,7 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
|
||||
data_list: List[bytes] = []
|
||||
|
||||
for constant in block.data:
|
||||
assert isinstance(constant.type3, type3types.Type3), (id(constant), type3placeholders.TYPE3_ASSERTION_ERROR)
|
||||
assert constant.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(constant, ourlang.ConstantMemoryStored) and block is not constant.data_block:
|
||||
# It's stored in a different block
|
||||
@ -960,6 +957,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()
|
||||
|
||||
@ -6,7 +6,6 @@ from typing import Dict, Iterable, List, Optional, Union
|
||||
|
||||
from . import prelude
|
||||
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import Type3
|
||||
|
||||
@ -17,10 +16,10 @@ class Expression:
|
||||
"""
|
||||
__slots__ = ('type3', )
|
||||
|
||||
type3: Type3OrPlaceholder
|
||||
type3: Type3 | None
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.type3 = PlaceholderForType([self])
|
||||
self.type3 = None
|
||||
|
||||
class Constant(Expression):
|
||||
"""
|
||||
@ -198,10 +197,10 @@ class AccessStructMember(Expression):
|
||||
__slots__ = ('varref', 'struct_type3', 'member', )
|
||||
|
||||
varref: VariableReference
|
||||
struct_type3: Type3OrPlaceholder
|
||||
struct_type3: Type3
|
||||
member: str
|
||||
|
||||
def __init__(self, varref: VariableReference, struct_type3: Type3OrPlaceholder, member: str) -> None:
|
||||
def __init__(self, varref: VariableReference, struct_type3: Type3, member: str) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.varref = varref
|
||||
@ -284,7 +283,7 @@ class FunctionParam:
|
||||
__slots__ = ('name', 'type3', )
|
||||
|
||||
name: str
|
||||
type3: Type3OrPlaceholder
|
||||
type3: Type3
|
||||
|
||||
def __init__(self, name: str, type3: Type3) -> None:
|
||||
self.name = name
|
||||
|
||||
@ -163,8 +163,8 @@ class OurVisitor:
|
||||
|
||||
arg_type = self.visit_type(module, arg.annotation)
|
||||
|
||||
# if isisntance(arg_type, TypeVariable):
|
||||
# arg_type = PlaceHolderForType()
|
||||
# FIXME: Allow TypeVariable in the function signature
|
||||
# This would also require FunctionParam to accept a placeholder
|
||||
|
||||
function.signature.args.append(arg_type)
|
||||
function.posonlyargs.append(FunctionParam(
|
||||
|
||||
@ -1,20 +1,27 @@
|
||||
"""
|
||||
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,
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
)
|
||||
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 +100,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 +113,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 +150,7 @@ PRELUDE_TYPES: dict[str, Type3] = {
|
||||
a = TypeVariable('a')
|
||||
b = TypeVariable('b')
|
||||
|
||||
t = TypeConstructorVariable('t', [])
|
||||
|
||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
||||
"""
|
||||
@ -286,6 +294,14 @@ Promotable = Type3Class('Promotable', [a, b], methods={
|
||||
|
||||
instance_type_class(Promotable, f32, f64)
|
||||
|
||||
Foldable = Type3Class('Foldable', [t], methods={
|
||||
'sum': [t(a), a],
|
||||
}, operators={}, additional_context={
|
||||
'sum': [Constraint_TypeClassInstanceExists(NatNum, [a])],
|
||||
})
|
||||
|
||||
instance_type_class(Foldable, static_array)
|
||||
|
||||
PRELUDE_TYPE_CLASSES = {
|
||||
'Eq': Eq,
|
||||
'Ord': Ord,
|
||||
@ -321,4 +337,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,10 +3,11 @@ 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, Iterable, List, Optional, Tuple, Union
|
||||
|
||||
from .. import ourlang, prelude
|
||||
from . import placeholders, typeclasses, types
|
||||
from .placeholders import PlaceholderForType
|
||||
|
||||
|
||||
class Error:
|
||||
@ -48,7 +49,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()
|
||||
@ -157,7 +158,7 @@ class SameTypeConstraint(ConstraintBase):
|
||||
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
|
||||
|
||||
class TupleMatchConstraint(ConstraintBase):
|
||||
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: List[placeholders.Type3OrPlaceholder], comment: str):
|
||||
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: Iterable[placeholders.Type3OrPlaceholder], comment: str):
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.exp_type = exp_type
|
||||
@ -348,8 +349,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x, y.type3)
|
||||
SameTypeConstraint(x, PlaceholderForType([y]))
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
@ -371,8 +376,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
LiteralFitsConstraint(sa_type, y)
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(sa_type, y.type3)
|
||||
SameTypeConstraint(sa_type, PlaceholderForType([y]))
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
@ -396,8 +405,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(st_args.values(), self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
|
||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_name}.{x_n}')
|
||||
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
@ -421,20 +434,27 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
"""
|
||||
A value that is subscipted, i.e. a[0] (tuple) or a[b] (static array)
|
||||
"""
|
||||
__slots__ = ('ret_type3', 'type3', 'index', 'index_type3', )
|
||||
__slots__ = ('ret_type3', 'type3', 'index', 'index_phft', )
|
||||
|
||||
ret_type3: placeholders.Type3OrPlaceholder
|
||||
type3: placeholders.Type3OrPlaceholder
|
||||
index: ourlang.Expression
|
||||
index_type3: placeholders.Type3OrPlaceholder
|
||||
index_phft: placeholders.Type3OrPlaceholder
|
||||
|
||||
def __init__(self, ret_type3: placeholders.Type3OrPlaceholder, type3: placeholders.Type3OrPlaceholder, index: ourlang.Expression, comment: Optional[str] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
ret_type3: placeholders.PlaceholderForType,
|
||||
type3: placeholders.PlaceholderForType,
|
||||
index: ourlang.Expression,
|
||||
index_phft: placeholders.PlaceholderForType,
|
||||
comment: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.ret_type3 = ret_type3
|
||||
self.type3 = type3
|
||||
self.index = index
|
||||
self.index_type3 = index.type3
|
||||
self.index_phft = index_phft
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
exp_type = self.type3
|
||||
@ -451,7 +471,7 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
result: List[ConstraintBase] = [
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
]
|
||||
|
||||
@ -478,13 +498,13 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment=f'Tuple subscript index {self.index.value}'),
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment=f'Tuple subscript index {self.index.value}'),
|
||||
SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
|
||||
]
|
||||
|
||||
if exp_type is prelude.bytes_:
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: bytes -> u32 -> u8'),
|
||||
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
]
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ from .constraints import (
|
||||
SameTypeConstraint,
|
||||
TupleMatchConstraint,
|
||||
)
|
||||
from .placeholders import PlaceholderForType
|
||||
|
||||
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
||||
|
||||
@ -28,40 +29,50 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
|
||||
|
||||
return [*module(ctx, inp)]
|
||||
|
||||
def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator:
|
||||
def constant(ctx: Context, inp: ourlang.Constant, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
|
||||
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
||||
yield LiteralFitsConstraint(
|
||||
inp.type3, inp,
|
||||
phft, inp,
|
||||
comment='The given literal must fit the expected type'
|
||||
)
|
||||
return
|
||||
|
||||
raise NotImplementedError(constant, inp)
|
||||
|
||||
def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
|
||||
if isinstance(inp, ourlang.Constant):
|
||||
yield from constant(ctx, inp)
|
||||
yield from constant(ctx, inp, phft)
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.VariableReference):
|
||||
yield SameTypeConstraint(inp.variable.type3, inp.type3,
|
||||
yield SameTypeConstraint(inp.variable.type3, phft,
|
||||
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall):
|
||||
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature
|
||||
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments
|
||||
|
||||
func_name = f'({inp.operator.name})' if isinstance(inp, ourlang.BinaryOp) else inp.function.name
|
||||
|
||||
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments
|
||||
|
||||
arg_placeholders = {
|
||||
arg_expr: PlaceholderForType([arg_expr])
|
||||
for arg_expr in arguments
|
||||
}
|
||||
arg_placeholders[inp] = phft
|
||||
|
||||
for call_arg in arguments:
|
||||
yield from expression(ctx, call_arg, arg_placeholders[call_arg])
|
||||
|
||||
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature
|
||||
type_var_map = {
|
||||
x: placeholders.PlaceholderForType([])
|
||||
for x in signature.args
|
||||
if isinstance(x, functions.TypeVariable)
|
||||
or isinstance(x, functions.TypeConstructorVariable)
|
||||
}
|
||||
|
||||
for call_arg in arguments:
|
||||
yield from expression(ctx, call_arg)
|
||||
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):
|
||||
@ -81,11 +92,11 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
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_expr.type3, comment=comment)
|
||||
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_expr.type3, comment=comment)
|
||||
yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment)
|
||||
continue
|
||||
|
||||
raise NotImplementedError(sig_part)
|
||||
@ -94,11 +105,12 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
if isinstance(inp, ourlang.TupleInstantiation):
|
||||
r_type = []
|
||||
for arg in inp.elements:
|
||||
yield from expression(ctx, arg)
|
||||
r_type.append(arg.type3)
|
||||
arg_phft = PlaceholderForType([arg])
|
||||
yield from expression(ctx, arg, arg_phft)
|
||||
r_type.append(arg_phft)
|
||||
|
||||
yield TupleMatchConstraint(
|
||||
inp.type3,
|
||||
phft,
|
||||
r_type,
|
||||
comment='The type of a tuple is a combination of its members'
|
||||
)
|
||||
@ -106,10 +118,13 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.Subscript):
|
||||
yield from expression(ctx, inp.varref)
|
||||
yield from expression(ctx, inp.index)
|
||||
varref_phft = PlaceholderForType([inp.varref])
|
||||
index_phft = PlaceholderForType([inp.index])
|
||||
|
||||
yield CanBeSubscriptedConstraint(inp.type3, inp.varref.type3, inp.index)
|
||||
yield from expression(ctx, inp.varref, varref_phft)
|
||||
yield from expression(ctx, inp.index, index_phft)
|
||||
|
||||
yield CanBeSubscriptedConstraint(phft, varref_phft, inp.index, index_phft)
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.AccessStructMember):
|
||||
@ -117,33 +132,40 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None # FIXME: See test_struct.py::test_struct_not_accessible
|
||||
|
||||
yield from expression(ctx, inp.varref)
|
||||
yield SameTypeConstraint(st_args[inp.member], inp.type3,
|
||||
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
|
||||
yield SameTypeConstraint(st_args[inp.member], phft,
|
||||
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.Fold):
|
||||
yield from expression(ctx, inp.base)
|
||||
yield from expression(ctx, inp.iter)
|
||||
base_phft = PlaceholderForType([inp.base])
|
||||
iter_phft = PlaceholderForType([inp.iter])
|
||||
|
||||
yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, inp.base.type3, inp.type3,
|
||||
yield from expression(ctx, inp.base, base_phft)
|
||||
yield from expression(ctx, inp.iter, iter_phft)
|
||||
|
||||
yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, base_phft, phft,
|
||||
comment='foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b')
|
||||
yield MustImplementTypeClassConstraint(ctx, 'Foldable', [inp.iter.type3])
|
||||
yield MustImplementTypeClassConstraint(ctx, 'Foldable', [iter_phft])
|
||||
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementReturn) -> ConstraintGenerator:
|
||||
yield from expression(ctx, inp.value)
|
||||
phft = PlaceholderForType([inp.value])
|
||||
|
||||
yield SameTypeConstraint(fun.returns_type3, inp.value.type3,
|
||||
yield from expression(ctx, inp.value, phft)
|
||||
|
||||
yield SameTypeConstraint(fun.returns_type3, phft,
|
||||
comment=f'The type of the value returned from function {fun.name} should match its return type')
|
||||
|
||||
def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator:
|
||||
yield from expression(ctx, inp.test)
|
||||
test_phft = PlaceholderForType([inp.test])
|
||||
|
||||
yield SameTypeConstraint(inp.test.type3, prelude.bool_,
|
||||
yield from expression(ctx, inp.test, test_phft)
|
||||
|
||||
yield SameTypeConstraint(test_phft, prelude.bool_,
|
||||
comment='Must pass a boolean expression to if')
|
||||
|
||||
for stmt in inp.statements:
|
||||
@ -173,8 +195,10 @@ def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator:
|
||||
yield from statement(ctx, inp, stmt)
|
||||
|
||||
def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> ConstraintGenerator:
|
||||
yield from constant(ctx, inp.constant)
|
||||
yield SameTypeConstraint(inp.type3, inp.constant.type3,
|
||||
phft = PlaceholderForType([inp.constant])
|
||||
|
||||
yield from constant(ctx, inp.constant, phft)
|
||||
yield SameTypeConstraint(inp.type3, phft,
|
||||
comment=f'The type of the value for module constant definition {inp.name} should match the type of that constant')
|
||||
|
||||
def module(ctx: Context, inp: ourlang.Module) -> ConstraintGenerator:
|
||||
|
||||
@ -115,8 +115,6 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
||||
# FIXME: This doesn't work with e.g. `:: [a] -> a`, as the placeholder is inside a type
|
||||
for plh, typ in placeholder_substitutes.items():
|
||||
for expr in plh.update_on_substitution:
|
||||
assert expr.type3 is plh
|
||||
|
||||
expr.type3 = typ
|
||||
|
||||
def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintBase) -> None:
|
||||
|
||||
@ -34,6 +34,25 @@ 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])
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeConstructorVariable({self.letter!r}, {self.args!r})'
|
||||
|
||||
class ConstraintBase:
|
||||
__slots__ = ()
|
||||
|
||||
@ -41,9 +60,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 +71,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)
|
||||
|
||||
@ -7,14 +7,13 @@ from typing import Any, Iterable, List, Optional, Protocol, Union
|
||||
|
||||
from .types import Type3
|
||||
|
||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
|
||||
|
||||
class ExpressionProtocol(Protocol):
|
||||
"""
|
||||
A protocol for classes that should be updated on substitution
|
||||
"""
|
||||
|
||||
type3: 'Type3OrPlaceholder'
|
||||
type3: Type3 | None
|
||||
"""
|
||||
The type to update
|
||||
"""
|
||||
|
||||
@ -2,7 +2,9 @@ from typing import Dict, Iterable, List, Mapping, Optional, Union
|
||||
|
||||
from .functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
ConstraintBase,
|
||||
FunctionSignature,
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
TypeVariableContext,
|
||||
)
|
||||
@ -26,7 +28,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,10 +36,11 @@ 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, Iterable[Union[Type3, TypeVariable, TypeConstructorVariable]]],
|
||||
operators: Mapping[str, Iterable[Union[Type3, TypeVariable, TypeConstructorVariable]]],
|
||||
inherited_classes: Optional[List['Type3Class']] = None,
|
||||
additional_context: Optional[Mapping[str, Iterable[ConstraintBase]]] = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.args = list(args)
|
||||
@ -55,5 +58,12 @@ class Type3Class:
|
||||
}
|
||||
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:
|
||||
return self.name
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ from typing import Any, Generator, Iterable, List, TextIO, Union
|
||||
from phasm import compiler, prelude
|
||||
from phasm.codestyle import phasm_render
|
||||
from phasm.runtime import calculate_alloc_size
|
||||
from phasm.type3 import placeholders as type3placeholders
|
||||
from phasm.type3 import types as type3types
|
||||
|
||||
from . import runners
|
||||
@ -65,9 +64,6 @@ class Suite:
|
||||
runner.interpreter_dump_memory(sys.stderr)
|
||||
|
||||
for arg, arg_typ in zip(args, func_args, strict=True):
|
||||
assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
|
||||
'Cannot call polymorphic function from outside'
|
||||
|
||||
if arg_typ in (prelude.u8, prelude.u32, prelude.u64, ):
|
||||
assert isinstance(arg, int)
|
||||
wasm_args.append(arg)
|
||||
@ -88,7 +84,6 @@ class Suite:
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
assert isinstance(arg_typ, type3types.Type3)
|
||||
sa_args = prelude.static_array.did_construct(arg_typ)
|
||||
if sa_args is not None:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
@ -222,8 +217,6 @@ def _allocate_memory_stored_value(
|
||||
|
||||
offset = adr
|
||||
for val_el_val, val_el_typ in zip(val, tp_args, strict=True):
|
||||
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
||||
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
return adr
|
||||
|
||||
@ -240,8 +233,6 @@ def _allocate_memory_stored_value(
|
||||
|
||||
offset = adr
|
||||
for val_el_name, val_el_typ in st_args.items():
|
||||
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
||||
|
||||
val_el_val = val[val_el_name]
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
return adr
|
||||
@ -299,8 +290,6 @@ def _load_memory_stored_returned_value(
|
||||
|
||||
return _load_bytes_from_address(runner, ret_type3, wasm_value)
|
||||
|
||||
assert isinstance(ret_type3, type3types.Type3) # Type hint
|
||||
|
||||
sa_args = prelude.static_array.did_construct(ret_type3)
|
||||
if sa_args is not None:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
@ -366,8 +355,6 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
|
||||
assert len(inp) == 4
|
||||
adr = struct.unpack('<I', inp)[0]
|
||||
|
||||
assert isinstance(typ, type3types.Type3)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(typ)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
@ -404,9 +391,6 @@ def _split_read_bytes(all_bytes: bytes, split_sizes: Iterable[int]) -> Generator
|
||||
def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3types.Type3, len_typ: type3types.IntType3, adr: int) -> Any:
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} {sub_typ:s} {len_typ:s}\n')
|
||||
|
||||
assert not isinstance(sub_typ, type3placeholders.PlaceholderForType)
|
||||
assert isinstance(len_typ, type3types.IntType3)
|
||||
|
||||
sa_len = len_typ.value
|
||||
|
||||
arg_size_1 = calculate_alloc_size(sub_typ, is_member=True)
|
||||
|
||||
55
tests/integration/test_lang/test_foldable.py
Normal file
55
tests/integration/test_lang/test_foldable.py
Normal file
@ -0,0 +1,55 @@
|
||||
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_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='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: i32) -> i32:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Foldable i32'):
|
||||
Suite(code_py).run_code()
|
||||
@ -6,7 +6,20 @@ from ..helpers import Suite
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_expr_constant_literal_does_not_fit():
|
||||
def test_expr_constant_literal_does_not_fit_module_constant():
|
||||
code_py = """
|
||||
CONSTANT: u8 = 1000
|
||||
|
||||
@exported
|
||||
def testEntry() -> u8:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_expr_constant_literal_does_not_fit_return():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> u8:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user