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