diff --git a/phasm/compiler.py b/phasm/compiler.py index cf2d99c..979543e 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -4,32 +4,33 @@ This module contains the code to convert parsed Ourlang into WebAssembly code from typing import Generator, Tuple from . import ourlang +from . import typing from . import wasm Statements = Generator[wasm.Statement, None, None] -def type_(inp: ourlang.OurType) -> wasm.WasmType: - if isinstance(inp, ourlang.OurTypeNone): +def type_(inp: typing.TypeBase) -> wasm.WasmType: + if isinstance(inp, typing.TypeNone): return wasm.WasmTypeNone() - if isinstance(inp, ourlang.OurTypeUInt8): + if isinstance(inp, typing.TypeUInt8): # WebAssembly has only support for 32 and 64 bits # So we need to store more memory per byte return wasm.WasmTypeInt32() - if isinstance(inp, ourlang.OurTypeInt32): + if isinstance(inp, typing.TypeInt32): return wasm.WasmTypeInt32() - if isinstance(inp, ourlang.OurTypeInt64): + if isinstance(inp, typing.TypeInt64): return wasm.WasmTypeInt64() - if isinstance(inp, ourlang.OurTypeFloat32): + if isinstance(inp, typing.TypeFloat32): return wasm.WasmTypeFloat32() - if isinstance(inp, ourlang.OurTypeFloat64): + if isinstance(inp, typing.TypeFloat64): return wasm.WasmTypeFloat64() - if isinstance(inp, (ourlang.Struct, ourlang.OurTypeTuple, ourlang.OurTypeBytes)): + if isinstance(inp, (typing.TypeStruct, typing.TypeTuple, typing.TypeBytes)): # Structs and tuples are passed as pointer # And pointers are i32 return wasm.WasmTypeInt32() @@ -87,25 +88,25 @@ def expression(inp: ourlang.Expression) -> Statements: yield from expression(inp.left) yield from expression(inp.right) - if isinstance(inp.type, ourlang.OurTypeInt32): + if isinstance(inp.type, typing.TypeInt32): if operator := OPERATOR_MAP.get(inp.operator, None): yield wasm.Statement(f'i32.{operator}') return if operator := I32_OPERATOR_MAP.get(inp.operator, None): yield wasm.Statement(f'i32.{operator}') return - if isinstance(inp.type, ourlang.OurTypeInt64): + if isinstance(inp.type, typing.TypeInt64): if operator := OPERATOR_MAP.get(inp.operator, None): yield wasm.Statement(f'i64.{operator}') return if operator := I64_OPERATOR_MAP.get(inp.operator, None): yield wasm.Statement(f'i64.{operator}') return - if isinstance(inp.type, ourlang.OurTypeFloat32): + if isinstance(inp.type, typing.TypeFloat32): if operator := OPERATOR_MAP.get(inp.operator, None): yield wasm.Statement(f'f32.{operator}') return - if isinstance(inp.type, ourlang.OurTypeFloat64): + if isinstance(inp.type, typing.TypeFloat64): if operator := OPERATOR_MAP.get(inp.operator, None): yield wasm.Statement(f'f64.{operator}') return @@ -115,18 +116,18 @@ def expression(inp: ourlang.Expression) -> Statements: if isinstance(inp, ourlang.UnaryOp): yield from expression(inp.right) - if isinstance(inp.type, ourlang.OurTypeFloat32): + if isinstance(inp.type, typing.TypeFloat32): if inp.operator in ourlang.WEBASSEMBLY_BUILDIN_FLOAT_OPS: yield wasm.Statement(f'f32.{inp.operator}') return - if isinstance(inp.type, ourlang.OurTypeFloat64): + if isinstance(inp.type, typing.TypeFloat64): if inp.operator in ourlang.WEBASSEMBLY_BUILDIN_FLOAT_OPS: yield wasm.Statement(f'f64.{inp.operator}') return - if isinstance(inp.type, ourlang.OurTypeInt32): + if isinstance(inp.type, typing.TypeInt32): if inp.operator == 'len': - if isinstance(inp.right.type, ourlang.OurTypeBytes): + if isinstance(inp.right.type, typing.TypeBytes): yield wasm.Statement('i32.load') return @@ -140,7 +141,7 @@ def expression(inp: ourlang.Expression) -> Statements: return if isinstance(inp, ourlang.AccessBytesIndex): - if not isinstance(inp.type, ourlang.OurTypeUInt8): + if not isinstance(inp.type, typing.TypeUInt8): raise NotImplementedError(inp, inp.type) yield from expression(inp.varref) @@ -149,14 +150,14 @@ def expression(inp: ourlang.Expression) -> Statements: return if isinstance(inp, ourlang.AccessStructMember): - if isinstance(inp.member.type, ourlang.OurTypeUInt8): + if isinstance(inp.member.type, typing.TypeUInt8): mtyp = 'i32' else: # FIXME: Properly implement this # inp.type.render() is also a hack that doesn't really work consistently if not isinstance(inp.member.type, ( - ourlang.OurTypeInt32, ourlang.OurTypeFloat32, - ourlang.OurTypeInt64, ourlang.OurTypeFloat64, + typing.TypeInt32, typing.TypeFloat32, + typing.TypeInt64, typing.TypeFloat64, )): raise NotImplementedError mtyp = inp.member.type.render() @@ -169,8 +170,8 @@ def expression(inp: ourlang.Expression) -> Statements: # FIXME: Properly implement this # inp.type.render() is also a hack that doesn't really work consistently if not isinstance(inp.type, ( - ourlang.OurTypeInt32, ourlang.OurTypeFloat32, - ourlang.OurTypeInt64, ourlang.OurTypeFloat64, + typing.TypeInt32, typing.TypeFloat32, + typing.TypeInt64, typing.TypeFloat64, )): raise NotImplementedError(inp, inp.type) @@ -213,7 +214,7 @@ def statement(inp: ourlang.Statement) -> Statements: raise NotImplementedError(statement, inp) -def function_argument(inp: Tuple[str, ourlang.OurType]) -> wasm.Param: +def function_argument(inp: Tuple[str, typing.TypeBase]) -> wasm.Param: return (inp[0], type_(inp[1]), ) def import_(inp: ourlang.Function) -> wasm.Import: @@ -352,8 +353,8 @@ def _generate_tuple_constructor(inp: ourlang.TupleConstructor) -> Statements: # FIXME: Properly implement this # inp.type.render() is also a hack that doesn't really work consistently if not isinstance(member.type, ( - ourlang.OurTypeInt32, ourlang.OurTypeFloat32, - ourlang.OurTypeInt64, ourlang.OurTypeFloat64, + typing.TypeInt32, typing.TypeFloat32, + typing.TypeInt64, typing.TypeFloat64, )): raise NotImplementedError @@ -370,14 +371,14 @@ def _generate_struct_constructor(inp: ourlang.StructConstructor) -> Statements: yield wasm.Statement('local.set', '$___new_reference___addr') for member in inp.struct.members: - if isinstance(member.type, ourlang.OurTypeUInt8): + if isinstance(member.type, typing.TypeUInt8): mtyp = 'i32' else: # FIXME: Properly implement this # inp.type.render() is also a hack that doesn't really work consistently if not isinstance(member.type, ( - ourlang.OurTypeInt32, ourlang.OurTypeFloat32, - ourlang.OurTypeInt64, ourlang.OurTypeFloat64, + typing.TypeInt32, typing.TypeFloat32, + typing.TypeInt64, typing.TypeFloat64, )): raise NotImplementedError mtyp = member.type.render() diff --git a/phasm/ourlang.py b/phasm/ourlang.py index 99156be..64bdedc 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -9,138 +9,17 @@ from typing_extensions import Final WEBASSEMBLY_BUILDIN_FLOAT_OPS: Final = ('abs', 'sqrt', 'ceil', 'floor', 'trunc', 'nearest', ) -class OurType: - """ - Type base class - """ - __slots__ = () - - def render(self) -> str: - """ - Renders the type back to source code format - - This'll look like Python code. - """ - raise NotImplementedError(self, 'render') - - def alloc_size(self) -> int: - """ - When allocating this type in memory, how many bytes do we need to reserve? - """ - raise NotImplementedError(self, 'alloc_size') - -class OurTypeNone(OurType): - """ - The None (or Void) type - """ - __slots__ = () - - def render(self) -> str: - return 'None' - -class OurTypeUInt8(OurType): - """ - The Integer type, unsigned and 8 bits wide - """ - __slots__ = () - - def render(self) -> str: - return 'u8' - - def alloc_size(self) -> int: - return 4 # Int32 under the hood - -class OurTypeInt32(OurType): - """ - The Integer type, signed and 32 bits wide - """ - __slots__ = () - - def render(self) -> str: - return 'i32' - - def alloc_size(self) -> int: - return 4 - -class OurTypeInt64(OurType): - """ - The Integer type, signed and 64 bits wide - """ - __slots__ = () - - def render(self) -> str: - return 'i64' - - def alloc_size(self) -> int: - return 8 - -class OurTypeFloat32(OurType): - """ - The Float type, 32 bits wide - """ - __slots__ = () - - def render(self) -> str: - return 'f32' - - def alloc_size(self) -> int: - return 4 - -class OurTypeFloat64(OurType): - """ - The Float type, 64 bits wide - """ - __slots__ = () - - def render(self) -> str: - return 'f64' - - def alloc_size(self) -> int: - return 8 - -class OurTypeBytes(OurType): - """ - The bytes type - """ - __slots__ = () - - def render(self) -> str: - return 'bytes' - -class TupleMember: - """ - Represents a tuple member - """ - def __init__(self, idx: int, type_: OurType, offset: int) -> None: - self.idx = idx - self.type = type_ - self.offset = offset - -class OurTypeTuple(OurType): - """ - The tuple type - """ - __slots__ = ('members', ) - - members: List[TupleMember] - - def __init__(self) -> None: - self.members = [] - - def render(self) -> str: - mems = ', '.join(x.type.render() for x in self.members) - return f'({mems}, )' - - def render_internal_name(self) -> str: - mems = '@'.join(x.type.render() for x in self.members) - assert ' ' not in mems, 'Not implement yet: subtuples' - return f'tuple@{mems}' - - def alloc_size(self) -> int: - return sum( - x.type.alloc_size() - for x in self.members - ) +from .typing import ( + TypeBase, + TypeNone, + TypeBool, + TypeUInt8, + TypeInt32, TypeInt64, + TypeFloat32, TypeFloat64, + TypeBytes, + TypeTuple, TypeTupleMember, + TypeStruct, TypeStructMember, +) class Expression: """ @@ -148,9 +27,9 @@ class Expression: """ __slots__ = ('type', ) - type: OurType + type: TypeBase - def __init__(self, type_: OurType) -> None: + def __init__(self, type_: TypeBase) -> None: self.type = type_ def render(self) -> str: @@ -175,7 +54,7 @@ class ConstantUInt8(Constant): value: int - def __init__(self, type_: OurTypeUInt8, value: int) -> None: + def __init__(self, type_: TypeUInt8, value: int) -> None: super().__init__(type_) self.value = value @@ -190,7 +69,7 @@ class ConstantInt32(Constant): value: int - def __init__(self, type_: OurTypeInt32, value: int) -> None: + def __init__(self, type_: TypeInt32, value: int) -> None: super().__init__(type_) self.value = value @@ -205,7 +84,7 @@ class ConstantInt64(Constant): value: int - def __init__(self, type_: OurTypeInt64, value: int) -> None: + def __init__(self, type_: TypeInt64, value: int) -> None: super().__init__(type_) self.value = value @@ -220,7 +99,7 @@ class ConstantFloat32(Constant): value: float - def __init__(self, type_: OurTypeFloat32, value: float) -> None: + def __init__(self, type_: TypeFloat32, value: float) -> None: super().__init__(type_) self.value = value @@ -235,7 +114,7 @@ class ConstantFloat64(Constant): value: float - def __init__(self, type_: OurTypeFloat64, value: float) -> None: + def __init__(self, type_: TypeFloat64, value: float) -> None: super().__init__(type_) self.value = value @@ -250,7 +129,7 @@ class VariableReference(Expression): name: str - def __init__(self, type_: OurType, name: str) -> None: + def __init__(self, type_: TypeBase, name: str) -> None: super().__init__(type_) self.name = name @@ -267,7 +146,7 @@ class BinaryOp(Expression): left: Expression right: Expression - def __init__(self, type_: OurType, operator: str, left: Expression, right: Expression) -> None: + def __init__(self, type_: TypeBase, operator: str, left: Expression, right: Expression) -> None: super().__init__(type_) self.operator = operator @@ -286,7 +165,7 @@ class UnaryOp(Expression): operator: str right: Expression - def __init__(self, type_: OurType, operator: str, right: Expression) -> None: + def __init__(self, type_: TypeBase, operator: str, right: Expression) -> None: super().__init__(type_) self.operator = operator @@ -336,7 +215,7 @@ class AccessBytesIndex(Expression): varref: VariableReference index: Expression - def __init__(self, type_: OurType, varref: VariableReference, index: Expression) -> None: + def __init__(self, type_: TypeBase, varref: VariableReference, index: Expression) -> None: super().__init__(type_) self.varref = varref @@ -352,9 +231,9 @@ class AccessStructMember(Expression): __slots__ = ('varref', 'member', ) varref: VariableReference - member: 'StructMember' + member: TypeStructMember - def __init__(self, varref: VariableReference, member: 'StructMember') -> None: + def __init__(self, varref: VariableReference, member: TypeStructMember) -> None: super().__init__(member.type) self.varref = varref @@ -370,9 +249,9 @@ class AccessTupleMember(Expression): __slots__ = ('varref', 'member', ) varref: VariableReference - member: TupleMember + member: TypeTupleMember - def __init__(self, varref: VariableReference, member: TupleMember, ) -> None: + def __init__(self, varref: VariableReference, member: TypeTupleMember, ) -> None: super().__init__(member.type) self.varref = varref @@ -465,8 +344,8 @@ class Function: exported: bool imported: bool statements: List[Statement] - returns: OurType - posonlyargs: List[Tuple[str, OurType]] + returns: TypeBase + posonlyargs: List[Tuple[str, TypeBase]] def __init__(self, name: str, lineno: int) -> None: self.name = name @@ -474,7 +353,7 @@ class Function: self.exported = False self.imported = False self.statements = [] - self.returns = OurTypeNone() + self.returns = TypeNone() self.posonlyargs = [] def render(self) -> str: @@ -509,9 +388,9 @@ class StructConstructor(Function): """ __slots__ = ('struct', ) - struct: 'Struct' + struct: TypeStruct - def __init__(self, struct: 'Struct') -> None: + def __init__(self, struct: TypeStruct) -> None: super().__init__(f'@{struct.name}@__init___@', -1) self.returns = struct @@ -527,9 +406,9 @@ class TupleConstructor(Function): """ __slots__ = ('tuple', ) - tuple: OurTypeTuple + tuple: TypeTuple - def __init__(self, tuple_: OurTypeTuple) -> None: + def __init__(self, tuple_: TypeTuple) -> None: name = tuple_.render_internal_name() super().__init__(f'@{name}@__init___@', -1) @@ -541,85 +420,25 @@ class TupleConstructor(Function): self.tuple = tuple_ -class StructMember: - """ - Represents a struct member - """ - def __init__(self, name: str, type_: OurType, offset: int) -> None: - self.name = name - self.type = type_ - self.offset = offset - -class Struct(OurType): - """ - A struct has named properties - """ - __slots__ = ('name', 'lineno', 'members', ) - - name: str - lineno: int - members: List[StructMember] - - def __init__(self, name: str, lineno: int) -> None: - self.name = name - self.lineno = lineno - self.members = [] - - def get_member(self, name: str) -> Optional[StructMember]: - """ - Returns a member by name - """ - for mem in self.members: - if mem.name == name: - return mem - - return None - - def render(self) -> str: - """ - Renders the type back to source code format - - This'll look like Python code. - """ - return self.name - - def render_definition(self) -> str: - """ - Renders the definition back to source code format - - This'll look like Python code. - """ - result = f'class {self.name}:\n' - for mem in self.members: - result += f' {mem.name}: {mem.type.render()}\n' - - return result - - def alloc_size(self) -> int: - return sum( - x.type.alloc_size() - for x in self.members - ) - class Module: """ A module is a file and consists of functions """ __slots__ = ('types', 'functions', 'structs', ) - types: Dict[str, OurType] + types: Dict[str, TypeBase] functions: Dict[str, Function] - structs: Dict[str, Struct] + structs: Dict[str, TypeStruct] def __init__(self) -> None: self.types = { - 'None': OurTypeNone(), - 'u8': OurTypeUInt8(), - 'i32': OurTypeInt32(), - 'i64': OurTypeInt64(), - 'f32': OurTypeFloat32(), - 'f64': OurTypeFloat64(), - 'bytes': OurTypeBytes(), + 'None': TypeNone(), + 'u8': TypeUInt8(), + 'i32': TypeInt32(), + 'i64': TypeInt64(), + 'f32': TypeFloat32(), + 'f64': TypeFloat64(), + 'bytes': TypeBytes(), } self.functions = {} self.structs = {} @@ -653,7 +472,7 @@ class StaticError(Exception): An error found during static analysis """ -OurLocals = Dict[str, OurType] +OurLocals = Dict[str, TypeBase] class OurVisitor: """ @@ -683,7 +502,7 @@ class OurVisitor: module.functions[res.name] = res - if isinstance(res, Struct): + if isinstance(res, TypeStruct): if res.name in module.structs: raise StaticError( f'{res.name} already defined on line {module.structs[res.name].lineno}' @@ -700,7 +519,7 @@ class OurVisitor: return module - def pre_visit_Module_stmt(self, module: Module, node: ast.stmt) -> Union[Function, Struct]: + def pre_visit_Module_stmt(self, module: Module, node: ast.stmt) -> Union[Function, TypeStruct]: if isinstance(node, ast.FunctionDef): return self.pre_visit_Module_FunctionDef(module, node) @@ -750,8 +569,8 @@ class OurVisitor: return function - def pre_visit_Module_ClassDef(self, module: Module, node: ast.ClassDef) -> Struct: - struct = Struct(node.name, node.lineno) + def pre_visit_Module_ClassDef(self, module: Module, node: ast.ClassDef) -> TypeStruct: + struct = TypeStruct(node.name, node.lineno) _not_implemented(not node.bases, 'ClassDef.bases') _not_implemented(not node.keywords, 'ClassDef.keywords') @@ -772,7 +591,7 @@ class OurVisitor: if stmt.simple != 1: raise NotImplementedError('Class with non-simple arguments') - member = StructMember(stmt.target.id, self.visit_type(module, stmt.annotation), offset) + member = TypeStructMember(stmt.target.id, self.visit_type(module, stmt.annotation), offset) struct.members.append(member) offset += member.type.alloc_size() @@ -831,7 +650,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: OurType, node: ast.expr) -> Expression: + def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, exp_type: TypeBase, node: ast.expr) -> Expression: if isinstance(node, ast.BinOp): if isinstance(node.op, ast.Add): operator = '+' @@ -924,7 +743,7 @@ class OurVisitor: if not isinstance(node.ctx, ast.Load): _raise_static_error(node, 'Must be load context') - if not isinstance(exp_type, OurTypeTuple): + if not isinstance(exp_type, TypeTuple): _raise_static_error(node, f'Expression is expecting a {exp_type.render()}, not a tuple') if len(exp_type.members) != len(node.elts): @@ -943,7 +762,7 @@ class OurVisitor: raise NotImplementedError(f'{node} as expr in FunctionDef') - def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, exp_type: OurType, node: ast.Call) -> Union[FunctionCall, UnaryOp]: + def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, exp_type: TypeBase, node: ast.Call) -> Union[FunctionCall, UnaryOp]: if node.keywords: _raise_static_error(node, 'Keyword calling not supported') # Yet? @@ -958,7 +777,7 @@ class OurVisitor: func = module.functions[struct_constructor.name] elif node.func.id in WEBASSEMBLY_BUILDIN_FLOAT_OPS: - if not isinstance(exp_type, (OurTypeFloat32, OurTypeFloat64, )): + if not isinstance(exp_type, (TypeFloat32, TypeFloat64, )): _raise_static_error(node, f'Cannot make {node.func.id} result in {exp_type}') if 1 != len(node.args): @@ -970,7 +789,7 @@ class OurVisitor: self.visit_Module_FunctionDef_expr(module, function, our_locals, exp_type, node.args[0]), ) elif node.func.id == 'len': - if not isinstance(exp_type, OurTypeInt32): + if not isinstance(exp_type, TypeInt32): _raise_static_error(node, f'Cannot make {node.func.id} result in {exp_type}') if 1 != len(node.args): @@ -1000,7 +819,7 @@ class OurVisitor: ) return result - def visit_Module_FunctionDef_Attribute(self, module: Module, function: Function, our_locals: OurLocals, exp_type: OurType, node: ast.Attribute) -> Expression: + def visit_Module_FunctionDef_Attribute(self, module: Module, function: Function, our_locals: OurLocals, exp_type: TypeBase, node: ast.Attribute) -> Expression: if not isinstance(node.value, ast.Name): _raise_static_error(node, 'Must reference a name') @@ -1011,7 +830,7 @@ class OurVisitor: _raise_static_error(node, f'Undefined variable {node.value.id}') node_typ = our_locals[node.value.id] - if not isinstance(node_typ, Struct): + if not isinstance(node_typ, TypeStruct): _raise_static_error(node, f'Cannot take attribute of non-struct {node.value.id}') member = node_typ.get_member(node.attr) @@ -1026,7 +845,7 @@ class OurVisitor: member, ) - def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, exp_type: OurType, node: ast.Subscript) -> Expression: + def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, exp_type: TypeBase, node: ast.Subscript) -> Expression: if not isinstance(node.value, ast.Name): _raise_static_error(node, 'Must reference a name') @@ -1045,14 +864,14 @@ class OurVisitor: module, function, our_locals, module.types['i32'], node.slice.value, ) - if isinstance(node_typ, OurTypeBytes): + if isinstance(node_typ, TypeBytes): return AccessBytesIndex( module.types['u8'], VariableReference(node_typ, node.value.id), slice_expr, ) - if isinstance(node_typ, OurTypeTuple): + if isinstance(node_typ, TypeTuple): if not isinstance(slice_expr, ConstantInt32): _raise_static_error(node, 'Must subscript using a constant index') @@ -1072,13 +891,13 @@ class OurVisitor: _raise_static_error(node, f'Cannot take index of {node_typ.render()} {node.value.id}') - def visit_Module_FunctionDef_Constant(self, module: Module, function: Function, exp_type: OurType, node: ast.Constant) -> Expression: + def visit_Module_FunctionDef_Constant(self, module: Module, function: Function, exp_type: TypeBase, node: ast.Constant) -> Expression: del module del function _not_implemented(node.kind is None, 'Constant.kind') - if isinstance(exp_type, OurTypeUInt8): + if isinstance(exp_type, TypeUInt8): if not isinstance(node.value, int): _raise_static_error(node, 'Expected integer value') @@ -1086,7 +905,7 @@ class OurVisitor: return ConstantUInt8(exp_type, node.value) - if isinstance(exp_type, OurTypeInt32): + if isinstance(exp_type, TypeInt32): if not isinstance(node.value, int): _raise_static_error(node, 'Expected integer value') @@ -1094,7 +913,7 @@ class OurVisitor: return ConstantInt32(exp_type, node.value) - if isinstance(exp_type, OurTypeInt64): + if isinstance(exp_type, TypeInt64): if not isinstance(node.value, int): _raise_static_error(node, 'Expected integer value') @@ -1102,7 +921,7 @@ class OurVisitor: return ConstantInt64(exp_type, node.value) - if isinstance(exp_type, OurTypeFloat32): + if isinstance(exp_type, TypeFloat32): if not isinstance(node.value, (float, int, )): _raise_static_error(node, 'Expected float value') @@ -1110,7 +929,7 @@ class OurVisitor: return ConstantFloat32(exp_type, node.value) - if isinstance(exp_type, OurTypeFloat64): + if isinstance(exp_type, TypeFloat64): if not isinstance(node.value, (float, int, )): _raise_static_error(node, 'Expected float value') @@ -1120,7 +939,7 @@ class OurVisitor: raise NotImplementedError(f'{node} as const for type {exp_type.render()}') - def visit_type(self, module: Module, node: ast.expr) -> OurType: + def visit_type(self, module: Module, node: ast.expr) -> TypeBase: if isinstance(node, ast.Constant): if node.value is None: return module.types['None'] @@ -1143,12 +962,12 @@ class OurVisitor: if not isinstance(node.ctx, ast.Load): _raise_static_error(node, 'Must be load context') - result = OurTypeTuple() + result = TypeTuple() offset = 0 for idx, elt in enumerate(node.elts): - member = TupleMember(idx, self.visit_type(module, elt), offset) + member = TypeTupleMember(idx, self.visit_type(module, elt), offset) result.members.append(member) offset += member.type.alloc_size() diff --git a/phasm/typing.py b/phasm/typing.py new file mode 100644 index 0000000..4252fdb --- /dev/null +++ b/phasm/typing.py @@ -0,0 +1,206 @@ +""" +The phasm type system +""" +from typing import Optional, List + +class TypeBase: + """ + TypeBase base class + """ + __slots__ = () + + def render(self) -> str: + """ + Renders the type back to source code format + + This'll look like Python code. + """ + raise NotImplementedError(self, 'render') + + def alloc_size(self) -> int: + """ + When allocating this type in memory, how many bytes do we need to reserve? + """ + raise NotImplementedError(self, 'alloc_size') + +class TypeNone(TypeBase): + """ + The None (or Void) type + """ + __slots__ = () + + def render(self) -> str: + return 'None' + +class TypeBool(TypeBase): + """ + The boolean type + """ + __slots__ = () + + def render(self) -> str: + return 'bool' + +class TypeUInt8(TypeBase): + """ + The Integer type, unsigned and 8 bits wide + """ + __slots__ = () + + def render(self) -> str: + return 'u8' + + def alloc_size(self) -> int: + return 4 # Int32 under the hood + +class TypeInt32(TypeBase): + """ + The Integer type, signed and 32 bits wide + """ + __slots__ = () + + def render(self) -> str: + return 'i32' + + def alloc_size(self) -> int: + return 4 + +class TypeInt64(TypeBase): + """ + The Integer type, signed and 64 bits wide + """ + __slots__ = () + + def render(self) -> str: + return 'i64' + + def alloc_size(self) -> int: + return 8 + +class TypeFloat32(TypeBase): + """ + The Float type, 32 bits wide + """ + __slots__ = () + + def render(self) -> str: + return 'f32' + + def alloc_size(self) -> int: + return 4 + +class TypeFloat64(TypeBase): + """ + The Float type, 64 bits wide + """ + __slots__ = () + + def render(self) -> str: + return 'f64' + + def alloc_size(self) -> int: + return 8 + +class TypeBytes(TypeBase): + """ + The bytes type + """ + __slots__ = () + + def render(self) -> str: + return 'bytes' + +class TypeTupleMember: + """ + Represents a tuple member + """ + def __init__(self, idx: int, type_: TypeBase, offset: int) -> None: + self.idx = idx + self.type = type_ + self.offset = offset + +class TypeTuple(TypeBase): + """ + The tuple type + """ + __slots__ = ('members', ) + + members: List[TypeTupleMember] + + def __init__(self) -> None: + self.members = [] + + def render(self) -> str: + mems = ', '.join(x.type.render() for x in self.members) + return f'({mems}, )' + + def render_internal_name(self) -> str: + mems = '@'.join(x.type.render() for x in self.members) + assert ' ' not in mems, 'Not implement yet: subtuples' + return f'tuple@{mems}' + + def alloc_size(self) -> int: + return sum( + x.type.alloc_size() + for x in self.members + ) + +class TypeStructMember: + """ + Represents a struct member + """ + def __init__(self, name: str, type_: TypeBase, offset: int) -> None: + self.name = name + self.type = type_ + self.offset = offset + +class TypeStruct(TypeBase): + """ + A struct has named properties + """ + __slots__ = ('name', 'lineno', 'members', ) + + name: str + lineno: int + members: List[TypeStructMember] + + def __init__(self, name: str, lineno: int) -> None: + self.name = name + self.lineno = lineno + self.members = [] + + def get_member(self, name: str) -> Optional[TypeStructMember]: + """ + Returns a member by name + """ + for mem in self.members: + if mem.name == name: + return mem + + return None + + def render(self) -> str: + """ + Renders the type back to source code format + + This'll look like Python code. + """ + return self.name + + def render_definition(self) -> str: + """ + Renders the definition back to source code format + + This'll look like Python code. + """ + result = f'class {self.name}:\n' + for mem in self.members: + result += f' {mem.name}: {mem.type.render()}\n' + + return result + + def alloc_size(self) -> int: + return sum( + x.type.alloc_size() + for x in self.members + )