MVP #1

Merged
jbwdevries merged 73 commits from idea_crc32 into master 2022-08-21 12:59:21 +00:00
15 changed files with 448 additions and 46 deletions
Showing only changes of commit e589223dbb - Show all commits

View File

@ -1,5 +1,5 @@
@exported
def index(inp: bytes, idx: i32) -> i32:
def index(inp: bytes, idx: u32) -> u8:
return inp[idx]
@exported

47
examples/crc32.html Normal file
View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Examples - CRC32</title>
</head>
<body>
<h1>Buffer</h1>
<a href="index.html">List</a> - <a href="crc32.py.html">Source</a> - <a href="crc32.wat.html">WebAssembly</a>
<div style="white-space: pre;" id="results"></div>
<script type="text/javascript">
let importObject = {};
let results = document.getElementById('results');
function log(txt)
{
let span = document.createElement('span');
span.innerHTML = txt;
results.appendChild(span);
let br = document.createElement('br');
results.appendChild(br);
}
WebAssembly.instantiateStreaming(fetch('crc32.wasm'), importObject)
.then(app => {
// Allocate room within the memory of the WebAssembly class
stdlib_types___alloc_bytes__ = app.instance.exports['stdlib.types.__alloc_bytes__']
let offset0 = stdlib_types___alloc_bytes__(0);
let offset1 = stdlib_types___alloc_bytes__(1);
// Write into allocated memory
var i8arr0 = new Uint8Array(app.instance.exports.memory.buffer, offset0 + 4, 0);
var i8arr1 = new Uint8Array(app.instance.exports.memory.buffer, offset1 + 4, 1);
i8arr1[0] = 0x61;
log('crc32([' + i8arr0 + ']) = ' + app.instance.exports.crc32(offset0));
log('crc32([' + i8arr1 + ']) = ' + app.instance.exports.crc32(offset1));
});
</script>
</body>
</html>

View File

@ -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)]

View File

@ -6,6 +6,7 @@
<h1>Examples</h1>
<h2>Standard</h2>
<ul>
<li><a href="crc32.html">CRC32</a></li>
<li><a href="fib.html">Fibonacci</a></li>
</ul>
<h2>Technical</h2>

View File

@ -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):

View File

@ -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('<i', inp)
return struct.pack('<I', inp)
def module_data_u64(inp: int) -> bytes:
"""
@ -479,6 +506,30 @@ def module_data_u64(inp: int) -> bytes:
"""
return struct.pack('<Q', inp)
def module_data_i32(inp: int) -> bytes:
"""
Compile: module data, i32 value
"""
return struct.pack('<i', inp)
def module_data_i64(inp: int) -> bytes:
"""
Compile: module data, i64 value
"""
return struct.pack('<q', inp)
def module_data_f32(inp: float) -> bytes:
"""
Compile: module data, f32 value
"""
return struct.pack('<f', inp)
def module_data_f64(inp: float) -> bytes:
"""
Compile: module data, f64 value
"""
return struct.pack('<d', inp)
def module_data(inp: ourlang.ModuleData) -> 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())

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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')

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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 = """

View File

@ -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)