Reworks bytes into dynamic array
bytes continues to be the preferred name for u8[...]. Also, putting bytes values into the VM and taking them out still uses Python bytes values. This also lets used use the len function on them, for whatever that's worth.
This commit is contained in:
parent
a72bd60de2
commit
83186cce78
@ -15,8 +15,10 @@ from .type3.types import (
|
|||||||
IntType3,
|
IntType3,
|
||||||
Type3,
|
Type3,
|
||||||
TypeApplication_Struct,
|
TypeApplication_Struct,
|
||||||
|
TypeApplication_Type,
|
||||||
TypeApplication_TypeInt,
|
TypeApplication_TypeInt,
|
||||||
TypeApplication_TypeStar,
|
TypeApplication_TypeStar,
|
||||||
|
TypeConstructor_DynamicArray,
|
||||||
TypeConstructor_Function,
|
TypeConstructor_Function,
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
TypeConstructor_Tuple,
|
TypeConstructor_Tuple,
|
||||||
@ -109,12 +111,25 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
|
|
||||||
args: tuple[Type3, ...]
|
args: tuple[Type3, ...]
|
||||||
|
|
||||||
if isinstance(inp.type3.application, TypeApplication_TypeStar):
|
alloc_size_header = None
|
||||||
|
|
||||||
|
if isinstance(inp.type3.application, TypeApplication_Type):
|
||||||
|
# Possibly paranoid assert. If we have a future variadic type,
|
||||||
|
# does it also do this tuple instantation like this?
|
||||||
|
assert isinstance(inp.type3.application.constructor, TypeConstructor_DynamicArray)
|
||||||
|
|
||||||
|
sa_type, = inp.type3.application.arguments
|
||||||
|
|
||||||
|
args = tuple(sa_type for _ in inp.elements)
|
||||||
|
alloc_size = 4 + calculate_alloc_size(sa_type, is_member=False) * len(inp.elements)
|
||||||
|
alloc_size_header = len(inp.elements)
|
||||||
|
elif isinstance(inp.type3.application, TypeApplication_TypeStar):
|
||||||
# Possibly paranoid assert. If we have a future variadic type,
|
# Possibly paranoid assert. If we have a future variadic type,
|
||||||
# does it also do this tuple instantation like this?
|
# does it also do this tuple instantation like this?
|
||||||
assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple)
|
assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple)
|
||||||
|
|
||||||
args = inp.type3.application.arguments
|
args = inp.type3.application.arguments
|
||||||
|
alloc_size = calculate_alloc_size(inp.type3, is_member=False)
|
||||||
elif isinstance(inp.type3.application, TypeApplication_TypeInt):
|
elif isinstance(inp.type3.application, TypeApplication_TypeInt):
|
||||||
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
||||||
# does it also do this tuple instantation like this?
|
# does it also do this tuple instantation like this?
|
||||||
@ -123,6 +138,7 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
sa_type, sa_len = inp.type3.application.arguments
|
sa_type, sa_len = inp.type3.application.arguments
|
||||||
|
|
||||||
args = tuple(sa_type for _ in range(sa_len.value))
|
args = tuple(sa_type for _ in range(sa_len.value))
|
||||||
|
alloc_size = calculate_alloc_size(inp.type3, is_member=False)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('tuple_instantiation', inp.type3)
|
raise NotImplementedError('tuple_instantiation', inp.type3)
|
||||||
|
|
||||||
@ -135,12 +151,17 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
wgn.add_statement('nop', comment=f'{tmp_var.name} := ({comment_elements})')
|
wgn.add_statement('nop', comment=f'{tmp_var.name} := ({comment_elements})')
|
||||||
|
|
||||||
# Allocated the required amounts of bytes in memory
|
# Allocated the required amounts of bytes in memory
|
||||||
wgn.i32.const(calculate_alloc_size(inp.type3, is_member=False))
|
wgn.i32.const(alloc_size)
|
||||||
wgn.call(stdlib_alloc.__alloc__)
|
wgn.call(stdlib_alloc.__alloc__)
|
||||||
wgn.local.set(tmp_var)
|
wgn.local.set(tmp_var)
|
||||||
|
|
||||||
|
if alloc_size_header is not None:
|
||||||
|
wgn.local.get(tmp_var)
|
||||||
|
wgn.i32.const(alloc_size_header)
|
||||||
|
wgn.i32.store()
|
||||||
|
|
||||||
# Store each element individually
|
# Store each element individually
|
||||||
offset = 0
|
offset = 0 if alloc_size_header is None else 4
|
||||||
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
||||||
assert element.type3 == exp_type3
|
assert element.type3 == exp_type3
|
||||||
|
|
||||||
|
|||||||
@ -301,6 +301,9 @@ class FunctionParam:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.type3 = type3
|
self.type3 = type3
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'FunctionParam({self.name!r}, {self.type3!r})'
|
||||||
|
|
||||||
class Function:
|
class Function:
|
||||||
"""
|
"""
|
||||||
A function processes input and produces output
|
A function processes input and produces output
|
||||||
|
|||||||
@ -655,8 +655,15 @@ class OurVisitor:
|
|||||||
|
|
||||||
if isinstance(node.slice, ast.Slice):
|
if isinstance(node.slice, ast.Slice):
|
||||||
_raise_static_error(node, 'Must subscript using an index')
|
_raise_static_error(node, 'Must subscript using an index')
|
||||||
|
|
||||||
if not isinstance(node.slice, ast.Constant):
|
if not isinstance(node.slice, ast.Constant):
|
||||||
_raise_static_error(node, 'Must subscript using a constant index')
|
_raise_static_error(node, 'Must subscript using a constant index')
|
||||||
|
|
||||||
|
if node.slice.value is Ellipsis:
|
||||||
|
return prelude.dynamic_array(
|
||||||
|
self.visit_type(module, node.value),
|
||||||
|
)
|
||||||
|
|
||||||
if not isinstance(node.slice.value, int):
|
if not isinstance(node.slice.value, int):
|
||||||
_raise_static_error(node, 'Must subscript using a constant integer index')
|
_raise_static_error(node, 'Must subscript using a constant integer index')
|
||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
|
|||||||
@ -20,6 +20,7 @@ from ..type3.types import (
|
|||||||
Type3,
|
Type3,
|
||||||
TypeApplication_Nullary,
|
TypeApplication_Nullary,
|
||||||
TypeConstructor_Base,
|
TypeConstructor_Base,
|
||||||
|
TypeConstructor_DynamicArray,
|
||||||
TypeConstructor_Function,
|
TypeConstructor_Function,
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
TypeConstructor_Struct,
|
TypeConstructor_Struct,
|
||||||
@ -158,9 +159,15 @@ f64 = Type3('f64', TypeApplication_Nullary(None, None))
|
|||||||
A 32-bits IEEE 754 float, of 64 bits width.
|
A 32-bits IEEE 754 float, of 64 bits width.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bytes_ = Type3('bytes', TypeApplication_Nullary(None, None))
|
def da_on_create(args: tuple[Type3], typ: Type3) -> None:
|
||||||
|
instance_type_class(InternalPassAsPointer, typ)
|
||||||
|
|
||||||
|
dynamic_array = TypeConstructor_DynamicArray('dynamic_array', on_create=da_on_create)
|
||||||
"""
|
"""
|
||||||
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
This is a dynamic length piece of memory.
|
||||||
|
|
||||||
|
It should be applied with two arguments. It has a runtime
|
||||||
|
determined length, and each argument is the same.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
|
def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
|
||||||
@ -168,12 +175,10 @@ def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
|
|||||||
|
|
||||||
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
|
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
|
||||||
"""
|
"""
|
||||||
A type constructor.
|
This is a fixed length piece of memory.
|
||||||
|
|
||||||
Any static array is a fixed length piece of memory that can be indexed at runtime.
|
It should be applied with two arguments. It has a compile time
|
||||||
|
determined length, and each argument is the same.
|
||||||
It should be applied with one argument. It has a runtime-dynamic length
|
|
||||||
of the same type repeated.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
||||||
@ -208,20 +213,6 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PRELUDE_TYPES: dict[str, Type3] = {
|
|
||||||
'none': none,
|
|
||||||
'bool': bool_,
|
|
||||||
'u8': u8,
|
|
||||||
'u32': u32,
|
|
||||||
'u64': u64,
|
|
||||||
'i8': i8,
|
|
||||||
'i32': i32,
|
|
||||||
'i64': i64,
|
|
||||||
'f32': f32,
|
|
||||||
'f64': f64,
|
|
||||||
'bytes': bytes_,
|
|
||||||
}
|
|
||||||
|
|
||||||
a = TypeVariable('a', TypeVariableApplication_Nullary(None, None))
|
a = TypeVariable('a', TypeVariableApplication_Nullary(None, None))
|
||||||
b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
|
b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
|
||||||
|
|
||||||
@ -232,7 +223,7 @@ InternalPassAsPointer = Type3Class('InternalPassAsPointer', (a, ), methods={}, o
|
|||||||
Internal type class to keep track which types we pass arounds as a pointer.
|
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)
|
||||||
@ -537,12 +528,15 @@ instance_type_class(Floating, f64, methods={
|
|||||||
'sqrt': stdtypes.f64_floating_sqrt,
|
'sqrt': stdtypes.f64_floating_sqrt,
|
||||||
})
|
})
|
||||||
|
|
||||||
Sized_ = Type3Class('Sized', (a, ), methods={
|
Sized_ = Type3Class('Sized', (t, ), methods={
|
||||||
'len': [a, u32],
|
'len': [t(a), u32],
|
||||||
}, operators={}) # FIXME: Once we get type class families, add [] here
|
}, operators={}) # FIXME: Once we get type class families, add [] here
|
||||||
|
|
||||||
instance_type_class(Sized_, bytes_, methods={
|
instance_type_class(Sized_, dynamic_array, methods={
|
||||||
'len': stdtypes.bytes_sized_len,
|
'len': stdtypes.dynamic_array_sized_len,
|
||||||
|
})
|
||||||
|
instance_type_class(Sized_, static_array, methods={
|
||||||
|
'len': stdtypes.static_array_sized_len,
|
||||||
})
|
})
|
||||||
|
|
||||||
Extendable = Type3Class('Extendable', (a, b, ), methods={
|
Extendable = Type3Class('Extendable', (a, b, ), methods={
|
||||||
@ -595,6 +589,22 @@ instance_type_class(Foldable, static_array, methods={
|
|||||||
'sum': stdtypes.static_array_sum,
|
'sum': stdtypes.static_array_sum,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
bytes_ = dynamic_array(u8)
|
||||||
|
|
||||||
|
PRELUDE_TYPES: dict[str, Type3] = {
|
||||||
|
'none': none,
|
||||||
|
'bool': bool_,
|
||||||
|
'u8': u8,
|
||||||
|
'u32': u32,
|
||||||
|
'u64': u64,
|
||||||
|
'i8': i8,
|
||||||
|
'i32': i32,
|
||||||
|
'i64': i64,
|
||||||
|
'f32': f32,
|
||||||
|
'f64': f64,
|
||||||
|
'bytes': bytes_,
|
||||||
|
}
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASSES = {
|
PRELUDE_TYPE_CLASSES = {
|
||||||
'Eq': Eq,
|
'Eq': Eq,
|
||||||
'Ord': Ord,
|
'Ord': Ord,
|
||||||
|
|||||||
@ -1006,11 +1006,19 @@ def f64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
|
|||||||
## ###
|
## ###
|
||||||
## Class Sized
|
## Class Sized
|
||||||
|
|
||||||
def bytes_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
|
def dynamic_array_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||||
del tv_map
|
del tv_map
|
||||||
# The length is stored in the first 4 bytes
|
# The length is stored in the first 4 bytes
|
||||||
g.i32.load()
|
g.i32.load()
|
||||||
|
|
||||||
|
def static_array_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
assert len(tv_map) == 1
|
||||||
|
sa_type, sa_len = next(iter(tv_map.values()))
|
||||||
|
assert isinstance(sa_type, Type3)
|
||||||
|
assert isinstance(sa_len, IntType3)
|
||||||
|
|
||||||
|
g.i32.const(sa_len.value)
|
||||||
|
|
||||||
## ###
|
## ###
|
||||||
## Extendable
|
## Extendable
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ from .types import (
|
|||||||
Type3,
|
Type3,
|
||||||
TypeApplication_Nullary,
|
TypeApplication_Nullary,
|
||||||
TypeApplication_Struct,
|
TypeApplication_Struct,
|
||||||
|
TypeApplication_Type,
|
||||||
TypeApplication_TypeInt,
|
TypeApplication_TypeInt,
|
||||||
TypeApplication_TypeStar,
|
TypeApplication_TypeStar,
|
||||||
TypeConstructor_Base,
|
TypeConstructor_Base,
|
||||||
@ -292,6 +293,14 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
self.exp_type = exp_type
|
self.exp_type = exp_type
|
||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
|
|
||||||
|
def _generate_dynamic_array(self, sa_args: tuple[Type3]) -> CheckResult:
|
||||||
|
sa_type, = sa_args
|
||||||
|
|
||||||
|
return [
|
||||||
|
SameTypeConstraint(arg, sa_type)
|
||||||
|
for arg in self.args
|
||||||
|
]
|
||||||
|
|
||||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||||
sa_type, sa_len = sa_args
|
sa_type, sa_len = sa_args
|
||||||
|
|
||||||
@ -313,6 +322,7 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]()
|
GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]()
|
||||||
|
GENERATE_ROUTER.add(prelude.dynamic_array, _generate_dynamic_array)
|
||||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||||
|
|
||||||
@ -340,7 +350,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
types: list[Type3OrPlaceholder]
|
types: list[Type3OrPlaceholder]
|
||||||
|
|
||||||
DATA = {
|
DATA = {
|
||||||
'bytes': {'Foldable'},
|
'dynamic_array': {'Foldable'},
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, context: Context, type_class3: Union[str, Type3Class], typ_list: list[Type3OrPlaceholder], comment: Optional[str] = None) -> None:
|
def __init__(self, context: Context, type_class3: Union[str, Type3Class], typ_list: list[Type3OrPlaceholder], comment: Optional[str] = None) -> None:
|
||||||
@ -363,7 +373,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
typ_list.append(typ)
|
typ_list.append(typ)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(typ.application, (TypeApplication_TypeInt, TypeApplication_TypeStar)):
|
if isinstance(typ.application, (TypeApplication_Type, TypeApplication_TypeInt, TypeApplication_TypeStar)):
|
||||||
typ_list.append(typ.application.constructor)
|
typ_list.append(typ.application.constructor)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -420,6 +430,29 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
self.type3 = type3
|
self.type3 = type3
|
||||||
self.literal = literal
|
self.literal = literal
|
||||||
|
|
||||||
|
def _generate_dynamic_array(self, da_args: tuple[Type3]) -> CheckResult:
|
||||||
|
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||||
|
return Error('Must be tuple', comment=self.comment)
|
||||||
|
|
||||||
|
da_type, = da_args
|
||||||
|
|
||||||
|
res: list[ConstraintBase] = []
|
||||||
|
|
||||||
|
res.extend(
|
||||||
|
LiteralFitsConstraint(da_type, y)
|
||||||
|
for y in self.literal.value
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate placeholders so each Literal expression
|
||||||
|
# gets updated when we figure out the type of the
|
||||||
|
# expression the literal is used in
|
||||||
|
res.extend(
|
||||||
|
SameTypeConstraint(da_type, PlaceholderForType([y]))
|
||||||
|
for y in self.literal.value
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||||
return Error('Must be tuple', comment=self.comment)
|
return Error('Must be tuple', comment=self.comment)
|
||||||
@ -501,6 +534,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
|
GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
|
||||||
|
GENERATE_ROUTER.add(prelude.dynamic_array, _generate_dynamic_array)
|
||||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||||
GENERATE_ROUTER.add(prelude.struct, _generate_struct)
|
GENERATE_ROUTER.add(prelude.struct, _generate_struct)
|
||||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||||
|
|||||||
@ -176,7 +176,10 @@ def _expression_function_call(
|
|||||||
if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
||||||
raise NotImplementedError(sig_arg.application)
|
raise NotImplementedError(sig_arg.application)
|
||||||
|
|
||||||
assert sig_arg.application.arguments in type_var_map # When does this happen?
|
if sig_arg.application.arguments not in type_var_map:
|
||||||
|
# e.g., len :: t a -> u32
|
||||||
|
# i.e. "a" does not matter at all
|
||||||
|
continue
|
||||||
|
|
||||||
yield SameTypeArgumentConstraint(
|
yield SameTypeArgumentConstraint(
|
||||||
type_var_map[sig_arg],
|
type_var_map[sig_arg],
|
||||||
|
|||||||
@ -6,7 +6,13 @@ from .functions import (
|
|||||||
TypeVariableApplication_Unary,
|
TypeVariableApplication_Unary,
|
||||||
)
|
)
|
||||||
from .typeclasses import Type3ClassArgs
|
from .typeclasses import Type3ClassArgs
|
||||||
from .types import KindArgument, Type3, TypeApplication_TypeInt, TypeConstructor_Base
|
from .types import (
|
||||||
|
KindArgument,
|
||||||
|
Type3,
|
||||||
|
TypeApplication_Type,
|
||||||
|
TypeApplication_TypeInt,
|
||||||
|
TypeConstructor_Base,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class NoRouteForTypeException(Exception):
|
class NoRouteForTypeException(Exception):
|
||||||
@ -104,6 +110,10 @@ class TypeClassArgsRouter[S, R]:
|
|||||||
key.append(typ.application.constructor)
|
key.append(typ.application.constructor)
|
||||||
|
|
||||||
if isinstance(tvar.application, TypeVariableApplication_Unary):
|
if isinstance(tvar.application, TypeVariableApplication_Unary):
|
||||||
|
if isinstance(typ.application, TypeApplication_Type):
|
||||||
|
arguments[tvar.application.arguments] = typ.application.arguments
|
||||||
|
continue
|
||||||
|
|
||||||
# FIXME: This feels sketchy. Shouldn't the type variable
|
# FIXME: This feels sketchy. Shouldn't the type variable
|
||||||
# have the exact same number as arguments?
|
# have the exact same number as arguments?
|
||||||
if isinstance(typ.application, TypeApplication_TypeInt):
|
if isinstance(typ.application, TypeApplication_TypeInt):
|
||||||
|
|||||||
@ -195,6 +195,26 @@ class TypeConstructor_Base[T]:
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'{self.__class__.__name__}({self.name!r}, ...)'
|
return f'{self.__class__.__name__}({self.name!r}, ...)'
|
||||||
|
|
||||||
|
class TypeConstructor_Type(TypeConstructor_Base[Tuple[Type3]]):
|
||||||
|
"""
|
||||||
|
Base class type constructors of kind: * -> *
|
||||||
|
|
||||||
|
Notably, static array.
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def make_application(self, key: Tuple[Type3]) -> 'TypeApplication_Type':
|
||||||
|
return TypeApplication_Type(self, key)
|
||||||
|
|
||||||
|
def make_name(self, key: Tuple[Type3]) -> str:
|
||||||
|
return f'{self.name} {key[0].name} '
|
||||||
|
|
||||||
|
def __call__(self, arg0: Type3) -> Type3:
|
||||||
|
return self.construct((arg0, ))
|
||||||
|
|
||||||
|
class TypeApplication_Type(TypeApplication_Base[TypeConstructor_Type, Tuple[Type3]]):
|
||||||
|
pass
|
||||||
|
|
||||||
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
|
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
|
||||||
"""
|
"""
|
||||||
Base class type constructors of kind: * -> Int -> *
|
Base class type constructors of kind: * -> Int -> *
|
||||||
@ -231,6 +251,13 @@ class TypeConstructor_TypeStar(TypeConstructor_Base[Tuple[Type3, ...]]):
|
|||||||
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
|
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class TypeConstructor_DynamicArray(TypeConstructor_Type):
|
||||||
|
def make_name(self, key: Tuple[Type3]) -> str:
|
||||||
|
if 'u8' == key[0].name:
|
||||||
|
return 'bytes'
|
||||||
|
|
||||||
|
return f'{key[0].name}[...]'
|
||||||
|
|
||||||
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
|
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
|
||||||
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
||||||
return f'{key[0].name}[{key[1].value}]'
|
return f'{key[0].name}[{key[1].value}]'
|
||||||
|
|||||||
@ -152,6 +152,25 @@ def _allocate_memory_stored_bytes(attrs: tuple[runners.RunnerBase, bytes]) -> in
|
|||||||
runner.interpreter_write_memory(adr + 4, val)
|
runner.interpreter_write_memory(adr + 4, val)
|
||||||
return adr
|
return adr
|
||||||
|
|
||||||
|
def _allocate_memory_stored_dynamic_array(attrs: tuple[runners.RunnerBase, Any], da_args: tuple[type3types.Type3]) -> int:
|
||||||
|
runner, val = attrs
|
||||||
|
|
||||||
|
da_type, = da_args
|
||||||
|
|
||||||
|
if not isinstance(val, tuple):
|
||||||
|
raise InvalidArgumentException(f'Expected tuple; got {val!r} instead')
|
||||||
|
|
||||||
|
alloc_size = 4 + len(val) * calculate_alloc_size(da_type, True)
|
||||||
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
|
assert isinstance(adr, int) # Type int
|
||||||
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
|
|
||||||
|
offset = adr
|
||||||
|
offset += _write_memory_stored_value(runner, offset, prelude.u32, len(val))
|
||||||
|
for val_el_val in val:
|
||||||
|
offset += _write_memory_stored_value(runner, offset, da_type, val_el_val)
|
||||||
|
return adr
|
||||||
|
|
||||||
def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int:
|
def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int:
|
||||||
runner, val = attrs
|
runner, val = attrs
|
||||||
|
|
||||||
@ -211,6 +230,7 @@ def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args
|
|||||||
|
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]()
|
ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]()
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes)
|
ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes)
|
||||||
|
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.dynamic_array, _allocate_memory_stored_dynamic_array)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array)
|
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct)
|
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple)
|
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple)
|
||||||
@ -325,6 +345,26 @@ def _split_read_bytes(all_bytes: bytes, split_sizes: Iterable[int]) -> Generator
|
|||||||
yield all_bytes[offset:offset + size]
|
yield all_bytes[offset:offset + size]
|
||||||
offset += size
|
offset += size
|
||||||
|
|
||||||
|
def _load_dynamic_array_from_address(attrs: tuple[runners.RunnerBase, int], da_args: tuple[type3types.Type3]) -> Any:
|
||||||
|
runner, adr = attrs
|
||||||
|
da_type, = da_args
|
||||||
|
|
||||||
|
sys.stderr.write(f'Reading 0x{adr:08x} {da_type:s}[...]\n')
|
||||||
|
|
||||||
|
read_bytes = runner.interpreter_read_memory(adr, 4)
|
||||||
|
array_len, = struct.unpack('<I', read_bytes)
|
||||||
|
adr += 4
|
||||||
|
|
||||||
|
arg_size_1 = calculate_alloc_size(da_type, is_member=True)
|
||||||
|
arg_sizes = [arg_size_1 for _ in range(array_len)] # _split_read_bytes requires one arg per value
|
||||||
|
|
||||||
|
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||||
|
|
||||||
|
return tuple(
|
||||||
|
_unpack(runner, da_type, arg_bytes)
|
||||||
|
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
|
||||||
|
)
|
||||||
|
|
||||||
def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> Any:
|
def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> Any:
|
||||||
runner, adr = attrs
|
runner, adr = attrs
|
||||||
sub_typ, len_typ = sa_args
|
sub_typ, len_typ = sa_args
|
||||||
@ -379,6 +419,7 @@ def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tup
|
|||||||
|
|
||||||
LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]()
|
LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]()
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address)
|
LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address)
|
||||||
|
LOAD_FROM_ADDRESS_ROUTER.add(prelude.dynamic_array, _load_dynamic_array_from_address)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address)
|
LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)
|
LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address)
|
LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address)
|
||||||
|
|||||||
@ -60,7 +60,7 @@ CONSTANT: (u32, ) = $VAL0
|
|||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_'):
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
'Tuple element count mismatch',
|
'Tuple element count mismatch',
|
||||||
'The given literal must fit the expected type',
|
'The given literal must fit the expected type',
|
||||||
@ -113,7 +113,7 @@ def testEntry() -> i32:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_'):
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
'Mismatch between applied types argument count',
|
'Mismatch between applied types argument count',
|
||||||
'The type of a tuple is a combination of its members',
|
'The type of a tuple is a combination of its members',
|
||||||
@ -175,7 +175,7 @@ def testEntry() -> i32:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
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('dynamic_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 returned from function constant should match its return type',
|
'The type of the value returned from function constant should match its return type',
|
||||||
@ -226,7 +226,7 @@ def select(x: $TYPE) -> (u32, ):
|
|||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
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('dynamic_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 returned from function select should match its return type',
|
'The type of the value returned from function select should match its return type',
|
||||||
@ -273,7 +273,7 @@ def testEntry() -> i32:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_'):
|
if TYPE_NAME.startswith('tuple_') or TYPE_NAME.startswith('static_array_') or TYPE_NAME.startswith('dynamic_array_'):
|
||||||
expect_type_error(
|
expect_type_error(
|
||||||
'Mismatch between applied types argument count',
|
'Mismatch between applied types argument count',
|
||||||
# FIXME: Shouldn't this be the same as for the else statement?
|
# FIXME: Shouldn't this be the same as for the else statement?
|
||||||
@ -330,7 +330,7 @@ def testEntry() -> i32:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
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('dynamic_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 0 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',
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"TYPE_NAME": "dynamic_array_u64",
|
||||||
|
"TYPE": "u64[...]",
|
||||||
|
"VAL0": "(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, )"
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@ from ..helpers import Suite
|
|||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||||
('bytes', b'Hello, world!', 13),
|
('bytes', b'Hello, world!', 13),
|
||||||
# ('u8[4]', (1, 2, 3, 4), 4), # FIXME: Implement this
|
('u8[4]', (1, 2, 3, 4), 4),
|
||||||
])
|
])
|
||||||
def test_len(type_, in_put, exp_result):
|
def test_len(type_, in_put, exp_result):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user