Compare commits
2 Commits
1a3bc19dce
...
f6b4f7c20a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b4f7c20a | ||
|
|
74a0d700f3 |
22
TODO.md
22
TODO.md
@ -22,3 +22,25 @@
|
|||||||
- Try to implement the min and max functions using select
|
- Try to implement the min and max functions using select
|
||||||
|
|
||||||
- Read https://bytecodealliance.org/articles/multi-value-all-the-wasm
|
- Read https://bytecodealliance.org/articles/multi-value-all-the-wasm
|
||||||
|
|
||||||
|
|
||||||
|
- GRose :: (* -> *) -> * -> *
|
||||||
|
- skolem => variable that cannot be unified
|
||||||
|
|
||||||
|
|
||||||
|
Limitations (for now):
|
||||||
|
- no type level nats
|
||||||
|
- only support first order kinds
|
||||||
|
Do not support yet:
|
||||||
|
```
|
||||||
|
data Record f = Record {
|
||||||
|
field: f Int
|
||||||
|
}
|
||||||
|
Record :: (* -> *) -> *
|
||||||
|
```
|
||||||
|
(Nested arrows)
|
||||||
|
- only support rank 1 types
|
||||||
|
```
|
||||||
|
mapRecord :: forall f g. (forall a. f a -> f b) -> Record f -> Record g
|
||||||
|
```
|
||||||
|
(Nested forall)
|
||||||
|
|||||||
@ -6,10 +6,6 @@ Contains nothing but the explicit compiler builtins.
|
|||||||
from typing import Any, Callable, NamedTuple, Type
|
from typing import Any, Callable, NamedTuple, Type
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from ..type3.functions import (
|
|
||||||
TypeConstructorVariable,
|
|
||||||
TypeVariable,
|
|
||||||
)
|
|
||||||
from ..type3.routers import (
|
from ..type3.routers import (
|
||||||
NoRouteForTypeException,
|
NoRouteForTypeException,
|
||||||
TypeApplicationRouter,
|
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 typing import Any, Generator
|
||||||
|
|
||||||
from . import ourlang
|
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:
|
def phasm_render(inp: ourlang.Module[Any]) -> str:
|
||||||
@ -141,12 +141,15 @@ def function(inp: ourlang.Function) -> str:
|
|||||||
if inp.imported:
|
if inp.imported:
|
||||||
result += '@imported\n'
|
result += '@imported\n'
|
||||||
|
|
||||||
|
params = [type3(x) for x in expect_function_type(inp.type3)]
|
||||||
|
return_type = params.pop()
|
||||||
|
|
||||||
args = ', '.join(
|
args = ', '.join(
|
||||||
f'{p.name}: {type3(p.type3)}'
|
f'{p_name}: {p_type3}'
|
||||||
for p in inp.posonlyargs
|
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:
|
if inp.imported:
|
||||||
result += ' pass\n'
|
result += ' pass\n'
|
||||||
|
|||||||
@ -21,6 +21,7 @@ from .type3.types import (
|
|||||||
TypeConstructor_Function,
|
TypeConstructor_Function,
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
TypeConstructor_Tuple,
|
TypeConstructor_Tuple,
|
||||||
|
expect_function_type,
|
||||||
)
|
)
|
||||||
from .wasm import (
|
from .wasm import (
|
||||||
WasmTypeFloat32,
|
WasmTypeFloat32,
|
||||||
@ -197,9 +198,9 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
|
|||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.VariableReference):
|
if isinstance(inp, ourlang.VariableReference):
|
||||||
if isinstance(inp.variable, ourlang.FunctionParam):
|
# if isinstance(inp.variable, ourlang.FunctionParam): # TODO
|
||||||
wgn.add_statement('local.get', '${}'.format(inp.variable.name))
|
# wgn.add_statement('local.get', '${}'.format(inp.variable.name))
|
||||||
return
|
# return
|
||||||
|
|
||||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
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] = {}
|
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
|
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if isinstance(type_var, Type3):
|
if isinstance(type_var, Type3):
|
||||||
@ -252,7 +254,8 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
|
|||||||
# FIXME: Duplicate code with BinaryOp
|
# FIXME: Duplicate code with BinaryOp
|
||||||
type_var_map = {}
|
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
|
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if isinstance(type_var, Type3):
|
if isinstance(type_var, Type3):
|
||||||
@ -277,13 +280,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
|
|||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp.function, ourlang.FunctionParam):
|
if isinstance(inp.function, ourlang.FunctionParam):
|
||||||
assert isinstance(inp.function.type3.application.constructor, TypeConstructor_Function)
|
params = [type3(mod, x) for x in expect_function_type(inp.function.type3)]
|
||||||
|
|
||||||
params = [
|
|
||||||
type3(mod, x)
|
|
||||||
for x in inp.function.type3.application.arguments
|
|
||||||
]
|
|
||||||
|
|
||||||
result = params.pop()
|
result = params.pop()
|
||||||
|
|
||||||
wgn.add_statement('local.get', '${}'.format(inp.function.name))
|
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)
|
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:
|
def import_(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.Import:
|
||||||
"""
|
"""
|
||||||
Compile: imported function
|
Compile: imported function
|
||||||
"""
|
"""
|
||||||
assert inp.imported
|
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(
|
return wasm.Import(
|
||||||
inp.imported,
|
inp.imported,
|
||||||
inp.name,
|
inp.name,
|
||||||
inp.name,
|
inp.name,
|
||||||
[
|
params,
|
||||||
function_argument(mod, x)
|
return_type,
|
||||||
for x in inp.posonlyargs
|
|
||||||
],
|
|
||||||
type3(mod, inp.returns_type3)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def function(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.Function:
|
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:
|
for stat in inp.statements:
|
||||||
statement(wgn, mod, inp, stat)
|
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(
|
return wasm.Function(
|
||||||
inp.name,
|
inp.name,
|
||||||
inp.name if inp.exported else None,
|
inp.name if inp.exported else None,
|
||||||
[
|
params,
|
||||||
function_argument(mod, x)
|
|
||||||
for x in inp.posonlyargs
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
(k, v.wasm_type(), )
|
(k, v.wasm_type(), )
|
||||||
for k, v in wgn.locals.items()
|
for k, v in wgn.locals.items()
|
||||||
],
|
],
|
||||||
type3(mod, inp.returns_type3),
|
return_type,
|
||||||
wgn.statements
|
wgn.statements
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,14 @@ Contains the syntax tree for ourlang
|
|||||||
from typing import Dict, Iterable, List, Optional, Union
|
from typing import Dict, Iterable, List, Optional, Union
|
||||||
|
|
||||||
from .build.base import BuildBase
|
from .build.base import BuildBase
|
||||||
from .type3.functions import FunctionSignature, TypeVariableContext
|
from .type3.functions import TypeVariableContext
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
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:
|
class Expression:
|
||||||
@ -277,38 +282,50 @@ class Function:
|
|||||||
"""
|
"""
|
||||||
A function processes input and produces output
|
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
|
name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
exported: bool
|
exported: bool
|
||||||
imported: Optional[str]
|
imported: Optional[str]
|
||||||
statements: List[Statement]
|
statements: List[Statement]
|
||||||
signature: FunctionSignature
|
type3: Type3
|
||||||
returns_type3: Type3
|
param_names: list[str]
|
||||||
posonlyargs: List[FunctionParam]
|
|
||||||
|
|
||||||
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.name = name
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
self.exported = False
|
self.exported = False
|
||||||
self.imported = None
|
self.imported = None
|
||||||
self.statements = []
|
self.statements = []
|
||||||
self.signature = FunctionSignature(TypeVariableContext(), [])
|
self.type3 = type3
|
||||||
self.returns_type3 = returns_type3
|
self.param_names = param_names
|
||||||
self.posonlyargs = []
|
|
||||||
|
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:
|
class StructDefinition:
|
||||||
"""
|
"""
|
||||||
The definition for a struct
|
The definition for a struct
|
||||||
"""
|
"""
|
||||||
__slots__ = ('struct_type3', 'lineno', )
|
__slots__ = ('struct_type3', 'const_type3', 'lineno', )
|
||||||
|
|
||||||
struct_type3: Type3
|
struct_type3: Type3
|
||||||
|
const_type3: Type3
|
||||||
lineno: int
|
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.struct_type3 = struct_type3
|
||||||
|
self.const_type3 = const_type3
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
|
|
||||||
class StructConstructor(Function):
|
class StructConstructor(Function):
|
||||||
@ -322,18 +339,13 @@ class StructConstructor(Function):
|
|||||||
|
|
||||||
struct_type3: Type3
|
struct_type3: Type3
|
||||||
|
|
||||||
def __init__(self, struct_type3: Type3) -> None:
|
def __init__(self, struct_type3: Type3, const_type3: Type3) -> None:
|
||||||
super().__init__(f'@{struct_type3.name}@__init___@', -1, struct_type3)
|
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:
|
super().__init__(f'@{struct_type3.name}@__init___@', -1, const_type3, param_names)
|
||||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
|
||||||
self.signature.args.append(typ)
|
|
||||||
|
|
||||||
self.signature.args.append(struct_type3)
|
|
||||||
|
|
||||||
self.struct_type3 = struct_type3
|
|
||||||
|
|
||||||
class ModuleConstantDef:
|
class ModuleConstantDef:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -49,7 +49,7 @@ def phasm_parse(source: str) -> Module[Generator]:
|
|||||||
our_visitor = OurVisitor(build)
|
our_visitor = OurVisitor(build)
|
||||||
return our_visitor.visit_Module(res)
|
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):
|
class OptimizerTransformer(ast.NodeTransformer):
|
||||||
"""
|
"""
|
||||||
@ -124,7 +124,7 @@ class OurVisitor[G]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
module.types[res.struct_type3.name] = res.struct_type3
|
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
|
# Store that the definition was done in this module for the formatter
|
||||||
module.struct_definitions[res.struct_type3.name] = res
|
module.struct_definitions[res.struct_type3.name] = res
|
||||||
|
|
||||||
@ -156,24 +156,27 @@ class OurVisitor[G]:
|
|||||||
raise NotImplementedError(f'{node} on Module')
|
raise NotImplementedError(f'{node} on Module')
|
||||||
|
|
||||||
def pre_visit_Module_FunctionDef(self, module: Module[G], node: ast.FunctionDef) -> Function:
|
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')
|
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
||||||
|
|
||||||
|
arg_names: list[str]
|
||||||
|
arg_types: list[Type3]
|
||||||
for arg in node.args.args:
|
for arg in node.args.args:
|
||||||
if arg.annotation is None:
|
if arg.annotation is None:
|
||||||
_raise_static_error(node, 'Must give a argument type')
|
_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
|
if node.returns is None: # Note: `-> None` would be a ast.Constant
|
||||||
# This would also require FunctionParam to accept a placeholder
|
_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 = Function(
|
||||||
function.posonlyargs.append(FunctionParam(
|
node.name,
|
||||||
arg.arg,
|
node.lineno,
|
||||||
arg_type,
|
self.build.function(*arg_types),
|
||||||
))
|
arg_names,
|
||||||
|
)
|
||||||
|
|
||||||
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
||||||
_not_implemented(not node.args.kwonlyargs, 'FunctionDef.args.kwonlyargs')
|
_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.kwarg, 'FunctionDef.args.kwarg')
|
||||||
_not_implemented(not node.args.defaults, 'FunctionDef.args.defaults')
|
_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:
|
for decorator in node.decorator_list:
|
||||||
if isinstance(decorator, ast.Call):
|
if isinstance(decorator, ast.Call):
|
||||||
if not isinstance(decorator.func, ast.Name):
|
if not isinstance(decorator.func, ast.Name):
|
||||||
@ -213,12 +214,6 @@ class OurVisitor[G]:
|
|||||||
else:
|
else:
|
||||||
function.imported = 'imports'
|
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')
|
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
||||||
|
|
||||||
return function
|
return function
|
||||||
@ -249,7 +244,14 @@ class OurVisitor[G]:
|
|||||||
|
|
||||||
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
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:
|
def pre_visit_Module_AnnAssign(self, module: Module[G], node: ast.AnnAssign) -> ModuleConstantDef:
|
||||||
if not isinstance(node.target, ast.Name):
|
if not isinstance(node.target, ast.Name):
|
||||||
@ -313,7 +315,7 @@ class OurVisitor[G]:
|
|||||||
|
|
||||||
our_locals: OurLocals = {
|
our_locals: OurLocals = {
|
||||||
x.name: x
|
x.name: x
|
||||||
for x in function.posonlyargs
|
for x in function.get_params()
|
||||||
}
|
}
|
||||||
|
|
||||||
for stmt in node.body:
|
for stmt in node.body:
|
||||||
@ -478,12 +480,12 @@ class OurVisitor[G]:
|
|||||||
if not isinstance(node.func.ctx, ast.Load):
|
if not isinstance(node.func.ctx, ast.Load):
|
||||||
_raise_static_error(node, 'Must be load context')
|
_raise_static_error(node, 'Must be load context')
|
||||||
|
|
||||||
func: Union[Function, FunctionParam, Type3ClassMethod]
|
func: Union[Function, Type3ClassMethod]
|
||||||
|
|
||||||
if node.func.id in module.methods:
|
if node.func.id in module.methods:
|
||||||
func = module.methods[node.func.id]
|
func = module.methods[node.func.id]
|
||||||
elif node.func.id in our_locals:
|
# elif node.func.id in our_locals: # TODO
|
||||||
func = our_locals[node.func.id]
|
# func = our_locals[node.func.id]
|
||||||
else:
|
else:
|
||||||
if node.func.id not in module.functions:
|
if node.func.id not in module.functions:
|
||||||
_raise_static_error(node, 'Call to undefined function')
|
_raise_static_error(node, 'Call to undefined function')
|
||||||
|
|||||||
@ -20,13 +20,12 @@ from .constraints import (
|
|||||||
from .functions import (
|
from .functions import (
|
||||||
Constraint_TypeClassInstanceExists,
|
Constraint_TypeClassInstanceExists,
|
||||||
FunctionArgument,
|
FunctionArgument,
|
||||||
FunctionSignature,
|
|
||||||
TypeVariable,
|
TypeVariable,
|
||||||
TypeVariableApplication_Unary,
|
TypeVariableApplication_Unary,
|
||||||
TypeVariableContext,
|
TypeVariableContext,
|
||||||
)
|
)
|
||||||
from .placeholders import PlaceholderForType
|
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]
|
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
||||||
|
|
||||||
@ -49,26 +48,17 @@ def expression_binary_op(ctx: Context, inp: ourlang.BinaryOp, phft: PlaceholderF
|
|||||||
return _expression_function_call(
|
return _expression_function_call(
|
||||||
ctx,
|
ctx,
|
||||||
f'({inp.operator.name})',
|
f'({inp.operator.name})',
|
||||||
inp.operator.signature,
|
inp.operator.type3,
|
||||||
[inp.left, inp.right],
|
[inp.left, inp.right],
|
||||||
inp,
|
inp,
|
||||||
phft,
|
phft,
|
||||||
)
|
)
|
||||||
|
|
||||||
def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: PlaceholderForType) -> ConstraintGenerator:
|
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(
|
return _expression_function_call(
|
||||||
ctx,
|
ctx,
|
||||||
inp.function.name,
|
inp.function.name,
|
||||||
signature,
|
inp.function.type3,
|
||||||
inp.arguments,
|
inp.arguments,
|
||||||
inp,
|
inp,
|
||||||
phft,
|
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:
|
def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||||
yield SameTypeConstraint(
|
yield SameTypeConstraint(
|
||||||
ctx,
|
ctx,
|
||||||
ctx.build.function(*(x.type3 for x in inp.function.posonlyargs), inp.function.returns_type3),
|
inp.function.type3,
|
||||||
phft,
|
phft,
|
||||||
comment=f'typeOf("{inp.function.name}") == typeOf({inp.function.name})',
|
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(
|
def _expression_function_call(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
func_name: str,
|
func_name: str,
|
||||||
signature: FunctionSignature,
|
type3: Type3,
|
||||||
arguments: list[ourlang.Expression],
|
arguments: list[ourlang.Expression],
|
||||||
return_expr: ourlang.Expression,
|
return_expr: ourlang.Expression,
|
||||||
return_phft: PlaceholderForType,
|
return_phft: PlaceholderForType,
|
||||||
@ -96,6 +86,8 @@ def _expression_function_call(
|
|||||||
A Binary operator functions pretty much the same as a function call
|
A Binary operator functions pretty much the same as a function call
|
||||||
with two arguments - it's only a syntactic difference.
|
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
|
# First create placeholders for all arguments, and generate their constraints
|
||||||
arg_placeholders = {
|
arg_placeholders = {
|
||||||
arg_expr: PlaceholderForType([arg_expr])
|
arg_expr: PlaceholderForType([arg_expr])
|
||||||
@ -115,82 +107,84 @@ def _expression_function_call(
|
|||||||
# subsituted - that's done by arg_placeholders.
|
# subsituted - that's done by arg_placeholders.
|
||||||
type_var_map = {
|
type_var_map = {
|
||||||
x: PlaceholderForType([])
|
x: PlaceholderForType([])
|
||||||
for x in signature.args
|
for x in param_types
|
||||||
if isinstance(x, TypeVariable)
|
if isinstance(x, TypeVariable)
|
||||||
}
|
}
|
||||||
|
|
||||||
for constraint in signature.context.constraints:
|
# for constraint in signature.context.constraints: # TODO
|
||||||
if isinstance(constraint, Constraint_TypeClassInstanceExists):
|
# if isinstance(constraint, Constraint_TypeClassInstanceExists):
|
||||||
yield MustImplementTypeClassConstraint(
|
# yield MustImplementTypeClassConstraint(
|
||||||
ctx,
|
# ctx,
|
||||||
constraint.type_class3,
|
# constraint.type_class3,
|
||||||
[type_var_map[x] for x in constraint.types],
|
# [type_var_map[x] for x in constraint.types],
|
||||||
)
|
# )
|
||||||
continue
|
# continue
|
||||||
|
|
||||||
raise NotImplementedError(constraint)
|
# raise NotImplementedError(constraint)
|
||||||
|
|
||||||
func_var_map = {
|
func_var_map = {
|
||||||
x: PlaceholderForType([])
|
x: PlaceholderForType([])
|
||||||
for x in signature.args
|
for x in param_types
|
||||||
if isinstance(x, FunctionArgument)
|
if isinstance(x, FunctionArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
# If some of the function arguments are functions,
|
# TODO
|
||||||
# we need to deal with those separately.
|
# # If some of the function arguments are functions,
|
||||||
for sig_arg in signature.args:
|
# # we need to deal with those separately.
|
||||||
if not isinstance(sig_arg, FunctionArgument):
|
# for sig_arg in param_types:
|
||||||
continue
|
# if not isinstance(sig_arg, FunctionArgument):
|
||||||
|
# continue
|
||||||
|
|
||||||
# Ensure that for all type variables in the function
|
# # Ensure that for all type variables in the function
|
||||||
# there are also type variables available
|
# # there are also type variables available
|
||||||
for func_arg in sig_arg.args:
|
# for func_arg in sig_arg.args:
|
||||||
if isinstance(func_arg, Type3):
|
# if isinstance(func_arg, Type3):
|
||||||
continue
|
# continue
|
||||||
|
|
||||||
type_var_map.setdefault(func_arg, PlaceholderForType([]))
|
# type_var_map.setdefault(func_arg, PlaceholderForType([]))
|
||||||
|
|
||||||
yield SameFunctionArgumentConstraint(
|
# yield SameFunctionArgumentConstraint(
|
||||||
ctx,
|
# ctx,
|
||||||
func_var_map[sig_arg],
|
# func_var_map[sig_arg],
|
||||||
sig_arg,
|
# sig_arg,
|
||||||
type_var_map,
|
# type_var_map,
|
||||||
comment=f'Ensure `{sig_arg.name}` matches in {signature}',
|
# comment=f'Ensure `{sig_arg.name}` matches in {type}',
|
||||||
)
|
# )
|
||||||
|
|
||||||
# If some of the function arguments are type constructors,
|
# TODO
|
||||||
# we need to deal with those separately.
|
# # If some of the function arguments are type constructors,
|
||||||
# That is, given `foo :: t a -> a` we need to ensure
|
# # we need to deal with those separately.
|
||||||
# that both a's are the same.
|
# # That is, given `foo :: t a -> a` we need to ensure
|
||||||
for sig_arg in signature.args:
|
# # that both a's are the same.
|
||||||
if isinstance(sig_arg, Type3):
|
# for sig_arg in param_types:
|
||||||
# Not a type variable at all
|
# if isinstance(sig_arg, Type3):
|
||||||
continue
|
# # Not a type variable at all
|
||||||
|
# continue
|
||||||
|
|
||||||
if isinstance(sig_arg, FunctionArgument):
|
# if isinstance(sig_arg, FunctionArgument):
|
||||||
continue
|
# continue
|
||||||
|
|
||||||
if sig_arg.application.constructor is None:
|
# if sig_arg.application.constructor is None:
|
||||||
# Not a type variable for a type constructor
|
# # Not a type variable for a type constructor
|
||||||
continue
|
# continue
|
||||||
|
|
||||||
if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
# if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
||||||
raise NotImplementedError(sig_arg.application)
|
# raise NotImplementedError(sig_arg.application)
|
||||||
|
|
||||||
if sig_arg.application.arguments not in type_var_map:
|
# if sig_arg.application.arguments not in type_var_map:
|
||||||
# e.g., len :: t a -> u32
|
# # e.g., len :: t a -> u32
|
||||||
# i.e. "a" does not matter at all
|
# # i.e. "a" does not matter at all
|
||||||
continue
|
# continue
|
||||||
|
|
||||||
yield SameTypeArgumentConstraint(
|
# yield SameTypeArgumentConstraint(
|
||||||
ctx,
|
# ctx,
|
||||||
type_var_map[sig_arg],
|
# type_var_map[sig_arg],
|
||||||
type_var_map[sig_arg.application.arguments],
|
# type_var_map[sig_arg.application.arguments],
|
||||||
comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {signature}',
|
# comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {type3}',
|
||||||
)
|
# )
|
||||||
|
|
||||||
# Lastly, tie the signature and expression together
|
# 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):
|
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'
|
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
|
||||||
else:
|
else:
|
||||||
@ -279,7 +273,48 @@ def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement
|
|||||||
|
|
||||||
yield from expression(ctx, inp.value, phft)
|
yield from expression(ctx, inp.value, phft)
|
||||||
|
|
||||||
yield SameTypeConstraint(ctx, fun.returns_type3, phft,
|
# callme :: (a -> b) -> a -> b :: *
|
||||||
|
# def callme(fun: (a -> b), arg: a) -> b:
|
||||||
|
# return fun(arg)
|
||||||
|
|
||||||
|
# todouble :: u32 -> f32 :: *
|
||||||
|
# def todouble(in: u32) -> f32:
|
||||||
|
# ....
|
||||||
|
|
||||||
|
# def main():
|
||||||
|
# print(callme(todouble, 15))
|
||||||
|
|
||||||
|
# dynamic_array :: * -> *
|
||||||
|
# static_array :: Int -> * -> *
|
||||||
|
#
|
||||||
|
# class Foldable f
|
||||||
|
#
|
||||||
|
# instance Foldable (static_array n)
|
||||||
|
#
|
||||||
|
# def foo(r: u32[4], l: u32[...]) -> ...
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
# def foo(in: a[4]) -> a[4]:
|
||||||
|
|
||||||
|
|
||||||
|
# def min(lft: a, rgt: a) -> a:
|
||||||
|
# if lft < rgt:
|
||||||
|
# return lft
|
||||||
|
# return rgt
|
||||||
|
#
|
||||||
|
# min :: NatNum a => a -> a -> a
|
||||||
|
#
|
||||||
|
# main :: IO
|
||||||
|
# main = do
|
||||||
|
# show $ min 3 4
|
||||||
|
# show $ min 3.1 3.2
|
||||||
|
|
||||||
|
# a ~ b => b := a
|
||||||
|
# a ~ c => c := a
|
||||||
|
# a ~ a => ignore
|
||||||
|
|
||||||
|
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')
|
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:
|
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:
|
# if TYPE_CHECKING:
|
||||||
from .typeclasses import Type3Class
|
# from .typeclasses import Type3Class
|
||||||
from .types import Type3
|
# from .types import Type3
|
||||||
|
|
||||||
|
|
||||||
class TypeVariableApplication_Base[T: Hashable, S: Hashable]:
|
# class TypeVariableApplication_Base[T: Hashable, S: Hashable]:
|
||||||
"""
|
# """
|
||||||
Records the constructor and arguments used to create this type.
|
# Records the constructor and arguments used to create this type.
|
||||||
|
|
||||||
Nullary types, or types of kind *, have both arguments set to None.
|
# Nullary types, or types of kind *, have both arguments set to None.
|
||||||
"""
|
# """
|
||||||
constructor: T
|
# constructor: T
|
||||||
arguments: S
|
# arguments: S
|
||||||
|
|
||||||
def __init__(self, constructor: T, arguments: S) -> None:
|
# def __init__(self, constructor: T, arguments: S) -> None:
|
||||||
self.constructor = constructor
|
# self.constructor = constructor
|
||||||
self.arguments = arguments
|
# self.arguments = arguments
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
# def __hash__(self) -> int:
|
||||||
return hash((self.constructor, self.arguments, ))
|
# return hash((self.constructor, self.arguments, ))
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
# def __eq__(self, other: Any) -> bool:
|
||||||
if not isinstance(other, TypeVariableApplication_Base):
|
# if not isinstance(other, TypeVariableApplication_Base):
|
||||||
raise NotImplementedError
|
# raise NotImplementedError
|
||||||
|
|
||||||
return (self.constructor == other.constructor # type: ignore[no-any-return]
|
# return (self.constructor == other.constructor # type: ignore[no-any-return]
|
||||||
and self.arguments == other.arguments)
|
# and self.arguments == other.arguments)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
# return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
||||||
|
|
||||||
class TypeVariable:
|
# class TypeVariable2:
|
||||||
"""
|
# """
|
||||||
Types variable are used in function definition.
|
# Types variable are used in function definition.
|
||||||
|
|
||||||
They are used in places where you don't know the exact type.
|
# They are used in places where you don't know the exact type.
|
||||||
They are different from PlaceholderForType, as those are instanced
|
# They are different from PlaceholderForType, as those are instanced
|
||||||
during type checking. These type variables are used solely in the
|
# during type checking. These type variables are used solely in the
|
||||||
function's definition
|
# function's definition
|
||||||
"""
|
# """
|
||||||
__slots__ = ('name', 'application', )
|
# __slots__ = ('name', 'application', )
|
||||||
|
|
||||||
name: str
|
# name: str
|
||||||
application: TypeVariableApplication_Base[Any, Any]
|
# application: TypeVariableApplication_Base[Any, Any]
|
||||||
|
|
||||||
def __init__(self, name: str, application: TypeVariableApplication_Base[Any, Any]) -> None:
|
# def __init__(self, name: str, application: TypeVariableApplication_Base[Any, Any]) -> None:
|
||||||
self.name = name
|
# self.name = name
|
||||||
self.application = application
|
# self.application = application
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
# def __hash__(self) -> int:
|
||||||
return hash((self.name, self.application, ))
|
# return hash((self.name, self.application, ))
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
# def __eq__(self, other: Any) -> bool:
|
||||||
if not isinstance(other, TypeVariable):
|
# if not isinstance(other, TypeVariable):
|
||||||
raise NotImplementedError
|
# raise NotImplementedError
|
||||||
|
|
||||||
return (self.name == other.name
|
# return (self.name == other.name
|
||||||
and self.application == other.application)
|
# and self.application == other.application)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f'TypeVariable({repr(self.name)})'
|
# return f'TypeVariable({repr(self.name)})'
|
||||||
|
|
||||||
class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
|
# class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
|
||||||
"""
|
# """
|
||||||
For the type for this function argument it's not relevant if it was constructed.
|
# For the type for this function argument it's not relevant if it was constructed.
|
||||||
"""
|
# """
|
||||||
|
|
||||||
def make_typevar(name: str) -> TypeVariable:
|
# def make_typevar(name: str) -> TypeVariable:
|
||||||
"""
|
# """
|
||||||
Helper function to make a type variable for a non-constructed type.
|
# Helper function to make a type variable for a non-constructed type.
|
||||||
"""
|
# """
|
||||||
return TypeVariable(name, TypeVariableApplication_Nullary(None, None))
|
# return TypeVariable(name, TypeVariableApplication_Nullary(None, None))
|
||||||
|
|
||||||
class TypeConstructorVariable:
|
# class TypeConstructorVariable:
|
||||||
"""
|
# """
|
||||||
Types constructor variable are used in function definition.
|
# Types constructor variable are used in function definition.
|
||||||
|
|
||||||
They are a lot like TypeVariable, except that they represent a
|
# They are a lot like TypeVariable, except that they represent a
|
||||||
type constructor rather than a type directly.
|
# type constructor rather than a type directly.
|
||||||
|
|
||||||
For now, we only have type constructor variables for kind
|
# For now, we only have type constructor variables for kind
|
||||||
* -> *.
|
# * -> *.
|
||||||
"""
|
# """
|
||||||
__slots__ = ('name', )
|
# __slots__ = ('name', )
|
||||||
|
|
||||||
name: str
|
# name: str
|
||||||
|
|
||||||
def __init__(self, name: str) -> None:
|
# def __init__(self, name: str) -> None:
|
||||||
self.name = name
|
# self.name = name
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
# def __hash__(self) -> int:
|
||||||
return hash((self.name, ))
|
# return hash((self.name, ))
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
# def __eq__(self, other: Any) -> bool:
|
||||||
if other is None:
|
# if other is None:
|
||||||
return False
|
# return False
|
||||||
|
|
||||||
if not isinstance(other, TypeConstructorVariable):
|
# if not isinstance(other, TypeConstructorVariable):
|
||||||
raise NotImplementedError(other)
|
# raise NotImplementedError(other)
|
||||||
|
|
||||||
return (self.name == other.name)
|
# return (self.name == other.name)
|
||||||
|
|
||||||
def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
|
# def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
|
||||||
return TypeVariable(
|
# return TypeVariable(
|
||||||
self.name + ' ' + tvar.name,
|
# self.name + ' ' + tvar.name,
|
||||||
TypeVariableApplication_Unary(self, tvar)
|
# TypeVariableApplication_Unary(self, tvar)
|
||||||
)
|
# )
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f'TypeConstructorVariable({self.name!r})'
|
# return f'TypeConstructorVariable({self.name!r})'
|
||||||
|
|
||||||
class TypeVariableApplication_Unary(TypeVariableApplication_Base[TypeConstructorVariable, TypeVariable]):
|
# class TypeVariableApplication_Unary(TypeVariableApplication_Base[TypeConstructorVariable, TypeVariable]):
|
||||||
"""
|
# """
|
||||||
The type for this function argument should be constructed from a type constructor.
|
# 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:
|
# class ConstraintBase:
|
||||||
__slots__ = ()
|
# __slots__ = ()
|
||||||
|
|
||||||
class Constraint_TypeClassInstanceExists(ConstraintBase):
|
# class Constraint_TypeClassInstanceExists(ConstraintBase):
|
||||||
__slots__ = ('type_class3', 'types', )
|
# __slots__ = ('type_class3', 'types', )
|
||||||
|
|
||||||
type_class3: 'Type3Class'
|
# type_class3: 'Type3Class'
|
||||||
types: list[TypeVariable]
|
# types: list[TypeVariable]
|
||||||
|
|
||||||
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
# def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
||||||
self.type_class3 = type_class3
|
# self.type_class3 = type_class3
|
||||||
self.types = list(types)
|
# self.types = list(types)
|
||||||
|
|
||||||
# Sanity check. AFAIK, if you have a multi-parameter type class,
|
# # Sanity check. AFAIK, if you have a multi-parameter type class,
|
||||||
# you can only add a constraint by supplying types for all variables
|
# # you can only add a constraint by supplying types for all variables
|
||||||
assert len(self.type_class3.args) == len(self.types)
|
# assert len(self.type_class3.args) == len(self.types)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
# def __str__(self) -> str:
|
||||||
return self.type_class3.name + ' ' + ' '.join(x.name for x in self.types)
|
# return self.type_class3.name + ' ' + ' '.join(x.name for x in self.types)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
|
# return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
|
||||||
|
|
||||||
class TypeVariableContext:
|
# class TypeVariableContext:
|
||||||
__slots__ = ('constraints', )
|
# __slots__ = ('constraints', )
|
||||||
|
|
||||||
constraints: list[ConstraintBase]
|
# constraints: list[ConstraintBase]
|
||||||
|
|
||||||
def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
|
# def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
|
||||||
self.constraints = list(constraints)
|
# self.constraints = list(constraints)
|
||||||
|
|
||||||
def __copy__(self) -> 'TypeVariableContext':
|
# def __copy__(self) -> 'TypeVariableContext':
|
||||||
return TypeVariableContext(self.constraints)
|
# return TypeVariableContext(self.constraints)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
# def __str__(self) -> str:
|
||||||
if not self.constraints:
|
# if not self.constraints:
|
||||||
return ''
|
# return ''
|
||||||
|
|
||||||
return '(' + ', '.join(str(x) for x in self.constraints) + ') => '
|
# return '(' + ', '.join(str(x) for x in self.constraints) + ') => '
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f'TypeVariableContext({self.constraints!r})'
|
# return f'TypeVariableContext({self.constraints!r})'
|
||||||
|
|
||||||
class FunctionArgument:
|
# class FunctionArgument:
|
||||||
__slots__ = ('args', 'name', )
|
# __slots__ = ('args', 'name', )
|
||||||
|
|
||||||
args: list[Type3 | TypeVariable]
|
# args: list[Type3 | TypeVariable]
|
||||||
name: str
|
# name: str
|
||||||
|
|
||||||
def __init__(self, args: list[Type3 | TypeVariable]) -> None:
|
# def __init__(self, args: list[Type3 | TypeVariable]) -> None:
|
||||||
self.args = args
|
# self.args = args
|
||||||
|
|
||||||
self.name = '(' + ' -> '.join(x.name for x in args) + ')'
|
# self.name = '(' + ' -> '.join(x.name for x in args) + ')'
|
||||||
|
|
||||||
class FunctionSignature:
|
# class FunctionSignature2:
|
||||||
__slots__ = ('context', 'args', )
|
# __slots__ = ('context', 'args', )
|
||||||
|
|
||||||
context: TypeVariableContext
|
# context: TypeVariableContext
|
||||||
args: List[Type3 | TypeVariable | FunctionArgument]
|
# args: List[Type3 | TypeVariable | FunctionArgument]
|
||||||
|
|
||||||
def __init__(self, context: TypeVariableContext, args: Iterable[Type3 | TypeVariable | list[Type3 | TypeVariable]]) -> None:
|
# def __init__(self, context: TypeVariableContext, args: Iterable[Type3 | TypeVariable | list[Type3 | TypeVariable]]) -> None:
|
||||||
self.context = context.__copy__()
|
# self.context = context.__copy__()
|
||||||
self.args = list(
|
# self.args = list(
|
||||||
FunctionArgument(x) if isinstance(x, list) else x
|
# FunctionArgument(x) if isinstance(x, list) else x
|
||||||
for x in args
|
# for x in args
|
||||||
)
|
# )
|
||||||
|
|
||||||
def __str__(self) -> str:
|
# def __str__(self) -> str:
|
||||||
return str(self.context) + ' -> '.join(x.name for x in self.args)
|
# return str(self.context) + ' -> '.join(x.name for x in self.args)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
# def __repr__(self) -> str:
|
||||||
return f'FunctionSignature({self.context!r}, {self.args!r})'
|
# return f'FunctionSignature({self.context!r}, {self.args!r})'
|
||||||
|
|||||||
@ -3,29 +3,30 @@ from typing import Dict, Iterable, List, Mapping, Optional
|
|||||||
from .functions import (
|
from .functions import (
|
||||||
Constraint_TypeClassInstanceExists,
|
Constraint_TypeClassInstanceExists,
|
||||||
ConstraintBase,
|
ConstraintBase,
|
||||||
FunctionSignature,
|
|
||||||
TypeConstructorVariable,
|
TypeConstructorVariable,
|
||||||
TypeVariable,
|
TypeVariable,
|
||||||
TypeVariableContext,
|
TypeVariableContext,
|
||||||
)
|
)
|
||||||
from .types import Type3
|
from .types import Type3, expect_function_type
|
||||||
|
|
||||||
|
|
||||||
class Type3ClassMethod:
|
class Type3ClassMethod:
|
||||||
__slots__ = ('name', 'signature', )
|
__slots__ = ('name', 'type3', )
|
||||||
|
|
||||||
name: str
|
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.name = name
|
||||||
self.signature = signature
|
self.type3 = type3
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f'{self.name} :: {self.signature}'
|
return f'{self.name} :: {self.type3}'
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
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]
|
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
|
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):
|
class IntType3(KindArgument):
|
||||||
"""
|
"""
|
||||||
Sometimes you can have an int on the type level, e.g. when using static arrays
|
Sometimes you can have an int on the type level, e.g. when using static arrays
|
||||||
@ -243,6 +263,26 @@ class TypeConstructor_TypeStar(TypeConstructor_Base[Tuple[Type3, ...]]):
|
|||||||
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
|
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# head :: [a] -> a
|
||||||
|
|
||||||
|
# (+) :: NatNum a => a -> a -> a
|
||||||
|
# instancen NatNum f32
|
||||||
|
# (+) :: wasm_f32_add
|
||||||
|
|
||||||
|
# inc1 :: NatNum a => a -> a
|
||||||
|
# inc1 = (+) 1
|
||||||
|
|
||||||
|
# data Type3
|
||||||
|
# | Type3KindS String None
|
||||||
|
# | Type3KindS_S String Type3
|
||||||
|
# | Type3KindS_S_S String Type3 Type3
|
||||||
|
|
||||||
|
# data TypeVariable3
|
||||||
|
# | TypeVariable3KindS String None
|
||||||
|
# | TypeVariable3KindS_S String Type3
|
||||||
|
# | TypeVariable3KindS_S_S String Type3 Type3
|
||||||
|
|
||||||
|
|
||||||
class TypeConstructor_DynamicArray(TypeConstructor_Type):
|
class TypeConstructor_DynamicArray(TypeConstructor_Type):
|
||||||
def make_name(self, key: Tuple[Type3]) -> str:
|
def make_name(self, key: Tuple[Type3]) -> str:
|
||||||
if 'u8' == key[0].name:
|
if 'u8' == key[0].name:
|
||||||
@ -262,6 +302,16 @@ class TypeConstructor_Function(TypeConstructor_TypeStar):
|
|||||||
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
||||||
return 'Callable[' + ', '.join(x.name for x in key) + ']'
|
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], ...]]):
|
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
||||||
"""
|
"""
|
||||||
Constructs struct types
|
Constructs struct types
|
||||||
@ -288,3 +338,13 @@ class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]
|
|||||||
|
|
||||||
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
||||||
pass
|
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:
|
if do_format_check:
|
||||||
assert self.code_py == '\n' + phasm_render(runner.phasm_ast) # \n for formatting in tests
|
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]
|
param_types = type3types.expect_function_type(runner.phasm_ast.functions[func_name].type3)
|
||||||
if len(func_args) != len(args):
|
param_types.pop()
|
||||||
|
if len(param_types) != len(args):
|
||||||
raise RuntimeError(f'Invalid number of args for {func_name}')
|
raise RuntimeError(f'Invalid number of args for {func_name}')
|
||||||
|
|
||||||
wasm_args: List[Union[float, int]] = []
|
wasm_args: List[Union[float, int]] = []
|
||||||
@ -125,7 +126,7 @@ class Suite:
|
|||||||
write_header(sys.stderr, 'Memory (pre alloc)')
|
write_header(sys.stderr, 'Memory (pre alloc)')
|
||||||
runner.interpreter_dump_memory(sys.stderr)
|
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)
|
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):
|
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,
|
func_name: str,
|
||||||
wasm_value: Any,
|
wasm_value: Any,
|
||||||
) -> 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:
|
if ret_type3.name in runner.phasm_ast.build.type_info_map:
|
||||||
type_info = runner.phasm_ast.build.type_info_map[ret_type3.name]
|
type_info = runner.phasm_ast.build.type_info_map[ret_type3.name]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user