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)