Compare commits

..

14 Commits

Author SHA1 Message Date
Johan B.W. de Vries
167560d7dd Replaced pylint with ruff 2023-11-15 12:06:25 +01:00
Johan B.W. de Vries
4236340ff6 Organized tests in type classes, removed some redundant ones 2023-11-15 11:59:59 +01:00
Johan B.W. de Vries
4a4c62c728 Support static array of tuples 2023-11-15 11:40:24 +01:00
Johan B.W. de Vries
fcbd32a880 Support using structs in tuples 2023-11-15 11:36:25 +01:00
Johan B.W. de Vries
bd80210ba3 Support structs in extracting and inputting values 2023-11-14 15:04:20 +01:00
Johan B.W. de Vries
a73a3b2bb4 Improve test information on errors during parsing 2023-11-14 14:09:30 +01:00
Johan B.W. de Vries
8fa2e4830e Support negative literals 2023-11-14 14:09:17 +01:00
Johan B.W. de Vries
ddc0bbdf30 Cleanup 2023-11-13 14:34:07 +01:00
Johan B.W. de Vries
0131b84146 Implemented round trip input / output 2023-11-13 14:32:17 +01:00
Johan B.W. de Vries
f4f068137a More work on type testing.
Also, reduced spam on typing dump by only showing the
'back on todo list' count, rather than repeat them all.

Also, typing on tests.integration.helpers
2023-11-13 13:00:34 +01:00
Johan B.W. de Vries
769eaaf243 Started on extracting values. Works for most, not for tuples yet. 2023-11-11 15:53:43 +01:00
Johan B.W. de Vries
dff5feed86 More tests and fixes 2023-11-11 15:19:33 +01:00
Johan B.W. de Vries
ff0286bcf6 More framework 2023-11-11 13:45:03 +01:00
Johan B.W. de Vries
ef00b3a91c Started on a test generation framework 2023-11-11 12:02:37 +01:00
35 changed files with 611 additions and 241 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,12 @@
"""
Contains the syntax tree for ourlang
"""
import enum
from typing import Dict, Iterable, List, Optional, Union
from typing_extensions import Final
import enum
from .type3 import types as type3types
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', )
from .type3.types import Type3, Type3OrPlaceholder, PlaceholderForType, StructType3
class Expression:
"""

View File

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

View File

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

View File

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

View File

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

View File

@ -3,11 +3,11 @@ This module contains possible constraints generated based on the AST
These need to be resolved before the program can be compiled.
"""
from typing import Dict, List, Optional, Tuple, Union
from typing import Dict, Optional, List, Tuple, Union
from .. import ourlang
from . import types
from . import types
class Error:
"""

View File

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

View File

@ -3,24 +3,12 @@ Entry point to the type3 system
"""
from typing import Dict, List
from .. import codestyle, ourlang
from .constraints import (
ConstraintBase,
Error,
RequireTypeSubstitutes,
SameTypeConstraint,
SubstitutionMap,
)
from .. import codestyle
from .. import ourlang
from .constraints import ConstraintBase, Error, RequireTypeSubstitutes, SameTypeConstraint, SubstitutionMap
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

View File

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

View File

@ -1,9 +1,10 @@
"""
Helper functions to generate WASM code by writing Python functions
"""
import functools
from typing import Any, Callable, Dict, List, Optional, Type
import functools
from . import wasm
# pylint: disable=C0103,C0115,C0116,R0902

View File

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

View File

@ -0,0 +1,16 @@
"""
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,11 +1,12 @@
from typing import Any, Generator, Iterable, List, TextIO, Union
import struct
import sys
from typing import Any, Generator, Iterable, List, TextIO, Union
from phasm import compiler
from phasm.codestyle import phasm_render
from phasm.runtime import calculate_alloc_size
from phasm.type3 import types as type3types
from phasm.runtime import calculate_alloc_size
from . import runners

View File

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

View File

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

View File

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

View File

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

View File

@ -1,154 +1,156 @@
from typing import Any, Dict
import functools
import json
import sys
from typing import Any
import marko
import marko.md_renderer
def get_tests(template):
test_data = None
for el in template.children:
if isinstance(el, marko.block.BlankLine):
continue
test_data = None
for el in template.children:
if isinstance(el, marko.block.BlankLine):
continue
if isinstance(el, marko.block.Heading):
if test_data is not None:
yield test_data
if isinstance(el, marko.block.Heading):
if test_data is not None:
yield test_data
test_data = []
test_data.append(el)
continue
test_data = []
test_data.append(el)
continue
if test_data is not None:
test_data.append(el)
if test_data is not None:
test_data.append(el)
if test_data is not None:
yield test_data
if test_data is not None:
yield test_data
def apply_settings(settings, txt):
for k, v in settings.items():
if k in ('CODE_HEADER', 'PYTHON'):
continue
for k, v in settings.items():
if k in ('CODE_HEADER', 'PYTHON'):
continue
txt = txt.replace(f'${k}', v)
return txt
txt = txt.replace(f'${k}', v)
return txt
def generate_assertion_expect(result, arg, given=None):
given = given or []
given = given or []
result.append('result = Suite(code_py).run_code(' + ', '.join(repr(x) for x in given) + ')')
result.append(f'assert {repr(arg)} == result.returned_value')
result.append('result = Suite(code_py).run_code(' + ', '.join(repr(x) for x in given) + ')')
result.append(f'assert {repr(arg)} == result.returned_value')
def generate_assertion_expect_type_error(result, error_msg, error_comment = None):
result.append('with pytest.raises(Type3Exception) as exc_info:')
result.append(' Suite(code_py).run_code()')
result.append(f'assert {repr(error_msg)} == exc_info.value.args[0][0].msg')
result.append(f'assert {repr(error_comment)} == exc_info.value.args[0][0].comment')
result.append('with pytest.raises(Type3Exception) as exc_info:')
result.append(' Suite(code_py).run_code()')
result.append(f'assert {repr(error_msg)} == exc_info.value.args[0][0].msg')
result.append(f'assert {repr(error_comment)} == exc_info.value.args[0][0].comment')
def json_does_not_support_byte_or_tuple_values_fix(inp: Any):
if isinstance(inp, (int, float, )):
return inp
if isinstance(inp, (int, float, )):
return inp
if isinstance(inp, str):
if inp.startswith('bytes:'):
return inp[6:].encode()
return inp
if isinstance(inp, str):
if inp.startswith('bytes:'):
return inp[6:].encode()
return inp
if isinstance(inp, list):
return tuple(map(json_does_not_support_byte_or_tuple_values_fix, inp))
if isinstance(inp, list):
return tuple(map(json_does_not_support_byte_or_tuple_values_fix, inp))
if isinstance(inp, dict):
return {
key: json_does_not_support_byte_or_tuple_values_fix(val)
for key, val in inp.items()
}
if isinstance(inp, dict):
key_names = list(inp)
return {
key: json_does_not_support_byte_or_tuple_values_fix(val)
for key, val in inp.items()
}
raise NotImplementedError(inp)
raise NotImplementedError(inp)
def generate_assertions(settings, result_code):
result = []
result = []
locals_ = {
'TYPE': settings['TYPE'],
'TYPE_NAME': settings['TYPE_NAME'],
'expect': functools.partial(generate_assertion_expect, result),
'expect_type_error': functools.partial(generate_assertion_expect_type_error, result),
}
locals_ = {
'TYPE': settings['TYPE'],
'TYPE_NAME': settings['TYPE_NAME'],
'expect': functools.partial(generate_assertion_expect, result),
'expect_type_error': functools.partial(generate_assertion_expect_type_error, result),
}
if 'PYTHON' in settings:
locals_.update(json_does_not_support_byte_or_tuple_values_fix(settings['PYTHON']))
if 'PYTHON' in settings:
locals_.update(json_does_not_support_byte_or_tuple_values_fix(settings['PYTHON']))
if 'VAL0' not in locals_:
locals_['VAL0'] = eval(settings['VAL0'])
if 'VAL0' not in locals_:
locals_['VAL0'] = eval(settings['VAL0'])
exec(result_code, {}, locals_)
exec(result_code, {}, locals_)
return ' ' + '\n '.join(result) + '\n'
return ' ' + '\n '.join(result) + '\n'
def generate_code(markdown, template, settings):
type_name = settings['TYPE_NAME']
type_name = settings['TYPE_NAME']
print('"""')
print('AUTO GENERATED')
print()
print('TEMPLATE:', sys.argv[1])
print('SETTINGS:', sys.argv[2])
print('"""')
print('import pytest')
print()
print('from phasm.type3.entry import Type3Exception')
print()
print('from ..helpers import Suite')
print()
print('"""')
print('AUTO GENERATED')
print()
print('TEMPLATE:', sys.argv[1])
print('SETTINGS:', sys.argv[2])
print('"""')
print('import pytest')
print()
print('from phasm.type3.entry import Type3Exception')
print()
print('from ..helpers import Suite')
print()
for test in get_tests(template):
assert len(test) == 4, test
heading, paragraph, code_block1, code_block2 = test
for test in get_tests(template):
assert len(test) == 4, test
heading, paragraph, code_block1, code_block2 = test
assert isinstance(heading, marko.block.Heading)
assert isinstance(paragraph, marko.block.Paragraph)
assert isinstance(code_block1, marko.block.FencedCode)
assert isinstance(code_block2, marko.block.FencedCode)
assert isinstance(heading, marko.block.Heading)
assert isinstance(paragraph, marko.block.Paragraph)
assert isinstance(code_block1, marko.block.FencedCode)
assert isinstance(code_block2, marko.block.FencedCode)
test_id = apply_settings(settings, heading.children[0].children)
user_story = apply_settings(settings, markdown.renderer.render(paragraph))
inp_code = apply_settings(settings, code_block1.children[0].children)
test_id = apply_settings(settings, heading.children[0].children)
user_story = apply_settings(settings, markdown.renderer.render(paragraph))
inp_code = apply_settings(settings, code_block1.children[0].children)
result_code = markdown.renderer.render_children(code_block2)
result_code = markdown.renderer.render_children(code_block2)
print('@pytest.mark.integration_test')
print(f'def test_{type_name}_{test_id}():')
print(' """')
print(' ' + user_story.replace('\n', '\n '))
print(' """')
print(' code_py = """')
if 'CODE_HEADER' in settings:
for lin in settings['CODE_HEADER']:
print(lin)
print()
print(inp_code.rstrip('\n'))
print('"""')
print()
print('@pytest.mark.integration_test')
print(f'def test_{type_name}_{test_id}():')
print(' """')
print(' ' + user_story.replace('\n', '\n '))
print(' """')
print(' code_py = """')
if 'CODE_HEADER' in settings:
for lin in settings['CODE_HEADER']:
print(lin)
print()
print(inp_code.rstrip('\n'))
print('"""')
print()
print(generate_assertions(settings, result_code))
print()
print(generate_assertions(settings, result_code))
print()
def main():
markdown = marko.Markdown(
renderer=marko.md_renderer.MarkdownRenderer,
)
with open(sys.argv[1], 'r', encoding='utf-8') as fil:
template = markdown.parse(fil.read())
markdown = marko.Markdown(
renderer=marko.md_renderer.MarkdownRenderer,
)
with open(sys.argv[1], 'r', encoding='utf-8') as fil:
template = markdown.parse(fil.read())
with open(sys.argv[2], 'r', encoding='utf-8') as fil:
settings = json.load(fil)
with open(sys.argv[2], 'r', encoding='utf-8') as fil:
settings = json.load(fil)
if 'TYPE_NAME' not in settings:
settings['TYPE_NAME'] = settings['TYPE']
if 'TYPE_NAME' not in settings:
settings['TYPE_NAME'] = settings['TYPE']
generate_code(markdown, template, settings)
generate_code(markdown, template, settings)
if __name__ == '__main__':
main()
main()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,31 +2,132 @@ import pytest
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
@pytest.mark.integration_test
def test_static_array_index_ok():
def test_module_constant_def():
code_py = """
CONSTANT: u8[3] = (24, 57, 80, )
@exported
def testEntry(f: u64[3]) -> u64:
return f[2]
def testEntry() -> i32:
return 0
"""
result = Suite(code_py).run_code((1, 2, 3, ))
result = Suite(code_py).run_code()
assert 3 == result.returned_value
assert 0 == result.returned_value
@pytest.mark.integration_test
def test_static_array_index_invalid_type():
code_py = """
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
def test_module_constant_3(type_):
code_py = f"""
CONSTANT: {type_}[3] = (24, 57, 80, )
@exported
def testEntry(f: f32[3]) -> u64:
return f[0]
def testEntry() -> {type_}:
return CONSTANT[1]
"""
with pytest.raises(Type3Exception, match=r'u64 must be f32 instead'):
Suite(code_py).run_code((0.0, 1.5, 2.25, ))
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 = """
CONSTANT: u64[3] = (250, 250000, 250000000, )
@exported
def testEntry() -> u64:
return helper(CONSTANT[0])
def helper(x: u64) -> u64:
return x
"""
result = Suite(code_py).run_code()
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
def test_module_constant_type_mismatch_not_subscriptable():

View File

@ -2,20 +2,61 @@ import pytest
from phasm.type3.entry import Type3Exception
from ..constants import (
ALL_INT_TYPES, TYPE_MAP
)
from ..helpers import Suite
@pytest.mark.integration_test
def test_struct_0():
def test_module_constant_def():
code_py = """
class CheckedValue:
value: i32
class SomeStruct:
value0: u8
value1: u32
value2: u64
CONSTANT: SomeStruct = SomeStruct(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_', 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))
def helper(cv: CheckedValue) -> i32:
def helper(cv: CheckedValue) -> {type_}:
return cv.value
"""
@ -63,6 +104,41 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32:
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.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
def test_type_mismatch_struct_member(type_):

View File

@ -2,8 +2,51 @@ import pytest
from phasm.type3.entry import Type3Exception
from ..constants import ALL_FLOAT_TYPES, COMPLETE_INT_TYPES, TYPE_MAP
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
def test_function_call_element_ok():
@ -22,6 +65,175 @@ def helper(x: u64) -> u64:
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.skip('SIMD support is but a dream')
def test_tuple_i32x4():

View File

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