From e589223dbb0a4c9bc743bfa6a0122ccc8835aa59 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Thu, 18 Aug 2022 20:53:21 +0200 Subject: [PATCH] Static Arrays. CRC32 compiles \o/ Doesn't give right answer yet and out of bound check fails. No constructor yet for static arrays, but module constants work. Which don't work yet for tuples and structs. Also, u32 for indexing please. Also, more module constant types. --- examples/buffer.py | 2 +- examples/crc32.html | 47 ++++++ examples/crc32.py | 4 +- examples/index.html | 1 + phasm/codestyle.py | 10 +- phasm/compiler.py | 73 +++++++++- phasm/ourlang.py | 32 +++- phasm/parser.py | 170 +++++++++++++++++----- phasm/typing.py | 24 +++ phasm/wasmgenerator.py | 2 + pylintrc | 5 + tests/integration/test_constants.py | 35 +++++ tests/integration/test_runtime_checks.py | 16 ++ tests/integration/test_simple.py | 40 ++++- tests/integration/test_static_checking.py | 33 ++++- 15 files changed, 448 insertions(+), 46 deletions(-) create mode 100644 examples/crc32.html diff --git a/examples/buffer.py b/examples/buffer.py index 51096c6..6f251ff 100644 --- a/examples/buffer.py +++ b/examples/buffer.py @@ -1,5 +1,5 @@ @exported -def index(inp: bytes, idx: i32) -> i32: +def index(inp: bytes, idx: u32) -> u8: return inp[idx] @exported diff --git a/examples/crc32.html b/examples/crc32.html new file mode 100644 index 0000000..7bd228a --- /dev/null +++ b/examples/crc32.html @@ -0,0 +1,47 @@ + + + +Examples - CRC32 + + +

Buffer

+ +List - Source - WebAssembly + +
+ + + + + + diff --git a/examples/crc32.py b/examples/crc32.py index 781bf21..d531056 100644 --- a/examples/crc32.py +++ b/examples/crc32.py @@ -14,7 +14,7 @@ # } # Might be the wrong table -_CRC32_Table: u32[256] = [ +_CRC32_Table: u32[256] = ( 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, @@ -79,7 +79,7 @@ _CRC32_Table: u32[256] = [ 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, -] +) def _crc32_f(crc: u32, byt: u8) -> u32: return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ u32(byt)] diff --git a/examples/index.html b/examples/index.html index f01ade3..96afa50 100644 --- a/examples/index.html +++ b/examples/index.html @@ -6,6 +6,7 @@

Examples

Standard

Technical

diff --git a/phasm/codestyle.py b/phasm/codestyle.py index a09b524..4b38e32 100644 --- a/phasm/codestyle.py +++ b/phasm/codestyle.py @@ -58,6 +58,9 @@ def type_(inp: typing.TypeBase) -> str: return f'({mems}, )' + if isinstance(inp, typing.TypeStaticArray): + return f'{type_(inp.member_type)}[{len(inp.members)}]' + if isinstance(inp, typing.TypeStruct): return inp.name @@ -94,7 +97,7 @@ def expression(inp: ourlang.Expression) -> str: # could not fit in the given float type return str(inp.value) - if isinstance(inp, ourlang.ConstantTuple): + if isinstance(inp, (ourlang.ConstantTuple, ourlang.ConstantStaticArray, )): return '(' + ', '.join( expression(x) for x in inp.value @@ -137,7 +140,10 @@ def expression(inp: ourlang.Expression) -> str: if isinstance(inp, ourlang.AccessStructMember): return f'{expression(inp.varref)}.{inp.member.name}' - if isinstance(inp, ourlang.AccessTupleMember): + if isinstance(inp, (ourlang.AccessTupleMember, ourlang.AccessStaticArrayMember, )): + if isinstance(inp.member, ourlang.Expression): + return f'{expression(inp.varref)}[{expression(inp.member)}]' + return f'{expression(inp.varref)}[{inp.member.idx}]' if isinstance(inp, ourlang.Fold): diff --git a/phasm/compiler.py b/phasm/compiler.py index 1f14aae..3b62065 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -62,7 +62,7 @@ def type_(inp: typing.TypeBase) -> wasm.WasmType: if isinstance(inp, typing.TypeFloat64): return wasm.WasmTypeFloat64() - if isinstance(inp, (typing.TypeStruct, typing.TypeTuple, typing.TypeBytes)): + if isinstance(inp, (typing.TypeStruct, typing.TypeTuple, typing.TypeStaticArray, typing.TypeBytes)): # Structs and tuples are passed as pointer # And pointers are i32 return wasm.WasmTypeInt32() @@ -273,6 +273,26 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) return + if isinstance(inp, ourlang.AccessStaticArrayMember): + mtyp = LOAD_STORE_TYPE_MAP.get(inp.static_array.member_type.__class__) + if mtyp is None: + # In the future might extend this by having structs or tuples + # as members of static arrays + raise NotImplementedError(expression, inp, inp.member) + + if isinstance(inp.member, typing.TypeStaticArrayMember): + expression(wgn, inp.varref) + wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) + return + + expression(wgn, inp.varref) + expression(wgn, inp.member) + wgn.i32.const(inp.static_array.member_type.alloc_size()) + wgn.i32.mul() + wgn.i32.add() + wgn.add_statement(f'{mtyp}.load') + return + if isinstance(inp, ourlang.Fold): expression_fold(wgn, inp) return @@ -285,6 +305,13 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: wgn.i32.const(inp.definition.data_block.address) return + if isinstance(inp.type, typing.TypeStaticArray): + assert isinstance(inp.definition.constant, ourlang.ConstantStaticArray) + 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 + assert inp.definition.data_block is None, 'Primitives are not memory stored' mtyp = LOAD_STORE_TYPE_MAP.get(inp.type.__class__) @@ -471,7 +498,7 @@ def module_data_u32(inp: int) -> bytes: """ Compile: module data, u32 value """ - return struct.pack(' bytes: """ @@ -479,6 +506,30 @@ def module_data_u64(inp: int) -> bytes: """ return struct.pack(' bytes: + """ + Compile: module data, i32 value + """ + return struct.pack(' bytes: + """ + Compile: module data, i64 value + """ + return struct.pack(' bytes: + """ + Compile: module data, f32 value + """ + return struct.pack(' bytes: + """ + Compile: module data, f64 value + """ + return struct.pack(' bytes: """ Compile: module data @@ -505,6 +556,22 @@ def module_data(inp: ourlang.ModuleData) -> bytes: data_list.append(module_data_u64(constant.value)) continue + if isinstance(constant, ourlang.ConstantInt32): + data_list.append(module_data_i32(constant.value)) + continue + + if isinstance(constant, ourlang.ConstantInt64): + data_list.append(module_data_i64(constant.value)) + continue + + if isinstance(constant, ourlang.ConstantFloat32): + data_list.append(module_data_f32(constant.value)) + continue + + if isinstance(constant, ourlang.ConstantFloat64): + data_list.append(module_data_f64(constant.value)) + continue + raise NotImplementedError(constant) block_data = b''.join(data_list) @@ -579,7 +646,7 @@ def _generate_tuple_constructor(wgn: WasmGenerator, inp: ourlang.TupleConstructo wgn.local.get(tmp_var) def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None: - tmp_var = wgn.temp_var_i32('tuple_adr') + tmp_var = wgn.temp_var_i32('struct_adr') # Allocated the required amounts of bytes in memory wgn.i32.const(inp.struct.alloc_size()) diff --git a/phasm/ourlang.py b/phasm/ourlang.py index d7cfffe..5f19d2e 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -1,7 +1,7 @@ """ Contains the syntax tree for ourlang """ -from typing import Dict, List, Tuple, Optional +from typing import Dict, List, Tuple, Optional, Union import enum @@ -19,6 +19,7 @@ from .typing import ( TypeFloat32, TypeFloat64, TypeBytes, TypeTuple, TypeTupleMember, + TypeStaticArray, TypeStaticArrayMember, TypeStruct, TypeStructMember, ) @@ -135,6 +136,18 @@ class ConstantTuple(Constant): super().__init__(type_) self.value = value +class ConstantStaticArray(Constant): + """ + A StaticArray constant value expression within a statement + """ + __slots__ = ('value', ) + + value: List[Constant] + + def __init__(self, type_: TypeStaticArray, value: List[Constant]) -> None: + super().__init__(type_) + self.value = value + class VariableReference(Expression): """ An variable reference expression within a statement @@ -239,6 +252,23 @@ class AccessTupleMember(Expression): self.varref = varref self.member = member +class AccessStaticArrayMember(Expression): + """ + Access a tuple member for reading of writing + """ + __slots__ = ('varref', 'static_array', 'member', ) + + varref: Union['ModuleConstantReference', VariableReference] + static_array: TypeStaticArray + member: Union[Expression, TypeStaticArrayMember] + + def __init__(self, varref: Union['ModuleConstantReference', VariableReference], static_array: TypeStaticArray, member: Union[TypeStaticArrayMember, Expression], ) -> None: + super().__init__(static_array.member_type) + + self.varref = varref + self.static_array = static_array + self.member = member + class Fold(Expression): """ A (left or right) fold diff --git a/phasm/parser.py b/phasm/parser.py index a9f2f63..d95bfce 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -19,6 +19,8 @@ from .typing import ( TypeStructMember, TypeTuple, TypeTupleMember, + TypeStaticArray, + TypeStaticArrayMember, ) from . import codestyle @@ -30,12 +32,12 @@ from .ourlang import ( Function, Expression, - AccessBytesIndex, AccessStructMember, AccessTupleMember, + AccessBytesIndex, AccessStructMember, AccessTupleMember, AccessStaticArrayMember, BinaryOp, Constant, ConstantFloat32, ConstantFloat64, ConstantInt32, ConstantInt64, ConstantUInt8, ConstantUInt32, ConstantUInt64, - ConstantTuple, + ConstantTuple, ConstantStaticArray, FunctionCall, StructConstructor, TupleConstructor, @@ -249,6 +251,34 @@ class OurVisitor: data_block, ) + if isinstance(exp_type, TypeStaticArray): + if not isinstance(node.value, ast.Tuple): + _raise_static_error(node, 'Must be static array') + + if len(exp_type.members) != len(node.value.elts): + _raise_static_error(node, 'Invalid number of static array values') + + static_array_data = [ + self.visit_Module_Constant(module, exp_type.member_type, arg_node) + for arg_node in node.value.elts + if isinstance(arg_node, ast.Constant) + ] + if len(exp_type.members) != len(static_array_data): + _raise_static_error(node, 'Static array arguments must be constants') + + # Allocate the data + data_block = ModuleDataBlock(static_array_data) + module.data.blocks.append(data_block) + + # Then return the constant as a pointer + return ModuleConstantDef( + node.target.id, + node.lineno, + exp_type, + ConstantStaticArray(exp_type, static_array_data), + data_block, + ) + raise NotImplementedError(f'{node} on Module AnnAssign') def visit_Module_stmt(self, module: Module, node: ast.stmt) -> None: @@ -416,22 +446,22 @@ class OurVisitor: if not isinstance(node.ctx, ast.Load): _raise_static_error(node, 'Must be load context') - if not isinstance(exp_type, TypeTuple): - _raise_static_error(node, f'Expression is expecting a {codestyle.type_(exp_type)}, not a tuple') + if isinstance(exp_type, TypeTuple): + if len(exp_type.members) != len(node.elts): + _raise_static_error(node, f'Expression is expecting a tuple of size {len(exp_type.members)}, but {len(node.elts)} are given') - if len(exp_type.members) != len(node.elts): - _raise_static_error(node, f'Expression is expecting a tuple of size {len(exp_type.members)}, but {len(node.elts)} are given') + tuple_constructor = TupleConstructor(exp_type) - tuple_constructor = TupleConstructor(exp_type) + func = module.functions[tuple_constructor.name] - func = module.functions[tuple_constructor.name] + result = FunctionCall(func) + result.arguments = [ + self.visit_Module_FunctionDef_expr(module, function, our_locals, mem.type, arg_node) + for arg_node, mem in zip(node.elts, exp_type.members) + ] + return result - result = FunctionCall(func) - result.arguments = [ - self.visit_Module_FunctionDef_expr(module, function, our_locals, mem.type, arg_node) - for arg_node, mem in zip(node.elts, exp_type.members) - ] - return result + _raise_static_error(node, f'Expression is expecting a {codestyle.type_(exp_type)}, not a tuple') raise NotImplementedError(f'{node} as expr in FunctionDef') @@ -582,24 +612,37 @@ class OurVisitor: if not isinstance(node.ctx, ast.Load): _raise_static_error(node, 'Must be load context') - if not node.value.id in our_locals: + varref: Union[ModuleConstantReference, VariableReference] + if node.value.id in our_locals: + node_typ = our_locals[node.value.id] + varref = VariableReference(node_typ, node.value.id) + elif node.value.id in module.constant_defs: + constant_def = module.constant_defs[node.value.id] + node_typ = constant_def.type + varref = ModuleConstantReference(node_typ, constant_def) + else: _raise_static_error(node, f'Undefined variable {node.value.id}') - node_typ = our_locals[node.value.id] - slice_expr = self.visit_Module_FunctionDef_expr( - module, function, our_locals, module.types['i32'], node.slice.value, + module, function, our_locals, module.types['u32'], node.slice.value, ) if isinstance(node_typ, TypeBytes): + t_u8 = module.types['u8'] + if exp_type != t_u8: + _raise_static_error(node, f'Expected {codestyle.type_(exp_type)}, {node.value.id}[{codestyle.expression(slice_expr)}] is actually {codestyle.type_(t_u8)}') + + if isinstance(varref, ModuleConstantReference): + raise NotImplementedError(f'{node} from module constant') + return AccessBytesIndex( - module.types['u8'], - VariableReference(node_typ, node.value.id), + t_u8, + varref, slice_expr, ) if isinstance(node_typ, TypeTuple): - if not isinstance(slice_expr, ConstantInt32): + if not isinstance(slice_expr, ConstantUInt32): _raise_static_error(node, 'Must subscript using a constant index') idx = slice_expr.value @@ -607,13 +650,40 @@ class OurVisitor: if len(node_typ.members) <= idx: _raise_static_error(node, f'Index {idx} out of bounds for tuple {node.value.id}') - member = node_typ.members[idx] - if exp_type != member.type: - _raise_static_error(node, f'Expected {codestyle.type_(exp_type)}, {node.value.id}[{idx}] is actually {codestyle.type_(member.type)}') + tuple_member = node_typ.members[idx] + if exp_type != tuple_member.type: + _raise_static_error(node, f'Expected {codestyle.type_(exp_type)}, {node.value.id}[{idx}] is actually {codestyle.type_(tuple_member.type)}') + + if isinstance(varref, ModuleConstantReference): + raise NotImplementedError(f'{node} from module constant') return AccessTupleMember( - VariableReference(node_typ, node.value.id), - member, + varref, + tuple_member, + ) + + if isinstance(node_typ, TypeStaticArray): + if exp_type != node_typ.member_type: + _raise_static_error(node, f'Expected {codestyle.type_(exp_type)}, {node.value.id}[{idx}] is actually {codestyle.type_(node_typ.member_type)}') + + if not isinstance(slice_expr, ConstantInt32): + return AccessStaticArrayMember( + varref, + node_typ, + slice_expr, + ) + + idx = slice_expr.value + + if len(node_typ.members) <= idx: + _raise_static_error(node, f'Index {idx} out of bounds for static array {node.value.id}') + + static_array_member = node_typ.members[idx] + + return AccessStaticArrayMember( + varref, + node_typ, + static_array_member, ) _raise_static_error(node, f'Cannot take index of {node_typ} {node.value.id}') @@ -705,25 +775,59 @@ class OurVisitor: _raise_static_error(node, f'Unrecognized type {node.id}') + if isinstance(node, ast.Subscript): + if not isinstance(node.value, ast.Name): + _raise_static_error(node, 'Must be name') + if not isinstance(node.slice, ast.Index): + _raise_static_error(node, 'Must subscript using an index') + if not isinstance(node.slice.value, ast.Constant): + _raise_static_error(node, 'Must subscript using a constant index') + if not isinstance(node.slice.value.value, int): + _raise_static_error(node, 'Must subscript using a constant integer index') + if not isinstance(node.ctx, ast.Load): + _raise_static_error(node, 'Must be load context') + + if node.value.id in module.types: + member_type = module.types[node.value.id] + else: + _raise_static_error(node, f'Unrecognized type {node.value.id}') + + type_static_array = TypeStaticArray(member_type) + + offset = 0 + + for idx in range(node.slice.value.value): + static_array_member = TypeStaticArrayMember(idx, offset) + + type_static_array.members.append(static_array_member) + offset += member_type.alloc_size() + + key = f'{node.value.id}[{node.slice.value.value}]' + + if key not in module.types: + module.types[key] = type_static_array + + return module.types[key] + if isinstance(node, ast.Tuple): if not isinstance(node.ctx, ast.Load): _raise_static_error(node, 'Must be load context') - result = TypeTuple() + type_tuple = TypeTuple() offset = 0 for idx, elt in enumerate(node.elts): - member = TypeTupleMember(idx, self.visit_type(module, elt), offset) + tuple_member = TypeTupleMember(idx, self.visit_type(module, elt), offset) - result.members.append(member) - offset += member.type.alloc_size() + type_tuple.members.append(tuple_member) + offset += tuple_member.type.alloc_size() - key = result.render_internal_name() + key = type_tuple.render_internal_name() if key not in module.types: - module.types[key] = result - constructor = TupleConstructor(result) + module.types[key] = type_tuple + constructor = TupleConstructor(type_tuple) module.functions[constructor.name] = constructor return module.types[key] diff --git a/phasm/typing.py b/phasm/typing.py index 0a29f5f..e56f7a9 100644 --- a/phasm/typing.py +++ b/phasm/typing.py @@ -137,6 +137,30 @@ class TypeTuple(TypeBase): for x in self.members ) +class TypeStaticArrayMember: + """ + Represents a static array member + """ + def __init__(self, idx: int, offset: int) -> None: + self.idx = idx + self.offset = offset + +class TypeStaticArray(TypeBase): + """ + The static array type + """ + __slots__ = ('member_type', 'members', ) + + member_type: TypeBase + members: List[TypeStaticArrayMember] + + def __init__(self, member_type: TypeBase) -> None: + self.member_type = member_type + self.members = [] + + def alloc_size(self) -> int: + return self.member_type.alloc_size() * len(self.members) + class TypeStructMember: """ Represents a struct member diff --git a/phasm/wasmgenerator.py b/phasm/wasmgenerator.py index 57b1fe5..48cca1a 100644 --- a/phasm/wasmgenerator.py +++ b/phasm/wasmgenerator.py @@ -30,6 +30,8 @@ class Generator_i32i64: # 2.4.1. Numeric Instructions # ibinop self.add = functools.partial(self.generator.add_statement, f'{prefix}.add') + self.sub = functools.partial(self.generator.add_statement, f'{prefix}.sub') + self.mul = functools.partial(self.generator.add_statement, f'{prefix}.mul') # irelop self.eq = functools.partial(self.generator.add_statement, f'{prefix}.eq') diff --git a/pylintrc b/pylintrc index b38e5c0..0591be3 100644 --- a/pylintrc +++ b/pylintrc @@ -1,5 +1,10 @@ [MASTER] disable=C0122,R0903,R0911,R0912,R0913,R0915,R1710,W0223 +max-line-length=180 + [stdlib] good-names=g + +[tests] +disable=C0116, diff --git a/tests/integration/test_constants.py b/tests/integration/test_constants.py index 36626b1..19f0203 100644 --- a/tests/integration/test_constants.py +++ b/tests/integration/test_constants.py @@ -50,3 +50,38 @@ def helper(vector: (u8, u8, u32, u32, u64, u64, )) -> u32: result = Suite(code_py).run_code() assert 3333 == result.returned_value + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64', ]) +def test_static_array_1(type_): + code_py = f""" +CONSTANT: {type_}[1] = (65, ) + +@exported +def testEntry() -> {type_}: + return helper(CONSTANT) + +def helper(vector: {type_}[1]) -> {type_}: + return vector[0] +""" + + result = Suite(code_py).run_code() + + assert 65 == result.returned_value + +@pytest.mark.integration_test +def test_static_array_6(): + code_py = """ +CONSTANT: u32[6] = (11, 22, 3333, 4444, 555555, 666666, ) + +@exported +def testEntry() -> u32: + return helper(CONSTANT) + +def helper(vector: u32[6]) -> u32: + return vector[2] +""" + + result = Suite(code_py).run_code() + + assert 3333 == result.returned_value diff --git a/tests/integration/test_runtime_checks.py b/tests/integration/test_runtime_checks.py index 6a70032..97d6542 100644 --- a/tests/integration/test_runtime_checks.py +++ b/tests/integration/test_runtime_checks.py @@ -13,3 +13,19 @@ def testEntry(f: bytes) -> u8: result = Suite(code_py).run_code(b'Short', b'Long' * 100) assert 0 == result.returned_value + +@pytest.mark.integration_test +def test_static_array_index_out_of_bounds(): + code_py = """ +CONSTANT0: u32[3] = (24, 57, 80, ) + +CONSTANT1: u32[16] = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ) + +@exported +def testEntry() -> u32: + return CONSTANT0[16] +""" + + result = Suite(code_py).run_code() + + assert 0 == result.returned_value diff --git a/tests/integration/test_simple.py b/tests/integration/test_simple.py index b6bfbc5..f0c2993 100644 --- a/tests/integration/test_simple.py +++ b/tests/integration/test_simple.py @@ -427,7 +427,7 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32: @pytest.mark.integration_test @pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES) -def test_tuple_simple(type_): +def test_tuple_simple_constructor(type_): code_py = f""" @exported def testEntry() -> {type_}: @@ -457,6 +457,44 @@ def helper(v: (f32, f32, f32, )) -> f32: assert 3.74 < result.returned_value < 3.75 +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES) +def test_static_array_module_constant(type_): + code_py = f""" +CONSTANT: {type_}[3] = (24, 57, 80, ) + +@exported +def testEntry() -> {type_}: + return helper(CONSTANT) + +def helper(array: {type_}[3]) -> {type_}: + return array[0] + array[1] + array[2] +""" + + result = Suite(code_py).run_code() + + assert 161 == result.returned_value + assert TYPE_MAP[type_] == type(result.returned_value) + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', COMPLETE_SIMPLE_TYPES) +def test_static_array_indexed(type_): + code_py = f""" +CONSTANT: {type_}[3] = (24, 57, 80, ) + +@exported +def testEntry() -> {type_}: + return helper(CONSTANT, 0, 1, 2) + +def helper(array: {type_}[3], i0: u32, i1: u32, i2: u32) -> {type_}: + return array[i0] + array[i1] + array[i2] +""" + + result = Suite(code_py).run_code() + + assert 161 == result.returned_value + assert TYPE_MAP[type_] == type(result.returned_value) + @pytest.mark.integration_test def test_bytes_address(): code_py = """ diff --git a/tests/integration/test_static_checking.py b/tests/integration/test_static_checking.py index ed25023..1544537 100644 --- a/tests/integration/test_static_checking.py +++ b/tests/integration/test_static_checking.py @@ -60,7 +60,7 @@ def test_tuple_constant_too_few_values(): CONSTANT: (u32, u8, u8, ) = (24, 57, ) """ - with pytest.raises(StaticError, match=f'Static error on line 2: Invalid number of tuple values'): + with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'): phasm_parse(code_py) @pytest.mark.integration_test @@ -69,7 +69,7 @@ def test_tuple_constant_too_many_values(): CONSTANT: (u32, u8, u8, ) = (24, 57, 1, 1, ) """ - with pytest.raises(StaticError, match=f'Static error on line 2: Invalid number of tuple values'): + with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'): phasm_parse(code_py) @pytest.mark.integration_test @@ -78,5 +78,32 @@ def test_tuple_constant_type_mismatch(): CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, ) """ - with pytest.raises(StaticError, match=f'Static error on line 2: Integer value out of range; expected 0..255, actual 4000'): + with pytest.raises(StaticError, match='Static error on line 2: Integer value out of range; expected 0..255, actual 4000'): + phasm_parse(code_py) + +@pytest.mark.integration_test +def test_static_array_constant_too_few_values(): + code_py = """ +CONSTANT: u8[3] = (24, 57, ) +""" + + with pytest.raises(StaticError, match='Static error on line 2: Invalid number of static array values'): + phasm_parse(code_py) + +@pytest.mark.integration_test +def test_static_array_constant_too_many_values(): + code_py = """ +CONSTANT: u8[3] = (24, 57, 1, 1, ) +""" + + with pytest.raises(StaticError, match='Static error on line 2: Invalid number of static array values'): + phasm_parse(code_py) + +@pytest.mark.integration_test +def test_static_array_constant_type_mismatch(): + code_py = """ +CONSTANT: u8[3] = (24, 4000, 1, ) +""" + + with pytest.raises(StaticError, match='Static error on line 2: Integer value out of range; expected 0..255, actual 4000'): phasm_parse(code_py)