Compare commits

..

1 Commits

Author SHA1 Message Date
Johan B.W. de Vries
97b61e3ee1 Test generation framework with typing improvements
Prior to this PR, each type would have its own handwritten
test suite. The end result was that not all types were tested
for all situations.

This PR adds a framework based on a Markdown file, which
generates the basic tests for the types defined in json
files. These are auto generated and updated by the Makefile
before the test suite is run.

Also, a number of unsupported type combinations are now
supported.

Also, we now support negative literals.

Also, allocation calculation fixes for nested types.

Also, the test helpers can now properly import and export
typed variables such as bytes, static arrays and tuples. This
may come in handy when it comes to phasm platform wanting to
route data.

Also, adds better support for i8 type.

Also, started on a runtime.py, since there's quite some code
now that deals with compile time handling of WebAssembly stuff.

Also, minor improvement to the type constrains, namely we
better match 'tuple' literals with static array types.

Also, reduced spam when printing the type analysis results;
constraints that go back on the backlog are now no longer
printed one by one. It now also prints the end results of
the typing analysis.

Also, reorganized the big test_primitives test into type
classes.

Also, replaced pylint with ruff.
2023-11-15 12:52:23 +01:00
35 changed files with 238 additions and 608 deletions

View File

@ -28,7 +28,7 @@ test: venv/.done $(subst .json,.py,$(subst /generator_,/test_generated_,$(wildca
venv/bin/pytest tests $(TEST_FLAGS) venv/bin/pytest tests $(TEST_FLAGS)
lint: venv/.done lint: venv/.done
venv/bin/ruff check phasm tests/integration/helpers.py tests/integration/runners.py venv/bin/ruff check phasm tests
typecheck: venv/.done typecheck: venv/.done
venv/bin/mypy --strict phasm tests/integration/helpers.py tests/integration/runners.py venv/bin/mypy --strict phasm tests/integration/helpers.py tests/integration/runners.py

View File

@ -4,9 +4,10 @@ Functions for using this module from CLI
import sys import sys
from .compiler import phasm_compile
from .parser import phasm_parse from .parser import phasm_parse
from .type3.entry import phasm_type3 from .type3.entry import phasm_type3
from .compiler import phasm_compile
def main(source: str, sink: str) -> int: def main(source: str, sink: str) -> int:
""" """

View File

@ -9,6 +9,7 @@ from . import ourlang
from .type3 import types as type3types from .type3 import types as type3types
from .type3.types import TYPE3_ASSERTION_ERROR, Type3, Type3OrPlaceholder from .type3.types import TYPE3_ASSERTION_ERROR, Type3, Type3OrPlaceholder
def phasm_render(inp: ourlang.Module) -> str: def phasm_render(inp: ourlang.Module) -> str:
""" """
Public method for rendering a Phasm module into Phasm code Public method for rendering a Phasm module into Phasm code

View File

@ -1,18 +1,14 @@
""" """
This module contains the code to convert parsed Ourlang into WebAssembly code This module contains the code to convert parsed Ourlang into WebAssembly code
""" """
import struct
from typing import List, Optional from typing import List, Optional
import struct from . import codestyle, ourlang, wasm
from .runtime import calculate_alloc_size, calculate_member_offset
from . import codestyle
from . import ourlang
from .type3 import types as type3types
from . import wasm
from .stdlib import alloc as stdlib_alloc from .stdlib import alloc as stdlib_alloc
from .stdlib import types as stdlib_types from .stdlib import types as stdlib_types
from .runtime import calculate_alloc_size, calculate_member_offset from .type3 import types as type3types
from .wasmgenerator import Generator as WasmGenerator from .wasmgenerator import Generator as WasmGenerator
LOAD_STORE_TYPE_MAP = { LOAD_STORE_TYPE_MAP = {

View File

@ -1,12 +1,16 @@
""" """
Contains the syntax tree for ourlang Contains the syntax tree for ourlang
""" """
import enum
from typing import Dict, Iterable, List, Optional, Union from typing import Dict, Iterable, List, Optional, Union
import enum from typing_extensions import Final
from .type3 import types as type3types from .type3 import types as type3types
from .type3.types import Type3, Type3OrPlaceholder, PlaceholderForType, StructType3 from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder
WEBASSEMBLY_BUILTIN_FLOAT_OPS: Final = ('abs', 'sqrt', 'ceil', 'floor', 'trunc', 'nearest', )
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
class Expression: class Expression:
""" """

View File

@ -1,37 +1,39 @@
""" """
Parses the source code from the plain text into a syntax tree Parses the source code from the plain text into a syntax tree
""" """
from typing import Any, Dict, NoReturn, Union
import ast import ast
from typing import Any, Dict, NoReturn, Union
from .type3 import types as type3types
from .exceptions import StaticError from .exceptions import StaticError
from .ourlang import ( from .ourlang import (
WEBASSEMBLY_BUILTIN_FLOAT_OPS, WEBASSEMBLY_BUILTIN_FLOAT_OPS,
AccessStructMember,
Module, ModuleDataBlock,
Function,
Expression,
BinaryOp, BinaryOp,
ConstantBytes,
ConstantPrimitive, ConstantPrimitive,
ConstantBytes, ConstantTuple, ConstantStruct, ConstantStruct,
TupleInstantiation, ConstantTuple,
Expression,
FunctionCall, AccessStructMember, Subscript,
StructDefinition, StructConstructor,
UnaryOp, VariableReference,
Fold, Fold,
Function,
Statement, FunctionCall,
StatementIf, StatementPass, StatementReturn,
FunctionParam, FunctionParam,
Module,
ModuleConstantDef, ModuleConstantDef,
ModuleDataBlock,
Statement,
StatementIf,
StatementPass,
StatementReturn,
StructConstructor,
StructDefinition,
Subscript,
TupleInstantiation,
UnaryOp,
VariableReference,
) )
from .type3 import types as type3types
def phasm_parse(source: str) -> Module: def phasm_parse(source: str) -> Module:
""" """

View File

@ -1,5 +1,6 @@
from .type3 import types as type3types from .type3 import types as type3types
def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int: def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
if typ in (type3types.u8, type3types.i8, ): if typ in (type3types.u8, type3types.i8, ):
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32 return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32

View File

@ -1,7 +1,8 @@
""" """
stdlib: Memory allocation stdlib: Memory allocation
""" """
from phasm.wasmgenerator import Generator, VarType_i32 as i32, func_wrapper from phasm.wasmgenerator import Generator, func_wrapper
from phasm.wasmgenerator import VarType_i32 as i32
IDENTIFIER = 0xA1C0 IDENTIFIER = 0xA1C0

View File

@ -1,9 +1,10 @@
""" """
stdlib: Standard types that are not wasm primitives stdlib: Standard types that are not wasm primitives
""" """
from phasm.wasmgenerator import Generator, VarType_i32 as i32, func_wrapper
from phasm.stdlib import alloc from phasm.stdlib import alloc
from phasm.wasmgenerator import Generator, func_wrapper
from phasm.wasmgenerator import VarType_i32 as i32
@func_wrapper() @func_wrapper()
def __alloc_bytes__(g: Generator, length: i32) -> i32: def __alloc_bytes__(g: Generator, length: i32) -> i32:

View File

@ -3,12 +3,12 @@ This module contains possible constraints generated based on the AST
These need to be resolved before the program can be compiled. These need to be resolved before the program can be compiled.
""" """
from typing import Dict, Optional, List, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
from .. import ourlang from .. import ourlang
from . import types from . import types
class Error: class Error:
""" """
An error returned by the check functions for a contraint An error returned by the check functions for a contraint

View File

@ -6,16 +6,16 @@ The constraints solver can then try to resolve all constraints.
from typing import Generator, List from typing import Generator, List
from .. import ourlang from .. import ourlang
from .constraints import (
Context,
ConstraintBase,
CastableConstraint, CanBeSubscriptedConstraint,
LiteralFitsConstraint, MustImplementTypeClassConstraint, SameTypeConstraint,
)
from . import types as type3types from . import types as type3types
from .constraints import (
CanBeSubscriptedConstraint,
CastableConstraint,
ConstraintBase,
Context,
LiteralFitsConstraint,
MustImplementTypeClassConstraint,
SameTypeConstraint,
)
ConstraintGenerator = Generator[ConstraintBase, None, None] ConstraintGenerator = Generator[ConstraintBase, None, None]

View File

@ -3,12 +3,24 @@ Entry point to the type3 system
""" """
from typing import Dict, List from typing import Dict, List
from .. import codestyle from .. import codestyle, ourlang
from .. import ourlang from .constraints import (
ConstraintBase,
from .constraints import ConstraintBase, Error, RequireTypeSubstitutes, SameTypeConstraint, SubstitutionMap Error,
RequireTypeSubstitutes,
SameTypeConstraint,
SubstitutionMap,
)
from .constraintsgenerator import phasm_type3_generate_constraints from .constraintsgenerator import phasm_type3_generate_constraints
from .types import AppliedType3, IntType3, PlaceholderForType, PrimitiveType3, StructType3, Type3, Type3OrPlaceholder from .types import (
AppliedType3,
IntType3,
PlaceholderForType,
PrimitiveType3,
StructType3,
Type3,
Type3OrPlaceholder,
)
MAX_RESTACK_COUNT = 100 MAX_RESTACK_COUNT = 100

View File

@ -5,6 +5,7 @@ and being able to conver it to Web Assembly Text Format
from typing import Iterable, List, Optional, Tuple from typing import Iterable, List, Optional, Tuple
class WatSerializable: class WatSerializable:
""" """
Mixin for clases that can be serialized as WebAssembly Text Mixin for clases that can be serialized as WebAssembly Text

View File

@ -1,9 +1,8 @@
""" """
Helper functions to generate WASM code by writing Python functions Helper functions to generate WASM code by writing Python functions
""" """
from typing import Any, Callable, Dict, List, Optional, Type
import functools import functools
from typing import Any, Callable, Dict, List, Optional, Type
from . import wasm from . import wasm

3
pyproject.toml Normal file
View File

@ -0,0 +1,3 @@
[tool.ruff.lint]
select = ["F", "E", "W", "I"]
ignore = ["E501"]

View File

@ -1,16 +0,0 @@
"""
Constants for use in the tests
"""
ALL_INT_TYPES = ['u8', 'u32', 'u64', 'i32', 'i64']
COMPLETE_INT_TYPES = ['u32', 'u64', 'i32', 'i64']
ALL_FLOAT_TYPES = ['f32', 'f64']
COMPLETE_FLOAT_TYPES = ALL_FLOAT_TYPES
TYPE_MAP = {
**{x: int for x in ALL_INT_TYPES},
**{x: float for x in ALL_FLOAT_TYPES},
}
COMPLETE_NUMERIC_TYPES = COMPLETE_INT_TYPES + COMPLETE_FLOAT_TYPES

View File

@ -1,12 +1,11 @@
from typing import Any, Generator, Iterable, List, TextIO, Union
import struct import struct
import sys import sys
from typing import Any, Generator, Iterable, List, TextIO, Union
from phasm import compiler from phasm import compiler
from phasm.codestyle import phasm_render from phasm.codestyle import phasm_render
from phasm.type3 import types as type3types
from phasm.runtime import calculate_alloc_size from phasm.runtime import calculate_alloc_size
from phasm.type3 import types as type3types
from . import runners from . import runners

View File

@ -1,21 +1,19 @@
""" """
Runners to help run WebAssembly code on various interpreters Runners to help run WebAssembly code on various interpreters
""" """
from typing import Any, Callable, Dict, Iterable, Optional, TextIO
import ctypes import ctypes
import io import io
from typing import Any, Callable, Dict, Iterable, Optional, TextIO
import pywasm.binary import pywasm.binary
import wasm3 import wasm3
import wasmer import wasmer
import wasmtime import wasmtime
from phasm import ourlang, wasm
from phasm.compiler import phasm_compile from phasm.compiler import phasm_compile
from phasm.parser import phasm_parse from phasm.parser import phasm_parse
from phasm.type3.entry import phasm_type3 from phasm.type3.entry import phasm_type3
from phasm import ourlang
from phasm import wasm
Imports = Optional[Dict[str, Callable[[Any], Any]]] Imports = Optional[Dict[str, Callable[[Any], Any]]]

View File

@ -2,6 +2,7 @@ import pytest
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.slow_integration_test @pytest.mark.slow_integration_test
def test_index(): def test_index():
with open('examples/buffer.py', 'r', encoding='ASCII') as fil: with open('examples/buffer.py', 'r', encoding='ASCII') as fil:

View File

@ -1,10 +1,10 @@
import binascii import binascii
import struct
import pytest import pytest
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.slow_integration_test @pytest.mark.slow_integration_test
def test_crc32(): def test_crc32():
# FIXME: Stub # FIXME: Stub

View File

@ -2,6 +2,7 @@ import pytest
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.slow_integration_test @pytest.mark.slow_integration_test
def test_fib(): def test_fib():
with open('./examples/fib.py', 'r', encoding='UTF-8') as fil: with open('./examples/fib.py', 'r', encoding='UTF-8') as fil:

View File

@ -1,12 +1,12 @@
from typing import Any, Dict
import functools import functools
import json import json
import sys import sys
from typing import Any
import marko import marko
import marko.md_renderer import marko.md_renderer
def get_tests(template): def get_tests(template):
test_data = None test_data = None
for el in template.children: for el in template.children:
@ -60,7 +60,6 @@ def json_does_not_support_byte_or_tuple_values_fix(inp: Any):
return tuple(map(json_does_not_support_byte_or_tuple_values_fix, inp)) return tuple(map(json_does_not_support_byte_or_tuple_values_fix, inp))
if isinstance(inp, dict): if isinstance(inp, dict):
key_names = list(inp)
return { return {
key: json_does_not_support_byte_or_tuple_values_fix(val) key: json_does_not_support_byte_or_tuple_values_fix(val)
for key, val in inp.items() for key, val in inp.items()
@ -136,7 +135,6 @@ def generate_code(markdown, template, settings):
print(generate_assertions(settings, result_code)) print(generate_assertions(settings, result_code))
print() print()
def main(): def main():
markdown = marko.Markdown( markdown = marko.Markdown(
renderer=marko.md_renderer.MarkdownRenderer, renderer=marko.md_renderer.MarkdownRenderer,

View File

@ -3,7 +3,7 @@ import pytest
from phasm.type3.entry import Type3Exception from phasm.type3.entry import Type3Exception
from ..helpers import Suite from ..helpers import Suite
from ..constants import ALL_INT_TYPES, ALL_FLOAT_TYPES, COMPLETE_INT_TYPES, TYPE_MAP
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['u32', 'u64']) # FIXME: Support u8, requires an extra AND operation @pytest.mark.parametrize('type_', ['u32', 'u64']) # FIXME: Support u8, requires an extra AND operation
@ -17,7 +17,7 @@ def testEntry() -> {type_}:
result = Suite(code_py).run_code() result = Suite(code_py).run_code()
assert 80 == result.returned_value assert 80 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value) assert isinstance(result.returned_value, int)
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['u32', 'u64']) @pytest.mark.parametrize('type_', ['u32', 'u64'])
@ -33,7 +33,7 @@ def testEntry() -> {type_}:
result = Suite(code_py).run_code(runtime='wasmtime') result = Suite(code_py).run_code(runtime='wasmtime')
assert 1 == result.returned_value assert 1 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value) assert isinstance(result.returned_value, int)
@pytest.mark.integration_test @pytest.mark.integration_test
def test_logical_right_shift_left_bit_one(): def test_logical_right_shift_left_bit_one():
@ -59,7 +59,7 @@ def testEntry() -> {type_}:
result = Suite(code_py).run_code() result = Suite(code_py).run_code()
assert 11 == result.returned_value assert 11 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value) assert isinstance(result.returned_value, int)
@pytest.mark.integration_test @pytest.mark.integration_test
def test_bitwise_or_inv_type(): def test_bitwise_or_inv_type():
@ -98,7 +98,7 @@ def testEntry() -> {type_}:
result = Suite(code_py).run_code() result = Suite(code_py).run_code()
assert 9 == result.returned_value assert 9 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value) assert isinstance(result.returned_value, int)
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64']) @pytest.mark.parametrize('type_', ['u8', 'u32', 'u64'])
@ -112,4 +112,4 @@ def testEntry() -> {type_}:
result = Suite(code_py).run_code() result = Suite(code_py).run_code()
assert 2 == result.returned_value assert 2 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value) assert isinstance(result.returned_value, int)

View File

@ -5,6 +5,7 @@ import pytest
from ..helpers import Suite, write_header from ..helpers import Suite, write_header
from ..runners import RunnerPywasm from ..runners import RunnerPywasm
def setup_interpreter(phash_code: str) -> RunnerPywasm: def setup_interpreter(phash_code: str) -> RunnerPywasm:
runner = RunnerPywasm(phash_code) runner = RunnerPywasm(phash_code)

View File

@ -4,6 +4,7 @@ from phasm.type3.entry import Type3Exception
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
def test_bytes_length(): def test_bytes_length():
code_py = """ code_py = """
@ -44,9 +45,9 @@ def testEntry(f: bytes, g: bytes) -> u8:
def test_bytes_index_invalid_type(): def test_bytes_index_invalid_type():
code_py = """ code_py = """
@exported @exported
def testEntry(f: bytes, g: bytes) -> u64: def testEntry(f: bytes) -> u64:
return f[50] return f[50]
""" """
with pytest.raises(Type3Exception, match=r'u64 must be u8 instead'): with pytest.raises(Type3Exception, match=r'u64 must be u8 instead'):
Suite(code_py).run_code(b'Short', b'Long' * 100) Suite(code_py).run_code(b'Short')

View File

@ -2,6 +2,7 @@ import pytest
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['f32', 'f64']) @pytest.mark.parametrize('type_', ['f32', 'f64'])
def test_builtins_sqrt(type_): def test_builtins_sqrt(type_):

View File

@ -2,6 +2,7 @@ import pytest
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
def test_call_pre_defined(): def test_call_pre_defined():
code_py = """ code_py = """

View File

@ -2,6 +2,7 @@ import pytest
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('inp', [9, 10, 11, 12]) @pytest.mark.parametrize('inp', [9, 10, 11, 12])
def test_if_simple(inp): def test_if_simple(inp):

View File

@ -4,6 +4,7 @@ from phasm.type3.entry import Type3Exception
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
def test_imported_ok(): def test_imported_ok():
code_py = """ code_py = """

View File

@ -3,7 +3,7 @@ import pytest
from phasm.type3.entry import Type3Exception from phasm.type3.entry import Type3Exception
from ..helpers import Suite from ..helpers import Suite
from ..constants import ALL_INT_TYPES, ALL_FLOAT_TYPES, COMPLETE_INT_TYPES, TYPE_MAP
@pytest.mark.integration_test @pytest.mark.integration_test
def test_expr_constant_literal_does_not_fit(): def test_expr_constant_literal_does_not_fit():

View File

@ -1,12 +1,21 @@
import pytest import pytest
from phasm.type3.entry import Type3Exception
from ..helpers import Suite from ..helpers import Suite
from ..constants import ALL_INT_TYPES, ALL_FLOAT_TYPES, COMPLETE_INT_TYPES, TYPE_MAP
INT_TYPES = ['u32', 'u64', 'i32', 'i64']
FLOAT_TYPES = ['f32', 'f64']
TYPE_MAP = {
'u32': int,
'u64': int,
'i32': int,
'i64': int,
'f32': float,
'f64': float,
}
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', COMPLETE_INT_TYPES) @pytest.mark.parametrize('type_', INT_TYPES)
def test_addition_int(type_): def test_addition_int(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -20,7 +29,7 @@ def testEntry() -> {type_}:
assert TYPE_MAP[type_] == type(result.returned_value) assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES) @pytest.mark.parametrize('type_', FLOAT_TYPES)
def test_addition_float(type_): def test_addition_float(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -34,7 +43,7 @@ def testEntry() -> {type_}:
assert TYPE_MAP[type_] == type(result.returned_value) assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', COMPLETE_INT_TYPES) @pytest.mark.parametrize('type_', INT_TYPES)
def test_subtraction_int(type_): def test_subtraction_int(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -48,7 +57,7 @@ def testEntry() -> {type_}:
assert TYPE_MAP[type_] == type(result.returned_value) assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES) @pytest.mark.parametrize('type_', FLOAT_TYPES)
def test_subtraction_float(type_): def test_subtraction_float(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -78,7 +87,7 @@ def testEntry() -> {type_}:
# TODO: Multiplication # TODO: Multiplication
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', COMPLETE_INT_TYPES) @pytest.mark.parametrize('type_', INT_TYPES)
def test_call_with_expression_int(type_): def test_call_with_expression_int(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -95,7 +104,7 @@ def helper(left: {type_}, right: {type_}) -> {type_}:
assert TYPE_MAP[type_] == type(result.returned_value) assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES) @pytest.mark.parametrize('type_', FLOAT_TYPES)
def test_call_with_expression_float(type_): def test_call_with_expression_float(type_):
code_py = f""" code_py = f"""
@exported @exported

View File

@ -2,132 +2,31 @@ import pytest
from phasm.type3.entry import Type3Exception from phasm.type3.entry import Type3Exception
from ..constants import (
ALL_FLOAT_TYPES, ALL_INT_TYPES, COMPLETE_INT_TYPES, COMPLETE_NUMERIC_TYPES, TYPE_MAP
)
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
def test_module_constant_def(): def test_static_array_index_ok():
code_py = """ code_py = """
CONSTANT: u8[3] = (24, 57, 80, )
@exported @exported
def testEntry() -> i32: def testEntry(f: u64[3]) -> u64:
return 0 return f[2]
""" """
result = Suite(code_py).run_code() result = Suite(code_py).run_code((1, 2, 3, ))
assert 0 == result.returned_value assert 3 == result.returned_value
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ALL_INT_TYPES) def test_static_array_index_invalid_type():
def test_module_constant_3(type_):
code_py = f"""
CONSTANT: {type_}[3] = (24, 57, 80, )
@exported
def testEntry() -> {type_}:
return CONSTANT[1]
"""
result = Suite(code_py).run_code()
assert 57 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test
@pytest.mark.parametrize('type_', COMPLETE_INT_TYPES)
def test_function_call_int(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_', ALL_FLOAT_TYPES)
def test_function_call_float(type_):
code_py = f"""
CONSTANT: {type_}[3] = (24.0, 57.5, 80.75, )
@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 162.25 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test
def test_function_call_element_ok():
code_py = """ code_py = """
CONSTANT: u64[3] = (250, 250000, 250000000, )
@exported @exported
def testEntry() -> u64: def testEntry(f: f32[3]) -> u64:
return helper(CONSTANT[0]) return f[0]
def helper(x: u64) -> u64:
return x
""" """
result = Suite(code_py).run_code() with pytest.raises(Type3Exception, match=r'u64 must be f32 instead'):
Suite(code_py).run_code((0.0, 1.5, 2.25, ))
assert 250 == result.returned_value
@pytest.mark.integration_test
def test_function_call_element_type_mismatch():
code_py = """
CONSTANT: u64[3] = (250, 250000, 250000000, )
@exported
def testEntry() -> u8:
return helper(CONSTANT[0])
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
def test_module_constant_type_mismatch_bitwidth():
code_py = """
CONSTANT: u8[3] = (24, 57, 280, )
"""
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
Suite(code_py).run_code()
@pytest.mark.integration_test
def test_return_as_int():
code_py = """
CONSTANT: u8[3] = (24, 57, 80, )
def testEntry() -> u32:
return CONSTANT
"""
with pytest.raises(Type3Exception, match=r'static_array \(u8\) \(3\) must be u32 instead'):
Suite(code_py).run_code()
@pytest.mark.integration_test @pytest.mark.integration_test
def test_module_constant_type_mismatch_not_subscriptable(): def test_module_constant_type_mismatch_not_subscriptable():

View File

@ -2,61 +2,20 @@ import pytest
from phasm.type3.entry import Type3Exception from phasm.type3.entry import Type3Exception
from ..constants import (
ALL_INT_TYPES, TYPE_MAP
)
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test
def test_module_constant_def():
code_py = """
class SomeStruct:
value0: u8
value1: u32
value2: u64
CONSTANT: SomeStruct = SomeStruct(250, 250000, 250000000) @pytest.mark.integration_test
def test_struct_0():
code_py = """
class CheckedValue:
value: i32
@exported @exported
def testEntry() -> i32: def testEntry() -> i32:
return 0
"""
result = Suite(code_py).run_code()
assert 0 == result.returned_value
@pytest.mark.integration_test
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
def test_module_constant(type_):
code_py = f"""
class CheckedValue:
value: {type_}
CONSTANT: CheckedValue = CheckedValue(24)
@exported
def testEntry() -> {type_}:
return CONSTANT.value
"""
result = Suite(code_py).run_code()
assert 24 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
def test_struct_0(type_):
code_py = f"""
class CheckedValue:
value: {type_}
@exported
def testEntry() -> {type_}:
return helper(CheckedValue(23)) return helper(CheckedValue(23))
def helper(cv: CheckedValue) -> {type_}: def helper(cv: CheckedValue) -> i32:
return cv.value return cv.value
""" """
@ -104,41 +63,6 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32:
assert 545 == result.returned_value assert 545 == result.returned_value
@pytest.mark.integration_test
def test_returned_struct():
code_py = """
class CheckedValue:
value: u8
CONSTANT: CheckedValue = CheckedValue(199)
def helper() -> CheckedValue:
return CONSTANT
def helper2(x: CheckedValue) -> u8:
return x.value
@exported
def testEntry() -> u8:
return helper2(helper())
"""
result = Suite(code_py).run_code()
assert 199 == result.returned_value
@pytest.mark.integration_test
def test_type_mismatch_arg_module_constant():
code_py = """
class Struct:
param: f32
STRUCT: Struct = Struct(1)
"""
with pytest.raises(Type3Exception, match='Must be real'):
Suite(code_py).run_code()
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64']) @pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
def test_type_mismatch_struct_member(type_): def test_type_mismatch_struct_member(type_):

View File

@ -2,51 +2,8 @@ import pytest
from phasm.type3.entry import Type3Exception from phasm.type3.entry import Type3Exception
from ..constants import ALL_FLOAT_TYPES, COMPLETE_INT_TYPES, TYPE_MAP
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test
def test_module_constant_def():
code_py = """
CONSTANT: (u8, u32, u64, ) = (250, 250000, 250000000, )
@exported
def testEntry() -> i32:
return 0
"""
result = Suite(code_py).run_code()
assert 0 == result.returned_value
@pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64', ])
def test_module_constant_1(type_):
code_py = f"""
CONSTANT: ({type_}, ) = (65, )
@exported
def testEntry() -> {type_}:
return CONSTANT[0]
"""
result = Suite(code_py).run_code()
assert 65 == result.returned_value
@pytest.mark.integration_test
def test_module_constant_6():
code_py = """
CONSTANT: (u8, u8, u32, u32, u64, u64, ) = (11, 22, 3333, 4444, 555555, 666666, )
@exported
def testEntry() -> u32:
return CONSTANT[2]
"""
result = Suite(code_py).run_code()
assert 3333 == result.returned_value
@pytest.mark.integration_test @pytest.mark.integration_test
def test_function_call_element_ok(): def test_function_call_element_ok():
@ -65,175 +22,6 @@ def helper(x: u64) -> u64:
assert 250000000 == result.returned_value assert 250000000 == result.returned_value
@pytest.mark.integration_test
def test_tuple():
code_py = """
def l1(c: (u64, )) -> u64:
return c[0]
@exported
def testEntry() -> u64:
return l1((32, ))
"""
result = Suite(code_py).run_code()
assert 32 == result.returned_value
@pytest.mark.integration_test
def test_tuple_of_tuple():
code_py = """
def l1(c: (u64, )) -> u64:
return c[0]
def l2(c: ((u64, ), u64, )) -> u64:
return l1(c[0])
@exported
def testEntry() -> u64:
return l2(((64, ), 32, ))
"""
result = Suite(code_py).run_code()
assert 64 == result.returned_value
@pytest.mark.integration_test
def test_tuple_of_tuple_of_tuple():
code_py = """
def l1(c: (u64, )) -> u64:
return c[0]
def l2(c: ((u64, ), u64, )) -> u64:
return l1(c[0])
def l3(c: (((u64, ), u64, ), u64, )) -> u64:
return l2(c[0])
@exported
def testEntry() -> u64:
return l3((((128, ), 64, ), 32, ))
"""
result = Suite(code_py).run_code()
assert 128 == result.returned_value
@pytest.mark.integration_test
def test_constant_tuple_of_tuple_of_tuple():
code_py = """
CONSTANT: (((u64, ), u64, ), u64, ) = (((128, ), 64, ), 32, )
def l1(c: (u64, )) -> u64:
return c[0]
def l2(c: ((u64, ), u64, )) -> u64:
return l1(c[0])
def l3(c: (((u64, ), u64, ), u64, )) -> u64:
return l2(c[0])
@exported
def testEntry() -> u64:
return l3(CONSTANT)
"""
result = Suite(code_py).run_code()
assert 128 == result.returned_value
@pytest.mark.integration_test
def test_bytes_as_part_of_tuple():
code_py = """
CONSTANT: (bytes, u64, ) = (b'ABCDEF', 19, )
def l0(c: bytes) -> u8:
return c[0]
def l1(c: (bytes, u64, )) -> u8:
return l0(c[0])
@exported
def testEntry() -> u8:
return l1(CONSTANT)
"""
result = Suite(code_py).run_code()
assert 0x41 == result.returned_value
@pytest.mark.integration_test
def test_struct_as_part_of_tuple():
code_py = """
class ValueStruct:
value: i32
CONSTANT: (ValueStruct, u64, ) = (ValueStruct(234), 19, )
def l0(c: ValueStruct) -> i32:
return c.value
def l1(c: (ValueStruct, u64, )) -> i32:
return l0(c[0])
@exported
def testEntry() -> i32:
return l1(CONSTANT)
"""
result = Suite(code_py).run_code()
assert 234 == 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_INT_TYPES)
def test_tuple_simple_constructor_int(type_):
code_py = f"""
@exported
def testEntry() -> {type_}:
return helper((24, 57, 80, ))
def helper(vector: ({type_}, {type_}, {type_}, )) -> {type_}:
return vector[0] + vector[1] + vector[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_', ALL_FLOAT_TYPES)
def test_tuple_simple_constructor_float(type_):
code_py = f"""
@exported
def testEntry() -> {type_}:
return helper((1.0, 2.0, 3.0, ))
def helper(v: ({type_}, {type_}, {type_}, )) -> {type_}:
return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2])
"""
result = Suite(code_py).run_code()
assert 3.74 < result.returned_value < 3.75
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.skip('SIMD support is but a dream') @pytest.mark.skip('SIMD support is but a dream')
def test_tuple_i32x4(): def test_tuple_i32x4():

View File

@ -5,6 +5,7 @@ import pytest
from ..helpers import write_header from ..helpers import write_header
from ..runners import RunnerPywasm3 as Runner from ..runners import RunnerPywasm3 as Runner
def setup_interpreter(phash_code: str) -> Runner: def setup_interpreter(phash_code: str) -> Runner:
runner = Runner(phash_code) runner = Runner(phash_code)