Started on attempt3 of the type system
This commit is contained in:
parent
55a45ff17c
commit
79ff11f622
@ -8,6 +8,7 @@ import struct
|
|||||||
from . import codestyle
|
from . import codestyle
|
||||||
from . import ourlang
|
from . import ourlang
|
||||||
from . import typing
|
from . import typing
|
||||||
|
from .type3 import types as type3types
|
||||||
from . import wasm
|
from . import wasm
|
||||||
|
|
||||||
from .stdlib import alloc as stdlib_alloc
|
from .stdlib import alloc as stdlib_alloc
|
||||||
@ -146,6 +147,9 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
Compile: Any expression
|
Compile: Any expression
|
||||||
"""
|
"""
|
||||||
if isinstance(inp, ourlang.ConstantPrimitive):
|
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||||
|
assert inp.type3 is not None, typing.ASSERTION_ERROR
|
||||||
|
|
||||||
|
|
||||||
assert inp.type_var is not None, typing.ASSERTION_ERROR
|
assert inp.type_var is not None, typing.ASSERTION_ERROR
|
||||||
|
|
||||||
stp = typing.simplify(inp.type_var)
|
stp = typing.simplify(inp.type_var)
|
||||||
@ -313,6 +317,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.Subscript):
|
if isinstance(inp, ourlang.Subscript):
|
||||||
|
assert inp.varref.type3 is not None, typing.ASSERTION_ERROR
|
||||||
|
|
||||||
assert inp.varref.type_var is not None, typing.ASSERTION_ERROR
|
assert inp.varref.type_var is not None, typing.ASSERTION_ERROR
|
||||||
tc_type = inp.varref.type_var.get_type()
|
tc_type = inp.varref.type_var.get_type()
|
||||||
if tc_type is None:
|
if tc_type is None:
|
||||||
@ -648,45 +654,44 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
|
|||||||
data_list: List[bytes] = []
|
data_list: List[bytes] = []
|
||||||
|
|
||||||
for constant in block.data:
|
for constant in block.data:
|
||||||
assert constant.type_var is not None
|
assert constant.type3 is not None
|
||||||
mtyp = typing.simplify(constant.type_var)
|
|
||||||
|
|
||||||
if mtyp == 'u8':
|
if constant.type3 is type3types.u8:
|
||||||
assert isinstance(constant.value, int)
|
assert isinstance(constant.value, int)
|
||||||
data_list.append(module_data_u8(constant.value))
|
data_list.append(module_data_u8(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mtyp == 'u32':
|
if constant.type3 is type3types.u32:
|
||||||
assert isinstance(constant.value, int)
|
assert isinstance(constant.value, int)
|
||||||
data_list.append(module_data_u32(constant.value))
|
data_list.append(module_data_u32(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mtyp == 'u64':
|
if constant.type3 is type3types.u64:
|
||||||
assert isinstance(constant.value, int)
|
assert isinstance(constant.value, int)
|
||||||
data_list.append(module_data_u64(constant.value))
|
data_list.append(module_data_u64(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mtyp == 'i32':
|
if constant.type3 is type3types.i32:
|
||||||
assert isinstance(constant.value, int)
|
assert isinstance(constant.value, int)
|
||||||
data_list.append(module_data_i32(constant.value))
|
data_list.append(module_data_i32(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mtyp == 'i64':
|
if constant.type3 is type3types.i64:
|
||||||
assert isinstance(constant.value, int)
|
assert isinstance(constant.value, int)
|
||||||
data_list.append(module_data_i64(constant.value))
|
data_list.append(module_data_i64(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mtyp == 'f32':
|
if constant.type3 is type3types.f32:
|
||||||
assert isinstance(constant.value, float)
|
assert isinstance(constant.value, float)
|
||||||
data_list.append(module_data_f32(constant.value))
|
data_list.append(module_data_f32(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if mtyp == 'f64':
|
if constant.type3 is type3types.f64:
|
||||||
assert isinstance(constant.value, float)
|
assert isinstance(constant.value, float)
|
||||||
data_list.append(module_data_f64(constant.value))
|
data_list.append(module_data_f64(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
raise NotImplementedError(constant, constant.type_var, mtyp)
|
raise NotImplementedError(constant, constant.type3, constant.value)
|
||||||
|
|
||||||
block_data = b''.join(data_list)
|
block_data = b''.join(data_list)
|
||||||
|
|
||||||
|
|||||||
@ -16,20 +16,27 @@ from .typing import (
|
|||||||
TypeVar,
|
TypeVar,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .type3.types import Type3
|
||||||
|
|
||||||
class Expression:
|
class Expression:
|
||||||
"""
|
"""
|
||||||
An expression within a statement
|
An expression within a statement
|
||||||
"""
|
"""
|
||||||
__slots__ = ('type_var', )
|
__slots__ = ('type3', 'type_var', )
|
||||||
|
|
||||||
|
type3: Optional[Type3]
|
||||||
|
|
||||||
type_var: Optional[TypeVar]
|
type_var: Optional[TypeVar]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
|
self.type3 = None
|
||||||
self.type_var = None
|
self.type_var = None
|
||||||
|
|
||||||
class Constant(Expression):
|
class Constant(Expression):
|
||||||
"""
|
"""
|
||||||
An constant value expression within a statement
|
An constant value expression within a statement
|
||||||
|
|
||||||
|
# FIXME: Rename to literal
|
||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
@ -217,13 +224,14 @@ class Function:
|
|||||||
"""
|
"""
|
||||||
A function processes input and produces output
|
A function processes input and produces output
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'returns_str', 'returns_type_var', 'posonlyargs', )
|
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'returns_type3', 'returns_str', 'returns_type_var', 'posonlyargs', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
exported: bool
|
exported: bool
|
||||||
imported: bool
|
imported: bool
|
||||||
statements: List[Statement]
|
statements: List[Statement]
|
||||||
|
returns_type3: Optional[Type3]
|
||||||
returns_str: str
|
returns_str: str
|
||||||
returns_type_var: Optional[TypeVar]
|
returns_type_var: Optional[TypeVar]
|
||||||
posonlyargs: List[FunctionParam]
|
posonlyargs: List[FunctionParam]
|
||||||
@ -284,19 +292,21 @@ class ModuleConstantDef:
|
|||||||
"""
|
"""
|
||||||
A constant definition within a module
|
A constant definition within a module
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'lineno', 'type_str', 'type_var', 'constant', 'data_block', )
|
__slots__ = ('name', 'lineno', 'type3', 'type_str', 'type_var', 'constant', 'data_block', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
|
type3: Type3
|
||||||
type_str: str
|
type_str: str
|
||||||
type_var: Optional[TypeVar]
|
type_var: Optional[TypeVar]
|
||||||
constant: Constant
|
constant: Constant
|
||||||
data_block: Optional['ModuleDataBlock']
|
data_block: Optional['ModuleDataBlock']
|
||||||
|
|
||||||
def __init__(self, name: str, lineno: int, type_str: str, constant: Constant, data_block: Optional['ModuleDataBlock']) -> None:
|
def __init__(self, name: str, lineno: int, type3: Type3, constant: Constant, data_block: Optional['ModuleDataBlock']) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
self.type_str = type_str
|
self.type3 = type3
|
||||||
|
self.type_str = type3.name
|
||||||
self.type_var = None
|
self.type_var = None
|
||||||
self.constant = constant
|
self.constant = constant
|
||||||
self.data_block = data_block
|
self.data_block = data_block
|
||||||
|
|||||||
@ -12,6 +12,8 @@ from .typing import (
|
|||||||
TypeStructMember,
|
TypeStructMember,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .type3 import types as type3types
|
||||||
|
|
||||||
from .exceptions import StaticError
|
from .exceptions import StaticError
|
||||||
from .ourlang import (
|
from .ourlang import (
|
||||||
WEBASSEMBLY_BUILTIN_FLOAT_OPS,
|
WEBASSEMBLY_BUILTIN_FLOAT_OPS,
|
||||||
@ -129,7 +131,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
function.posonlyargs.append(FunctionParam(
|
function.posonlyargs.append(FunctionParam(
|
||||||
arg.arg,
|
arg.arg,
|
||||||
self.visit_type(module, arg.annotation),
|
self.visit_type(module, arg.annotation).name,
|
||||||
))
|
))
|
||||||
|
|
||||||
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
||||||
@ -153,7 +155,8 @@ class OurVisitor:
|
|||||||
function.imported = True
|
function.imported = True
|
||||||
|
|
||||||
if node.returns:
|
if node.returns:
|
||||||
function.returns_str = self.visit_type(module, node.returns)
|
function.returns_type3 = self.visit_type(module, node.returns)
|
||||||
|
function.returns_str = function.returns_type3.name
|
||||||
|
|
||||||
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
||||||
|
|
||||||
@ -196,10 +199,11 @@ class OurVisitor:
|
|||||||
_raise_static_error(node, 'Must be load context')
|
_raise_static_error(node, 'Must be load context')
|
||||||
|
|
||||||
if isinstance(node.value, ast.Constant):
|
if isinstance(node.value, ast.Constant):
|
||||||
|
type3 = self.visit_type(module, node.annotation)
|
||||||
return ModuleConstantDef(
|
return ModuleConstantDef(
|
||||||
node.target.id,
|
node.target.id,
|
||||||
node.lineno,
|
node.lineno,
|
||||||
self.visit_type(module, node.annotation),
|
type3,
|
||||||
self.visit_Module_Constant(module, node.value),
|
self.visit_Module_Constant(module, node.value),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@ -675,10 +679,10 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node.value} as constant')
|
raise NotImplementedError(f'{node.value} as constant')
|
||||||
|
|
||||||
def visit_type(self, module: Module, node: ast.expr) -> str:
|
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
|
||||||
if isinstance(node, ast.Constant):
|
if isinstance(node, ast.Constant):
|
||||||
if node.value is None:
|
if node.value is None:
|
||||||
return 'None'
|
return type3types.none
|
||||||
|
|
||||||
_raise_static_error(node, f'Unrecognized type {node.value}')
|
_raise_static_error(node, f'Unrecognized type {node.value}')
|
||||||
|
|
||||||
@ -686,15 +690,15 @@ 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')
|
||||||
|
|
||||||
if node.id in BUILTIN_TYPES:
|
if node.id in type3types.LOOKUP_TABLE:
|
||||||
return node.id
|
return type3types.LOOKUP_TABLE[node.id]
|
||||||
|
|
||||||
raise NotImplementedError('TODO: Broken after type system')
|
raise NotImplementedError('TODO: Broken after type system')
|
||||||
|
#
|
||||||
if node.id in module.structs:
|
# if node.id in module.structs:
|
||||||
return module.structs[node.id]
|
# return module.structs[node.id]
|
||||||
|
#
|
||||||
_raise_static_error(node, f'Unrecognized type {node.id}')
|
# _raise_static_error(node, f'Unrecognized type {node.id}')
|
||||||
|
|
||||||
if isinstance(node, ast.Subscript):
|
if isinstance(node, ast.Subscript):
|
||||||
if not isinstance(node.value, ast.Name):
|
if not isinstance(node.value, ast.Name):
|
||||||
@ -708,35 +712,24 @@ 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')
|
||||||
|
|
||||||
if node.value.id not in BUILTIN_TYPES: # FIXME: Tuple of tuples?
|
if node.value.id not in type3types.LOOKUP_TABLE: # FIXME: Tuple of tuples?
|
||||||
_raise_static_error(node, f'Unrecognized type {node.value.id}')
|
_raise_static_error(node, f'Unrecognized type {node.value.id}')
|
||||||
|
|
||||||
return f'{node.value.id}[{node.slice.value.value}]'
|
return type3types.AppliedType3(
|
||||||
|
type3types.static_array,
|
||||||
|
[self.visit_type(module, node.value)],
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(node, ast.Tuple):
|
if isinstance(node, ast.Tuple):
|
||||||
raise NotImplementedError('TODO: Broken after new type system')
|
if not isinstance(node.ctx, ast.Load):
|
||||||
|
_raise_static_error(node, 'Must be load context')
|
||||||
|
|
||||||
# if not isinstance(node.ctx, ast.Load):
|
elements = ', '.join(
|
||||||
# _raise_static_error(node, 'Must be load context')
|
self.visit_type(module, elt).name
|
||||||
#
|
for elt in node.elts
|
||||||
# type_tuple = TypeTuple()
|
)
|
||||||
#
|
|
||||||
# offset = 0
|
return type3types.Type3(f'({elements}, )')
|
||||||
#
|
|
||||||
# for idx, elt in enumerate(node.elts):
|
|
||||||
# tuple_member = TypeTupleMember(idx, self.visit_type(module, elt), offset)
|
|
||||||
#
|
|
||||||
# type_tuple.members.append(tuple_member)
|
|
||||||
# offset += tuple_member.type.alloc_size()
|
|
||||||
#
|
|
||||||
# key = type_tuple.render_internal_name()
|
|
||||||
#
|
|
||||||
# if key not in module.types:
|
|
||||||
# module.types[key] = type_tuple
|
|
||||||
# constructor = TupleConstructor(type_tuple)
|
|
||||||
# module.functions[constructor.name] = constructor
|
|
||||||
#
|
|
||||||
# return module.types[key]
|
|
||||||
|
|
||||||
raise NotImplementedError(f'{node} as type')
|
raise NotImplementedError(f'{node} as type')
|
||||||
|
|
||||||
|
|||||||
0
phasm/type3/__init__.py
Normal file
0
phasm/type3/__init__.py
Normal file
84
phasm/type3/constraints.py
Normal file
84
phasm/type3/constraints.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
"""
|
||||||
|
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 .. import ourlang
|
||||||
|
|
||||||
|
from . import types
|
||||||
|
|
||||||
|
class Context:
|
||||||
|
"""
|
||||||
|
Context for constraints
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
class ConstraintBase:
|
||||||
|
"""
|
||||||
|
Base class for constraints
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def check(self) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Checks if the contraint hold, returning an error if it doesn't
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class LiteralFitsConstraint(ConstraintBase):
|
||||||
|
"""
|
||||||
|
A literal value fits a given type
|
||||||
|
"""
|
||||||
|
__slots__ = ('type3', 'literal', )
|
||||||
|
|
||||||
|
type3: types.Type3
|
||||||
|
literal: ourlang.ConstantPrimitive
|
||||||
|
|
||||||
|
def __init__(self, type3: types.Type3, literal: ourlang.ConstantPrimitive) -> None:
|
||||||
|
self.type3 = type3
|
||||||
|
self.literal = literal
|
||||||
|
|
||||||
|
def check(self) -> Optional[str]:
|
||||||
|
val = self.literal.value
|
||||||
|
|
||||||
|
int_table: Dict[str, Tuple[int, bool]] = {
|
||||||
|
'u8': (1, False),
|
||||||
|
'u32': (4, False),
|
||||||
|
'u64': (8, False),
|
||||||
|
'i8': (1, True),
|
||||||
|
'i32': (4, True),
|
||||||
|
'i64': (8, True),
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.type3.name in int_table:
|
||||||
|
bts, sgn = int_table[self.type3.name]
|
||||||
|
|
||||||
|
if isinstance(val, int):
|
||||||
|
try:
|
||||||
|
val.to_bytes(bts, 'big', signed=sgn)
|
||||||
|
except OverflowError:
|
||||||
|
return f'Must fit in {bts} byte(s)' # FIXME: Add line information
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
return '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 isinstance(val, float):
|
||||||
|
# FIXME: Bit check
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
return 'Must be real' # FIXME: Add line information
|
||||||
|
|
||||||
|
raise NotImplementedError
|
||||||
83
phasm/type3/constraintsgenerator.py
Normal file
83
phasm/type3/constraintsgenerator.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
This module generates the typing constraints for Phasm.
|
||||||
|
|
||||||
|
The constraints solver can then try to resolve all constraints.
|
||||||
|
"""
|
||||||
|
from typing import Generator, List
|
||||||
|
|
||||||
|
from .. import ourlang
|
||||||
|
|
||||||
|
from .constraints import (
|
||||||
|
Context,
|
||||||
|
|
||||||
|
ConstraintBase,
|
||||||
|
LiteralFitsConstraint,
|
||||||
|
)
|
||||||
|
from . import types
|
||||||
|
|
||||||
|
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
|
||||||
|
ctx = Context()
|
||||||
|
|
||||||
|
return [*module(ctx, inp)]
|
||||||
|
|
||||||
|
def constant(ctx: Context, inp: ourlang.Constant) -> Generator[ConstraintBase, None, None]:
|
||||||
|
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||||
|
# A constant by itself doesn't have any constraints
|
||||||
|
yield from ()
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.ConstantTuple):
|
||||||
|
# A constant by itself doesn't have any constraints
|
||||||
|
yield from ()
|
||||||
|
return
|
||||||
|
|
||||||
|
raise NotImplementedError(constant, inp)
|
||||||
|
|
||||||
|
def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBase, None, None]:
|
||||||
|
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||||
|
print('hi')
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> Generator[ConstraintBase, None, None]:
|
||||||
|
|
||||||
|
yield from constant(ctx, inp.constant)
|
||||||
|
|
||||||
|
if (
|
||||||
|
inp.type3 is types.u8
|
||||||
|
or inp.type3 is types.u32
|
||||||
|
or inp.type3 is types.u64
|
||||||
|
or inp.type3 is types.i32
|
||||||
|
or inp.type3 is types.i64
|
||||||
|
or inp.type3 is types.f32
|
||||||
|
or inp.type3 is types.f64
|
||||||
|
) and isinstance(inp.constant, ourlang.ConstantPrimitive):
|
||||||
|
yield LiteralFitsConstraint(inp.type3, inp.constant)
|
||||||
|
inp.constant.type3 = inp.type3
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(inp.type3, types.AppliedType3):
|
||||||
|
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]
|
||||||
|
return
|
||||||
|
|
||||||
|
raise NotImplementedError(constant, inp, inp.type3)
|
||||||
|
|
||||||
|
def module(ctx: Context, inp: ourlang.Module) -> Generator[ConstraintBase, None, None]:
|
||||||
|
for func in inp.functions.values():
|
||||||
|
assert func.returns_type3 is not None, 'TODO'
|
||||||
|
for param in func.posonlyargs:
|
||||||
|
assert False, 'TODO'
|
||||||
|
|
||||||
|
for cdef in inp.constant_defs.values():
|
||||||
|
yield from module_constant_def(ctx, cdef)
|
||||||
|
|
||||||
|
for func in inp.functions.values():
|
||||||
|
yield from function(ctx, func)
|
||||||
26
phasm/type3/entry.py
Normal file
26
phasm/type3/entry.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"""
|
||||||
|
Entry point to the type3 system
|
||||||
|
"""
|
||||||
|
from .. import ourlang
|
||||||
|
|
||||||
|
from .constraintsgenerator import phasm_type3_generate_constraints
|
||||||
|
|
||||||
|
class Type3Exception(BaseException):
|
||||||
|
"""
|
||||||
|
Thrown when the Type3 system detects constraints that do not hold
|
||||||
|
"""
|
||||||
|
|
||||||
|
def phasm_type3(inp: ourlang.Module) -> None:
|
||||||
|
constraints = phasm_type3_generate_constraints(inp)
|
||||||
|
assert constraints
|
||||||
|
|
||||||
|
error_list = []
|
||||||
|
for constraint in constraints:
|
||||||
|
error = constraint.check()
|
||||||
|
if error is not None:
|
||||||
|
error_list.append(error)
|
||||||
|
|
||||||
|
if error_list:
|
||||||
|
raise Type3Exception(error_list)
|
||||||
|
|
||||||
|
# TODO: Implement type substitution
|
||||||
159
phasm/type3/types.py
Normal file
159
phasm/type3/types.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
class Type3:
|
||||||
|
"""
|
||||||
|
Base class for the type3 types
|
||||||
|
"""
|
||||||
|
__slots__ = ('name', )
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""
|
||||||
|
The name of the string, as parsed and outputted by codestyle.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name: str) -> None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'Type3("{self.name}")'
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __format__(self, format_spec: str) -> str:
|
||||||
|
if format_spec != 's':
|
||||||
|
raise TypeError('unsupported format string passed to Type3.__format__')
|
||||||
|
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __ne__(self, other: Any) -> bool:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __bool__(self) -> bool:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class AppliedType3(Type3):
|
||||||
|
"""
|
||||||
|
A Type3 that has been applied to another type
|
||||||
|
"""
|
||||||
|
__slots__ = ('base', 'args', )
|
||||||
|
|
||||||
|
base: Type3
|
||||||
|
"""
|
||||||
|
The base type
|
||||||
|
"""
|
||||||
|
|
||||||
|
args: List[Type3]
|
||||||
|
"""
|
||||||
|
The applied types
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, base: Type3, args: Iterable[Type3]) -> None:
|
||||||
|
super().__init__(
|
||||||
|
base.name
|
||||||
|
+ ' ('
|
||||||
|
+ ') ('.join(x.name for x in args)
|
||||||
|
+ ')'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.base = base
|
||||||
|
self.args = [*args]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
|
||||||
|
|
||||||
|
none = Type3('none')
|
||||||
|
"""
|
||||||
|
The none type, for when functions simply don't return anything. e.g., IO().
|
||||||
|
"""
|
||||||
|
|
||||||
|
u8 = Type3('u8')
|
||||||
|
"""
|
||||||
|
The unsigned 8-bit integer type.
|
||||||
|
|
||||||
|
Operations on variables employ modular arithmetic, with modulus 2^8.
|
||||||
|
"""
|
||||||
|
|
||||||
|
u32 = Type3('u32')
|
||||||
|
"""
|
||||||
|
The unsigned 32-bit integer type.
|
||||||
|
|
||||||
|
Operations on variables employ modular arithmetic, with modulus 2^32.
|
||||||
|
"""
|
||||||
|
|
||||||
|
u64 = Type3('u64')
|
||||||
|
"""
|
||||||
|
The unsigned 64-bit integer type.
|
||||||
|
|
||||||
|
Operations on variables employ modular arithmetic, with modulus 2^64.
|
||||||
|
"""
|
||||||
|
|
||||||
|
i8 = Type3('i8')
|
||||||
|
"""
|
||||||
|
The signed 8-bit integer type.
|
||||||
|
|
||||||
|
Operations on variables employ modular arithmetic, with modulus 2^8, but
|
||||||
|
with the middel point being 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
i32 = Type3('i32')
|
||||||
|
"""
|
||||||
|
The unsigned 32-bit integer type.
|
||||||
|
|
||||||
|
Operations on variables employ modular arithmetic, with modulus 2^32, but
|
||||||
|
with the middel point being 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
i64 = Type3('i64')
|
||||||
|
"""
|
||||||
|
The unsigned 64-bit integer type.
|
||||||
|
|
||||||
|
Operations on variables employ modular arithmetic, with modulus 2^64, but
|
||||||
|
with the middel point being 0.
|
||||||
|
"""
|
||||||
|
|
||||||
|
f32 = Type3('f32')
|
||||||
|
"""
|
||||||
|
A 32-bits IEEE 754 float, of 32 bits width.
|
||||||
|
"""
|
||||||
|
|
||||||
|
f64 = Type3('f64')
|
||||||
|
"""
|
||||||
|
A 32-bits IEEE 754 float, of 64 bits width.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tbd = Type3('tbd')
|
||||||
|
"""
|
||||||
|
This type is yet to be determined
|
||||||
|
"""
|
||||||
|
|
||||||
|
LOOKUP_TABLE: Dict[str, Type3] = {
|
||||||
|
'none': none,
|
||||||
|
'u8': u8,
|
||||||
|
'u32': u32,
|
||||||
|
'u64': u64,
|
||||||
|
'i8': i8,
|
||||||
|
'i32': i32,
|
||||||
|
'i64': i64,
|
||||||
|
'f32': f32,
|
||||||
|
'f64': f64,
|
||||||
|
}
|
||||||
@ -201,14 +201,14 @@ class TypingNarrowError(TypingError):
|
|||||||
|
|
||||||
class TypeConstraintBase:
|
class TypeConstraintBase:
|
||||||
"""
|
"""
|
||||||
Base class for classes implementing a contraint on a type
|
Base class for classes implementing a constraint on a type
|
||||||
"""
|
"""
|
||||||
def narrow(self, ctx: 'Context', other: 'TypeConstraintBase') -> 'TypeConstraintBase':
|
def narrow(self, ctx: 'Context', other: 'TypeConstraintBase') -> 'TypeConstraintBase':
|
||||||
raise NotImplementedError('narrow', self, other)
|
raise NotImplementedError('narrow', self, other)
|
||||||
|
|
||||||
class TypeConstraintSigned(TypeConstraintBase):
|
class TypeConstraintSigned(TypeConstraintBase):
|
||||||
"""
|
"""
|
||||||
Contraint on whether a signed value can be used or not, or whether
|
Constraint on whether a signed value can be used or not, or whether
|
||||||
a value can be used in a signed expression
|
a value can be used in a signed expression
|
||||||
"""
|
"""
|
||||||
__slots__ = ('signed', )
|
__slots__ = ('signed', )
|
||||||
@ -237,7 +237,7 @@ class TypeConstraintSigned(TypeConstraintBase):
|
|||||||
|
|
||||||
class TypeConstraintBitWidth(TypeConstraintBase):
|
class TypeConstraintBitWidth(TypeConstraintBase):
|
||||||
"""
|
"""
|
||||||
Contraint on how many bits an expression has or can possibly have
|
Constraint on how many bits an expression has or can possibly have
|
||||||
"""
|
"""
|
||||||
__slots__ = ('oneof', )
|
__slots__ = ('oneof', )
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ class TypeConstraintBitWidth(TypeConstraintBase):
|
|||||||
|
|
||||||
class TypeConstraintSubscript(TypeConstraintBase):
|
class TypeConstraintSubscript(TypeConstraintBase):
|
||||||
"""
|
"""
|
||||||
Contraint on allowing a type to be subscripted
|
Constraint on allowing a type to be subscripted
|
||||||
"""
|
"""
|
||||||
__slots__ = ('members', )
|
__slots__ = ('members', )
|
||||||
|
|
||||||
@ -434,7 +434,7 @@ class Context:
|
|||||||
r_type = self.var_types[r_ctx_id]
|
r_type = self.var_types[r_ctx_id]
|
||||||
l_r_var_list = self.vars_by_id[l_ctx_id] + self.vars_by_id[r_ctx_id]
|
l_r_var_list = self.vars_by_id[l_ctx_id] + self.vars_by_id[r_ctx_id]
|
||||||
|
|
||||||
# Create a new TypeVar, with the combined contraints
|
# Create a new TypeVar, with the combined constraints
|
||||||
# and locations of the old ones
|
# and locations of the old ones
|
||||||
n = self.new_var()
|
n = self.new_var()
|
||||||
|
|
||||||
@ -499,7 +499,7 @@ def simplify(inp: TypeVar) -> Optional[str]:
|
|||||||
return f'{base}{bitwidth}'
|
return f'{base}{bitwidth}'
|
||||||
|
|
||||||
if inp.get_type() is PhasmTypeReal:
|
if inp.get_type() is PhasmTypeReal:
|
||||||
if tc_bits is None or tc_sign is not None: # Floats should not hava sign contraint
|
if tc_bits is None or tc_sign is not None: # Floats should not hava sign constraint
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if len(tc_bits.oneof) != 1:
|
if len(tc_bits.oneof) != 1:
|
||||||
@ -640,4 +640,14 @@ def from_str(ctx: Context, inp: str, location: Optional[str] = None) -> TypeVar:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
if inp.startswith('static_array ('):
|
||||||
|
# Temp workaround while both type2 and type3 are running simultaneous
|
||||||
|
result = ctx.new_var(PhasmTypeStaticArray)
|
||||||
|
result.add_location(inp)
|
||||||
|
|
||||||
|
if location is not None:
|
||||||
|
result.add_location(location)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
raise NotImplementedError(from_str, inp)
|
raise NotImplementedError(from_str, inp)
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import wasmtime
|
|||||||
from phasm.compiler import phasm_compile
|
from phasm.compiler import phasm_compile
|
||||||
from phasm.parser import phasm_parse
|
from phasm.parser import phasm_parse
|
||||||
from phasm.typer import phasm_type
|
from phasm.typer import phasm_type
|
||||||
|
from phasm.type3.entry import phasm_type3
|
||||||
from phasm import ourlang
|
from phasm import ourlang
|
||||||
from phasm import wasm
|
from phasm import wasm
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ class RunnerBase:
|
|||||||
Parses the Phasm code into an AST
|
Parses the Phasm code into an AST
|
||||||
"""
|
"""
|
||||||
self.phasm_ast = phasm_parse(self.phasm_code)
|
self.phasm_ast = phasm_parse(self.phasm_code)
|
||||||
|
phasm_type3(self.phasm_ast)
|
||||||
phasm_type(self.phasm_ast)
|
phasm_type(self.phasm_ast)
|
||||||
|
|
||||||
def compile_ast(self) -> None:
|
def compile_ast(self) -> None:
|
||||||
|
|||||||
@ -75,6 +75,7 @@ def testEntry() -> {type_}:
|
|||||||
assert 32.125 == result.returned_value
|
assert 32.125 == result.returned_value
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.skip('Awaiting result of Type3 experiment')
|
||||||
def test_module_constant_entanglement():
|
def test_module_constant_entanglement():
|
||||||
code_py = """
|
code_py = """
|
||||||
CONSTANT: u8 = 1000
|
CONSTANT: u8 = 1000
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from ..helpers import Suite
|
|||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64', ])
|
@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64', ])
|
||||||
def test_tuple_1(type_):
|
def test_module_constant_1(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
CONSTANT: ({type_}, ) = (65, )
|
CONSTANT: ({type_}, ) = (65, )
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ def helper(vector: ({type_}, )) -> {type_}:
|
|||||||
assert 65 == result.returned_value
|
assert 65 == result.returned_value
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
def test_tuple_6():
|
def test_module_constant_6():
|
||||||
code_py = """
|
code_py = """
|
||||||
CONSTANT: (u8, u8, u32, u32, u64, u64, ) = (11, 22, 3333, 4444, 555555, 666666, )
|
CONSTANT: (u8, u8, u32, u32, u64, u64, ) = (11, 22, 3333, 4444, 555555, 666666, )
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user