Compare commits
1 Commits
b285eb9d05
...
bfb3d2b3a0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfb3d2b3a0 |
@ -17,7 +17,6 @@ from .type3.types import (
|
|||||||
TypeApplication_Struct,
|
TypeApplication_Struct,
|
||||||
TypeApplication_TypeInt,
|
TypeApplication_TypeInt,
|
||||||
TypeApplication_TypeStar,
|
TypeApplication_TypeStar,
|
||||||
TypeConstructor_Function,
|
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
TypeConstructor_Tuple,
|
TypeConstructor_Tuple,
|
||||||
)
|
)
|
||||||
@ -356,21 +355,6 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
raise NotImplementedError(str(inp.function), type_var_map)
|
raise NotImplementedError(str(inp.function), type_var_map)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp.function, ourlang.FunctionParam):
|
|
||||||
assert isinstance(inp.function.type3.application.constructor, TypeConstructor_Function)
|
|
||||||
|
|
||||||
params = [
|
|
||||||
type3(x).to_wat()
|
|
||||||
for x in inp.function.type3.application.arguments
|
|
||||||
]
|
|
||||||
|
|
||||||
result = params.pop()
|
|
||||||
|
|
||||||
params_str = ' '.join(params)
|
|
||||||
wgn.add_statement('local.get', '${}'.format(inp.function.name))
|
|
||||||
wgn.add_statement(f'call_indirect (param {params_str}) (result {result})')
|
|
||||||
return
|
|
||||||
|
|
||||||
wgn.add_statement('call', '${}'.format(inp.function.name))
|
wgn.add_statement('call', '${}'.format(inp.function.name))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@ -151,10 +151,10 @@ class FunctionCall(Expression):
|
|||||||
"""
|
"""
|
||||||
__slots__ = ('function', 'arguments', )
|
__slots__ = ('function', 'arguments', )
|
||||||
|
|
||||||
function: Union['Function', 'FunctionParam', Type3ClassMethod]
|
function: Union['Function', Type3ClassMethod]
|
||||||
arguments: List[Expression]
|
arguments: List[Expression]
|
||||||
|
|
||||||
def __init__(self, function: Union['Function', 'FunctionParam', Type3ClassMethod]) -> None:
|
def __init__(self, function: Union['Function', Type3ClassMethod]) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.function = function
|
self.function = function
|
||||||
@ -378,15 +378,15 @@ class Module:
|
|||||||
"""
|
"""
|
||||||
A module is a file and consists of functions
|
A module is a file and consists of functions
|
||||||
"""
|
"""
|
||||||
__slots__ = ('data', 'types', 'struct_definitions', 'constant_defs', 'functions', 'operators', 'functions_table', )
|
__slots__ = ('data', 'types', 'struct_definitions', 'constant_defs', 'functions', 'functions_table', 'operators', )
|
||||||
|
|
||||||
data: ModuleData
|
data: ModuleData
|
||||||
types: dict[str, Type3]
|
types: dict[str, Type3]
|
||||||
struct_definitions: Dict[str, StructDefinition]
|
struct_definitions: Dict[str, StructDefinition]
|
||||||
constant_defs: Dict[str, ModuleConstantDef]
|
constant_defs: Dict[str, ModuleConstantDef]
|
||||||
functions: Dict[str, Function]
|
functions: Dict[str, Function]
|
||||||
operators: Dict[str, Type3ClassMethod]
|
|
||||||
functions_table: dict[Function, int]
|
functions_table: dict[Function, int]
|
||||||
|
operators: Dict[str, Type3ClassMethod]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.data = ModuleData()
|
self.data = ModuleData()
|
||||||
|
|||||||
@ -447,8 +447,7 @@ class OurVisitor:
|
|||||||
return VariableReference(cdef)
|
return VariableReference(cdef)
|
||||||
|
|
||||||
if node.id in module.functions:
|
if node.id in module.functions:
|
||||||
fun = module.functions[node.id]
|
return FunctionReference(module.functions[node.id])
|
||||||
return FunctionReference(fun)
|
|
||||||
|
|
||||||
_raise_static_error(node, f'Undefined variable {node.id}')
|
_raise_static_error(node, f'Undefined variable {node.id}')
|
||||||
|
|
||||||
@ -475,18 +474,21 @@ class OurVisitor:
|
|||||||
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 PRELUDE_METHODS:
|
if node.func.id in PRELUDE_METHODS:
|
||||||
func = PRELUDE_METHODS[node.func.id]
|
func = PRELUDE_METHODS[node.func.id]
|
||||||
elif node.func.id in our_locals:
|
|
||||||
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')
|
||||||
|
|
||||||
func = module.functions[node.func.id]
|
func = module.functions[node.func.id]
|
||||||
|
|
||||||
|
exp_arg_count = len(func.signature.args) - 1
|
||||||
|
|
||||||
|
if exp_arg_count != len(node.args):
|
||||||
|
_raise_static_error(node, f'Function {node.func.id} requires {exp_arg_count} arguments but {len(node.args)} are given')
|
||||||
|
|
||||||
result = FunctionCall(func)
|
result = FunctionCall(func)
|
||||||
result.arguments.extend(
|
result.arguments.extend(
|
||||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_expr)
|
self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_expr)
|
||||||
@ -614,22 +616,6 @@ class OurVisitor:
|
|||||||
_raise_static_error(node, f'Unrecognized type {node.id}')
|
_raise_static_error(node, f'Unrecognized type {node.id}')
|
||||||
|
|
||||||
if isinstance(node, ast.Subscript):
|
if isinstance(node, ast.Subscript):
|
||||||
if isinstance(node.value, ast.Name) and node.value.id == 'Callable':
|
|
||||||
func_arg_types: list[ast.expr]
|
|
||||||
|
|
||||||
if isinstance(node.slice, ast.Name):
|
|
||||||
func_arg_types = [node.slice]
|
|
||||||
elif isinstance(node.slice, ast.Tuple):
|
|
||||||
func_arg_types = node.slice.elts
|
|
||||||
else:
|
|
||||||
_raise_static_error(node, 'Must subscript using a list of types')
|
|
||||||
|
|
||||||
# Function type
|
|
||||||
return prelude.function(*[
|
|
||||||
self.visit_type(module, e)
|
|
||||||
for e in func_arg_types
|
|
||||||
])
|
|
||||||
|
|
||||||
if isinstance(node.slice, ast.Slice):
|
if isinstance(node.slice, ast.Slice):
|
||||||
_raise_static_error(node, 'Must subscript using an index')
|
_raise_static_error(node, 'Must subscript using an index')
|
||||||
if not isinstance(node.slice, ast.Constant):
|
if not isinstance(node.slice, ast.Constant):
|
||||||
|
|||||||
@ -188,9 +188,7 @@ determined length, and each argument can be different.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def fn_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
def fn_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
||||||
# Not really a pointer; but still a i32
|
pass # ? instance_type_class(InternalPassAsPointer, typ)
|
||||||
# (It's actually a table lookup)
|
|
||||||
instance_type_class(InternalPassAsPointer, typ)
|
|
||||||
|
|
||||||
function = TypeConstructor_Function('function', on_create=fn_on_create)
|
function = TypeConstructor_Function('function', on_create=fn_on_create)
|
||||||
"""
|
"""
|
||||||
@ -475,7 +473,7 @@ instance_type_class(IntNum, f64, methods={
|
|||||||
'neg': stdtypes.f64_intnum_neg,
|
'neg': stdtypes.f64_intnum_neg,
|
||||||
})
|
})
|
||||||
|
|
||||||
Integral = Type3Class('Integral', (a, ), methods={
|
Integral = Type3Class('Eq', (a, ), methods={
|
||||||
}, operators={
|
}, operators={
|
||||||
'//': [a, a, a],
|
'//': [a, a, a],
|
||||||
'%': [a, a, a],
|
'%': [a, a, a],
|
||||||
|
|||||||
@ -159,7 +159,7 @@ class SameTypeConstraint(ConstraintBase):
|
|||||||
return (
|
return (
|
||||||
' == '.join('{t' + str(idx) + '}' for idx in range(len(self.type_list))),
|
' == '.join('{t' + str(idx) + '}' for idx in range(len(self.type_list))),
|
||||||
{
|
{
|
||||||
't' + str(idx): typ
|
't' + str(idx): typ.name if isinstance(typ, ourlang.Function) else typ
|
||||||
for idx, typ in enumerate(self.type_list)
|
for idx, typ in enumerate(self.type_list)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -257,6 +257,9 @@ class SameFunctionArgumentConstraint(ConstraintBase):
|
|||||||
if isinstance(tv, Type3) or tv in type_var_map
|
if isinstance(tv, Type3) or tv in type_var_map
|
||||||
]
|
]
|
||||||
|
|
||||||
|
print('self.func_arg.args', self.func_arg.args)
|
||||||
|
print('exp_type_arg_list', exp_type_arg_list)
|
||||||
|
|
||||||
if len(exp_type_arg_list) != len(self.func_arg.args):
|
if len(exp_type_arg_list) != len(self.func_arg.args):
|
||||||
return RequireTypeSubstitutes()
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
|
|||||||
@ -23,10 +23,9 @@ from .functions import (
|
|||||||
FunctionSignature,
|
FunctionSignature,
|
||||||
TypeVariable,
|
TypeVariable,
|
||||||
TypeVariableApplication_Unary,
|
TypeVariableApplication_Unary,
|
||||||
TypeVariableContext,
|
|
||||||
)
|
)
|
||||||
from .placeholders import PlaceholderForType
|
from .placeholders import PlaceholderForType
|
||||||
from .types import Type3, TypeApplication_Struct, TypeConstructor_Function
|
from .types import Type3, TypeApplication_Struct
|
||||||
|
|
||||||
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
||||||
|
|
||||||
@ -57,31 +56,15 @@ def expression_binary_op(ctx: Context, inp: ourlang.BinaryOp, phft: PlaceholderF
|
|||||||
)
|
)
|
||||||
|
|
||||||
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.signature,
|
||||||
inp.arguments,
|
inp.arguments,
|
||||||
inp,
|
inp,
|
||||||
phft,
|
phft,
|
||||||
)
|
)
|
||||||
|
|
||||||
def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference, phft: PlaceholderForType) -> ConstraintGenerator:
|
|
||||||
yield SameTypeConstraint(
|
|
||||||
prelude.function(*(x.type3 for x in inp.function.posonlyargs), inp.function.returns_type3),
|
|
||||||
phft,
|
|
||||||
comment=f'typeOf("{inp.function.name}") == typeOf({inp.function.name})',
|
|
||||||
)
|
|
||||||
|
|
||||||
def _expression_function_call(
|
def _expression_function_call(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
func_name: str,
|
func_name: str,
|
||||||
@ -206,6 +189,13 @@ def _expression_function_call(
|
|||||||
raise NotImplementedError(sig_part)
|
raise NotImplementedError(sig_part)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||||
|
yield SameTypeConstraint(
|
||||||
|
prelude.function(*(x.type3 for x in inp.function.posonlyargs), inp.function.returns_type3),
|
||||||
|
phft,
|
||||||
|
comment=f'typeOf("{inp.function.name}") == typeOf({inp.function.name})',
|
||||||
|
)
|
||||||
|
|
||||||
def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType) -> ConstraintGenerator:
|
def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||||
if isinstance(inp, ourlang.Constant):
|
if isinstance(inp, ourlang.Constant):
|
||||||
yield from constant(ctx, inp, phft)
|
yield from constant(ctx, inp, phft)
|
||||||
|
|||||||
@ -241,7 +241,7 @@ class TypeConstructor_Tuple(TypeConstructor_TypeStar):
|
|||||||
|
|
||||||
class TypeConstructor_Function(TypeConstructor_TypeStar):
|
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 '(' + ' -> '.join(x.name for x in key) + ')'
|
||||||
|
|
||||||
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -186,8 +186,8 @@ class Module(WatSerializable):
|
|||||||
"""
|
"""
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.imports: List[Import] = []
|
self.imports: List[Import] = []
|
||||||
self.table: dict[int, str] = {}
|
|
||||||
self.functions: List[Function] = []
|
self.functions: List[Function] = []
|
||||||
|
self.table: dict[int, str] = {}
|
||||||
self.memory = ModuleMemory()
|
self.memory = ModuleMemory()
|
||||||
|
|
||||||
def to_wat(self) -> str:
|
def to_wat(self) -> str:
|
||||||
|
|||||||
@ -1,182 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from phasm.type3.entry import Type3Exception
|
|
||||||
|
|
||||||
from ..helpers import Suite
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_in_code_0_arg():
|
|
||||||
code_py = """
|
|
||||||
def thirteen() -> i32:
|
|
||||||
return 13
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32]) -> i32:
|
|
||||||
return applicable()
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(thirteen)
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert 13 == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_in_code_1_arg():
|
|
||||||
code_py = """
|
|
||||||
def double(left: i32) -> i32:
|
|
||||||
return left * 2
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32, i32], left: i32) -> i32:
|
|
||||||
return applicable(left)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(double, 13)
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert 26 == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_in_code_2_arg():
|
|
||||||
code_py = """
|
|
||||||
def add(left: i32, right: i32) -> i32:
|
|
||||||
return left + right
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32, i32, i32], left: i32, right: i32) -> i32:
|
|
||||||
return applicable(left, right)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(add, 13, 14)
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert 27 == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_in_code_3_arg():
|
|
||||||
code_py = """
|
|
||||||
def add(left: i32, mid: i32, right: i32) -> i32:
|
|
||||||
return left + mid + right
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32, i32, i32, i32], left: i32, mid: i32, right: i32) -> i32:
|
|
||||||
return applicable(left, mid, right)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(add, 13, 14, 15)
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert 42 == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_wrong_argument_type():
|
|
||||||
code_py = """
|
|
||||||
def double(left: f32) -> f32:
|
|
||||||
return left * 2
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32, i32], left: i32) -> i32:
|
|
||||||
return applicable(left)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(double, 13)
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'Callable\[f32, f32\] must be Callable\[i32, i32\] instead'):
|
|
||||||
Suite(code_py).run_code()
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_wrong_return():
|
|
||||||
code_py = """
|
|
||||||
def double(left: i32) -> i32:
|
|
||||||
return left * 2
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32, i32], left: i32) -> f32:
|
|
||||||
return applicable(left)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(double, 13)
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'f32 must be i32 instead'):
|
|
||||||
Suite(code_py).run_code()
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
@pytest.mark.skip('FIXME: Probably have the remainder be the a function type')
|
|
||||||
def test_sof_wrong_not_enough_args_call():
|
|
||||||
code_py = """
|
|
||||||
def add(left: i32, right: i32) -> i32:
|
|
||||||
return left + right
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32, i32, i32], left: i32) -> i32:
|
|
||||||
return applicable(left)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(add, 13)
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'f32 must be i32 instead'):
|
|
||||||
Suite(code_py).run_code()
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_wrong_not_enough_args_refere():
|
|
||||||
code_py = """
|
|
||||||
def double(left: i32) -> i32:
|
|
||||||
return left * 2
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32, i32, i32], left: i32, right: i32) -> i32:
|
|
||||||
return applicable(left, right)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(double, 13, 14)
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'Callable\[i32, i32\] must be Callable\[i32, i32, i32\] instead'):
|
|
||||||
Suite(code_py).run_code()
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
@pytest.mark.skip('FIXME: Probably have the remainder be the a function type')
|
|
||||||
def test_sof_wrong_too_many_args_call():
|
|
||||||
code_py = """
|
|
||||||
def thirteen() -> i32:
|
|
||||||
return 13
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32], left: i32) -> i32:
|
|
||||||
return applicable(left)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(thirteen, 13)
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'f32 must be i32 instead'):
|
|
||||||
Suite(code_py).run_code()
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_sof_wrong_too_many_args_refere():
|
|
||||||
code_py = """
|
|
||||||
def double(left: i32) -> i32:
|
|
||||||
return left * 2
|
|
||||||
|
|
||||||
def action(applicable: Callable[i32]) -> i32:
|
|
||||||
return applicable()
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return action(double)
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'Callable\[i32, i32\] must be Callable\[i32\] instead'):
|
|
||||||
Suite(code_py).run_code()
|
|
||||||
Loading…
x
Reference in New Issue
Block a user