From 25b5d6fc06e15e351031e6ff260b5f955b7973ea Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sat, 24 Dec 2022 19:40:07 +0100 Subject: [PATCH] More work on tuples --- phasm/compiler.py | 39 ++++++++++++++---- phasm/type3/constraints.py | 15 +++++++ tests/integration/test_lang/test_tuple.py | 50 +++++++++++++++++++---- 3 files changed, 88 insertions(+), 16 deletions(-) diff --git a/phasm/compiler.py b/phasm/compiler.py index 58e1e9c..b686dac 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -203,14 +203,13 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: wgn.i32.const(inp.variable.data_block.address) return - # TODO: Broken after new type system - # if isinstance(inp.type, typing.TypeTuple): - # assert isinstance(inp.definition.constant, ourlang.ConstantTuple) - # assert inp.definition.data_block is not None, 'Combined values are memory stored' - # assert inp.definition.data_block.address is not None, 'Value not allocated' - # wgn.i32.const(inp.definition.data_block.address) - # return - # + if inp.type3.base == type3types.tuple: + assert inp.variable.data_block is not None, 'Tuples must be memory stored' + assert inp.variable.data_block.address is not None, 'Value not allocated' + wgn.i32.const(inp.variable.data_block.address) + return + + raise NotImplementedError(expression, inp.variable, inp.type3.base) assert inp.variable.data_block is None, 'Primitives are not memory stored' @@ -338,6 +337,30 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: wgn.add_statement(f'{mtyp}.load') return + if inp.varref.type3.base == type3types.tuple: + assert isinstance(inp.index, ourlang.ConstantPrimitive) + assert isinstance(inp.index.value, int) + + offset = 0 + for el_type in inp.varref.type3.args[0:inp.index.value]: + assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR + offset += _calculate_alloc_size(el_type) + + # This doubles as the out of bounds check + el_type = inp.varref.type3.args[inp.index.value] + assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR + + expression(wgn, inp.varref) + + mtyp = LOAD_STORE_TYPE_MAP.get(el_type.name) + if mtyp is None: + # In the future might extend this by having structs or tuples + # as members of struct or tuples + raise NotImplementedError(expression, inp, el_type) + + wgn.add_statement(f'{mtyp}.load', f'offset={offset}') + return + raise NotImplementedError(expression, inp, inp.varref.type3) if isinstance(inp, ourlang.AccessStructMember): diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index 06e0dfc..34a0158 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -379,6 +379,21 @@ class CanBeSubscriptedConstraint(ConstraintBase): SameTypeConstraint(self.type3.args[0], self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'), ] + if self.type3.base == types.tuple: + if not isinstance(self.index, ourlang.ConstantPrimitive): + return Error('Must index with literal') + + if not isinstance(self.index.value, int): + return Error('Must index with integer literal') + + if self.index.value < 0 or len(self.type3.args) <= self.index.value: + return Error('Tuple index out of range') + + return [ + SameTypeConstraint(types.u32, self.index_type3, comment=f'Tuple subscript index {self.index.value}'), + SameTypeConstraint(self.type3.args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'), + ] + # FIXME: bytes if self.type3.name in types.LOOKUP_TABLE: diff --git a/tests/integration/test_lang/test_tuple.py b/tests/integration/test_lang/test_tuple.py index 1e29da8..4ed482f 100644 --- a/tests/integration/test_lang/test_tuple.py +++ b/tests/integration/test_lang/test_tuple.py @@ -2,7 +2,7 @@ import pytest from phasm.type3.entry import Type3Exception -from ..constants import COMPLETE_NUMERIC_TYPES, TYPE_MAP +from ..constants import ALL_FLOAT_TYPES, COMPLETE_NUMERIC_TYPES, TYPE_MAP from ..helpers import Suite @pytest.mark.integration_test @@ -48,9 +48,42 @@ def testEntry() -> u32: assert 3333 == result.returned_value +@pytest.mark.integration_test +def test_function_call_element_ok(): + code_py = """ +CONSTANT: (u8, u32, u64, ) = (250, 250000, 250000000, ) + +@exported +def testEntry() -> u64: + return helper(CONSTANT[2]) + +def helper(x: u64) -> u64: + return x +""" + + result = Suite(code_py).run_code() + + assert 250000000 == result.returned_value + +@pytest.mark.integration_test +def test_function_call_element_type_mismatch(): + code_py = """ +CONSTANT: (u8, u32, u64, ) = (250, 250000, 250000000, ) + +@exported +def testEntry() -> u8: + return helper(CONSTANT[2]) + +def helper(x: u8) -> u8: + return x +""" + + with pytest.raises(Type3Exception, match=r'u8 must be u64 instead'): + Suite(code_py).run_code() + @pytest.mark.integration_test @pytest.mark.parametrize('type_', COMPLETE_NUMERIC_TYPES) -def test_tuple_simple_constructor(type_): +def test_tuple_simple_constructor_int(type_): code_py = f""" @exported def testEntry() -> {type_}: @@ -66,13 +99,14 @@ def helper(vector: ({type_}, {type_}, {type_}, )) -> {type_}: assert TYPE_MAP[type_] == type(result.returned_value) @pytest.mark.integration_test -def test_tuple_float(): - code_py = """ +@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES) +def test_tuple_simple_constructor_float(type_): + code_py = f""" @exported -def testEntry() -> f32: +def testEntry() -> {type_}: return helper((1.0, 2.0, 3.0, )) -def helper(v: (f32, f32, f32, )) -> f32: +def helper(v: ({type_}, {type_}, {type_}, )) -> {type_}: return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]) """ @@ -139,7 +173,7 @@ def testEntry(x: (u8, u32, u64)) -> u64: return x[CONSTANT] """ - with pytest.raises(Type3Exception, match='Tuples must be indexed with literals'): + with pytest.raises(Type3Exception, match='Must index with literal'): Suite(code_py).run_code() @pytest.mark.integration_test @@ -150,5 +184,5 @@ def testEntry(x: (u8, u32, u64)) -> u64: return x[0.0] """ - with pytest.raises(Type3Exception, match='Must be integer'): + with pytest.raises(Type3Exception, match='Must index with integer literal'): Suite(code_py).run_code()