Fixes
- Tuple wasn't an applied type yet - phasm_type3 would re-order type IDs between prints - AppliedType3 wouldn't store the args for iterators -
This commit is contained in:
parent
3bac625714
commit
30a4cee5af
3
TODO.md
3
TODO.md
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
- Implement a proper type matching / checking system
|
- Implement a proper type matching / checking system
|
||||||
- Implement subscript as an operator
|
- Implement subscript as an operator
|
||||||
- Implement another operator
|
- Re-implement Subscript contraints - Doing the LiteralFitsConstraint with a tuple doesn't put the types on the tuple elements
|
||||||
- Figure out how to do type classes
|
|
||||||
- Implement structs again, with the `.foo` notation working
|
- Implement structs again, with the `.foo` notation working
|
||||||
|
|
||||||
- Rename constant to literal
|
- Rename constant to literal
|
||||||
|
|||||||
@ -59,6 +59,9 @@ class ConstantTuple(Constant):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'ConstantTuple({repr(self.value)})'
|
||||||
|
|
||||||
class VariableReference(Expression):
|
class VariableReference(Expression):
|
||||||
"""
|
"""
|
||||||
An variable reference expression within a statement
|
An variable reference expression within a statement
|
||||||
|
|||||||
@ -714,13 +714,11 @@ class OurVisitor:
|
|||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
_raise_static_error(node, 'Must be load context')
|
_raise_static_error(node, 'Must be load context')
|
||||||
|
|
||||||
elements = ', '.join(
|
return type3types.AppliedType3(
|
||||||
self.visit_type(module, elt).name
|
type3types.tuple,
|
||||||
for elt in node.elts
|
(self.visit_type(module, elt) for elt in node.elts)
|
||||||
)
|
)
|
||||||
|
|
||||||
return type3types.Type3(f'({elements}, )')
|
|
||||||
|
|
||||||
raise NotImplementedError(f'{node} as type')
|
raise NotImplementedError(f'{node} as type')
|
||||||
|
|
||||||
def _not_implemented(check: Any, msg: str) -> None:
|
def _not_implemented(check: Any, msg: str) -> None:
|
||||||
|
|||||||
@ -178,18 +178,13 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
__slots__ = ('type3', 'literal', )
|
__slots__ = ('type3', 'literal', )
|
||||||
|
|
||||||
type3: types.Type3OrPlaceholder
|
type3: types.Type3OrPlaceholder
|
||||||
literal: ourlang.ConstantPrimitive
|
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantTuple]
|
||||||
|
|
||||||
def __init__(self, type3: types.Type3OrPlaceholder, literal: ourlang.ConstantPrimitive) -> None:
|
def __init__(self, type3: types.Type3OrPlaceholder, literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantTuple]) -> None:
|
||||||
self.type3 = type3
|
self.type3 = type3
|
||||||
self.literal = literal
|
self.literal = literal
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
if isinstance(self.type3, types.PlaceholderForType):
|
|
||||||
return RequireTypeSubstitutes()
|
|
||||||
|
|
||||||
val = self.literal.value
|
|
||||||
|
|
||||||
int_table: Dict[str, Tuple[int, bool]] = {
|
int_table: Dict[str, Tuple[int, bool]] = {
|
||||||
'u8': (1, False),
|
'u8': (1, False),
|
||||||
'u32': (4, False),
|
'u32': (4, False),
|
||||||
@ -199,8 +194,19 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
'i64': (8, True),
|
'i64': (8, True),
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.type3.name in int_table:
|
float_table: Dict[str, None] = {
|
||||||
bts, sgn = int_table[self.type3.name]
|
'f32': None,
|
||||||
|
'f64': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _check(type3: types.Type3OrPlaceholder, literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantTuple]) -> CheckResult:
|
||||||
|
if isinstance(type3, types.PlaceholderForType):
|
||||||
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
|
val = literal.value
|
||||||
|
|
||||||
|
if type3.name in int_table:
|
||||||
|
bts, sgn = int_table[type3.name]
|
||||||
|
|
||||||
if isinstance(val, int):
|
if isinstance(val, int):
|
||||||
try:
|
try:
|
||||||
@ -212,13 +218,9 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return Error('Must be integer') # FIXME: Add line information
|
return Error('Must be integer') # FIXME: Add line information
|
||||||
|
|
||||||
float_table: Dict[str, None] = {
|
|
||||||
'f32': None,
|
|
||||||
'f64': None,
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.type3.name in float_table:
|
if type3.name in float_table:
|
||||||
_ = float_table[self.type3.name]
|
_ = float_table[type3.name]
|
||||||
|
|
||||||
if isinstance(val, float):
|
if isinstance(val, float):
|
||||||
# FIXME: Bit check
|
# FIXME: Bit check
|
||||||
@ -227,8 +229,26 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
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:
|
||||||
|
if not isinstance(literal, ourlang.ConstantTuple):
|
||||||
|
return Error('Must be tuple')
|
||||||
|
|
||||||
|
assert isinstance(val, list) # type hint
|
||||||
|
|
||||||
|
if len(type3.args) != len(val):
|
||||||
|
return Error('Tuple element count mismatch')
|
||||||
|
|
||||||
|
for elt_typ, elt_lit in zip(type3.args, val):
|
||||||
|
res = _check(elt_typ, elt_lit)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
return _check(self.type3, self.literal)
|
||||||
|
|
||||||
def substitute_placeholders(self, smap: SubstitutionMap) -> None: # FIXME: Duplicate code
|
def substitute_placeholders(self, smap: SubstitutionMap) -> None: # FIXME: Duplicate code
|
||||||
if isinstance(self.type3, types.PlaceholderForType) and self.type3 in smap: # FIXME: Check recursive?
|
if isinstance(self.type3, types.PlaceholderForType) and self.type3 in smap: # FIXME: Check recursive?
|
||||||
self.type3.get_substituted(smap[self.type3])
|
self.type3.get_substituted(smap[self.type3])
|
||||||
@ -245,3 +265,62 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'LiteralFitsConstraint({repr(self.type3)}, {repr(self.literal)})'
|
return f'LiteralFitsConstraint({repr(self.type3)}, {repr(self.literal)})'
|
||||||
|
|
||||||
|
class CanBeSubscriptedConstraint(ConstraintBase):
|
||||||
|
"""
|
||||||
|
A value that is subscipted, i.e. a[0] (tuple) or a[b] (static array)
|
||||||
|
"""
|
||||||
|
__slots__ = ('type3', 'index', 'index_type3', )
|
||||||
|
|
||||||
|
type3: types.Type3OrPlaceholder
|
||||||
|
index: ourlang.Expression
|
||||||
|
index_type3: types.Type3OrPlaceholder
|
||||||
|
|
||||||
|
def __init__(self, type3: types.Type3OrPlaceholder, index: ourlang.Expression) -> None:
|
||||||
|
self.type3 = type3
|
||||||
|
self.index = index
|
||||||
|
self.index_type3 = index.type3
|
||||||
|
|
||||||
|
def check(self) -> CheckResult:
|
||||||
|
if isinstance(self.type3, types.PlaceholderForType):
|
||||||
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
|
if isinstance(self.index_type3, types.PlaceholderForType):
|
||||||
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
|
if not isinstance(self.type3, types.AppliedType3):
|
||||||
|
return Error(f'Cannot subscript {self.type3:s}')
|
||||||
|
|
||||||
|
if self.type3.base is types.tuple:
|
||||||
|
return None
|
||||||
|
|
||||||
|
raise NotImplementedError(self.type3)
|
||||||
|
|
||||||
|
def get_new_placeholder_substitutes(self) -> SubstitutionMap:
|
||||||
|
if isinstance(self.type3, types.AppliedType3) and self.type3.base is types.tuple and isinstance(self.index_type3, types.PlaceholderForType):
|
||||||
|
return {
|
||||||
|
self.index_type3: types.u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def substitute_placeholders(self, smap: SubstitutionMap) -> None: # FIXME: Duplicate code
|
||||||
|
if isinstance(self.type3, types.PlaceholderForType) and self.type3 in smap: # FIXME: Check recursive?
|
||||||
|
self.type3.get_substituted(smap[self.type3])
|
||||||
|
self.type3 = smap[self.type3]
|
||||||
|
|
||||||
|
if isinstance(self.index_type3, types.PlaceholderForType) and self.index_type3 in smap: # FIXME: Check recursive?
|
||||||
|
self.index_type3.get_substituted(smap[self.index_type3])
|
||||||
|
self.index_type3 = smap[self.index_type3]
|
||||||
|
|
||||||
|
def human_readable(self) -> HumanReadableRet:
|
||||||
|
return (
|
||||||
|
'{type3}[{index}]',
|
||||||
|
{
|
||||||
|
'type3': self.type3,
|
||||||
|
'index': self.index,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'CanBeSubscriptedConstraint({repr(self.type3)}, {repr(self.index)})'
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from .constraints import (
|
|||||||
Context,
|
Context,
|
||||||
|
|
||||||
ConstraintBase,
|
ConstraintBase,
|
||||||
|
CanBeSubscriptedConstraint,
|
||||||
LiteralFitsConstraint, MustImplementTypeClassConstraint, SameTypeConstraint,
|
LiteralFitsConstraint, MustImplementTypeClassConstraint, SameTypeConstraint,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
|
|||||||
return [*module(ctx, inp)]
|
return [*module(ctx, inp)]
|
||||||
|
|
||||||
def constant(ctx: Context, inp: ourlang.Constant) -> Generator[ConstraintBase, None, None]:
|
def constant(ctx: Context, inp: ourlang.Constant) -> Generator[ConstraintBase, None, None]:
|
||||||
if isinstance(inp, ourlang.ConstantPrimitive):
|
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantTuple, )):
|
||||||
yield LiteralFitsConstraint(inp.type3, inp)
|
yield LiteralFitsConstraint(inp.type3, inp)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -59,6 +60,13 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas
|
|||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.Subscript):
|
||||||
|
yield from expression(ctx, inp.varref)
|
||||||
|
yield from expression(ctx, inp.index)
|
||||||
|
|
||||||
|
yield CanBeSubscriptedConstraint(inp.varref.type3, inp.index)
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.AccessStructMember):
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
yield SameTypeConstraint(inp.struct_type3.members[inp.member], inp.type3,
|
yield SameTypeConstraint(inp.struct_type3.members[inp.member], inp.type3,
|
||||||
f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||||
|
|||||||
@ -22,6 +22,7 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
|||||||
assert constraint_list
|
assert constraint_list
|
||||||
|
|
||||||
placeholder_substitutes: Dict[PlaceholderForType, Type3] = {}
|
placeholder_substitutes: Dict[PlaceholderForType, Type3] = {}
|
||||||
|
placeholder_id_map: Dict[int, str] = {}
|
||||||
|
|
||||||
restack_counter = 0
|
restack_counter = 0
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
|||||||
while constraint_list:
|
while constraint_list:
|
||||||
if verbose:
|
if verbose:
|
||||||
print()
|
print()
|
||||||
print_constraint_list(constraint_list, placeholder_substitutes)
|
print_constraint_list(placeholder_id_map, constraint_list, placeholder_substitutes)
|
||||||
|
|
||||||
constraint = constraint_list.pop(0)
|
constraint = constraint_list.pop(0)
|
||||||
|
|
||||||
@ -92,9 +93,7 @@ def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintB
|
|||||||
else:
|
else:
|
||||||
print('- ' + txt.format(**act_fmt))
|
print('- ' + txt.format(**act_fmt))
|
||||||
|
|
||||||
def print_constraint_list(constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None:
|
def print_constraint_list(placeholder_id_map: Dict[int, str], constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None:
|
||||||
placeholder_id_map: Dict[int, str] = {}
|
|
||||||
|
|
||||||
print('=== v type3 constraint_list v === ')
|
print('=== v type3 constraint_list v === ')
|
||||||
for psk, psv in placeholder_substitutes.items():
|
for psk, psv in placeholder_substitutes.items():
|
||||||
print_constraint(placeholder_id_map, SameTypeConstraint(psk, psv, 'Deduced type'))
|
print_constraint(placeholder_id_map, SameTypeConstraint(psk, psv, 'Deduced type'))
|
||||||
|
|||||||
@ -130,6 +130,8 @@ class AppliedType3(Type3):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, base: Type3, args: Iterable[Type3OrPlaceholder]) -> None:
|
def __init__(self, base: Type3, args: Iterable[Type3OrPlaceholder]) -> None:
|
||||||
|
args = [*args]
|
||||||
|
|
||||||
super().__init__(
|
super().__init__(
|
||||||
base.name
|
base.name
|
||||||
+ ' ('
|
+ ' ('
|
||||||
@ -138,7 +140,7 @@ class AppliedType3(Type3):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.base = base
|
self.base = base
|
||||||
self.args = [*args]
|
self.args = args
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
|
return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
|
||||||
@ -232,7 +234,16 @@ static_array = Type3('static_array')
|
|||||||
"""
|
"""
|
||||||
This is a fixed length piece of memory that can be indexed at runtime.
|
This is a fixed length piece of memory that can be indexed at runtime.
|
||||||
|
|
||||||
It should be applied with one argument.
|
It should be applied with one argument. It has a runtime-dynamic length
|
||||||
|
of the same type repeated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tuple = Type3('tuple') # pylint: disable=W0622
|
||||||
|
"""
|
||||||
|
This is a fixed length piece of memory.
|
||||||
|
|
||||||
|
It should be applied with zero or more arguments. It has a compile time
|
||||||
|
determined length, and each argument can be different.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
LOOKUP_TABLE: Dict[str, Type3] = {
|
LOOKUP_TABLE: Dict[str, Type3] = {
|
||||||
|
|||||||
@ -167,7 +167,6 @@ def testEntry() -> u64:
|
|||||||
|
|
||||||
with pytest.raises(Type3Exception, match='u64 must be u32 instead'):
|
with pytest.raises(Type3Exception, match='u64 must be u32 instead'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
assert False
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64'])
|
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64'])
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
@ -11,10 +13,7 @@ CONSTANT: ({type_}, ) = (65, )
|
|||||||
|
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> {type_}:
|
def testEntry() -> {type_}:
|
||||||
return helper(CONSTANT)
|
return CONSTANT[0]
|
||||||
|
|
||||||
def helper(vector: ({type_}, )) -> {type_}:
|
|
||||||
return vector[0]
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
result = Suite(code_py).run_code()
|
||||||
@ -28,10 +27,7 @@ CONSTANT: (u8, u8, u32, u32, u64, u64, ) = (11, 22, 3333, 4444, 555555, 666666,
|
|||||||
|
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> u32:
|
def testEntry() -> u32:
|
||||||
return helper(CONSTANT)
|
return CONSTANT[2]
|
||||||
|
|
||||||
def helper(vector: (u8, u8, u32, u32, u64, u64, )) -> u32:
|
|
||||||
return vector[2]
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
result = Suite(code_py).run_code()
|
||||||
@ -83,13 +79,22 @@ def testEntry() -> i32x4:
|
|||||||
|
|
||||||
assert (1, 2, 3, 0) == result.returned_value
|
assert (1, 2, 3, 0) == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_assign_to_tuple_with_tuple():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: (u32, ) = 0
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(Type3Exception, match='Must be tuple'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
def test_tuple_constant_too_few_values():
|
def test_tuple_constant_too_few_values():
|
||||||
code_py = """
|
code_py = """
|
||||||
CONSTANT: (u32, u8, u8, ) = (24, 57, )
|
CONSTANT: (u32, u8, u8, ) = (24, 57, )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
|
with pytest.raises(Type3Exception, match='Tuple element count mismatch'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@ -98,7 +103,7 @@ def test_tuple_constant_too_many_values():
|
|||||||
CONSTANT: (u32, u8, u8, ) = (24, 57, 1, 1, )
|
CONSTANT: (u32, u8, u8, ) = (24, 57, 1, 1, )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
|
with pytest.raises(Type3Exception, match='Tuple element count mismatch'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@ -107,5 +112,29 @@ def test_tuple_constant_type_mismatch():
|
|||||||
CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(StaticError, match='Static error on line 2: Integer value out of range; expected 0..255, actual 4000'):
|
with pytest.raises(Type3Exception, match='Must fit in 1 byte(s)'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_tuple_must_use_literal_for_indexing():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: u32 = 0
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry(x: (u8, u32, u64)) -> u64:
|
||||||
|
return x[CONSTANT]
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(Type3Exception, match='Tuples must be indexed with literals'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_tuple_must_use_integer_for_indexing():
|
||||||
|
code_py = """
|
||||||
|
@exported
|
||||||
|
def testEntry(x: (u8, u32, u64)) -> u64:
|
||||||
|
return x[0.0]
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(Type3Exception, match='Must be integer'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user