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.
|
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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user