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) venv/bin/pytest tests $(TEST_FLAGS)
lint: venv/.done 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 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,10 +4,9 @@ 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,7 +9,6 @@ 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,14 +1,18 @@
""" """
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
from . import codestyle, ourlang, wasm import struct
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 .type3 import types as type3types from .runtime import calculate_alloc_size, calculate_member_offset
from .wasmgenerator import Generator as WasmGenerator from .wasmgenerator import Generator as WasmGenerator
LOAD_STORE_TYPE_MAP = { LOAD_STORE_TYPE_MAP = {

View File

@ -1,16 +1,12 @@
""" """
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
from typing_extensions import Final import enum
from .type3 import types as type3types from .type3 import types as type3types
from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder from .type3.types import Type3, Type3OrPlaceholder, PlaceholderForType, StructType3
WEBASSEMBLY_BUILTIN_FLOAT_OPS: Final = ('abs', 'sqrt', 'ceil', 'floor', 'trunc', 'nearest', )
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
class Expression: class Expression:
""" """

View File

@ -1,39 +1,37 @@
""" """
Parses the source code from the plain text into a syntax tree Parses the source code from the plain text into a syntax tree
""" """
import ast
from typing import Any, Dict, NoReturn, Union from typing import Any, Dict, NoReturn, Union
import ast
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,
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: def phasm_parse(source: str) -> Module:
""" """

View File

@ -1,6 +1,5 @@
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,8 +1,7 @@
""" """
stdlib: Memory allocation stdlib: Memory allocation
""" """
from phasm.wasmgenerator import Generator, func_wrapper from phasm.wasmgenerator import Generator, VarType_i32 as i32, func_wrapper
from phasm.wasmgenerator import VarType_i32 as i32
IDENTIFIER = 0xA1C0 IDENTIFIER = 0xA1C0

View File

@ -1,10 +1,9 @@
""" """
stdlib: Standard types that are not wasm primitives stdlib: Standard types that are not wasm primitives
""" """
from phasm.stdlib import alloc 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
from phasm.stdlib import alloc
@func_wrapper() @func_wrapper()
def __alloc_bytes__(g: Generator, length: i32) -> i32: 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. 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 ourlang
from . import types
from . import types
class Error: class Error:
""" """

View File

@ -6,17 +6,17 @@ 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 . import types as type3types
from .constraints import ( from .constraints import (
CanBeSubscriptedConstraint,
CastableConstraint,
ConstraintBase,
Context, Context,
LiteralFitsConstraint,
MustImplementTypeClassConstraint, ConstraintBase,
SameTypeConstraint, CastableConstraint, CanBeSubscriptedConstraint,
LiteralFitsConstraint, MustImplementTypeClassConstraint, SameTypeConstraint,
) )
from . import types as type3types
ConstraintGenerator = Generator[ConstraintBase, None, None] ConstraintGenerator = Generator[ConstraintBase, None, None]
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]: 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 typing import Dict, List
from .. import codestyle, ourlang from .. import codestyle
from .constraints import ( from .. import ourlang
ConstraintBase,
Error, from .constraints import ConstraintBase, Error, RequireTypeSubstitutes, SameTypeConstraint, SubstitutionMap
RequireTypeSubstitutes,
SameTypeConstraint,
SubstitutionMap,
)
from .constraintsgenerator import phasm_type3_generate_constraints from .constraintsgenerator import phasm_type3_generate_constraints
from .types import ( from .types import AppliedType3, IntType3, PlaceholderForType, PrimitiveType3, StructType3, Type3, Type3OrPlaceholder
AppliedType3,
IntType3,
PlaceholderForType,
PrimitiveType3,
StructType3,
Type3,
Type3OrPlaceholder,
)
MAX_RESTACK_COUNT = 100 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 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,10 @@
""" """
Helper functions to generate WASM code by writing Python functions Helper functions to generate WASM code by writing Python functions
""" """
import functools
from typing import Any, Callable, Dict, List, Optional, Type from typing import Any, Callable, Dict, List, Optional, Type
import functools
from . import wasm from . import wasm
# pylint: disable=C0103,C0115,C0116,R0902 # 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 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.runtime import calculate_alloc_size
from phasm.type3 import types as type3types from phasm.type3 import types as type3types
from phasm.runtime import calculate_alloc_size
from . import runners from . import runners

View File

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

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 isinstance(result.returned_value, int) assert TYPE_MAP[type_] == type(result.returned_value)
@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 isinstance(result.returned_value, int) assert TYPE_MAP[type_] == type(result.returned_value)
@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 isinstance(result.returned_value, int) assert TYPE_MAP[type_] == type(result.returned_value)
@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 isinstance(result.returned_value, int) assert TYPE_MAP[type_] == type(result.returned_value)
@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 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 ..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,7 +4,6 @@ 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 = """
@ -45,9 +44,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) -> u64: def testEntry(f: bytes, g: 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') Suite(code_py).run_code(b'Short', b'Long' * 100)

View File

@ -2,7 +2,6 @@ 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,7 +2,6 @@ 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,7 +2,6 @@ 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,7 +4,6 @@ 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,21 +1,12 @@
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_', INT_TYPES) @pytest.mark.parametrize('type_', COMPLETE_INT_TYPES)
def test_addition_int(type_): def test_addition_int(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -29,7 +20,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_', FLOAT_TYPES) @pytest.mark.parametrize('type_', ALL_FLOAT_TYPES)
def test_addition_float(type_): def test_addition_float(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -43,7 +34,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_', INT_TYPES) @pytest.mark.parametrize('type_', COMPLETE_INT_TYPES)
def test_subtraction_int(type_): def test_subtraction_int(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -57,7 +48,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_', FLOAT_TYPES) @pytest.mark.parametrize('type_', ALL_FLOAT_TYPES)
def test_subtraction_float(type_): def test_subtraction_float(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -87,7 +78,7 @@ def testEntry() -> {type_}:
# TODO: Multiplication # TODO: Multiplication
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', INT_TYPES) @pytest.mark.parametrize('type_', COMPLETE_INT_TYPES)
def test_call_with_expression_int(type_): def test_call_with_expression_int(type_):
code_py = f""" code_py = f"""
@exported @exported
@ -104,7 +95,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_', FLOAT_TYPES) @pytest.mark.parametrize('type_', ALL_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,31 +2,132 @@ 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_static_array_index_ok(): def test_module_constant_def():
code_py = """ code_py = """
CONSTANT: u8[3] = (24, 57, 80, )
@exported @exported
def testEntry(f: u64[3]) -> u64: def testEntry() -> i32:
return f[2] 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 @pytest.mark.integration_test
def test_static_array_index_invalid_type(): @pytest.mark.parametrize('type_', ALL_INT_TYPES)
code_py = """ def test_module_constant_3(type_):
code_py = f"""
CONSTANT: {type_}[3] = (24, 57, 80, )
@exported @exported
def testEntry(f: f32[3]) -> u64: def testEntry() -> {type_}:
return f[0] return CONSTANT[1]
""" """
with pytest.raises(Type3Exception, match=r'u64 must be f32 instead'): result = Suite(code_py).run_code()
Suite(code_py).run_code((0.0, 1.5, 2.25, ))
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 @pytest.mark.integration_test
def test_module_constant_type_mismatch_not_subscriptable(): def test_module_constant_type_mismatch_not_subscriptable():

View File

@ -2,20 +2,61 @@ 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 @pytest.mark.integration_test
def test_struct_0(): def test_module_constant_def():
code_py = """ code_py = """
class CheckedValue: class SomeStruct:
value: i32 value0: u8
value1: u32
value2: u64
CONSTANT: SomeStruct = SomeStruct(250, 250000, 250000000)
@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) -> i32: def helper(cv: CheckedValue) -> {type_}:
return cv.value return cv.value
""" """
@ -63,6 +104,41 @@ 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,8 +2,51 @@ 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():
@ -22,6 +65,175 @@ 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,7 +5,6 @@ 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)