More work on type3

This commit is contained in:
Johan B.W. de Vries 2022-11-24 15:43:23 +01:00
parent b5a28daebf
commit 9f21d0fd1d
9 changed files with 308 additions and 220 deletions

View File

@ -1,7 +1,12 @@
# TODO # TODO
- Implement a trace() builtin for debugging
- Implement a proper type matching / checking system - Implement a proper type matching / checking system
- Implement subscript as an operator
- Implement another operator
- Figure out how to do type classes
- Implement structs again, with the `.foo` notation working
- 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. - 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.
- Implement a FizzBuzz example - Implement a FizzBuzz example

View File

@ -36,16 +36,15 @@ def type3(inp: Type3OrPlaceholder) -> str:
return inp.name return inp.name
# def struct_definition(inp: typing.TypeStruct) -> str: def struct_definition(inp: ourlang.StructDefinition) -> str:
# """ """
# Render: TypeStruct's definition Render: TypeStruct's definition
# """ """
# result = f'class {inp.name}:\n' result = f'class {inp.struct_type3.name}:\n'
# for mem in inp.members: # TODO: Broken after new type system for mem, typ in inp.struct_type3.members.items():
# raise NotImplementedError('Structs broken after new type system') result += f' {mem}: {type3(typ)}\n'
# # result += f' {mem.name}: {type_(mem.type)}\n'
# return result
# return result
def constant_definition(inp: ourlang.ModuleConstantDef) -> str: def constant_definition(inp: ourlang.ModuleConstantDef) -> str:
""" """
@ -95,10 +94,10 @@ def expression(inp: ourlang.Expression) -> str:
for arg in inp.arguments for arg in inp.arguments
) )
if isinstance(inp.function, ourlang.StructConstructor):
return f'{inp.function.struct_type3.name}({args})'
# TODO: Broken after new type system # TODO: Broken after new type system
# if isinstance(inp.function, ourlang.StructConstructor):
# return f'{inp.function.struct.name}({args})'
#
# if isinstance(inp.function, ourlang.TupleConstructor): # if isinstance(inp.function, ourlang.TupleConstructor):
# return f'({args}, )' # return f'({args}, )'
@ -110,9 +109,8 @@ def expression(inp: ourlang.Expression) -> str:
return f'{varref}[{index}]' return f'{varref}[{index}]'
# TODO: Broken after new type system if isinstance(inp, ourlang.AccessStructMember):
# if isinstance(inp, ourlang.AccessStructMember): return f'{expression(inp.varref)}.{inp.member}'
# return f'{expression(inp.varref)}.{inp.member.name}'
if isinstance(inp, ourlang.Fold): if isinstance(inp, ourlang.Fold):
fold_name = 'foldl' if ourlang.Fold.Direction.LEFT == inp.dir else 'foldr' fold_name = 'foldl' if ourlang.Fold.Direction.LEFT == inp.dir else 'foldr'
@ -180,10 +178,10 @@ def module(inp: ourlang.Module) -> str:
""" """
result = '' result = ''
# for struct in inp.structs.values(): for struct in inp.struct_definitions.values():
# if result: if result:
# result += '\n' result += '\n'
# result += struct_definition(struct) result += struct_definition(struct)
for cdef in inp.constant_defs.values(): for cdef in inp.constant_defs.values():
if result: if result:

View File

@ -1,7 +1,7 @@
""" """
This module contains the code to convert parsed Ourlang into WebAssembly code This module contains the code to convert parsed Ourlang into WebAssembly code
""" """
from typing import List, Optional from typing import List, Union
import struct import struct
@ -14,6 +14,16 @@ from .stdlib import alloc as stdlib_alloc
from .stdlib import types as stdlib_types from .stdlib import types as stdlib_types
from .wasmgenerator import Generator as WasmGenerator from .wasmgenerator import Generator as WasmGenerator
LOAD_STORE_TYPE_MAP = {
'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',
}
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
@ -53,16 +63,10 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
if inp is type3types.f64: if inp is type3types.f64:
return wasm.WasmTypeFloat64() return wasm.WasmTypeFloat64()
# if tc_prim.primitive is typing.TypeConstraintPrimitive.Primitive.STATIC_ARRAY: if isinstance(inp, type3types.StructType3):
# # StaticArray, Tuples and Structs are passed as pointer # Structs and tuples are passed as pointer
# # And pointers are i32 # And pointers are i32
# return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
# TODO: Broken after new type system
# if isinstance(inp, (typing.TypeStruct, typing.TypeTuple, typing.TypeStaticArray, typing.TypeBytes)):
# # Structs and tuples are passed as pointer
# # And pointers are i32
# return wasm.WasmTypeInt32()
raise NotImplementedError(type3, inp) raise NotImplementedError(type3, inp)
@ -349,17 +353,19 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
# wgn.call(stdlib_types.__subscript_bytes__) # wgn.call(stdlib_types.__subscript_bytes__)
# return # return
# if isinstance(inp, ourlang.AccessStructMember): if isinstance(inp, ourlang.AccessStructMember):
# mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__) mtyp = LOAD_STORE_TYPE_MAP.get(inp.struct_type3.members[inp.member].name)
# if mtyp is None: if mtyp is None:
# # In the future might extend this by having structs or tuples # In the future might extend this by having structs or tuples
# # as members of struct or tuples # as members of struct or tuples
# raise NotImplementedError(expression, inp, inp.member) raise NotImplementedError(expression, inp, inp.struct_type3)
#
# expression(wgn, inp.varref) expression(wgn, inp.varref)
# wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) wgn.add_statement(f'{mtyp}.load', 'offset=' + str(_calculate_member_offset(
# return inp.struct_type3, inp.member
# )))
return
# if isinstance(inp, ourlang.AccessTupleMember): # if isinstance(inp, ourlang.AccessTupleMember):
# mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__) # mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__)
# if mtyp is None: # if mtyp is None:
@ -544,8 +550,8 @@ def function(inp: ourlang.Function) -> wasm.Function:
if False: # TODO: isinstance(inp, ourlang.TupleConstructor): if False: # TODO: isinstance(inp, ourlang.TupleConstructor):
pass # _generate_tuple_constructor(wgn, inp) pass # _generate_tuple_constructor(wgn, inp)
elif False: # TODO: isinstance(inp, ourlang.StructConstructor): elif isinstance(inp, ourlang.StructConstructor):
pass # _generate_struct_constructor(wgn, inp) _generate_struct_constructor(wgn, inp)
else: else:
for stat in inp.statements: for stat in inp.statements:
statement(wgn, stat) statement(wgn, stat)
@ -733,26 +739,34 @@ def module(inp: ourlang.Module) -> wasm.Module:
# #
# # Return the allocated address # # Return the allocated address
# wgn.local.get(tmp_var) # wgn.local.get(tmp_var)
#
# def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None: def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
# tmp_var = wgn.temp_var_i32('struct_adr') tmp_var = wgn.temp_var_i32('struct_adr')
#
# # Allocated the required amounts of bytes in memory # Allocated the required amounts of bytes in memory
# wgn.i32.const(inp.struct.alloc_size()) wgn.i32.const(_calculate_alloc_size(inp.struct_type3))
# wgn.call(stdlib_alloc.__alloc__) wgn.call(stdlib_alloc.__alloc__)
# wgn.local.set(tmp_var) wgn.local.set(tmp_var)
#
# # Store each member individually # Store each member individually
# for member in inp.struct.members: for memname, mtyp3 in inp.struct_type3.members.items():
# mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__) mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
# if mtyp is None: if mtyp is None:
# # In the future might extend this by having structs or tuples # In the future might extend this by having structs or tuples
# # as members of struct or tuples # as members of struct or tuples
# raise NotImplementedError(expression, inp, member) raise NotImplementedError(expression, inp, mtyp3)
#
# wgn.local.get(tmp_var) wgn.local.get(tmp_var)
# wgn.add_statement('local.get', f'${member.name}') wgn.add_statement('local.get', f'${memname}')
# wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset)) wgn.add_statement(f'{mtyp}.store', 'offset=' + str(_calculate_member_offset(
# inp.struct_type3, memname
# # Return the allocated address )))
# wgn.local.get(tmp_var)
# Return the allocated address
wgn.local.get(tmp_var)
def _calculate_alloc_size(type3: Union[type3types.StructType3, type3types.Type3]) -> int:
return 0 # FIXME: Stub
def _calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int:
return 0 # FIXME: Stub

View File

@ -11,7 +11,7 @@ WEBASSEMBLY_BUILTIN_FLOAT_OPS: Final = ('abs', 'sqrt', 'ceil', 'floor', 'trunc',
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', ) WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
from .type3 import types as type3types from .type3 import types as type3types
from .type3.types import Type3, Type3OrPlaceholder, PlaceholderForType from .type3.types import Type3, Type3OrPlaceholder, PlaceholderForType, StructType3
class Expression: class Expression:
""" """
@ -134,6 +134,23 @@ class Subscript(Expression):
self.varref = varref self.varref = varref
self.index = index self.index = index
class AccessStructMember(Expression):
"""
Access a struct member for reading of writing
"""
__slots__ = ('varref', 'struct_type3', 'member', )
varref: VariableReference
struct_type3: StructType3
member: str
def __init__(self, varref: VariableReference, struct_type3: StructType3, member: str) -> None:
super().__init__()
self.varref = varref
self.struct_type3 = struct_type3
self.member = member
class Fold(Expression): class Fold(Expression):
""" """
A (left or right) fold A (left or right) fold
@ -240,28 +257,41 @@ class Function:
self.returns_str = 'None' self.returns_str = 'None'
self.posonlyargs = [] self.posonlyargs = []
class StructDefinition:
"""
The definition for a struct
"""
__slots__ = ('struct_type3', 'lineno', )
struct_type3: StructType3
lineno: int
def __init__(self, struct_type3: StructType3, lineno: int) -> None:
self.struct_type3 = struct_type3
self.lineno = lineno
class StructConstructor(Function):
"""
The constructor method for a struct
A function will generated to instantiate a struct. The arguments
will be the defaults
"""
__slots__ = ('struct_type3', )
struct_type3: StructType3
def __init__(self, struct_type3: StructType3) -> None:
super().__init__(f'@{struct_type3.name}@__init___@', -1)
self.returns_type3 = struct_type3
for mem, typ in struct_type3.members.items():
self.posonlyargs.append(FunctionParam(mem, typ, ))
self.struct_type3 = struct_type3
# TODO: Broken after new type system # TODO: Broken after new type system
# class StructConstructor(Function):
# """
# The constructor method for a struct
#
# A function will generated to instantiate a struct. The arguments
# will be the defaults
# """
# __slots__ = ('struct', )
#
# struct: TypeStruct
#
# def __init__(self, struct: TypeStruct) -> None:
# super().__init__(f'@{struct.name}@__init___@', -1)
#
# self.returns = struct
#
# for mem in struct.members:
# self.posonlyargs.append(FunctionParam(mem.name, mem.type, ))
#
# self.struct = struct
#
# class TupleConstructor(Function): # class TupleConstructor(Function):
# """ # """
# The constructor method for a tuple # The constructor method for a tuple
@ -331,15 +361,15 @@ class Module:
""" """
A module is a file and consists of functions A module is a file and consists of functions
""" """
__slots__ = ('data', 'types', 'structs', 'constant_defs', 'functions',) __slots__ = ('data', 'types', 'struct_definitions', 'constant_defs', 'functions',)
data: ModuleData data: ModuleData
# structs: Dict[str, TypeStruct] struct_definitions: Dict[str, StructDefinition]
constant_defs: Dict[str, ModuleConstantDef] constant_defs: Dict[str, ModuleConstantDef]
functions: Dict[str, Function] functions: Dict[str, Function]
def __init__(self) -> None: def __init__(self) -> None:
self.data = ModuleData() self.data = ModuleData()
# self.structs = {} self.struct_definitions = {}
self.constant_defs = {} self.constant_defs = {}
self.functions = {} self.functions = {}

View File

@ -18,8 +18,9 @@ from .ourlang import (
BinaryOp, BinaryOp,
ConstantPrimitive, ConstantTuple, ConstantPrimitive, ConstantTuple,
FunctionCall, Subscript, FunctionCall, AccessStructMember, Subscript,
# StructConstructor, TupleConstructor, StructDefinition, StructConstructor,
# TupleConstructor,
UnaryOp, VariableReference, UnaryOp, VariableReference,
Fold, Fold,
@ -75,16 +76,15 @@ class OurVisitor:
module.constant_defs[res.name] = res module.constant_defs[res.name] = res
# TODO: Broken after type system if isinstance(res, StructDefinition):
# if isinstance(res, TypeStruct): if res.struct_type3.name in module.struct_definitions:
# if res.name in module.structs: raise StaticError(
# raise StaticError( f'{res.struct_type3.name} already defined on line {module.struct_definitions[res.struct_type3.name].lineno}'
# f'{res.name} already defined on line {module.structs[res.name].lineno}' )
# )
# module.struct_definitions[res.struct_type3.name] = res
# module.structs[res.name] = res constructor = StructConstructor(res.struct_type3)
# constructor = StructConstructor(res) module.functions[constructor.name] = constructor
# module.functions[constructor.name] = constructor
if isinstance(res, Function): if isinstance(res, Function):
if res.name in module.functions: if res.name in module.functions:
@ -101,12 +101,12 @@ class OurVisitor:
return module return module
def pre_visit_Module_stmt(self, module: Module, node: ast.stmt) -> Union[Function, ModuleConstantDef]: # TypeStruct def pre_visit_Module_stmt(self, module: Module, node: ast.stmt) -> Union[Function, StructDefinition, ModuleConstantDef]:
if isinstance(node, ast.FunctionDef): if isinstance(node, ast.FunctionDef):
return self.pre_visit_Module_FunctionDef(module, node) return self.pre_visit_Module_FunctionDef(module, node)
# if isinstance(node, ast.ClassDef): if isinstance(node, ast.ClassDef):
# return self.pre_visit_Module_ClassDef(module, node) return self.pre_visit_Module_ClassDef(module, node)
if isinstance(node, ast.AnnAssign): if isinstance(node, ast.AnnAssign):
return self.pre_visit_Module_AnnAssign(module, node) return self.pre_visit_Module_AnnAssign(module, node)
@ -155,36 +155,33 @@ class OurVisitor:
return function return function
def pre_visit_Module_ClassDef(self, module: Module, node: ast.ClassDef) -> None: # TypeStruct: def pre_visit_Module_ClassDef(self, module: Module, node: ast.ClassDef) -> StructDefinition:
raise NotImplementedError
# struct = TypeStruct(node.name, node.lineno) _not_implemented(not node.bases, 'ClassDef.bases')
# _not_implemented(not node.keywords, 'ClassDef.keywords')
# _not_implemented(not node.bases, 'ClassDef.bases') _not_implemented(not node.decorator_list, 'ClassDef.decorator_list')
# _not_implemented(not node.keywords, 'ClassDef.keywords')
# _not_implemented(not node.decorator_list, 'ClassDef.decorator_list') members: Dict[str, type3types.Type3] = {}
#
# offset = 0 for stmt in node.body:
# if not isinstance(stmt, ast.AnnAssign):
# for stmt in node.body: raise NotImplementedError(f'Class with {stmt} nodes')
# if not isinstance(stmt, ast.AnnAssign):
# raise NotImplementedError(f'Class with {stmt} nodes') if not isinstance(stmt.target, ast.Name):
# raise NotImplementedError('Class with default values')
# if not isinstance(stmt.target, ast.Name):
# raise NotImplementedError('Class with default values') if not stmt.value is None:
# raise NotImplementedError('Class with default values')
# if not stmt.value is None:
# raise NotImplementedError('Class with default values') if stmt.simple != 1:
# raise NotImplementedError('Class with non-simple arguments')
# if stmt.simple != 1:
# raise NotImplementedError('Class with non-simple arguments') if stmt.target.id in members:
# _raise_static_error(stmt, 'Struct members must have unique names')
# raise NotImplementedError('TODO: Broken after new type system')
# member = TypeStructMember(stmt.target.id, self.visit_type(module, stmt.annotation), offset) members[stmt.target.id] = self.visit_type(module, stmt.annotation)
#
# struct.members.append(member) return StructDefinition(type3types.StructType3(node.name, members), node.lineno)
# offset += member.type.alloc_size()
#
# return struct
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef: def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
if not isinstance(node.target, ast.Name): if not isinstance(node.target, ast.Name):
@ -474,13 +471,14 @@ class OurVisitor:
if not isinstance(node.func.ctx, ast.Load): if not isinstance(node.func.ctx, ast.Load):
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
# if node.func.id in module.structs: if node.func.id in module.struct_definitions:
# raise NotImplementedError('TODO: Broken after new type system') struct_definition = module.struct_definitions[node.func.id]
# struct = module.structs[node.func.id] struct_constructor = StructConstructor(struct_definition.struct_type3)
# struct_constructor = StructConstructor(struct)
# # FIXME: Defer struct de-allocation
# func = module.functions[struct_constructor.name]
if node.func.id in WEBASSEMBLY_BUILTIN_FLOAT_OPS: func = module.functions[struct_constructor.name]
elif node.func.id in WEBASSEMBLY_BUILTIN_FLOAT_OPS:
if 1 != len(node.args): if 1 != len(node.args):
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given') _raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
@ -552,33 +550,33 @@ class OurVisitor:
return result return result
def visit_Module_FunctionDef_Attribute(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Attribute) -> Expression: def visit_Module_FunctionDef_Attribute(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Attribute) -> Expression:
raise NotImplementedError('Broken after new type system') del module
# del module del function
# del function
# if not isinstance(node.value, ast.Name):
# if not isinstance(node.value, ast.Name): _raise_static_error(node, 'Must reference a name')
# _raise_static_error(node, 'Must reference a name')
# if not isinstance(node.ctx, ast.Load):
# if not isinstance(node.ctx, ast.Load): _raise_static_error(node, 'Must be load context')
# _raise_static_error(node, 'Must be load context')
# if not node.value.id in our_locals:
# if not node.value.id in our_locals: _raise_static_error(node, f'Undefined variable {node.value.id}')
# _raise_static_error(node, f'Undefined variable {node.value.id}')
# param = our_locals[node.value.id]
# param = our_locals[node.value.id]
# node_typ = param.type3
# node_typ = param.type if not isinstance(node_typ, type3types.StructType3):
# if not isinstance(node_typ, TypeStruct): _raise_static_error(node, f'Cannot take attribute of non-struct {node.value.id}')
# _raise_static_error(node, f'Cannot take attribute of non-struct {node.value.id}')
# member = node_typ.members.get(node.attr)
# member = node_typ.get_member(node.attr) if member is None:
# if member is None: _raise_static_error(node, f'{node_typ.name} has no attribute {node.attr}')
# _raise_static_error(node, f'{node_typ.name} has no attribute {node.attr}')
# return AccessStructMember(
# return AccessStructMember( VariableReference(param),
# VariableReference(param), node_typ,
# member, node.attr,
# ) )
def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Subscript) -> Expression: def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Subscript) -> Expression:
if not isinstance(node.value, ast.Name): if not isinstance(node.value, ast.Name):
@ -687,12 +685,10 @@ class OurVisitor:
if node.id in type3types.LOOKUP_TABLE: if node.id in type3types.LOOKUP_TABLE:
return type3types.LOOKUP_TABLE[node.id] return type3types.LOOKUP_TABLE[node.id]
raise NotImplementedError('TODO: Broken after type system') if node.id in module.struct_definitions:
# return module.struct_definitions[node.id].struct_type3
# if node.id in module.structs:
# return module.structs[node.id] _raise_static_error(node, f'Unrecognized type {node.id}')
#
# _raise_static_error(node, f'Unrecognized type {node.id}')
if isinstance(node, ast.Subscript): if isinstance(node, ast.Subscript):
if not isinstance(node.value, ast.Name): if not isinstance(node.value, ast.Name):

View File

@ -13,7 +13,6 @@ from .constraints import (
ConstraintBase, ConstraintBase,
LiteralFitsConstraint, SameTypeConstraint, LiteralFitsConstraint, SameTypeConstraint,
) )
from . import types
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]: def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
ctx = Context() ctx = Context()
@ -48,9 +47,17 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas
return return
if isinstance(inp, ourlang.AccessStructMember):
yield SameTypeConstraint(inp.struct_type3.members[inp.member], inp.type3,
f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
return
raise NotImplementedError(expression, inp) raise NotImplementedError(expression, inp)
def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]: def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]:
if isinstance(inp, ourlang.StructConstructor):
return
if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn): if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn):
raise NotImplementedError('Functions with not just a return statement') raise NotImplementedError('Functions with not just a return statement')

View File

@ -139,6 +139,31 @@ class AppliedType3(Type3):
def __repr__(self) -> str: def __repr__(self) -> str:
return f'AppliedType3({repr(self.base)}, {repr(self.args)})' return f'AppliedType3({repr(self.base)}, {repr(self.args)})'
class StructType3(Type3):
"""
A Type3 struct with named members
"""
__slots__ = ('name', 'members', )
name: str
"""
The structs fully qualified name
"""
members: Dict[str, Type3]
"""
The struct's field definitions
"""
def __init__(self, name: str, members: Dict[str, Type3]) -> None:
super().__init__(name)
self.name = name
self.members = dict(members)
def __repr__(self) -> str:
return f'StructType3(repr({self.name}), repr({self.members}))'
none = Type3('none') none = Type3('none')
""" """
The none type, for when functions simply don't return anything. e.g., IO(). The none type, for when functions simply don't return anything. e.g., IO().

View File

@ -1,12 +1,36 @@
import pytest import pytest
from phasm.exceptions import StaticError from phasm.exceptions import StaticError
from phasm.type3.entry import Type3Exception
from phasm.parser import phasm_parse from phasm.parser import phasm_parse
from ..constants import (
ALL_INT_TYPES
)
from ..helpers import Suite from ..helpers import Suite
@pytest.mark.integration_test @pytest.mark.integration_test
@pytest.mark.parametrize('type_', ('i32', 'f64', )) @pytest.mark.parametrize('type_', ALL_INT_TYPES)
def test_module_constant(type_):
code_py = f"""
class CheckedValue:
value: {type_}
CONSTANT: CheckedValue = CheckedValue(24)
@exported
def testEntry() -> {type_}:
return CONSTANT.value
"""
result = Suite(code_py).run_code()
assert 24 == result.returned_value
assert TYPE_MAP[type_] == type(result.returned_value)
@pytest.mark.integration_test
@pytest.mark.parametrize('type_', ALL_INT_TYPES)
def test_struct_0(type_): def test_struct_0(type_):
code_py = f""" code_py = f"""
class CheckedValue: class CheckedValue:
@ -75,43 +99,5 @@ def testEntry(arg: Struct) -> (i32, i32, ):
return arg.param return arg.param
""" """
with pytest.raises(StaticError, match=f'Static error on line 6: Expected \\(i32, i32, \\), arg.param is actually {type_}'): with pytest.raises(Type3Exception, match=r'\(i32, i32, \) must be ' + type_ + ' instead'):
phasm_parse(code_py) Suite(code_py).run_code()
@pytest.mark.integration_test
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
def test_type_mismatch_tuple_member(type_):
code_py = f"""
def testEntry(arg: ({type_}, )) -> (i32, i32, ):
return arg[0]
"""
with pytest.raises(StaticError, match=f'Static error on line 3: Expected \\(i32, i32, \\), arg\\[0\\] is actually {type_}'):
phasm_parse(code_py)
@pytest.mark.integration_test
def test_tuple_constant_too_few_values():
code_py = """
CONSTANT: (u32, u8, u8, ) = (24, 57, )
"""
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
phasm_parse(code_py)
@pytest.mark.integration_test
def test_tuple_constant_too_many_values():
code_py = """
CONSTANT: (u32, u8, u8, ) = (24, 57, 1, 1, )
"""
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
phasm_parse(code_py)
@pytest.mark.integration_test
def test_tuple_constant_type_mismatch():
code_py = """
CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
"""
with pytest.raises(StaticError, match='Static error on line 2: Integer value out of range; expected 0..255, actual 4000'):
phasm_parse(code_py)

View File

@ -82,3 +82,30 @@ def testEntry() -> i32x4:
result = Suite(code_py).run_code() result = Suite(code_py).run_code()
assert (1, 2, 3, 0) == result.returned_value assert (1, 2, 3, 0) == result.returned_value
@pytest.mark.integration_test
def test_tuple_constant_too_few_values():
code_py = """
CONSTANT: (u32, u8, u8, ) = (24, 57, )
"""
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
Suite(code_py).run_code()
@pytest.mark.integration_test
def test_tuple_constant_too_many_values():
code_py = """
CONSTANT: (u32, u8, u8, ) = (24, 57, 1, 1, )
"""
with pytest.raises(StaticError, match='Static error on line 2: Invalid number of tuple values'):
Suite(code_py).run_code()
@pytest.mark.integration_test
def test_tuple_constant_type_mismatch():
code_py = """
CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
"""
with pytest.raises(StaticError, match='Static error on line 2: Integer value out of range; expected 0..255, actual 4000'):
Suite(code_py).run_code()