Compare commits
2 Commits
43411c4562
...
0e8540c611
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e8540c611 | ||
|
|
46b06dbcf1 |
4
TODO.md
4
TODO.md
@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
- Implement a trace() builtin for debugging
|
- Implement a trace() builtin for debugging
|
||||||
- Check if we can use DataView in the Javascript examples, e.g. with setUint32
|
- Check if we can use DataView in the Javascript examples, e.g. with setUint32
|
||||||
- Storing u8 in memory still claims 32 bits (since that's what you need in local variables). However, using load8_u / loadu_s we can optimize this.
|
|
||||||
- At least on static arrays, tuples and structs. For dynamic arrays, it's complicated. This needs cleaning up.
|
|
||||||
- Implement a FizzBuzz example
|
- Implement a FizzBuzz example
|
||||||
- Also, check the codes for FIXME and TODO
|
- Also, check the codes for FIXME and TODO
|
||||||
- Allocation is done using pointers for members, is this desired?
|
- Allocation is done using pointers for members, is this desired?
|
||||||
@ -18,9 +16,11 @@
|
|||||||
- 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.
|
||||||
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
||||||
- Why is expression_subscript_bytes using a helper method but expression_subscript_static_array is not?
|
- Why is expression_subscript_bytes using a helper method but expression_subscript_static_array is not?
|
||||||
|
- calculate_alloc_size can be reworked; is_member isn't useful with TYPE_INFO_MAP
|
||||||
|
|
||||||
- Parser is putting stuff in ModuleDataBlock
|
- Parser is putting stuff in ModuleDataBlock
|
||||||
- Surely the compiler should build data blocks
|
- Surely the compiler should build data blocks
|
||||||
|
- Also put the struct.pack constants in TYPE_INFO_MAP
|
||||||
- Make prelude more an actual thing
|
- Make prelude more an actual thing
|
||||||
- Merge in compiler.INSTANCES
|
- Merge in compiler.INSTANCES
|
||||||
- Make it less build in - have a environment class of some kind
|
- Make it less build in - have a environment class of some kind
|
||||||
|
|||||||
@ -2,12 +2,13 @@
|
|||||||
This module contains the code to convert parsed Ourlang into WebAssembly code
|
This module contains the code to convert parsed Ourlang into WebAssembly code
|
||||||
"""
|
"""
|
||||||
import struct
|
import struct
|
||||||
from typing import List, Optional
|
from typing import List
|
||||||
|
|
||||||
from . import 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 .stdlib.types import TYPE_INFO_CONSTRUCTED, TYPE_INFO_MAP
|
||||||
from .type3.functions import FunctionArgument, TypeVariable
|
from .type3.functions import FunctionArgument, TypeVariable
|
||||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
from .type3.typeclasses import Type3ClassMethod
|
||||||
@ -27,20 +28,6 @@ from .wasmgenerator import Generator as WasmGenerator
|
|||||||
|
|
||||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
||||||
|
|
||||||
LOAD_STORE_TYPE_MAP = {
|
|
||||||
'i8': 'i32', # Have to use an u32, since there is no native i8 type
|
|
||||||
'u8': 'i32', # Have to use an u32, since there is no native u8 type
|
|
||||||
|
|
||||||
'i32': 'i32',
|
|
||||||
'i64': 'i64',
|
|
||||||
'u32': 'i32',
|
|
||||||
'u64': 'i64',
|
|
||||||
'f32': 'f32',
|
|
||||||
'f64': 'f64',
|
|
||||||
|
|
||||||
'bytes': 'i32', # Bytes are passed around as pointers
|
|
||||||
}
|
|
||||||
|
|
||||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||||
"""
|
"""
|
||||||
Public method for compiling a parsed Phasm module into
|
Public method for compiling a parsed Phasm module into
|
||||||
@ -55,53 +42,8 @@ def type3(inp: Type3) -> wasm.WasmType:
|
|||||||
Types are used for example in WebAssembly function parameters
|
Types are used for example in WebAssembly function parameters
|
||||||
and return types.
|
and return types.
|
||||||
"""
|
"""
|
||||||
assert inp is not None, TYPE3_ASSERTION_ERROR
|
typ_info = TYPE_INFO_MAP.get(inp.name, TYPE_INFO_CONSTRUCTED)
|
||||||
|
return typ_info.wasm_type()
|
||||||
if inp == prelude.none:
|
|
||||||
return wasm.WasmTypeNone()
|
|
||||||
|
|
||||||
if inp == prelude.bool_:
|
|
||||||
# WebAssembly stores booleans as i32
|
|
||||||
# See e.g. f32.eq, which is [f32 f32] -> [i32]
|
|
||||||
return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
if inp == prelude.u8:
|
|
||||||
# WebAssembly has only support for 32 and 64 bits
|
|
||||||
# So we need to store more memory per byte
|
|
||||||
return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
if inp == prelude.u32:
|
|
||||||
return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
if inp == prelude.u64:
|
|
||||||
return wasm.WasmTypeInt64()
|
|
||||||
|
|
||||||
if inp == prelude.i8:
|
|
||||||
# WebAssembly has only support for 32 and 64 bits
|
|
||||||
# So we need to store more memory per byte
|
|
||||||
return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
if inp == prelude.i32:
|
|
||||||
return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
if inp == prelude.i64:
|
|
||||||
return wasm.WasmTypeInt64()
|
|
||||||
|
|
||||||
if inp == prelude.f32:
|
|
||||||
return wasm.WasmTypeFloat32()
|
|
||||||
|
|
||||||
if inp == prelude.f64:
|
|
||||||
return wasm.WasmTypeFloat64()
|
|
||||||
|
|
||||||
if inp == prelude.bytes_:
|
|
||||||
# bytes are passed as pointer
|
|
||||||
# And pointers are i32
|
|
||||||
return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
if (prelude.InternalPassAsPointer, (inp, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
|
||||||
return wasm.WasmTypeInt32()
|
|
||||||
|
|
||||||
raise NotImplementedError(type3, inp)
|
|
||||||
|
|
||||||
def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.TupleInstantiation) -> None:
|
def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.TupleInstantiation) -> None:
|
||||||
"""
|
"""
|
||||||
@ -121,7 +63,9 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
sa_type, = inp.type3.application.arguments
|
sa_type, = inp.type3.application.arguments
|
||||||
|
|
||||||
args = tuple(sa_type for _ in inp.elements)
|
args = tuple(sa_type for _ in inp.elements)
|
||||||
alloc_size = 4 + calculate_alloc_size(sa_type, is_member=False) * len(inp.elements)
|
# Can't use calculate_alloc_size directly since that doesn't
|
||||||
|
# know the dynamic array's length
|
||||||
|
alloc_size = 4 + calculate_alloc_size(sa_type, is_member=True) * len(inp.elements)
|
||||||
alloc_size_header = len(inp.elements)
|
alloc_size_header = len(inp.elements)
|
||||||
elif isinstance(inp.type3.application, TypeApplication_TypeStar):
|
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,
|
||||||
@ -165,15 +109,12 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
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
|
||||||
|
|
||||||
if (prelude.InternalPassAsPointer, (exp_type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
exp_type_info = TYPE_INFO_MAP.get(exp_type3.name, TYPE_INFO_CONSTRUCTED)
|
||||||
mtyp = 'i32'
|
|
||||||
else:
|
|
||||||
mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name]
|
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='PRE')
|
wgn.add_statement('nop', comment='PRE')
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
expression(wgn, mod, element)
|
expression(wgn, mod, element)
|
||||||
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(offset))
|
wgn.add_statement(exp_type_info.wasm_store_func, 'offset=' + str(offset))
|
||||||
wgn.add_statement('nop', comment='POST')
|
wgn.add_statement('nop', comment='POST')
|
||||||
|
|
||||||
offset += calculate_alloc_size(exp_type3, is_member=True)
|
offset += calculate_alloc_size(exp_type3, is_member=True)
|
||||||
@ -213,14 +154,14 @@ def expression_subscript_static_array(
|
|||||||
with wgn.if_():
|
with wgn.if_():
|
||||||
wgn.unreachable(comment='Out of bounds')
|
wgn.unreachable(comment='Out of bounds')
|
||||||
|
|
||||||
|
el_type_info = TYPE_INFO_MAP.get(el_type.name, TYPE_INFO_CONSTRUCTED)
|
||||||
|
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
wgn.i32.const(calculate_alloc_size(el_type))
|
wgn.i32.const(el_type_info.alloc_size)
|
||||||
wgn.i32.mul()
|
wgn.i32.mul()
|
||||||
wgn.i32.add()
|
wgn.i32.add()
|
||||||
|
|
||||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
wgn.add_statement(el_type_info.wasm_load_func)
|
||||||
|
|
||||||
wgn.add_statement(f'{mtyp}.load')
|
|
||||||
|
|
||||||
def expression_subscript_tuple(
|
def expression_subscript_tuple(
|
||||||
attrs: tuple[WasmGenerator, ourlang.Module, ourlang.Subscript],
|
attrs: tuple[WasmGenerator, ourlang.Module, ourlang.Subscript],
|
||||||
@ -234,19 +175,16 @@ def expression_subscript_tuple(
|
|||||||
offset = 0
|
offset = 0
|
||||||
for el_type in args[0:inp.index.value]:
|
for el_type in args[0:inp.index.value]:
|
||||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||||
offset += calculate_alloc_size(el_type)
|
el_type_info = TYPE_INFO_MAP.get(el_type.name, TYPE_INFO_CONSTRUCTED)
|
||||||
|
offset += el_type_info.alloc_size
|
||||||
|
|
||||||
el_type = args[inp.index.value]
|
el_type = args[inp.index.value]
|
||||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
expression(wgn, mod, inp.varref)
|
expression(wgn, mod, inp.varref)
|
||||||
|
|
||||||
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
el_type_info = TYPE_INFO_MAP.get(el_type.name, TYPE_INFO_CONSTRUCTED)
|
||||||
mtyp = 'i32'
|
wgn.add_statement(el_type_info.wasm_load_func, f'offset={offset}')
|
||||||
else:
|
|
||||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
|
||||||
|
|
||||||
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
|
||||||
|
|
||||||
SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Module, ourlang.Subscript], None]()
|
SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Module, ourlang.Subscript], None]()
|
||||||
SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes)
|
SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes)
|
||||||
@ -305,7 +243,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if (prelude.InternalPassAsPointer, (inp.type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
if inp.type3.name not in TYPE_INFO_MAP:
|
||||||
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
|
||||||
@ -421,11 +359,10 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||||
|
|
||||||
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
|
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||||
|
member_type_info = TYPE_INFO_MAP.get(member_type.name, TYPE_INFO_CONSTRUCTED)
|
||||||
mtyp = LOAD_STORE_TYPE_MAP[member_type.name]
|
|
||||||
|
|
||||||
expression(wgn, mod, inp.varref)
|
expression(wgn, mod, inp.varref)
|
||||||
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(calculate_member_offset(
|
wgn.add_statement(member_type_info.wasm_load_func, 'offset=' + str(calculate_member_offset(
|
||||||
inp.struct_type3.name, inp.struct_type3.application.arguments, inp.member
|
inp.struct_type3.name, inp.struct_type3.application.arguments, inp.member
|
||||||
)))
|
)))
|
||||||
return
|
return
|
||||||
@ -526,10 +463,8 @@ def function(mod: ourlang.Module, inp: ourlang.Function) -> wasm.Function:
|
|||||||
def module_data_u8(inp: int) -> bytes:
|
def module_data_u8(inp: int) -> bytes:
|
||||||
"""
|
"""
|
||||||
Compile: module data, u8 value
|
Compile: module data, u8 value
|
||||||
|
|
||||||
# FIXME: All u8 values are stored as u32
|
|
||||||
"""
|
"""
|
||||||
return struct.pack('<I', inp) # Should be 'B'
|
return struct.pack('<B', inp)
|
||||||
|
|
||||||
def module_data_u32(inp: int) -> bytes:
|
def module_data_u32(inp: int) -> bytes:
|
||||||
"""
|
"""
|
||||||
@ -546,10 +481,8 @@ def module_data_u64(inp: int) -> bytes:
|
|||||||
def module_data_i8(inp: int) -> bytes:
|
def module_data_i8(inp: int) -> bytes:
|
||||||
"""
|
"""
|
||||||
Compile: module data, i8 value
|
Compile: module data, i8 value
|
||||||
|
|
||||||
# FIXME: All i8 values are stored as i32
|
|
||||||
"""
|
"""
|
||||||
return struct.pack('<i', inp) # Should be a 'b'
|
return struct.pack('<b', inp)
|
||||||
|
|
||||||
def module_data_i32(inp: int) -> bytes:
|
def module_data_i32(inp: int) -> bytes:
|
||||||
"""
|
"""
|
||||||
@ -738,18 +671,11 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
|||||||
|
|
||||||
# Store each member individually
|
# Store each member individually
|
||||||
for memname, mtyp3 in st_args:
|
for memname, mtyp3 in st_args:
|
||||||
mtyp: Optional[str]
|
mtyp3_info = TYPE_INFO_MAP.get(mtyp3.name, TYPE_INFO_CONSTRUCTED)
|
||||||
if (prelude.InternalPassAsPointer, (mtyp3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
|
||||||
mtyp = 'i32'
|
|
||||||
else:
|
|
||||||
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
|
|
||||||
|
|
||||||
if mtyp is None:
|
|
||||||
raise NotImplementedError(expression, inp, mtyp3)
|
|
||||||
|
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
wgn.add_statement('local.get', f'${memname}')
|
wgn.add_statement('local.get', f'${memname}')
|
||||||
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(calculate_member_offset(
|
wgn.add_statement(mtyp3_info.wasm_store_func, 'offset=' + str(calculate_member_offset(
|
||||||
inp.struct_type3.name, st_args, memname
|
inp.struct_type3.name, st_args, memname
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ from ..type3.functions import (
|
|||||||
from ..type3.routers import TypeClassArgsRouter, TypeVariableLookup
|
from ..type3.routers import TypeClassArgsRouter, TypeVariableLookup
|
||||||
from ..type3.typeclasses import Type3Class, Type3ClassMethod
|
from ..type3.typeclasses import Type3Class, Type3ClassMethod
|
||||||
from ..type3.types import (
|
from ..type3.types import (
|
||||||
IntType3,
|
|
||||||
Type3,
|
Type3,
|
||||||
TypeApplication_Nullary,
|
TypeApplication_Nullary,
|
||||||
TypeConstructor_Base,
|
TypeConstructor_Base,
|
||||||
@ -159,10 +158,7 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def da_on_create(args: tuple[Type3], typ: Type3) -> None:
|
dynamic_array = TypeConstructor_DynamicArray('dynamic_array')
|
||||||
instance_type_class(InternalPassAsPointer, typ)
|
|
||||||
|
|
||||||
dynamic_array = TypeConstructor_DynamicArray('dynamic_array', on_create=da_on_create)
|
|
||||||
"""
|
"""
|
||||||
This is a dynamic length piece of memory.
|
This is a dynamic length piece of memory.
|
||||||
|
|
||||||
@ -170,10 +166,7 @@ It should be applied with two arguments. It has a runtime
|
|||||||
determined length, and each argument is the same.
|
determined length, and each argument is the same.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
|
static_array = TypeConstructor_StaticArray('static_array')
|
||||||
instance_type_class(InternalPassAsPointer, typ)
|
|
||||||
|
|
||||||
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
|
|
||||||
"""
|
"""
|
||||||
This is a fixed length piece of memory.
|
This is a fixed length piece of memory.
|
||||||
|
|
||||||
@ -181,10 +174,7 @@ It should be applied with two arguments. It has a compile time
|
|||||||
determined length, and each argument is the same.
|
determined length, and each argument is the same.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
tuple_ = TypeConstructor_Tuple('tuple')
|
||||||
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.
|
||||||
|
|
||||||
@ -192,22 +182,14 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def fn_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
function = TypeConstructor_Function('function')
|
||||||
# Not really a pointer; but still a i32
|
|
||||||
# (It's actually a table lookup)
|
|
||||||
instance_type_class(InternalPassAsPointer, typ)
|
|
||||||
|
|
||||||
function = TypeConstructor_Function('function', on_create=fn_on_create)
|
|
||||||
"""
|
"""
|
||||||
This is a function.
|
This is a function.
|
||||||
|
|
||||||
It should be applied with one or more arguments. The last argument is the 'return' type.
|
It should be applied with one or more arguments. The last argument is the 'return' type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def st_on_create(args: tuple[tuple[str, Type3], ...], typ: Type3) -> None:
|
struct = TypeConstructor_Struct('struct')
|
||||||
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.
|
||||||
@ -218,16 +200,6 @@ b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
|
|||||||
|
|
||||||
t = TypeConstructorVariable('t')
|
t = TypeConstructorVariable('t')
|
||||||
|
|
||||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', (a, ), methods={}, operators={})
|
|
||||||
"""
|
|
||||||
Internal type class to keep track which types we pass arounds as a pointer.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# instance_type_class(InternalPassAsPointer, bytes_)
|
|
||||||
# instance_type_class(InternalPassAsPointer, static_array)
|
|
||||||
# instance_type_class(InternalPassAsPointer, tuple_)
|
|
||||||
# instance_type_class(InternalPassAsPointer, struct)
|
|
||||||
|
|
||||||
Eq = Type3Class('Eq', (a, ), methods={}, operators={
|
Eq = Type3Class('Eq', (a, ), methods={}, operators={
|
||||||
'==': [a, a, bool_],
|
'==': [a, a, bool_],
|
||||||
'!=': [a, a, bool_],
|
'!=': [a, a, bool_],
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
from . import prelude
|
from . import prelude
|
||||||
|
from .stdlib.types import TYPE_INFO_CONSTRUCTED, TYPE_INFO_MAP
|
||||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||||
from .type3.types import IntType3, Type3
|
from .type3.types import IntType3, Type3
|
||||||
|
|
||||||
|
|
||||||
def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntType3]) -> int:
|
def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntType3]) -> int:
|
||||||
if is_member:
|
if is_member:
|
||||||
return 4
|
return TYPE_INFO_CONSTRUCTED.alloc_size
|
||||||
|
|
||||||
sa_type, sa_len = args
|
sa_type, sa_len = args
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntTyp
|
|||||||
|
|
||||||
def calculate_alloc_size_tuple(is_member: bool, args: tuple[Type3, ...]) -> int:
|
def calculate_alloc_size_tuple(is_member: bool, args: tuple[Type3, ...]) -> int:
|
||||||
if is_member:
|
if is_member:
|
||||||
return 4
|
return TYPE_INFO_CONSTRUCTED.alloc_size
|
||||||
|
|
||||||
return sum(
|
return sum(
|
||||||
calculate_alloc_size(x, is_member=True)
|
calculate_alloc_size(x, is_member=True)
|
||||||
@ -22,7 +23,7 @@ def calculate_alloc_size_tuple(is_member: bool, args: tuple[Type3, ...]) -> int:
|
|||||||
|
|
||||||
def calculate_alloc_size_struct(is_member: bool, args: tuple[tuple[str, Type3], ...]) -> int:
|
def calculate_alloc_size_struct(is_member: bool, args: tuple[tuple[str, Type3], ...]) -> int:
|
||||||
if is_member:
|
if is_member:
|
||||||
return 4
|
return TYPE_INFO_CONSTRUCTED.alloc_size
|
||||||
|
|
||||||
return sum(
|
return sum(
|
||||||
calculate_alloc_size(x, is_member=True)
|
calculate_alloc_size(x, is_member=True)
|
||||||
@ -35,14 +36,9 @@ ALLOC_SIZE_ROUTER.add(prelude.struct, calculate_alloc_size_struct)
|
|||||||
ALLOC_SIZE_ROUTER.add(prelude.tuple_, calculate_alloc_size_tuple)
|
ALLOC_SIZE_ROUTER.add(prelude.tuple_, calculate_alloc_size_tuple)
|
||||||
|
|
||||||
def calculate_alloc_size(typ: Type3, is_member: bool = False) -> int:
|
def calculate_alloc_size(typ: Type3, is_member: bool = False) -> int:
|
||||||
if typ in (prelude.u8, prelude.i8, ):
|
typ_info = TYPE_INFO_MAP.get(typ.name)
|
||||||
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32
|
if typ_info is not None:
|
||||||
|
return typ_info.alloc_size
|
||||||
if typ in (prelude.u32, prelude.i32, prelude.f32, ):
|
|
||||||
return 4
|
|
||||||
|
|
||||||
if typ in (prelude.u64, prelude.i64, prelude.f64, ):
|
|
||||||
return 8
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return ALLOC_SIZE_ROUTER(is_member, typ)
|
return ALLOC_SIZE_ROUTER(is_member, typ)
|
||||||
@ -50,7 +46,7 @@ def calculate_alloc_size(typ: Type3, is_member: bool = False) -> int:
|
|||||||
if is_member:
|
if is_member:
|
||||||
# By default, 'boxed' or 'constructed' types are
|
# By default, 'boxed' or 'constructed' types are
|
||||||
# stored as pointers when a member of a struct or tuple
|
# stored as pointers when a member of a struct or tuple
|
||||||
return 4
|
return TYPE_INFO_CONSTRUCTED.alloc_size
|
||||||
|
|
||||||
raise NotImplementedError(typ)
|
raise NotImplementedError(typ)
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,63 @@
|
|||||||
"""
|
"""
|
||||||
stdlib: Standard types that are not wasm primitives
|
stdlib: Standard types that are not wasm primitives
|
||||||
"""
|
"""
|
||||||
|
from typing import Mapping, NamedTuple, Type
|
||||||
|
|
||||||
from phasm.stdlib import alloc
|
from phasm.stdlib import alloc
|
||||||
from phasm.type3.routers import TypeVariableLookup
|
from phasm.type3.routers import TypeVariableLookup
|
||||||
from phasm.type3.types import IntType3, Type3
|
from phasm.type3.types import IntType3, Type3
|
||||||
|
from phasm.wasm import (
|
||||||
|
WasmType,
|
||||||
|
WasmTypeFloat32,
|
||||||
|
WasmTypeFloat64,
|
||||||
|
WasmTypeInt32,
|
||||||
|
WasmTypeInt64,
|
||||||
|
WasmTypeNone,
|
||||||
|
)
|
||||||
from phasm.wasmgenerator import Generator, VarType_Base, func_wrapper
|
from phasm.wasmgenerator import Generator, VarType_Base, func_wrapper
|
||||||
from phasm.wasmgenerator import VarType_f32 as f32
|
from phasm.wasmgenerator import VarType_f32 as f32
|
||||||
from phasm.wasmgenerator import VarType_f64 as f64
|
from phasm.wasmgenerator import VarType_f64 as f64
|
||||||
from phasm.wasmgenerator import VarType_i32 as i32
|
from phasm.wasmgenerator import VarType_i32 as i32
|
||||||
from phasm.wasmgenerator import VarType_i64 as i64
|
from phasm.wasmgenerator import VarType_i64 as i64
|
||||||
|
|
||||||
|
TypeInfo = NamedTuple('TypeInfo', [
|
||||||
|
# Name of the type
|
||||||
|
('typ', str, ),
|
||||||
|
# What WebAssembly type to use when passing this value around
|
||||||
|
# For example in function arguments
|
||||||
|
('wasm_type', Type[WasmType]),
|
||||||
|
# What WebAssembly function to use when loading a value from memory
|
||||||
|
('wasm_load_func', str),
|
||||||
|
# What WebAssembly function to use when storing a value to memory
|
||||||
|
('wasm_store_func', str),
|
||||||
|
# When storing this value in memory, how many bytes do we use?
|
||||||
|
# Only valid for non-constructed types, see calculate_alloc_size
|
||||||
|
# Should match wasm_load_func / wasm_store_func
|
||||||
|
('alloc_size', int),
|
||||||
|
])
|
||||||
|
|
||||||
|
TYPE_INFO_MAP: Mapping[str, TypeInfo] = {
|
||||||
|
'none': TypeInfo('none', WasmTypeNone, 'nop', 'nop', 0),
|
||||||
|
'bool': TypeInfo('bool', WasmTypeInt32, 'i32.load8_u', 'i32.store8', 1),
|
||||||
|
|
||||||
|
'u8': TypeInfo('u8', WasmTypeInt32, 'i32.load8_u', 'i32.store8', 1),
|
||||||
|
'i8': TypeInfo('i8', WasmTypeInt32, 'i32.load8_s', 'i32.store8', 1),
|
||||||
|
|
||||||
|
'u32': TypeInfo('u32', WasmTypeInt32, 'i32.load', 'i32.store', 4),
|
||||||
|
'u64': TypeInfo('u64', WasmTypeInt64, 'i64.load', 'i64.store', 8),
|
||||||
|
'i32': TypeInfo('i32', WasmTypeInt32, 'i32.load', 'i32.store', 4),
|
||||||
|
'i64': TypeInfo('i64', WasmTypeInt64, 'i64.load', 'i64.store', 8),
|
||||||
|
'f32': TypeInfo('f32', WasmTypeFloat32, 'f32.load', 'f32.store', 4),
|
||||||
|
'f64': TypeInfo('f64', WasmTypeFloat64, 'f64.load', 'f64.store', 8),
|
||||||
|
'ptr': TypeInfo('ptr', WasmTypeInt32, 'i32.load', 'i32.store', 4),
|
||||||
|
}
|
||||||
|
|
||||||
|
# By default, constructed types are passed as pointers
|
||||||
|
# NOTE: ALLOC SIZE HERE DOES NOT WORK FOR CONSTRUCTED TYPES
|
||||||
|
# USE runtime.calculate_alloc_size FOR ACCURATE RESULTS
|
||||||
|
# Functions count as constructed types - even though they are
|
||||||
|
# not memory pointers but table addresses instead.
|
||||||
|
TYPE_INFO_CONSTRUCTED = TypeInfo('t a', WasmTypeInt32, 'i32.load', 'i32.store', 4)
|
||||||
|
|
||||||
@func_wrapper()
|
@func_wrapper()
|
||||||
def __alloc_bytes__(g: Generator, length: i32) -> i32:
|
def __alloc_bytes__(g: Generator, length: i32) -> i32:
|
||||||
@ -1118,26 +1166,9 @@ def static_array_sum(g: Generator, tvl: TypeVariableLookup) -> None:
|
|||||||
if sa_len.value < 1:
|
if sa_len.value < 1:
|
||||||
raise NotImplementedError('Default value in case sum is empty')
|
raise NotImplementedError('Default value in case sum is empty')
|
||||||
|
|
||||||
# FIXME: We should probably use LOAD_STORE_TYPE_MAP for this?
|
sa_type_info = TYPE_INFO_MAP.get(sa_type.name, TYPE_INFO_CONSTRUCTED)
|
||||||
mtyp_map = {
|
|
||||||
'u32': 'i32',
|
|
||||||
'u64': 'i64',
|
|
||||||
'i32': 'i32',
|
|
||||||
'i64': 'i64',
|
|
||||||
'f32': 'f32',
|
|
||||||
'f64': 'f64',
|
|
||||||
}
|
|
||||||
|
|
||||||
# FIXME: We should probably use calc_alloc_size for this?
|
|
||||||
type_var_size_map = {
|
|
||||||
'u32': 4,
|
|
||||||
'u64': 8,
|
|
||||||
'i32': 4,
|
|
||||||
'i64': 8,
|
|
||||||
'f32': 4,
|
|
||||||
'f64': 8,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# FIXME: This breaks when users start implementing their own NatNum classes
|
||||||
type_var_add_generator = {
|
type_var_add_generator = {
|
||||||
'u32': u32_natnum_add,
|
'u32': u32_natnum_add,
|
||||||
'u64': u64_natnum_add,
|
'u64': u64_natnum_add,
|
||||||
@ -1146,11 +1177,6 @@ def static_array_sum(g: Generator, tvl: TypeVariableLookup) -> None:
|
|||||||
'f32': f32_natnum_add,
|
'f32': f32_natnum_add,
|
||||||
'f64': f64_natnum_add,
|
'f64': f64_natnum_add,
|
||||||
}
|
}
|
||||||
|
|
||||||
# By default, constructed types are passed as pointers
|
|
||||||
# FIXME: We don't know what add function to call
|
|
||||||
sa_type_mtyp = mtyp_map.get(sa_type.name, 'i32')
|
|
||||||
sa_type_alloc_size = type_var_size_map.get(sa_type.name, 4)
|
|
||||||
sa_type_add_gen = type_var_add_generator[sa_type.name]
|
sa_type_add_gen = type_var_add_generator[sa_type.name]
|
||||||
|
|
||||||
# Definitions
|
# Definitions
|
||||||
@ -1169,7 +1195,7 @@ def static_array_sum(g: Generator, tvl: TypeVariableLookup) -> None:
|
|||||||
# Stack: []
|
# Stack: []
|
||||||
g.nop(comment='Calculate address at which to stop looping')
|
g.nop(comment='Calculate address at which to stop looping')
|
||||||
g.local.get(sum_adr)
|
g.local.get(sum_adr)
|
||||||
g.i32.const(sa_len.value * sa_type_alloc_size)
|
g.i32.const(sa_len.value * sa_type_info.alloc_size)
|
||||||
g.i32.add()
|
g.i32.add()
|
||||||
g.local.set(sum_stop)
|
g.local.set(sum_stop)
|
||||||
|
|
||||||
@ -1177,30 +1203,30 @@ def static_array_sum(g: Generator, tvl: TypeVariableLookup) -> None:
|
|||||||
# Stack: [] -> [sum]
|
# Stack: [] -> [sum]
|
||||||
g.nop(comment='Get the first array value as starting point')
|
g.nop(comment='Get the first array value as starting point')
|
||||||
g.local.get(sum_adr)
|
g.local.get(sum_adr)
|
||||||
g.add_statement(f'{sa_type_mtyp}.load')
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
|
||||||
# Since we did the first one, increase adr
|
# Since we did the first one, increase adr
|
||||||
# adr = adr + sa_type_alloc_size
|
# adr = adr + sa_type_alloc_size
|
||||||
# Stack: [sum] -> [sum]
|
# Stack: [sum] -> [sum]
|
||||||
g.local.get(sum_adr)
|
g.local.get(sum_adr)
|
||||||
g.i32.const(sa_type_alloc_size)
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
g.i32.add()
|
g.i32.add()
|
||||||
g.local.set(sum_adr)
|
g.local.set(sum_adr)
|
||||||
|
|
||||||
if sa_len.value > 1:
|
if sa_len.value > 1:
|
||||||
with g.loop(params=[sa_type_mtyp], result=sa_type_mtyp):
|
with g.loop(params=[sa_type_info.wasm_type().to_wat()], result=sa_type_info.wasm_type().to_wat()):
|
||||||
# sum = sum + *adr
|
# sum = sum + *adr
|
||||||
# Stack: [sum] -> [sum + *adr]
|
# Stack: [sum] -> [sum + *adr]
|
||||||
g.nop(comment='Add array value')
|
g.nop(comment='Add array value')
|
||||||
g.local.get(sum_adr)
|
g.local.get(sum_adr)
|
||||||
g.add_statement(f'{sa_type_mtyp}.load')
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
sa_type_add_gen(g, ({}, {}, ))
|
sa_type_add_gen(g, ({}, {}, ))
|
||||||
|
|
||||||
# adr = adr + sa_type_alloc_size
|
# adr = adr + sa_type_alloc_size
|
||||||
# Stack: [sum] -> [sum]
|
# Stack: [sum] -> [sum]
|
||||||
g.nop(comment='Calculate address of the next value')
|
g.nop(comment='Calculate address of the next value')
|
||||||
g.local.get(sum_adr)
|
g.local.get(sum_adr)
|
||||||
g.i32.const(sa_type_alloc_size)
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
g.i32.add()
|
g.i32.add()
|
||||||
g.local.tee(sum_adr)
|
g.local.tee(sum_adr)
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,6 @@ Contains the final types for use in Phasm, as well as construtors.
|
|||||||
"""
|
"""
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
|
||||||
Hashable,
|
Hashable,
|
||||||
Self,
|
Self,
|
||||||
Tuple,
|
Tuple,
|
||||||
@ -141,27 +140,21 @@ class TypeConstructor_Base[T]:
|
|||||||
"""
|
"""
|
||||||
Base class for type construtors
|
Base class for type construtors
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'on_create', '_cache', )
|
__slots__ = ('name', '_cache', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
"""
|
"""
|
||||||
The name of the type constructor
|
The name of the type constructor
|
||||||
"""
|
"""
|
||||||
|
|
||||||
on_create: Callable[[T, Type3], None]
|
|
||||||
"""
|
|
||||||
Who to let know if a type is created
|
|
||||||
"""
|
|
||||||
|
|
||||||
_cache: dict[T, Type3]
|
_cache: dict[T, Type3]
|
||||||
"""
|
"""
|
||||||
When constructing a type with the same arguments,
|
When constructing a type with the same arguments,
|
||||||
it should produce the exact same result.
|
it should produce the exact same result.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str, on_create: Callable[[T, Type3], None]) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.on_create = on_create
|
|
||||||
|
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
|
|
||||||
@ -188,7 +181,6 @@ class TypeConstructor_Base[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.make_application(key))
|
self._cache[key] = result = Type3(self.make_name(key), self.make_application(key))
|
||||||
self.on_create(key, result)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -292,7 +284,6 @@ class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]
|
|||||||
|
|
||||||
def __call__(self, name: str, args: tuple[tuple[str, Type3], ...]) -> Type3:
|
def __call__(self, name: str, args: tuple[tuple[str, Type3], ...]) -> Type3:
|
||||||
result = Type3(name, self.make_application(args))
|
result = Type3(name, self.make_application(args))
|
||||||
self.on_create(args, result)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from phasm.runtime import (
|
|||||||
calculate_alloc_size_struct,
|
calculate_alloc_size_struct,
|
||||||
calculate_alloc_size_tuple,
|
calculate_alloc_size_tuple,
|
||||||
)
|
)
|
||||||
|
from phasm.stdlib.types import TYPE_INFO_CONSTRUCTED, TYPE_INFO_MAP
|
||||||
from phasm.type3 import types as type3types
|
from phasm.type3 import types as type3types
|
||||||
from phasm.type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
from phasm.type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||||
|
|
||||||
@ -151,7 +152,7 @@ def _write_memory_stored_value(
|
|||||||
try:
|
try:
|
||||||
adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ)
|
adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ)
|
||||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||||
return 4
|
return TYPE_INFO_CONSTRUCTED.alloc_size
|
||||||
except NoRouteForTypeException:
|
except NoRouteForTypeException:
|
||||||
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
||||||
runner.interpreter_write_memory(adr, to_write)
|
runner.interpreter_write_memory(adr, to_write)
|
||||||
@ -303,43 +304,36 @@ def _load_memory_stored_returned_value(
|
|||||||
return LOAD_FROM_ADDRESS_ROUTER((runner, wasm_value), ret_type3)
|
return LOAD_FROM_ADDRESS_ROUTER((runner, wasm_value), ret_type3)
|
||||||
|
|
||||||
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
||||||
|
typ_info = TYPE_INFO_MAP.get(typ.name, TYPE_INFO_CONSTRUCTED)
|
||||||
|
|
||||||
|
assert len(inp) == typ_info.alloc_size
|
||||||
|
|
||||||
if typ is prelude.u8:
|
if typ is prelude.u8:
|
||||||
# See compiler.py, LOAD_STORE_TYPE_MAP and module_data_u8
|
return struct.unpack('<B', inp)[0]
|
||||||
assert len(inp) == 4
|
|
||||||
return struct.unpack('<I', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.u32:
|
if typ is prelude.u32:
|
||||||
assert len(inp) == 4
|
|
||||||
return struct.unpack('<I', inp)[0]
|
return struct.unpack('<I', inp)[0]
|
||||||
|
|
||||||
if typ is prelude.u64:
|
if typ is prelude.u64:
|
||||||
assert len(inp) == 8
|
|
||||||
return struct.unpack('<Q', inp)[0]
|
return struct.unpack('<Q', inp)[0]
|
||||||
|
|
||||||
if typ is prelude.i8:
|
if typ is prelude.i8:
|
||||||
# See compiler.py, LOAD_STORE_TYPE_MAP and module_data_i8
|
return struct.unpack('<b', inp)[0]
|
||||||
assert len(inp) == 4
|
|
||||||
return struct.unpack('<i', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.i32:
|
if typ is prelude.i32:
|
||||||
assert len(inp) == 4
|
|
||||||
return struct.unpack('<i', inp)[0]
|
return struct.unpack('<i', inp)[0]
|
||||||
|
|
||||||
if typ is prelude.i64:
|
if typ is prelude.i64:
|
||||||
assert len(inp) == 8
|
|
||||||
return struct.unpack('<q', inp)[0]
|
return struct.unpack('<q', inp)[0]
|
||||||
|
|
||||||
if typ is prelude.f32:
|
if typ is prelude.f32:
|
||||||
assert len(inp) == 4
|
|
||||||
return struct.unpack('<f', inp)[0]
|
return struct.unpack('<f', inp)[0]
|
||||||
|
|
||||||
if typ is prelude.f64:
|
if typ is prelude.f64:
|
||||||
assert len(inp) == 8
|
|
||||||
return struct.unpack('<d', inp)[0]
|
return struct.unpack('<d', inp)[0]
|
||||||
|
|
||||||
if (prelude.InternalPassAsPointer, (typ, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
if typ_info is TYPE_INFO_CONSTRUCTED:
|
||||||
# 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
|
|
||||||
adr = struct.unpack('<I', inp)[0]
|
adr = struct.unpack('<I', inp)[0]
|
||||||
|
|
||||||
return LOAD_FROM_ADDRESS_ROUTER((runner, adr), typ)
|
return LOAD_FROM_ADDRESS_ROUTER((runner, adr), typ)
|
||||||
|
|||||||
@ -23,6 +23,23 @@ def testEntry(f: {type_}) -> u8:
|
|||||||
|
|
||||||
assert exp_result == result.returned_value
|
assert exp_result == result.returned_value
|
||||||
|
|
||||||
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||||
|
('(u8, u8, u8, )', (45, 46, 47), 46, ),
|
||||||
|
('u8[5]', (45, 46, 47, 48, 49), 46, ),
|
||||||
|
('bytes', b'This is a test', 104)
|
||||||
|
])
|
||||||
|
def test_subscript_1(type_, in_put, exp_result):
|
||||||
|
code_py = f"""
|
||||||
|
@exported
|
||||||
|
def testEntry(f: {type_}) -> u8:
|
||||||
|
return f[1]
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = Suite(code_py).run_code(in_put)
|
||||||
|
|
||||||
|
assert exp_result == result.returned_value
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||||
('(u8, u8, u8, )', (45, 46, 47), 47, ),
|
('(u8, u8, u8, )', (45, 46, 47), 47, ),
|
||||||
@ -82,7 +99,7 @@ def testEntry(x: (u8, u32, u64)) -> u64:
|
|||||||
@pytest.mark.parametrize('type_, in_put', [
|
@pytest.mark.parametrize('type_, in_put', [
|
||||||
('(u8, u8, )', (45, 46), ),
|
('(u8, u8, )', (45, 46), ),
|
||||||
('u8[2]', (45, 46), ),
|
('u8[2]', (45, 46), ),
|
||||||
# bytes isn't known at runtime so works like normal
|
# dynamic_array isn't known at runtime so works like normal
|
||||||
])
|
])
|
||||||
def test_subscript_oob_constant_low(type_, in_put):
|
def test_subscript_oob_constant_low(type_, in_put):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user