Typing is a chapter of its own
This commit is contained in:
parent
d32613d9b8
commit
cc762cfa44
@ -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()
|
||||
|
||||
313
phasm/ourlang.py
313
phasm/ourlang.py
@ -9,137 +9,16 @@ 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()
|
||||
|
||||
206
phasm/typing.py
Normal file
206
phasm/typing.py
Normal file
@ -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
|
||||
)
|
||||
Loading…
x
Reference in New Issue
Block a user