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]
|
||||
for typ in known_types[1:]:
|
||||
if isinstance(first_type, types.AppliedType3) and isinstance(typ, types.AppliedType3):
|
||||
if len(first_type.args) != len(typ.args):
|
||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||
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)
|
||||
|
||||
for typ_arg in typ.args:
|
||||
new_constraint_list.append(SameTypeConstraint(
|
||||
first_type.args[0], typ_arg
|
||||
))
|
||||
continue
|
||||
|
||||
if first_type.base != typ.base:
|
||||
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):
|
||||
new_constraint_list.append(SameTypeConstraint(
|
||||
first_type_arg, typ_arg
|
||||
@ -365,11 +383,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
try:
|
||||
self.literal.value.to_bytes(bts, 'big', signed=sgn)
|
||||
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 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:
|
||||
_ = float_table[self.type3.name]
|
||||
@ -379,13 +397,13 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
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 isinstance(self.literal.value, bytes):
|
||||
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
|
||||
|
||||
|
||||
@ -91,6 +91,9 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
||||
|
||||
constraint_list = new_constraint_list
|
||||
|
||||
if verbose:
|
||||
print_constraint_list(placeholder_id_map, constraint_list, placeholder_substitutes)
|
||||
|
||||
if constraint_list:
|
||||
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
|
||||
|
||||
```py
|
||||
CONSTANT: $OTHER_TYPE = $VAL0
|
||||
|
||||
@exported
|
||||
def testEntry() -> i32:
|
||||
return 0
|
||||
CONSTANT: (u32, ) = $VAL0
|
||||
```
|
||||
|
||||
```py
|
||||
if TYPE_NAME.startswith('tuple_'):
|
||||
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
||||
expect_type_error(
|
||||
'Tuple element count mismatch',
|
||||
'The given literal must fit the expected type',
|
||||
@ -43,3 +39,263 @@ else:
|
||||
'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:
|
||||
settings['TYPE_NAME'] = settings['TYPE']
|
||||
|
||||
settings['OTHER_TYPE'] = '(u32, )'
|
||||
|
||||
generate_code(markdown, template, settings)
|
||||
|
||||
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\)'):
|
||||
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.parametrize('type_', ['u32', 'u64']) # FIXME: Support u8, requires an extra AND operation
|
||||
def test_logical_left_shift(type_):
|
||||
@ -338,20 +295,6 @@ def testEntry() -> {type_}:
|
||||
assert 5 == 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.skip('TODO')
|
||||
def test_explicit_positive_number():
|
||||
@ -378,21 +321,6 @@ def testEntry() -> i32:
|
||||
|
||||
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
|
||||
def test_call_pre_defined():
|
||||
code_py = """
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user