Compare commits
3 Commits
b40a6a4cdd
...
a2774d0fa6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2774d0fa6 | ||
|
|
11fde4cb9e | ||
|
|
c8009403c4 |
@ -116,10 +116,6 @@ def expression(inp: ourlang.Expression) -> str:
|
|||||||
if isinstance(inp, ourlang.AccessStructMember):
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
return f'{expression(inp.varref)}.{inp.member}'
|
return f'{expression(inp.varref)}.{inp.member}'
|
||||||
|
|
||||||
if isinstance(inp, ourlang.Fold):
|
|
||||||
fold_name = 'foldl' if ourlang.Fold.Direction.LEFT == inp.dir else 'foldr'
|
|
||||||
return f'{fold_name}({inp.func.name}, {expression(inp.base)}, {expression(inp.iter)})'
|
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp)
|
raise NotImplementedError(expression, inp)
|
||||||
|
|
||||||
def statement(inp: ourlang.Statement) -> Statements:
|
def statement(inp: ourlang.Statement) -> Statements:
|
||||||
|
|||||||
@ -4,10 +4,11 @@ This module contains the code to convert parsed Ourlang into WebAssembly code
|
|||||||
import struct
|
import struct
|
||||||
from typing import Dict, List, Optional
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
from . import codestyle, ourlang, prelude, wasm
|
from . import ourlang, prelude, wasm
|
||||||
from .runtime import calculate_alloc_size, calculate_member_offset
|
from .runtime import calculate_alloc_size, calculate_member_offset
|
||||||
from .stdlib import alloc as stdlib_alloc
|
from .stdlib import alloc as stdlib_alloc
|
||||||
from .stdlib import types as stdlib_types
|
from .stdlib import types as stdlib_types
|
||||||
|
from .type3 import functions as type3functions
|
||||||
from .type3 import placeholders as type3placeholders
|
from .type3 import placeholders as type3placeholders
|
||||||
from .type3 import typeclasses as type3classes
|
from .type3 import typeclasses as type3classes
|
||||||
from .type3 import types as type3types
|
from .type3 import types as type3types
|
||||||
@ -335,7 +336,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
|||||||
|
|
||||||
# Store each element individually
|
# Store each element individually
|
||||||
offset = 0
|
offset = 0
|
||||||
for element, exp_type3 in zip(inp.elements, args):
|
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
||||||
if isinstance(exp_type3, type3placeholders.PlaceholderForType):
|
if isinstance(exp_type3, type3placeholders.PlaceholderForType):
|
||||||
assert exp_type3.resolve_as is not None
|
assert exp_type3.resolve_as is not None
|
||||||
assert isinstance(exp_type3.resolve_as, type3types.Type3)
|
assert isinstance(exp_type3.resolve_as, type3types.Type3)
|
||||||
@ -434,10 +435,10 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
|
|
||||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
type_var_map: Dict[type3classes.TypeVariable, type3types.Type3] = {}
|
type_var_map: Dict[type3functions.TypeVariable, type3types.Type3] = {}
|
||||||
|
|
||||||
for type_var, arg_expr in zip(inp.operator.signature, [inp.left, inp.right, inp]):
|
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
|
||||||
if not isinstance(type_var, type3classes.TypeVariable):
|
if not isinstance(type_var, type3functions.TypeVariable):
|
||||||
# Fixed type, not part of the lookup requirements
|
# Fixed type, not part of the lookup requirements
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -476,8 +477,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
# FIXME: Duplicate code with BinaryOp
|
# FIXME: Duplicate code with BinaryOp
|
||||||
type_var_map = {}
|
type_var_map = {}
|
||||||
|
|
||||||
for type_var, arg_expr in zip(inp.function.signature, inp.arguments + [inp]):
|
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
|
||||||
if not isinstance(type_var, type3classes.TypeVariable):
|
if not isinstance(type_var, type3functions.TypeVariable):
|
||||||
# Fixed type, not part of the lookup requirements
|
# Fixed type, not part of the lookup requirements
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -587,89 +588,85 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
)))
|
)))
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.Fold):
|
|
||||||
expression_fold(wgn, inp)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp)
|
raise NotImplementedError(expression, inp)
|
||||||
|
|
||||||
def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
|
# def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
|
||||||
"""
|
# """
|
||||||
Compile: Fold expression
|
# Compile: Fold expression
|
||||||
"""
|
# """
|
||||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
# assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if inp.iter.type3 is not prelude.bytes_:
|
# if inp.iter.type3 is not prelude.bytes_:
|
||||||
raise NotImplementedError(expression_fold, inp, inp.iter.type3)
|
# raise NotImplementedError(expression_fold, inp, inp.iter.type3)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='acu :: u8')
|
# wgn.add_statement('nop', comment='acu :: u8')
|
||||||
acu_var = wgn.temp_var_u8(f'fold_{codestyle.type3(inp.type3)}_acu')
|
# acu_var = wgn.temp_var_u8(f'fold_{codestyle.type3(inp.type3)}_acu')
|
||||||
wgn.add_statement('nop', comment='adr :: bytes*')
|
# wgn.add_statement('nop', comment='adr :: bytes*')
|
||||||
adr_var = wgn.temp_var_i32('fold_i32_adr')
|
# adr_var = wgn.temp_var_i32('fold_i32_adr')
|
||||||
wgn.add_statement('nop', comment='len :: i32')
|
# wgn.add_statement('nop', comment='len :: i32')
|
||||||
len_var = wgn.temp_var_i32('fold_i32_len')
|
# len_var = wgn.temp_var_i32('fold_i32_len')
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='acu = base')
|
# wgn.add_statement('nop', comment='acu = base')
|
||||||
expression(wgn, inp.base)
|
# expression(wgn, inp.base)
|
||||||
wgn.local.set(acu_var)
|
# wgn.local.set(acu_var)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='adr = adr(iter)')
|
# wgn.add_statement('nop', comment='adr = adr(iter)')
|
||||||
expression(wgn, inp.iter)
|
# expression(wgn, inp.iter)
|
||||||
wgn.local.set(adr_var)
|
# wgn.local.set(adr_var)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='len = len(iter)')
|
# wgn.add_statement('nop', comment='len = len(iter)')
|
||||||
wgn.local.get(adr_var)
|
# wgn.local.get(adr_var)
|
||||||
wgn.i32.load()
|
# wgn.i32.load()
|
||||||
wgn.local.set(len_var)
|
# wgn.local.set(len_var)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='i = 0')
|
# wgn.add_statement('nop', comment='i = 0')
|
||||||
idx_var = wgn.temp_var_i32(f'fold_{codestyle.type3(inp.type3)}_idx')
|
# idx_var = wgn.temp_var_i32(f'fold_{codestyle.type3(inp.type3)}_idx')
|
||||||
wgn.i32.const(0)
|
# wgn.i32.const(0)
|
||||||
wgn.local.set(idx_var)
|
# wgn.local.set(idx_var)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='if i < len')
|
# wgn.add_statement('nop', comment='if i < len')
|
||||||
wgn.local.get(idx_var)
|
# wgn.local.get(idx_var)
|
||||||
wgn.local.get(len_var)
|
# wgn.local.get(len_var)
|
||||||
wgn.i32.lt_u()
|
# wgn.i32.lt_u()
|
||||||
with wgn.if_():
|
# with wgn.if_():
|
||||||
# From here on, adr_var is the address of byte we're referencing
|
# # From here on, adr_var is the address of byte we're referencing
|
||||||
# This is akin to calling stdlib_types.__subscript_bytes__
|
# # This is akin to calling stdlib_types.__subscript_bytes__
|
||||||
# But since we already know we are inside of bounds,
|
# # But since we already know we are inside of bounds,
|
||||||
# can just bypass it and load the memory directly.
|
# # can just bypass it and load the memory directly.
|
||||||
wgn.local.get(adr_var)
|
# wgn.local.get(adr_var)
|
||||||
wgn.i32.const(3) # Bytes header -1, since we do a +1 every loop
|
# wgn.i32.const(3) # Bytes header -1, since we do a +1 every loop
|
||||||
wgn.i32.add()
|
# wgn.i32.add()
|
||||||
wgn.local.set(adr_var)
|
# wgn.local.set(adr_var)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='while True')
|
# wgn.add_statement('nop', comment='while True')
|
||||||
with wgn.loop():
|
# with wgn.loop():
|
||||||
wgn.add_statement('nop', comment='acu = func(acu, iter[i])')
|
# wgn.add_statement('nop', comment='acu = func(acu, iter[i])')
|
||||||
wgn.local.get(acu_var)
|
# wgn.local.get(acu_var)
|
||||||
|
|
||||||
# Get the next byte, write back the address
|
# # Get the next byte, write back the address
|
||||||
wgn.local.get(adr_var)
|
# wgn.local.get(adr_var)
|
||||||
wgn.i32.const(1)
|
# wgn.i32.const(1)
|
||||||
wgn.i32.add()
|
# wgn.i32.add()
|
||||||
wgn.local.tee(adr_var)
|
# wgn.local.tee(adr_var)
|
||||||
wgn.i32.load8_u()
|
# wgn.i32.load8_u()
|
||||||
|
|
||||||
wgn.add_statement('call', f'${inp.func.name}')
|
# wgn.add_statement('call', f'${inp.func.name}')
|
||||||
wgn.local.set(acu_var)
|
# wgn.local.set(acu_var)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='i = i + 1')
|
# wgn.add_statement('nop', comment='i = i + 1')
|
||||||
wgn.local.get(idx_var)
|
# wgn.local.get(idx_var)
|
||||||
wgn.i32.const(1)
|
# wgn.i32.const(1)
|
||||||
wgn.i32.add()
|
# wgn.i32.add()
|
||||||
wgn.local.set(idx_var)
|
# wgn.local.set(idx_var)
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='if i >= len: break')
|
# wgn.add_statement('nop', comment='if i >= len: break')
|
||||||
wgn.local.get(idx_var)
|
# wgn.local.get(idx_var)
|
||||||
wgn.local.get(len_var)
|
# wgn.local.get(len_var)
|
||||||
wgn.i32.lt_u()
|
# wgn.i32.lt_u()
|
||||||
wgn.br_if(0)
|
# wgn.br_if(0)
|
||||||
|
|
||||||
# return acu
|
# # return acu
|
||||||
wgn.local.get(acu_var)
|
# wgn.local.get(acu_var)
|
||||||
|
|
||||||
def statement_return(wgn: WasmGenerator, inp: ourlang.StatementReturn) -> None:
|
def statement_return(wgn: WasmGenerator, inp: ourlang.StatementReturn) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
Contains the syntax tree for ourlang
|
Contains the syntax tree for ourlang
|
||||||
"""
|
"""
|
||||||
import enum
|
|
||||||
from typing import Dict, Iterable, List, Optional, Union
|
from typing import Dict, Iterable, List, Optional, Union
|
||||||
|
|
||||||
from . import prelude
|
from . import prelude
|
||||||
|
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||||
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
|
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
from .type3.typeclasses import Type3ClassMethod
|
||||||
from .type3.types import Type3
|
from .type3.types import Type3
|
||||||
@ -222,36 +222,6 @@ class AccessStructMember(Expression):
|
|||||||
self.struct_type3 = struct_type3
|
self.struct_type3 = struct_type3
|
||||||
self.member = member
|
self.member = member
|
||||||
|
|
||||||
class Fold(Expression):
|
|
||||||
"""
|
|
||||||
A (left or right) fold
|
|
||||||
"""
|
|
||||||
class Direction(enum.Enum):
|
|
||||||
"""
|
|
||||||
Which direction to fold in
|
|
||||||
"""
|
|
||||||
LEFT = 0
|
|
||||||
RIGHT = 1
|
|
||||||
|
|
||||||
dir: Direction
|
|
||||||
func: 'Function'
|
|
||||||
base: Expression
|
|
||||||
iter: Expression
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
dir_: Direction,
|
|
||||||
func: 'Function',
|
|
||||||
base: Expression,
|
|
||||||
iter_: Expression,
|
|
||||||
) -> None:
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.dir = dir_
|
|
||||||
self.func = func
|
|
||||||
self.base = base
|
|
||||||
self.iter = iter_
|
|
||||||
|
|
||||||
class Statement:
|
class Statement:
|
||||||
"""
|
"""
|
||||||
A statement within a function
|
A statement within a function
|
||||||
@ -300,21 +270,22 @@ class FunctionParam:
|
|||||||
name: str
|
name: str
|
||||||
type3: Type3OrPlaceholder
|
type3: Type3OrPlaceholder
|
||||||
|
|
||||||
def __init__(self, name: str, type3: Optional[Type3]) -> None:
|
def __init__(self, name: str, type3: Type3) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type3 = PlaceholderForType([self]) if type3 is None else type3
|
self.type3 = type3
|
||||||
|
|
||||||
class Function:
|
class Function:
|
||||||
"""
|
"""
|
||||||
A function processes input and produces output
|
A function processes input and produces output
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'returns_type3', 'posonlyargs', )
|
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'signature', 'returns_type3', 'posonlyargs', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
exported: bool
|
exported: bool
|
||||||
imported: Optional[str]
|
imported: Optional[str]
|
||||||
statements: List[Statement]
|
statements: List[Statement]
|
||||||
|
signature: FunctionSignature
|
||||||
returns_type3: Type3
|
returns_type3: Type3
|
||||||
posonlyargs: List[FunctionParam]
|
posonlyargs: List[FunctionParam]
|
||||||
|
|
||||||
@ -324,6 +295,7 @@ class Function:
|
|||||||
self.exported = False
|
self.exported = False
|
||||||
self.imported = None
|
self.imported = None
|
||||||
self.statements = []
|
self.statements = []
|
||||||
|
self.signature = FunctionSignature(TypeVariableContext(), [])
|
||||||
self.returns_type3 = prelude.none # FIXME: This could be a placeholder
|
self.returns_type3 = prelude.none # FIXME: This could be a placeholder
|
||||||
self.posonlyargs = []
|
self.posonlyargs = []
|
||||||
|
|
||||||
@ -354,12 +326,14 @@ class StructConstructor(Function):
|
|||||||
def __init__(self, struct_type3: Type3) -> None:
|
def __init__(self, struct_type3: Type3) -> None:
|
||||||
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
||||||
|
|
||||||
self.returns_type3 = struct_type3
|
|
||||||
|
|
||||||
st_args = prelude.struct.did_construct(struct_type3)
|
st_args = prelude.struct.did_construct(struct_type3)
|
||||||
assert st_args is not None
|
assert st_args is not None
|
||||||
for mem, typ in st_args.items():
|
for mem, typ in st_args.items():
|
||||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||||
|
self.signature.args.append(typ)
|
||||||
|
|
||||||
|
self.returns_type3 = struct_type3
|
||||||
|
self.signature.args.append(struct_type3)
|
||||||
|
|
||||||
self.struct_type3 = struct_type3
|
self.struct_type3 = struct_type3
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,6 @@ from .ourlang import (
|
|||||||
ConstantStruct,
|
ConstantStruct,
|
||||||
ConstantTuple,
|
ConstantTuple,
|
||||||
Expression,
|
Expression,
|
||||||
Fold,
|
|
||||||
Function,
|
Function,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
FunctionParam,
|
FunctionParam,
|
||||||
@ -159,9 +158,18 @@ class OurVisitor:
|
|||||||
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
||||||
|
|
||||||
for arg in node.args.args:
|
for arg in node.args.args:
|
||||||
|
if arg.annotation is None:
|
||||||
|
_raise_static_error(node, 'Must give a argument type')
|
||||||
|
|
||||||
|
arg_type = self.visit_type(module, arg.annotation)
|
||||||
|
|
||||||
|
# if isisntance(arg_type, TypeVariable):
|
||||||
|
# arg_type = PlaceHolderForType()
|
||||||
|
|
||||||
|
function.signature.args.append(arg_type)
|
||||||
function.posonlyargs.append(FunctionParam(
|
function.posonlyargs.append(FunctionParam(
|
||||||
arg.arg,
|
arg.arg,
|
||||||
self.visit_type(module, arg.annotation) if arg.annotation else None,
|
arg_type,
|
||||||
))
|
))
|
||||||
|
|
||||||
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
_not_implemented(not node.args.vararg, 'FunctionDef.args.vararg')
|
||||||
@ -202,11 +210,11 @@ class OurVisitor:
|
|||||||
else:
|
else:
|
||||||
function.imported = 'imports'
|
function.imported = 'imports'
|
||||||
|
|
||||||
if node.returns is not None: # Note: `-> None` would be a ast.Constant
|
if node.returns is None: # Note: `-> None` would be a ast.Constant
|
||||||
function.returns_type3 = self.visit_type(module, node.returns)
|
_raise_static_error(node, 'Must give a return type')
|
||||||
else:
|
return_type = self.visit_type(module, node.returns)
|
||||||
# FIXME: Mostly works already, needs to fix Function.returns_type3 and have it updated
|
function.signature.args.append(return_type)
|
||||||
raise NotImplementedError('Function without an explicit return type')
|
function.returns_type3 = return_type
|
||||||
|
|
||||||
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
_not_implemented(not node.type_comment, 'FunctionDef.type_comment')
|
||||||
|
|
||||||
@ -467,7 +475,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[FunctionCall, UnaryOp]:
|
||||||
if node.keywords:
|
if node.keywords:
|
||||||
_raise_static_error(node, 'Keyword calling not supported') # Yet?
|
_raise_static_error(node, 'Keyword calling not supported') # Yet?
|
||||||
|
|
||||||
@ -490,35 +498,35 @@ class OurVisitor:
|
|||||||
)
|
)
|
||||||
unary_op.type3 = prelude.u32
|
unary_op.type3 = prelude.u32
|
||||||
return unary_op
|
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')
|
||||||
|
|
||||||
# TODO: This is not generic, you cannot return a function
|
# # TODO: This is not generic, you cannot return a function
|
||||||
subnode = node.args[0]
|
# subnode = node.args[0]
|
||||||
if not isinstance(subnode, ast.Name):
|
# if not isinstance(subnode, ast.Name):
|
||||||
raise NotImplementedError(f'Calling methods that are not a name {subnode}')
|
# raise NotImplementedError(f'Calling methods that are not a name {subnode}')
|
||||||
if not isinstance(subnode.ctx, ast.Load):
|
# if not isinstance(subnode.ctx, ast.Load):
|
||||||
_raise_static_error(subnode, 'Must be load context')
|
# _raise_static_error(subnode, 'Must be load context')
|
||||||
if subnode.id not in module.functions:
|
# if subnode.id not in module.functions:
|
||||||
_raise_static_error(subnode, 'Reference to undefined function')
|
# _raise_static_error(subnode, 'Reference to undefined function')
|
||||||
func = module.functions[subnode.id]
|
# func = module.functions[subnode.id]
|
||||||
if 2 != len(func.posonlyargs):
|
# if 2 != len(func.posonlyargs):
|
||||||
_raise_static_error(node, f'Function {node.func.id} requires a function with 2 arguments but a function with {len(func.posonlyargs)} args is given')
|
# _raise_static_error(node, f'Function {node.func.id} requires a function with 2 arguments but a function with {len(func.posonlyargs)} args is given')
|
||||||
|
|
||||||
return Fold(
|
# return Fold(
|
||||||
Fold.Direction.LEFT,
|
# Fold.Direction.LEFT,
|
||||||
func,
|
# func,
|
||||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[1]),
|
# self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[1]),
|
||||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[2]),
|
# self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[2]),
|
||||||
)
|
# )
|
||||||
else:
|
else:
|
||||||
if node.func.id not in module.functions:
|
if node.func.id not in module.functions:
|
||||||
_raise_static_error(node, 'Call to undefined function')
|
_raise_static_error(node, 'Call to undefined function')
|
||||||
|
|
||||||
func = module.functions[node.func.id]
|
func = module.functions[node.func.id]
|
||||||
|
|
||||||
exp_arg_count = len(func.posonlyargs) if isinstance(func, Function) else len(func.signature) - 1
|
exp_arg_count = len(func.signature.args) - 1
|
||||||
|
|
||||||
if exp_arg_count != len(node.args):
|
if exp_arg_count != len(node.args):
|
||||||
_raise_static_error(node, f'Function {node.func.id} requires {exp_arg_count} arguments but {len(node.args)} are given')
|
_raise_static_error(node, f'Function {node.func.id} requires {exp_arg_count} arguments but {len(node.args)} are given')
|
||||||
|
|||||||
@ -2,7 +2,12 @@
|
|||||||
The prelude are all the builtin types, type classes and methods
|
The prelude are all the builtin types, type classes and methods
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..type3.typeclasses import Type3Class, TypeVariable, instance_type_class
|
from ..type3.typeclasses import (
|
||||||
|
Type3Class,
|
||||||
|
TypeConstructorVariable,
|
||||||
|
TypeVariable,
|
||||||
|
instance_type_class,
|
||||||
|
)
|
||||||
from ..type3.types import (
|
from ..type3.types import (
|
||||||
Type3,
|
Type3,
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
@ -121,7 +126,8 @@ PRELUDE_TYPES: dict[str, Type3] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a = TypeVariable('a')
|
a = TypeVariable('a')
|
||||||
|
b = TypeVariable('b')
|
||||||
|
t = TypeConstructorVariable('t')
|
||||||
|
|
||||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
||||||
"""
|
"""
|
||||||
@ -247,6 +253,10 @@ Sized_ = Type3Class('Sized', [a], methods={
|
|||||||
|
|
||||||
instance_type_class(Sized_, bytes_)
|
instance_type_class(Sized_, bytes_)
|
||||||
|
|
||||||
|
Foldable = Type3Class('Foldable', [t], methods={
|
||||||
|
'foldl': [[a, b, b], b, t(a), b],
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASSES = {
|
PRELUDE_TYPE_CLASSES = {
|
||||||
'Eq': Eq,
|
'Eq': Eq,
|
||||||
'Ord': Ord,
|
'Ord': Ord,
|
||||||
|
|||||||
@ -186,7 +186,7 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(arg, oth_arg)
|
SameTypeConstraint(arg, oth_arg)
|
||||||
for arg, oth_arg in zip(self.args, tp_args)
|
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
||||||
]
|
]
|
||||||
|
|
||||||
raise NotImplementedError(exp_type)
|
raise NotImplementedError(exp_type)
|
||||||
@ -241,14 +241,10 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
"""
|
"""
|
||||||
__slots__ = ('type_class3', 'type3', )
|
__slots__ = ('type_class3', 'type3', )
|
||||||
|
|
||||||
type_class3: Union[str, typeclasses.Type3Class]
|
type_class3: typeclasses.Type3Class
|
||||||
type3: placeholders.Type3OrPlaceholder
|
type3: placeholders.Type3OrPlaceholder
|
||||||
|
|
||||||
DATA = {
|
def __init__(self, type_class3: typeclasses.Type3Class, type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||||
'bytes': {'Foldable'},
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
|
||||||
super().__init__(comment=comment)
|
super().__init__(comment=comment)
|
||||||
|
|
||||||
self.type_class3 = type_class3
|
self.type_class3 = type_class3
|
||||||
@ -262,12 +258,8 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
if isinstance(typ, placeholders.PlaceholderForType):
|
if isinstance(typ, placeholders.PlaceholderForType):
|
||||||
return RequireTypeSubstitutes()
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
if isinstance(self.type_class3, typeclasses.Type3Class):
|
if self.type_class3 in typ.classes:
|
||||||
if self.type_class3 in typ.classes:
|
return None
|
||||||
return None
|
|
||||||
else:
|
|
||||||
if self.type_class3 in self.__class__.DATA.get(typ.name, set()):
|
|
||||||
return None
|
|
||||||
|
|
||||||
return Error(f'{typ.name} does not implement the {self.type_class3} type class')
|
return Error(f'{typ.name} does not implement the {self.type_class3} type class')
|
||||||
|
|
||||||
@ -369,11 +361,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(x, y)
|
LiteralFitsConstraint(x, y)
|
||||||
for x, y in zip(tp_args, self.literal.value)
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(x, y.type3)
|
SameTypeConstraint(x, y.type3)
|
||||||
for x, y in zip(tp_args, self.literal.value)
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@ -417,11 +409,11 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(x, y)
|
LiteralFitsConstraint(x, y)
|
||||||
for x, y in zip(st_args.values(), self.literal.value)
|
for x, y in zip(st_args.values(), self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
|
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
|
||||||
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value)
|
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|||||||
@ -6,8 +6,9 @@ The constraints solver can then try to resolve all constraints.
|
|||||||
from typing import Generator, List
|
from typing import Generator, List
|
||||||
|
|
||||||
from .. import ourlang, prelude
|
from .. import ourlang, prelude
|
||||||
|
from . import functions as functions
|
||||||
from . import placeholders as placeholders
|
from . import placeholders as placeholders
|
||||||
from . import typeclasses as type3typeclasses
|
from . import typeclasses as typeclasses
|
||||||
from . import types as type3types
|
from . import types as type3types
|
||||||
from .constraints import (
|
from .constraints import (
|
||||||
CanBeSubscriptedConstraint,
|
CanBeSubscriptedConstraint,
|
||||||
@ -55,81 +56,51 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
|||||||
|
|
||||||
raise NotImplementedError(expression, inp, inp.operator)
|
raise NotImplementedError(expression, inp, inp.operator)
|
||||||
|
|
||||||
if isinstance(inp, ourlang.BinaryOp):
|
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall):
|
||||||
|
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
|
||||||
|
|
||||||
|
func_name = f'({inp.operator.name})' if isinstance(inp, ourlang.BinaryOp) else inp.function.name
|
||||||
|
|
||||||
type_var_map = {
|
type_var_map = {
|
||||||
x: placeholders.PlaceholderForType([])
|
x: placeholders.PlaceholderForType([])
|
||||||
for x in inp.operator.signature
|
for x in signature.args
|
||||||
if isinstance(x, type3typeclasses.TypeVariable)
|
if isinstance(x, functions.TypeVariable)
|
||||||
}
|
}
|
||||||
|
|
||||||
yield from expression(ctx, inp.left)
|
for call_arg in arguments:
|
||||||
yield from expression(ctx, inp.right)
|
yield from expression(ctx, call_arg)
|
||||||
|
|
||||||
for type_var in inp.operator.type3_class.args:
|
for type_var, constraint_list in signature.context.constraints.items():
|
||||||
assert type_var in type_var_map # When can this happen?
|
assert type_var in type_var_map # When can this happen?
|
||||||
|
|
||||||
yield MustImplementTypeClassConstraint(
|
for constraint in constraint_list:
|
||||||
inp.operator.type3_class,
|
if isinstance(constraint, functions.TypeVariableConstraint_TypeHasTypeClass):
|
||||||
type_var_map[type_var],
|
yield MustImplementTypeClassConstraint(
|
||||||
)
|
constraint.type_class3,
|
||||||
|
type_var_map[type_var],
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
for sig_part, arg_expr in zip(inp.operator.signature, [inp.left, inp.right, inp]):
|
raise NotImplementedError(constraint)
|
||||||
if isinstance(sig_part, type3typeclasses.TypeVariable):
|
|
||||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
|
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [inp], strict=True)):
|
||||||
|
if arg_no == len(arguments):
|
||||||
|
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
|
||||||
|
else:
|
||||||
|
comment = f'The type of the value passed to argument {arg_no} of function {func_name} should match the type of that argument'
|
||||||
|
|
||||||
|
if isinstance(sig_part, functions.TypeVariable):
|
||||||
|
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3, comment=comment)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(sig_part, type3types.Type3):
|
if isinstance(sig_part, type3types.Type3):
|
||||||
yield SameTypeConstraint(sig_part, arg_expr.type3)
|
yield SameTypeConstraint(sig_part, arg_expr.type3, comment=comment)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
raise NotImplementedError(sig_part)
|
raise NotImplementedError(sig_part)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.FunctionCall):
|
|
||||||
if isinstance(inp.function, type3typeclasses.Type3ClassMethod):
|
|
||||||
# FIXME: Duplicate code with BinaryOp
|
|
||||||
|
|
||||||
type_var_map = {
|
|
||||||
x: placeholders.PlaceholderForType([])
|
|
||||||
for x in inp.function.signature
|
|
||||||
if isinstance(x, type3typeclasses.TypeVariable)
|
|
||||||
}
|
|
||||||
|
|
||||||
for call_arg in inp.arguments:
|
|
||||||
yield from expression(ctx, call_arg)
|
|
||||||
|
|
||||||
for type_var in inp.function.type3_class.args:
|
|
||||||
assert type_var in type_var_map # When can this happen?
|
|
||||||
|
|
||||||
yield MustImplementTypeClassConstraint(
|
|
||||||
inp.function.type3_class,
|
|
||||||
type_var_map[type_var],
|
|
||||||
)
|
|
||||||
|
|
||||||
for sig_part, arg_expr in zip(inp.function.signature, inp.arguments + [inp]):
|
|
||||||
if isinstance(sig_part, type3typeclasses.TypeVariable):
|
|
||||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(sig_part, type3types.Type3):
|
|
||||||
yield SameTypeConstraint(sig_part, arg_expr.type3)
|
|
||||||
continue
|
|
||||||
|
|
||||||
raise NotImplementedError(sig_part)
|
|
||||||
return
|
|
||||||
|
|
||||||
yield SameTypeConstraint(inp.function.returns_type3, inp.type3,
|
|
||||||
comment=f'The type of a function call to {inp.function.name} is the same as the type that the function returns')
|
|
||||||
|
|
||||||
assert len(inp.arguments) == len(inp.function.posonlyargs) # FIXME: Make this a Constraint
|
|
||||||
|
|
||||||
for fun_arg, call_arg in zip(inp.function.posonlyargs, inp.arguments):
|
|
||||||
yield from expression(ctx, call_arg)
|
|
||||||
yield SameTypeConstraint(fun_arg.type3, call_arg.type3,
|
|
||||||
comment=f'The type of the value passed to argument {fun_arg.name} of function {inp.function.name} should match the type of that argument')
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.TupleInstantiation):
|
if isinstance(inp, ourlang.TupleInstantiation):
|
||||||
r_type = []
|
r_type = []
|
||||||
for arg in inp.elements:
|
for arg in inp.elements:
|
||||||
@ -161,16 +132,6 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
|||||||
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.Fold):
|
|
||||||
yield from expression(ctx, inp.base)
|
|
||||||
yield from expression(ctx, inp.iter)
|
|
||||||
|
|
||||||
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')
|
|
||||||
yield MustImplementTypeClassConstraint('Foldable', inp.iter.type3)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp)
|
raise NotImplementedError(expression, inp)
|
||||||
|
|
||||||
def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementReturn) -> ConstraintGenerator:
|
def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementReturn) -> ConstraintGenerator:
|
||||||
|
|||||||
67
phasm/type3/functions.py
Normal file
67
phasm/type3/functions.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
from typing import TYPE_CHECKING, Any, Iterable, List, Union
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .typeclasses import Type3Class
|
||||||
|
from .types import Type3
|
||||||
|
|
||||||
|
|
||||||
|
class TypeVariable:
|
||||||
|
"""
|
||||||
|
Types variable are used in function definition.
|
||||||
|
|
||||||
|
They are used in places where you don't know the exact type.
|
||||||
|
They are different from PlaceholderForType, as those are instanced
|
||||||
|
during type checking. These type variables are used solely in the
|
||||||
|
function's definition
|
||||||
|
"""
|
||||||
|
__slots__ = ('letter', )
|
||||||
|
|
||||||
|
letter: str
|
||||||
|
|
||||||
|
def __init__(self, letter: str) -> None:
|
||||||
|
assert len(letter) == 1, f'{letter} is not a valid type variable'
|
||||||
|
self.letter = letter
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(self.letter)
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
if not isinstance(other, TypeVariable):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
return self.letter == other.letter
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'TypeVariable({repr(self.letter)})'
|
||||||
|
|
||||||
|
class TypeVariableConstraintBase:
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
class TypeVariableConstraint_TypeHasTypeClass(TypeVariableConstraintBase):
|
||||||
|
__slots__ = ('type_class3', )
|
||||||
|
|
||||||
|
def __init__(self, type_class3: 'Type3Class') -> None:
|
||||||
|
self.type_class3 = type_class3
|
||||||
|
|
||||||
|
class TypeVariableContext:
|
||||||
|
__slots__ = ('constraints', )
|
||||||
|
|
||||||
|
constraints: dict[TypeVariable, list[TypeVariableConstraintBase]]
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.constraints = {}
|
||||||
|
|
||||||
|
def __copy__(self) -> 'TypeVariableContext':
|
||||||
|
result = TypeVariableContext()
|
||||||
|
result.constraints.update(self.constraints)
|
||||||
|
return result
|
||||||
|
|
||||||
|
class FunctionSignature:
|
||||||
|
__slots__ = ('context', 'args', )
|
||||||
|
|
||||||
|
context: TypeVariableContext
|
||||||
|
args: List[Union['Type3', TypeVariable]]
|
||||||
|
|
||||||
|
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable]]) -> None:
|
||||||
|
self.context = context.__copy__()
|
||||||
|
self.args = list(args)
|
||||||
@ -1,5 +1,11 @@
|
|||||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
|
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
|
||||||
|
|
||||||
|
from .functions import (
|
||||||
|
FunctionSignature,
|
||||||
|
TypeVariable,
|
||||||
|
TypeVariableConstraint_TypeHasTypeClass,
|
||||||
|
TypeVariableContext,
|
||||||
|
)
|
||||||
from .types import Type3, TypeConstructor, TypeConstructor_Struct
|
from .types import Type3, TypeConstructor, TypeConstructor_Struct
|
||||||
|
|
||||||
|
|
||||||
@ -24,6 +30,27 @@ class TypeVariable:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'TypeVariable({repr(self.letter)})'
|
return f'TypeVariable({repr(self.letter)})'
|
||||||
|
|
||||||
|
class TypeConstructorVariable:
|
||||||
|
__slots__ = ('letter', )
|
||||||
|
|
||||||
|
letter: str
|
||||||
|
|
||||||
|
def __init__(self, letter: str) -> None:
|
||||||
|
assert len(letter) == 1, f'{letter} is not a valid type variable'
|
||||||
|
self.letter = letter
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(self.letter)
|
||||||
|
|
||||||
|
def __eq__(self, other: Any) -> bool:
|
||||||
|
if not isinstance(other, TypeVariable):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
return self.letter == other.letter
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'TypeVariable({repr(self.letter)})'
|
||||||
|
|
||||||
class TypeReference:
|
class TypeReference:
|
||||||
__slots__ = ('name', )
|
__slots__ = ('name', )
|
||||||
|
|
||||||
@ -45,26 +72,27 @@ class TypeReference:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'TypeReference({repr(self.name)})'
|
return f'TypeReference({repr(self.name)})'
|
||||||
|
|
||||||
|
Signature = List[Union[Type3, TypeVariable, list[TypeVariable]]]
|
||||||
|
|
||||||
class Type3ClassMethod:
|
class Type3ClassMethod:
|
||||||
__slots__ = ('type3_class', 'name', 'signature', )
|
__slots__ = ('name', 'signature', )
|
||||||
|
|
||||||
type3_class: 'Type3Class'
|
|
||||||
name: str
|
name: str
|
||||||
signature: List[Union[Type3, TypeVariable]]
|
signature: FunctionSignature
|
||||||
|
|
||||||
|
def __init__(self, name: str, signature: FunctionSignature) -> None:
|
||||||
|
|
||||||
def __init__(self, type3_class: 'Type3Class', name: str, signature: Iterable[Union[Type3, TypeVariable]]) -> None:
|
|
||||||
self.type3_class = type3_class
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.signature = list(signature)
|
self.signature = signature
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Type3ClassMethod({repr(self.type3_class)}, {repr(self.name)}, {repr(self.signature)})'
|
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
|
||||||
|
|
||||||
class Type3Class:
|
class Type3Class:
|
||||||
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
args: List[TypeVariable]
|
args: List[Union[TypeVariable, TypeConstructorVariable]]
|
||||||
methods: Dict[str, Type3ClassMethod]
|
methods: Dict[str, Type3ClassMethod]
|
||||||
operators: Dict[str, Type3ClassMethod]
|
operators: Dict[str, Type3ClassMethod]
|
||||||
inherited_classes: List['Type3Class']
|
inherited_classes: List['Type3Class']
|
||||||
@ -72,19 +100,33 @@ class Type3Class:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
args: Iterable[TypeVariable],
|
args: Iterable[Union[TypeVariable, TypeConstructorVariable]],
|
||||||
methods: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
methods: Mapping[str, Signature],
|
||||||
operators: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
operators: Mapping[str, Signature],
|
||||||
inherited_classes: Optional[List['Type3Class']] = None,
|
inherited_classes: Optional[List['Type3Class']] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
|
|
||||||
|
context = TypeVariableContext()
|
||||||
|
for arg in 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(self, k, v)
|
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||||
for k, v in methods.items()
|
for k, v in methods.items()
|
||||||
}
|
}
|
||||||
self.operators = {
|
self.operators = {
|
||||||
k: Type3ClassMethod(self, k, v)
|
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||||
for k, v in operators.items()
|
for k, v in operators.items()
|
||||||
}
|
}
|
||||||
self.inherited_classes = inherited_classes or []
|
self.inherited_classes = inherited_classes or []
|
||||||
|
|||||||
@ -64,7 +64,7 @@ class Suite:
|
|||||||
write_header(sys.stderr, 'Memory (pre alloc)')
|
write_header(sys.stderr, 'Memory (pre alloc)')
|
||||||
runner.interpreter_dump_memory(sys.stderr)
|
runner.interpreter_dump_memory(sys.stderr)
|
||||||
|
|
||||||
for arg, arg_typ in zip(args, func_args):
|
for arg, arg_typ in zip(args, func_args, strict=True):
|
||||||
assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
|
assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
|
||||||
'Cannot call polymorphic function from outside'
|
'Cannot call polymorphic function from outside'
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ def _allocate_memory_stored_value(
|
|||||||
assert len(val) == len(tp_args)
|
assert len(val) == len(tp_args)
|
||||||
|
|
||||||
offset = adr
|
offset = adr
|
||||||
for val_el_val, val_el_typ in zip(val, tp_args):
|
for val_el_val, val_el_typ in zip(val, tp_args, strict=True):
|
||||||
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
||||||
|
|
||||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||||
@ -424,7 +424,7 @@ def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3ty
|
|||||||
|
|
||||||
return tuple(
|
return tuple(
|
||||||
_unpack(runner, arg_typ, arg_bytes)
|
_unpack(runner, arg_typ, arg_bytes)
|
||||||
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes))
|
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, type3types.Type3], adr: int) -> Any:
|
def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, type3types.Type3], adr: int) -> Any:
|
||||||
@ -444,5 +444,5 @@ def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, typ
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
||||||
for arg_name, arg_typ, arg_bytes in zip(name_list, typ_list, _split_read_bytes(read_bytes, arg_sizes))
|
for arg_name, arg_typ, arg_bytes in zip(name_list, typ_list, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -282,7 +282,7 @@ if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
|||||||
elif TYPE_NAME.startswith('struct_'):
|
elif TYPE_NAME.startswith('struct_'):
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
TYPE + ' must be (u32, ) instead',
|
TYPE + ' must be (u32, ) instead',
|
||||||
'The type of the value passed to argument x of function helper should match the type of that argument',
|
'The type of the value passed to argument 0 of function helper should match the type of that argument',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
@ -333,11 +333,11 @@ def testEntry() -> i32:
|
|||||||
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('struct_'):
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('struct_'):
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
TYPE + ' must be (u32, ) instead',
|
TYPE + ' must be (u32, ) instead',
|
||||||
'The type of the value passed to argument x of function helper should match the type of that argument',
|
'The type of the value passed to argument 0 of function helper should match the type of that argument',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
TYPE_NAME + ' must be (u32, ) instead',
|
TYPE_NAME + ' must be (u32, ) instead',
|
||||||
'The type of the value passed to argument x of function helper should match the type of that argument',
|
'The type of the value passed to argument 0 of function helper should match the type of that argument',
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,27 +1,8 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ..helpers import Suite, write_header
|
from ..helpers import Suite
|
||||||
from ..runners import RunnerWasmtime
|
|
||||||
|
|
||||||
|
|
||||||
def setup_interpreter(phash_code: str) -> RunnerWasmtime:
|
|
||||||
runner = RunnerWasmtime(phash_code)
|
|
||||||
|
|
||||||
runner.parse()
|
|
||||||
runner.compile_ast()
|
|
||||||
runner.compile_wat()
|
|
||||||
runner.interpreter_setup()
|
|
||||||
runner.interpreter_load()
|
|
||||||
|
|
||||||
write_header(sys.stderr, 'Phasm')
|
|
||||||
runner.dump_phasm_code(sys.stderr)
|
|
||||||
write_header(sys.stderr, 'Assembly')
|
|
||||||
runner.dump_wasm_wat(sys.stderr)
|
|
||||||
|
|
||||||
return runner
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
def test_foldl_1():
|
def test_foldl_1():
|
||||||
code_py = """
|
code_py = """
|
||||||
|
|||||||
20
tests/integration/test_lang/test_typeclass.py
Normal file
20
tests/integration/test_lang/test_typeclass.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from phasm.exceptions import StaticError
|
||||||
|
|
||||||
|
from ..helpers import Suite
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
def test_must_give_type_annotations():
|
||||||
|
code_py = """
|
||||||
|
def is_equal(lft, rgt) -> bool:
|
||||||
|
return lft == rgt
|
||||||
|
|
||||||
|
def testEntry() -> bool:
|
||||||
|
return is_equal(5, 15)
|
||||||
|
"""
|
||||||
|
suite = Suite(code_py)
|
||||||
|
|
||||||
|
with pytest.raises(StaticError, match='Must give a argument type'):
|
||||||
|
suite.run_code()
|
||||||
Loading…
x
Reference in New Issue
Block a user