348 lines
12 KiB
Python
348 lines
12 KiB
Python
from typing import Any, Generator
|
|
|
|
from .. import ourlang
|
|
from ..type3 import functions as type3functions
|
|
from ..type3 import typeclasses as type3classes
|
|
from ..type3 import types as type3types
|
|
from .constraints import (
|
|
CanAccessStructMemberConstraint,
|
|
CanBeSubscriptedConstraint,
|
|
ConstraintBase,
|
|
Context,
|
|
FromLiteralBytes,
|
|
FromLiteralFloat,
|
|
FromLiteralInteger,
|
|
FromTupleConstraint,
|
|
TypeClassInstanceExistsConstraint,
|
|
UnifyTypesConstraint,
|
|
)
|
|
from .kindexpr import Star
|
|
from .typeexpr import TypeApplication, TypeExpr, TypeVariable
|
|
|
|
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
|
|
|
|
|
def phasm_type5_generate_constraints(ctx: Context, inp: ourlang.Module[Any]) -> list[ConstraintBase]:
|
|
return [*module(ctx, inp)]
|
|
|
|
def expression_constant_primitive(ctx: Context, inp: ourlang.ConstantPrimitive, phft: TypeVariable) -> ConstraintGenerator:
|
|
if isinstance(inp.value, int):
|
|
yield FromLiteralInteger(ctx, inp.sourceref, phft, inp.value)
|
|
return
|
|
|
|
if isinstance(inp.value, float):
|
|
yield FromLiteralFloat(ctx, inp.sourceref, phft, inp.value)
|
|
return
|
|
|
|
raise NotImplementedError(inp.value)
|
|
|
|
def expression_constant_bytes(ctx: Context, inp: ourlang.ConstantBytes, phft: TypeVariable) -> ConstraintGenerator:
|
|
yield FromLiteralBytes(ctx, inp.sourceref, phft, inp.value)
|
|
|
|
def expression_constant_tuple(ctx: Context, inp: ourlang.ConstantTuple, phft: TypeVariable) -> ConstraintGenerator:
|
|
member_type5_list = [
|
|
ctx.make_placeholder(arg)
|
|
for arg in inp.value
|
|
]
|
|
|
|
for arg, arg_phft in zip(inp.value, member_type5_list):
|
|
yield from expression(ctx, arg, arg_phft)
|
|
|
|
yield FromTupleConstraint(ctx, inp.sourceref, phft, member_type5_list)
|
|
|
|
def expression_constant_struct(ctx: Context, inp: ourlang.ConstantStruct, phft: TypeVariable) -> ConstraintGenerator:
|
|
member_type5_list = [
|
|
ctx.make_placeholder(arg)
|
|
for arg in inp.value
|
|
]
|
|
|
|
for arg, arg_phft in zip(inp.value, member_type5_list):
|
|
yield from expression(ctx, arg, arg_phft)
|
|
|
|
lft = ctx.build.type5_make_function([x[1] for x in inp.struct_type5.fields] + [inp.struct_type5])
|
|
rgt = ctx.build.type5_make_function(member_type5_list + [phft])
|
|
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, lft, rgt)
|
|
|
|
def expression_constant_memory_stored(ctx: Context, inp: ourlang.ConstantMemoryStored, phft: TypeVariable) -> ConstraintGenerator:
|
|
if isinstance(inp, ourlang.ConstantBytes):
|
|
yield from expression_constant_bytes(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.ConstantTuple):
|
|
yield from expression_constant_tuple(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.ConstantStruct):
|
|
yield from expression_constant_struct(ctx, inp, phft)
|
|
return
|
|
|
|
raise NotImplementedError(inp)
|
|
|
|
def expression_constant(ctx: Context, inp: ourlang.Constant, phft: TypeVariable) -> ConstraintGenerator:
|
|
if isinstance(inp, ourlang.ConstantPrimitive):
|
|
yield from expression_constant_primitive(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.ConstantMemoryStored):
|
|
yield from expression_constant_memory_stored(ctx, inp, phft)
|
|
return
|
|
|
|
raise NotImplementedError(inp)
|
|
|
|
def expression_variable_reference(ctx: Context, inp: ourlang.VariableReference, phft: TypeVariable) -> ConstraintGenerator:
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, inp.variable.type5, phft)
|
|
|
|
def expression_binary_operator(ctx: Context, inp: ourlang.BinaryOp, phft: TypeVariable) -> ConstraintGenerator:
|
|
yield from expression_function_call(
|
|
ctx,
|
|
_binary_op_to_function(ctx, inp),
|
|
phft,
|
|
)
|
|
|
|
def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: TypeVariable) -> ConstraintGenerator:
|
|
arg_typ_list = []
|
|
for arg in inp.arguments:
|
|
arg_tv = ctx.make_placeholder(arg)
|
|
yield from expression(ctx, arg, arg_tv)
|
|
arg_typ_list.append(arg_tv)
|
|
|
|
if isinstance(inp.function, type3classes.Type3ClassMethod):
|
|
func_type, constraints = _signature_to_type5(ctx, inp.sourceref, inp.function.signature)
|
|
else:
|
|
assert isinstance(inp.function.type5, TypeExpr)
|
|
func_type = inp.function.type5
|
|
constraints = []
|
|
|
|
expr_type = ctx.build.type5_make_function(arg_typ_list + [phft])
|
|
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, func_type, expr_type)
|
|
yield from constraints
|
|
|
|
def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference, phft: TypeVariable) -> ConstraintGenerator:
|
|
assert inp.function.type5 is not None # Todo: Make not nullable
|
|
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, inp.function.type5, phft)
|
|
|
|
def expression_tuple_instantiation(ctx: Context, inp: ourlang.TupleInstantiation, phft: TypeVariable) -> ConstraintGenerator:
|
|
arg_typ_list = []
|
|
for arg in inp.elements:
|
|
arg_tv = ctx.make_placeholder(arg)
|
|
yield from expression(ctx, arg, arg_tv)
|
|
arg_typ_list.append(arg_tv)
|
|
|
|
yield FromTupleConstraint(ctx, inp.sourceref, phft, arg_typ_list)
|
|
|
|
def expression_subscript(ctx: Context, inp: ourlang.Subscript, phft: TypeVariable) -> ConstraintGenerator:
|
|
varref_phft = ctx.make_placeholder(inp.varref)
|
|
index_phft = ctx.make_placeholder(inp.index)
|
|
|
|
yield from expression(ctx, inp.varref, varref_phft)
|
|
yield from expression(ctx, inp.index, index_phft)
|
|
|
|
if isinstance(inp.index, ourlang.ConstantPrimitive) and isinstance(inp.index.value, int):
|
|
yield CanBeSubscriptedConstraint(ctx, inp.sourceref, phft, varref_phft, index_phft, inp.index.value)
|
|
else:
|
|
yield CanBeSubscriptedConstraint(ctx, inp.sourceref, phft, varref_phft, index_phft, None)
|
|
|
|
def expression_access_struct_member(ctx: Context, inp: ourlang.AccessStructMember, phft: TypeVariable) -> ConstraintGenerator:
|
|
varref_phft = ctx.make_placeholder(inp.varref)
|
|
|
|
yield from expression_variable_reference(ctx, inp.varref, varref_phft)
|
|
|
|
yield CanAccessStructMemberConstraint(ctx, inp.sourceref, phft, varref_phft, inp.member)
|
|
|
|
def expression(ctx: Context, inp: ourlang.Expression, phft: TypeVariable) -> ConstraintGenerator:
|
|
if isinstance(inp, ourlang.Constant):
|
|
yield from expression_constant(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.VariableReference):
|
|
yield from expression_variable_reference(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.BinaryOp):
|
|
yield from expression_binary_operator(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.FunctionCall):
|
|
yield from expression_function_call(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.FunctionReference):
|
|
yield from expression_function_reference(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.TupleInstantiation):
|
|
yield from expression_tuple_instantiation(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.Subscript):
|
|
yield from expression_subscript(ctx, inp, phft)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.AccessStructMember):
|
|
yield from expression_access_struct_member(ctx, inp, phft)
|
|
return
|
|
|
|
raise NotImplementedError(inp)
|
|
|
|
def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementReturn) -> ConstraintGenerator:
|
|
phft = ctx.make_placeholder(inp.value)
|
|
|
|
if fun.type5 is None:
|
|
raise NotImplementedError("Deducing function type - you'll have to annotate it.")
|
|
|
|
if isinstance(fun.type5, TypeApplication):
|
|
args = ctx.build.type5_is_function(fun.type5)
|
|
assert args is not None
|
|
type5 = args[-1]
|
|
else:
|
|
type5 = fun.type5
|
|
|
|
yield from expression(ctx, inp.value, phft)
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, type5, phft)
|
|
|
|
def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator:
|
|
test_phft = ctx.make_placeholder(inp.test)
|
|
|
|
yield from expression(ctx, inp.test, test_phft)
|
|
|
|
yield UnifyTypesConstraint(ctx, inp.test.sourceref, test_phft, ctx.build.bool_type5)
|
|
|
|
for stmt in inp.statements:
|
|
yield from statement(ctx, fun, stmt)
|
|
|
|
for stmt in inp.else_statements:
|
|
yield from statement(ctx, fun, stmt)
|
|
|
|
def statement(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement) -> ConstraintGenerator:
|
|
if isinstance(inp, ourlang.StatementReturn):
|
|
yield from statement_return(ctx, fun, inp)
|
|
return
|
|
|
|
if isinstance(inp, ourlang.StatementIf):
|
|
yield from statement_if(ctx, fun, inp)
|
|
return
|
|
|
|
raise NotImplementedError(inp)
|
|
|
|
def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator:
|
|
for stmt in inp.statements:
|
|
yield from statement(ctx, inp, stmt)
|
|
|
|
def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> ConstraintGenerator:
|
|
phft = ctx.make_placeholder(inp.constant)
|
|
|
|
yield from expression_constant(ctx, inp.constant, phft)
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, inp.type5, phft)
|
|
|
|
def module(ctx: Context, inp: ourlang.Module[Any]) -> ConstraintGenerator:
|
|
for cdef in inp.constant_defs.values():
|
|
yield from module_constant_def(ctx, cdef)
|
|
|
|
for func in inp.functions.values():
|
|
if func.imported:
|
|
continue
|
|
|
|
yield from function(ctx, func)
|
|
|
|
# TODO: Generalize?
|
|
|
|
def _binary_op_to_function(ctx: Context, inp: ourlang.BinaryOp) -> ourlang.FunctionCall:
|
|
"""
|
|
Temporary method while migrating from type3 to type5
|
|
"""
|
|
assert inp.sourceref is not None # TODO: sourceref required
|
|
call = ourlang.FunctionCall(inp.operator, inp.sourceref)
|
|
call.arguments = [inp.left, inp.right]
|
|
return call
|
|
|
|
class TypeVarMap:
|
|
__slots__ = ('ctx', 'cache', )
|
|
|
|
ctx: Context
|
|
cache: dict[type3functions.TypeVariable, TypeVariable | TypeApplication]
|
|
|
|
def __init__(self, ctx: Context):
|
|
self.ctx = ctx
|
|
self.cache = {}
|
|
|
|
def __getitem__(self, var: type3functions.TypeVariable) -> TypeVariable | TypeApplication:
|
|
exists = self.cache.get(var)
|
|
if exists is not None:
|
|
return exists
|
|
|
|
if isinstance(var.application, type3functions.TypeVariableApplication_Nullary):
|
|
res_var = self.ctx.make_placeholder()
|
|
self.cache[var] = res_var
|
|
return res_var
|
|
|
|
if isinstance(var.application, type3functions.TypeVariableApplication_Unary):
|
|
# TODO: t a -> t a
|
|
# solve by caching var.application.constructor in separate map
|
|
cvar = self.ctx.make_placeholder(kind=Star() >> Star())
|
|
avar = self.__getitem__(var.application.arguments)
|
|
res_app = TypeApplication(constructor=cvar, argument=avar)
|
|
self.cache[var] = res_app
|
|
return res_app
|
|
|
|
raise NotImplementedError(var)
|
|
|
|
def _signature_to_type5(
|
|
ctx: Context,
|
|
sourceref: ourlang.SourceRef,
|
|
signature: type3functions.FunctionSignature,
|
|
) -> tuple[TypeExpr, list[TypeClassInstanceExistsConstraint]]:
|
|
"""
|
|
Temporary hack while migrating from type3 to type5
|
|
"""
|
|
tv_map = TypeVarMap(ctx)
|
|
|
|
args: list[TypeExpr] = []
|
|
for t3arg in signature.args:
|
|
if isinstance(t3arg, type3types.Type3):
|
|
args.append(ctx.build.type5s[t3arg.name])
|
|
continue
|
|
|
|
if isinstance(t3arg, type3functions.TypeVariable):
|
|
args.append(tv_map[t3arg])
|
|
continue
|
|
|
|
if isinstance(t3arg, type3functions.FunctionArgument):
|
|
func_t3arg_list: list[TypeExpr] = []
|
|
for func_t3arg in t3arg.args:
|
|
if isinstance(func_t3arg, type3types.Type3):
|
|
func_t3arg_list.append(ctx.build.type5s[t3arg.name])
|
|
continue
|
|
|
|
if isinstance(func_t3arg, type3functions.TypeVariable):
|
|
func_t3arg_list.append(tv_map[func_t3arg])
|
|
continue
|
|
|
|
raise NotImplementedError
|
|
|
|
args.append(ctx.build.type5_make_function(func_t3arg_list))
|
|
continue
|
|
|
|
raise NotImplementedError(t3arg)
|
|
|
|
constraints: list[TypeClassInstanceExistsConstraint] = []
|
|
|
|
for const in signature.context.constraints:
|
|
if isinstance(const, type3functions.Constraint_TypeClassInstanceExists):
|
|
constraints.append(TypeClassInstanceExistsConstraint(
|
|
ctx,
|
|
sourceref,
|
|
const.type_class3.name,
|
|
[
|
|
tv_map[x]
|
|
for x in const.types
|
|
],
|
|
))
|
|
continue
|
|
|
|
raise NotImplementedError(const)
|
|
|
|
return (ctx.build.type5_make_function(args), constraints, )
|