More ideas for type3

This commit is contained in:
Johan B.W. de Vries 2022-11-17 14:51:14 +01:00
parent 79ff11f622
commit a0645d94dd
4 changed files with 192 additions and 32 deletions

View File

@ -3,12 +3,23 @@ This module contains possible constraints generated based on the AST
These need to be resolved before the program can be compiled. These need to be resolved before the program can be compiled.
""" """
from typing import Dict, Optional, Tuple from typing import Dict, Tuple, Union
from .. import ourlang from .. import ourlang
from . import types from . import types
class Error:
def __init__(self, msg: str) -> None:
self.msg = msg
class RequireTypeSubstitutes:
pass
CheckResult = Union[None, Error, RequireTypeSubstitutes]
SubstitutionMap = Dict[types.PlaceholderForType, types.Type3]
class Context: class Context:
""" """
Context for constraints Context for constraints
@ -22,26 +33,78 @@ class ConstraintBase:
""" """
__slots__ = () __slots__ = ()
def check(self) -> Optional[str]: def check(self) -> CheckResult:
""" """
Checks if the contraint hold, returning an error if it doesn't Checks if the constraint hold, returning an error if it doesn't
""" """
raise NotImplementedError raise NotImplementedError
def get_new_placeholder_substitutes(self) -> SubstitutionMap:
"""
Returns any new placeholders that can be substituted for actual types
"""
return {}
def substitute_placeholders(self, smap: SubstitutionMap) -> None:
"""
Called with type substitutes, so you can update any placeholders
you may have with the known types. Note that this does not guarantee
that all types are known, you may still have some placeholders left.
"""
raise NotImplementedError(self, self.substitute_placeholders)
class SameTypeConstraint(ConstraintBase):
"""
Verifies that an expression has an expected type
"""
__slots__ = ('expected', 'actual', )
expected: types.Type3
actual: types.Type3OrPlaceholder
def __init__(self, expected: types.Type3, actual: types.Type3OrPlaceholder) -> None:
self.expected = expected
self.actual = actual
def check(self) -> CheckResult:
if isinstance(self.actual, types.PlaceholderForType):
return RequireTypeSubstitutes()
if self.expected is self.actual:
return None
raise NotImplementedError
def get_new_placeholder_substitutes(self) -> SubstitutionMap:
if isinstance(self.actual, types.PlaceholderForType):
return {
self.actual: self.expected
}
return {}
def substitute_placeholders(self, smap: SubstitutionMap) -> None:
if isinstance(self.actual, types.PlaceholderForType) and self.actual in smap:
self.actual.get_substituted(smap[self.actual])
self.actual = smap[self.actual]
class LiteralFitsConstraint(ConstraintBase): class LiteralFitsConstraint(ConstraintBase):
""" """
A literal value fits a given type A literal value fits a given type
""" """
__slots__ = ('type3', 'literal', ) __slots__ = ('type3', 'literal', )
type3: types.Type3 type3: types.Type3OrPlaceholder
literal: ourlang.ConstantPrimitive literal: ourlang.ConstantPrimitive
def __init__(self, type3: types.Type3, literal: ourlang.ConstantPrimitive) -> None: def __init__(self, type3: types.Type3OrPlaceholder, literal: ourlang.ConstantPrimitive) -> None:
self.type3 = type3 self.type3 = type3
self.literal = literal self.literal = literal
def check(self) -> Optional[str]: def check(self) -> CheckResult:
if isinstance(self.type3, types.PlaceholderForType):
return RequireTypeSubstitutes()
val = self.literal.value val = self.literal.value
int_table: Dict[str, Tuple[int, bool]] = { int_table: Dict[str, Tuple[int, bool]] = {
@ -60,11 +123,11 @@ class LiteralFitsConstraint(ConstraintBase):
try: try:
val.to_bytes(bts, 'big', signed=sgn) val.to_bytes(bts, 'big', signed=sgn)
except OverflowError: except OverflowError:
return f'Must fit in {bts} byte(s)' # FIXME: Add line information return Error(f'Must fit in {bts} byte(s)') # FIXME: Add line information
return None return None
return 'Must be integer' # FIXME: Add line information return Error('Must be integer') # FIXME: Add line information
float_table: Dict[str, None] = { float_table: Dict[str, None] = {
'f32': None, 'f32': None,
@ -79,6 +142,6 @@ class LiteralFitsConstraint(ConstraintBase):
return None return None
return 'Must be real' # FIXME: Add line information return Error('Must be real') # FIXME: Add line information
raise NotImplementedError raise NotImplementedError

View File

@ -11,7 +11,7 @@ from .constraints import (
Context, Context,
ConstraintBase, ConstraintBase,
LiteralFitsConstraint, LiteralFitsConstraint, SameTypeConstraint,
) )
from . import types from . import types
@ -34,16 +34,30 @@ def constant(ctx: Context, inp: ourlang.Constant) -> Generator[ConstraintBase, N
raise NotImplementedError(constant, inp) raise NotImplementedError(constant, inp)
def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBase, None, None]: def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBase, None, None]:
if isinstance(inp, ourlang.ConstantPrimitive): if isinstance(inp, ourlang.Constant):
print('hi') yield from constant(ctx, inp)
return
raise NotImplementedError(expression, inp) raise NotImplementedError(expression, inp)
def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]: def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]:
if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn): if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn):
raise NotImplementedError('Functions with not just a return statement') raise NotImplementedError('Functions with not just a return statement')
yield from expression(ctx, inp.statements[0].value) yield from expression(ctx, inp.statements[0].value)
if inp.returns_type3 is None:
raise NotImplementedError
actual: types.Type3OrPlaceholder
if inp.statements[0].value.type3 is None:
print('inp.statements[0].value', inp.statements[0].value)
actual = types.PlaceholderForType([inp.statements[0].value])
else:
actual = inp.statements[0].value.type3
yield SameTypeConstraint(inp.returns_type3, actual)
def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> Generator[ConstraintBase, None, None]: def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> Generator[ConstraintBase, None, None]:
yield from constant(ctx, inp.constant) yield from constant(ctx, inp.constant)
@ -65,7 +79,8 @@ def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> Generat
if inp.type3.base is types.static_array and isinstance(inp.constant, ourlang.ConstantTuple): if inp.type3.base is types.static_array and isinstance(inp.constant, ourlang.ConstantTuple):
for lit in inp.constant.value: for lit in inp.constant.value:
yield LiteralFitsConstraint(inp.type3.args[0], lit) yield LiteralFitsConstraint(inp.type3.args[0], lit)
lit.type3 = inp.type3.args[0] # lit.type3 = inp.type3.args[0]
raise NotImplementedError
return return
raise NotImplementedError(constant, inp, inp.type3) raise NotImplementedError(constant, inp, inp.type3)

View File

@ -1,9 +1,13 @@
""" """
Entry point to the type3 system Entry point to the type3 system
""" """
from typing import Dict, List
from .. import ourlang from .. import ourlang
from .constraints import Error, RequireTypeSubstitutes
from .constraintsgenerator import phasm_type3_generate_constraints from .constraintsgenerator import phasm_type3_generate_constraints
from .types import PlaceholderForType, Type3
class Type3Exception(BaseException): class Type3Exception(BaseException):
""" """
@ -11,16 +15,33 @@ class Type3Exception(BaseException):
""" """
def phasm_type3(inp: ourlang.Module) -> None: def phasm_type3(inp: ourlang.Module) -> None:
constraints = phasm_type3_generate_constraints(inp) constraint_list = phasm_type3_generate_constraints(inp)
assert constraints assert constraint_list
error_list = [] placeholder_substitutes: Dict[PlaceholderForType, Type3] = {}
for constraint in constraints:
error = constraint.check() error_list: List[Error] = []
if error is not None: while constraint_list: # FIXME: How to detect infinite loop? Is that necessary?
error_list.append(error) constraint = constraint_list.pop(0)
constraint.substitute_placeholders(placeholder_substitutes)
placeholder_substitutes.update(constraint.get_new_placeholder_substitutes())
check_result = constraint.check()
if check_result is None:
continue
if isinstance(check_result, Error):
error_list.append(check_result)
if isinstance(check_result, RequireTypeSubstitutes):
constraint_list.append(constraint)
continue
raise NotImplementedError(constraint, check_result)
if error_list: if error_list:
raise Type3Exception(error_list) raise Type3Exception(error_list)
# TODO: Implement type substitution # TODO: Implement type substitution on the AST
print('placeholder_substitutes', placeholder_substitutes)

View File

@ -4,7 +4,17 @@ Contains the final types for use in Phasm
These are actual, instantiated types; not the abstract types that the These are actual, instantiated types; not the abstract types that the
constraint generator works with. constraint generator works with.
""" """
from typing import Any, Dict, Iterable, List from typing import Any, ClassVar, Dict, Iterable, Optional, List, Protocol, Union
class ExpressionProtocol(Protocol):
"""
A protocol for classes that should be updated on substitution
"""
type3: Optional['Type3']
"""
The type to update
"""
class Type3: class Type3:
""" """
@ -30,7 +40,7 @@ class Type3:
if format_spec != 's': if format_spec != 's':
raise TypeError('unsupported format string passed to Type3.__format__') raise TypeError('unsupported format string passed to Type3.__format__')
return self.name return str(self)
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
raise NotImplementedError raise NotImplementedError
@ -44,6 +54,62 @@ class Type3:
def __bool__(self) -> bool: def __bool__(self) -> bool:
raise NotImplementedError raise NotImplementedError
class PlaceholderForType:
"""
A placeholder type, for when we don't know the final type yet
"""
__slots__ = ('number', 'update_on_substitution', )
number: int
update_on_substitution: List[ExpressionProtocol]
NEXT_NUMBER: ClassVar[int] = 1
def __init__(self, update_on_substitution: Iterable[ExpressionProtocol]) -> None:
self.number = PlaceholderForType.NEXT_NUMBER
PlaceholderForType.NEXT_NUMBER += 1
self.update_on_substitution = [*update_on_substitution]
def get_substituted(self, result_type: Type3) -> None:
"""
Informs this Placeholder that it's getting substituted
This will also clear the update_on_substitution list
"""
for uos in self.update_on_substitution:
uos.type3 = result_type
self.update_on_substitution = []
def __repr__(self) -> str:
uos = ', '.join(repr(x) for x in self.update_on_substitution)
return f'PlaceholderForType({self.number} [{uos}])'
def __str__(self) -> str:
return f'T{self.number}'
def __format__(self, format_spec: str) -> str:
if format_spec != 's':
raise TypeError('unsupported format string passed to Type3.__format__')
return str(self)
def __eq__(self, other: Any) -> bool:
raise NotImplementedError
def __ne__(self, other: Any) -> bool:
raise NotImplementedError
def __hash__(self) -> int:
return 0 # Valid but performs badly
def __bool__(self) -> bool:
raise NotImplementedError
Type3OrPlaceholder = Union[Type3, PlaceholderForType]
class AppliedType3(Type3): class AppliedType3(Type3):
""" """
A Type3 that has been applied to another type A Type3 that has been applied to another type
@ -55,16 +121,16 @@ class AppliedType3(Type3):
The base type The base type
""" """
args: List[Type3] args: List[Type3OrPlaceholder]
""" """
The applied types The applied types (or placeholders there for)
""" """
def __init__(self, base: Type3, args: Iterable[Type3]) -> None: def __init__(self, base: Type3, args: Iterable[Type3OrPlaceholder]) -> None:
super().__init__( super().__init__(
base.name base.name
+ ' (' + ' ('
+ ') ('.join(x.name for x in args) + ') ('.join(str(x) for x in args) # FIXME: Do we need to redo the name on substitution?
+ ')' + ')'
) )
@ -141,11 +207,6 @@ 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.
""" """
tbd = Type3('tbd')
"""
This type is yet to be determined
"""
LOOKUP_TABLE: Dict[str, Type3] = { LOOKUP_TABLE: Dict[str, Type3] = {
'none': none, 'none': none,
'u8': u8, 'u8': u8,