Constraints can now explode into more constraints
What could possibly go wrong. All struct tests seem to pass.
This commit is contained in:
parent
7ce3f0f11c
commit
d4d5e9e482
@ -6,6 +6,7 @@ It's intented to be a "any color, as long as it's black" kind of renderer
|
|||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
from . import ourlang
|
from . import ourlang
|
||||||
|
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:
|
||||||
@ -34,6 +35,12 @@ def type3(inp: Type3OrPlaceholder) -> str:
|
|||||||
"""
|
"""
|
||||||
assert isinstance(inp, Type3), TYPE3_ASSERTION_ERROR
|
assert isinstance(inp, Type3), TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
|
if isinstance(inp, type3types.AppliedType3) and inp.base is type3types.tuple:
|
||||||
|
return '(' + ', '.join(
|
||||||
|
type3(x)
|
||||||
|
for x in inp.args
|
||||||
|
) + ', )'
|
||||||
|
|
||||||
return inp.name
|
return inp.name
|
||||||
|
|
||||||
def struct_definition(inp: ourlang.StructDefinition) -> str:
|
def struct_definition(inp: ourlang.StructDefinition) -> str:
|
||||||
|
|||||||
@ -186,6 +186,12 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||||
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
|
if isinstance(inp.type3, type3types.StructType3):
|
||||||
|
assert inp.variable.data_block is not None, 'Structs must be memory stored'
|
||||||
|
assert inp.variable.data_block.address is not None, 'Value not allocated'
|
||||||
|
wgn.i32.const(inp.variable.data_block.address)
|
||||||
|
return
|
||||||
|
|
||||||
# TODO: Broken after new type system
|
# TODO: Broken after new type system
|
||||||
# if isinstance(inp.type, typing.TypeTuple):
|
# if isinstance(inp.type, typing.TypeTuple):
|
||||||
# assert isinstance(inp.definition.constant, ourlang.ConstantTuple)
|
# assert isinstance(inp.definition.constant, ourlang.ConstantTuple)
|
||||||
@ -634,7 +640,7 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
|
|||||||
data_list: List[bytes] = []
|
data_list: List[bytes] = []
|
||||||
|
|
||||||
for constant in block.data:
|
for constant in block.data:
|
||||||
assert constant.type3 is not None
|
assert isinstance(constant.type3, type3types.Type3), (id(constant), type3types.TYPE3_ASSERTION_ERROR)
|
||||||
|
|
||||||
if constant.type3 is type3types.u8:
|
if constant.type3 is type3types.u8:
|
||||||
assert isinstance(constant.value, int)
|
assert isinstance(constant.value, int)
|
||||||
|
|||||||
@ -33,7 +33,9 @@ class RequireTypeSubstitutes:
|
|||||||
|
|
||||||
SubstitutionMap = Dict[types.PlaceholderForType, types.Type3]
|
SubstitutionMap = Dict[types.PlaceholderForType, types.Type3]
|
||||||
|
|
||||||
CheckResult = Union[None, SubstitutionMap, Error, RequireTypeSubstitutes]
|
NewConstraintList = List['ConstraintBase']
|
||||||
|
|
||||||
|
CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes]
|
||||||
|
|
||||||
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, types.PlaceholderForType]]]
|
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, types.PlaceholderForType]]]
|
||||||
|
|
||||||
@ -231,21 +233,18 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
'f64': None,
|
'f64': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _check(type3: types.Type3OrPlaceholder, literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantTuple, ourlang.ConstantStruct]) -> CheckResult:
|
if isinstance(self.type3, types.PlaceholderForType):
|
||||||
if isinstance(type3, types.PlaceholderForType):
|
if self.type3 not in smap:
|
||||||
if type3 not in smap:
|
|
||||||
return RequireTypeSubstitutes()
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
type3 = smap[type3]
|
self.type3 = smap[self.type3]
|
||||||
|
|
||||||
val = literal.value
|
if self.type3.name in int_table:
|
||||||
|
bts, sgn = int_table[self.type3.name]
|
||||||
|
|
||||||
if type3.name in int_table:
|
if isinstance(self.literal.value, int):
|
||||||
bts, sgn = int_table[type3.name]
|
|
||||||
|
|
||||||
if isinstance(val, int):
|
|
||||||
try:
|
try:
|
||||||
val.to_bytes(bts, 'big', signed=sgn)
|
self.literal.value.to_bytes(bts, 'big', signed=sgn)
|
||||||
except OverflowError:
|
except OverflowError:
|
||||||
return Error(f'Must fit in {bts} byte(s)') # FIXME: Add line information
|
return Error(f'Must fit in {bts} byte(s)') # FIXME: Add line information
|
||||||
|
|
||||||
@ -253,52 +252,63 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return Error('Must be integer') # FIXME: Add line information
|
return Error('Must be integer') # FIXME: Add line information
|
||||||
|
|
||||||
|
if self.type3.name in float_table:
|
||||||
|
_ = float_table[self.type3.name]
|
||||||
|
|
||||||
if type3.name in float_table:
|
if isinstance(self.literal.value, float):
|
||||||
_ = float_table[type3.name]
|
|
||||||
|
|
||||||
if isinstance(val, float):
|
|
||||||
# FIXME: Bit check
|
# FIXME: Bit check
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return Error('Must be real') # FIXME: Add line information
|
return Error('Must be real') # FIXME: Add line information
|
||||||
|
|
||||||
if isinstance(type3, types.AppliedType3) and type3.base is types.tuple:
|
res: NewConstraintList
|
||||||
if not isinstance(literal, ourlang.ConstantTuple):
|
|
||||||
|
if isinstance(self.type3, types.AppliedType3) and self.type3.base is types.tuple:
|
||||||
|
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||||
return Error('Must be tuple')
|
return Error('Must be tuple')
|
||||||
|
|
||||||
assert isinstance(val, list) # type hint
|
if len(self.type3.args) != len(self.literal.value):
|
||||||
|
|
||||||
if len(type3.args) != len(val):
|
|
||||||
return Error('Tuple element count mismatch')
|
return Error('Tuple element count mismatch')
|
||||||
|
|
||||||
for elt_typ, elt_lit in zip(type3.args, val):
|
res = []
|
||||||
res = _check(elt_typ, elt_lit)
|
|
||||||
if res is not None:
|
res.extend(
|
||||||
|
LiteralFitsConstraint(x, y)
|
||||||
|
for x, y in zip(self.type3.args, self.literal.value)
|
||||||
|
)
|
||||||
|
res.extend(
|
||||||
|
SameTypeConstraint(x, y.type3)
|
||||||
|
for x, y in zip(self.type3.args, self.literal.value)
|
||||||
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
return None
|
if isinstance(self.type3, types.StructType3):
|
||||||
|
if not isinstance(self.literal, ourlang.ConstantStruct):
|
||||||
if isinstance(type3, types.StructType3):
|
|
||||||
if not isinstance(literal, ourlang.ConstantStruct):
|
|
||||||
return Error('Must be struct')
|
return Error('Must be struct')
|
||||||
|
|
||||||
assert isinstance(val, list) # type hint
|
if self.literal.struct_name != self.type3.name:
|
||||||
|
return Error('Struct mismatch')
|
||||||
|
|
||||||
if len(type3.members) != len(val):
|
|
||||||
|
if len(self.type3.members) != len(self.literal.value):
|
||||||
return Error('Struct element count mismatch')
|
return Error('Struct element count mismatch')
|
||||||
|
|
||||||
for elt_typ, elt_lit in zip(type3.members.values(), val):
|
res = []
|
||||||
res = _check(elt_typ, elt_lit)
|
|
||||||
if res is not None:
|
res.extend(
|
||||||
|
LiteralFitsConstraint(x, y)
|
||||||
|
for x, y in zip(self.type3.members.values(), self.literal.value)
|
||||||
|
)
|
||||||
|
res.extend(
|
||||||
|
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
|
||||||
|
for (x_n, x_t, ), y in zip(self.type3.members.items(), self.literal.value)
|
||||||
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
return None
|
raise NotImplementedError(self.type3, self.literal)
|
||||||
|
|
||||||
raise NotImplementedError(type3, literal)
|
|
||||||
|
|
||||||
return _check(self.type3, self.literal)
|
|
||||||
|
|
||||||
def human_readable(self) -> HumanReadableRet:
|
def human_readable(self) -> HumanReadableRet:
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -107,6 +107,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas
|
|||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.AccessStructMember):
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
|
yield from expression(ctx, inp.varref)
|
||||||
yield SameTypeConstraint(inp.struct_type3.members[inp.member], inp.type3,
|
yield SameTypeConstraint(inp.struct_type3.members[inp.member], inp.type3,
|
||||||
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||||
return
|
return
|
||||||
|
|||||||
@ -65,6 +65,14 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
|||||||
print('-> Back on the todo list')
|
print('-> Back on the todo list')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if isinstance(check_result, list):
|
||||||
|
new_constraint_list.extend(check_result)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print_constraint(placeholder_id_map, constraint)
|
||||||
|
print(f'-> Resulted in {len(check_result)} new constraints')
|
||||||
|
continue
|
||||||
|
|
||||||
raise NotImplementedError(constraint, check_result)
|
raise NotImplementedError(constraint, check_result)
|
||||||
|
|
||||||
if not new_constraint_list:
|
if not new_constraint_list:
|
||||||
|
|||||||
@ -7,6 +7,25 @@ from ..constants import (
|
|||||||
)
|
)
|
||||||
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)
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 0 == result.returned_value
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
||||||
def test_module_constant(type_):
|
def test_module_constant(type_):
|
||||||
@ -85,6 +104,29 @@ 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
|
@pytest.mark.integration_test
|
||||||
def test_type_mismatch_arg_module_constant():
|
def test_type_mismatch_arg_module_constant():
|
||||||
code_py = """
|
code_py = """
|
||||||
@ -94,7 +136,7 @@ class Struct:
|
|||||||
STRUCT: Struct = Struct(1)
|
STRUCT: Struct = Struct(1)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match='todo'):
|
with pytest.raises(Type3Exception, match='Must be real'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
|
|||||||
@ -5,6 +5,20 @@ from phasm.type3.entry import Type3Exception
|
|||||||
from ..constants import COMPLETE_NUMERIC_TYPES, TYPE_MAP
|
from ..constants import COMPLETE_NUMERIC_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.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64', ])
|
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64', ])
|
||||||
def test_module_constant_1(type_):
|
def test_module_constant_1(type_):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user