Feature: Tuples with tuples in constants

This commit is contained in:
Johan B.W. de Vries 2023-11-10 15:23:10 +01:00
parent de92504394
commit 16ec664cb6
4 changed files with 97 additions and 49 deletions

View File

@ -716,57 +716,65 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
for constant in block.data: for constant in block.data:
assert isinstance(constant.type3, type3types.Type3), (id(constant), type3types.TYPE3_ASSERTION_ERROR) assert isinstance(constant.type3, type3types.Type3), (id(constant), type3types.TYPE3_ASSERTION_ERROR)
if isinstance(constant, ourlang.ConstantMemoryStored) and block is not constant.data_block:
# It's stored in a different block
# We only need to store its address
# This happens for example when a tuple refers
# to a bytes constant
assert constant.data_block.address is not None, 'Referred memory not yet stored'
data_list.append(module_data_u32(constant.data_block.address))
continue
if constant.type3 == type3types.u8: if constant.type3 == type3types.u8:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_u8(constant.value)) data_list.append(module_data_u8(constant.value))
continue continue
if constant.type3 == type3types.u32: if constant.type3 == type3types.u32:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_u32(constant.value)) data_list.append(module_data_u32(constant.value))
continue continue
if constant.type3 == type3types.u64: if constant.type3 == type3types.u64:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_u64(constant.value)) data_list.append(module_data_u64(constant.value))
continue continue
if constant.type3 == type3types.i32: if constant.type3 == type3types.i32:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_i32(constant.value)) data_list.append(module_data_i32(constant.value))
continue continue
if constant.type3 == type3types.i64: if constant.type3 == type3types.i64:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_i64(constant.value)) data_list.append(module_data_i64(constant.value))
continue continue
if constant.type3 == type3types.f32: if constant.type3 == type3types.f32:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, float) assert isinstance(constant.value, float)
data_list.append(module_data_f32(constant.value)) data_list.append(module_data_f32(constant.value))
continue continue
if constant.type3 == type3types.f64: if constant.type3 == type3types.f64:
assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, float) assert isinstance(constant.value, float)
data_list.append(module_data_f64(constant.value)) data_list.append(module_data_f64(constant.value))
continue continue
if constant.type3 == type3types.bytes: if constant.type3 == type3types.bytes:
assert isinstance(constant, ourlang.ConstantBytes) assert isinstance(constant, ourlang.ConstantBytes)
assert isinstance(constant.value, bytes)
if block is not constant.data_block: data_list.append(module_data_u32(len(constant.value)))
# It's stored in a different block data_list.append(constant.value)
# We only need to store its address
# This happens for example when a tuple refers
# to a bytes constant
assert constant.data_block.address is not None
data_list.append(module_data_u32(constant.data_block.address))
else:
data_list.append(module_data_u32(len(constant.value)))
data_list.append(constant.value)
continue continue
raise NotImplementedError(constant, constant.type3, constant.value) raise NotImplementedError(constant, constant.type3)
block_data = b''.join(data_list) block_data = b''.join(data_list)

View File

@ -1,7 +1,7 @@
""" """
Contains the syntax tree for ourlang Contains the syntax tree for ourlang
""" """
from typing import Dict, List, Optional, Union from typing import Dict, Iterable, List, Optional, Union
import enum import enum
@ -47,19 +47,31 @@ class ConstantPrimitive(Constant):
def __repr__(self) -> str: def __repr__(self) -> str:
return f'ConstantPrimitive({repr(self.value)})' return f'ConstantPrimitive({repr(self.value)})'
class ConstantBytes(Constant): class ConstantMemoryStored(Constant):
"""
An constant value expression within a statement
# FIXME: Rename to literal
"""
__slots__ = ('data_block', )
data_block: 'ModuleDataBlock'
def __init__(self, data_block: 'ModuleDataBlock') -> None:
super().__init__()
self.data_block = data_block
class ConstantBytes(ConstantMemoryStored):
""" """
A bytes constant value expression within a statement A bytes constant value expression within a statement
""" """
__slots__ = ('value', 'data_block', ) __slots__ = ('value', )
value: bytes value: bytes
data_block: 'ModuleDataBlock'
def __init__(self, value: bytes, data_block: 'ModuleDataBlock') -> None: def __init__(self, value: bytes, data_block: 'ModuleDataBlock') -> None:
super().__init__() super().__init__(data_block)
self.value = value self.value = value
self.data_block = data_block
def __repr__(self) -> str: def __repr__(self) -> str:
# Do not repr the whole ModuleDataBlock # Do not repr the whole ModuleDataBlock
@ -67,19 +79,17 @@ class ConstantBytes(Constant):
# which it needs to compile the data into the program # which it needs to compile the data into the program
return f'ConstantBytes({repr(self.value)}, @{repr(self.data_block.address)})' return f'ConstantBytes({repr(self.value)}, @{repr(self.data_block.address)})'
class ConstantTuple(Constant): class ConstantTuple(ConstantMemoryStored):
""" """
A Tuple constant value expression within a statement A Tuple constant value expression within a statement
""" """
__slots__ = ('value', 'data_block', ) __slots__ = ('value', )
value: List[Union[ConstantPrimitive, ConstantBytes]] # FIXME: Tuple of tuples? value: List[Union[ConstantPrimitive, ConstantBytes, 'ConstantTuple']]
data_block: 'ModuleDataBlock'
def __init__(self, value: List[Union[ConstantPrimitive, ConstantBytes]], data_block: 'ModuleDataBlock') -> None: def __init__(self, value: List[Union[ConstantPrimitive, ConstantBytes, 'ConstantTuple']], data_block: 'ModuleDataBlock') -> None:
super().__init__() super().__init__(data_block)
self.value = value self.value = value
self.data_block = data_block
def __repr__(self) -> str: def __repr__(self) -> str:
# Do not repr the whole ModuleDataBlock # Do not repr the whole ModuleDataBlock
@ -87,21 +97,19 @@ class ConstantTuple(Constant):
# which it needs to compile the data into the program # which it needs to compile the data into the program
return f'ConstantTuple({repr(self.value)}, @{repr(self.data_block.address)})' return f'ConstantTuple({repr(self.value)}, @{repr(self.data_block.address)})'
class ConstantStruct(Constant): class ConstantStruct(ConstantMemoryStored):
""" """
A Struct constant value expression within a statement A Struct constant value expression within a statement
""" """
__slots__ = ('struct_name', 'value', 'data_block', ) __slots__ = ('struct_name', 'value', )
struct_name: str struct_name: str
value: List[Union[ConstantPrimitive, ConstantBytes]] # FIXME: Struct of structs? value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple]]
data_block: 'ModuleDataBlock'
def __init__(self, struct_name: str, value: List[Union[ConstantPrimitive, ConstantBytes]], data_block: 'ModuleDataBlock') -> None: def __init__(self, struct_name: str, value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple]], data_block: 'ModuleDataBlock') -> None:
super().__init__() super().__init__(data_block)
self.struct_name = struct_name self.struct_name = struct_name
self.value = value self.value = value
self.data_block = data_block
def __repr__(self) -> str: def __repr__(self) -> str:
# Do not repr the whole ModuleDataBlock # Do not repr the whole ModuleDataBlock
@ -376,11 +384,11 @@ class ModuleDataBlock:
""" """
__slots__ = ('data', 'address', ) __slots__ = ('data', 'address', )
data: List[Union[ConstantPrimitive, ConstantBytes]] data: List[Union[ConstantPrimitive, ConstantMemoryStored]]
address: Optional[int] address: Optional[int]
def __init__(self, data: List[Union[ConstantPrimitive, ConstantBytes]]) -> None: def __init__(self, data: Iterable[Union[ConstantPrimitive, ConstantMemoryStored]]) -> None:
self.data = data self.data = [*data]
self.address = None self.address = None
class ModuleData: class ModuleData:

View File

@ -1,7 +1,7 @@
""" """
Parses the source code from the plain text into a syntax tree Parses the source code from the plain text into a syntax tree
""" """
from typing import Any, Dict, NoReturn, Union from typing import Any, Dict, List, NoReturn, Union
import ast import ast
@ -16,7 +16,8 @@ from .ourlang import (
Expression, Expression,
BinaryOp, BinaryOp,
ConstantPrimitive, ConstantBytes, ConstantTuple, ConstantStruct, ConstantPrimitive, ConstantMemoryStored,
ConstantBytes, ConstantTuple, ConstantStruct,
TupleInstantiation, TupleInstantiation,
FunctionCall, AccessStructMember, Subscript, FunctionCall, AccessStructMember, Subscript,
@ -219,24 +220,16 @@ class OurVisitor:
) )
if isinstance(node.value, ast.Tuple): if isinstance(node.value, ast.Tuple):
tuple_data = [ value_data = self.visit_Module_Constant(module, node.value)
self.visit_Module_Constant(module, arg_node)
for arg_node in node.value.elts
if isinstance(arg_node, ast.Constant)
]
if len(node.value.elts) != len(tuple_data):
_raise_static_error(node, 'Tuple arguments must be constants')
# Allocate the data assert isinstance(value_data, ConstantTuple) # type hint
data_block = ModuleDataBlock(tuple_data)
module.data.blocks.append(data_block)
# Then return the constant as a pointer # Then return the constant as a pointer
return ModuleConstantDef( return ModuleConstantDef(
node.target.id, node.target.id,
node.lineno, node.lineno,
self.visit_type(module, node.annotation), self.visit_type(module, node.annotation),
ConstantTuple(tuple_data, data_block), value_data,
) )
if isinstance(node.value, ast.Call): if isinstance(node.value, ast.Call):
@ -568,7 +561,23 @@ class OurVisitor:
return Subscript(varref, slice_expr) return Subscript(varref, slice_expr)
def visit_Module_Constant(self, module: Module, node: ast.Constant) -> Union[ConstantPrimitive, ConstantBytes]: def visit_Module_Constant(self, module: Module, node: Union[ast.Constant, ast.Tuple]) -> Union[ConstantPrimitive, ConstantBytes, ConstantTuple]:
if isinstance(node, ast.Tuple):
tuple_data = [
self.visit_Module_Constant(module, arg_node)
for arg_node in node.elts
if isinstance(arg_node, (ast.Constant, ast.Tuple, ))
]
if len(node.elts) != len(tuple_data):
_raise_static_error(node, 'Tuple arguments must be constants')
# Allocate the data
data_block = ModuleDataBlock(tuple_data)
module.data.blocks.append(data_block)
return ConstantTuple(tuple_data, data_block)
_not_implemented(node.kind is None, 'Constant.kind') _not_implemented(node.kind is None, 'Constant.kind')
if isinstance(node.value, (int, float, )): if isinstance(node.value, (int, float, )):

View File

@ -119,6 +119,29 @@ def testEntry() -> u64:
assert 128 == result.returned_value assert 128 == result.returned_value
@pytest.mark.integration_test
def test_constant_tuple_of_tuple_of_tuple():
code_py = """
CONSTANT: (((u64, ), u64, ), u64, ) = (((128, ), 64, ), 32, )
def l1(c: (u64, )) -> u64:
return c[0]
def l2(c: ((u64, ), u64, )) -> u64:
return l1(c[0])
def l3(c: (((u64, ), u64, ), u64, )) -> u64:
return l2(c[0])
@exported
def testEntry() -> u64:
return l3(CONSTANT)
"""
result = Suite(code_py).run_code()
assert 128 == result.returned_value
@pytest.mark.integration_test @pytest.mark.integration_test
def test_bytes_as_part_of_tuple(): def test_bytes_as_part_of_tuple():
code_py = """ code_py = """