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 subscript as an operator
|
||||
- Implement another operator
|
||||
- Figure out how to do type classes
|
||||
- Re-implement Subscript contraints - Doing the LiteralFitsConstraint with a tuple doesn't put the types on the tuple elements
|
||||
- Implement structs again, with the `.foo` notation working
|
||||
|
||||
- Rename constant to literal
|
||||
|
||||
@ -59,6 +59,9 @@ class ConstantTuple(Constant):
|
||||
super().__init__()
|
||||
self.value = value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'ConstantTuple({repr(self.value)})'
|
||||
|
||||
class VariableReference(Expression):
|
||||
"""
|
||||
An variable reference expression within a statement
|
||||
|
||||
@ -714,13 +714,11 @@ class OurVisitor:
|
||||
if not isinstance(node.ctx, ast.Load):
|
||||
_raise_static_error(node, 'Must be load context')
|
||||
|
||||
elements = ', '.join(
|
||||
self.visit_type(module, elt).name
|
||||
for elt in node.elts
|
||||
return type3types.AppliedType3(
|
||||
type3types.tuple,
|
||||
(self.visit_type(module, elt) for elt in node.elts)
|
||||
)
|
||||
|
||||
return type3types.Type3(f'({elements}, )')
|
||||
|
||||
raise NotImplementedError(f'{node} as type')
|
||||
|
||||
def _not_implemented(check: Any, msg: str) -> None:
|
||||
|
||||
@ -178,18 +178,13 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
__slots__ = ('type3', 'literal', )
|
||||
|
||||
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.literal = literal
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
if isinstance(self.type3, types.PlaceholderForType):
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
val = self.literal.value
|
||||
|
||||
int_table: Dict[str, Tuple[int, bool]] = {
|
||||
'u8': (1, False),
|
||||
'u32': (4, False),
|
||||
@ -199,8 +194,19 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
'i64': (8, True),
|
||||
}
|
||||
|
||||
if self.type3.name in int_table:
|
||||
bts, sgn = int_table[self.type3.name]
|
||||
float_table: Dict[str, None] = {
|
||||
'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):
|
||||
try:
|
||||
@ -212,13 +218,9 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
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:
|
||||
_ = float_table[self.type3.name]
|
||||
if type3.name in float_table:
|
||||
_ = float_table[type3.name]
|
||||
|
||||
if isinstance(val, float):
|
||||
# FIXME: Bit check
|
||||
@ -227,8 +229,26 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
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
|
||||
|
||||
return _check(self.type3, self.literal)
|
||||
|
||||
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])
|
||||
@ -245,3 +265,62 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
def __repr__(self) -> str:
|
||||
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,
|
||||
|
||||
ConstraintBase,
|
||||
CanBeSubscriptedConstraint,
|
||||
LiteralFitsConstraint, MustImplementTypeClassConstraint, SameTypeConstraint,
|
||||
)
|
||||
|
||||
@ -20,7 +21,7 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
|
||||
return [*module(ctx, inp)]
|
||||
|
||||
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)
|
||||
return
|
||||
|
||||
@ -59,6 +60,13 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas
|
||||
|
||||
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):
|
||||
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}')
|
||||
|
||||
@ -22,6 +22,7 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
||||
assert constraint_list
|
||||
|
||||
placeholder_substitutes: Dict[PlaceholderForType, Type3] = {}
|
||||
placeholder_id_map: Dict[int, str] = {}
|
||||
|
||||
restack_counter = 0
|
||||
|
||||
@ -29,7 +30,7 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
||||
while constraint_list:
|
||||
if verbose:
|
||||
print()
|
||||
print_constraint_list(constraint_list, placeholder_substitutes)
|
||||
print_constraint_list(placeholder_id_map, constraint_list, placeholder_substitutes)
|
||||
|
||||
constraint = constraint_list.pop(0)
|
||||
|
||||
@ -92,9 +93,7 @@ def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintB
|
||||
else:
|
||||
print('- ' + txt.format(**act_fmt))
|
||||
|
||||
def print_constraint_list(constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None:
|
||||
placeholder_id_map: Dict[int, str] = {}
|
||||
|
||||
def print_constraint_list(placeholder_id_map: Dict[int, str], constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None:
|
||||
print('=== v type3 constraint_list v === ')
|
||||
for psk, psv in placeholder_substitutes.items():
|
||||
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:
|
||||
args = [*args]
|
||||
|
||||
super().__init__(
|
||||
base.name
|
||||
+ ' ('
|
||||
@ -138,7 +140,7 @@ class AppliedType3(Type3):
|
||||
)
|
||||
|
||||
self.base = base
|
||||
self.args = [*args]
|
||||
self.args = args
|
||||
|
||||
def __repr__(self) -> str:
|
||||
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.
|
||||
|
||||
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] = {
|
||||
|
||||
@ -167,7 +167,6 @@ def testEntry() -> u64:
|
||||
|
||||
with pytest.raises(Type3Exception, match='u64 must be u32 instead'):
|
||||
Suite(code_py).run_code()
|
||||
assert False
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64'])
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
from ..constants import COMPLETE_NUMERIC_TYPES, TYPE_MAP
|
||||
from ..helpers import Suite
|
||||
|
||||
@ -11,10 +13,7 @@ CONSTANT: ({type_}, ) = (65, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> {type_}:
|
||||
return helper(CONSTANT)
|
||||
|
||||
def helper(vector: ({type_}, )) -> {type_}:
|
||||
return vector[0]
|
||||
return CONSTANT[0]
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
@ -28,10 +27,7 @@ CONSTANT: (u8, u8, u32, u32, u64, u64, ) = (11, 22, 3333, 4444, 555555, 666666,
|
||||
|
||||
@exported
|
||||
def testEntry() -> u32:
|
||||
return helper(CONSTANT)
|
||||
|
||||
def helper(vector: (u8, u8, u32, u32, u64, u64, )) -> u32:
|
||||
return vector[2]
|
||||
return CONSTANT[2]
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
@ -83,13 +79,22 @@ def testEntry() -> i32x4:
|
||||
|
||||
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
|
||||
def test_tuple_constant_too_few_values():
|
||||
code_py = """
|
||||
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()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@ -98,7 +103,7 @@ def test_tuple_constant_too_many_values():
|
||||
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()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@ -107,5 +112,29 @@ def test_tuple_constant_type_mismatch():
|
||||
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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user