First attempts
This commit is contained in:
parent
74a0d700f3
commit
f6b4f7c20a
@ -6,10 +6,6 @@ Contains nothing but the explicit compiler builtins.
|
||||
from typing import Any, Callable, NamedTuple, Type
|
||||
from warnings import warn
|
||||
|
||||
from ..type3.functions import (
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
)
|
||||
from ..type3.routers import (
|
||||
NoRouteForTypeException,
|
||||
TypeApplicationRouter,
|
||||
|
||||
@ -6,7 +6,7 @@ It's intented to be a "any color, as long as it's black" kind of renderer
|
||||
from typing import Any, Generator
|
||||
|
||||
from . import ourlang
|
||||
from .type3.types import Type3, TypeApplication_Struct
|
||||
from .type3.types import Type3, TypeApplication_Struct, expect_function_type
|
||||
|
||||
|
||||
def phasm_render(inp: ourlang.Module[Any]) -> str:
|
||||
@ -141,12 +141,15 @@ def function(inp: ourlang.Function) -> str:
|
||||
if inp.imported:
|
||||
result += '@imported\n'
|
||||
|
||||
params = [type3(x) for x in expect_function_type(inp.type3)]
|
||||
return_type = params.pop()
|
||||
|
||||
args = ', '.join(
|
||||
f'{p.name}: {type3(p.type3)}'
|
||||
for p in inp.posonlyargs
|
||||
f'{p_name}: {p_type3}'
|
||||
for p_name, p_type3 in zip(inp.param_names, params, strict=True)
|
||||
)
|
||||
|
||||
result += f'def {inp.name}({args}) -> {type3(inp.returns_type3)}:\n'
|
||||
result += f'def {inp.name}({args}) -> {return_type}:\n'
|
||||
|
||||
if inp.imported:
|
||||
result += ' pass\n'
|
||||
|
||||
@ -21,6 +21,7 @@ from .type3.types import (
|
||||
TypeConstructor_Function,
|
||||
TypeConstructor_StaticArray,
|
||||
TypeConstructor_Tuple,
|
||||
expect_function_type,
|
||||
)
|
||||
from .wasm import (
|
||||
WasmTypeFloat32,
|
||||
@ -197,9 +198,9 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.VariableReference):
|
||||
if isinstance(inp.variable, ourlang.FunctionParam):
|
||||
wgn.add_statement('local.get', '${}'.format(inp.variable.name))
|
||||
return
|
||||
# if isinstance(inp.variable, ourlang.FunctionParam): # TODO
|
||||
# wgn.add_statement('local.get', '${}'.format(inp.variable.name))
|
||||
# return
|
||||
|
||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
@ -223,7 +224,8 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
|
||||
|
||||
type_var_map: dict[TypeVariable, Type3] = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
|
||||
param_types = expect_function_type(inp.operator.type3)
|
||||
for type_var, arg_expr in zip(param_types, [inp.left, inp.right, inp], strict=True):
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(type_var, Type3):
|
||||
@ -252,7 +254,8 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
|
||||
# FIXME: Duplicate code with BinaryOp
|
||||
type_var_map = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
|
||||
param_types = expect_function_type(inp.function.type3)
|
||||
for type_var, arg_expr in zip(param_types, inp.arguments + [inp], strict=True):
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(type_var, Type3):
|
||||
@ -277,13 +280,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
|
||||
return
|
||||
|
||||
if isinstance(inp.function, ourlang.FunctionParam):
|
||||
assert isinstance(inp.function.type3.application.constructor, TypeConstructor_Function)
|
||||
|
||||
params = [
|
||||
type3(mod, x)
|
||||
for x in inp.function.type3.application.arguments
|
||||
]
|
||||
|
||||
params = [type3(mod, x) for x in expect_function_type(inp.function.type3)]
|
||||
result = params.pop()
|
||||
|
||||
wgn.add_statement('local.get', '${}'.format(inp.function.name))
|
||||
@ -388,27 +385,26 @@ def statement(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], fun: ourla
|
||||
|
||||
raise NotImplementedError(statement, inp)
|
||||
|
||||
def function_argument(mod: ourlang.Module[WasmGenerator], inp: ourlang.FunctionParam) -> wasm.Param:
|
||||
"""
|
||||
Compile: function argument
|
||||
"""
|
||||
return (inp.name, type3(mod, inp.type3), )
|
||||
|
||||
def import_(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.Import:
|
||||
"""
|
||||
Compile: imported function
|
||||
"""
|
||||
assert inp.imported
|
||||
|
||||
param_types = [type3(mod, x) for x in expect_function_type(inp.type3)]
|
||||
return_type = param_types.pop()
|
||||
|
||||
params = [
|
||||
(p_name, p_type)
|
||||
for p_name, p_type in zip(inp.param_names, param_types, strict=True)
|
||||
]
|
||||
|
||||
return wasm.Import(
|
||||
inp.imported,
|
||||
inp.name,
|
||||
inp.name,
|
||||
[
|
||||
function_argument(mod, x)
|
||||
for x in inp.posonlyargs
|
||||
],
|
||||
type3(mod, inp.returns_type3)
|
||||
params,
|
||||
return_type,
|
||||
)
|
||||
|
||||
def function(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.Function:
|
||||
@ -425,18 +421,23 @@ def function(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.
|
||||
for stat in inp.statements:
|
||||
statement(wgn, mod, inp, stat)
|
||||
|
||||
param_types = [type3(mod, x) for x in expect_function_type(inp.type3)]
|
||||
return_type = param_types.pop()
|
||||
|
||||
params = [
|
||||
(p_name, p_type)
|
||||
for p_name, p_type in zip(inp.param_names, param_types, strict=True)
|
||||
]
|
||||
|
||||
return wasm.Function(
|
||||
inp.name,
|
||||
inp.name if inp.exported else None,
|
||||
[
|
||||
function_argument(mod, x)
|
||||
for x in inp.posonlyargs
|
||||
],
|
||||
params,
|
||||
[
|
||||
(k, v.wasm_type(), )
|
||||
for k, v in wgn.locals.items()
|
||||
],
|
||||
type3(mod, inp.returns_type3),
|
||||
return_type,
|
||||
wgn.statements
|
||||
)
|
||||
|
||||
|
||||
@ -4,9 +4,14 @@ Contains the syntax tree for ourlang
|
||||
from typing import Dict, Iterable, List, Optional, Union
|
||||
|
||||
from .build.base import BuildBase
|
||||
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||
from .type3.functions import TypeVariableContext
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import Type3, TypeApplication_Struct
|
||||
from .type3.types import (
|
||||
Type3,
|
||||
TypeApplication_Struct,
|
||||
expect_function_type,
|
||||
expect_struct_type,
|
||||
)
|
||||
|
||||
|
||||
class Expression:
|
||||
@ -256,7 +261,7 @@ class StatementIf(Statement):
|
||||
self.test = test
|
||||
self.statements = []
|
||||
self.else_statements = []
|
||||
|
||||
|
||||
class FunctionParam:
|
||||
"""
|
||||
A parameter for a Function
|
||||
@ -277,38 +282,50 @@ class Function:
|
||||
"""
|
||||
A function processes input and produces output
|
||||
"""
|
||||
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'signature', 'returns_type3', 'posonlyargs', )
|
||||
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'signature', 'type3', 'param_names', )
|
||||
|
||||
name: str
|
||||
lineno: int
|
||||
exported: bool
|
||||
imported: Optional[str]
|
||||
statements: List[Statement]
|
||||
signature: FunctionSignature
|
||||
returns_type3: Type3
|
||||
posonlyargs: List[FunctionParam]
|
||||
type3: Type3
|
||||
param_names: list[str]
|
||||
|
||||
def __init__(self, name: str, lineno: int, returns_type3: Type3) -> None:
|
||||
def __init__(self, name: str, lineno: int, type3: Type3, param_names: list[str]) -> None:
|
||||
self.name = name
|
||||
self.lineno = lineno
|
||||
self.exported = False
|
||||
self.imported = None
|
||||
self.statements = []
|
||||
self.signature = FunctionSignature(TypeVariableContext(), [])
|
||||
self.returns_type3 = returns_type3
|
||||
self.posonlyargs = []
|
||||
self.type3 = type3
|
||||
self.param_names = param_names
|
||||
|
||||
def get_params(self) -> list[FunctionParam]:
|
||||
param_types = list(expect_function_type(self.type3))
|
||||
param_types.pop()
|
||||
|
||||
return [
|
||||
FunctionParam(p_name, p_type)
|
||||
for p_name, p_type in zip(self.param_names, param_types)
|
||||
]
|
||||
|
||||
class StructDefinition:
|
||||
"""
|
||||
The definition for a struct
|
||||
"""
|
||||
__slots__ = ('struct_type3', 'lineno', )
|
||||
__slots__ = ('struct_type3', 'const_type3', 'lineno', )
|
||||
|
||||
struct_type3: Type3
|
||||
const_type3: Type3
|
||||
lineno: int
|
||||
|
||||
def __init__(self, struct_type3: Type3, lineno: int) -> None:
|
||||
def __init__(self, struct_type3: Type3, const_type3: Type3, lineno: int) -> None:
|
||||
expect_struct_type(struct_type3)
|
||||
expect_function_type(const_type3)
|
||||
|
||||
self.struct_type3 = struct_type3
|
||||
self.const_type3 = const_type3
|
||||
self.lineno = lineno
|
||||
|
||||
class StructConstructor(Function):
|
||||
@ -322,18 +339,13 @@ class StructConstructor(Function):
|
||||
|
||||
struct_type3: Type3
|
||||
|
||||
def __init__(self, struct_type3: Type3) -> None:
|
||||
super().__init__(f'@{struct_type3.name}@__init___@', -1, struct_type3)
|
||||
def __init__(self, struct_type3: Type3, const_type3: Type3) -> None:
|
||||
struct_params = expect_struct_type(struct_type3)
|
||||
expect_function_type(const_type3)
|
||||
|
||||
assert isinstance(struct_type3.application, TypeApplication_Struct)
|
||||
param_names = [x[0] for x in struct_params]
|
||||
|
||||
for mem, typ in struct_type3.application.arguments:
|
||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||
self.signature.args.append(typ)
|
||||
|
||||
self.signature.args.append(struct_type3)
|
||||
|
||||
self.struct_type3 = struct_type3
|
||||
super().__init__(f'@{struct_type3.name}@__init___@', -1, const_type3, param_names)
|
||||
|
||||
class ModuleConstantDef:
|
||||
"""
|
||||
|
||||
@ -49,7 +49,7 @@ def phasm_parse(source: str) -> Module[Generator]:
|
||||
our_visitor = OurVisitor(build)
|
||||
return our_visitor.visit_Module(res)
|
||||
|
||||
OurLocals = Dict[str, Union[FunctionParam]] # FIXME: Does it become easier if we add ModuleConstantDef to this dict?
|
||||
OurLocals = Dict[str, FunctionParam]
|
||||
|
||||
class OptimizerTransformer(ast.NodeTransformer):
|
||||
"""
|
||||
@ -124,7 +124,7 @@ class OurVisitor[G]:
|
||||
)
|
||||
|
||||
module.types[res.struct_type3.name] = res.struct_type3
|
||||
module.functions[res.struct_type3.name] = StructConstructor(res.struct_type3)
|
||||
module.functions[res.struct_type3.name] = StructConstructor(res.struct_type3, res.const_type3)
|
||||
# Store that the definition was done in this module for the formatter
|
||||
module.struct_definitions[res.struct_type3.name] = res
|
||||
|
||||
@ -156,24 +156,27 @@ class OurVisitor[G]:
|
||||
raise NotImplementedError(f'{node} on Module')
|
||||
|
||||
def pre_visit_Module_FunctionDef(self, module: Module[G], node: ast.FunctionDef) -> Function:
|
||||
function = Function(node.name, node.lineno, self.build.none_)
|
||||
|
||||
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
||||
|
||||
arg_names: list[str]
|
||||
arg_types: list[Type3]
|
||||
for arg in node.args.args:
|
||||
if arg.annotation is None:
|
||||
_raise_static_error(node, 'Must give a argument type')
|
||||
|
||||
arg_type = self.visit_type(module, arg.annotation)
|
||||
arg_names.append(arg.arg)
|
||||
arg_types.append(self.visit_type(module, arg.annotation))
|
||||
|
||||
# FIXME: Allow TypeVariable in the function signature
|
||||
# This would also require FunctionParam to accept a placeholder
|
||||
if node.returns is None: # Note: `-> None` would be a ast.Constant
|
||||
_raise_static_error(node, 'Must give a return type')
|
||||
arg_types.append(self.visit_type(module, node.returns))
|
||||
|
||||
function.signature.args.append(arg_type)
|
||||
function.posonlyargs.append(FunctionParam(
|
||||
arg.arg,
|
||||
arg_type,
|
||||
))
|
||||
function = Function(
|
||||
node.name,
|
||||
node.lineno,
|
||||
self.build.function(*arg_types),
|
||||
arg_names,
|
||||
)
|
||||
|
||||
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
||||
_not_implemented(not node.args.kwonlyargs, 'FunctionDef.args.kwonlyargs')
|
||||
@ -181,8 +184,6 @@ class OurVisitor[G]:
|
||||
_not_implemented(not node.args.kwarg, 'FunctionDef.args.kwarg')
|
||||
_not_implemented(not node.args.defaults, 'FunctionDef.args.defaults')
|
||||
|
||||
# Do stmts at the end so we have the return value
|
||||
|
||||
for decorator in node.decorator_list:
|
||||
if isinstance(decorator, ast.Call):
|
||||
if not isinstance(decorator.func, ast.Name):
|
||||
@ -213,12 +214,6 @@ class OurVisitor[G]:
|
||||
else:
|
||||
function.imported = 'imports'
|
||||
|
||||
if node.returns is None: # Note: `-> None` would be a ast.Constant
|
||||
_raise_static_error(node, 'Must give a return type')
|
||||
return_type = self.visit_type(module, node.returns)
|
||||
function.signature.args.append(return_type)
|
||||
function.returns_type3 = return_type
|
||||
|
||||
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
||||
|
||||
return function
|
||||
@ -249,7 +244,14 @@ class OurVisitor[G]:
|
||||
|
||||
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
||||
|
||||
return StructDefinition(module.build.struct(node.name, tuple(members.items())), node.lineno)
|
||||
struct_type3 = module.build.struct(node.name, tuple(members.items()))
|
||||
const_type3 = module.build.function(*members.values(), struct_type3)
|
||||
|
||||
return StructDefinition(
|
||||
const_type3,
|
||||
struct_type3,
|
||||
node.lineno,
|
||||
)
|
||||
|
||||
def pre_visit_Module_AnnAssign(self, module: Module[G], node: ast.AnnAssign) -> ModuleConstantDef:
|
||||
if not isinstance(node.target, ast.Name):
|
||||
@ -313,7 +315,7 @@ class OurVisitor[G]:
|
||||
|
||||
our_locals: OurLocals = {
|
||||
x.name: x
|
||||
for x in function.posonlyargs
|
||||
for x in function.get_params()
|
||||
}
|
||||
|
||||
for stmt in node.body:
|
||||
@ -478,12 +480,12 @@ class OurVisitor[G]:
|
||||
if not isinstance(node.func.ctx, ast.Load):
|
||||
_raise_static_error(node, 'Must be load context')
|
||||
|
||||
func: Union[Function, FunctionParam, Type3ClassMethod]
|
||||
func: Union[Function, Type3ClassMethod]
|
||||
|
||||
if node.func.id in module.methods:
|
||||
func = module.methods[node.func.id]
|
||||
elif node.func.id in our_locals:
|
||||
func = our_locals[node.func.id]
|
||||
# elif node.func.id in our_locals: # TODO
|
||||
# func = our_locals[node.func.id]
|
||||
else:
|
||||
if node.func.id not in module.functions:
|
||||
_raise_static_error(node, 'Call to undefined function')
|
||||
|
||||
@ -20,13 +20,12 @@ from .constraints import (
|
||||
from .functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
FunctionArgument,
|
||||
FunctionSignature,
|
||||
TypeVariable,
|
||||
TypeVariableApplication_Unary,
|
||||
TypeVariableContext,
|
||||
)
|
||||
from .placeholders import PlaceholderForType
|
||||
from .types import Type3, TypeApplication_Struct, TypeConstructor_Function
|
||||
from .types import Type3, TypeApplication_Struct, TypeConstructor_Function, expect_function_type
|
||||
|
||||
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
||||
|
||||
@ -49,26 +48,17 @@ def expression_binary_op(ctx: Context, inp: ourlang.BinaryOp, phft: PlaceholderF
|
||||
return _expression_function_call(
|
||||
ctx,
|
||||
f'({inp.operator.name})',
|
||||
inp.operator.signature,
|
||||
inp.operator.type3,
|
||||
[inp.left, inp.right],
|
||||
inp,
|
||||
phft,
|
||||
)
|
||||
|
||||
def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
if isinstance(inp.function, ourlang.FunctionParam):
|
||||
assert isinstance(inp.function.type3.application.constructor, TypeConstructor_Function)
|
||||
signature = FunctionSignature(
|
||||
TypeVariableContext(),
|
||||
inp.function.type3.application.arguments,
|
||||
)
|
||||
else:
|
||||
signature = inp.function.signature
|
||||
|
||||
return _expression_function_call(
|
||||
ctx,
|
||||
inp.function.name,
|
||||
signature,
|
||||
inp.function.type3,
|
||||
inp.arguments,
|
||||
inp,
|
||||
phft,
|
||||
@ -77,7 +67,7 @@ def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: Plac
|
||||
def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
yield SameTypeConstraint(
|
||||
ctx,
|
||||
ctx.build.function(*(x.type3 for x in inp.function.posonlyargs), inp.function.returns_type3),
|
||||
inp.function.type3,
|
||||
phft,
|
||||
comment=f'typeOf("{inp.function.name}") == typeOf({inp.function.name})',
|
||||
)
|
||||
@ -85,7 +75,7 @@ def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference,
|
||||
def _expression_function_call(
|
||||
ctx: Context,
|
||||
func_name: str,
|
||||
signature: FunctionSignature,
|
||||
type3: Type3,
|
||||
arguments: list[ourlang.Expression],
|
||||
return_expr: ourlang.Expression,
|
||||
return_phft: PlaceholderForType,
|
||||
@ -96,6 +86,8 @@ def _expression_function_call(
|
||||
A Binary operator functions pretty much the same as a function call
|
||||
with two arguments - it's only a syntactic difference.
|
||||
"""
|
||||
param_types = expect_function_type(type3)
|
||||
|
||||
# First create placeholders for all arguments, and generate their constraints
|
||||
arg_placeholders = {
|
||||
arg_expr: PlaceholderForType([arg_expr])
|
||||
@ -115,82 +107,84 @@ def _expression_function_call(
|
||||
# subsituted - that's done by arg_placeholders.
|
||||
type_var_map = {
|
||||
x: PlaceholderForType([])
|
||||
for x in signature.args
|
||||
for x in param_types
|
||||
if isinstance(x, TypeVariable)
|
||||
}
|
||||
|
||||
for constraint in signature.context.constraints:
|
||||
if isinstance(constraint, Constraint_TypeClassInstanceExists):
|
||||
yield MustImplementTypeClassConstraint(
|
||||
ctx,
|
||||
constraint.type_class3,
|
||||
[type_var_map[x] for x in constraint.types],
|
||||
)
|
||||
continue
|
||||
# for constraint in signature.context.constraints: # TODO
|
||||
# if isinstance(constraint, Constraint_TypeClassInstanceExists):
|
||||
# yield MustImplementTypeClassConstraint(
|
||||
# ctx,
|
||||
# constraint.type_class3,
|
||||
# [type_var_map[x] for x in constraint.types],
|
||||
# )
|
||||
# continue
|
||||
|
||||
raise NotImplementedError(constraint)
|
||||
# raise NotImplementedError(constraint)
|
||||
|
||||
func_var_map = {
|
||||
x: PlaceholderForType([])
|
||||
for x in signature.args
|
||||
for x in param_types
|
||||
if isinstance(x, FunctionArgument)
|
||||
}
|
||||
|
||||
# If some of the function arguments are functions,
|
||||
# we need to deal with those separately.
|
||||
for sig_arg in signature.args:
|
||||
if not isinstance(sig_arg, FunctionArgument):
|
||||
continue
|
||||
# TODO
|
||||
# # If some of the function arguments are functions,
|
||||
# # we need to deal with those separately.
|
||||
# for sig_arg in param_types:
|
||||
# if not isinstance(sig_arg, FunctionArgument):
|
||||
# continue
|
||||
|
||||
# Ensure that for all type variables in the function
|
||||
# there are also type variables available
|
||||
for func_arg in sig_arg.args:
|
||||
if isinstance(func_arg, Type3):
|
||||
continue
|
||||
# # Ensure that for all type variables in the function
|
||||
# # there are also type variables available
|
||||
# for func_arg in sig_arg.args:
|
||||
# if isinstance(func_arg, Type3):
|
||||
# continue
|
||||
|
||||
type_var_map.setdefault(func_arg, PlaceholderForType([]))
|
||||
# type_var_map.setdefault(func_arg, PlaceholderForType([]))
|
||||
|
||||
yield SameFunctionArgumentConstraint(
|
||||
ctx,
|
||||
func_var_map[sig_arg],
|
||||
sig_arg,
|
||||
type_var_map,
|
||||
comment=f'Ensure `{sig_arg.name}` matches in {signature}',
|
||||
)
|
||||
# yield SameFunctionArgumentConstraint(
|
||||
# ctx,
|
||||
# func_var_map[sig_arg],
|
||||
# sig_arg,
|
||||
# type_var_map,
|
||||
# comment=f'Ensure `{sig_arg.name}` matches in {type}',
|
||||
# )
|
||||
|
||||
# If some of the function arguments are type constructors,
|
||||
# we need to deal with those separately.
|
||||
# That is, given `foo :: t a -> a` we need to ensure
|
||||
# that both a's are the same.
|
||||
for sig_arg in signature.args:
|
||||
if isinstance(sig_arg, Type3):
|
||||
# Not a type variable at all
|
||||
continue
|
||||
# TODO
|
||||
# # If some of the function arguments are type constructors,
|
||||
# # we need to deal with those separately.
|
||||
# # That is, given `foo :: t a -> a` we need to ensure
|
||||
# # that both a's are the same.
|
||||
# for sig_arg in param_types:
|
||||
# if isinstance(sig_arg, Type3):
|
||||
# # Not a type variable at all
|
||||
# continue
|
||||
|
||||
if isinstance(sig_arg, FunctionArgument):
|
||||
continue
|
||||
# if isinstance(sig_arg, FunctionArgument):
|
||||
# continue
|
||||
|
||||
if sig_arg.application.constructor is None:
|
||||
# Not a type variable for a type constructor
|
||||
continue
|
||||
# if sig_arg.application.constructor is None:
|
||||
# # Not a type variable for a type constructor
|
||||
# continue
|
||||
|
||||
if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
||||
raise NotImplementedError(sig_arg.application)
|
||||
# if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
||||
# raise NotImplementedError(sig_arg.application)
|
||||
|
||||
if sig_arg.application.arguments not in type_var_map:
|
||||
# e.g., len :: t a -> u32
|
||||
# i.e. "a" does not matter at all
|
||||
continue
|
||||
# if sig_arg.application.arguments not in type_var_map:
|
||||
# # e.g., len :: t a -> u32
|
||||
# # i.e. "a" does not matter at all
|
||||
# continue
|
||||
|
||||
yield SameTypeArgumentConstraint(
|
||||
ctx,
|
||||
type_var_map[sig_arg],
|
||||
type_var_map[sig_arg.application.arguments],
|
||||
comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {signature}',
|
||||
)
|
||||
# yield SameTypeArgumentConstraint(
|
||||
# ctx,
|
||||
# type_var_map[sig_arg],
|
||||
# type_var_map[sig_arg.application.arguments],
|
||||
# comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {type3}',
|
||||
# )
|
||||
|
||||
# Lastly, tie the signature and expression together
|
||||
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [return_expr], strict=True)):
|
||||
for arg_no, (sig_part, arg_expr) in enumerate(zip(param_types, arguments + [return_expr], strict=True)):
|
||||
if arg_no == len(arguments):
|
||||
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
|
||||
else:
|
||||
@ -320,7 +314,7 @@ def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement
|
||||
# a ~ c => c := a
|
||||
# a ~ a => ignore
|
||||
|
||||
yield SameTypeConstraint(ctx, fun.returns_type3, phft,
|
||||
yield SameTypeConstraint(ctx, fun.type3.application.arguments[-1], 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:
|
||||
|
||||
@ -1,194 +1,194 @@
|
||||
from __future__ import annotations
|
||||
# from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Hashable, Iterable, List
|
||||
# from typing import TYPE_CHECKING, Any, Hashable, Iterable, List
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .typeclasses import Type3Class
|
||||
from .types import Type3
|
||||
# if TYPE_CHECKING:
|
||||
# from .typeclasses import Type3Class
|
||||
# from .types import Type3
|
||||
|
||||
|
||||
class TypeVariableApplication_Base[T: Hashable, S: Hashable]:
|
||||
"""
|
||||
Records the constructor and arguments used to create this type.
|
||||
# class TypeVariableApplication_Base[T: Hashable, S: Hashable]:
|
||||
# """
|
||||
# Records the constructor and arguments used to create this type.
|
||||
|
||||
Nullary types, or types of kind *, have both arguments set to None.
|
||||
"""
|
||||
constructor: T
|
||||
arguments: S
|
||||
# Nullary types, or types of kind *, have both arguments set to None.
|
||||
# """
|
||||
# constructor: T
|
||||
# arguments: S
|
||||
|
||||
def __init__(self, constructor: T, arguments: S) -> None:
|
||||
self.constructor = constructor
|
||||
self.arguments = arguments
|
||||
# def __init__(self, constructor: T, arguments: S) -> None:
|
||||
# self.constructor = constructor
|
||||
# self.arguments = arguments
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.constructor, self.arguments, ))
|
||||
# def __hash__(self) -> int:
|
||||
# return hash((self.constructor, self.arguments, ))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if not isinstance(other, TypeVariableApplication_Base):
|
||||
raise NotImplementedError
|
||||
# def __eq__(self, other: Any) -> bool:
|
||||
# if not isinstance(other, TypeVariableApplication_Base):
|
||||
# raise NotImplementedError
|
||||
|
||||
return (self.constructor == other.constructor # type: ignore[no-any-return]
|
||||
and self.arguments == other.arguments)
|
||||
# return (self.constructor == other.constructor # type: ignore[no-any-return]
|
||||
# and self.arguments == other.arguments)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
||||
# def __repr__(self) -> str:
|
||||
# return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
||||
|
||||
class TypeVariable:
|
||||
"""
|
||||
Types variable are used in function definition.
|
||||
# class TypeVariable2:
|
||||
# """
|
||||
# Types variable are used in function definition.
|
||||
|
||||
They are used in places where you don't know the exact type.
|
||||
They are different from PlaceholderForType, as those are instanced
|
||||
during type checking. These type variables are used solely in the
|
||||
function's definition
|
||||
"""
|
||||
__slots__ = ('name', 'application', )
|
||||
# They are used in places where you don't know the exact type.
|
||||
# They are different from PlaceholderForType, as those are instanced
|
||||
# during type checking. These type variables are used solely in the
|
||||
# function's definition
|
||||
# """
|
||||
# __slots__ = ('name', 'application', )
|
||||
|
||||
name: str
|
||||
application: TypeVariableApplication_Base[Any, Any]
|
||||
# name: str
|
||||
# application: TypeVariableApplication_Base[Any, Any]
|
||||
|
||||
def __init__(self, name: str, application: TypeVariableApplication_Base[Any, Any]) -> None:
|
||||
self.name = name
|
||||
self.application = application
|
||||
# def __init__(self, name: str, application: TypeVariableApplication_Base[Any, Any]) -> None:
|
||||
# self.name = name
|
||||
# self.application = application
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.name, self.application, ))
|
||||
# def __hash__(self) -> int:
|
||||
# return hash((self.name, self.application, ))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if not isinstance(other, TypeVariable):
|
||||
raise NotImplementedError
|
||||
# def __eq__(self, other: Any) -> bool:
|
||||
# if not isinstance(other, TypeVariable):
|
||||
# raise NotImplementedError
|
||||
|
||||
return (self.name == other.name
|
||||
and self.application == other.application)
|
||||
# return (self.name == other.name
|
||||
# and self.application == other.application)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeVariable({repr(self.name)})'
|
||||
# def __repr__(self) -> str:
|
||||
# return f'TypeVariable({repr(self.name)})'
|
||||
|
||||
class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
|
||||
"""
|
||||
For the type for this function argument it's not relevant if it was constructed.
|
||||
"""
|
||||
# class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
|
||||
# """
|
||||
# For the type for this function argument it's not relevant if it was constructed.
|
||||
# """
|
||||
|
||||
def make_typevar(name: str) -> TypeVariable:
|
||||
"""
|
||||
Helper function to make a type variable for a non-constructed type.
|
||||
"""
|
||||
return TypeVariable(name, TypeVariableApplication_Nullary(None, None))
|
||||
# def make_typevar(name: str) -> TypeVariable:
|
||||
# """
|
||||
# Helper function to make a type variable for a non-constructed type.
|
||||
# """
|
||||
# return TypeVariable(name, TypeVariableApplication_Nullary(None, None))
|
||||
|
||||
class TypeConstructorVariable:
|
||||
"""
|
||||
Types constructor variable are used in function definition.
|
||||
# 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.
|
||||
# They are a lot like TypeVariable, except that they represent a
|
||||
# type constructor rather than a type directly.
|
||||
|
||||
For now, we only have type constructor variables for kind
|
||||
* -> *.
|
||||
"""
|
||||
__slots__ = ('name', )
|
||||
# For now, we only have type constructor variables for kind
|
||||
# * -> *.
|
||||
# """
|
||||
# __slots__ = ('name', )
|
||||
|
||||
name: str
|
||||
# name: str
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
# def __init__(self, name: str) -> None:
|
||||
# self.name = name
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.name, ))
|
||||
# def __hash__(self) -> int:
|
||||
# return hash((self.name, ))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if other is None:
|
||||
return False
|
||||
# def __eq__(self, other: Any) -> bool:
|
||||
# if other is None:
|
||||
# return False
|
||||
|
||||
if not isinstance(other, TypeConstructorVariable):
|
||||
raise NotImplementedError(other)
|
||||
# if not isinstance(other, TypeConstructorVariable):
|
||||
# raise NotImplementedError(other)
|
||||
|
||||
return (self.name == other.name)
|
||||
# return (self.name == other.name)
|
||||
|
||||
def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
|
||||
return TypeVariable(
|
||||
self.name + ' ' + tvar.name,
|
||||
TypeVariableApplication_Unary(self, tvar)
|
||||
)
|
||||
# def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
|
||||
# return TypeVariable(
|
||||
# self.name + ' ' + tvar.name,
|
||||
# TypeVariableApplication_Unary(self, tvar)
|
||||
# )
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeConstructorVariable({self.name!r})'
|
||||
# def __repr__(self) -> str:
|
||||
# return f'TypeConstructorVariable({self.name!r})'
|
||||
|
||||
class TypeVariableApplication_Unary(TypeVariableApplication_Base[TypeConstructorVariable, TypeVariable]):
|
||||
"""
|
||||
The type for this function argument should be constructed from a type constructor.
|
||||
# class TypeVariableApplication_Unary(TypeVariableApplication_Base[TypeConstructorVariable, TypeVariable]):
|
||||
# """
|
||||
# The type for this function argument should be constructed from a type constructor.
|
||||
|
||||
And we need to know what construtor that was, since that's the one we support.
|
||||
"""
|
||||
# And we need to know what construtor that was, since that's the one we support.
|
||||
# """
|
||||
|
||||
class ConstraintBase:
|
||||
__slots__ = ()
|
||||
# class ConstraintBase:
|
||||
# __slots__ = ()
|
||||
|
||||
class Constraint_TypeClassInstanceExists(ConstraintBase):
|
||||
__slots__ = ('type_class3', 'types', )
|
||||
# class Constraint_TypeClassInstanceExists(ConstraintBase):
|
||||
# __slots__ = ('type_class3', 'types', )
|
||||
|
||||
type_class3: 'Type3Class'
|
||||
types: list[TypeVariable]
|
||||
# type_class3: 'Type3Class'
|
||||
# types: list[TypeVariable]
|
||||
|
||||
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
||||
self.type_class3 = type_class3
|
||||
self.types = list(types)
|
||||
# def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
||||
# self.type_class3 = type_class3
|
||||
# self.types = list(types)
|
||||
|
||||
# Sanity check. AFAIK, if you have a multi-parameter type class,
|
||||
# you can only add a constraint by supplying types for all variables
|
||||
assert len(self.type_class3.args) == len(self.types)
|
||||
# # Sanity check. AFAIK, if you have a multi-parameter type class,
|
||||
# # you can only add a constraint by supplying types for all variables
|
||||
# assert len(self.type_class3.args) == len(self.types)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.type_class3.name + ' ' + ' '.join(x.name for x in self.types)
|
||||
# def __str__(self) -> str:
|
||||
# return self.type_class3.name + ' ' + ' '.join(x.name for x in self.types)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
|
||||
# def __repr__(self) -> str:
|
||||
# return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
|
||||
|
||||
class TypeVariableContext:
|
||||
__slots__ = ('constraints', )
|
||||
# class TypeVariableContext:
|
||||
# __slots__ = ('constraints', )
|
||||
|
||||
constraints: list[ConstraintBase]
|
||||
# constraints: list[ConstraintBase]
|
||||
|
||||
def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
|
||||
self.constraints = list(constraints)
|
||||
# def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
|
||||
# self.constraints = list(constraints)
|
||||
|
||||
def __copy__(self) -> 'TypeVariableContext':
|
||||
return TypeVariableContext(self.constraints)
|
||||
# def __copy__(self) -> 'TypeVariableContext':
|
||||
# return TypeVariableContext(self.constraints)
|
||||
|
||||
def __str__(self) -> str:
|
||||
if not self.constraints:
|
||||
return ''
|
||||
# def __str__(self) -> str:
|
||||
# if not self.constraints:
|
||||
# return ''
|
||||
|
||||
return '(' + ', '.join(str(x) for x in self.constraints) + ') => '
|
||||
# return '(' + ', '.join(str(x) for x in self.constraints) + ') => '
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeVariableContext({self.constraints!r})'
|
||||
# def __repr__(self) -> str:
|
||||
# return f'TypeVariableContext({self.constraints!r})'
|
||||
|
||||
class FunctionArgument:
|
||||
__slots__ = ('args', 'name', )
|
||||
# class FunctionArgument:
|
||||
# __slots__ = ('args', 'name', )
|
||||
|
||||
args: list[Type3 | TypeVariable]
|
||||
name: str
|
||||
# args: list[Type3 | TypeVariable]
|
||||
# name: str
|
||||
|
||||
def __init__(self, args: list[Type3 | TypeVariable]) -> None:
|
||||
self.args = args
|
||||
# def __init__(self, args: list[Type3 | TypeVariable]) -> None:
|
||||
# self.args = args
|
||||
|
||||
self.name = '(' + ' -> '.join(x.name for x in args) + ')'
|
||||
# self.name = '(' + ' -> '.join(x.name for x in args) + ')'
|
||||
|
||||
class FunctionSignature:
|
||||
__slots__ = ('context', 'args', )
|
||||
# class FunctionSignature2:
|
||||
# __slots__ = ('context', 'args', )
|
||||
|
||||
context: TypeVariableContext
|
||||
args: List[Type3 | TypeVariable | FunctionArgument]
|
||||
# context: TypeVariableContext
|
||||
# args: List[Type3 | TypeVariable | FunctionArgument]
|
||||
|
||||
def __init__(self, context: TypeVariableContext, args: Iterable[Type3 | TypeVariable | list[Type3 | TypeVariable]]) -> None:
|
||||
self.context = context.__copy__()
|
||||
self.args = list(
|
||||
FunctionArgument(x) if isinstance(x, list) else x
|
||||
for x in args
|
||||
)
|
||||
# def __init__(self, context: TypeVariableContext, args: Iterable[Type3 | TypeVariable | list[Type3 | TypeVariable]]) -> None:
|
||||
# self.context = context.__copy__()
|
||||
# self.args = list(
|
||||
# FunctionArgument(x) if isinstance(x, list) else x
|
||||
# for x in args
|
||||
# )
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.context) + ' -> '.join(x.name for x in self.args)
|
||||
# def __str__(self) -> str:
|
||||
# return str(self.context) + ' -> '.join(x.name for x in self.args)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'FunctionSignature({self.context!r}, {self.args!r})'
|
||||
# def __repr__(self) -> str:
|
||||
# return f'FunctionSignature({self.context!r}, {self.args!r})'
|
||||
|
||||
@ -3,29 +3,30 @@ from typing import Dict, Iterable, List, Mapping, Optional
|
||||
from .functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
ConstraintBase,
|
||||
FunctionSignature,
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
TypeVariableContext,
|
||||
)
|
||||
from .types import Type3
|
||||
from .types import Type3, expect_function_type
|
||||
|
||||
|
||||
class Type3ClassMethod:
|
||||
__slots__ = ('name', 'signature', )
|
||||
__slots__ = ('name', 'type3', )
|
||||
|
||||
name: str
|
||||
signature: FunctionSignature
|
||||
type3: Type3
|
||||
|
||||
def __init__(self, name: str, type3: Type3) -> None:
|
||||
expect_function_type(type3)
|
||||
|
||||
def __init__(self, name: str, signature: FunctionSignature) -> None:
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
self.type3 = type3
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.name} :: {self.signature}'
|
||||
return f'{self.name} :: {self.type3}'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
|
||||
return f'Type3ClassMethod({repr(self.name)}, {repr(self.type3)})'
|
||||
|
||||
Type3ClassArgs = tuple[TypeVariable] | tuple[TypeVariable, TypeVariable] | tuple[TypeConstructorVariable]
|
||||
|
||||
|
||||
@ -97,6 +97,26 @@ class TypeApplication_Nullary(TypeApplication_Base[None, None]):
|
||||
There was no constructor used to create this type - it's a 'simple' type like u32
|
||||
"""
|
||||
|
||||
def make_type(name: str) -> Type3:
|
||||
"""
|
||||
Make a type of kind *
|
||||
"""
|
||||
return Type3(name, TypeApplication_Nullary(None, None))
|
||||
|
||||
class TypeApplication_Variable(TypeApplication_Base[None, None]):
|
||||
"""
|
||||
We don't know what the actual type is, it's to be determined.
|
||||
"""
|
||||
|
||||
def make_type_variable(name: str) -> Type3:
|
||||
"""
|
||||
Make a type variable
|
||||
|
||||
Type variables always have kind *. But you can construct a type
|
||||
from a type constructor using a type variable.
|
||||
"""
|
||||
return Type3(name, TypeApplication_Variable(None, None))
|
||||
|
||||
class IntType3(KindArgument):
|
||||
"""
|
||||
Sometimes you can have an int on the type level, e.g. when using static arrays
|
||||
@ -282,6 +302,16 @@ class TypeConstructor_Function(TypeConstructor_TypeStar):
|
||||
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
||||
return 'Callable[' + ', '.join(x.name for x in key) + ']'
|
||||
|
||||
def expect_function_type(typ: Type3) -> list[Type3]:
|
||||
"""
|
||||
Checks for a function type and returns its argument types.
|
||||
|
||||
Raises an AssertionError if typ is not a function type.
|
||||
"""
|
||||
assert isinstance(typ.application, TypeApplication_TypeStar)
|
||||
assert isinstance(typ.application.constructor, TypeConstructor_Function)
|
||||
return list(typ.application.arguments)
|
||||
|
||||
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
||||
"""
|
||||
Constructs struct types
|
||||
@ -308,3 +338,13 @@ class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]
|
||||
|
||||
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
||||
pass
|
||||
|
||||
def expect_struct_type(typ: Type3) -> list[tuple[str, Type3]]:
|
||||
"""
|
||||
Checks for a struct type and returns its argument types.
|
||||
|
||||
Raises an AssertionError if typ is not a struct type.
|
||||
"""
|
||||
assert isinstance(typ.application, TypeApplication_Struct)
|
||||
assert isinstance(typ.application.constructor, TypeConstructor_Struct)
|
||||
return list(typ.application.arguments)
|
||||
|
||||
@ -115,8 +115,9 @@ class Suite:
|
||||
if do_format_check:
|
||||
assert self.code_py == '\n' + phasm_render(runner.phasm_ast) # \n for formatting in tests
|
||||
|
||||
func_args = [x.type3 for x in runner.phasm_ast.functions[func_name].posonlyargs]
|
||||
if len(func_args) != len(args):
|
||||
param_types = type3types.expect_function_type(runner.phasm_ast.functions[func_name].type3)
|
||||
param_types.pop()
|
||||
if len(param_types) != len(args):
|
||||
raise RuntimeError(f'Invalid number of args for {func_name}')
|
||||
|
||||
wasm_args: List[Union[float, int]] = []
|
||||
@ -125,7 +126,7 @@ class Suite:
|
||||
write_header(sys.stderr, 'Memory (pre alloc)')
|
||||
runner.interpreter_dump_memory(sys.stderr)
|
||||
|
||||
for arg, arg_typ in zip(args, func_args, strict=True):
|
||||
for arg, arg_typ in zip(args, param_types, strict=True):
|
||||
arg_typ_info = runner.phasm_ast.build.type_info_map.get(arg_typ.name)
|
||||
|
||||
if arg_typ_info and (arg_typ_info.wasm_type is WasmTypeInt32 or arg_typ_info.wasm_type is WasmTypeInt64):
|
||||
@ -287,7 +288,8 @@ def _load_memory_stored_returned_value(
|
||||
func_name: str,
|
||||
wasm_value: Any,
|
||||
) -> Any:
|
||||
ret_type3 = runner.phasm_ast.functions[func_name].returns_type3
|
||||
param_types = type3types.expect_function_type(runner.phasm_ast.functions[func_name].type3)
|
||||
ret_type3 = param_types[-1]
|
||||
|
||||
if ret_type3.name in runner.phasm_ast.build.type_info_map:
|
||||
type_info = runner.phasm_ast.build.type_info_map[ret_type3.name]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user