phasm/phasm/type5/fromast.py
Johan B.W. de Vries 820b8dfdce Notes
2025-08-01 18:45:25 +02:00

244 lines
8.9 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,
FromTupleConstraint,
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)
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 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)