More ideas for type3
This commit is contained in:
parent
79ff11f622
commit
a0645d94dd
@ -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.
|
||||
"""
|
||||
from typing import Dict, Optional, Tuple
|
||||
from typing import Dict, Tuple, Union
|
||||
|
||||
from .. import ourlang
|
||||
|
||||
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:
|
||||
"""
|
||||
Context for constraints
|
||||
@ -22,26 +33,78 @@ class ConstraintBase:
|
||||
"""
|
||||
__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
|
||||
|
||||
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):
|
||||
"""
|
||||
A literal value fits a given type
|
||||
"""
|
||||
__slots__ = ('type3', 'literal', )
|
||||
|
||||
type3: types.Type3
|
||||
type3: types.Type3OrPlaceholder
|
||||
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.literal = literal
|
||||
|
||||
def check(self) -> Optional[str]:
|
||||
def check(self) -> CheckResult:
|
||||
if isinstance(self.type3, types.PlaceholderForType):
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
val = self.literal.value
|
||||
|
||||
int_table: Dict[str, Tuple[int, bool]] = {
|
||||
@ -60,11 +123,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
try:
|
||||
val.to_bytes(bts, 'big', signed=sgn)
|
||||
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 'Must be integer' # FIXME: Add line information
|
||||
return Error('Must be integer') # FIXME: Add line information
|
||||
|
||||
float_table: Dict[str, None] = {
|
||||
'f32': None,
|
||||
@ -79,6 +142,6 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
return None
|
||||
|
||||
return 'Must be real' # FIXME: Add line information
|
||||
return Error('Must be real') # FIXME: Add line information
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
@ -11,7 +11,7 @@ from .constraints import (
|
||||
Context,
|
||||
|
||||
ConstraintBase,
|
||||
LiteralFitsConstraint,
|
||||
LiteralFitsConstraint, SameTypeConstraint,
|
||||
)
|
||||
from . import types
|
||||
|
||||
@ -34,16 +34,30 @@ def constant(ctx: Context, inp: ourlang.Constant) -> Generator[ConstraintBase, N
|
||||
raise NotImplementedError(constant, inp)
|
||||
|
||||
def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBase, None, None]:
|
||||
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||
print('hi')
|
||||
if isinstance(inp, ourlang.Constant):
|
||||
yield from constant(ctx, inp)
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]:
|
||||
if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn):
|
||||
raise NotImplementedError('Functions with not just a return statement')
|
||||
|
||||
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]:
|
||||
|
||||
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):
|
||||
for lit in inp.constant.value:
|
||||
yield LiteralFitsConstraint(inp.type3.args[0], lit)
|
||||
lit.type3 = inp.type3.args[0]
|
||||
# lit.type3 = inp.type3.args[0]
|
||||
raise NotImplementedError
|
||||
return
|
||||
|
||||
raise NotImplementedError(constant, inp, inp.type3)
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
"""
|
||||
Entry point to the type3 system
|
||||
"""
|
||||
from typing import Dict, List
|
||||
|
||||
from .. import ourlang
|
||||
|
||||
from .constraints import Error, RequireTypeSubstitutes
|
||||
from .constraintsgenerator import phasm_type3_generate_constraints
|
||||
from .types import PlaceholderForType, Type3
|
||||
|
||||
class Type3Exception(BaseException):
|
||||
"""
|
||||
@ -11,16 +15,33 @@ class Type3Exception(BaseException):
|
||||
"""
|
||||
|
||||
def phasm_type3(inp: ourlang.Module) -> None:
|
||||
constraints = phasm_type3_generate_constraints(inp)
|
||||
assert constraints
|
||||
constraint_list = phasm_type3_generate_constraints(inp)
|
||||
assert constraint_list
|
||||
|
||||
error_list = []
|
||||
for constraint in constraints:
|
||||
error = constraint.check()
|
||||
if error is not None:
|
||||
error_list.append(error)
|
||||
placeholder_substitutes: Dict[PlaceholderForType, Type3] = {}
|
||||
|
||||
error_list: List[Error] = []
|
||||
while constraint_list: # FIXME: How to detect infinite loop? Is that necessary?
|
||||
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:
|
||||
raise Type3Exception(error_list)
|
||||
|
||||
# TODO: Implement type substitution
|
||||
# TODO: Implement type substitution on the AST
|
||||
print('placeholder_substitutes', placeholder_substitutes)
|
||||
|
||||
@ -4,7 +4,17 @@ Contains the final types for use in Phasm
|
||||
These are actual, instantiated types; not the abstract types that the
|
||||
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:
|
||||
"""
|
||||
@ -30,7 +40,7 @@ class Type3:
|
||||
if format_spec != 's':
|
||||
raise TypeError('unsupported format string passed to Type3.__format__')
|
||||
|
||||
return self.name
|
||||
return str(self)
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
raise NotImplementedError
|
||||
@ -44,6 +54,62 @@ class Type3:
|
||||
def __bool__(self) -> bool:
|
||||
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):
|
||||
"""
|
||||
A Type3 that has been applied to another type
|
||||
@ -55,16 +121,16 @@ class AppliedType3(Type3):
|
||||
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__(
|
||||
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.
|
||||
"""
|
||||
|
||||
tbd = Type3('tbd')
|
||||
"""
|
||||
This type is yet to be determined
|
||||
"""
|
||||
|
||||
LOOKUP_TABLE: Dict[str, Type3] = {
|
||||
'none': none,
|
||||
'u8': u8,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user