More tests and fixes
This commit is contained in:
parent
ff0286bcf6
commit
dff5feed86
@ -135,12 +135,30 @@ class SameTypeConstraint(ConstraintBase):
|
|||||||
first_type = known_types[0]
|
first_type = known_types[0]
|
||||||
for typ in known_types[1:]:
|
for typ in known_types[1:]:
|
||||||
if isinstance(first_type, types.AppliedType3) and isinstance(typ, types.AppliedType3):
|
if isinstance(first_type, types.AppliedType3) and isinstance(typ, types.AppliedType3):
|
||||||
if len(first_type.args) != len(typ.args):
|
if first_type.base is types.tuple and typ.base is types.static_array:
|
||||||
|
# Swap so we can reuse the code below
|
||||||
|
# Hope that it still gives proper type errors
|
||||||
|
first_type, typ = typ, first_type
|
||||||
|
|
||||||
|
if first_type.base is types.static_array and typ.base is types.tuple:
|
||||||
|
assert isinstance(first_type.args[1], types.IntType3)
|
||||||
|
length = first_type.args[1].value
|
||||||
|
|
||||||
|
if len(typ.args) != length:
|
||||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||||
|
|
||||||
|
for typ_arg in typ.args:
|
||||||
|
new_constraint_list.append(SameTypeConstraint(
|
||||||
|
first_type.args[0], typ_arg
|
||||||
|
))
|
||||||
|
continue
|
||||||
|
|
||||||
if first_type.base != typ.base:
|
if first_type.base != typ.base:
|
||||||
return Error('Mismatch between applied types base', comment=self.comment)
|
return Error('Mismatch between applied types base', comment=self.comment)
|
||||||
|
|
||||||
|
if len(first_type.args) != len(typ.args):
|
||||||
|
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||||
|
|
||||||
for first_type_arg, typ_arg in zip(first_type.args, typ.args):
|
for first_type_arg, typ_arg in zip(first_type.args, typ.args):
|
||||||
new_constraint_list.append(SameTypeConstraint(
|
new_constraint_list.append(SameTypeConstraint(
|
||||||
first_type_arg, typ_arg
|
first_type_arg, typ_arg
|
||||||
@ -365,11 +383,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
try:
|
try:
|
||||||
self.literal.value.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)', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return Error('Must be integer') # FIXME: Add line information
|
return Error('Must be integer', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
if self.type3.name in float_table:
|
if self.type3.name in float_table:
|
||||||
_ = float_table[self.type3.name]
|
_ = float_table[self.type3.name]
|
||||||
@ -379,13 +397,13 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return Error('Must be real') # FIXME: Add line information
|
return Error('Must be real', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
if self.type3 is types.bytes:
|
if self.type3 is types.bytes:
|
||||||
if isinstance(self.literal.value, bytes):
|
if isinstance(self.literal.value, bytes):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return Error('Must be bytes') # FIXME: Add line information
|
return Error('Must be bytes', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
res: NewConstraintList
|
res: NewConstraintList
|
||||||
|
|
||||||
|
|||||||
@ -91,6 +91,9 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
|||||||
|
|
||||||
constraint_list = new_constraint_list
|
constraint_list = new_constraint_list
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print_constraint_list(placeholder_id_map, constraint_list, placeholder_substitutes)
|
||||||
|
|
||||||
if constraint_list:
|
if constraint_list:
|
||||||
raise Exception(f'Cannot type this program - tried {MAX_RESTACK_COUNT} iterations')
|
raise Exception(f'Cannot type this program - tried {MAX_RESTACK_COUNT} iterations')
|
||||||
|
|
||||||
|
|||||||
@ -24,15 +24,11 @@ I want to receive a type error on an invalid assignment on a $TYPE module consta
|
|||||||
In order to make debugging easier
|
In order to make debugging easier
|
||||||
|
|
||||||
```py
|
```py
|
||||||
CONSTANT: $OTHER_TYPE = $VAL0
|
CONSTANT: (u32, ) = $VAL0
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return 0
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
if TYPE_NAME.startswith('tuple_'):
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
'Tuple element count mismatch',
|
'Tuple element count mismatch',
|
||||||
'The given literal must fit the expected type',
|
'The given literal must fit the expected type',
|
||||||
@ -43,3 +39,263 @@ else:
|
|||||||
'The given literal must fit the expected type',
|
'The given literal must fit the expected type',
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# function_result_is_literal_ok
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to use return a literal from a function
|
||||||
|
In order to define constants in a more dynamic way
|
||||||
|
|
||||||
|
```py
|
||||||
|
def drop_arg_return_9(x: $TYPE) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
def constant() -> $TYPE:
|
||||||
|
return $VAL0
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return drop_arg_return_9(constant())
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
expect(9)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_result_is_literal_bad
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to receive a type error when returning a $TYPE literal for a function that doesn't return that type
|
||||||
|
In order to make debugging easier
|
||||||
|
|
||||||
|
```py
|
||||||
|
def drop_arg_return_9(x: (u32, )) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
def constant() -> (u32, ):
|
||||||
|
return $VAL0
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return drop_arg_return_9(constant())
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
||||||
|
expect_type_error(
|
||||||
|
'Mismatch between applied types argument count',
|
||||||
|
'The type of the value returned from function constant should match its return type',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
expect_type_error(
|
||||||
|
'Must be tuple',
|
||||||
|
'The given literal must fit the expected type',
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_result_is_module_constant_ok
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to use return a $TYPE module constant from a function
|
||||||
|
In order to use my module constants in return statements
|
||||||
|
|
||||||
|
```py
|
||||||
|
CONSTANT: $TYPE = $VAL0
|
||||||
|
|
||||||
|
def helper(x: $TYPE) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
def constant() -> $TYPE:
|
||||||
|
return CONSTANT
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return helper(constant())
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
expect(9)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_result_is_module_constant_bad
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to receive a type error when returning a $TYPE module constant for a function that doesn't return that type
|
||||||
|
In order to make debugging easier
|
||||||
|
|
||||||
|
```py
|
||||||
|
CONSTANT: $TYPE = $VAL0
|
||||||
|
|
||||||
|
def drop_arg_return_9(x: (u32, )) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
def constant() -> (u32, ):
|
||||||
|
return CONSTANT
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return drop_arg_return_9(constant())
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
||||||
|
expect_type_error(
|
||||||
|
'Mismatch between applied types argument count',
|
||||||
|
'The type of the value returned from function constant should match its return type',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
expect_type_error(
|
||||||
|
TYPE_NAME + ' must be tuple (u32) instead',
|
||||||
|
'The type of the value returned from function constant should match its return type',
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_result_is_arg_ok
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to use return a $TYPE function argument
|
||||||
|
In order to make it possible to select a value using a function
|
||||||
|
|
||||||
|
```py
|
||||||
|
CONSTANT: $TYPE = $VAL0
|
||||||
|
|
||||||
|
def drop_arg_return_9(x: $TYPE) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
def select(x: $TYPE) -> $TYPE:
|
||||||
|
return x
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return drop_arg_return_9(select(CONSTANT))
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
expect(9)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_result_is_arg_bad
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to receive a type error when returning a $TYPE argument for a function that doesn't return that type
|
||||||
|
In order to make debugging easier
|
||||||
|
|
||||||
|
```py
|
||||||
|
def drop_arg_return_9(x: (u32, )) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
def select(x: $TYPE) -> (u32, ):
|
||||||
|
return x
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
||||||
|
expect_type_error(
|
||||||
|
'Mismatch between applied types argument count',
|
||||||
|
'The type of the value returned from function select should match its return type',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
expect_type_error(
|
||||||
|
TYPE_NAME + ' must be tuple (u32) instead',
|
||||||
|
'The type of the value returned from function select should match its return type',
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_arg_literal_ok
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to use a $TYPE literal by passing it to a function
|
||||||
|
In order to use a pre-existing function with the values I specify
|
||||||
|
|
||||||
|
```py
|
||||||
|
def helper(x: $TYPE) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return helper($VAL0)
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
expect(9)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_arg_literal_bad
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to receive a type error when passing a $TYPE literal to a function that does not accept it
|
||||||
|
In order to make debugging easier
|
||||||
|
|
||||||
|
```py
|
||||||
|
def helper(x: (u32, )) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return helper($VAL0)
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
||||||
|
expect_type_error(
|
||||||
|
'Mismatch between applied types argument count',
|
||||||
|
# FIXME: Shouldn't this be the same as for the else statement?
|
||||||
|
'The type of the value passed to argument x of function helper should match the type of that argument',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
expect_type_error(
|
||||||
|
'Must be tuple',
|
||||||
|
'The given literal must fit the expected type',
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_arg_module_constant_def_ok
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to use a $TYPE module constant by passing it to a function
|
||||||
|
In order to use my defined value with a pre-existing function
|
||||||
|
|
||||||
|
```py
|
||||||
|
CONSTANT: $TYPE = $VAL0
|
||||||
|
|
||||||
|
def helper(x: $TYPE) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return helper(CONSTANT)
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
expect(9)
|
||||||
|
```
|
||||||
|
|
||||||
|
# function_arg_module_constant_def_bad
|
||||||
|
|
||||||
|
As a developer
|
||||||
|
I want to receive a type error when passing a $TYPE module constant to a function that does not accept it
|
||||||
|
In order to make debugging easier
|
||||||
|
|
||||||
|
```py
|
||||||
|
CONSTANT: $TYPE = $VAL0
|
||||||
|
|
||||||
|
def helper(x: (u32, )) -> i32:
|
||||||
|
return 9
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> i32:
|
||||||
|
return helper(CONSTANT)
|
||||||
|
```
|
||||||
|
|
||||||
|
```py
|
||||||
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
||||||
|
expect_type_error(
|
||||||
|
'Mismatch between applied types argument count',
|
||||||
|
'The type of the value passed to argument x of function helper should match the type of that argument',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
expect_type_error(
|
||||||
|
TYPE_NAME + ' must be tuple (u32) instead',
|
||||||
|
'The type of the value passed to argument x of function helper should match the type of that argument',
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|||||||
@ -110,8 +110,6 @@ def main():
|
|||||||
if 'TYPE_NAME' not in settings:
|
if 'TYPE_NAME' not in settings:
|
||||||
settings['TYPE_NAME'] = settings['TYPE']
|
settings['TYPE_NAME'] = settings['TYPE']
|
||||||
|
|
||||||
settings['OTHER_TYPE'] = '(u32, )'
|
|
||||||
|
|
||||||
generate_code(markdown, template, settings)
|
generate_code(markdown, template, settings)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
4
tests/integration/test_lang/generator_bytes.json
Normal file
4
tests/integration/test_lang/generator_bytes.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"TYPE": "bytes",
|
||||||
|
"VAL0": "b'ABCDEFG'"
|
||||||
|
}
|
||||||
4
tests/integration/test_lang/generator_f32.json
Normal file
4
tests/integration/test_lang/generator_f32.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"TYPE": "f32",
|
||||||
|
"VAL0": "1000000.125"
|
||||||
|
}
|
||||||
4
tests/integration/test_lang/generator_f64.json
Normal file
4
tests/integration/test_lang/generator_f64.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"TYPE": "f64",
|
||||||
|
"VAL0": "1000000.125"
|
||||||
|
}
|
||||||
4
tests/integration/test_lang/generator_i32.json
Normal file
4
tests/integration/test_lang/generator_i32.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"TYPE": "i32",
|
||||||
|
"VAL0": "1000000"
|
||||||
|
}
|
||||||
4
tests/integration/test_lang/generator_i64.json
Normal file
4
tests/integration/test_lang/generator_i64.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"TYPE": "i64",
|
||||||
|
"VAL0": "1000000"
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"TYPE_NAME": "static_array_u64_32",
|
||||||
|
"TYPE": "u64[32]",
|
||||||
|
"VAL0": "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, )"
|
||||||
|
}
|
||||||
4
tests/integration/test_lang/generator_u64.json
Normal file
4
tests/integration/test_lang/generator_u64.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"TYPE": "u64",
|
||||||
|
"VAL0": "1000000"
|
||||||
|
}
|
||||||
@ -44,49 +44,6 @@ def testEntry() -> u8:
|
|||||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
|
|
||||||
def test_module_constant_int(type_):
|
|
||||||
code_py = f"""
|
|
||||||
CONSTANT: {type_} = 13
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> {type_}:
|
|
||||||
return CONSTANT
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert 13 == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
@pytest.mark.parametrize('type_', ALL_FLOAT_TYPES)
|
|
||||||
def test_module_constant_float(type_):
|
|
||||||
code_py = f"""
|
|
||||||
CONSTANT: {type_} = 32.125
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> {type_}:
|
|
||||||
return CONSTANT
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert 32.125 == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_module_constant_type_failure():
|
|
||||||
code_py = """
|
|
||||||
CONSTANT: u8 = 1000
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> u32:
|
|
||||||
return 14
|
|
||||||
"""
|
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
|
||||||
Suite(code_py).run_code()
|
|
||||||
|
|
||||||
@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
|
||||||
def test_logical_left_shift(type_):
|
def test_logical_left_shift(type_):
|
||||||
@ -338,20 +295,6 @@ def testEntry() -> {type_}:
|
|||||||
assert 5 == result.returned_value
|
assert 5 == result.returned_value
|
||||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
assert TYPE_MAP[type_] == type(result.returned_value)
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
@pytest.mark.parametrize('type_', TYPE_MAP.keys())
|
|
||||||
def test_function_argument(type_):
|
|
||||||
code_py = f"""
|
|
||||||
@exported
|
|
||||||
def testEntry(a: {type_}) -> {type_}:
|
|
||||||
return a
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code(125)
|
|
||||||
|
|
||||||
assert 125 == result.returned_value
|
|
||||||
assert TYPE_MAP[type_] == type(result.returned_value)
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.skip('TODO')
|
@pytest.mark.skip('TODO')
|
||||||
def test_explicit_positive_number():
|
def test_explicit_positive_number():
|
||||||
@ -378,21 +321,6 @@ def testEntry() -> i32:
|
|||||||
|
|
||||||
assert -19 == result.returned_value
|
assert -19 == result.returned_value
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_call_no_args():
|
|
||||||
code_py = """
|
|
||||||
def helper() -> i32:
|
|
||||||
return 19
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> i32:
|
|
||||||
return helper()
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert 19 == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
def test_call_pre_defined():
|
def test_call_pre_defined():
|
||||||
code_py = """
|
code_py = """
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user