From 07c0688d1b9aec0870500f611aec8a5ef8e87634 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sat, 17 Sep 2022 20:50:06 +0200 Subject: [PATCH] Ripping out old type system. Will have to reimplement bytes, static array, tuple and struct. --- phasm/codestyle.py | 13 +- phasm/compiler.py | 234 ++++++++++----------- phasm/ourlang.py | 118 +++++------ phasm/parser.py | 513 ++++++++++++++++++++++----------------------- phasm/typer.py | 71 +------ phasm/typing.py | 156 +++++++------- 6 files changed, 504 insertions(+), 601 deletions(-) diff --git a/phasm/codestyle.py b/phasm/codestyle.py index 334b3eb..04ee043 100644 --- a/phasm/codestyle.py +++ b/phasm/codestyle.py @@ -33,7 +33,7 @@ def struct_definition(inp: typing.TypeStruct) -> str: Render: TypeStruct's definition """ result = f'class {inp.name}:\n' - for mem in inp.members: + for mem in inp.members: # TODO: Broken after new type system raise NotImplementedError('Structs broken after new type system') # result += f' {mem.name}: {type_(mem.type)}\n' @@ -87,11 +87,12 @@ def expression(inp: ourlang.Expression) -> str: for arg in inp.arguments ) - if isinstance(inp.function, ourlang.StructConstructor): - return f'{inp.function.struct.name}({args})' - - if isinstance(inp.function, ourlang.TupleConstructor): - return f'({args}, )' + # 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): + # return f'({args}, )' return f'{inp.function.name}({args})' diff --git a/phasm/compiler.py b/phasm/compiler.py index edd9066..7f6d53f 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -1,7 +1,7 @@ """ This module contains the code to convert parsed Ourlang into WebAssembly code """ -from typing import List +from typing import List, Optional import struct @@ -14,19 +14,6 @@ from .stdlib import alloc as stdlib_alloc from .stdlib import types as stdlib_types from .wasmgenerator import Generator as WasmGenerator -LOAD_STORE_TYPE_MAP = { - typing.TypeUInt8: 'i32', - typing.TypeUInt32: 'i32', - typing.TypeUInt64: 'i64', - typing.TypeInt32: 'i32', - typing.TypeInt64: 'i64', - typing.TypeFloat32: 'f32', - typing.TypeFloat64: 'f64', -} -""" -When generating code, we sometimes need to load or store simple values -""" - def phasm_compile(inp: ourlang.Module) -> wasm.Module: """ Public method for compiling a parsed Phasm module into @@ -34,42 +21,44 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module: """ return module(inp) -def type_(inp: typing.TypeBase) -> wasm.WasmType: +def type_var(inp: Optional[typing.TypeVar]) -> wasm.WasmType: """ Compile: type """ - if isinstance(inp, typing.TypeNone): - return wasm.WasmTypeNone() + assert inp is not None, typing.ASSERTION_ERROR - if isinstance(inp, typing.TypeUInt8): + mtyp = typing.simplify(inp) + + if mtyp == 'u8': # WebAssembly has only support for 32 and 64 bits # So we need to store more memory per byte return wasm.WasmTypeInt32() - if isinstance(inp, typing.TypeUInt32): + if mtyp == 'u32': return wasm.WasmTypeInt32() - if isinstance(inp, typing.TypeUInt64): + if mtyp == 'u64': return wasm.WasmTypeInt64() - if isinstance(inp, typing.TypeInt32): + if mtyp == 'i32': return wasm.WasmTypeInt32() - if isinstance(inp, typing.TypeInt64): + if mtyp == 'i64': return wasm.WasmTypeInt64() - if isinstance(inp, typing.TypeFloat32): + if mtyp == 'f32': return wasm.WasmTypeFloat32() - if isinstance(inp, typing.TypeFloat64): + if mtyp == 'f64': return wasm.WasmTypeFloat64() - 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() + # 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(type_, inp) + raise NotImplementedError(inp, mtyp) # Operators that work for i32, i64, f32, f64 OPERATOR_MAP = { @@ -268,47 +257,47 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: # wgn.call(stdlib_types.__subscript_bytes__) # return - if isinstance(inp, ourlang.AccessStructMember): - mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__) - if mtyp is None: - # In the future might extend this by having structs or tuples - # as members of struct or tuples - raise NotImplementedError(expression, inp, inp.member) - - expression(wgn, inp.varref) - wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) - return - - if isinstance(inp, ourlang.AccessTupleMember): - mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__) - if mtyp is None: - # In the future might extend this by having structs or tuples - # as members of struct or tuples - raise NotImplementedError(expression, inp, inp.member) - - expression(wgn, inp.varref) - wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) - return - - if isinstance(inp, ourlang.AccessStaticArrayMember): - mtyp = LOAD_STORE_TYPE_MAP.get(inp.static_array.member_type.__class__) - if mtyp is None: - # In the future might extend this by having structs or tuples - # as members of static arrays - raise NotImplementedError(expression, inp, inp.member) - - if isinstance(inp.member, typing.TypeStaticArrayMember): - expression(wgn, inp.varref) - wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) - return - - expression(wgn, inp.varref) - expression(wgn, inp.member) - wgn.i32.const(inp.static_array.member_type.alloc_size()) - wgn.i32.mul() - wgn.i32.add() - wgn.add_statement(f'{mtyp}.load') - return + # if isinstance(inp, ourlang.AccessStructMember): + # mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__) + # if mtyp is None: + # # In the future might extend this by having structs or tuples + # # as members of struct or tuples + # raise NotImplementedError(expression, inp, inp.member) + # + # expression(wgn, inp.varref) + # wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) + # return + # + # if isinstance(inp, ourlang.AccessTupleMember): + # mtyp = LOAD_STORE_TYPE_MAP.get(inp.member.type.__class__) + # if mtyp is None: + # # In the future might extend this by having structs or tuples + # # as members of struct or tuples + # raise NotImplementedError(expression, inp, inp.member) + # + # expression(wgn, inp.varref) + # wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) + # return + # + # if isinstance(inp, ourlang.AccessStaticArrayMember): + # mtyp = LOAD_STORE_TYPE_MAP.get(inp.static_array.member_type.__class__) + # if mtyp is None: + # # In the future might extend this by having structs or tuples + # # as members of static arrays + # raise NotImplementedError(expression, inp, inp.member) + # + # if isinstance(inp.member, typing.TypeStaticArrayMember): + # expression(wgn, inp.varref) + # wgn.add_statement(f'{mtyp}.load', 'offset=' + str(inp.member.offset)) + # return + # + # expression(wgn, inp.varref) + # expression(wgn, inp.member) + # wgn.i32.const(inp.static_array.member_type.alloc_size()) + # wgn.i32.mul() + # wgn.i32.add() + # wgn.add_statement(f'{mtyp}.load') + # return if isinstance(inp, ourlang.Fold): expression_fold(wgn, inp) @@ -472,7 +461,7 @@ def function_argument(inp: ourlang.FunctionParam) -> wasm.Param: """ Compile: function argument """ - return (inp.name, type_(inp.type), ) + return (inp.name, type_var(inp.type_var), ) def import_(inp: ourlang.Function) -> wasm.Import: """ @@ -488,7 +477,7 @@ def import_(inp: ourlang.Function) -> wasm.Import: function_argument(x) for x in inp.posonlyargs ], - type_(inp.returns) + type_var(inp.returns_type_var) ) def function(inp: ourlang.Function) -> wasm.Function: @@ -499,10 +488,10 @@ def function(inp: ourlang.Function) -> wasm.Function: wgn = WasmGenerator() - if isinstance(inp, ourlang.TupleConstructor): - _generate_tuple_constructor(wgn, inp) - elif isinstance(inp, ourlang.StructConstructor): - _generate_struct_constructor(wgn, inp) + if False: # TODO: isinstance(inp, ourlang.TupleConstructor): + pass # _generate_tuple_constructor(wgn, inp) + elif False: # TODO: isinstance(inp, ourlang.StructConstructor): + pass # _generate_struct_constructor(wgn, inp) else: for stat in inp.statements: statement(wgn, stat) @@ -518,7 +507,7 @@ def function(inp: ourlang.Function) -> wasm.Function: (k, v.wasm_type(), ) for k, v in wgn.locals.items() ], - type_(inp.returns), + type_var(inp.returns_type_var), wgn.statements ) @@ -660,48 +649,49 @@ def module(inp: ourlang.Module) -> wasm.Module: return result -def _generate_tuple_constructor(wgn: WasmGenerator, inp: ourlang.TupleConstructor) -> None: - tmp_var = wgn.temp_var_i32('tuple_adr') - - # Allocated the required amounts of bytes in memory - wgn.i32.const(inp.tuple.alloc_size()) - wgn.call(stdlib_alloc.__alloc__) - wgn.local.set(tmp_var) - - # Store each member individually - for member in inp.tuple.members: - mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__) - if mtyp is None: - # In the future might extend this by having structs or tuples - # as members of struct or tuples - raise NotImplementedError(expression, inp, member) - - wgn.local.get(tmp_var) - wgn.add_statement('local.get', f'$arg{member.idx}') - wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset)) - - # Return the allocated address - wgn.local.get(tmp_var) - -def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None: - tmp_var = wgn.temp_var_i32('struct_adr') - - # Allocated the required amounts of bytes in memory - wgn.i32.const(inp.struct.alloc_size()) - wgn.call(stdlib_alloc.__alloc__) - wgn.local.set(tmp_var) - - # Store each member individually - for member in inp.struct.members: - mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__) - if mtyp is None: - # In the future might extend this by having structs or tuples - # as members of struct or tuples - raise NotImplementedError(expression, inp, member) - - wgn.local.get(tmp_var) - wgn.add_statement('local.get', f'${member.name}') - wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset)) - - # Return the allocated address - wgn.local.get(tmp_var) +# TODO: Broken after new type system +# def _generate_tuple_constructor(wgn: WasmGenerator, inp: ourlang.TupleConstructor) -> None: +# tmp_var = wgn.temp_var_i32('tuple_adr') +# +# # Allocated the required amounts of bytes in memory +# wgn.i32.const(inp.tuple.alloc_size()) +# wgn.call(stdlib_alloc.__alloc__) +# wgn.local.set(tmp_var) +# +# # Store each member individually +# for member in inp.tuple.members: +# mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__) +# if mtyp is None: +# # In the future might extend this by having structs or tuples +# # as members of struct or tuples +# raise NotImplementedError(expression, inp, member) +# +# wgn.local.get(tmp_var) +# wgn.add_statement('local.get', f'$arg{member.idx}') +# wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset)) +# +# # Return the allocated address +# wgn.local.get(tmp_var) +# +# def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None: +# tmp_var = wgn.temp_var_i32('struct_adr') +# +# # Allocated the required amounts of bytes in memory +# wgn.i32.const(inp.struct.alloc_size()) +# wgn.call(stdlib_alloc.__alloc__) +# wgn.local.set(tmp_var) +# +# # Store each member individually +# for member in inp.struct.members: +# mtyp = LOAD_STORE_TYPE_MAP.get(member.type.__class__) +# if mtyp is None: +# # In the future might extend this by having structs or tuples +# # as members of struct or tuples +# raise NotImplementedError(expression, inp, member) +# +# wgn.local.get(tmp_var) +# wgn.add_statement('local.get', f'${member.name}') +# wgn.add_statement(f'{mtyp}.store', 'offset=' + str(member.offset)) +# +# # Return the allocated address +# wgn.local.get(tmp_var) diff --git a/phasm/ourlang.py b/phasm/ourlang.py index ebc13d7..9e16e4b 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -11,11 +11,6 @@ WEBASSEMBLY_BUILDIN_FLOAT_OPS: Final = ('abs', 'sqrt', 'ceil', 'floor', 'trunc', WEBASSEMBLY_BUILDIN_BYTES_OPS: Final = ('len', ) from .typing import ( - TypeBase, - TypeNone, - TypeUInt8, TypeUInt32, TypeUInt64, - TypeInt32, TypeInt64, - TypeFloat32, TypeFloat64, TypeBytes, TypeTuple, TypeTupleMember, TypeStaticArray, TypeStaticArrayMember, @@ -280,29 +275,29 @@ class FunctionParam: """ A parameter for a Function """ - __slots__ = ('name', 'type', 'type_var', ) + __slots__ = ('name', 'type_str', 'type_var', ) name: str - type: TypeBase + type_str: str type_var: Optional[TypeVar] - def __init__(self, name: str, type_: TypeBase) -> None: + def __init__(self, name: str, type_str: str) -> None: self.name = name - self.type = type_ + self.type_str = type_str self.type_var = None class Function: """ A function processes input and produces output """ - __slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'returns', 'returns_type_var', 'posonlyargs', ) + __slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'returns_str', 'returns_type_var', 'posonlyargs', ) name: str lineno: int exported: bool imported: bool statements: List[Statement] - returns: TypeBase + returns_str: str returns_type_var: Optional[TypeVar] posonlyargs: List[FunctionParam] @@ -312,68 +307,67 @@ class Function: self.exported = False self.imported = False self.statements = [] - self.returns = TypeNone() + self.returns_str = 'None' self.returns_type_var = None self.posonlyargs = [] -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): - """ - The constructor method for a tuple - """ - __slots__ = ('tuple', ) - - tuple: TypeTuple - - def __init__(self, tuple_: TypeTuple) -> None: - name = tuple_.render_internal_name() - - super().__init__(f'@{name}@__init___@', -1) - - self.returns = tuple_ - - for mem in tuple_.members: - self.posonlyargs.append(FunctionParam(f'arg{mem.idx}', mem.type, )) - - self.tuple = tuple_ +# 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): +# """ +# The constructor method for a tuple +# """ +# __slots__ = ('tuple', ) +# +# tuple: TypeTuple +# +# def __init__(self, tuple_: TypeTuple) -> None: +# name = tuple_.render_internal_name() +# +# super().__init__(f'@{name}@__init___@', -1) +# +# self.returns = tuple_ +# +# for mem in tuple_.members: +# self.posonlyargs.append(FunctionParam(f'arg{mem.idx}', mem.type, )) +# +# self.tuple = tuple_ class ModuleConstantDef: """ A constant definition within a module """ - __slots__ = ('name', 'lineno', 'type', 'type_var', 'constant', 'data_block', ) + __slots__ = ('name', 'lineno', 'type_var', 'constant', 'data_block', ) name: str lineno: int - type: TypeBase type_var: Optional[TypeVar] constant: Constant data_block: Optional['ModuleDataBlock'] - def __init__(self, name: str, lineno: int, type_: TypeBase, constant: Constant, data_block: Optional['ModuleDataBlock']) -> None: + def __init__(self, name: str, lineno: int, constant: Constant, data_block: Optional['ModuleDataBlock']) -> None: self.name = name self.lineno = lineno - self.type = type_ self.type_var = None self.constant = constant self.data_block = data_block @@ -409,23 +403,11 @@ class Module: __slots__ = ('data', 'types', 'structs', 'constant_defs', 'functions',) data: ModuleData - types: Dict[str, TypeBase] structs: Dict[str, TypeStruct] constant_defs: Dict[str, ModuleConstantDef] functions: Dict[str, Function] def __init__(self) -> None: - self.types = { - 'None': TypeNone(), - 'u8': TypeUInt8(), - 'u32': TypeUInt32(), - 'u64': TypeUInt64(), - 'i32': TypeInt32(), - 'i64': TypeInt64(), - 'f32': TypeFloat32(), - 'f64': TypeFloat64(), - 'bytes': TypeBytes(), - } self.data = ModuleData() self.structs = {} self.constant_defs = {} diff --git a/phasm/parser.py b/phasm/parser.py index d8e94af..6fe96fc 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -6,8 +6,6 @@ from typing import Any, Dict, NoReturn, Union import ast from .typing import ( - TypeBase, - TypeBytes, TypeStruct, TypeStructMember, TypeTuple, @@ -16,7 +14,6 @@ from .typing import ( TypeStaticArrayMember, ) -from . import codestyle from .exceptions import StaticError from .ourlang import ( WEBASSEMBLY_BUILDIN_FLOAT_OPS, @@ -30,7 +27,7 @@ from .ourlang import ( ConstantPrimitive, ConstantTuple, ConstantStaticArray, FunctionCall, - StructConstructor, TupleConstructor, + # StructConstructor, TupleConstructor, UnaryOp, VariableReference, Fold, ModuleConstantReference, @@ -86,15 +83,16 @@ class OurVisitor: module.constant_defs[res.name] = res - if isinstance(res, TypeStruct): - if res.name in module.structs: - raise StaticError( - f'{res.name} already defined on line {module.structs[res.name].lineno}' - ) - - module.structs[res.name] = res - constructor = StructConstructor(res) - module.functions[constructor.name] = constructor + # TODO: Broken after type system + # if isinstance(res, TypeStruct): + # if res.name in module.structs: + # raise StaticError( + # f'{res.name} already defined on line {module.structs[res.name].lineno}' + # ) + # + # module.structs[res.name] = res + # constructor = StructConstructor(res) + # module.functions[constructor.name] = constructor if isinstance(res, Function): if res.name in module.functions: @@ -158,7 +156,7 @@ class OurVisitor: function.imported = True if node.returns: - function.returns = self.visit_type(module, node.returns) + function.returns_str = self.visit_type(module, node.returns) _not_implemented(not node.type_comment, 'FunctionDef.type_comment') @@ -186,6 +184,7 @@ class OurVisitor: if stmt.simple != 1: raise NotImplementedError('Class with non-simple arguments') + raise NotImplementedError('TODO: Broken after new type system') member = TypeStructMember(stmt.target.id, self.visit_type(module, stmt.annotation), offset) struct.members.append(member) @@ -199,74 +198,72 @@ class OurVisitor: if not isinstance(node.target.ctx, ast.Store): _raise_static_error(node, 'Must be load context') - exp_type = self.visit_type(module, node.annotation) - if isinstance(node.value, ast.Constant): return ModuleConstantDef( node.target.id, node.lineno, - exp_type, self.visit_Module_Constant(module, node.value), None, ) - if isinstance(exp_type, TypeTuple): - if not isinstance(node.value, ast.Tuple): - _raise_static_error(node, 'Must be tuple') + raise NotImplementedError('TODO: Broken after new typing system') - if len(exp_type.members) != len(node.value.elts): - _raise_static_error(node, 'Invalid number of tuple values') - - tuple_data = [ - self.visit_Module_Constant(module, arg_node) - for arg_node, mem in zip(node.value.elts, exp_type.members) - if isinstance(arg_node, ast.Constant) - ] - if len(exp_type.members) != 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) - - # Then return the constant as a pointer - return ModuleConstantDef( - node.target.id, - node.lineno, - exp_type, - ConstantTuple(tuple_data), - data_block, - ) - - if isinstance(exp_type, TypeStaticArray): - if not isinstance(node.value, ast.Tuple): - _raise_static_error(node, 'Must be static array') - - if len(exp_type.members) != len(node.value.elts): - _raise_static_error(node, 'Invalid number of static array values') - - static_array_data = [ - self.visit_Module_Constant(module, arg_node) - for arg_node in node.value.elts - if isinstance(arg_node, ast.Constant) - ] - if len(exp_type.members) != len(static_array_data): - _raise_static_error(node, 'Static array arguments must be constants') - - # Allocate the data - data_block = ModuleDataBlock(static_array_data) - module.data.blocks.append(data_block) - - # Then return the constant as a pointer - return ModuleConstantDef( - node.target.id, - node.lineno, - exp_type, - ConstantStaticArray(static_array_data), - data_block, - ) - - raise NotImplementedError(f'{node} on Module AnnAssign') + # if isinstance(exp_type, TypeTuple): + # if not isinstance(node.value, ast.Tuple): + # _raise_static_error(node, 'Must be tuple') + # + # if len(exp_type.members) != len(node.value.elts): + # _raise_static_error(node, 'Invalid number of tuple values') + # + # tuple_data = [ + # self.visit_Module_Constant(module, arg_node) + # for arg_node, mem in zip(node.value.elts, exp_type.members) + # if isinstance(arg_node, ast.Constant) + # ] + # if len(exp_type.members) != 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) + # + # # Then return the constant as a pointer + # return ModuleConstantDef( + # node.target.id, + # node.lineno, + # exp_type, + # ConstantTuple(tuple_data), + # data_block, + # ) + # + # if isinstance(exp_type, TypeStaticArray): + # if not isinstance(node.value, ast.Tuple): + # _raise_static_error(node, 'Must be static array') + # + # if len(exp_type.members) != len(node.value.elts): + # _raise_static_error(node, 'Invalid number of static array values') + # + # static_array_data = [ + # self.visit_Module_Constant(module, arg_node) + # for arg_node in node.value.elts + # if isinstance(arg_node, ast.Constant) + # ] + # if len(exp_type.members) != len(static_array_data): + # _raise_static_error(node, 'Static array arguments must be constants') + # + # # Allocate the data + # data_block = ModuleDataBlock(static_array_data) + # module.data.blocks.append(data_block) + # + # # Then return the constant as a pointer + # return ModuleConstantDef( + # node.target.id, + # node.lineno, + # ConstantStaticArray(static_array_data), + # data_block, + # ) + # + # raise NotImplementedError(f'{node} on Module AnnAssign') def visit_Module_stmt(self, module: Module, node: ast.stmt) -> None: if isinstance(node, ast.FunctionDef): @@ -301,12 +298,12 @@ class OurVisitor: _raise_static_error(node, 'Return must have an argument') return StatementReturn( - self.visit_Module_FunctionDef_expr(module, function, our_locals, function.returns, node.value) + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.value) ) if isinstance(node, ast.If): result = StatementIf( - self.visit_Module_FunctionDef_expr(module, function, our_locals, function.returns, node.test) + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.test) ) for stmt in node.body: @@ -326,7 +323,7 @@ class OurVisitor: raise NotImplementedError(f'{node} as stmt in FunctionDef') - def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, exp_type: TypeBase, node: ast.expr) -> Expression: + def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression: if isinstance(node, ast.BinOp): if isinstance(node.op, ast.Add): operator = '+' @@ -352,8 +349,8 @@ class OurVisitor: return BinaryOp( operator, - self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.left), - self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.right), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.left), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.right), ) if isinstance(node, ast.UnaryOp): @@ -366,7 +363,7 @@ class OurVisitor: return UnaryOp( operator, - self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.operand), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.operand), ) if isinstance(node, ast.Compare): @@ -387,12 +384,12 @@ class OurVisitor: return BinaryOp( operator, - self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.left), - self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.comparators[0]), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.left), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.comparators[0]), ) if isinstance(node, ast.Call): - return self.visit_Module_FunctionDef_Call(module, function, our_locals, exp_type, node) + return self.visit_Module_FunctionDef_Call(module, function, our_locals, node) if isinstance(node, ast.Constant): return self.visit_Module_Constant( @@ -426,29 +423,29 @@ class OurVisitor: if isinstance(node, ast.Tuple): raise NotImplementedError('TODO: Broken after new type system') - if not isinstance(node.ctx, ast.Load): - _raise_static_error(node, 'Must be load context') - - if isinstance(exp_type, TypeTuple): - if len(exp_type.members) != len(node.elts): - _raise_static_error(node, f'Expression is expecting a tuple of size {len(exp_type.members)}, but {len(node.elts)} are given') - - tuple_constructor = TupleConstructor(exp_type) - - func = module.functions[tuple_constructor.name] - - result = FunctionCall(func) - result.arguments = [ - self.visit_Module_FunctionDef_expr(module, function, our_locals, mem.type, arg_node) - for arg_node, mem in zip(node.elts, exp_type.members) - ] - return result - - _raise_static_error(node, f'Expression is expecting a {codestyle.type_(exp_type)}, not a tuple') + # if not isinstance(node.ctx, ast.Load): + # _raise_static_error(node, 'Must be load context') + # + # if isinstance(exp_type, TypeTuple): + # if len(exp_type.members) != len(node.elts): + # _raise_static_error(node, f'Expression is expecting a tuple of size {len(exp_type.members)}, but {len(node.elts)} are given') + # + # tuple_constructor = TupleConstructor(exp_type) + # + # func = module.functions[tuple_constructor.name] + # + # result = FunctionCall(func) + # result.arguments = [ + # self.visit_Module_FunctionDef_expr(module, function, our_locals, mem.type, arg_node) + # for arg_node, mem in zip(node.elts, exp_type.members) + # ] + # return result + # + # _raise_static_error(node, f'Expression is expecting a {codestyle.type_(exp_type)}, not a tuple') raise NotImplementedError(f'{node} as expr in FunctionDef') - def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, exp_type: TypeBase, node: ast.Call) -> Union[Fold, FunctionCall, UnaryOp]: + def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Call) -> Union[Fold, FunctionCall, UnaryOp]: if node.keywords: _raise_static_error(node, 'Keyword calling not supported') # Yet? @@ -458,17 +455,18 @@ class OurVisitor: _raise_static_error(node, 'Must be load context') if node.func.id in module.structs: - struct = module.structs[node.func.id] - struct_constructor = StructConstructor(struct) - - func = module.functions[struct_constructor.name] + raise NotImplementedError('TODO: Broken after new type system') + # struct = module.structs[node.func.id] + # struct_constructor = StructConstructor(struct) + # + # func = module.functions[struct_constructor.name] elif node.func.id in WEBASSEMBLY_BUILDIN_FLOAT_OPS: if 1 != len(node.args): _raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given') return UnaryOp( 'sqrt', - self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.args[0]), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]), ) elif node.func.id == 'u32': if 1 != len(node.args): @@ -478,7 +476,7 @@ class OurVisitor: return UnaryOp( 'cast', - self.visit_Module_FunctionDef_expr(module, function, our_locals, module.types['u8'], node.args[0]), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]), ) elif node.func.id == 'len': if 1 != len(node.args): @@ -486,7 +484,7 @@ class OurVisitor: return UnaryOp( 'len', - self.visit_Module_FunctionDef_expr(module, function, our_locals, module.types['bytes'], node.args[0]), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]), ) elif node.func.id == 'foldl': # TODO: This should a much more generic function! @@ -511,20 +509,11 @@ class OurVisitor: raise NotImplementedError('TODO: Broken after new type system') - if exp_type.__class__ != func.returns.__class__: - _raise_static_error(node, f'Expected {codestyle.type_(exp_type)}, {func.name} actually returns {codestyle.type_(func.returns)}') - - if func.returns.__class__ != func.posonlyargs[0].type.__class__: - _raise_static_error(node, f'Expected a foldable function, {func.name} returns a {codestyle.type_(func.returns)} but expects a {codestyle.type_(func.posonlyargs[0].type)}') - - if module.types['u8'].__class__ != func.posonlyargs[1].type.__class__: - _raise_static_error(node, 'Only folding over bytes (u8) is supported at this time') - return Fold( Fold.Direction.LEFT, func, - self.visit_Module_FunctionDef_expr(module, function, our_locals, func.returns, node.args[1]), - self.visit_Module_FunctionDef_expr(module, function, our_locals, module.types['bytes'], node.args[2]), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[1]), + self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[2]), ) else: if node.func.id not in module.functions: @@ -532,20 +521,18 @@ class OurVisitor: func = module.functions[node.func.id] - # if func.returns != exp_type: - # _raise_static_error(node, f'Expected {codestyle.type_(exp_type)}, {func.name} actually returns {codestyle.type_(func.returns)}') - if len(func.posonlyargs) != len(node.args): _raise_static_error(node, f'Function {node.func.id} requires {len(func.posonlyargs)} arguments but {len(node.args)} are given') result = FunctionCall(func) result.arguments.extend( - self.visit_Module_FunctionDef_expr(module, function, our_locals, param.type, arg_expr) + self.visit_Module_FunctionDef_expr(module, function, our_locals, arg_expr) for arg_expr, param in zip(node.args, func.posonlyargs) ) return result 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 function @@ -574,87 +561,89 @@ class OurVisitor: ) def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Subscript) -> Expression: - if not isinstance(node.value, ast.Name): - _raise_static_error(node, 'Must reference a name') + raise NotImplementedError('TODO: Broken after new type system') - if not isinstance(node.slice, ast.Index): - _raise_static_error(node, 'Must subscript using an index') - - if not isinstance(node.ctx, ast.Load): - _raise_static_error(node, 'Must be load context') - - varref: Union[ModuleConstantReference, VariableReference] - if node.value.id in our_locals: - param = our_locals[node.value.id] - node_typ = param.type - varref = VariableReference(param) - elif node.value.id in module.constant_defs: - constant_def = module.constant_defs[node.value.id] - node_typ = constant_def.type - varref = ModuleConstantReference(constant_def) - else: - _raise_static_error(node, f'Undefined variable {node.value.id}') - - slice_expr = self.visit_Module_FunctionDef_expr( - module, function, our_locals, module.types['u32'], node.slice.value, - ) - - if isinstance(node_typ, TypeBytes): - if isinstance(varref, ModuleConstantReference): - raise NotImplementedError(f'{node} from module constant') - - return AccessBytesIndex( - varref, - slice_expr, - ) - - if isinstance(node_typ, TypeTuple): - if not isinstance(slice_expr, ConstantPrimitive): - _raise_static_error(node, 'Must subscript using a constant index') - - idx = slice_expr.value - - if not isinstance(idx, int): - _raise_static_error(node, 'Must subscript using a constant integer index') - - if not (0 <= idx < len(node_typ.members)): - _raise_static_error(node, f'Index {idx} out of bounds for tuple {node.value.id}') - - tuple_member = node_typ.members[idx] - - if isinstance(varref, ModuleConstantReference): - raise NotImplementedError(f'{node} from module constant') - - return AccessTupleMember( - varref, - tuple_member, - ) - - if isinstance(node_typ, TypeStaticArray): - if not isinstance(slice_expr, ConstantPrimitive): - return AccessStaticArrayMember( - varref, - node_typ, - slice_expr, - ) - - idx = slice_expr.value - - if not isinstance(idx, int): - _raise_static_error(node, 'Must subscript using an integer index') - - if not (0 <= idx < len(node_typ.members)): - _raise_static_error(node, f'Index {idx} out of bounds for static array {node.value.id}') - - static_array_member = node_typ.members[idx] - - return AccessStaticArrayMember( - varref, - node_typ, - static_array_member, - ) - - _raise_static_error(node, f'Cannot take index of {node_typ} {node.value.id}') + # if not isinstance(node.value, ast.Name): + # _raise_static_error(node, 'Must reference a name') + # + # if not isinstance(node.slice, ast.Index): + # _raise_static_error(node, 'Must subscript using an index') + # + # if not isinstance(node.ctx, ast.Load): + # _raise_static_error(node, 'Must be load context') + # + # varref: Union[ModuleConstantReference, VariableReference] + # if node.value.id in our_locals: + # param = our_locals[node.value.id] + # node_typ = param.type + # varref = VariableReference(param) + # elif node.value.id in module.constant_defs: + # constant_def = module.constant_defs[node.value.id] + # node_typ = constant_def.type + # varref = ModuleConstantReference(constant_def) + # else: + # _raise_static_error(node, f'Undefined variable {node.value.id}') + # + # slice_expr = self.visit_Module_FunctionDef_expr( + # module, function, our_locals, node.slice.value, + # ) + # + # if isinstance(node_typ, TypeBytes): + # if isinstance(varref, ModuleConstantReference): + # raise NotImplementedError(f'{node} from module constant') + # + # return AccessBytesIndex( + # varref, + # slice_expr, + # ) + # + # if isinstance(node_typ, TypeTuple): + # if not isinstance(slice_expr, ConstantPrimitive): + # _raise_static_error(node, 'Must subscript using a constant index') + # + # idx = slice_expr.value + # + # if not isinstance(idx, int): + # _raise_static_error(node, 'Must subscript using a constant integer index') + # + # if not (0 <= idx < len(node_typ.members)): + # _raise_static_error(node, f'Index {idx} out of bounds for tuple {node.value.id}') + # + # tuple_member = node_typ.members[idx] + # + # if isinstance(varref, ModuleConstantReference): + # raise NotImplementedError(f'{node} from module constant') + # + # return AccessTupleMember( + # varref, + # tuple_member, + # ) + # + # if isinstance(node_typ, TypeStaticArray): + # if not isinstance(slice_expr, ConstantPrimitive): + # return AccessStaticArrayMember( + # varref, + # node_typ, + # slice_expr, + # ) + # + # idx = slice_expr.value + # + # if not isinstance(idx, int): + # _raise_static_error(node, 'Must subscript using an integer index') + # + # if not (0 <= idx < len(node_typ.members)): + # _raise_static_error(node, f'Index {idx} out of bounds for static array {node.value.id}') + # + # static_array_member = node_typ.members[idx] + # + # return AccessStaticArrayMember( + # varref, + # node_typ, + # static_array_member, + # ) + # + # _raise_static_error(node, f'Cannot take index of {node_typ} {node.value.id}') def visit_Module_Constant(self, module: Module, node: ast.Constant) -> ConstantPrimitive: del module @@ -666,10 +655,10 @@ class OurVisitor: raise NotImplementedError(f'{node.value} as constant') - def visit_type(self, module: Module, node: ast.expr) -> TypeBase: + def visit_type(self, module: Module, node: ast.expr) -> str: if isinstance(node, ast.Constant): if node.value is None: - return module.types['None'] + return 'None' _raise_static_error(node, f'Unrecognized type {node.value}') @@ -677,8 +666,10 @@ class OurVisitor: if not isinstance(node.ctx, ast.Load): _raise_static_error(node, 'Must be load context') - if node.id in module.types: - return module.types[node.id] + if node.id in ('u8', 'u32', 'u64', 'i32', 'i64', 'f32', 'f64'): # FIXME: Source this list somewhere + return node.id + + raise NotImplementedError('TODO: Broken after type system') if node.id in module.structs: return module.structs[node.id] @@ -686,61 +677,65 @@ class OurVisitor: _raise_static_error(node, f'Unrecognized type {node.id}') if isinstance(node, ast.Subscript): - if not isinstance(node.value, ast.Name): - _raise_static_error(node, 'Must be name') - if not isinstance(node.slice, ast.Index): - _raise_static_error(node, 'Must subscript using an index') - if not isinstance(node.slice.value, ast.Constant): - _raise_static_error(node, 'Must subscript using a constant index') - if not isinstance(node.slice.value.value, int): - _raise_static_error(node, 'Must subscript using a constant integer index') - if not isinstance(node.ctx, ast.Load): - _raise_static_error(node, 'Must be load context') + raise NotImplementedError('TODO: Broken after new type system') - if node.value.id in module.types: - member_type = module.types[node.value.id] - else: - _raise_static_error(node, f'Unrecognized type {node.value.id}') - - type_static_array = TypeStaticArray(member_type) - - offset = 0 - - for idx in range(node.slice.value.value): - static_array_member = TypeStaticArrayMember(idx, offset) - - type_static_array.members.append(static_array_member) - offset += member_type.alloc_size() - - key = f'{node.value.id}[{node.slice.value.value}]' - - if key not in module.types: - module.types[key] = type_static_array - - return module.types[key] + # if not isinstance(node.value, ast.Name): + # _raise_static_error(node, 'Must be name') + # if not isinstance(node.slice, ast.Index): + # _raise_static_error(node, 'Must subscript using an index') + # if not isinstance(node.slice.value, ast.Constant): + # _raise_static_error(node, 'Must subscript using a constant index') + # if not isinstance(node.slice.value.value, int): + # _raise_static_error(node, 'Must subscript using a constant integer index') + # if not isinstance(node.ctx, ast.Load): + # _raise_static_error(node, 'Must be load context') + # + # if node.value.id in module.types: + # member_type = module.types[node.value.id] + # else: + # _raise_static_error(node, f'Unrecognized type {node.value.id}') + # + # type_static_array = TypeStaticArray(member_type) + # + # offset = 0 + # + # for idx in range(node.slice.value.value): + # static_array_member = TypeStaticArrayMember(idx, offset) + # + # type_static_array.members.append(static_array_member) + # offset += member_type.alloc_size() + # + # key = f'{node.value.id}[{node.slice.value.value}]' + # + # if key not in module.types: + # module.types[key] = type_static_array + # + # return module.types[key] if isinstance(node, ast.Tuple): - if not isinstance(node.ctx, ast.Load): - _raise_static_error(node, 'Must be load context') + raise NotImplementedError('TODO: Broken after new type system') - type_tuple = TypeTuple() - - offset = 0 - - for idx, elt in enumerate(node.elts): - tuple_member = TypeTupleMember(idx, self.visit_type(module, elt), offset) - - type_tuple.members.append(tuple_member) - offset += tuple_member.type.alloc_size() - - key = type_tuple.render_internal_name() - - if key not in module.types: - module.types[key] = type_tuple - constructor = TupleConstructor(type_tuple) - module.functions[constructor.name] = constructor - - return module.types[key] + # if not isinstance(node.ctx, ast.Load): + # _raise_static_error(node, 'Must be load context') + # + # type_tuple = TypeTuple() + # + # offset = 0 + # + # for idx, elt in enumerate(node.elts): + # tuple_member = TypeTupleMember(idx, self.visit_type(module, elt), offset) + # + # type_tuple.members.append(tuple_member) + # offset += tuple_member.type.alloc_size() + # + # key = type_tuple.render_internal_name() + # + # if key not in module.types: + # module.types[key] = type_tuple + # constructor = TupleConstructor(type_tuple) + # module.functions[constructor.name] = constructor + # + # return module.types[key] raise NotImplementedError(f'{node} as type') diff --git a/phasm/typer.py b/phasm/typer.py index 1230d01..3041ec3 100644 --- a/phasm/typer.py +++ b/phasm/typer.py @@ -3,12 +3,12 @@ Type checks and enriches the given ast """ from . import ourlang -from .typing import Context, TypeConstraintBitWidth, TypeConstraintPrimitive, TypeConstraintSigned, TypeVar +from .typing import Context, TypeConstraintBitWidth, TypeConstraintPrimitive, TypeConstraintSigned, TypeVar, from_str def phasm_type(inp: ourlang.Module) -> None: module(inp) -def constant(ctx: 'Context', inp: ourlang.Constant) -> 'TypeVar': +def constant(ctx: Context, inp: ourlang.Constant) -> TypeVar: if isinstance(inp, ourlang.ConstantPrimitive): result = ctx.new_var() @@ -57,7 +57,7 @@ def constant(ctx: 'Context', inp: ourlang.Constant) -> 'TypeVar': raise NotImplementedError(constant, inp) -def expression(ctx: 'Context', inp: ourlang.Expression) -> 'TypeVar': +def expression(ctx: Context, inp: ourlang.Expression) -> 'TypeVar': if isinstance(inp, ourlang.Constant): return constant(ctx, inp) @@ -109,7 +109,7 @@ def expression(ctx: 'Context', inp: ourlang.Expression) -> 'TypeVar': raise NotImplementedError(expression, inp) -def function(ctx: 'Context', inp: ourlang.Function) -> None: +def function(ctx: Context, inp: ourlang.Function) -> None: if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn): raise NotImplementedError('Functions with not just a return statement') typ = expression(ctx, inp.statements[0].value) @@ -117,10 +117,11 @@ def function(ctx: 'Context', inp: ourlang.Function) -> None: assert inp.returns_type_var is not None ctx.unify(inp.returns_type_var, typ) -def module_constant_def(ctx: 'Context', inp: ourlang.ModuleConstantDef) -> None: - inp.type_var = _convert_old_type(ctx, inp.type, inp.name) +def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> None: constant(ctx, inp.constant) + inp.type_var = ctx.new_var() + assert inp.constant.type_var is not None ctx.unify(inp.type_var, inp.constant.type_var) @@ -128,66 +129,12 @@ def module(inp: ourlang.Module) -> None: ctx = Context() for func in inp.functions.values(): - func.returns_type_var = _convert_old_type(ctx, func.returns, f'{func.name}.(returns)') + func.returns_type_var = from_str(ctx, func.returns_str, f'{func.name}.(returns)') for param in func.posonlyargs: - param.type_var = _convert_old_type(ctx, param.type, f'{func.name}.{param.name}') + param.type_var = from_str(ctx, param.type_str, f'{func.name}.{param.name}') for cdef in inp.constant_defs.values(): module_constant_def(ctx, cdef) for func in inp.functions.values(): function(ctx, func) - -from . import typing - -def _convert_old_type(ctx: Context, inp: typing.TypeBase, location: str) -> TypeVar: - result = ctx.new_var() - - if isinstance(inp, typing.TypeUInt8): - result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) - result.add_constraint(TypeConstraintBitWidth(minb=8, maxb=8)) - result.add_constraint(TypeConstraintSigned(False)) - result.add_location(location) - return result - - if isinstance(inp, typing.TypeUInt32): - result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) - result.add_constraint(TypeConstraintBitWidth(minb=32, maxb=32)) - result.add_constraint(TypeConstraintSigned(False)) - result.add_location(location) - return result - - if isinstance(inp, typing.TypeUInt64): - result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) - result.add_constraint(TypeConstraintBitWidth(minb=64, maxb=64)) - result.add_constraint(TypeConstraintSigned(False)) - result.add_location(location) - return result - - if isinstance(inp, typing.TypeInt32): - result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) - result.add_constraint(TypeConstraintBitWidth(minb=32, maxb=32)) - result.add_constraint(TypeConstraintSigned(True)) - result.add_location(location) - return result - - if isinstance(inp, typing.TypeInt64): - result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) - result.add_constraint(TypeConstraintBitWidth(minb=64, maxb=64)) - result.add_constraint(TypeConstraintSigned(True)) - result.add_location(location) - return result - - if isinstance(inp, typing.TypeFloat32): - result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.FLOAT)) - result.add_constraint(TypeConstraintBitWidth(minb=32, maxb=32)) - result.add_location(location) - return result - - if isinstance(inp, typing.TypeFloat64): - result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.FLOAT)) - result.add_constraint(TypeConstraintBitWidth(minb=64, maxb=64)) - result.add_location(location) - return result - - raise NotImplementedError(_convert_old_type, inp) diff --git a/phasm/typing.py b/phasm/typing.py index 94bb602..de92a7b 100644 --- a/phasm/typing.py +++ b/phasm/typing.py @@ -19,88 +19,6 @@ class TypeBase: """ raise NotImplementedError(self, 'alloc_size') -class TypeNone(TypeBase): - """ - The None (or Void) type - """ - __slots__ = () - -class TypeBool(TypeBase): - """ - The boolean type - """ - __slots__ = () - -class TypeUInt8(TypeBase): - """ - The Integer type, unsigned and 8 bits wide - - Note that under the hood we need to use i32 to represent - these values in expressions. So we need to add some operations - to make sure the math checks out. - - So while this does save bytes in memory, it may not actually - speed up or improve your code. - """ - __slots__ = () - - def alloc_size(self) -> int: - return 4 # Int32 under the hood - -class TypeUInt32(TypeBase): - """ - The Integer type, unsigned and 32 bits wide - """ - __slots__ = () - - def alloc_size(self) -> int: - return 4 - -class TypeUInt64(TypeBase): - """ - The Integer type, unsigned and 64 bits wide - """ - __slots__ = () - - def alloc_size(self) -> int: - return 8 - -class TypeInt32(TypeBase): - """ - The Integer type, signed and 32 bits wide - """ - __slots__ = () - - def alloc_size(self) -> int: - return 4 - -class TypeInt64(TypeBase): - """ - The Integer type, signed and 64 bits wide - """ - __slots__ = () - - def alloc_size(self) -> int: - return 8 - -class TypeFloat32(TypeBase): - """ - The Float type, 32 bits wide - """ - __slots__ = () - - def alloc_size(self) -> int: - return 4 - -class TypeFloat64(TypeBase): - """ - The Float type, 64 bits wide - """ - __slots__ = () - - def alloc_size(self) -> int: - return 8 - class TypeBytes(TypeBase): """ The bytes type @@ -207,7 +125,7 @@ class TypeStruct(TypeBase): ## NEW STUFF BELOW -# This error can also mean that the type somewhere forgot to write a type +# This error can also mean that the typer somewhere forgot to write a type # back to the AST. If so, we need to fix the typer. ASSERTION_ERROR = 'You must call phasm_type after calling phasm_parse before you can call any other method' @@ -392,7 +310,12 @@ class Context: return result - def unify(self, l: 'TypeVar', r: 'TypeVar') -> None: + def unify(self, l: Optional[TypeVar], r: Optional[TypeVar]) -> None: + # FIXME: Write method doc, find out why pylint doesn't error + + assert l is not None, ASSERTION_ERROR + assert r is not None, ASSERTION_ERROR + assert l.ctx_id != r.ctx_id # Dunno if this'll happen, if so, just return # Backup some values that we'll overwrite @@ -438,6 +361,8 @@ def simplify(inp: TypeVar) -> Optional[str]: """ Simplifies a TypeVar into a string that wasm can work with and users can recognize + + Should round trip with from_str """ tc_prim = inp.ctx.var_constraints[inp.ctx_id].get(TypeConstraintPrimitive) tc_bits = inp.ctx.var_constraints[inp.ctx_id].get(TypeConstraintBitWidth) @@ -473,3 +398,66 @@ def simplify(inp: TypeVar) -> Optional[str]: return f'f{tc_bits.minb}' return None + +def from_str(ctx: Context, inp: str, location: str) -> TypeVar: + """ + Creates a new TypeVar from the string + + Should round trip with simplify + + The location is a reference to where you found the string + in the source code. + + This could be conidered part of parsing. Though that would give trouble + with the context creation. + """ + result = ctx.new_var() + + if inp == 'u8': + result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) + result.add_constraint(TypeConstraintBitWidth(minb=8, maxb=8)) + result.add_constraint(TypeConstraintSigned(False)) + result.add_location(location) + return result + + if inp == 'u32': + result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) + result.add_constraint(TypeConstraintBitWidth(minb=32, maxb=32)) + result.add_constraint(TypeConstraintSigned(False)) + result.add_location(location) + return result + + if inp == 'u64': + result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) + result.add_constraint(TypeConstraintBitWidth(minb=64, maxb=64)) + result.add_constraint(TypeConstraintSigned(False)) + result.add_location(location) + return result + + if inp == 'i32': + result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) + result.add_constraint(TypeConstraintBitWidth(minb=32, maxb=32)) + result.add_constraint(TypeConstraintSigned(True)) + result.add_location(location) + return result + + if inp == 'i64': + result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.INT)) + result.add_constraint(TypeConstraintBitWidth(minb=64, maxb=64)) + result.add_constraint(TypeConstraintSigned(True)) + result.add_location(location) + return result + + if inp == 'f32': + result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.FLOAT)) + result.add_constraint(TypeConstraintBitWidth(minb=32, maxb=32)) + result.add_location(location) + return result + + if inp == 'f64': + result.add_constraint(TypeConstraintPrimitive(TypeConstraintPrimitive.Primitive.FLOAT)) + result.add_constraint(TypeConstraintBitWidth(minb=64, maxb=64)) + result.add_location(location) + return result + + raise NotImplementedError(from_str, inp)