246 lines
9.0 KiB
Python
246 lines
9.0 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,
|
|
LiteralFitsConstraint,
|
|
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(ctx: Context, inp: ourlang.Constant, phft: TypeVariable) -> ConstraintGenerator:
|
|
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
|
yield LiteralFitsConstraint(
|
|
ctx, inp.sourceref, phft, inp,
|
|
comment='The given literal must fit the expected type'
|
|
)
|
|
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 = _signature_to_type5(ctx, inp.function.signature)
|
|
else:
|
|
assert isinstance(inp.function.type5, TypeExpr)
|
|
func_type = inp.function.type5
|
|
|
|
expr_type = ctx.build.type5_make_function(arg_typ_list + [phft])
|
|
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, func_type, expr_type)
|
|
|
|
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)
|
|
|
|
# TODO: How to fix static or dynamic array
|
|
expr_type = ctx.build.type5_make_tuple(arg_typ_list)
|
|
|
|
yield UnifyTypesConstraint(ctx, inp.sourceref, expr_type, phft)
|
|
|
|
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 UnifyTypesConstraint(ctx, inp.sourceref, type5, phft)
|
|
yield from expression(ctx, inp.value, 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
|
|
"""
|
|
opr = inp.operator
|
|
fun = ourlang.Function(opr.name, ourlang.SourceRef("(generated)", -1, -1), ctx.build.none_)
|
|
fun.type5 = _signature_to_type5(ctx, opr.signature)
|
|
|
|
assert inp.sourceref is not None # TODO: sourceref required
|
|
call = ourlang.FunctionCall(fun, inp.sourceref)
|
|
call.arguments = [inp.left, inp.right]
|
|
return call
|
|
|
|
def _signature_to_type5(ctx: Context, signature: type3functions.FunctionSignature) -> TypeExpr:
|
|
"""
|
|
Temporary hack while migrating from type3 to type5
|
|
"""
|
|
tv_map: dict[type3functions.TypeVariable, TypeVariable] = {}
|
|
|
|
s = Star()
|
|
|
|
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):
|
|
tv_map.setdefault(t3arg, TypeVariable(kind=s, name=t3arg.name))
|
|
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):
|
|
tv_map.setdefault(func_t3arg, TypeVariable(kind=s, name=t3arg.name))
|
|
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)
|
|
|
|
return ctx.build.type5_make_function(args)
|