Compare commits
4 Commits
11fde4cb9e
...
bee0c845a8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bee0c845a8 | ||
|
|
44b95af4ba | ||
|
|
1da1adac9f | ||
|
|
a2e1dfd799 |
1
TODO.md
1
TODO.md
@ -29,7 +29,6 @@
|
|||||||
- Implemented Bounded: https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelude.html#t:Bounded
|
- Implemented Bounded: https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelude.html#t:Bounded
|
||||||
- Try to implement the min and max functions using select
|
- Try to implement the min and max functions using select
|
||||||
- Filter out methods that aren't used; other the other way around (easier?) only add __ methods when needed
|
- Filter out methods that aren't used; other the other way around (easier?) only add __ methods when needed
|
||||||
- Move UnaryOp.operator into type class methods
|
|
||||||
- Move foldr into type class methods
|
- Move foldr into type class methods
|
||||||
- PrimitiveType is just the type, nothing primitive about it (change the name). u32 are still basic types or something.
|
- PrimitiveType is just the type, nothing primitive about it (change the name). u32 are still basic types or something.
|
||||||
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
||||||
|
|||||||
@ -51,7 +51,7 @@ _CRC32_Table: u32[256] = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _crc32_f(crc: u32, byt: u8) -> u32:
|
def _crc32_f(crc: u32, byt: u8) -> u32:
|
||||||
return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ u32(byt)]
|
return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ extend(byt)]
|
||||||
|
|
||||||
@exported
|
@exported
|
||||||
def crc32(data: bytes) -> u32:
|
def crc32(data: bytes) -> u32:
|
||||||
|
|||||||
@ -75,16 +75,6 @@ def expression(inp: ourlang.Expression) -> str:
|
|||||||
if isinstance(inp, ourlang.VariableReference):
|
if isinstance(inp, ourlang.VariableReference):
|
||||||
return str(inp.variable.name)
|
return str(inp.variable.name)
|
||||||
|
|
||||||
if isinstance(inp, ourlang.UnaryOp):
|
|
||||||
if inp.operator == 'cast':
|
|
||||||
mtyp = type3(inp.type3)
|
|
||||||
if mtyp is None:
|
|
||||||
raise NotImplementedError(f'Casting to type {inp.type_var}')
|
|
||||||
|
|
||||||
return f'{mtyp}({expression(inp.right)})'
|
|
||||||
|
|
||||||
return f'{inp.operator}{expression(inp.right)}'
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.BinaryOp):
|
if isinstance(inp, ourlang.BinaryOp):
|
||||||
return f'{expression(inp.left)} {inp.operator.name} {expression(inp.right)}'
|
return f'{expression(inp.left)} {inp.operator.name} {expression(inp.right)}'
|
||||||
|
|
||||||
|
|||||||
@ -237,6 +237,28 @@ INSTANCES = {
|
|||||||
prelude.Sized_.methods['len']: {
|
prelude.Sized_.methods['len']: {
|
||||||
'a=bytes': stdlib_types.bytes_sized_len,
|
'a=bytes': stdlib_types.bytes_sized_len,
|
||||||
},
|
},
|
||||||
|
prelude.Extendable.methods['extend']: {
|
||||||
|
'a=u8,b=u32': stdlib_types.u8_u32_extend,
|
||||||
|
'a=u8,b=u64': stdlib_types.u8_u64_extend,
|
||||||
|
'a=u32,b=u64': stdlib_types.u32_u64_extend,
|
||||||
|
'a=i8,b=i32': stdlib_types.i8_i32_extend,
|
||||||
|
'a=i8,b=i64': stdlib_types.i8_i64_extend,
|
||||||
|
'a=i32,b=i64': stdlib_types.i32_i64_extend,
|
||||||
|
},
|
||||||
|
prelude.Extendable.methods['wrap']: {
|
||||||
|
'a=u8,b=u32': stdlib_types.u8_u32_wrap,
|
||||||
|
'a=u8,b=u64': stdlib_types.u8_u64_wrap,
|
||||||
|
'a=u32,b=u64': stdlib_types.u32_u64_wrap,
|
||||||
|
'a=i8,b=i32': stdlib_types.i8_i32_wrap,
|
||||||
|
'a=i8,b=i64': stdlib_types.i8_i64_wrap,
|
||||||
|
'a=i32,b=i64': stdlib_types.i32_i64_wrap,
|
||||||
|
},
|
||||||
|
prelude.Promotable.methods['promote']: {
|
||||||
|
'a=f32,b=f64': stdlib_types.f32_f64_promote,
|
||||||
|
},
|
||||||
|
prelude.Promotable.methods['demote']: {
|
||||||
|
'a=f32,b=f64': stdlib_types.f32_f64_demote,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||||
@ -296,7 +318,7 @@ def type3(inp: type3placeholders.Type3OrPlaceholder) -> wasm.WasmType:
|
|||||||
# And pointers are i32
|
# And pointers are i32
|
||||||
return wasm.WasmTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
if prelude.InternalPassAsPointer in inp.classes:
|
if (prelude.InternalPassAsPointer, (inp, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
return wasm.WasmTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
raise NotImplementedError(type3, inp)
|
raise NotImplementedError(type3, inp)
|
||||||
@ -344,7 +366,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
|||||||
|
|
||||||
assert element.type3 == exp_type3
|
assert element.type3 == exp_type3
|
||||||
|
|
||||||
if prelude.InternalPassAsPointer in exp_type3.classes:
|
if (prelude.InternalPassAsPointer, (exp_type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
mtyp = 'i32'
|
mtyp = 'i32'
|
||||||
else:
|
else:
|
||||||
assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||||
@ -413,7 +435,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if prelude.InternalPassAsPointer in inp.type3.classes:
|
if (prelude.InternalPassAsPointer, (inp.type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, ))
|
assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, ))
|
||||||
|
|
||||||
address = inp.variable.constant.data_block.address
|
address = inp.variable.constant.data_block.address
|
||||||
@ -457,18 +479,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
|
|
||||||
raise NotImplementedError(inp.operator, instance_key)
|
raise NotImplementedError(inp.operator, instance_key)
|
||||||
|
|
||||||
if isinstance(inp, ourlang.UnaryOp):
|
|
||||||
expression(wgn, inp.right)
|
|
||||||
|
|
||||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
|
||||||
|
|
||||||
if inp.operator == 'cast':
|
|
||||||
if inp.type3 == prelude.u32 and inp.right.type3 == prelude.u8:
|
|
||||||
# Nothing to do, you can use an u8 value as a u32 no problem
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp.type3, inp.operator)
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.FunctionCall):
|
if isinstance(inp, ourlang.FunctionCall):
|
||||||
for arg in inp.arguments:
|
for arg in inp.arguments:
|
||||||
expression(wgn, arg)
|
expression(wgn, arg)
|
||||||
@ -487,7 +497,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
|
|
||||||
instance_key = ','.join(
|
instance_key = ','.join(
|
||||||
f'{k.letter}={v.name}'
|
f'{k.letter}={v.name}'
|
||||||
for k, v in type_var_map.items()
|
for k, v in sorted(type_var_map.items(), key=lambda x: x[0].letter)
|
||||||
)
|
)
|
||||||
|
|
||||||
instance = INSTANCES.get(inp.function, {}).get(instance_key, None)
|
instance = INSTANCES.get(inp.function, {}).get(instance_key, None)
|
||||||
@ -560,7 +570,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
|
|
||||||
expression(wgn, inp.varref)
|
expression(wgn, inp.varref)
|
||||||
|
|
||||||
if prelude.InternalPassAsPointer in el_type.classes:
|
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
mtyp = 'i32'
|
mtyp = 'i32'
|
||||||
else:
|
else:
|
||||||
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||||
@ -972,7 +982,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
|||||||
# Store each member individually
|
# Store each member individually
|
||||||
for memname, mtyp3 in st_args.items():
|
for memname, mtyp3 in st_args.items():
|
||||||
mtyp: Optional[str]
|
mtyp: Optional[str]
|
||||||
if prelude.InternalPassAsPointer in mtyp3.classes:
|
if (prelude.InternalPassAsPointer, (mtyp3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
mtyp = 'i32'
|
mtyp = 'i32'
|
||||||
else:
|
else:
|
||||||
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
|
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
|
||||||
|
|||||||
@ -127,21 +127,6 @@ class VariableReference(Expression):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.variable = variable
|
self.variable = variable
|
||||||
|
|
||||||
class UnaryOp(Expression):
|
|
||||||
"""
|
|
||||||
A unary operator expression within a statement
|
|
||||||
"""
|
|
||||||
__slots__ = ('operator', 'right', )
|
|
||||||
|
|
||||||
operator: str
|
|
||||||
right: Expression
|
|
||||||
|
|
||||||
def __init__(self, operator: str, right: Expression) -> None:
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.operator = operator
|
|
||||||
self.right = right
|
|
||||||
|
|
||||||
class BinaryOp(Expression):
|
class BinaryOp(Expression):
|
||||||
"""
|
"""
|
||||||
A binary operator expression within a statement
|
A binary operator expression within a statement
|
||||||
|
|||||||
@ -29,7 +29,6 @@ from .ourlang import (
|
|||||||
StructDefinition,
|
StructDefinition,
|
||||||
Subscript,
|
Subscript,
|
||||||
TupleInstantiation,
|
TupleInstantiation,
|
||||||
UnaryOp,
|
|
||||||
VariableReference,
|
VariableReference,
|
||||||
)
|
)
|
||||||
from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES
|
from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES
|
||||||
@ -247,7 +246,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
||||||
|
|
||||||
return StructDefinition(prelude.struct(node.name, members, set()), node.lineno)
|
return StructDefinition(prelude.struct(node.name, members), node.lineno)
|
||||||
|
|
||||||
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
||||||
if not isinstance(node.target, ast.Name):
|
if not isinstance(node.target, ast.Name):
|
||||||
@ -389,19 +388,6 @@ class OurVisitor:
|
|||||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.right),
|
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.right),
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(node, ast.UnaryOp):
|
|
||||||
if isinstance(node.op, ast.UAdd):
|
|
||||||
operator = '+'
|
|
||||||
elif isinstance(node.op, ast.USub):
|
|
||||||
operator = '-'
|
|
||||||
else:
|
|
||||||
raise NotImplementedError(f'Operator {node.op}')
|
|
||||||
|
|
||||||
return UnaryOp(
|
|
||||||
operator,
|
|
||||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.operand),
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(node, ast.Compare):
|
if isinstance(node, ast.Compare):
|
||||||
if 1 < len(node.ops):
|
if 1 < len(node.ops):
|
||||||
raise NotImplementedError('Multiple operators')
|
raise NotImplementedError('Multiple operators')
|
||||||
@ -476,7 +462,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node} as expr in FunctionDef')
|
raise NotImplementedError(f'{node} as expr in FunctionDef')
|
||||||
|
|
||||||
def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Call) -> Union[Fold, FunctionCall, UnaryOp]:
|
def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Call) -> Union[Fold, FunctionCall]:
|
||||||
if node.keywords:
|
if node.keywords:
|
||||||
_raise_static_error(node, 'Keyword calling not supported') # Yet?
|
_raise_static_error(node, 'Keyword calling not supported') # Yet?
|
||||||
|
|
||||||
@ -489,16 +475,6 @@ class OurVisitor:
|
|||||||
|
|
||||||
if node.func.id in PRELUDE_METHODS:
|
if node.func.id in PRELUDE_METHODS:
|
||||||
func = PRELUDE_METHODS[node.func.id]
|
func = PRELUDE_METHODS[node.func.id]
|
||||||
elif node.func.id == 'u32':
|
|
||||||
if 1 != len(node.args):
|
|
||||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
|
||||||
|
|
||||||
unary_op = UnaryOp(
|
|
||||||
'cast',
|
|
||||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]),
|
|
||||||
)
|
|
||||||
unary_op.type3 = prelude.u32
|
|
||||||
return unary_op
|
|
||||||
elif node.func.id == 'foldl':
|
elif node.func.id == 'foldl':
|
||||||
if 3 != len(node.args):
|
if 3 != len(node.args):
|
||||||
_raise_static_error(node, f'Function {node.func.id} requires 3 arguments but {len(node.args)} are given')
|
_raise_static_error(node, f'Function {node.func.id} requires 3 arguments but {len(node.args)} are given')
|
||||||
|
|||||||
@ -3,7 +3,7 @@ The prelude are all the builtin types, type classes and methods
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from ..type3.functions import TypeVariable
|
from ..type3.functions import TypeVariable
|
||||||
from ..type3.typeclasses import Type3Class, instance_type_class
|
from ..type3.typeclasses import Type3Class
|
||||||
from ..type3.types import (
|
from ..type3.types import (
|
||||||
Type3,
|
Type3,
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
@ -11,40 +11,50 @@ from ..type3.types import (
|
|||||||
TypeConstructor_Tuple,
|
TypeConstructor_Tuple,
|
||||||
)
|
)
|
||||||
|
|
||||||
none = Type3('none', [])
|
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3, ...]]] = set()
|
||||||
|
|
||||||
|
|
||||||
|
def instance_type_class(cls: Type3Class, *typ: Type3) -> None:
|
||||||
|
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
|
||||||
|
|
||||||
|
# TODO: Check for required existing instantiations
|
||||||
|
|
||||||
|
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, tuple(typ), ))
|
||||||
|
|
||||||
|
none = Type3('none')
|
||||||
"""
|
"""
|
||||||
The none type, for when functions simply don't return anything. e.g., IO().
|
The none type, for when functions simply don't return anything. e.g., IO().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bool_ = Type3('bool', [])
|
bool_ = Type3('bool')
|
||||||
"""
|
"""
|
||||||
The bool type, either True or False
|
The bool type, either True or False
|
||||||
|
|
||||||
Suffixes with an underscores, as it's a Python builtin
|
Suffixes with an underscores, as it's a Python builtin
|
||||||
"""
|
"""
|
||||||
|
|
||||||
u8 = Type3('u8', [])
|
u8 = Type3('u8')
|
||||||
"""
|
"""
|
||||||
The unsigned 8-bit integer type.
|
The unsigned 8-bit integer type.
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^8.
|
Operations on variables employ modular arithmetic, with modulus 2^8.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
u32 = Type3('u32', [])
|
u32 = Type3('u32')
|
||||||
"""
|
"""
|
||||||
The unsigned 32-bit integer type.
|
The unsigned 32-bit integer type.
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^32.
|
Operations on variables employ modular arithmetic, with modulus 2^32.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
u64 = Type3('u64', [])
|
u64 = Type3('u64')
|
||||||
"""
|
"""
|
||||||
The unsigned 64-bit integer type.
|
The unsigned 64-bit integer type.
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^64.
|
Operations on variables employ modular arithmetic, with modulus 2^64.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
i8 = Type3('i8', [])
|
i8 = Type3('i8')
|
||||||
"""
|
"""
|
||||||
The signed 8-bit integer type.
|
The signed 8-bit integer type.
|
||||||
|
|
||||||
@ -52,7 +62,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
|
|||||||
with the middel point being 0.
|
with the middel point being 0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
i32 = Type3('i32', [])
|
i32 = Type3('i32')
|
||||||
"""
|
"""
|
||||||
The unsigned 32-bit integer type.
|
The unsigned 32-bit integer type.
|
||||||
|
|
||||||
@ -60,7 +70,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but
|
|||||||
with the middel point being 0.
|
with the middel point being 0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
i64 = Type3('i64', [])
|
i64 = Type3('i64')
|
||||||
"""
|
"""
|
||||||
The unsigned 64-bit integer type.
|
The unsigned 64-bit integer type.
|
||||||
|
|
||||||
@ -68,22 +78,25 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but
|
|||||||
with the middel point being 0.
|
with the middel point being 0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
f32 = Type3('f32', [])
|
f32 = Type3('f32')
|
||||||
"""
|
"""
|
||||||
A 32-bits IEEE 754 float, of 32 bits width.
|
A 32-bits IEEE 754 float, of 32 bits width.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
f64 = Type3('f64', [])
|
f64 = Type3('f64')
|
||||||
"""
|
"""
|
||||||
A 32-bits IEEE 754 float, of 64 bits width.
|
A 32-bits IEEE 754 float, of 64 bits width.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bytes_ = Type3('bytes', [])
|
bytes_ = Type3('bytes')
|
||||||
"""
|
"""
|
||||||
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
static_array = TypeConstructor_StaticArray('static_array', [], [])
|
def sa_on_create(typ: Type3) -> None:
|
||||||
|
instance_type_class(InternalPassAsPointer, typ)
|
||||||
|
|
||||||
|
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
|
||||||
"""
|
"""
|
||||||
A type constructor.
|
A type constructor.
|
||||||
|
|
||||||
@ -93,7 +106,10 @@ It should be applied with one argument. It has a runtime-dynamic length
|
|||||||
of the same type repeated.
|
of the same type repeated.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
tuple_ = TypeConstructor_Tuple('tuple', [], [])
|
def tp_on_create(typ: Type3) -> None:
|
||||||
|
instance_type_class(InternalPassAsPointer, typ)
|
||||||
|
|
||||||
|
tuple_ = TypeConstructor_Tuple('tuple', on_create=tp_on_create)
|
||||||
"""
|
"""
|
||||||
This is a fixed length piece of memory.
|
This is a fixed length piece of memory.
|
||||||
|
|
||||||
@ -101,7 +117,10 @@ It should be applied with zero or more arguments. It has a compile time
|
|||||||
determined length, and each argument can be different.
|
determined length, and each argument can be different.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
struct = TypeConstructor_Struct('struct', [], [])
|
def st_on_create(typ: Type3) -> None:
|
||||||
|
instance_type_class(InternalPassAsPointer, typ)
|
||||||
|
|
||||||
|
struct = TypeConstructor_Struct('struct', on_create=st_on_create)
|
||||||
"""
|
"""
|
||||||
This is like a tuple, but each argument is named, so that developers
|
This is like a tuple, but each argument is named, so that developers
|
||||||
can get and set fields by name.
|
can get and set fields by name.
|
||||||
@ -122,6 +141,7 @@ PRELUDE_TYPES: dict[str, Type3] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a = TypeVariable('a')
|
a = TypeVariable('a')
|
||||||
|
b = TypeVariable('b')
|
||||||
|
|
||||||
|
|
||||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
||||||
@ -130,9 +150,9 @@ Internal type class to keep track which types we pass arounds as a pointer.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
instance_type_class(InternalPassAsPointer, bytes_)
|
instance_type_class(InternalPassAsPointer, bytes_)
|
||||||
instance_type_class(InternalPassAsPointer, static_array)
|
# instance_type_class(InternalPassAsPointer, static_array)
|
||||||
instance_type_class(InternalPassAsPointer, tuple_)
|
# instance_type_class(InternalPassAsPointer, tuple_)
|
||||||
instance_type_class(InternalPassAsPointer, struct)
|
# instance_type_class(InternalPassAsPointer, struct)
|
||||||
|
|
||||||
Eq = Type3Class('Eq', [a], methods={}, operators={
|
Eq = Type3Class('Eq', [a], methods={}, operators={
|
||||||
'==': [a, a, bool_],
|
'==': [a, a, bool_],
|
||||||
@ -148,7 +168,6 @@ instance_type_class(Eq, i32)
|
|||||||
instance_type_class(Eq, i64)
|
instance_type_class(Eq, i64)
|
||||||
instance_type_class(Eq, f32)
|
instance_type_class(Eq, f32)
|
||||||
instance_type_class(Eq, f64)
|
instance_type_class(Eq, f64)
|
||||||
instance_type_class(Eq, static_array)
|
|
||||||
|
|
||||||
Ord = Type3Class('Ord', [a], methods={
|
Ord = Type3Class('Ord', [a], methods={
|
||||||
'min': [a, a, a],
|
'min': [a, a, a],
|
||||||
@ -248,6 +267,25 @@ Sized_ = Type3Class('Sized', [a], methods={
|
|||||||
|
|
||||||
instance_type_class(Sized_, bytes_)
|
instance_type_class(Sized_, bytes_)
|
||||||
|
|
||||||
|
Extendable = Type3Class('Extendable', [a, b], methods={
|
||||||
|
'extend': [a, b],
|
||||||
|
'wrap': [b, a],
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
instance_type_class(Extendable, u8, u32)
|
||||||
|
instance_type_class(Extendable, u8, u64)
|
||||||
|
instance_type_class(Extendable, u32, u64)
|
||||||
|
instance_type_class(Extendable, i8, i32)
|
||||||
|
instance_type_class(Extendable, i8, i64)
|
||||||
|
instance_type_class(Extendable, i32, i64)
|
||||||
|
|
||||||
|
Promotable = Type3Class('Promotable', [a, b], methods={
|
||||||
|
'promote': [a, b],
|
||||||
|
'demote': [b, a],
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
instance_type_class(Promotable, f32, f64)
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASSES = {
|
PRELUDE_TYPE_CLASSES = {
|
||||||
'Eq': Eq,
|
'Eq': Eq,
|
||||||
'Ord': Ord,
|
'Ord': Ord,
|
||||||
@ -257,6 +295,8 @@ PRELUDE_TYPE_CLASSES = {
|
|||||||
'Integral': Integral,
|
'Integral': Integral,
|
||||||
'Fractional': Fractional,
|
'Fractional': Fractional,
|
||||||
'Floating': Floating,
|
'Floating': Floating,
|
||||||
|
'Extendable': Extendable,
|
||||||
|
'Promotable': Promotable,
|
||||||
}
|
}
|
||||||
|
|
||||||
PRELUDE_OPERATORS = {
|
PRELUDE_OPERATORS = {
|
||||||
@ -279,4 +319,6 @@ PRELUDE_METHODS = {
|
|||||||
**IntNum.methods,
|
**IntNum.methods,
|
||||||
**NatNum.methods,
|
**NatNum.methods,
|
||||||
**Sized_.methods,
|
**Sized_.methods,
|
||||||
|
**Extendable.methods,
|
||||||
|
**Promotable.methods,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -865,3 +865,59 @@ def f64_intnum_neg(g: Generator) -> None:
|
|||||||
def bytes_sized_len(g: Generator) -> None:
|
def bytes_sized_len(g: Generator) -> None:
|
||||||
# The length is stored in the first 4 bytes
|
# The length is stored in the first 4 bytes
|
||||||
g.i32.load()
|
g.i32.load()
|
||||||
|
|
||||||
|
## ###
|
||||||
|
## Extendable
|
||||||
|
|
||||||
|
def u8_u32_extend(g: Generator) -> None:
|
||||||
|
# No-op
|
||||||
|
# u8 is already stored as u32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def u8_u64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
|
||||||
|
def u32_u64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
|
||||||
|
def i8_i32_extend(g: Generator) -> None:
|
||||||
|
# No-op
|
||||||
|
# i8 is already stored as i32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def i8_i64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_s()
|
||||||
|
|
||||||
|
def i32_i64_extend(g: Generator) -> None:
|
||||||
|
g.i64.extend_i32_s()
|
||||||
|
|
||||||
|
def u8_u32_wrap(g: Generator) -> None:
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def u8_u64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def u32_u64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
def i8_i32_wrap(g: Generator) -> None:
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def i8_i64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
def i32_i64_wrap(g: Generator) -> None:
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
## ###
|
||||||
|
## Promotable
|
||||||
|
|
||||||
|
def f32_f64_promote(g: Generator) -> None:
|
||||||
|
g.f64.promote_f32()
|
||||||
|
|
||||||
|
def f32_f64_demote(g: Generator) -> None:
|
||||||
|
g.f32.demote_f64()
|
||||||
|
|||||||
@ -45,7 +45,13 @@ class Context:
|
|||||||
Context for constraints
|
Context for constraints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ('type_class_instances_existing', )
|
||||||
|
|
||||||
|
# Constraint_TypeClassInstanceExists
|
||||||
|
type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[types.Type3, ...]]]
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.type_class_instances_existing = set()
|
||||||
|
|
||||||
class ConstraintBase:
|
class ConstraintBase:
|
||||||
"""
|
"""
|
||||||
@ -191,97 +197,68 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
|
|
||||||
raise NotImplementedError(exp_type)
|
raise NotImplementedError(exp_type)
|
||||||
|
|
||||||
class CastableConstraint(ConstraintBase):
|
|
||||||
"""
|
|
||||||
A type can be cast to another type
|
|
||||||
"""
|
|
||||||
__slots__ = ('from_type3', 'to_type3', )
|
|
||||||
|
|
||||||
from_type3: placeholders.Type3OrPlaceholder
|
|
||||||
to_type3: placeholders.Type3OrPlaceholder
|
|
||||||
|
|
||||||
def __init__(self, from_type3: placeholders.Type3OrPlaceholder, to_type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
|
||||||
super().__init__(comment=comment)
|
|
||||||
|
|
||||||
self.from_type3 = from_type3
|
|
||||||
self.to_type3 = to_type3
|
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
|
||||||
ftyp = self.from_type3
|
|
||||||
if isinstance(ftyp, placeholders.PlaceholderForType) and ftyp.resolve_as is not None:
|
|
||||||
ftyp = ftyp.resolve_as
|
|
||||||
|
|
||||||
ttyp = self.to_type3
|
|
||||||
if isinstance(ttyp, placeholders.PlaceholderForType) and ttyp.resolve_as is not None:
|
|
||||||
ttyp = ttyp.resolve_as
|
|
||||||
|
|
||||||
if isinstance(ftyp, placeholders.PlaceholderForType) or isinstance(ttyp, placeholders.PlaceholderForType):
|
|
||||||
return RequireTypeSubstitutes()
|
|
||||||
|
|
||||||
if ftyp is prelude.u8 and ttyp is prelude.u32:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return Error(f'Cannot cast {ftyp.name} to {ttyp.name}')
|
|
||||||
|
|
||||||
def human_readable(self) -> HumanReadableRet:
|
|
||||||
return (
|
|
||||||
'{to_type3}({from_type3})',
|
|
||||||
{
|
|
||||||
'to_type3': self.to_type3,
|
|
||||||
'from_type3': self.from_type3,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'CastableConstraint({repr(self.from_type3)}, {repr(self.to_type3)}, comment={repr(self.comment)})'
|
|
||||||
|
|
||||||
class MustImplementTypeClassConstraint(ConstraintBase):
|
class MustImplementTypeClassConstraint(ConstraintBase):
|
||||||
"""
|
"""
|
||||||
A type must implement a given type class
|
A type must implement a given type class
|
||||||
"""
|
"""
|
||||||
__slots__ = ('type_class3', 'type3', )
|
__slots__ = ('context', 'type_class3', 'types', )
|
||||||
|
|
||||||
|
context: Context
|
||||||
type_class3: Union[str, typeclasses.Type3Class]
|
type_class3: Union[str, typeclasses.Type3Class]
|
||||||
type3: placeholders.Type3OrPlaceholder
|
types: list[placeholders.Type3OrPlaceholder]
|
||||||
|
|
||||||
DATA = {
|
DATA = {
|
||||||
'bytes': {'Foldable'},
|
'bytes': {'Foldable'},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
def __init__(self, context: Context, type_class3: Union[str, typeclasses.Type3Class], types: list[placeholders.Type3OrPlaceholder], comment: Optional[str] = None) -> None:
|
||||||
super().__init__(comment=comment)
|
super().__init__(comment=comment)
|
||||||
|
|
||||||
|
self.context = context
|
||||||
self.type_class3 = type_class3
|
self.type_class3 = type_class3
|
||||||
self.type3 = type3
|
self.types = types
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
typ = self.type3
|
typ_list = []
|
||||||
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
|
for typ in self.types:
|
||||||
typ = typ.resolve_as
|
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
|
||||||
|
typ = typ.resolve_as
|
||||||
|
|
||||||
if isinstance(typ, placeholders.PlaceholderForType):
|
if isinstance(typ, placeholders.PlaceholderForType):
|
||||||
return RequireTypeSubstitutes()
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
|
typ_list.append(typ)
|
||||||
|
|
||||||
|
assert len(typ_list) == len(self.types)
|
||||||
|
|
||||||
if isinstance(self.type_class3, typeclasses.Type3Class):
|
if isinstance(self.type_class3, typeclasses.Type3Class):
|
||||||
if self.type_class3 in typ.classes:
|
key = (self.type_class3, tuple(typ_list), )
|
||||||
|
if key in self.context.type_class_instances_existing:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
if self.type_class3 in self.__class__.DATA.get(typ.name, set()):
|
if self.type_class3 in self.__class__.DATA.get(typ_list[0].name, set()):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return Error(f'{typ.name} does not implement the {self.type_class3} type class')
|
typ_cls_name = self.type_class3 if isinstance(self.type_class3, str) else self.type_class3.name
|
||||||
|
typ_name_list = ' '.join(x.name for x in typ_list)
|
||||||
|
return Error(f'Missing type class instantation: {typ_cls_name} {typ_name_list}')
|
||||||
|
|
||||||
def human_readable(self) -> HumanReadableRet:
|
def human_readable(self) -> HumanReadableRet:
|
||||||
|
keys = {
|
||||||
|
f'type{idx}': typ
|
||||||
|
for idx, typ in enumerate(self.types)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
'{type3} derives {type_class3}',
|
'Exists instance {type_class3} ' + ' '.join(f'{{{x}}}' for x in keys),
|
||||||
{
|
{
|
||||||
'type_class3': str(self.type_class3),
|
'type_class3': str(self.type_class3),
|
||||||
'type3': self.type3,
|
**keys,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'MustImplementTypeClassConstraint({repr(self.type_class3)}, {repr(self.type3)}, comment={repr(self.comment)})'
|
return f'MustImplementTypeClassConstraint({repr(self.type_class3)}, {repr(self.types)}, comment={repr(self.comment)})'
|
||||||
|
|
||||||
class LiteralFitsConstraint(ConstraintBase):
|
class LiteralFitsConstraint(ConstraintBase):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -12,7 +12,6 @@ from . import typeclasses as typeclasses
|
|||||||
from . import types as type3types
|
from . import types as type3types
|
||||||
from .constraints import (
|
from .constraints import (
|
||||||
CanBeSubscriptedConstraint,
|
CanBeSubscriptedConstraint,
|
||||||
CastableConstraint,
|
|
||||||
ConstraintBase,
|
ConstraintBase,
|
||||||
Context,
|
Context,
|
||||||
LiteralFitsConstraint,
|
LiteralFitsConstraint,
|
||||||
@ -25,6 +24,7 @@ ConstraintGenerator = Generator[ConstraintBase, None, None]
|
|||||||
|
|
||||||
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
|
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
|
||||||
ctx = Context()
|
ctx = Context()
|
||||||
|
ctx.type_class_instances_existing.update(prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING)
|
||||||
|
|
||||||
return [*module(ctx, inp)]
|
return [*module(ctx, inp)]
|
||||||
|
|
||||||
@ -48,14 +48,6 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
|||||||
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.UnaryOp):
|
|
||||||
if 'cast' == inp.operator:
|
|
||||||
yield from expression(ctx, inp.right)
|
|
||||||
yield CastableConstraint(inp.right.type3, inp.type3)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp, inp.operator)
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall):
|
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall):
|
||||||
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature
|
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature
|
||||||
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments
|
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments
|
||||||
@ -71,18 +63,16 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
|||||||
for call_arg in arguments:
|
for call_arg in arguments:
|
||||||
yield from expression(ctx, call_arg)
|
yield from expression(ctx, call_arg)
|
||||||
|
|
||||||
for type_var, constraint_list in signature.context.constraints.items():
|
for constraint in signature.context.constraints:
|
||||||
assert type_var in type_var_map # When can this happen?
|
if isinstance(constraint, functions.Constraint_TypeClassInstanceExists):
|
||||||
|
yield MustImplementTypeClassConstraint(
|
||||||
|
ctx,
|
||||||
|
constraint.type_class3,
|
||||||
|
[type_var_map[x] for x in constraint.types],
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
for constraint in constraint_list:
|
raise NotImplementedError(constraint)
|
||||||
if isinstance(constraint, functions.TypeVariableConstraint_TypeHasTypeClass):
|
|
||||||
yield MustImplementTypeClassConstraint(
|
|
||||||
constraint.type_class3,
|
|
||||||
type_var_map[type_var],
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
raise NotImplementedError(constraint)
|
|
||||||
|
|
||||||
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [inp], strict=True)):
|
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [inp], strict=True)):
|
||||||
if arg_no == len(arguments):
|
if arg_no == len(arguments):
|
||||||
@ -138,7 +128,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
|||||||
|
|
||||||
yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, inp.base.type3, inp.type3,
|
yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, inp.base.type3, inp.type3,
|
||||||
comment='foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b')
|
comment='foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b')
|
||||||
yield MustImplementTypeClassConstraint('Foldable', inp.iter.type3)
|
yield MustImplementTypeClassConstraint(ctx, 'Foldable', [inp.iter.type3])
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@ -34,26 +34,37 @@ class TypeVariable:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'TypeVariable({repr(self.letter)})'
|
return f'TypeVariable({repr(self.letter)})'
|
||||||
|
|
||||||
class TypeVariableConstraintBase:
|
class ConstraintBase:
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
class TypeVariableConstraint_TypeHasTypeClass(TypeVariableConstraintBase):
|
class Constraint_TypeClassInstanceExists(ConstraintBase):
|
||||||
__slots__ = ('type_class3', )
|
__slots__ = ('type_class3', 'types', )
|
||||||
|
|
||||||
def __init__(self, type_class3: 'Type3Class') -> None:
|
type_class3: 'Type3Class'
|
||||||
|
types: list[TypeVariable]
|
||||||
|
|
||||||
|
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
||||||
self.type_class3 = type_class3
|
self.type_class3 = type_class3
|
||||||
|
self.types = list(types)
|
||||||
|
|
||||||
|
# Sanity check. AFAIK, if you have a multi-parameter type class,
|
||||||
|
# you can only add a constraint by supplying types for all variables
|
||||||
|
assert len(self.type_class3.args) == len(self.types)
|
||||||
|
|
||||||
class TypeVariableContext:
|
class TypeVariableContext:
|
||||||
__slots__ = ('constraints', )
|
__slots__ = ('variables', 'constraints', )
|
||||||
|
|
||||||
constraints: dict[TypeVariable, list[TypeVariableConstraintBase]]
|
variables: set[TypeVariable]
|
||||||
|
constraints: list[ConstraintBase]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.constraints = {}
|
self.variables = set()
|
||||||
|
self.constraints = []
|
||||||
|
|
||||||
def __copy__(self) -> 'TypeVariableContext':
|
def __copy__(self) -> 'TypeVariableContext':
|
||||||
result = TypeVariableContext()
|
result = TypeVariableContext()
|
||||||
result.constraints.update(self.constraints)
|
result.variables.update(self.variables)
|
||||||
|
result.constraints.extend(self.constraints)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class FunctionSignature:
|
class FunctionSignature:
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
|
from typing import Dict, Iterable, List, Mapping, Optional, Union
|
||||||
|
|
||||||
from .functions import (
|
from .functions import (
|
||||||
|
Constraint_TypeClassInstanceExists,
|
||||||
FunctionSignature,
|
FunctionSignature,
|
||||||
TypeVariable,
|
TypeVariable,
|
||||||
TypeVariableConstraint_TypeHasTypeClass,
|
|
||||||
TypeVariableContext,
|
TypeVariableContext,
|
||||||
)
|
)
|
||||||
from .types import Type3, TypeConstructor, TypeConstructor_Struct
|
from .types import Type3
|
||||||
|
|
||||||
|
|
||||||
class Type3ClassMethod:
|
class Type3ClassMethod:
|
||||||
@ -43,17 +43,7 @@ class Type3Class:
|
|||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
|
|
||||||
context = TypeVariableContext()
|
context = TypeVariableContext()
|
||||||
for arg in args:
|
context.constraints.append(Constraint_TypeClassInstanceExists(self, args))
|
||||||
context.constraints[arg] = [
|
|
||||||
TypeVariableConstraint_TypeHasTypeClass(self)
|
|
||||||
]
|
|
||||||
|
|
||||||
# FIXME: Multi parameter class types
|
|
||||||
# To fix this, realise that an instantiation of a multi paramater type class
|
|
||||||
# means that the instantiation depends on the combination of type classes
|
|
||||||
# and so we can't store the type classes on the types anymore
|
|
||||||
# This also means constraints should store a tuple of types as its key
|
|
||||||
assert len(context.constraints) <= 1
|
|
||||||
|
|
||||||
self.methods = {
|
self.methods = {
|
||||||
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||||
@ -67,9 +57,3 @@ class Type3Class:
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def instance_type_class(cls: Type3Class, typ: Type3 | TypeConstructor[Any] | TypeConstructor_Struct) -> None:
|
|
||||||
if isinstance(typ, Type3):
|
|
||||||
typ.classes.add(cls)
|
|
||||||
else:
|
|
||||||
typ.type_classes.add(cls)
|
|
||||||
|
|||||||
@ -2,18 +2,13 @@
|
|||||||
Contains the final types for use in Phasm, as well as construtors.
|
Contains the final types for use in Phasm, as well as construtors.
|
||||||
"""
|
"""
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
|
||||||
Any,
|
Any,
|
||||||
|
Callable,
|
||||||
Generic,
|
Generic,
|
||||||
Iterable,
|
|
||||||
Set,
|
|
||||||
Tuple,
|
Tuple,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .typeclasses import Type3Class
|
|
||||||
|
|
||||||
|
|
||||||
class KindArgument:
|
class KindArgument:
|
||||||
pass
|
pass
|
||||||
@ -25,32 +20,18 @@ class Type3(KindArgument):
|
|||||||
(Having a separate name makes it easier to distinguish from
|
(Having a separate name makes it easier to distinguish from
|
||||||
Python's Type)
|
Python's Type)
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'classes', )
|
__slots__ = ('name', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
"""
|
"""
|
||||||
The name of the string, as parsed and outputted by codestyle.
|
The name of the string, as parsed and outputted by codestyle.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classes: Set['Type3Class']
|
def __init__(self, name: str) -> None:
|
||||||
"""
|
|
||||||
The type classes that this type implements
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name: str, classes: Iterable['Type3Class']) -> None:
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.classes = set(classes)
|
|
||||||
|
|
||||||
for cls in self.classes:
|
|
||||||
for inh_cls in cls.inherited_classes:
|
|
||||||
if inh_cls not in self.classes:
|
|
||||||
raise Exception(
|
|
||||||
f'No instance for ({inh_cls} {self.name})'
|
|
||||||
f'; required for ({cls} {self.name})'
|
|
||||||
)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Type3({repr(self.name)}, {repr(self.classes)})'
|
return f'Type3({repr(self.name)})'
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
@ -118,21 +99,16 @@ class TypeConstructor(Generic[T]):
|
|||||||
"""
|
"""
|
||||||
Base class for type construtors
|
Base class for type construtors
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
|
__slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
"""
|
"""
|
||||||
The name of the type constructor
|
The name of the type constructor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classes: Set['Type3Class']
|
on_create: Callable[[Type3], None]
|
||||||
"""
|
"""
|
||||||
The type classes that this constructor implements
|
Who to let know if a type is created
|
||||||
"""
|
|
||||||
|
|
||||||
type_classes: Set['Type3Class']
|
|
||||||
"""
|
|
||||||
The type classes that the constructed types implement
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_cache: dict[T, Type3]
|
_cache: dict[T, Type3]
|
||||||
@ -146,10 +122,9 @@ class TypeConstructor(Generic[T]):
|
|||||||
Sometimes we need to know the key that created a type.
|
Sometimes we need to know the key that created a type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None:
|
def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.classes = set(classes)
|
self.on_create = on_create
|
||||||
self.type_classes = set(type_classes)
|
|
||||||
|
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
self._reverse_cache = {}
|
self._reverse_cache = {}
|
||||||
@ -175,8 +150,9 @@ class TypeConstructor(Generic[T]):
|
|||||||
"""
|
"""
|
||||||
result = self._cache.get(key, None)
|
result = self._cache.get(key, None)
|
||||||
if result is None:
|
if result is None:
|
||||||
self._cache[key] = result = Type3(self.make_name(key), self.type_classes)
|
self._cache[key] = result = Type3(self.make_name(key))
|
||||||
self._reverse_cache[result] = key
|
self._reverse_cache[result] = key
|
||||||
|
self.on_create(result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -225,21 +201,16 @@ class TypeConstructor_Struct:
|
|||||||
"""
|
"""
|
||||||
Base class for type construtors
|
Base class for type construtors
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
|
__slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
"""
|
"""
|
||||||
The name of the type constructor
|
The name of the type constructor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
classes: Set['Type3Class']
|
on_create: Callable[[Type3], None]
|
||||||
"""
|
"""
|
||||||
The type classes that this constructor implements
|
Who to let know if a type is created
|
||||||
"""
|
|
||||||
|
|
||||||
type_classes: Set['Type3Class']
|
|
||||||
"""
|
|
||||||
The type classes that the constructed types implement
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_cache: dict[str, Type3]
|
_cache: dict[str, Type3]
|
||||||
@ -254,10 +225,9 @@ class TypeConstructor_Struct:
|
|||||||
used for making the type
|
used for making the type
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None:
|
def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.classes = set(classes)
|
self.on_create = on_create
|
||||||
self.type_classes = set(type_classes)
|
|
||||||
|
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
self._reverse_cache = {}
|
self._reverse_cache = {}
|
||||||
@ -270,8 +240,9 @@ class TypeConstructor_Struct:
|
|||||||
"""
|
"""
|
||||||
return self._reverse_cache.get(typ)
|
return self._reverse_cache.get(typ)
|
||||||
|
|
||||||
def __call__(self, name: str, args: dict[str, Type3], classes: Set['Type3Class']) -> Type3:
|
def __call__(self, name: str, args: dict[str, Type3]) -> Type3:
|
||||||
result = Type3(name, classes | self.type_classes)
|
result = Type3(name)
|
||||||
self._reverse_cache[result] = args
|
self._reverse_cache[result] = args
|
||||||
|
self.on_create(result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -74,6 +74,9 @@ class Generator_i32(Generator_i32i64):
|
|||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('i32', generator)
|
super().__init__('i32', generator)
|
||||||
|
|
||||||
|
# 2.4.1. Numeric Instructions
|
||||||
|
self.wrap_i64 = functools.partial(self.generator.add_statement, 'i32.wrap_i64')
|
||||||
|
|
||||||
class Generator_i64(Generator_i32i64):
|
class Generator_i64(Generator_i32i64):
|
||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('i64', generator)
|
super().__init__('i64', generator)
|
||||||
@ -132,10 +135,16 @@ class Generator_f32(Generator_f32f64):
|
|||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('f32', generator)
|
super().__init__('f32', generator)
|
||||||
|
|
||||||
|
# 2.4.1 Numeric Instructions
|
||||||
|
self.demote_f64 = functools.partial(self.generator.add_statement, 'f32.demote_f64')
|
||||||
|
|
||||||
class Generator_f64(Generator_f32f64):
|
class Generator_f64(Generator_f32f64):
|
||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
super().__init__('f64', generator)
|
super().__init__('f64', generator)
|
||||||
|
|
||||||
|
# 2.4.1 Numeric Instructions
|
||||||
|
self.promote_f32 = functools.partial(self.generator.add_statement, 'f64.promote_f32')
|
||||||
|
|
||||||
class Generator_Local:
|
class Generator_Local:
|
||||||
def __init__(self, generator: 'Generator') -> None:
|
def __init__(self, generator: 'Generator') -> None:
|
||||||
self.generator = generator
|
self.generator = generator
|
||||||
|
|||||||
@ -264,6 +264,13 @@ def _load_memory_stored_returned_value(
|
|||||||
|
|
||||||
if ret_type3 in (prelude.i8, prelude.i32, prelude.i64):
|
if ret_type3 in (prelude.i8, prelude.i32, prelude.i64):
|
||||||
assert isinstance(wasm_value, int), wasm_value
|
assert isinstance(wasm_value, int), wasm_value
|
||||||
|
|
||||||
|
if ret_type3 is prelude.i8:
|
||||||
|
# Values are actually i32
|
||||||
|
# Have to reinterpret to load proper value
|
||||||
|
data = struct.pack('<i', wasm_value)
|
||||||
|
wasm_value, = struct.unpack('<bxxx', data)
|
||||||
|
|
||||||
return wasm_value
|
return wasm_value
|
||||||
|
|
||||||
if ret_type3 in (prelude.u8, prelude.u32, prelude.u64):
|
if ret_type3 in (prelude.u8, prelude.u32, prelude.u64):
|
||||||
@ -354,7 +361,7 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
|
|||||||
|
|
||||||
return _load_bytes_from_address(runner, typ, adr)
|
return _load_bytes_from_address(runner, typ, adr)
|
||||||
|
|
||||||
if prelude.InternalPassAsPointer in typ.classes:
|
if (prelude.InternalPassAsPointer, (typ, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
# Note: For applied types, inp should contain a 4 byte pointer
|
# Note: For applied types, inp should contain a 4 byte pointer
|
||||||
assert len(inp) == 4
|
assert len(inp) == 4
|
||||||
adr = struct.unpack('<I', inp)[0]
|
adr = struct.unpack('<I', inp)[0]
|
||||||
|
|||||||
@ -49,7 +49,7 @@ def testEntry(a: bytes, b: bytes) -> u8:
|
|||||||
def test_foldl_3():
|
def test_foldl_3():
|
||||||
code_py = """
|
code_py = """
|
||||||
def xor(l: u32, r: u8) -> u32:
|
def xor(l: u32, r: u8) -> u32:
|
||||||
return l ^ u32(r)
|
return l ^ extend(r)
|
||||||
|
|
||||||
@exported
|
@exported
|
||||||
def testEntry(a: bytes) -> u32:
|
def testEntry(a: bytes) -> u32:
|
||||||
|
|||||||
@ -29,7 +29,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
|
|||||||
return x == y
|
return x == y
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match='Foo does not implement the Eq type class'):
|
with pytest.raises(Type3Exception, match='Missing type class instantation: Eq Foo'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@ -111,7 +111,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
|
|||||||
return x != y
|
return x != y
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match='Foo does not implement the Eq type class'):
|
with pytest.raises(Type3Exception, match='Missing type class instantation: Eq Foo'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
|
|||||||
112
tests/integration/test_lang/test_extendable.py
Normal file
112
tests/integration/test_lang/test_extendable.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from phasm.type3.entry import Type3Exception
|
||||||
|
|
||||||
|
from ..helpers import Suite
|
||||||
|
|
||||||
|
EXTENTABLE = [
|
||||||
|
('u8', 'u32', ),
|
||||||
|
('u8', 'u64', ),
|
||||||
|
('u32', 'u64', ),
|
||||||
|
|
||||||
|
('i8', 'i32', ),
|
||||||
|
('i8', 'i64', ),
|
||||||
|
('i32', 'i64', ),
|
||||||
|
]
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_extend_not_implemented():
|
||||||
|
code_py = """
|
||||||
|
class Foo:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
class Baz:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry(x: Foo) -> Baz:
|
||||||
|
return extend(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(Type3Exception, match='Missing type class instantation: Extendable Foo Baz'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_from,ext_to', EXTENTABLE)
|
||||||
|
def test_extend_ok(ext_from,ext_to):
|
||||||
|
code_py = f"""
|
||||||
|
CONSTANT: {ext_from} = 10
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {ext_to}:
|
||||||
|
return extend(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10 == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_from,in_put,ext_to,exp_out', [
|
||||||
|
('u8', 241, 'u32', 241),
|
||||||
|
('u32', 4059165169, 'u64', 4059165169),
|
||||||
|
('u8', 241, 'u64', 241),
|
||||||
|
|
||||||
|
('i8', 113, 'i32', 113),
|
||||||
|
('i32', 1911681521, 'i64', 1911681521),
|
||||||
|
('i8', 113, 'i64', 113),
|
||||||
|
|
||||||
|
('i8', -15, 'i32', -15),
|
||||||
|
('i32', -15, 'i64', -15),
|
||||||
|
('i8', -15, 'i64', -15),
|
||||||
|
|
||||||
|
])
|
||||||
|
def test_extend_results(ext_from, ext_to, in_put, exp_out):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry(x: {ext_from}) -> {ext_to}:
|
||||||
|
return extend(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code(in_put)
|
||||||
|
|
||||||
|
assert exp_out == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_from,ext_to', EXTENTABLE)
|
||||||
|
def test_wrap_ok(ext_from,ext_to):
|
||||||
|
code_py = f"""
|
||||||
|
CONSTANT: {ext_to} = 10
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> {ext_from}:
|
||||||
|
return wrap(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10 == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('ext_to,in_put,ext_from,exp_out', [
|
||||||
|
('u32', 0xF1F1F1F1, 'u8', 0xF1),
|
||||||
|
('u64', 0xF1F1F1F1F1F1F1F1, 'u32', 0xF1F1F1F1),
|
||||||
|
('u64', 0xF1F1F1F1F1F1F1F1, 'u8', 0xF1),
|
||||||
|
|
||||||
|
('i32', 0xF1F1F171, 'i8', 113),
|
||||||
|
('i32', 0xF1F1F1F1, 'i8', -15),
|
||||||
|
('i64', 0x71F1F1F171F1F1F1, 'i32', 1911681521),
|
||||||
|
('i64', 0x71F1F1F1F1F1F1F1, 'i32', -235802127),
|
||||||
|
('i64', 0xF1F1F1F1F1F1F171, 'i8', 113),
|
||||||
|
('i64', 0xF1F1F1F1F1F1F1F1, 'i8', -15),
|
||||||
|
])
|
||||||
|
def test_wrap_results(ext_from, ext_to, in_put, exp_out):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry(x: {ext_to}) -> {ext_from}:
|
||||||
|
return wrap(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code(in_put)
|
||||||
|
|
||||||
|
assert exp_out == result.returned_value
|
||||||
@ -27,7 +27,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
|
|||||||
return x + y
|
return x + y
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with pytest.raises(Type3Exception, match='Foo does not implement the NatNum type class'):
|
with pytest.raises(Type3Exception, match='Missing type class instantation: NatNum Foo'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
|
|||||||
51
tests/integration/test_lang/test_promotable.py
Normal file
51
tests/integration/test_lang/test_promotable.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from phasm.type3.entry import Type3Exception
|
||||||
|
|
||||||
|
from ..helpers import Suite
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_promote_not_implemented():
|
||||||
|
code_py = """
|
||||||
|
class Foo:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
class Baz:
|
||||||
|
val: i32
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry(x: Foo) -> Baz:
|
||||||
|
return promote(x)
|
||||||
|
"""
|
||||||
|
|
||||||
|
with pytest.raises(Type3Exception, match='Missing type class instantation: Promotable Foo Baz'):
|
||||||
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_promote_ok():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: f32 = 10.5
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> f64:
|
||||||
|
return promote(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10.5 == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_demote_ok():
|
||||||
|
code_py = """
|
||||||
|
CONSTANT: f64 = 10.5
|
||||||
|
|
||||||
|
@exported
|
||||||
|
def testEntry() -> f32:
|
||||||
|
return demote(CONSTANT)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
|
assert 10.5 == result.returned_value
|
||||||
Loading…
x
Reference in New Issue
Block a user