Compare commits

..

No commits in common. "f6b4f7c20aadfe79add7e2996ddbc3291ea418dd" and "1a3bc19dce791faabcd7e78b76b716c57153cae7" have entirely different histories.

11 changed files with 305 additions and 439 deletions

22
TODO.md
View File

@ -22,25 +22,3 @@
- Try to implement the min and max functions using select
- 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)

View File

@ -6,6 +6,10 @@ 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,

View File

@ -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, expect_function_type
from .type3.types import Type3, TypeApplication_Struct
def phasm_render(inp: ourlang.Module[Any]) -> str:
@ -141,15 +141,12 @@ 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}: {p_type3}'
for p_name, p_type3 in zip(inp.param_names, params, strict=True)
f'{p.name}: {type3(p.type3)}'
for p in inp.posonlyargs
)
result += f'def {inp.name}({args}) -> {return_type}:\n'
result += f'def {inp.name}({args}) -> {type3(inp.returns_type3)}:\n'
if inp.imported:
result += ' pass\n'

View File

@ -21,7 +21,6 @@ from .type3.types import (
TypeConstructor_Function,
TypeConstructor_StaticArray,
TypeConstructor_Tuple,
expect_function_type,
)
from .wasm import (
WasmTypeFloat32,
@ -198,9 +197,9 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
return
if isinstance(inp, ourlang.VariableReference):
# if isinstance(inp.variable, ourlang.FunctionParam): # TODO
# wgn.add_statement('local.get', '${}'.format(inp.variable.name))
# return
if isinstance(inp.variable, ourlang.FunctionParam):
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
@ -224,8 +223,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
type_var_map: dict[TypeVariable, Type3] = {}
param_types = expect_function_type(inp.operator.type3)
for type_var, arg_expr in zip(param_types, [inp.left, inp.right, inp], strict=True):
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
if isinstance(type_var, Type3):
@ -254,8 +252,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
# FIXME: Duplicate code with BinaryOp
type_var_map = {}
param_types = expect_function_type(inp.function.type3)
for type_var, arg_expr in zip(param_types, inp.arguments + [inp], strict=True):
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
if isinstance(type_var, Type3):
@ -280,7 +277,13 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourl
return
if isinstance(inp.function, ourlang.FunctionParam):
params = [type3(mod, x) for x in expect_function_type(inp.function.type3)]
assert isinstance(inp.function.type3.application.constructor, TypeConstructor_Function)
params = [
type3(mod, x)
for x in inp.function.type3.application.arguments
]
result = params.pop()
wgn.add_statement('local.get', '${}'.format(inp.function.name))
@ -385,26 +388,27 @@ 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,
params,
return_type,
[
function_argument(mod, x)
for x in inp.posonlyargs
],
type3(mod, inp.returns_type3)
)
def function(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.Function:
@ -421,23 +425,18 @@ 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,
params,
[
function_argument(mod, x)
for x in inp.posonlyargs
],
[
(k, v.wasm_type(), )
for k, v in wgn.locals.items()
],
return_type,
type3(mod, inp.returns_type3),
wgn.statements
)

View File

@ -4,14 +4,9 @@ Contains the syntax tree for ourlang
from typing import Dict, Iterable, List, Optional, Union
from .build.base import BuildBase
from .type3.functions import TypeVariableContext
from .type3.functions import FunctionSignature, TypeVariableContext
from .type3.typeclasses import Type3ClassMethod
from .type3.types import (
Type3,
TypeApplication_Struct,
expect_function_type,
expect_struct_type,
)
from .type3.types import Type3, TypeApplication_Struct
class Expression:
@ -261,7 +256,7 @@ class StatementIf(Statement):
self.test = test
self.statements = []
self.else_statements = []
class FunctionParam:
"""
A parameter for a Function
@ -282,50 +277,38 @@ class Function:
"""
A function processes input and produces output
"""
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'signature', 'type3', 'param_names', )
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'signature', 'returns_type3', 'posonlyargs', )
name: str
lineno: int
exported: bool
imported: Optional[str]
statements: List[Statement]
type3: Type3
param_names: list[str]
signature: FunctionSignature
returns_type3: Type3
posonlyargs: List[FunctionParam]
def __init__(self, name: str, lineno: int, type3: Type3, param_names: list[str]) -> None:
def __init__(self, name: str, lineno: int, returns_type3: Type3) -> None:
self.name = name
self.lineno = lineno
self.exported = False
self.imported = None
self.statements = []
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)
]
self.signature = FunctionSignature(TypeVariableContext(), [])
self.returns_type3 = returns_type3
self.posonlyargs = []
class StructDefinition:
"""
The definition for a struct
"""
__slots__ = ('struct_type3', 'const_type3', 'lineno', )
__slots__ = ('struct_type3', 'lineno', )
struct_type3: Type3
const_type3: Type3
lineno: int
def __init__(self, struct_type3: Type3, const_type3: Type3, lineno: int) -> None:
expect_struct_type(struct_type3)
expect_function_type(const_type3)
def __init__(self, struct_type3: Type3, lineno: int) -> None:
self.struct_type3 = struct_type3
self.const_type3 = const_type3
self.lineno = lineno
class StructConstructor(Function):
@ -339,13 +322,18 @@ class StructConstructor(Function):
struct_type3: Type3
def __init__(self, struct_type3: Type3, const_type3: Type3) -> None:
struct_params = expect_struct_type(struct_type3)
expect_function_type(const_type3)
def __init__(self, struct_type3: Type3) -> None:
super().__init__(f'@{struct_type3.name}@__init___@', -1, struct_type3)
param_names = [x[0] for x in struct_params]
assert isinstance(struct_type3.application, TypeApplication_Struct)
super().__init__(f'@{struct_type3.name}@__init___@', -1, const_type3, param_names)
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
class ModuleConstantDef:
"""

View File

@ -49,7 +49,7 @@ def phasm_parse(source: str) -> Module[Generator]:
our_visitor = OurVisitor(build)
return our_visitor.visit_Module(res)
OurLocals = Dict[str, FunctionParam]
OurLocals = Dict[str, Union[FunctionParam]] # FIXME: Does it become easier if we add ModuleConstantDef to this dict?
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, res.const_type3)
module.functions[res.struct_type3.name] = StructConstructor(res.struct_type3)
# Store that the definition was done in this module for the formatter
module.struct_definitions[res.struct_type3.name] = res
@ -156,27 +156,24 @@ 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_names.append(arg.arg)
arg_types.append(self.visit_type(module, arg.annotation))
arg_type = self.visit_type(module, arg.annotation)
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))
# FIXME: Allow TypeVariable in the function signature
# This would also require FunctionParam to accept a placeholder
function = Function(
node.name,
node.lineno,
self.build.function(*arg_types),
arg_names,
)
function.signature.args.append(arg_type)
function.posonlyargs.append(FunctionParam(
arg.arg,
arg_type,
))
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
_not_implemented(not node.args.kwonlyargs, 'FunctionDef.args.kwonlyargs')
@ -184,6 +181,8 @@ 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):
@ -214,6 +213,12 @@ 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
@ -244,14 +249,7 @@ class OurVisitor[G]:
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
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,
)
return StructDefinition(module.build.struct(node.name, tuple(members.items())), node.lineno)
def pre_visit_Module_AnnAssign(self, module: Module[G], node: ast.AnnAssign) -> ModuleConstantDef:
if not isinstance(node.target, ast.Name):
@ -315,7 +313,7 @@ class OurVisitor[G]:
our_locals: OurLocals = {
x.name: x
for x in function.get_params()
for x in function.posonlyargs
}
for stmt in node.body:
@ -480,12 +478,12 @@ class OurVisitor[G]:
if not isinstance(node.func.ctx, ast.Load):
_raise_static_error(node, 'Must be load context')
func: Union[Function, Type3ClassMethod]
func: Union[Function, FunctionParam, Type3ClassMethod]
if node.func.id in module.methods:
func = module.methods[node.func.id]
# elif node.func.id in our_locals: # TODO
# func = our_locals[node.func.id]
elif node.func.id in our_locals:
func = our_locals[node.func.id]
else:
if node.func.id not in module.functions:
_raise_static_error(node, 'Call to undefined function')

View File

@ -20,12 +20,13 @@ 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, expect_function_type
from .types import Type3, TypeApplication_Struct, TypeConstructor_Function
ConstraintGenerator = Generator[ConstraintBase, None, None]
@ -48,17 +49,26 @@ def expression_binary_op(ctx: Context, inp: ourlang.BinaryOp, phft: PlaceholderF
return _expression_function_call(
ctx,
f'({inp.operator.name})',
inp.operator.type3,
inp.operator.signature,
[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,
inp.function.type3,
signature,
inp.arguments,
inp,
phft,
@ -67,7 +77,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,
inp.function.type3,
ctx.build.function(*(x.type3 for x in inp.function.posonlyargs), inp.function.returns_type3),
phft,
comment=f'typeOf("{inp.function.name}") == typeOf({inp.function.name})',
)
@ -75,7 +85,7 @@ def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference,
def _expression_function_call(
ctx: Context,
func_name: str,
type3: Type3,
signature: FunctionSignature,
arguments: list[ourlang.Expression],
return_expr: ourlang.Expression,
return_phft: PlaceholderForType,
@ -86,8 +96,6 @@ 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])
@ -107,84 +115,82 @@ def _expression_function_call(
# subsituted - that's done by arg_placeholders.
type_var_map = {
x: PlaceholderForType([])
for x in param_types
for x in signature.args
if isinstance(x, TypeVariable)
}
# 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
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
# raise NotImplementedError(constraint)
raise NotImplementedError(constraint)
func_var_map = {
x: PlaceholderForType([])
for x in param_types
for x in signature.args
if isinstance(x, FunctionArgument)
}
# 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
# 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
# # 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 {type}',
# )
yield SameFunctionArgumentConstraint(
ctx,
func_var_map[sig_arg],
sig_arg,
type_var_map,
comment=f'Ensure `{sig_arg.name}` matches in {signature}',
)
# 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 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
# 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 {type3}',
# )
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}',
)
# Lastly, tie the signature and expression together
for arg_no, (sig_part, arg_expr) in enumerate(zip(param_types, arguments + [return_expr], strict=True)):
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, 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:
@ -273,48 +279,7 @@ def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement
yield from expression(ctx, inp.value, 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,
yield SameTypeConstraint(ctx, fun.returns_type3, 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:

View File

@ -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 TypeVariable2:
# """
# Types variable are used in function definition.
class TypeVariable:
"""
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 FunctionSignature2:
# __slots__ = ('context', 'args', )
class FunctionSignature:
__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})'

View File

@ -3,30 +3,29 @@ from typing import Dict, Iterable, List, Mapping, Optional
from .functions import (
Constraint_TypeClassInstanceExists,
ConstraintBase,
FunctionSignature,
TypeConstructorVariable,
TypeVariable,
TypeVariableContext,
)
from .types import Type3, expect_function_type
from .types import Type3
class Type3ClassMethod:
__slots__ = ('name', 'type3', )
__slots__ = ('name', 'signature', )
name: str
type3: Type3
def __init__(self, name: str, type3: Type3) -> None:
expect_function_type(type3)
signature: FunctionSignature
def __init__(self, name: str, signature: FunctionSignature) -> None:
self.name = name
self.type3 = type3
self.signature = signature
def __str__(self) -> str:
return f'{self.name} :: {self.type3}'
return f'{self.name} :: {self.signature}'
def __repr__(self) -> str:
return f'Type3ClassMethod({repr(self.name)}, {repr(self.type3)})'
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
Type3ClassArgs = tuple[TypeVariable] | tuple[TypeVariable, TypeVariable] | tuple[TypeConstructorVariable]

View File

@ -97,26 +97,6 @@ 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
@ -263,26 +243,6 @@ class TypeConstructor_TypeStar(TypeConstructor_Base[Tuple[Type3, ...]]):
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
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):
def make_name(self, key: Tuple[Type3]) -> str:
if 'u8' == key[0].name:
@ -302,16 +262,6 @@ 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
@ -338,13 +288,3 @@ 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)

View File

@ -115,9 +115,8 @@ class Suite:
if do_format_check:
assert self.code_py == '\n' + phasm_render(runner.phasm_ast) # \n for formatting in tests
param_types = type3types.expect_function_type(runner.phasm_ast.functions[func_name].type3)
param_types.pop()
if len(param_types) != len(args):
func_args = [x.type3 for x in runner.phasm_ast.functions[func_name].posonlyargs]
if len(func_args) != len(args):
raise RuntimeError(f'Invalid number of args for {func_name}')
wasm_args: List[Union[float, int]] = []
@ -126,7 +125,7 @@ class Suite:
write_header(sys.stderr, 'Memory (pre alloc)')
runner.interpreter_dump_memory(sys.stderr)
for arg, arg_typ in zip(args, param_types, strict=True):
for arg, arg_typ in zip(args, func_args, 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):
@ -288,8 +287,7 @@ def _load_memory_stored_returned_value(
func_name: str,
wasm_value: Any,
) -> Any:
param_types = type3types.expect_function_type(runner.phasm_ast.functions[func_name].type3)
ret_type3 = param_types[-1]
ret_type3 = runner.phasm_ast.functions[func_name].returns_type3
if ret_type3.name in runner.phasm_ast.build.type_info_map:
type_info = runner.phasm_ast.build.type_info_map[ret_type3.name]